diff --git a/Cargo.lock b/Cargo.lock index 006e46f00..3cd66d153 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3573,8 +3573,6 @@ dependencies = [ [[package]] name = "miden-prover" version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb2c94e36f57684d7fa0cd382adeedc1728d502dbbe69ad1c12f4a931f45511" dependencies = [ "bincode", "miden-air", @@ -3700,8 +3698,6 @@ dependencies = [ [[package]] name = "miden-tx" version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d788795041ce5e6f947a3256314373171e4877c11b86fafeabcec4d8b8628d9" dependencies = [ "miden-processor", "miden-protocol", diff --git a/Cargo.toml b/Cargo.toml index 567ca37ca..7ae45967f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,8 +35,10 @@ rust-version = "1.93" version = "0.14.9" # Optimize the cryptography for faster tests involving account creation. -[profile.test.package.miden-crypto] -opt-level = 2 +[profile.test.package."*"] +opt-level = 3 +debug-assertions = false +overflow-checks = false [profile.release] debug = true @@ -120,6 +122,10 @@ tracing = { version = "0.1" } tracing-subscriber = { features = ["env-filter", "fmt", "json"], version = "0.3" } url = { features = ["serde"], version = "2.5" } +[patch.crates-io] +miden-prover = { path = "vendor/miden-prover" } +miden-tx = { path = "vendor/miden-tx" } + # Lints are set to warn for development, which are promoted to errors in CI. [workspace.lints.clippy] # Pedantic lints are set to a lower priority which allows lints in the group to be selectively enabled. diff --git a/bin/remote-prover/src/server/prover.rs b/bin/remote-prover/src/server/prover.rs index 2931cc70f..1200e956d 100644 --- a/bin/remote-prover/src/server/prover.rs +++ b/bin/remote-prover/src/server/prover.rs @@ -100,9 +100,11 @@ impl ProveRequest for LocalTransactionProver { type Output = ProvenTransaction; async fn prove(&self, input: Self::Input) -> Result { - LocalTransactionProver::prove(self, input).await.map_err(|e| { - tonic::Status::internal(e.as_report_context("failed to prove transaction")) - }) + LocalTransactionProver::prove_transaction_inputs(self, input) + .await + .map_err(|e| { + tonic::Status::internal(e.as_report_context("failed to prove transaction")) + }) } } diff --git a/bin/remote-prover/src/server/tests.rs b/bin/remote-prover/src/server/tests.rs index bc3d89262..74bfcd8e5 100644 --- a/bin/remote-prover/src/server/tests.rs +++ b/bin/remote-prover/src/server/tests.rs @@ -126,7 +126,10 @@ impl ProofRequest { .unwrap(); let tx = Box::pin(tx.execute()).await.unwrap(); - let tx = LocalTransactionProver::default().prove(tx.tx_inputs().clone()).await.unwrap(); + let tx = LocalTransactionProver::default() + .prove_transaction_inputs(tx.tx_inputs().clone()) + .await + .unwrap(); ProposedBatch::new( vec![Arc::new(tx)], diff --git a/crates/ntx-builder/src/actor/execute.rs b/crates/ntx-builder/src/actor/execute.rs index 9718056ee..d0e5fc218 100644 --- a/crates/ntx-builder/src/actor/execute.rs +++ b/crates/ntx-builder/src/actor/execute.rs @@ -321,7 +321,7 @@ impl NtxContext { } else { // Only perform tx inputs clone for local proving. let tx_inputs = tx_inputs.clone(); - LocalTransactionProver::default().prove(tx_inputs).await + LocalTransactionProver::default().prove_transaction_inputs(tx_inputs).await } .map_err(NtxError::Proving) } diff --git a/vendor/miden-prover/.cargo-ok b/vendor/miden-prover/.cargo-ok new file mode 100644 index 000000000..5f8b79583 --- /dev/null +++ b/vendor/miden-prover/.cargo-ok @@ -0,0 +1 @@ +{"v":1} \ No newline at end of file diff --git a/vendor/miden-prover/.cargo_vcs_info.json b/vendor/miden-prover/.cargo_vcs_info.json new file mode 100644 index 000000000..9a8bd46dd --- /dev/null +++ b/vendor/miden-prover/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "b59f1d07b76a23e628511d9ea538d07c144c2044" + }, + "path_in_vcs": "prover" +} \ No newline at end of file diff --git a/vendor/miden-prover/Cargo.lock b/vendor/miden-prover/Cargo.lock new file mode 100644 index 000000000..4a51ecf60 --- /dev/null +++ b/vendor/miden-prover/Cargo.lock @@ -0,0 +1,2292 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "blake3" +version = "1.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d2d5991425dfd0785aed03aedcf0b321d61975c9b5b3689c774a2610ae0b51e" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "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 = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "cc" +version = "1.2.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures 0.2.17", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version 0.4.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version 0.4.1", + "syn 2.0.117", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dissimilar" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aeda16ab4059c5fd2a83f2b9c9e9c981327b18aa8e3b313f7e6563799d4f093e" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "spin 0.9.8", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-sink", + "futures-task", + "pin-project-lite", +] + +[[package]] +name = "generator" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f04ae4152da20c76fe800fa48659201d5cf627c5149ca0b707b69d7eef6cf9" +dependencies = [ + "cc", + "cfg-if", + "libc", + "log", + "rustversion", + "windows-link", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "indenter" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" + +[[package]] +name = "indexmap" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "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", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.184" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "miden-air" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d15646ebc95906b2a7cb66711d1e184f53fd6edc2605730bbcf0c2a129f792cf" +dependencies = [ + "miden-core", + "miden-crypto", + "miden-utils-indexing", + "thiserror", + "tracing", +] + +[[package]] +name = "miden-core" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdec54a321cdf3d23e9ef615e91cb858038c6b4d4202507bdec048fc6d7763e4" +dependencies = [ + "derive_more", + "itertools", + "miden-crypto", + "miden-debug-types", + "miden-formatting", + "miden-utils-core-derive", + "miden-utils-indexing", + "miden-utils-sync", + "num-derive", + "num-traits", + "thiserror", +] + +[[package]] +name = "miden-crypto" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0a034a460e27723dcfdf25effffab84331c3b46b13e7a1bd674197cc71bfe" +dependencies = [ + "blake3", + "cc", + "chacha20poly1305", + "curve25519-dalek", + "ed25519-dalek", + "flume", + "glob", + "hkdf", + "k256", + "miden-crypto-derive", + "miden-field", + "miden-serde-utils", + "num", + "num-complex", + "p3-blake3", + "p3-challenger", + "p3-dft", + "p3-goldilocks", + "p3-keccak", + "p3-matrix", + "p3-maybe-rayon", + "p3-miden-lifted-stark", + "p3-symmetric", + "p3-util", + "rand 0.9.2", + "rand_chacha", + "rand_core 0.9.5", + "rand_hc", + "rayon", + "serde", + "sha2", + "sha3", + "subtle", + "thiserror", + "x25519-dalek", +] + +[[package]] +name = "miden-crypto-derive" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8bf6ebde028e79bcc61a3632d2f375a5cc64caa17d014459f75015238cb1e08" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "miden-debug-types" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6e50274d11c80b901cf6c90362de8c98c8c8ad6030c80624d683b63d899a0fb" +dependencies = [ + "memchr", + "miden-crypto", + "miden-formatting", + "miden-miette", + "miden-utils-indexing", + "miden-utils-sync", + "paste", + "serde", + "serde_spanned", + "thiserror", +] + +[[package]] +name = "miden-field" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38011348f4fb4c9e5ce1f471203d024721c00e3b60a91aa91aaefe6738d8b5ea" +dependencies = [ + "miden-serde-utils", + "num-bigint", + "p3-challenger", + "p3-field", + "p3-goldilocks", + "paste", + "rand 0.10.0", + "serde", + "subtle", + "thiserror", +] + +[[package]] +name = "miden-formatting" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e392e0a8c34b32671012b439de35fa8987bf14f0f8aac279b97f8b8cc6e263b" +dependencies = [ + "unicode-width 0.1.14", +] + +[[package]] +name = "miden-miette" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eef536978f24a179d94fa2a41e4f92b28e7d8aab14b8d23df28ad2a3d7098b20" +dependencies = [ + "cfg-if", + "futures", + "indenter", + "lazy_static", + "miden-miette-derive", + "owo-colors", + "regex", + "rustc_version 0.2.3", + "rustversion", + "serde_json", + "spin 0.9.8", + "strip-ansi-escapes", + "syn 2.0.117", + "textwrap", + "thiserror", + "trybuild", + "unicode-width 0.1.14", +] + +[[package]] +name = "miden-miette-derive" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86a905f3ea65634dd4d1041a4f0fd0a3e77aa4118341d265af1a94339182222f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "miden-processor" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ec6cecbf22bd92b73a931ee80b424e46b8b7cdf4f2f3c364c25c5c15d2840da" +dependencies = [ + "itertools", + "miden-air", + "miden-core", + "miden-debug-types", + "miden-utils-diagnostics", + "miden-utils-indexing", + "paste", + "rayon", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "miden-prover" +version = "0.22.1" +dependencies = [ + "bincode", + "miden-air", + "miden-core", + "miden-crypto", + "miden-debug-types", + "miden-processor", + "serde", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "miden-serde-utils" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff78082e9b4ca89863e68da01b35f8a4029ee6fd912e39fa41fde4273a7debab" +dependencies = [ + "p3-field", + "p3-goldilocks", +] + +[[package]] +name = "miden-utils-core-derive" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3846c8674ccec0c37005f99c1a599a24790ba2a5e5f4e1c7aec5f456821df835" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "miden-utils-diagnostics" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397f5d1e8679cf17cf7713ffd9654840791a6ed5818b025bbc2fbfdce846579a" +dependencies = [ + "miden-crypto", + "miden-debug-types", + "miden-miette", + "paste", + "tracing", +] + +[[package]] +name = "miden-utils-indexing" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8834e76299686bcce3de1685158aa4cff49b7fa5e0e00a6cc811e8f2cf5775f" +dependencies = [ + "miden-crypto", + "thiserror", +] + +[[package]] +name = "miden-utils-sync" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a9e9747e9664c1a0997bb040ae291306ea0a1c74a572141ec66cec855c1b0e8" +dependencies = [ + "lock_api", + "loom", + "once_cell", + "parking_lot", +] + +[[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 = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +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.117", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "owo-colors" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" + +[[package]] +name = "p3-air" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f2ec9cbfc642fc5173817287c3f8b789d07743b5f7e812d058b7a03e344f9ab" +dependencies = [ + "p3-field", + "p3-matrix", + "tracing", +] + +[[package]] +name = "p3-blake3" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b667f43b19499dd939c9e2553aa95688936a88360d50117dae3c8848d07dbc70" +dependencies = [ + "blake3", + "p3-symmetric", + "p3-util", +] + +[[package]] +name = "p3-challenger" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0b490c745a7d2adeeafff06411814c8078c432740162332b3cd71be0158a76" +dependencies = [ + "p3-field", + "p3-maybe-rayon", + "p3-monty-31", + "p3-symmetric", + "p3-util", + "tracing", +] + +[[package]] +name = "p3-commit" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916ae7989d5c3b49f887f5c55b2f9826bdbb81aaebf834503c4145d8b267c829" +dependencies = [ + "itertools", + "p3-field", + "p3-matrix", + "p3-util", + "serde", +] + +[[package]] +name = "p3-dft" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55301e91544440254977108b85c32c09d7ea05f2f0dd61092a2825339906a4a7" +dependencies = [ + "itertools", + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-util", + "spin 0.10.0", + "tracing", +] + +[[package]] +name = "p3-field" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85affca7fc983889f260655c4cf74163eebb94605f702e4b6809ead707cba54f" +dependencies = [ + "itertools", + "num-bigint", + "p3-maybe-rayon", + "p3-util", + "paste", + "rand 0.10.0", + "serde", + "tracing", +] + +[[package]] +name = "p3-goldilocks" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca1081f5c47b940f2d75a11c04f62ea1cc58a5d480dd465fef3861c045c63cd" +dependencies = [ + "num-bigint", + "p3-challenger", + "p3-dft", + "p3-field", + "p3-mds", + "p3-poseidon1", + "p3-poseidon2", + "p3-symmetric", + "p3-util", + "paste", + "rand 0.10.0", + "serde", +] + +[[package]] +name = "p3-keccak" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcf27615ece1995e4fcf4c69740f1cf515d1481367a20b4b3ce7f4f1b8d70f7" +dependencies = [ + "p3-symmetric", + "p3-util", + "tiny-keccak", +] + +[[package]] +name = "p3-matrix" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53428126b009071563d1d07305a9de8be0d21de00b57d2475289ee32ffca6577" +dependencies = [ + "itertools", + "p3-field", + "p3-maybe-rayon", + "p3-util", + "rand 0.10.0", + "serde", + "tracing", +] + +[[package]] +name = "p3-maybe-rayon" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "082bf467011c06c768c579ec6eb9accb5e1e62108891634cc770396e917f978a" +dependencies = [ + "rayon", +] + +[[package]] +name = "p3-mds" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35209e6214102ea6ec6b8cb1b9c15a9b8e597a39f9173597c957f123bced81b3" +dependencies = [ + "p3-dft", + "p3-field", + "p3-symmetric", + "p3-util", + "rand 0.10.0", +] + +[[package]] +name = "p3-miden-lifted-air" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5c31c65fdc88952d7b301546add9670676e5b878aa0066dd929f107c203b006" +dependencies = [ + "p3-air", + "p3-field", + "p3-matrix", + "p3-util", + "thiserror", +] + +[[package]] +name = "p3-miden-lifted-fri" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab9932f1b0a16609a45cd4ee10a4d35412728bc4b38837c7979d7c85d8dcc9fc" +dependencies = [ + "p3-challenger", + "p3-commit", + "p3-dft", + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-miden-lmcs", + "p3-miden-transcript", + "p3-util", + "rand 0.10.0", + "thiserror", + "tracing", +] + +[[package]] +name = "p3-miden-lifted-stark" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3956ab7270c3cdd53ca9796d39ae1821984eb977415b0672110f9666bff5d8" +dependencies = [ + "p3-challenger", + "p3-dft", + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-miden-lifted-air", + "p3-miden-lifted-fri", + "p3-miden-lmcs", + "p3-miden-stateful-hasher", + "p3-miden-transcript", + "p3-util", + "thiserror", + "tracing", +] + +[[package]] +name = "p3-miden-lmcs" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c46791c983e772136db3d48f102431457451447abb9087deb6c8ce3c1efc86" +dependencies = [ + "p3-commit", + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-miden-stateful-hasher", + "p3-miden-transcript", + "p3-symmetric", + "p3-util", + "rand 0.10.0", + "serde", + "thiserror", + "tracing", +] + +[[package]] +name = "p3-miden-stateful-hasher" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec47a9d9615eb3d9d2a59b00d19751d9ad85384b55886827913d680d912eac6a" +dependencies = [ + "p3-field", + "p3-symmetric", +] + +[[package]] +name = "p3-miden-transcript" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c565647487e4a949f67e6f115b0391d6cb82ac8e561165789939bab23d0ae7" +dependencies = [ + "p3-challenger", + "p3-field", + "serde", + "thiserror", +] + +[[package]] +name = "p3-monty-31" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffa8c99ec50c035020bbf5457c6a729ba6a975719c1a8dd3f16421081e4f650c" +dependencies = [ + "itertools", + "num-bigint", + "p3-dft", + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-mds", + "p3-poseidon1", + "p3-poseidon2", + "p3-symmetric", + "p3-util", + "paste", + "rand 0.10.0", + "serde", + "spin 0.10.0", + "tracing", +] + +[[package]] +name = "p3-poseidon1" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a018b618e3fa0aec8be933b1d8e404edd23f46991f6bf3f5c2f3f95e9413fe9" +dependencies = [ + "p3-field", + "p3-symmetric", + "rand 0.10.0", +] + +[[package]] +name = "p3-poseidon2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "256a668a9ba916f8767552f13d0ba50d18968bc74a623bfdafa41e2970c944d0" +dependencies = [ + "p3-field", + "p3-mds", + "p3-symmetric", + "p3-util", + "rand 0.10.0", +] + +[[package]] +name = "p3-symmetric" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c60a71a1507c13611b0f2b0b6e83669fd5b76f8e3115bcbced5ccfdf3ca7807" +dependencies = [ + "itertools", + "p3-field", + "p3-util", + "serde", +] + +[[package]] +name = "p3-util" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8b766b9e9254bf3fa98d76e42cf8a5b30628c182dfd5272d270076ee12f0fc0" +dependencies = [ + "rayon", + "serde", + "transpose", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures 0.2.17", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core 0.9.5", +] + +[[package]] +name = "rand" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" +dependencies = [ + "rand_core 0.10.0", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[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" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_core" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" + +[[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 = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.28", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "strength_reduce" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" + +[[package]] +name = "strip-ansi-escapes" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a8f8038e7e7969abb3f1b7c2a811225e9296da208539e0f79c5251d6cac0025" +dependencies = [ + "vte", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "target-triple" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "591ef38edfb78ca4771ee32cf494cb8771944bee237a9b91fc9c1424ac4b777b" + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width 0.2.2", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tokio" +version = "1.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bd1c4c0fc4a7ab90fc15ef6daaa3ec3b893f004f915f2392557ed23237820cd" +dependencies = [ + "pin-project-lite", + "tokio-macros", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "transpose" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad61aed86bc3faea4300c7aee358b4c6d0c8d6ccc36524c96e4c92ccf26e77e" +dependencies = [ + "num-integer", + "strength_reduce", +] + +[[package]] +name = "trybuild" +version = "1.0.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47c635f0191bd3a2941013e5062667100969f8c4e9cd787c14f977265d73616e" +dependencies = [ + "dissimilar", + "glob", + "serde", + "serde_derive", + "serde_json", + "target-triple", + "termcolor", + "toml", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vte" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "231fdcd7ef3037e8330d8e17e61011a2c244126acc0a982f4040ac3f9f0bc077" +dependencies = [ + "memchr", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.117", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "winnow" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" + +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core 0.6.4", +] + +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/vendor/miden-prover/Cargo.toml b/vendor/miden-prover/Cargo.toml new file mode 100644 index 000000000..88ff74696 --- /dev/null +++ b/vendor/miden-prover/Cargo.toml @@ -0,0 +1,114 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2024" +rust-version = "1.90" +name = "miden-prover" +version = "0.22.1" +authors = ["Miden contributors"] +build = false +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "Miden VM prover" +homepage = "https://miden.xyz" +documentation = "https://docs.rs/miden-prover" +readme = "README.md" +keywords = [ + "miden", + "prover", + "stark", + "zkp", +] +categories = [ + "cryptography", + "emulators", + "no-std", +] +license = "MIT OR Apache-2.0" +repository = "https://github.com/0xMiden/miden-vm" + +[features] +concurrent = [ + "std", + "miden-crypto/concurrent", + "miden-processor/concurrent", +] +default = ["std"] +std = [ + "miden-air/std", + "miden-debug-types/std", + "miden-processor/std", +] + +[lib] +name = "miden_prover" +path = "src/lib.rs" + +[dependencies.bincode] +version = "1.3" + +[dependencies.miden-air] +version = "0.22.0" +default-features = false + +[dependencies.miden-core] +version = "0.22.0" +default-features = false + +[dependencies.miden-crypto] +version = "0.23" +default-features = false + +[dependencies.miden-debug-types] +version = "0.22.0" +default-features = false + +[dependencies.miden-processor] +version = "0.22.0" +default-features = false + +[dependencies.serde] +version = "1.0" +features = [ + "alloc", + "derive", + "rc", +] +default-features = false + +[dependencies.thiserror] +version = "2.0" +default-features = false + +[dependencies.tracing] +version = "0.1" +features = ["attributes"] +default-features = false + +[target.'cfg(not(target_family = "wasm"))'.dependencies.tokio] +version = "1.48" +features = [ + "rt", + "rt-multi-thread", +] +default-features = false + +[target.'cfg(target_family = "wasm")'.dependencies.tokio] +version = "1.48" +features = [ + "rt", + "macros", +] +default-features = false diff --git a/vendor/miden-prover/Cargo.toml.orig b/vendor/miden-prover/Cargo.toml.orig new file mode 100644 index 000000000..365f58dfd --- /dev/null +++ b/vendor/miden-prover/Cargo.toml.orig @@ -0,0 +1,42 @@ +[package] +name = "miden-prover" +version.workspace = true +description = "Miden VM prover" +documentation = "https://docs.rs/miden-prover" +readme = "README.md" +categories = ["cryptography", "emulators", "no-std"] +keywords = ["miden", "prover", "stark", "zkp"] +license.workspace = true +authors.workspace = true +homepage.workspace = true +repository.workspace = true +rust-version.workspace = true +edition.workspace = true + +[features] +default = ["std"] +concurrent = ["std", "miden-crypto/concurrent", "miden-processor/concurrent"] +std = ["miden-air/std", "miden-debug-types/std", "miden-processor/std"] + +[dependencies] +# Miden dependencies +miden-air.workspace = true +miden-core.workspace = true +miden-debug-types.workspace = true +miden-processor.workspace = true +miden-crypto.workspace = true + +# External dependencies +bincode.workspace = true +serde.workspace = true +thiserror.workspace = true +tracing.workspace = true + +# Platform-specific tokio dependencies +# On non-wasm targets, enable rt-multi-thread for better performance +[target.'cfg(not(target_family = "wasm"))'.dependencies] +tokio = { workspace = true, features = ["rt", "rt-multi-thread"] } + +# On wasm32, only basic tokio features are supported +[target.'cfg(target_family = "wasm")'.dependencies] +tokio = { workspace = true, features = ["rt", "macros"] } diff --git a/vendor/miden-prover/README.md b/vendor/miden-prover/README.md new file mode 100644 index 000000000..0ee7b268a --- /dev/null +++ b/vendor/miden-prover/README.md @@ -0,0 +1,69 @@ +# Miden prover +This crate contains the Miden VM prover, which proves correct execution of Miden VM. Internally, the prover uses [Miden processor](../processor/) to execute the programs, and then uses [Plonky3](https://github.com/0xMiden/Plonky3) to generate STARK proofs. + +## Usage +This crate exposes a `prove()` function which can be used to execute Miden VM programs and generate proofs of their execution. The function takes the following parameters: + +* `program: &Program` - a reference to a Miden program to be executed. +* `stack_inputs: StackInputs` - a set of public inputs with which to execute the program. +* `host: Host` - an instance of a `Host` which can be used to supply non-deterministic inputs to the VM and receive messages from the VM. +* `options: &ProvingOptions` - config parameters for proof generation. The default options target 96-bit security level. + +If the program is executed successfully, the function returns a tuple with 2 elements: + +* `outputs: StackOutputs` - the outputs generated by the program. +* `proof: ExecutionProof` - proof of program execution. `ExecutionProof` can be easily serialized and deserialized using `to_bytes()` and `from_bytes()` functions respectively. + +### Proof generation example +Here is a simple example of executing a program which pushes two numbers onto the stack and computes their sum: +```Rust +use miden_assembly::Assembler; +use miden_prover::{prove, ProvingOptions, StackInputs, DefaultHost}; + +// instantiate the assembler +let assembler = Assembler::default(); + +// this is our program, we compile it from assembly code +let program = assembler.compile("begin push.3 push.5 add end").unwrap(); + +// let's execute it and generate a STARK proof +let (outputs, proof) = prove( + &program, + StackInputs::default(), // we won't provide any stack inputs + &mut DefaultHost::default(), // we'll be using a default host + &ProvingOptions::default(), // we'll be using default options +) +.unwrap(); + +// the output should be 8 +assert_eq!(8, outputs.first().unwrap().as_canonical_u64()); +``` + +## STARK Backend + +The prover uses [Plonky3](https://github.com/0xMiden/Plonky3), a modular STARK proving framework. +STARK configurations are defined in the `miden-air` crate and shared between the prover and verifier, ensuring consistency across the system. + +### Hash Function Selection + +Different hash functions offer different tradeoffs: +- **BLAKE3/Keccak**: Fast proving but not efficient for recursion +- **RPO256/Poseidon2/RPX256**: Slower proving but efficient for recursive verification in Miden VM + +## Crate features +Miden prover can be compiled with the following features: + +* `std` - enabled by default and relies on the Rust standard library. +* `concurrent` - implies `std` and also enables multi-threaded proof generation. +* `no_std` does not rely on the Rust standard library and enables compilation to WebAssembly. + * Only the `wasm32-unknown-unknown` and `wasm32-wasip1` targets are officially supported. + +To compile with `no_std`, disable default features via `--no-default-features` flag. + +### Concurrent proof generation +When compiled with `concurrent` feature enabled, the prover will generate STARK proofs using multiple threads. For benefits of concurrent proof generation check out these [benchmarks](../README.md#Performance). + +Internally, we use [rayon](https://github.com/rayon-rs/rayon) for parallel computations. To control the number of threads used to generate a STARK proof, you can use `RAYON_NUM_THREADS` environment variable. + +## License +This project is dual-licensed under the [MIT](http://opensource.org/licenses/MIT) and [Apache 2.0](https://opensource.org/license/apache-2-0) licenses. diff --git a/vendor/miden-prover/src/lib.rs b/vendor/miden-prover/src/lib.rs new file mode 100644 index 000000000..340d1f516 --- /dev/null +++ b/vendor/miden-prover/src/lib.rs @@ -0,0 +1,326 @@ +#![no_std] + +extern crate alloc; + +#[cfg(feature = "std")] +extern crate std; + +use alloc::{string::ToString, sync::Arc, vec::Vec}; + +use ::serde::Serialize; +use miden_core::{ + Felt, + events::{EventId, EventName}, + field::QuadFelt, + mast::MastForest, + utils::{Matrix, RowMajorMatrix}, +}; +use miden_crypto::stark::{ + StarkConfig, air::VarLenPublicInputs, challenger::CanObserve, lmcs::Lmcs, proof::StarkOutput, +}; +use miden_debug_types::{Location, SourceFile, SourceSpan}; +use miden_processor::{ + FastProcessor, Program, ProcessorState, + advice::AdviceMutation, + event::EventError, + trace::{AuxTraceBuilders, ExecutionTrace, build_trace}, +}; +use tracing::instrument; + +mod proving_options; + +// EXPORTS +// ================================================================================================ +pub use miden_air::{DeserializationError, ProcessorAir, PublicInputs, config}; +pub use miden_core::proof::{ExecutionProof, HashFunction}; +pub use miden_processor::{ + ExecutionError, Host, InputError, StackInputs, StackOutputs, Word, advice::AdviceInputs, + crypto, field, serde, utils, +}; +pub use proving_options::ProvingOptions; + +// DYNAMIC HOST SUPPORT +// ================================================================================================ + +/// Object-safe host trait used by [`prove_dyn_host`] and [`prove_sync_dyn_host`]. +/// +/// This keeps async proving code monomorphic for host-related call sites while preserving the +/// existing generic [`prove`] API. +pub trait HostDyn: Send { + fn get_label_and_source_file( + &self, + location: &Location, + ) -> (SourceSpan, Option>); + + fn get_mast_forest_sync(&self, node_digest: &Word) -> Option>; + + fn on_event_sync_dyn( + &mut self, + process: &ProcessorState<'_>, + ) -> Result, EventError>; + + fn resolve_event(&self, _event_id: EventId) -> Option<&EventName> { + None + } +} + +struct HostDynAdapter<'a> { + inner: &'a mut dyn HostDyn, +} + +impl<'a> HostDynAdapter<'a> { + fn new(inner: &'a mut dyn HostDyn) -> Self { + Self { inner } + } +} + +impl Host for HostDynAdapter<'_> { + fn get_label_and_source_file( + &self, + location: &Location, + ) -> (SourceSpan, Option>) { + self.inner.get_label_and_source_file(location) + } + + fn get_mast_forest(&self, node_digest: &Word) -> impl miden_processor::FutureMaybeSend>> { + let result = self.inner.get_mast_forest_sync(node_digest); + async move { result } + } + + fn on_event( + &mut self, + process: &ProcessorState<'_>, + ) -> impl miden_processor::FutureMaybeSend, EventError>> { + let result = self.inner.on_event_sync_dyn(process); + async move { result } + } + + fn resolve_event(&self, event_id: EventId) -> Option<&EventName> { + self.inner.resolve_event(event_id) + } +} + +// PROVER +// ================================================================================================ + +/// Performs STARK proving for a fully built execution trace. +/// +/// Kept non-async and marked `inline(never)` so heavy proving codegen stays in this crate and +/// doesn't get pulled into upstream test crates through async call-site monomorphization. +#[inline(never)] +fn prove_built_trace( + trace: ExecutionTrace, + options: ProvingOptions, +) -> Result<(StackOutputs, ExecutionProof), ExecutionError> { + tracing::event!( + tracing::Level::INFO, + "Generated execution trace of {} columns and {} steps (padded from {})", + miden_air::trace::TRACE_WIDTH, + trace.trace_len_summary().padded_trace_len(), + trace.trace_len_summary().trace_len() + ); + + let stack_outputs = *trace.stack_outputs(); + let precompile_requests = trace.precompile_requests().to_vec(); + let hash_fn = options.hash_fn(); + + // Convert trace to row-major format + let trace_matrix = { + let _span = tracing::info_span!("to_row_major_matrix").entered(); + trace.to_row_major_matrix() + }; + + // Build public inputs and extract fixed/variable-length components + let (public_values, kernel_felts) = trace.public_inputs().to_air_inputs(); + let var_len_public_inputs: &[&[Felt]] = &[&kernel_felts]; + + // Get aux trace builders + let aux_builder = trace.aux_trace_builders(); + + // Generate STARK proof using lifted prover + let params = config::pcs_params(); + let proof_bytes = match hash_fn { + HashFunction::Blake3_256 => { + let config = config::blake3_256_config(params); + prove_stark(&config, &trace_matrix, &public_values, var_len_public_inputs, &aux_builder) + }, + HashFunction::Keccak => { + let config = config::keccak_config(params); + prove_stark(&config, &trace_matrix, &public_values, var_len_public_inputs, &aux_builder) + }, + HashFunction::Rpo256 => { + let config = config::rpo_config(params); + prove_stark(&config, &trace_matrix, &public_values, var_len_public_inputs, &aux_builder) + }, + HashFunction::Poseidon2 => { + let config = config::poseidon2_config(params); + prove_stark(&config, &trace_matrix, &public_values, var_len_public_inputs, &aux_builder) + }, + HashFunction::Rpx256 => { + let config = config::rpx_config(params); + prove_stark(&config, &trace_matrix, &public_values, var_len_public_inputs, &aux_builder) + }, + }?; + + let proof = ExecutionProof::new(proof_bytes, hash_fn, precompile_requests); + + Ok((stack_outputs, proof)) +} + +/// Executes and proves the specified `program` and returns the result together with a STARK-based +/// proof of the program's execution. +/// +/// This is an async function that works on all platforms including wasm32. +/// +/// - `stack_inputs` specifies the initial state of the stack for the VM. +/// - `host` specifies the host environment which contain non-deterministic (secret) inputs for the +/// prover +/// - `options` defines parameters for STARK proof generation. +/// +/// # Errors +/// Returns an error if program execution or STARK proof generation fails for any reason. +#[instrument("prove_program", skip_all)] +pub async fn prove( + program: &Program, + stack_inputs: StackInputs, + advice_inputs: AdviceInputs, + host: &mut impl Host, + options: ProvingOptions, +) -> Result<(StackOutputs, ExecutionProof), ExecutionError> { + // execute the program to create an execution trace using FastProcessor + let processor = + FastProcessor::new_with_options(stack_inputs, advice_inputs, *options.execution_options()); + + let (execution_output, trace_generation_context) = + processor.execute_for_trace(program, host).await?; + + let trace = build_trace(execution_output, trace_generation_context, program.to_info())?; + prove_built_trace(trace, options) +} + +/// A dyn-host variant of [`prove`]. +#[instrument("prove_program_dyn_host", skip_all)] +pub async fn prove_dyn_host( + program: &Program, + stack_inputs: StackInputs, + advice_inputs: AdviceInputs, + host: &mut dyn HostDyn, + options: ProvingOptions, +) -> Result<(StackOutputs, ExecutionProof), ExecutionError> { + let mut host = HostDynAdapter::new(host); + + // execute the program to create an execution trace using FastProcessor + let processor = + FastProcessor::new_with_options(stack_inputs, advice_inputs, *options.execution_options()); + + let (execution_output, trace_generation_context) = + processor.execute_for_trace(program, &mut host).await?; + + let trace = build_trace(execution_output, trace_generation_context, program.to_info())?; + prove_built_trace(trace, options) +} + +/// Synchronous wrapper for the async `prove()` function. +/// +/// This method is only available on non-wasm32 targets. On wasm32, use the +/// async `prove()` method directly since wasm32 runs in the browser's event loop. +/// +/// # Panics +/// Panics if called from within an existing Tokio runtime. Use the async `prove()` +/// method instead in async contexts. +#[cfg(not(target_family = "wasm"))] +#[instrument("prove_program_sync", skip_all)] +pub fn prove_sync( + program: &Program, + stack_inputs: StackInputs, + advice_inputs: AdviceInputs, + host: &mut impl Host, + options: ProvingOptions, +) -> Result<(StackOutputs, ExecutionProof), ExecutionError> { + match tokio::runtime::Handle::try_current() { + Ok(_handle) => { + // We're already inside a Tokio runtime - this is not supported + // because we cannot safely create a nested runtime or move the + // non-Send host reference to another thread + panic!( + "Cannot call prove_sync from within a Tokio runtime. \ + Use the async prove() method instead." + ) + }, + Err(_) => { + // No runtime exists - create one and use it + let rt = tokio::runtime::Builder::new_current_thread().build().unwrap(); + rt.block_on(prove(program, stack_inputs, advice_inputs, host, options)) + }, + } +} + +/// A dyn-host variant of [`prove_sync`]. +#[cfg(not(target_family = "wasm"))] +#[instrument("prove_program_sync_dyn_host", skip_all)] +pub fn prove_sync_dyn_host( + program: &Program, + stack_inputs: StackInputs, + advice_inputs: AdviceInputs, + host: &mut dyn HostDyn, + options: ProvingOptions, +) -> Result<(StackOutputs, ExecutionProof), ExecutionError> { + match tokio::runtime::Handle::try_current() { + Ok(_handle) => { + panic!( + "Cannot call prove_sync_dyn_host from within a Tokio runtime. \ + Use the async prove_dyn_host() method instead." + ) + }, + Err(_) => { + let rt = tokio::runtime::Builder::new_current_thread().build().unwrap(); + rt.block_on(prove_dyn_host(program, stack_inputs, advice_inputs, host, options)) + }, + } +} + +// STARK PROOF GENERATION +// ================================================================================================ + +/// Generates a STARK proof for the given trace and public values. +/// +/// Pre-seeds the challenger with `public_values`, then delegates to the lifted +/// prover. Returns the serialized proof bytes. +pub fn prove_stark( + config: &SC, + trace: &RowMajorMatrix, + public_values: &[Felt], + var_len_public_inputs: VarLenPublicInputs<'_, Felt>, + aux_builder: &AuxTraceBuilders, +) -> Result, ExecutionError> +where + SC: StarkConfig, + ::Commitment: Serialize, +{ + let log_trace_height = trace.height().ilog2() as u8; + + let mut challenger = config.challenger(); + challenger.observe_slice(public_values); + // TODO: observe log_trace_height in the transcript for Fiat-Shamir binding. + // TODO: observe var_len_public_inputs in the transcript for Fiat-Shamir binding. + // This also requires updating the recursive verifier to absorb both fixed and + // variable-length public inputs. + // TODO: observe ACE commitment once ACE verification is integrated. + // See https://github.com/0xMiden/miden-vm/issues/2822 + let output: StarkOutput = miden_crypto::stark::prover::prove_single( + config, + &ProcessorAir, + trace, + public_values, + var_len_public_inputs, + aux_builder, + challenger, + ) + .map_err(|e| ExecutionError::ProvingError(e.to_string()))?; + // Proof serialization via bincode; see https://github.com/0xMiden/miden-vm/issues/2550 + // We serialize `(log_trace_height, proof)` as a tuple; this is a temporary approach until + // the lifted STARK integrates trace height on its side. + let proof_bytes = bincode::serialize(&(log_trace_height, &output.proof)) + .map_err(|e| ExecutionError::ProvingError(e.to_string()))?; + Ok(proof_bytes) +} diff --git a/vendor/miden-prover/src/proving_options.rs b/vendor/miden-prover/src/proving_options.rs new file mode 100644 index 000000000..ba77c82e7 --- /dev/null +++ b/vendor/miden-prover/src/proving_options.rs @@ -0,0 +1,68 @@ +use miden_core::proof::HashFunction; +use miden_processor::ExecutionOptions; + +// PROVING OPTIONS +// ================================================================================================ + +/// A set of parameters specifying how Miden VM execution proofs are to be generated. +/// +/// This struct combines execution options (VM parameters) with the hash function to use +/// for proof generation. The actual STARK proving parameters (FRI config, security level, etc.) +/// are determined by the hash function and hardcoded in the prover's config module. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct ProvingOptions { + exec_options: ExecutionOptions, + hash_fn: HashFunction, +} + +impl ProvingOptions { + // CONSTRUCTORS + // -------------------------------------------------------------------------------------------- + + /// Creates a new instance of [ProvingOptions] with the specified hash function. + /// + /// The STARK proving parameters (security level, FRI config, etc.) are determined + /// by the hash function and hardcoded in the prover's config module. + pub fn new(hash_fn: HashFunction) -> Self { + Self { + exec_options: ExecutionOptions::default(), + hash_fn, + } + } + + /// Creates a new instance of [ProvingOptions] targeting 96-bit security level. + /// + /// Note: The actual security parameters are hardcoded in the prover's config module. + /// This is a convenience constructor that is equivalent to `new(hash_fn)`. + pub fn with_96_bit_security(hash_fn: HashFunction) -> Self { + Self::new(hash_fn) + } + + /// Sets [ExecutionOptions] for this [ProvingOptions]. + /// + /// This sets the maximum number of cycles a program is allowed to execute as well as + /// the number of cycles the program is expected to execute. + pub fn with_execution_options(mut self, exec_options: ExecutionOptions) -> Self { + self.exec_options = exec_options; + self + } + + // PUBLIC ACCESSORS + // -------------------------------------------------------------------------------------------- + + /// Returns the hash function to be used in STARK proof generation. + pub const fn hash_fn(&self) -> HashFunction { + self.hash_fn + } + + /// Returns the execution options specified for this [ProvingOptions] + pub const fn execution_options(&self) -> &ExecutionOptions { + &self.exec_options + } +} + +impl Default for ProvingOptions { + fn default() -> Self { + Self::new(HashFunction::Blake3_256) + } +} diff --git a/vendor/miden-tx/.cargo-ok b/vendor/miden-tx/.cargo-ok new file mode 100644 index 000000000..5f8b79583 --- /dev/null +++ b/vendor/miden-tx/.cargo-ok @@ -0,0 +1 @@ +{"v":1} \ No newline at end of file diff --git a/vendor/miden-tx/.cargo_vcs_info.json b/vendor/miden-tx/.cargo_vcs_info.json new file mode 100644 index 000000000..26e71916c --- /dev/null +++ b/vendor/miden-tx/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "7f1077f6a5e448c431d692d71f9da3fed51c80ef" + }, + "path_in_vcs": "crates/miden-tx" +} \ No newline at end of file diff --git a/vendor/miden-tx/Cargo.lock b/vendor/miden-tx/Cargo.lock new file mode 100644 index 000000000..a8abcec7b --- /dev/null +++ b/vendor/miden-tx/Cargo.lock @@ -0,0 +1,3044 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[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_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bech32" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f" + +[[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 = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "blake3" +version = "1.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d2d5991425dfd0785aed03aedcf0b321d61975c9b5b3689c774a2610ae0b51e" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "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 = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "cc" +version = "1.2.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures 0.2.17", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version 0.4.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version 0.4.1", + "syn 2.0.117", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dissimilar" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aeda16ab4059c5fd2a83f2b9c9e9c981327b18aa8e3b313f7e6563799d4f093e" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pkcs8", + "rand_core 0.6.4", + "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 = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e90c2accc4b07a8456ea0debdc2e7587bdd890680d71173a15d4ae604f6eef" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0621c04f2196ac3f488dd583365b9c09be011a4ab8b9f37248ffcc8f6198b56a" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased-serde" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "spin 0.9.8", +] + +[[package]] +name = "fs-err" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fde052dbfc920003cfd2c8e2c6e6d4cc7c1091538c3a24226cec0665ab08c0" +dependencies = [ + "autocfg", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generator" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f04ae4152da20c76fe800fa48659201d5cf627c5149ca0b707b69d7eef6cf9" +dependencies = [ + "cc", + "cfg-if", + "libc", + "log", + "rustversion", + "windows-link", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "indenter" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" + +[[package]] +name = "indexmap" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jiff" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", +] + +[[package]] +name = "jiff-static" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "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", +] + +[[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", + "regex", + "regex-syntax", + "sha3", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.184" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "miden-air" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d15646ebc95906b2a7cb66711d1e184f53fd6edc2605730bbcf0c2a129f792cf" +dependencies = [ + "miden-core", + "miden-crypto", + "miden-utils-indexing", + "thiserror", + "tracing", +] + +[[package]] +name = "miden-assembly" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae6013b3a390e0dcb29242f4480a7727965887bbf0903466c88f362b4cb20c0e" +dependencies = [ + "env_logger", + "log", + "miden-assembly-syntax", + "miden-core", + "miden-mast-package", + "miden-package-registry", + "miden-project", + "smallvec", + "thiserror", +] + +[[package]] +name = "miden-assembly-syntax" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "996156b8f7c5fe6be17dea71089c6d7985c2dec1e3a4fec068b1dfc690e25df5" +dependencies = [ + "aho-corasick", + "env_logger", + "lalrpop", + "lalrpop-util", + "log", + "miden-core", + "miden-debug-types", + "miden-utils-diagnostics", + "midenc-hir-type", + "proptest", + "proptest-derive", + "regex", + "rustc_version 0.4.1", + "semver 1.0.28", + "serde", + "smallvec", + "thiserror", +] + +[[package]] +name = "miden-core" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdec54a321cdf3d23e9ef615e91cb858038c6b4d4202507bdec048fc6d7763e4" +dependencies = [ + "derive_more", + "itertools", + "miden-crypto", + "miden-debug-types", + "miden-formatting", + "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.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "621e8fa911a790bcf3cd3aedce80bc10922a19d6181f08ff3ca078f955cff70b" +dependencies = [ + "env_logger", + "fs-err", + "miden-assembly", + "miden-core", + "miden-crypto", + "miden-package-registry", + "miden-processor", + "miden-utils-sync", + "thiserror", +] + +[[package]] +name = "miden-crypto" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0a034a460e27723dcfdf25effffab84331c3b46b13e7a1bd674197cc71bfe" +dependencies = [ + "blake3", + "cc", + "chacha20poly1305", + "curve25519-dalek", + "ed25519-dalek", + "flume", + "glob", + "hkdf", + "k256", + "miden-crypto-derive", + "miden-field", + "miden-serde-utils", + "num", + "num-complex", + "p3-blake3", + "p3-challenger", + "p3-dft", + "p3-goldilocks", + "p3-keccak", + "p3-matrix", + "p3-maybe-rayon", + "p3-miden-lifted-stark", + "p3-symmetric", + "p3-util", + "rand 0.9.2", + "rand_chacha", + "rand_core 0.9.5", + "rand_hc", + "rayon", + "serde", + "sha2", + "sha3", + "subtle", + "thiserror", + "x25519-dalek", +] + +[[package]] +name = "miden-crypto-derive" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8bf6ebde028e79bcc61a3632d2f375a5cc64caa17d014459f75015238cb1e08" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "miden-debug-types" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6e50274d11c80b901cf6c90362de8c98c8c8ad6030c80624d683b63d899a0fb" +dependencies = [ + "memchr", + "miden-crypto", + "miden-formatting", + "miden-miette", + "miden-utils-indexing", + "miden-utils-sync", + "paste", + "serde", + "serde_spanned", + "thiserror", +] + +[[package]] +name = "miden-field" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38011348f4fb4c9e5ce1f471203d024721c00e3b60a91aa91aaefe6738d8b5ea" +dependencies = [ + "miden-serde-utils", + "num-bigint", + "p3-challenger", + "p3-field", + "p3-goldilocks", + "paste", + "rand 0.10.0", + "serde", + "subtle", + "thiserror", +] + +[[package]] +name = "miden-formatting" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e392e0a8c34b32671012b439de35fa8987bf14f0f8aac279b97f8b8cc6e263b" +dependencies = [ + "unicode-width 0.1.14", +] + +[[package]] +name = "miden-mast-package" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc8b2e3447fcde1f0e6b76e5219f129517639772cb02ca543177f0584e315288" +dependencies = [ + "derive_more", + "miden-assembly-syntax", + "miden-core", + "miden-debug-types", + "serde", + "thiserror", +] + +[[package]] +name = "miden-miette" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eef536978f24a179d94fa2a41e4f92b28e7d8aab14b8d23df28ad2a3d7098b20" +dependencies = [ + "cfg-if", + "futures", + "indenter", + "lazy_static", + "miden-miette-derive", + "owo-colors", + "regex", + "rustc_version 0.2.3", + "rustversion", + "serde_json", + "spin 0.9.8", + "strip-ansi-escapes", + "syn 2.0.117", + "textwrap", + "thiserror", + "trybuild", + "unicode-width 0.1.14", +] + +[[package]] +name = "miden-miette-derive" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86a905f3ea65634dd4d1041a4f0fd0a3e77aa4118341d265af1a94339182222f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "miden-package-registry" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "969ba3942052e52b3968e34dbd1c52c707e75777ee42ebdae2c8f57af56cf6cf" +dependencies = [ + "miden-assembly-syntax", + "miden-core", + "miden-mast-package", + "pubgrub", + "serde", + "smallvec", + "thiserror", +] + +[[package]] +name = "miden-processor" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ec6cecbf22bd92b73a931ee80b424e46b8b7cdf4f2f3c364c25c5c15d2840da" +dependencies = [ + "itertools", + "miden-air", + "miden-core", + "miden-debug-types", + "miden-utils-diagnostics", + "miden-utils-indexing", + "paste", + "rayon", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "miden-project" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3840520c01881534fbbceb6b3687ec1c407fbaf310a35ce415fd3510abc52fdb" +dependencies = [ + "miden-assembly-syntax", + "miden-core", + "miden-mast-package", + "miden-package-registry", + "serde", + "serde-untagged", + "thiserror", + "toml", +] + +[[package]] +name = "miden-protocol" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e860cc978d3467297de076e9bd22f0573b82ef73a3d223d6bb957731a45b8164" +dependencies = [ + "bech32", + "fs-err", + "getrandom 0.3.4", + "miden-assembly", + "miden-assembly-syntax", + "miden-core", + "miden-core-lib", + "miden-crypto", + "miden-mast-package", + "miden-processor", + "miden-protocol-macros", + "miden-utils-sync", + "miden-verifier", + "rand 0.9.2", + "rand_chacha", + "rand_xoshiro", + "regex", + "semver 1.0.28", + "serde", + "thiserror", + "toml", + "walkdir", +] + +[[package]] +name = "miden-protocol-macros" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4daec4a5a6f050a670a8639e78e017ab11ef0bf2e253b012505f25e6247c13e7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "miden-prover" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb2c94e36f57684d7fa0cd382adeedc1728d502dbbe69ad1c12f4a931f45511" +dependencies = [ + "bincode", + "miden-air", + "miden-core", + "miden-crypto", + "miden-debug-types", + "miden-processor", + "serde", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "miden-serde-utils" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff78082e9b4ca89863e68da01b35f8a4029ee6fd912e39fa41fde4273a7debab" +dependencies = [ + "p3-field", + "p3-goldilocks", +] + +[[package]] +name = "miden-standards" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f455a087f41c30636b45ead961d1e66114d2d20661887b307cede05307eeb942" +dependencies = [ + "fs-err", + "miden-assembly", + "miden-core", + "miden-core-lib", + "miden-processor", + "miden-protocol", + "rand 0.9.2", + "regex", + "thiserror", + "walkdir", +] + +[[package]] +name = "miden-tx" +version = "0.14.4" +dependencies = [ + "anyhow", + "assert_matches", + "miden-assembly", + "miden-processor", + "miden-protocol", + "miden-prover", + "miden-standards", + "miden-verifier", + "rstest", + "thiserror", +] + +[[package]] +name = "miden-utils-core-derive" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3846c8674ccec0c37005f99c1a599a24790ba2a5e5f4e1c7aec5f456821df835" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "miden-utils-diagnostics" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397f5d1e8679cf17cf7713ffd9654840791a6ed5818b025bbc2fbfdce846579a" +dependencies = [ + "miden-crypto", + "miden-debug-types", + "miden-miette", + "paste", + "tracing", +] + +[[package]] +name = "miden-utils-indexing" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8834e76299686bcce3de1685158aa4cff49b7fa5e0e00a6cc811e8f2cf5775f" +dependencies = [ + "miden-crypto", + "serde", + "thiserror", +] + +[[package]] +name = "miden-utils-sync" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a9e9747e9664c1a0997bb040ae291306ea0a1c74a572141ec66cec855c1b0e8" +dependencies = [ + "lock_api", + "loom", + "once_cell", + "parking_lot", +] + +[[package]] +name = "miden-verifier" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4580df640d889c9f3c349cd2268968e44a99a8cf0df6c36ae5b1fb273712b00" +dependencies = [ + "bincode", + "miden-air", + "miden-core", + "miden-crypto", + "serde", + "thiserror", + "tracing", +] + +[[package]] +name = "midenc-hir-type" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb29d7c049fb69373c7e775e3d4411e63e4ee608bc43826282ba62c6ec9f891" +dependencies = [ + "miden-formatting", + "miden-serde-utils", + "serde", + "serde_repr", + "smallvec", + "thiserror", +] + +[[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 = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +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.117", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +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 = "owo-colors" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" + +[[package]] +name = "p3-air" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f2ec9cbfc642fc5173817287c3f8b789d07743b5f7e812d058b7a03e344f9ab" +dependencies = [ + "p3-field", + "p3-matrix", + "tracing", +] + +[[package]] +name = "p3-blake3" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b667f43b19499dd939c9e2553aa95688936a88360d50117dae3c8848d07dbc70" +dependencies = [ + "blake3", + "p3-symmetric", + "p3-util", +] + +[[package]] +name = "p3-challenger" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0b490c745a7d2adeeafff06411814c8078c432740162332b3cd71be0158a76" +dependencies = [ + "p3-field", + "p3-maybe-rayon", + "p3-monty-31", + "p3-symmetric", + "p3-util", + "tracing", +] + +[[package]] +name = "p3-commit" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916ae7989d5c3b49f887f5c55b2f9826bdbb81aaebf834503c4145d8b267c829" +dependencies = [ + "itertools", + "p3-field", + "p3-matrix", + "p3-util", + "serde", +] + +[[package]] +name = "p3-dft" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55301e91544440254977108b85c32c09d7ea05f2f0dd61092a2825339906a4a7" +dependencies = [ + "itertools", + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-util", + "spin 0.10.0", + "tracing", +] + +[[package]] +name = "p3-field" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85affca7fc983889f260655c4cf74163eebb94605f702e4b6809ead707cba54f" +dependencies = [ + "itertools", + "num-bigint", + "p3-maybe-rayon", + "p3-util", + "paste", + "rand 0.10.0", + "serde", + "tracing", +] + +[[package]] +name = "p3-goldilocks" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca1081f5c47b940f2d75a11c04f62ea1cc58a5d480dd465fef3861c045c63cd" +dependencies = [ + "num-bigint", + "p3-challenger", + "p3-dft", + "p3-field", + "p3-mds", + "p3-poseidon1", + "p3-poseidon2", + "p3-symmetric", + "p3-util", + "paste", + "rand 0.10.0", + "serde", +] + +[[package]] +name = "p3-keccak" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcf27615ece1995e4fcf4c69740f1cf515d1481367a20b4b3ce7f4f1b8d70f7" +dependencies = [ + "p3-symmetric", + "p3-util", + "tiny-keccak", +] + +[[package]] +name = "p3-matrix" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53428126b009071563d1d07305a9de8be0d21de00b57d2475289ee32ffca6577" +dependencies = [ + "itertools", + "p3-field", + "p3-maybe-rayon", + "p3-util", + "rand 0.10.0", + "serde", + "tracing", +] + +[[package]] +name = "p3-maybe-rayon" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "082bf467011c06c768c579ec6eb9accb5e1e62108891634cc770396e917f978a" +dependencies = [ + "rayon", +] + +[[package]] +name = "p3-mds" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35209e6214102ea6ec6b8cb1b9c15a9b8e597a39f9173597c957f123bced81b3" +dependencies = [ + "p3-dft", + "p3-field", + "p3-symmetric", + "p3-util", + "rand 0.10.0", +] + +[[package]] +name = "p3-miden-lifted-air" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5c31c65fdc88952d7b301546add9670676e5b878aa0066dd929f107c203b006" +dependencies = [ + "p3-air", + "p3-field", + "p3-matrix", + "p3-util", + "thiserror", +] + +[[package]] +name = "p3-miden-lifted-fri" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab9932f1b0a16609a45cd4ee10a4d35412728bc4b38837c7979d7c85d8dcc9fc" +dependencies = [ + "p3-challenger", + "p3-commit", + "p3-dft", + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-miden-lmcs", + "p3-miden-transcript", + "p3-util", + "rand 0.10.0", + "thiserror", + "tracing", +] + +[[package]] +name = "p3-miden-lifted-stark" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3956ab7270c3cdd53ca9796d39ae1821984eb977415b0672110f9666bff5d8" +dependencies = [ + "p3-challenger", + "p3-dft", + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-miden-lifted-air", + "p3-miden-lifted-fri", + "p3-miden-lmcs", + "p3-miden-stateful-hasher", + "p3-miden-transcript", + "p3-util", + "thiserror", + "tracing", +] + +[[package]] +name = "p3-miden-lmcs" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c46791c983e772136db3d48f102431457451447abb9087deb6c8ce3c1efc86" +dependencies = [ + "p3-commit", + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-miden-stateful-hasher", + "p3-miden-transcript", + "p3-symmetric", + "p3-util", + "rand 0.10.0", + "serde", + "thiserror", + "tracing", +] + +[[package]] +name = "p3-miden-stateful-hasher" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec47a9d9615eb3d9d2a59b00d19751d9ad85384b55886827913d680d912eac6a" +dependencies = [ + "p3-field", + "p3-symmetric", +] + +[[package]] +name = "p3-miden-transcript" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c565647487e4a949f67e6f115b0391d6cb82ac8e561165789939bab23d0ae7" +dependencies = [ + "p3-challenger", + "p3-field", + "serde", + "thiserror", +] + +[[package]] +name = "p3-monty-31" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffa8c99ec50c035020bbf5457c6a729ba6a975719c1a8dd3f16421081e4f650c" +dependencies = [ + "itertools", + "num-bigint", + "p3-dft", + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-mds", + "p3-poseidon1", + "p3-poseidon2", + "p3-symmetric", + "p3-util", + "paste", + "rand 0.10.0", + "serde", + "spin 0.10.0", + "tracing", +] + +[[package]] +name = "p3-poseidon1" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a018b618e3fa0aec8be933b1d8e404edd23f46991f6bf3f5c2f3f95e9413fe9" +dependencies = [ + "p3-field", + "p3-symmetric", + "rand 0.10.0", +] + +[[package]] +name = "p3-poseidon2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "256a668a9ba916f8767552f13d0ba50d18968bc74a623bfdafa41e2970c944d0" +dependencies = [ + "p3-field", + "p3-mds", + "p3-symmetric", + "p3-util", + "rand 0.10.0", +] + +[[package]] +name = "p3-symmetric" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c60a71a1507c13611b0f2b0b6e83669fd5b76f8e3115bcbced5ccfdf3ca7807" +dependencies = [ + "itertools", + "p3-field", + "p3-util", + "serde", +] + +[[package]] +name = "p3-util" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8b766b9e9254bf3fa98d76e42cf8a5b30628c182dfd5272d270076ee12f0fc0" +dependencies = [ + "rayon", + "serde", + "transpose", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "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-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures 0.2.17", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "091397be61a01d4be58e7841595bd4bfedb15f1cd54977d79b8271e94ed799a3" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "priority-queue" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93980406f12d9f8140ed5abe7155acb10bb1e69ea55c88960b9c2f117445ef96" +dependencies = [ + "equivalent", + "indexmap", + "serde", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" +dependencies = [ + "bitflags", + "num-traits", + "rand 0.9.2", + "rand_chacha", + "rand_xorshift", + "regex-syntax", + "unarray", +] + +[[package]] +name = "proptest-derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb6dc647500e84a25a85b100e76c85b8ace114c209432dc174f20aac11d4ed6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "pubgrub" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f5df7e552bc7edd075f5783a87fbfc21d6a546e32c16985679c488c18192d83" +dependencies = [ + "indexmap", + "log", + "priority-queue", + "rustc-hash", + "thiserror", + "version-ranges", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core 0.9.5", +] + +[[package]] +name = "rand" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" +dependencies = [ + "rand_core 0.10.0", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[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" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_core" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.5", +] + +[[package]] +name = "rand_xoshiro" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f703f4665700daf5512dcca5f43afa6af89f09db47fb56be587f80636bda2d41" +dependencies = [ + "rand_core 0.9.5", +] + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "relative-path" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rstest" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49" +dependencies = [ + "futures-timer", + "futures-util", + "rstest_macros", +] + +[[package]] +name = "rstest_macros" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0" +dependencies = [ + "cfg-if", + "glob", + "proc-macro-crate", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version 0.4.1", + "syn 2.0.117", + "unicode-ident", +] + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.28", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-untagged" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" +dependencies = [ + "erased-serde", + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "siphasher" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +dependencies = [ + "serde", +] + +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "strength_reduce" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a8f8038e7e7969abb3f1b7c2a811225e9296da208539e0f79c5251d6cac0025" +dependencies = [ + "vte", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "target-triple" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "591ef38edfb78ca4771ee32cf494cb8771944bee237a9b91fc9c1424ac4b777b" + +[[package]] +name = "term" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width 0.2.2", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tokio" +version = "1.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bd1c4c0fc4a7ab90fc15ef6daaa3ec3b893f004f915f2392557ed23237820cd" +dependencies = [ + "pin-project-lite", + "tokio-macros", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.25.10+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82418ca169e235e6c399a84e395ab6debeb3bc90edc959bf0f48647c6a32d1b" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "transpose" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad61aed86bc3faea4300c7aee358b4c6d0c8d6ccc36524c96e4c92ccf26e77e" +dependencies = [ + "num-integer", + "strength_reduce", +] + +[[package]] +name = "trybuild" +version = "1.0.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47c635f0191bd3a2941013e5062667100969f8c4e9cd787c14f977265d73616e" +dependencies = [ + "dissimilar", + "glob", + "serde", + "serde_derive", + "serde_json", + "target-triple", + "termcolor", + "toml", +] + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "version-ranges" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3595ffe225639f1e0fd8d7269dcc05d2fbfea93cfac2fea367daf1adb60aae91" +dependencies = [ + "smallvec", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vte" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "231fdcd7ef3037e8330d8e17e61011a2c244126acc0a982f4040ac3f9f0bc077" +dependencies = [ + "memchr", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.117", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "winnow" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" + +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core 0.6.4", +] + +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/vendor/miden-tx/Cargo.toml b/vendor/miden-tx/Cargo.toml new file mode 100644 index 000000000..951566a6f --- /dev/null +++ b/vendor/miden-tx/Cargo.toml @@ -0,0 +1,101 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2024" +rust-version = "1.90" +name = "miden-tx" +version = "0.14.4" +authors = ["Miden contributors"] +build = false +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "Miden blockchain transaction executor and prover" +homepage = "https://miden.xyz" +readme = "README.md" +keywords = [ + "miden", + "transaction", +] +categories = ["no-std"] +license = "MIT" +repository = "https://github.com/0xMiden/protocol" + +[features] +concurrent = [ + "miden-prover/concurrent", + "std", +] +default = ["std"] +std = [ + "miden-processor/std", + "miden-protocol/std", + "miden-prover/std", + "miden-standards/std", + "miden-verifier/std", +] +testing = [ + "miden-processor/testing", + "miden-protocol/testing", + "miden-standards/testing", +] + +[lib] +name = "miden_tx" +path = "src/lib.rs" + +[dependencies.miden-processor] +version = "0.22" +default-features = false + +[dependencies.miden-protocol] +version = "0.14" +default-features = false + +[dependencies.miden-prover] +version = "0.22" +default-features = false + +[dependencies.miden-standards] +version = "0.14" +default-features = false + +[dependencies.miden-verifier] +version = "0.22" +default-features = false + +[dependencies.thiserror] +version = "2.0" +default-features = false + +[dev-dependencies.anyhow] +version = "1.0" +features = [ + "backtrace", + "std", + "backtrace", + "std", +] +default-features = false + +[dev-dependencies.assert_matches] +version = "1.5" +default-features = false + +[dev-dependencies.miden-assembly] +version = "0.22" +default-features = false + +[dev-dependencies.rstest] +version = "0.26" diff --git a/vendor/miden-tx/Cargo.toml.orig b/vendor/miden-tx/Cargo.toml.orig new file mode 100644 index 000000000..e78138760 --- /dev/null +++ b/vendor/miden-tx/Cargo.toml.orig @@ -0,0 +1,39 @@ +[package] +authors.workspace = true +categories = ["no-std"] +description = "Miden blockchain transaction executor and prover" +edition.workspace = true +homepage.workspace = true +keywords = ["miden", "transaction"] +license.workspace = true +name = "miden-tx" +readme = "README.md" +repository.workspace = true +rust-version.workspace = true +version.workspace = true + +[features] +concurrent = ["miden-prover/concurrent", "std"] +default = ["std"] +std = ["miden-processor/std", "miden-protocol/std", "miden-prover/std", "miden-standards/std", "miden-verifier/std"] +testing = ["miden-processor/testing", "miden-protocol/testing", "miden-standards/testing"] + +[dependencies] +# Workspace dependencies +miden-protocol = { workspace = true } +miden-standards = { workspace = true } + +# Miden dependencies +miden-processor = { workspace = true } +miden-prover = { workspace = true } +miden-verifier = { workspace = true } + +# External dependencies +thiserror = { workspace = true } + +[dev-dependencies] +anyhow = { features = ["backtrace", "std"], workspace = true } +assert_matches = { workspace = true } +miden-assembly = { workspace = true } +miden-tx = { features = ["testing"], path = "." } +rstest = { workspace = true } diff --git a/vendor/miden-tx/README.md b/vendor/miden-tx/README.md new file mode 100644 index 000000000..52edc143a --- /dev/null +++ b/vendor/miden-tx/README.md @@ -0,0 +1,45 @@ +# Miden Transaction + +This crate contains tool for creating, executing, and proving Miden blockchain transaction. + +## Usage + +This crate exposes a few components to compile, run, and prove transactions. + +The first requirement is to have a `DataStore` implementation. `DataStore` objects are responsible to load the data needed by the transactions executor, especially the account's code, the reference block data, and the note's storage. + +```rust +let store = DataStore:new(); +``` + +Once a store is available, a `TransactionExecutor` object can be used to execute a transaction. Consuming a zero or more notes, and possibly calling some of the account's code. + +```rust +let executor = TransactionExecutor::new(store); +let executed_transaction = executor.execute_transaction(account_id, block_ref, notes, tx_args); +``` + +With the transaction execution done, it is then possible to create a proof: + +```rust +let prover = LocalTransactionProver::new(ProvingOptions::default()); +let proven_transaction = prover.prove(executed_transaction); +``` + +And to verify a proof: + +```rust +let verifier = TransactionVerifier::new(SECURITY_LEVEL); +verifier.verify(proven_transaction); +``` + +## Features + +| Features | Description | +| ------------ | --------------------------------------------------------------------------------------------- | +| `std` | Enable usage of Rust's `std`, use `--no-default-features` for `no-std` support. | +| `concurrent` | Enables concurrent code to speed up runtime execution. | + +## License + +This project is [MIT licensed](../../LICENSE). diff --git a/vendor/miden-tx/src/auth/mod.rs b/vendor/miden-tx/src/auth/mod.rs new file mode 100644 index 000000000..7dd2be0a4 --- /dev/null +++ b/vendor/miden-tx/src/auth/mod.rs @@ -0,0 +1,7 @@ +mod tx_authenticator; +pub use tx_authenticator::{ + BasicAuthenticator, + SigningInputs, + TransactionAuthenticator, + UnreachableAuth, +}; diff --git a/vendor/miden-tx/src/auth/tx_authenticator.rs b/vendor/miden-tx/src/auth/tx_authenticator.rs new file mode 100644 index 000000000..877d29aa6 --- /dev/null +++ b/vendor/miden-tx/src/auth/tx_authenticator.rs @@ -0,0 +1,345 @@ +use alloc::boxed::Box; +use alloc::collections::BTreeMap; +use alloc::string::ToString; +use alloc::sync::Arc; +use alloc::vec::Vec; + +use miden_processor::FutureMaybeSend; +use miden_protocol::account::auth::{AuthSecretKey, PublicKey, PublicKeyCommitment, Signature}; +use miden_protocol::crypto::SequentialCommit; +use miden_protocol::transaction::TransactionSummary; +use miden_protocol::{Felt, Hasher, Word}; + +use crate::errors::AuthenticationError; +use crate::utils::serde::{ + ByteReader, + ByteWriter, + Deserializable, + DeserializationError, + Serializable, +}; + +// SIGNATURE DATA +// ================================================================================================ + +/// Data types on which a signature can be requested. +/// +/// It supports three modes: +/// - `TransactionSummary`: Structured transaction summary, recommended for authenticating +/// transactions. +/// - `Arbitrary`: Arbitrary payload provided by the application. It is up to the authenticator to +/// display it appropriately. +/// - `Blind`: The underlying data is not meant to be displayed in a human-readable format. It must +/// be a cryptographic commitment to some data. +#[derive(Debug, Clone)] +pub enum SigningInputs { + TransactionSummary(Box), + Arbitrary(Vec), + Blind(Word), +} + +impl SequentialCommit for SigningInputs { + type Commitment = Word; + + fn to_elements(&self) -> Vec { + match self { + SigningInputs::TransactionSummary(tx_summary) => tx_summary.as_ref().to_elements(), + SigningInputs::Arbitrary(elements) => elements.clone(), + SigningInputs::Blind(word) => word.as_elements().to_vec(), + } + } + + fn to_commitment(&self) -> Self::Commitment { + match self { + // `TransactionSummary` knows how to derive a commitment to itself. + SigningInputs::TransactionSummary(tx_summary) => tx_summary.as_ref().to_commitment(), + // use the default implementation. + SigningInputs::Arbitrary(elements) => Hasher::hash_elements(elements), + // `Blind` is assumed to already be a commitment. + SigningInputs::Blind(word) => *word, + } + } +} + +/// Convenience methods for [SigningInputs]. +impl SigningInputs { + /// Computes the commitment to [SigningInputs]. + pub fn to_commitment(&self) -> Word { + ::to_commitment(self) + } + + /// Returns a representation of the [SigningInputs] as a sequence of field elements. + pub fn to_elements(&self) -> Vec { + ::to_elements(self) + } +} + +// SERIALIZATION +// ================================================================================================ + +impl Serializable for SigningInputs { + fn write_into(&self, target: &mut W) { + match self { + SigningInputs::TransactionSummary(tx_summary) => { + target.write_u8(0); + tx_summary.as_ref().write_into(target); + }, + SigningInputs::Arbitrary(elements) => { + target.write_u8(1); + elements.write_into(target); + }, + SigningInputs::Blind(word) => { + target.write_u8(2); + word.write_into(target); + }, + } + } +} + +impl Deserializable for SigningInputs { + fn read_from(source: &mut R) -> Result { + let discriminant = source.read_u8()?; + match discriminant { + 0 => { + let tx_summary: TransactionSummary = source.read()?; + Ok(SigningInputs::TransactionSummary(Box::new(tx_summary))) + }, + 1 => { + let elements: Vec = source.read()?; + Ok(SigningInputs::Arbitrary(elements)) + }, + 2 => { + let word: Word = source.read()?; + Ok(SigningInputs::Blind(word)) + }, + other => Err(DeserializationError::InvalidValue(format!( + "invalid SigningInputs variant: {other}" + ))), + } + } +} + +// TRANSACTION AUTHENTICATOR +// ================================================================================================ + +/// Defines an authenticator for transactions. +/// +/// The main purpose of the authenticator is to generate signatures for a given message against +/// a key managed by the authenticator. That is, the authenticator maintains a set of public- +/// private key pairs, and can be requested to generate signatures against any of the managed keys. +/// +/// The public keys are defined by [PublicKeyCommitment]'s which are the hashes of the actual +/// public keys. +pub trait TransactionAuthenticator { + /// Retrieves a signature for a specific message as a list of [Felt]. + /// + /// The request is initiated by the VM as a consequence of the SigToStack advice + /// injector. + /// + /// - `pub_key_commitment`: the hash of the public key used for signature generation. + /// - `signing_inputs`: description of the message to be singed. The inputs could contain + /// arbitrary data or a [TransactionSummary] which would describe the changes made to the + /// account up to the point of calling `get_signature()`. This allows the authenticator to + /// review any alterations to the account prior to signing. It should not be directly used in + /// the signature computation. + fn get_signature( + &self, + pub_key_commitment: PublicKeyCommitment, + signing_inputs: &SigningInputs, + ) -> impl FutureMaybeSend>; + + /// Retrieves a public key for a specific public key commitment. + fn get_public_key( + &self, + pub_key_commitment: PublicKeyCommitment, + ) -> impl FutureMaybeSend>>; +} + +/// A placeholder type for the generic trait bound of `TransactionAuthenticator<'_,'_,_,T>` +/// when we do not want to provide one, but must provide the `T` in `Option`. +/// +/// Note: Asserts when `get_signature` is called. +#[derive(Debug, Clone, Copy)] +pub struct UnreachableAuth { + // ensure the type cannot be instantiated + _protect: core::marker::PhantomData, +} + +impl TransactionAuthenticator for UnreachableAuth { + #[allow(clippy::manual_async_fn)] + fn get_signature( + &self, + _pub_key_commitment: PublicKeyCommitment, + _signing_inputs: &SigningInputs, + ) -> impl FutureMaybeSend> { + async { unreachable!("Type `UnreachableAuth` must not be instantiated") } + } + + fn get_public_key( + &self, + _pub_key_commitment: PublicKeyCommitment, + ) -> impl FutureMaybeSend>> { + async { unreachable!("Type `UnreachableAuth` must not be instantiated") } + } +} + +// BASIC AUTHENTICATOR +// ================================================================================================ + +/// Represents a signer for [AuthSecretKey] keys. +#[derive(Clone, Debug)] +pub struct BasicAuthenticator { + /// pub_key |-> (secret_key, public_key) mapping + keys: BTreeMap)>, +} + +impl BasicAuthenticator { + pub fn new(keys: &[AuthSecretKey]) -> Self { + let mut key_map = BTreeMap::new(); + for secret_key in keys { + let pub_key = secret_key.public_key(); + key_map.insert(pub_key.to_commitment(), (secret_key.clone(), pub_key.into())); + } + + BasicAuthenticator { keys: key_map } + } + + pub fn from_key_pairs(keys: &[(AuthSecretKey, PublicKey)]) -> Self { + let mut key_map = BTreeMap::new(); + for (secret_key, public_key) in keys { + key_map.insert( + public_key.to_commitment(), + (secret_key.clone(), public_key.clone().into()), + ); + } + + BasicAuthenticator { keys: key_map } + } + + /// Returns a reference to the keys map. + /// + /// Map keys represent the public key commitments, and values represent the (secret_key, + /// public_key) pair that the authenticator would use to sign messages. + pub fn keys(&self) -> &BTreeMap)> { + &self.keys + } +} + +impl TransactionAuthenticator for BasicAuthenticator { + /// Gets a signature over a message, given a public key commitment. + /// + /// The key should be included in the `keys` map and should be a variant of [AuthSecretKey]. + /// + /// # Errors + /// If the public key is not contained in the `keys` map, + /// [`AuthenticationError::UnknownPublicKey`] is returned. + fn get_signature( + &self, + pub_key_commitment: PublicKeyCommitment, + signing_inputs: &SigningInputs, + ) -> impl FutureMaybeSend> { + let message = signing_inputs.to_commitment(); + + async move { + match self.keys.get(&pub_key_commitment) { + Some((auth_key, _)) => Ok(auth_key.sign(message)), + None => Err(AuthenticationError::UnknownPublicKey(pub_key_commitment)), + } + } + } + + /// Returns the public key associated with the given public key commitment. + /// + /// If the public key commitment is not contained in the `keys` map, `None` is returned. + fn get_public_key( + &self, + pub_key_commitment: PublicKeyCommitment, + ) -> impl FutureMaybeSend>> { + async move { self.keys.get(&pub_key_commitment).map(|(_, pub_key)| pub_key.clone()) } + } +} + +// EMPTY AUTHENTICATOR +// ================================================================================================ + +impl TransactionAuthenticator for () { + #[allow(clippy::manual_async_fn)] + fn get_signature( + &self, + _pub_key_commitment: PublicKeyCommitment, + _signing_inputs: &SigningInputs, + ) -> impl FutureMaybeSend> { + async { + Err(AuthenticationError::RejectedSignature( + "default authenticator cannot provide signatures".to_string(), + )) + } + } + + fn get_public_key( + &self, + _pub_key_commitment: PublicKeyCommitment, + ) -> impl FutureMaybeSend>> { + async { None } + } +} + +// TESTS +// ================================================================================================ + +#[cfg(test)] +mod test { + use miden_protocol::account::auth::AuthSecretKey; + use miden_protocol::utils::serde::{Deserializable, Serializable}; + use miden_protocol::{Felt, Word}; + + use super::SigningInputs; + + #[test] + fn serialize_auth_key() { + let auth_key = AuthSecretKey::new_falcon512_poseidon2(); + let serialized = auth_key.to_bytes(); + let deserialized = AuthSecretKey::read_from_bytes(&serialized).unwrap(); + + assert_eq!(auth_key, deserialized); + } + + #[test] + fn serialize_deserialize_signing_inputs_arbitrary() { + let elements = vec![ + Felt::new(0), + Felt::new(1), + Felt::new(2), + Felt::new(3), + Felt::new(4), + Felt::new(5), + Felt::new(6), + Felt::new(7), + ]; + let inputs = SigningInputs::Arbitrary(elements.clone()); + let bytes = inputs.to_bytes(); + let decoded = SigningInputs::read_from_bytes(&bytes).unwrap(); + + match decoded { + SigningInputs::Arbitrary(decoded_elements) => { + assert_eq!(decoded_elements, elements); + }, + _ => panic!("expected Arbitrary variant"), + } + } + + #[test] + fn serialize_deserialize_signing_inputs_blind() { + let word = Word::from([Felt::new(10), Felt::new(20), Felt::new(30), Felt::new(40)]); + let inputs = SigningInputs::Blind(word); + let bytes = inputs.to_bytes(); + let decoded = SigningInputs::read_from_bytes(&bytes).unwrap(); + + match decoded { + SigningInputs::Blind(w) => { + assert_eq!(w, word); + }, + _ => panic!("expected Blind variant"), + } + } +} diff --git a/vendor/miden-tx/src/errors/mod.rs b/vendor/miden-tx/src/errors/mod.rs new file mode 100644 index 000000000..f9727fcae --- /dev/null +++ b/vendor/miden-tx/src/errors/mod.rs @@ -0,0 +1,435 @@ +use alloc::boxed::Box; +use alloc::string::String; +use alloc::vec::Vec; +use core::error::Error; + +use miden_processor::ExecutionError; +use miden_processor::serde::DeserializationError; +use miden_protocol::account::auth::PublicKeyCommitment; +use miden_protocol::account::{AccountId, StorageMapKey}; +use miden_protocol::assembly::diagnostics::reporting::PrintDiagnostic; +use miden_protocol::asset::AssetVaultKey; +use miden_protocol::block::BlockNumber; +use miden_protocol::crypto::merkle::smt::SmtProofError; +use miden_protocol::errors::{ + AccountDeltaError, + AccountError, + AssetError, + NoteError, + OutputNoteError, + ProvenTransactionError, + TransactionInputError, + TransactionInputsExtractionError, + TransactionOutputError, +}; +use miden_protocol::note::{NoteId, NoteMetadata}; +use miden_protocol::transaction::TransactionSummary; +use miden_protocol::{Felt, Word}; +use miden_verifier::VerificationError; +use thiserror::Error; + +// NOTE EXECUTION ERROR +// ================================================================================================ + +#[derive(Debug, Error)] +pub enum NoteCheckerError { + #[error("invalid input note count {0} is out of range)")] + InputNoteCountOutOfRange(usize), + #[error("transaction preparation failed: {0}")] + TransactionPreparation(#[source] TransactionExecutorError), + #[error("transaction execution prologue failed: {0}")] + PrologueExecution(#[source] TransactionExecutorError), +} + +// TRANSACTION CHECKER ERROR +// ================================================================================================ + +#[derive(Debug, Error)] +pub(crate) enum TransactionCheckerError { + #[error("transaction preparation failed: {0}")] + TransactionPreparation(#[source] TransactionExecutorError), + #[error("transaction execution prologue failed: {0}")] + PrologueExecution(#[source] TransactionExecutorError), + #[error("transaction execution epilogue failed: {0}")] + EpilogueExecution(#[source] TransactionExecutorError), + #[error("transaction note execution failed on note index {failed_note_index}: {error}")] + NoteExecution { + failed_note_index: usize, + error: TransactionExecutorError, + }, +} + +impl From for TransactionExecutorError { + fn from(error: TransactionCheckerError) -> Self { + match error { + TransactionCheckerError::TransactionPreparation(error) => error, + TransactionCheckerError::PrologueExecution(error) => error, + TransactionCheckerError::EpilogueExecution(error) => error, + TransactionCheckerError::NoteExecution { error, .. } => error, + } + } +} + +// TRANSACTION EXECUTOR ERROR +// ================================================================================================ + +#[derive(Debug, Error)] +pub enum TransactionExecutorError { + #[error("failed to read fee asset from transaction inputs")] + FeeAssetRetrievalFailed(#[source] TransactionInputsExtractionError), + #[error("failed to fetch transaction inputs from the data store")] + FetchTransactionInputsFailed(#[source] DataStoreError), + #[error("failed to fetch asset witnesses from the data store")] + FetchAssetWitnessFailed(#[source] DataStoreError), + #[error("fee asset must be fungible but was non-fungible")] + FeeAssetMustBeFungible, + #[error("foreign account inputs for ID {0} are not anchored on reference block")] + ForeignAccountNotAnchoredInReference(AccountId), + #[error( + "execution options' cycles must be between {min_cycles} and {max_cycles}, but found {actual}" + )] + InvalidExecutionOptionsCycles { + min_cycles: u32, + max_cycles: u32, + actual: u32, + }, + #[error("failed to create transaction inputs")] + InvalidTransactionInputs(#[source] TransactionInputError), + #[error("failed to process account update commitment: {0}")] + AccountUpdateCommitment(&'static str), + #[error( + "account delta commitment computed in transaction kernel ({in_kernel_commitment}) does not match account delta computed via the host ({host_commitment})" + )] + InconsistentAccountDeltaCommitment { + in_kernel_commitment: Word, + host_commitment: Word, + }, + #[error("failed to remove the fee asset from the pre-fee account delta")] + RemoveFeeAssetFromDelta(#[source] AccountDeltaError), + #[error("input account ID {input_id} does not match output account ID {output_id}")] + InconsistentAccountId { + input_id: AccountId, + output_id: AccountId, + }, + #[error("expected account nonce delta to be {expected}, found {actual}")] + InconsistentAccountNonceDelta { expected: Felt, actual: Felt }, + #[error( + "native asset amount {account_balance} in the account vault is not sufficient to cover the transaction fee of {tx_fee}" + )] + InsufficientFee { account_balance: u64, tx_fee: u64 }, + #[error("account witness provided for account ID {0} is invalid")] + InvalidAccountWitness(AccountId, #[source] SmtProofError), + #[error( + "input note {0} was created in a block past the transaction reference block number ({1})" + )] + NoteBlockPastReferenceBlock(NoteId, BlockNumber), + #[error("failed to construct transaction outputs")] + TransactionOutputConstructionFailed(#[source] TransactionOutputError), + // Print the diagnostic directly instead of returning the source error. In the source error + // case, the diagnostic is lost if the execution error is not explicitly unwrapped. + #[error("failed to execute transaction kernel program:\n{}", PrintDiagnostic::new(.0))] + TransactionProgramExecutionFailed(ExecutionError), + /// This variant can be matched on to get the summary of a transaction for signing purposes. + // It is boxed to avoid triggering clippy::result_large_err for functions that return this type. + #[error("transaction is unauthorized with summary {0:?}")] + Unauthorized(Box), + #[error( + "failed to respond to signature requested since no authenticator is assigned to the host" + )] + MissingAuthenticator, +} + +// TRANSACTION PROVER ERROR +// ================================================================================================ + +#[derive(Debug, Error)] +pub enum TransactionProverError { + #[error("failed to apply account delta")] + AccountDeltaApplyFailed(#[source] AccountError), + #[error("failed to remove the fee asset from the pre-fee account delta")] + RemoveFeeAssetFromDelta(#[source] AccountDeltaError), + #[error("failed to construct transaction outputs")] + TransactionOutputConstructionFailed(#[source] TransactionOutputError), + #[error("failed to shrink output note")] + OutputNoteShrinkFailed(#[source] OutputNoteError), + #[error("failed to build proven transaction")] + ProvenTransactionBuildFailed(#[source] ProvenTransactionError), + // Print the diagnostic directly instead of returning the source error. In the source error + // case, the diagnostic is lost if the execution error is not explicitly unwrapped. + #[error("failed to execute transaction kernel program:\n{}", PrintDiagnostic::new(.0))] + TransactionProgramExecutionFailed(ExecutionError), + /// Custom error variant for errors not covered by the other variants. + #[error("{error_msg}")] + Other { + error_msg: Box, + // thiserror will return this when calling Error::source on DataStoreError. + source: Option>, + }, +} + +impl TransactionProverError { + /// Creates a custom error using the [`TransactionProverError::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 [`TransactionProverError::Other`] variant from an error + /// message and a source error. + pub fn other_with_source( + message: impl Into, + source: impl Error + Send + Sync + 'static, + ) -> Self { + let message: String = message.into(); + Self::Other { + error_msg: message.into(), + source: Some(Box::new(source)), + } + } +} + +// TRANSACTION VERIFIER ERROR +// ================================================================================================ + +#[derive(Debug, Error)] +pub enum TransactionVerifierError { + #[error("failed to verify transaction")] + TransactionVerificationFailed(#[source] VerificationError), + #[error("transaction proof security level is {actual} but must be at least {expected_minimum}")] + InsufficientProofSecurityLevel { actual: u32, expected_minimum: u32 }, +} + +// TRANSACTION KERNEL ERROR +// ================================================================================================ + +#[derive(Debug, Error)] +pub enum TransactionKernelError { + #[error("failed to add asset to account delta")] + AccountDeltaAddAssetFailed(#[source] AccountDeltaError), + #[error("failed to remove asset from account delta")] + AccountDeltaRemoveAssetFailed(#[source] AccountDeltaError), + #[error("failed to add asset to note")] + FailedToAddAssetToNote(#[source] NoteError), + #[error("note storage has commitment {actual} but expected commitment {expected}")] + InvalidNoteStorage { expected: Word, actual: Word }, + #[error( + "failed to respond to signature requested since no authenticator is assigned to the host" + )] + MissingAuthenticator, + #[error("failed to generate signature")] + SignatureGenerationFailed(#[source] AuthenticationError), + #[error("transaction returned unauthorized event but a commitment did not match: {0}")] + TransactionSummaryCommitmentMismatch(#[source] Box), + #[error("failed to construct transaction summary")] + TransactionSummaryConstructionFailed(#[source] Box), + #[error("asset data extracted from the stack by event handler `{handler}` is not well formed")] + MalformedAssetInEventHandler { + handler: &'static str, + source: AssetError, + }, + #[error( + "note storage data extracted from the advice map by the event handler is not well formed" + )] + MalformedNoteStorage(#[source] NoteError), + #[error( + "note script data `{data:?}` extracted from the advice map by the event handler is not well formed" + )] + MalformedNoteScript { + data: Vec, + source: DeserializationError, + }, + #[error("recipient data `{0:?}` in the advice provider is not well formed")] + MalformedRecipientData(Vec), + #[error("cannot add asset to note with index {0}, note does not exist in the advice provider")] + MissingNote(usize), + #[error( + "public note with metadata {0:?} and recipient digest {1} is missing details in the advice provider" + )] + PublicNoteMissingDetails(NoteMetadata, Word), + #[error("attachment provided to set_attachment must be empty when attachment kind is None")] + NoteAttachmentNoneIsNotEmpty, + #[error( + "commitment of note attachment {actual} does not match attachment {provided} provided to set_attachment" + )] + NoteAttachmentArrayMismatch { actual: Word, provided: Word }, + #[error( + "note storage in advice provider contains fewer items ({actual}) than specified ({specified}) by its number of storage items" + )] + TooFewElementsForNoteStorage { specified: u64, actual: u64 }, + #[error("account procedure with procedure root {0} is not in the account procedure index map")] + UnknownAccountProcedure(Word), + #[error("code commitment {0} is not in the account procedure index map")] + UnknownCodeCommitment(Word), + #[error("account storage slots number is missing in memory at address {0}")] + AccountStorageSlotsNumMissing(u32), + #[error("account nonce can only be incremented once")] + NonceCanOnlyIncrementOnce, + #[error("failed to convert fee asset into fungible asset")] + FailedToConvertFeeAsset(#[source] AssetError), + #[error( + "failed to get inputs for foreign account {foreign_account_id} from data store at reference block {ref_block}" + )] + GetForeignAccountInputs { + foreign_account_id: AccountId, + ref_block: BlockNumber, + // thiserror will return this when calling Error::source on TransactionKernelError. + source: DataStoreError, + }, + #[error( + "failed to get vault asset witness from data store for vault root {vault_root} and vault_key {asset_key}" + )] + GetVaultAssetWitness { + vault_root: Word, + asset_key: AssetVaultKey, + // thiserror will return this when calling Error::source on TransactionKernelError. + source: DataStoreError, + }, + #[error( + "failed to get storage map witness from data store for map root {map_root} and map_key {map_key}" + )] + GetStorageMapWitness { + map_root: Word, + map_key: StorageMapKey, + // thiserror will return this when calling Error::source on TransactionKernelError. + source: DataStoreError, + }, + #[error( + "native asset amount {account_balance} in the account vault is not sufficient to cover the transaction fee of {tx_fee}" + )] + InsufficientFee { account_balance: u64, tx_fee: u64 }, + /// This variant signals that a signature over the contained commitments is required, but + /// missing. + #[error("transaction requires a signature")] + Unauthorized(Box), + /// A generic error returned when the transaction kernel did not behave as expected. + #[error("{message}")] + Other { + message: Box, + // thiserror will return this when calling Error::source on TransactionKernelError. + source: Option>, + }, +} + +impl TransactionKernelError { + /// Creates a custom error using the [`TransactionKernelError::Other`] variant from an error + /// message. + pub fn other(message: impl Into) -> Self { + let message: String = message.into(); + Self::Other { message: message.into(), source: None } + } + + /// Creates a custom error using the [`TransactionKernelError::Other`] variant from an error + /// message and a source error. + pub fn other_with_source( + message: impl Into, + source: impl Error + Send + Sync + 'static, + ) -> Self { + let message: String = message.into(); + Self::Other { + message: message.into(), + source: Some(Box::new(source)), + } + } +} + +// DATA STORE ERROR +// ================================================================================================ + +#[derive(Debug, Error)] +pub enum DataStoreError { + #[error("account with id {0} not found in data store")] + AccountNotFound(AccountId), + #[error("block with number {0} not found in data store")] + BlockNotFound(BlockNumber), + /// Custom error variant for implementors of the [`DataStore`](crate::executor::DataStore) + /// trait. + #[error("{error_msg}")] + Other { + error_msg: Box, + // thiserror will return this when calling Error::source on DataStoreError. + source: Option>, + }, +} + +impl DataStoreError { + /// Creates a custom error using the [`DataStoreError::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 [`DataStoreError::Other`] variant from an error message and + /// a source error. + pub fn other_with_source( + message: impl Into, + source: impl Error + Send + Sync + 'static, + ) -> Self { + let message: String = message.into(); + Self::Other { + error_msg: message.into(), + source: Some(Box::new(source)), + } + } +} + +// AUTHENTICATION ERROR +// ================================================================================================ + +#[derive(Debug, Error)] +pub enum AuthenticationError { + #[error("signature rejected: {0}")] + RejectedSignature(String), + #[error("public key `{0}` is not contained in the authenticator's keys")] + UnknownPublicKey(PublicKeyCommitment), + /// Custom error variant for implementors of the + /// [`TransactionAuthenticator`](crate::auth::TransactionAuthenticator) trait. + #[error("{error_msg}")] + Other { + error_msg: Box, + // thiserror will return this when calling Error::source on DataStoreError. + source: Option>, + }, +} + +impl AuthenticationError { + /// Creates a custom error using the [`AuthenticationError::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 [`AuthenticationError::Other`] variant from an error + /// message and a source error. + pub fn other_with_source( + message: impl Into, + source: impl Error + Send + Sync + 'static, + ) -> Self { + let message: String = message.into(); + Self::Other { + error_msg: message.into(), + source: Some(Box::new(source)), + } + } +} + +#[cfg(test)] +mod error_assertions { + use super::*; + + /// Asserts at compile time that the passed error has Send + Sync + 'static bounds. + fn _assert_error_is_send_sync_static(_: E) {} + + fn _assert_data_store_error_bounds(err: DataStoreError) { + _assert_error_is_send_sync_static(err); + } + + fn _assert_authentication_error_bounds(err: AuthenticationError) { + _assert_error_is_send_sync_static(err); + } + + fn _assert_transaction_kernel_error_bounds(err: TransactionKernelError) { + _assert_error_is_send_sync_static(err); + } +} diff --git a/vendor/miden-tx/src/executor/data_store.rs b/vendor/miden-tx/src/executor/data_store.rs new file mode 100644 index 000000000..e0525d491 --- /dev/null +++ b/vendor/miden-tx/src/executor/data_store.rs @@ -0,0 +1,89 @@ +use alloc::collections::BTreeSet; +use alloc::vec::Vec; + +use miden_processor::{FutureMaybeSend, MastForestStore, Word}; +use miden_protocol::account::{AccountId, PartialAccount, StorageMapKey, StorageMapWitness}; +use miden_protocol::asset::{AssetVaultKey, AssetWitness}; +use miden_protocol::block::{BlockHeader, BlockNumber}; +use miden_protocol::note::NoteScript; +use miden_protocol::transaction::{AccountInputs, PartialBlockchain}; + +use crate::DataStoreError; + +// DATA STORE TRAIT +// ================================================================================================ + +/// The [DataStore] trait defines the interface that transaction objects use to fetch data +/// required for transaction execution. +pub trait DataStore: MastForestStore { + /// Returns all the data required to execute a transaction against the account with the + /// specified ID and consuming input notes created in blocks in the input `ref_blocks` set. + /// + /// The highest block number in `ref_blocks` will be the transaction reference block. In + /// general, it is recommended that the reference corresponds to the latest block available + /// in the data store. + /// + /// # Errors + /// Returns an error if: + /// - The account with the specified ID could not be found in the data store. + /// - The block with the specified number could not be found in the data store. + /// - The combination of specified inputs resulted in a transaction input error. + /// - The data store encountered some internal error + fn get_transaction_inputs( + &self, + account_id: AccountId, + ref_blocks: BTreeSet, + ) -> impl FutureMaybeSend>; + + /// Returns a partial foreign account state together with a witness, proving its validity in the + /// specified transaction reference block. + fn get_foreign_account_inputs( + &self, + foreign_account_id: AccountId, + ref_block: BlockNumber, + ) -> impl FutureMaybeSend>; + + /// Returns witnesses for the asset vault keys in the requested account's vault with the + /// requested vault root. + /// + /// These are the witnesses that need to be added to the advice provider's merkle store and + /// advice map to make access to the corresponding assets possible. + fn get_vault_asset_witnesses( + &self, + account_id: AccountId, + vault_root: Word, + vault_keys: BTreeSet, + ) -> impl FutureMaybeSend, DataStoreError>>; + + /// Returns a witness for a storage map item identified by `map_key` in the requested account's + /// storage with the requested storage `map_root`. + /// + /// Note that the `map_key` needs to be hashed in order to get the actual key into the storage + /// map. + /// + /// This is the witness that needs to be added to the advice provider's merkle store and advice + /// map to make access to the specified storage map item possible. + fn get_storage_map_witness( + &self, + account_id: AccountId, + map_root: Word, + map_key: StorageMapKey, + ) -> impl FutureMaybeSend>; + + /// Returns a note script with the specified root, or `None` if not found. + /// + /// This method will try to find a note script with the specified root in the data store. + /// If the script is not found, it returns `Ok(None)` rather than an error, as "not found" + /// is a valid, expected outcome. + /// + /// **Note:** Data store implementers do not need to handle standard note scripts (e.g. P2ID). + /// These are resolved directly by the transaction executor and will not trigger this method. + /// + /// # Errors + /// Returns an error if the data store encountered an internal error while attempting to + /// retrieve the script. + fn get_note_script( + &self, + script_root: Word, + ) -> impl FutureMaybeSend, DataStoreError>>; +} diff --git a/vendor/miden-tx/src/executor/exec_host.rs b/vendor/miden-tx/src/executor/exec_host.rs new file mode 100644 index 000000000..a277eb8f1 --- /dev/null +++ b/vendor/miden-tx/src/executor/exec_host.rs @@ -0,0 +1,717 @@ +use alloc::boxed::Box; +use alloc::collections::{BTreeMap, BTreeSet}; +use alloc::sync::Arc; +use alloc::vec::Vec; + +use miden_processor::advice::AdviceMutation; +use miden_processor::event::EventError; +use miden_processor::mast::MastForest; +use miden_processor::{FutureMaybeSend, Host, ProcessorState}; +use miden_protocol::account::auth::PublicKeyCommitment; +use miden_protocol::account::{ + AccountCode, + AccountDelta, + AccountId, + PartialAccount, + StorageMapKey, + StorageSlotId, + StorageSlotName, +}; +use miden_protocol::assembly::debuginfo::Location; +use miden_protocol::assembly::{SourceFile, SourceManagerSync, SourceSpan}; +use miden_protocol::asset::{AssetVaultKey, AssetWitness, FungibleAsset}; +use miden_protocol::block::BlockNumber; +use miden_protocol::crypto::merkle::smt::SmtProof; +use miden_protocol::note::{NoteMetadata, NoteRecipient, NoteScript, NoteStorage}; +use miden_protocol::transaction::{ + InputNote, + InputNotes, + RawOutputNote, + TransactionAdviceInputs, + TransactionSummary, +}; +use miden_protocol::vm::{AdviceMap, EventId, EventName}; +use miden_protocol::{Felt, Hasher, Word}; +use miden_standards::note::StandardNote; + +use crate::auth::{SigningInputs, TransactionAuthenticator}; +use crate::errors::TransactionKernelError; +use crate::host::{ + RecipientData, + ScriptMastForestStore, + TransactionBaseHost, + TransactionEvent, + TransactionProgress, + TransactionProgressEvent, +}; +use crate::{AccountProcedureIndexMap, DataStore}; + +// TRANSACTION EXECUTOR HOST +// ================================================================================================ + +/// The transaction executor host is responsible for handling [`FutureMaybeSend`] requests made by +/// the transaction kernel during execution. In particular, it responds to signature generation +/// requests by forwarding the request to the contained [`TransactionAuthenticator`]. +/// +/// Transaction hosts are created on a per-transaction basis. That is, a transaction host is meant +/// to support execution of a single transaction and is discarded after the transaction finishes +/// execution. +pub struct TransactionExecutorHost<'store, 'auth, STORE, AUTH> +where + STORE: DataStore, + AUTH: TransactionAuthenticator, +{ + /// The underlying base transaction host. + base_host: TransactionBaseHost<'store, STORE>, + + /// Tracks the number of cycles for each of the transaction execution stages. + /// + /// The progress is updated event handlers. + tx_progress: TransactionProgress, + + /// Serves signature generation requests from the transaction runtime for signatures which are + /// not present in the `generated_signatures` field. + authenticator: Option<&'auth AUTH>, + + /// The reference block of the transaction. + ref_block: BlockNumber, + + /// The foreign account code that was lazy loaded during transaction execution. + /// + /// This is required for re-executing the transaction, e.g. as part of transaction proving. + accessed_foreign_account_code: Vec, + + /// Storage slot names for foreign accounts accessed during transaction execution. + foreign_account_slot_names: BTreeMap, + + /// Contains generated signatures (as a message |-> signature map) required for transaction + /// execution. Once a signature was created for a given message, it is inserted into this map. + /// After transaction execution, these can be inserted into the advice inputs to re-execute the + /// transaction without having to regenerate the signature or requiring access to the + /// authenticator that produced it. + generated_signatures: BTreeMap>, + + /// The initial balance of the fee asset in the native account's vault. + initial_fee_asset_balance: u64, + + /// The source manager to track source code file span information, improving any MASM related + /// error messages. + source_manager: Arc, +} + +impl<'store, 'auth, STORE, AUTH> TransactionExecutorHost<'store, 'auth, STORE, AUTH> +where + STORE: DataStore + Sync, + AUTH: TransactionAuthenticator + Sync, +{ + // CONSTRUCTORS + // -------------------------------------------------------------------------------------------- + + /// Creates a new [`TransactionExecutorHost`] instance from the provided inputs. + #[allow(clippy::too_many_arguments)] + pub fn new( + account: &PartialAccount, + input_notes: InputNotes, + mast_store: &'store STORE, + scripts_mast_store: ScriptMastForestStore, + acct_procedure_index_map: AccountProcedureIndexMap, + authenticator: Option<&'auth AUTH>, + ref_block: BlockNumber, + initial_fee_asset_balance: u64, + source_manager: Arc, + ) -> Self { + let base_host = TransactionBaseHost::new( + account, + input_notes, + mast_store, + scripts_mast_store, + acct_procedure_index_map, + ); + + Self { + base_host, + tx_progress: TransactionProgress::default(), + authenticator, + ref_block, + accessed_foreign_account_code: Vec::new(), + foreign_account_slot_names: BTreeMap::new(), + generated_signatures: BTreeMap::new(), + initial_fee_asset_balance, + source_manager, + } + } + + // PUBLIC ACCESSORS + // -------------------------------------------------------------------------------------------- + + /// Returns a reference to the `tx_progress` field of this transaction host. + pub fn tx_progress(&self) -> &TransactionProgress { + &self.tx_progress + } + + /// Returns a reference to the foreign account slot names collected during execution. + pub fn foreign_account_slot_names(&self) -> &BTreeMap { + &self.foreign_account_slot_names + } + + // EVENT HANDLERS + // -------------------------------------------------------------------------------------------- + + /// Handles a request for a foreign account by querying the data store for its account inputs. + async fn on_foreign_account_requested( + &mut self, + foreign_account_id: AccountId, + ) -> Result, TransactionKernelError> { + let foreign_account_inputs = self + .base_host + .store() + .get_foreign_account_inputs(foreign_account_id, self.ref_block) + .await + .map_err(|err| TransactionKernelError::GetForeignAccountInputs { + foreign_account_id, + ref_block: self.ref_block, + source: err, + })?; + + let mut tx_advice_inputs = TransactionAdviceInputs::default(); + tx_advice_inputs.add_foreign_accounts([&foreign_account_inputs]); + + // Extract and store slot names for this foreign account and store. + foreign_account_inputs.storage().header().slots().for_each(|slot| { + self.foreign_account_slot_names.insert(slot.id(), slot.name().clone()); + }); + + self.base_host.load_foreign_account_code(foreign_account_inputs.code()); + + // Add the foreign account's code to the list of accessed code. + self.accessed_foreign_account_code.push(foreign_account_inputs.code().clone()); + + Ok(tx_advice_inputs.into_advice_mutations().collect()) + } + + /// Pushes a signature to the advice stack as a response to the `AuthRequest` event. + /// + /// The signature is requested from the host's authenticator. + pub async fn on_auth_requested( + &mut self, + pub_key_hash: Word, + tx_summary: TransactionSummary, + ) -> Result, TransactionKernelError> { + let signing_inputs = SigningInputs::TransactionSummary(Box::new(tx_summary)); + + let authenticator = + self.authenticator.ok_or(TransactionKernelError::MissingAuthenticator)?; + + // get the message that will be signed by the authenticator + let message = signing_inputs.to_commitment(); + + let signature: Vec = authenticator + .get_signature(PublicKeyCommitment::from(pub_key_hash), &signing_inputs) + .await + .map_err(TransactionKernelError::SignatureGenerationFailed)? + .to_prepared_signature(message); + + let signature_key = Hasher::merge(&[pub_key_hash, message]); + self.generated_signatures.insert(signature_key, signature.clone()); + + Ok(vec![AdviceMutation::extend_stack(signature)]) + } + + /// Handles the [`TransactionEvent::EpilogueBeforeTxFeeRemovedFromAccount`] and returns an error + /// if the account cannot pay the fee. + async fn on_before_tx_fee_removed_from_account( + &self, + fee_asset: FungibleAsset, + ) -> Result, TransactionKernelError> { + // Construct initial fee asset. + let initial_fee_asset = + FungibleAsset::new(fee_asset.faucet_id(), self.initial_fee_asset_balance) + .expect("fungible asset created from fee asset should be valid"); + + // Compute the current balance of the native asset in the account based on the initial value + // and the delta. + let current_fee_asset = { + let fee_asset_amount_delta = self + .base_host + .account_delta_tracker() + .vault_delta() + .fungible() + .amount(&initial_fee_asset.vault_key()) + .unwrap_or(0); + + // SAFETY: Initial native asset faucet ID should be a fungible faucet and amount should + // be less than MAX_AMOUNT as checked by the account delta. + let fee_asset_delta = FungibleAsset::new( + initial_fee_asset.faucet_id(), + fee_asset_amount_delta.unsigned_abs(), + ) + .expect("faucet ID and amount should be valid"); + + // SAFETY: These computations are essentially the same as the ones executed by the + // transaction kernel, which should have aborted if they weren't valid. + if fee_asset_amount_delta > 0 { + initial_fee_asset + .add(fee_asset_delta) + .expect("transaction kernel should ensure amounts do not exceed MAX_AMOUNT") + } else { + initial_fee_asset + .sub(fee_asset_delta) + .expect("transaction kernel should ensure amount is not negative") + } + }; + + // Return an error if the balance in the account does not cover the fee. + if current_fee_asset.amount() < fee_asset.amount() { + return Err(TransactionKernelError::InsufficientFee { + account_balance: current_fee_asset.amount(), + tx_fee: fee_asset.amount(), + }); + } + + Ok(Vec::new()) + } + + /// Handles a request for a storage map witness by querying the data store for a merkle path. + /// + /// Note that we request witnesses against the _initial_ map root of the accounts. See also + /// [`Self::on_account_vault_asset_witness_requested`] for more on this topic. + async fn on_account_storage_map_witness_requested( + &self, + active_account_id: AccountId, + map_root: Word, + map_key: StorageMapKey, + ) -> Result, TransactionKernelError> { + let storage_map_witness = self + .base_host + .store() + .get_storage_map_witness(active_account_id, map_root, map_key) + .await + .map_err(|err| TransactionKernelError::GetStorageMapWitness { + map_root, + map_key, + source: err, + })?; + + // Get the nodes in the proof and insert them into the merkle store. + let merkle_store_ext = + AdviceMutation::extend_merkle_store(storage_map_witness.authenticated_nodes()); + + let smt_proof = SmtProof::from(storage_map_witness); + let map_ext = AdviceMutation::extend_map(AdviceMap::from_iter([( + smt_proof.leaf().hash(), + smt_proof.leaf().to_elements().collect::>(), + )])); + + Ok(vec![merkle_store_ext, map_ext]) + } + + /// Handles a request to an asset witness by querying the data store for a merkle path. + /// + /// ## Native Account + /// + /// For the native account we always request witnesses for the initial vault root, because the + /// data store only has the state of the account vault at the beginning of the transaction. + /// Since the vault root can change as the transaction progresses, this means the witnesses + /// may become _partially_ or fully outdated. To see why they can only be _partially_ outdated, + /// consider the following example: + /// + /// ```text + /// A A' + /// / \ / \ + /// B C -> B' C + /// / \ / \ / \ / \ + /// D E F G D E' F G + /// ``` + /// + /// Leaf E was updated to E', in turn updating nodes B and A. If we now request the merkle path + /// to G against root A (the initial vault root), we'll get nodes F and B. F is a node in the + /// updated tree, while B is not. We insert both into the merkle store anyway. Now, if the + /// transaction attempts to verify the merkle path to G, it can do so because F and B' are in + /// the merkle store. Note that B' is in the store because the transaction inserted it into the + /// merkle store as part of updating E, not because we inserted it. B is present in the store, + /// but is simply ignored for the purpose of verifying G's inclusion. + /// + /// ## Foreign Accounts + /// + /// Foreign accounts are read-only and so they cannot change throughout transaction execution. + /// This means their _current_ vault root is always equivalent to their _initial_ vault root. + /// So, for foreign accounts, just like for the native account, we also always request + /// witnesses for the initial vault root. + async fn on_account_vault_asset_witness_requested( + &self, + active_account_id: AccountId, + vault_root: Word, + asset_key: AssetVaultKey, + ) -> Result, TransactionKernelError> { + let asset_witnesses = self + .base_host + .store() + .get_vault_asset_witnesses( + active_account_id, + vault_root, + BTreeSet::from_iter([asset_key]), + ) + .await + .map_err(|err| TransactionKernelError::GetVaultAssetWitness { + vault_root, + asset_key, + source: err, + })?; + + Ok(asset_witnesses.into_iter().flat_map(asset_witness_to_advice_mutation).collect()) + } + + /// Handles a request for a [`NoteScript`] during transaction execution when the script is not + /// already in the advice provider. + /// + /// Standard note scripts (P2ID, etc.) are resolved directly from [`StandardNote`], avoiding a + /// data store round-trip. Non-standard scripts are fetched from the [`DataStore`]. + /// + /// The resolved script is used to build a [`NoteRecipient`], which is then used to create + /// an [`OutputNoteBuilder`]. This function is only called for notes where the script is not + /// already in the advice provider. + /// + /// # Errors + /// Returns an error if: + /// - The note is public and the script is not found in the data store. + /// - Constructing the recipient with the fetched script does not match the expected recipient + /// digest. + /// - The data store returns an error when fetching the script. + async fn on_note_script_requested( + &mut self, + note_idx: usize, + recipient_digest: Word, + script_root: Word, + metadata: NoteMetadata, + note_storage: NoteStorage, + serial_num: Word, + ) -> Result, TransactionKernelError> { + // Resolve standard note scripts directly, avoiding a data store round-trip. + let note_script: Option = + if let Some(standard_note) = StandardNote::from_script_root(script_root) { + Some(standard_note.script()) + } else { + self.base_host.store().get_note_script(script_root).await.map_err(|err| { + TransactionKernelError::other_with_source( + "failed to retrieve note script from data store", + err, + ) + })? + }; + + match note_script { + Some(note_script) => { + let script_felts: Vec = (¬e_script).into(); + let recipient = NoteRecipient::new(serial_num, note_script, note_storage); + + if recipient.digest() != recipient_digest { + return Err(TransactionKernelError::other(format!( + "recipient digest is {recipient_digest}, but recipient constructed from raw inputs has digest {}", + recipient.digest() + ))); + } + + self.base_host.output_note_from_recipient(note_idx, metadata, recipient)?; + + Ok(vec![AdviceMutation::extend_map(AdviceMap::from_iter([( + script_root, + script_felts, + )]))]) + }, + None if metadata.is_private() => { + self.base_host.output_note_from_recipient_digest( + note_idx, + metadata, + recipient_digest, + )?; + + Ok(Vec::new()) + }, + None => Err(TransactionKernelError::other(format!( + "note script with root {script_root} not found in data store for public note" + ))), + } + } + + /// Consumes `self` and returns the account delta, output notes, generated signatures and + /// transaction progress. + #[allow(clippy::type_complexity)] + pub fn into_parts( + self, + ) -> ( + AccountDelta, + InputNotes, + Vec, + Vec, + BTreeMap>, + TransactionProgress, + BTreeMap, + ) { + let (account_delta, input_notes, output_notes) = self.base_host.into_parts(); + + ( + account_delta, + input_notes, + output_notes, + self.accessed_foreign_account_code, + self.generated_signatures, + self.tx_progress, + self.foreign_account_slot_names, + ) + } +} + +// HOST IMPLEMENTATION +// ================================================================================================ + +impl Host for TransactionExecutorHost<'_, '_, STORE, AUTH> +where + STORE: DataStore + Sync, + AUTH: TransactionAuthenticator + Sync, +{ + fn get_label_and_source_file( + &self, + location: &Location, + ) -> (SourceSpan, Option>) { + let source_manager = self.source_manager.as_ref(); + let maybe_file = source_manager.get_by_uri(location.uri()); + let span = source_manager.location_to_span(location.clone()).unwrap_or_default(); + (span, maybe_file) + } + + fn get_mast_forest(&self, node_digest: &Word) -> impl FutureMaybeSend>> { + let mast_forest = self.base_host.get_mast_forest(node_digest); + async move { mast_forest } + } + + fn on_event( + &mut self, + process: &ProcessorState, + ) -> impl FutureMaybeSend, EventError>> { + let core_lib_event_result = self.base_host.handle_core_lib_events(process); + + // If the event was handled by a core lib handler (Ok(Some)), we will return the result from + // within the async block below. So, we only need to extract th tx event if the event was + // not yet handled (Ok(None)). + let tx_event_result = match core_lib_event_result { + Ok(None) => Some(TransactionEvent::extract(&self.base_host, process)), + _ => None, + }; + + async move { + if let Some(mutations) = core_lib_event_result? { + return Ok(mutations); + } + + // The outer None means the event was handled by core lib handlers. + let Some(tx_event_result) = tx_event_result else { + return Ok(Vec::new()); + }; + // The inner None means the transaction event ID does not need to be handled. + let Some(tx_event) = tx_event_result? else { + return Ok(Vec::new()); + }; + + let result = match tx_event { + TransactionEvent::AccountBeforeForeignLoad { foreign_account_id: account_id } => { + self.on_foreign_account_requested(account_id).await + }, + + TransactionEvent::AccountVaultAfterRemoveAsset { asset } => { + self.base_host.on_account_vault_after_remove_asset(asset) + }, + TransactionEvent::AccountVaultAfterAddAsset { asset } => { + self.base_host.on_account_vault_after_add_asset(asset) + }, + + TransactionEvent::AccountStorageAfterSetItem { slot_name, new_value } => { + self.base_host.on_account_storage_after_set_item(slot_name, new_value) + }, + + TransactionEvent::AccountStorageAfterSetMapItem { + slot_name, + key, + old_value: prev_map_value, + new_value, + } => self.base_host.on_account_storage_after_set_map_item( + slot_name, + key, + prev_map_value, + new_value, + ), + + TransactionEvent::AccountVaultBeforeAssetAccess { + active_account_id, + vault_root, + asset_key, + } => { + self.on_account_vault_asset_witness_requested( + active_account_id, + vault_root, + asset_key, + ) + .await + }, + + TransactionEvent::AccountStorageBeforeMapItemAccess { + active_account_id, + map_root, + map_key, + } => { + self.on_account_storage_map_witness_requested( + active_account_id, + map_root, + map_key, + ) + .await + }, + + TransactionEvent::AccountAfterIncrementNonce => { + self.base_host.on_account_after_increment_nonce() + }, + + TransactionEvent::AccountPushProcedureIndex { code_commitment, procedure_root } => { + self.base_host.on_account_push_procedure_index(code_commitment, procedure_root) + }, + + TransactionEvent::NoteBeforeCreated { note_idx, metadata, recipient_data } => { + match recipient_data { + RecipientData::Digest(recipient_digest) => { + self.base_host.output_note_from_recipient_digest( + note_idx, + metadata, + recipient_digest, + ) + }, + RecipientData::Recipient(note_recipient) => self + .base_host + .output_note_from_recipient(note_idx, metadata, note_recipient), + RecipientData::ScriptMissing { + recipient_digest, + serial_num, + script_root, + note_storage, + } => { + self.on_note_script_requested( + note_idx, + recipient_digest, + script_root, + metadata, + note_storage, + serial_num, + ) + .await + }, + } + }, + + TransactionEvent::NoteBeforeAddAsset { note_idx, asset } => { + self.base_host.on_note_before_add_asset(note_idx, asset) + }, + + TransactionEvent::NoteBeforeSetAttachment { note_idx, attachment } => self + .base_host + .on_note_before_set_attachment(note_idx, attachment) + .map(|_| Vec::new()), + + TransactionEvent::AuthRequest { pub_key_hash, tx_summary, signature } => { + if let Some(signature) = signature { + Ok(self.base_host.on_auth_requested(signature)) + } else { + self.on_auth_requested(pub_key_hash, tx_summary).await + } + }, + + // This always returns an error to abort the transaction. + TransactionEvent::Unauthorized { tx_summary } => { + Err(TransactionKernelError::Unauthorized(Box::new(tx_summary))) + }, + + TransactionEvent::EpilogueBeforeTxFeeRemovedFromAccount { fee_asset } => { + self.on_before_tx_fee_removed_from_account(fee_asset).await + }, + + TransactionEvent::LinkMapSet { advice_mutation } => Ok(advice_mutation), + TransactionEvent::LinkMapGet { advice_mutation } => Ok(advice_mutation), + TransactionEvent::Progress(tx_progress) => match tx_progress { + TransactionProgressEvent::PrologueStart(clk) => { + self.tx_progress.start_prologue(clk); + Ok(Vec::new()) + }, + TransactionProgressEvent::PrologueEnd(clk) => { + self.tx_progress.end_prologue(clk); + Ok(Vec::new()) + }, + TransactionProgressEvent::NotesProcessingStart(clk) => { + self.tx_progress.start_notes_processing(clk); + Ok(Vec::new()) + }, + TransactionProgressEvent::NotesProcessingEnd(clk) => { + self.tx_progress.end_notes_processing(clk); + Ok(Vec::new()) + }, + TransactionProgressEvent::NoteExecutionStart { note_id, clk } => { + self.tx_progress.start_note_execution(clk, note_id); + Ok(Vec::new()) + }, + TransactionProgressEvent::NoteExecutionEnd(clk) => { + self.tx_progress.end_note_execution(clk); + Ok(Vec::new()) + }, + TransactionProgressEvent::TxScriptProcessingStart(clk) => { + self.tx_progress.start_tx_script_processing(clk); + Ok(Vec::new()) + }, + TransactionProgressEvent::TxScriptProcessingEnd(clk) => { + self.tx_progress.end_tx_script_processing(clk); + Ok(Vec::new()) + }, + TransactionProgressEvent::EpilogueStart(clk) => { + self.tx_progress.start_epilogue(clk); + Ok(Vec::new()) + }, + TransactionProgressEvent::EpilogueEnd(clk) => { + self.tx_progress.end_epilogue(clk); + Ok(Vec::new()) + }, + TransactionProgressEvent::EpilogueAuthProcStart(clk) => { + self.tx_progress.start_auth_procedure(clk); + Ok(Vec::new()) + }, + TransactionProgressEvent::EpilogueAuthProcEnd(clk) => { + self.tx_progress.end_auth_procedure(clk); + Ok(Vec::new()) + }, + TransactionProgressEvent::EpilogueAfterTxCyclesObtained(clk) => { + self.tx_progress.epilogue_after_tx_cycles_obtained(clk); + Ok(Vec::new()) + }, + }, + }; + + result.map_err(EventError::from) + } + } + + fn resolve_event(&self, event_id: EventId) -> Option<&EventName> { + self.base_host.resolve_event(event_id) + } +} + +// HELPER FUNCTIONS +// ================================================================================================ + +/// Converts an [`AssetWitness`] into the set of advice mutations that need to be inserted in order +/// to access the asset. +fn asset_witness_to_advice_mutation(asset_witness: AssetWitness) -> [AdviceMutation; 2] { + // Get the nodes in the proof and insert them into the merkle store. + let merkle_store_ext = AdviceMutation::extend_merkle_store(asset_witness.authenticated_nodes()); + + let smt_proof = SmtProof::from(asset_witness); + let map_ext = AdviceMutation::extend_map(AdviceMap::from_iter([( + smt_proof.leaf().hash(), + smt_proof.leaf().to_elements().collect::>(), + )])); + + [merkle_store_ext, map_ext] +} diff --git a/vendor/miden-tx/src/executor/mod.rs b/vendor/miden-tx/src/executor/mod.rs new file mode 100644 index 000000000..279e5d833 --- /dev/null +++ b/vendor/miden-tx/src/executor/mod.rs @@ -0,0 +1,553 @@ +use alloc::collections::BTreeSet; +use alloc::sync::Arc; +use core::marker::PhantomData; + +use miden_processor::advice::AdviceInputs; +use miden_processor::{ExecutionError, FastProcessor, StackInputs}; +pub use miden_processor::{ExecutionOptions, MastForestStore}; +use miden_protocol::account::AccountId; +use miden_protocol::assembly::DefaultSourceManager; +use miden_protocol::assembly::debuginfo::SourceManagerSync; +use miden_protocol::asset::{Asset, AssetVaultKey}; +use miden_protocol::block::BlockNumber; +use miden_protocol::transaction::{ + ExecutedTransaction, + InputNote, + InputNotes, + TransactionArgs, + TransactionInputs, + TransactionKernel, + TransactionScript, +}; +use miden_protocol::vm::StackOutputs; +use miden_protocol::{Felt, MAX_TX_EXECUTION_CYCLES, MIN_TX_EXECUTION_CYCLES}; + +use super::TransactionExecutorError; +use crate::auth::TransactionAuthenticator; +use crate::errors::TransactionKernelError; +use crate::host::{AccountProcedureIndexMap, ScriptMastForestStore}; + +mod exec_host; +pub use exec_host::TransactionExecutorHost; + +mod data_store; +pub use data_store::DataStore; + +mod notes_checker; +pub use notes_checker::{ + FailedNote, + MAX_NUM_CHECKER_NOTES, + NoteConsumptionChecker, + NoteConsumptionInfo, +}; + +mod program_executor; +pub use program_executor::ProgramExecutor; + +// TRANSACTION EXECUTOR +// ================================================================================================ + +/// The transaction executor is responsible for executing Miden blockchain transactions. +/// +/// Transaction execution consists of the following steps: +/// - Fetch the data required to execute a transaction from the [DataStore]. +/// - Execute the transaction program and create an [ExecutedTransaction]. +/// +/// The transaction executor uses dynamic dispatch with trait objects for the [DataStore] and +/// [TransactionAuthenticator], allowing it to be used with different backend implementations. +/// At the moment of execution, the [DataStore] is expected to provide all required MAST nodes. +pub struct TransactionExecutor< + 'store, + 'auth, + STORE: 'store, + AUTH: 'auth, + EXEC: ProgramExecutor = FastProcessor, +> { + data_store: &'store STORE, + authenticator: Option<&'auth AUTH>, + source_manager: Arc, + exec_options: ExecutionOptions, + _executor: PhantomData, +} + +impl<'store, 'auth, STORE, AUTH> TransactionExecutor<'store, 'auth, STORE, AUTH> +where + STORE: DataStore + 'store + Sync, + AUTH: TransactionAuthenticator + 'auth + Sync, +{ + // CONSTRUCTORS + // -------------------------------------------------------------------------------------------- + + /// Creates a new [TransactionExecutor] instance with the specified [DataStore]. + /// + /// The created executor will not have the authenticator or source manager set, and tracing and + /// debug mode will be turned off. + /// + /// By default, the executor uses [`FastProcessor`](miden_processor::FastProcessor) for program + /// execution. Use [`with_program_executor`](Self::with_program_executor) to plug in a + /// different execution engine. + pub fn new(data_store: &'store STORE) -> Self { + const _: () = assert!(MIN_TX_EXECUTION_CYCLES <= MAX_TX_EXECUTION_CYCLES); + Self { + data_store, + authenticator: None, + source_manager: Arc::new(DefaultSourceManager::default()), + exec_options: ExecutionOptions::new( + Some(MAX_TX_EXECUTION_CYCLES), + MIN_TX_EXECUTION_CYCLES, + ExecutionOptions::DEFAULT_CORE_TRACE_FRAGMENT_SIZE, + false, + false, + ) + .expect("Must not fail while max cycles is more than min trace length"), + _executor: PhantomData, + } + } +} + +impl<'store, 'auth, STORE, AUTH, EXEC> TransactionExecutor<'store, 'auth, STORE, AUTH, EXEC> +where + STORE: DataStore + 'store + Sync, + AUTH: TransactionAuthenticator + 'auth + Sync, + EXEC: ProgramExecutor, +{ + /// Replaces the transaction program executor with a different implementation. + /// + /// This allows plugging in alternative execution engines while preserving the rest of the + /// transaction executor configuration. + pub fn with_program_executor( + self, + ) -> TransactionExecutor<'store, 'auth, STORE, AUTH, EXEC2> { + TransactionExecutor::<'store, 'auth, STORE, AUTH, EXEC2> { + data_store: self.data_store, + authenticator: self.authenticator, + source_manager: self.source_manager, + exec_options: self.exec_options, + _executor: PhantomData, + } + } + + /// Adds the specified [TransactionAuthenticator] to the executor and returns the resulting + /// executor. + /// + /// This will overwrite any previously set authenticator. + #[must_use] + pub fn with_authenticator(mut self, authenticator: &'auth AUTH) -> Self { + self.authenticator = Some(authenticator); + self + } + + /// Adds the specified source manager to the executor and returns the resulting executor. + /// + /// The `source_manager` is used to map potential errors back to their source code. To get the + /// most value out of it, use the same source manager as was used with the + /// [`Assembler`](miden_protocol::assembly::Assembler) that assembled the Miden Assembly code + /// that should be debugged, e.g. account components, note scripts or transaction scripts. + /// + /// This will overwrite any previously set source manager. + #[must_use] + pub fn with_source_manager(mut self, source_manager: Arc) -> Self { + self.source_manager = source_manager; + self + } + + /// Sets the [ExecutionOptions] for the executor to the provided options and returns the + /// resulting executor. + /// + /// # Errors + /// Returns an error if the specified cycle values (`max_cycles` and `expected_cycles`) in + /// the [ExecutionOptions] are not within the range [`MIN_TX_EXECUTION_CYCLES`] and + /// [`MAX_TX_EXECUTION_CYCLES`]. + pub fn with_options( + mut self, + exec_options: ExecutionOptions, + ) -> Result { + validate_num_cycles(exec_options.max_cycles())?; + validate_num_cycles(exec_options.expected_cycles())?; + + self.exec_options = exec_options; + Ok(self) + } + + /// Puts the [TransactionExecutor] into debug mode and returns the resulting executor. + /// + /// When transaction executor is in debug mode, all transaction-related code (note scripts, + /// account code) will be compiled and executed in debug mode. This will ensure that all debug + /// instructions present in the original source code are executed. + #[must_use] + pub fn with_debug_mode(mut self) -> Self { + self.exec_options = self.exec_options.with_debugging(true); + self + } + + /// Enables tracing for the created instance of [TransactionExecutor] and returns the resulting + /// executor. + /// + /// When tracing is enabled, the executor will receive tracing events as various stages of the + /// transaction kernel complete. This enables collecting basic stats about how long different + /// stages of transaction execution take. + #[must_use] + pub fn with_tracing(mut self) -> Self { + self.exec_options = self.exec_options.with_tracing(true); + self + } + + // TRANSACTION EXECUTION + // -------------------------------------------------------------------------------------------- + + /// Prepares and executes a transaction specified by the provided arguments and returns an + /// [`ExecutedTransaction`]. + /// + /// The method first fetches the data required to execute the transaction from the [`DataStore`] + /// and compile the transaction into an executable program. In particular, it fetches the + /// account identified by the account ID from the store as well as `block_ref`, the header of + /// the reference block of the transaction and the set of headers from the blocks in which the + /// provided `notes` were created. Then, it executes the transaction program and creates an + /// [`ExecutedTransaction`]. + /// + /// # Errors: + /// + /// Returns an error if: + /// - If required data can not be fetched from the [`DataStore`]. + /// - If the transaction arguments contain foreign account data not anchored in the reference + /// block. + /// - If any input notes were created in block numbers higher than the reference block. + pub async fn execute_transaction( + &self, + account_id: AccountId, + block_ref: BlockNumber, + notes: InputNotes, + tx_args: TransactionArgs, + ) -> Result { + let tx_inputs = self.prepare_tx_inputs(account_id, block_ref, notes, tx_args).await?; + + let (mut host, stack_inputs, advice_inputs) = self.prepare_transaction(&tx_inputs).await?; + + // instantiate the processor in debug mode only when debug mode is specified via execution + // options; this is important because in debug mode execution is almost 100x slower + let processor = EXEC::new(stack_inputs, advice_inputs, self.exec_options); + + let output = processor + .execute(&TransactionKernel::main(), &mut host) + .await + .map_err(map_execution_error)?; + let stack_outputs = output.stack; + let advice_provider = output.advice; + + // The stack is not necessary since it is being reconstructed when re-executing. + let (_stack, advice_map, merkle_store, _pc_requests) = advice_provider.into_parts(); + let advice_inputs = AdviceInputs { + map: advice_map, + store: merkle_store, + ..Default::default() + }; + + build_executed_transaction(advice_inputs, tx_inputs, stack_outputs, host) + } + + // SCRIPT EXECUTION + // -------------------------------------------------------------------------------------------- + + /// Executes an arbitrary script against the given account and returns the stack state at the + /// end of execution. + /// + /// # Errors: + /// Returns an error if: + /// - If required data can not be fetched from the [DataStore]. + /// - If the transaction host can not be created from the provided values. + /// - If the execution of the provided program fails. + pub async fn execute_tx_view_script( + &self, + account_id: AccountId, + block_ref: BlockNumber, + tx_script: TransactionScript, + advice_inputs: AdviceInputs, + ) -> Result<[Felt; 16], TransactionExecutorError> { + let mut tx_args = TransactionArgs::default().with_tx_script(tx_script); + tx_args.extend_advice_inputs(advice_inputs); + + let notes = InputNotes::default(); + let tx_inputs = self.prepare_tx_inputs(account_id, block_ref, notes, tx_args).await?; + + let (mut host, stack_inputs, advice_inputs) = self.prepare_transaction(&tx_inputs).await?; + + let processor = EXEC::new(stack_inputs, advice_inputs, self.exec_options); + let output = processor + .execute(&TransactionKernel::tx_script_main(), &mut host) + .await + .map_err(TransactionExecutorError::TransactionProgramExecutionFailed)?; + let stack_outputs = output.stack; + + Ok(*stack_outputs) + } + + // HELPER METHODS + // -------------------------------------------------------------------------------------------- + + // Validates input notes and account inputs after retrieving transaction inputs from the store. + // + // This method has a one-to-many call relationship with the `prepare_transaction` method. This + // method needs to be called only once in order to allow many transactions to be prepared based + // on the transaction inputs returned by this method. + async fn prepare_tx_inputs( + &self, + account_id: AccountId, + block_ref: BlockNumber, + input_notes: InputNotes, + tx_args: TransactionArgs, + ) -> Result { + let (mut asset_vault_keys, mut ref_blocks) = validate_input_notes(&input_notes, block_ref)?; + ref_blocks.insert(block_ref); + + let (account, block_header, blockchain) = self + .data_store + .get_transaction_inputs(account_id, ref_blocks) + .await + .map_err(TransactionExecutorError::FetchTransactionInputsFailed)?; + + let native_account_vault_root = account.vault().root(); + let fee_asset_vault_key = + AssetVaultKey::new_fungible(block_header.fee_parameters().native_asset_id()) + .expect("fee asset should be a fungible asset"); + + let mut tx_inputs = TransactionInputs::new(account, block_header, blockchain, input_notes) + .map_err(TransactionExecutorError::InvalidTransactionInputs)? + .with_tx_args(tx_args); + + // Add the vault key for the fee asset to the list of asset vault keys which will need to be + // accessed at the end of the transaction. + asset_vault_keys.insert(fee_asset_vault_key); + + // filter out any asset vault keys for which we already have witnesses in the advice inputs + asset_vault_keys.retain(|asset_key| { + !tx_inputs.has_vault_asset_witness(native_account_vault_root, asset_key) + }); + + // if any of the witnesses are missing, fetch them from the data store and add to tx_inputs + if !asset_vault_keys.is_empty() { + let asset_witnesses = self + .data_store + .get_vault_asset_witnesses(account_id, native_account_vault_root, asset_vault_keys) + .await + .map_err(TransactionExecutorError::FetchAssetWitnessFailed)?; + + tx_inputs = tx_inputs.with_asset_witnesses(asset_witnesses); + } + + Ok(tx_inputs) + } + + /// Prepares the data needed for transaction execution. + /// + /// Preparation includes loading transaction inputs from the data store, validating them, and + /// instantiating a transaction host. + async fn prepare_transaction( + &self, + tx_inputs: &TransactionInputs, + ) -> Result< + (TransactionExecutorHost<'store, 'auth, STORE, AUTH>, StackInputs, AdviceInputs), + TransactionExecutorError, + > { + let (stack_inputs, tx_advice_inputs) = TransactionKernel::prepare_inputs(tx_inputs); + let input_notes = tx_inputs.input_notes(); + + let script_mast_store = ScriptMastForestStore::new( + tx_inputs.tx_script(), + input_notes.iter().map(|n| n.note().script()), + ); + + // To start executing the transaction, the procedure index map only needs to contain the + // native account's procedures. Foreign accounts are inserted into the map on first access. + let account_procedure_index_map = + AccountProcedureIndexMap::new([tx_inputs.account().code()]); + + let initial_fee_asset_balance = { + let vault_root = tx_inputs.account().vault().root(); + let native_asset_id = tx_inputs.block_header().fee_parameters().native_asset_id(); + let fee_asset_vault_key = AssetVaultKey::new_fungible(native_asset_id) + .expect("fee asset should be a fungible asset"); + + let fee_asset = tx_inputs + .read_vault_asset(vault_root, fee_asset_vault_key) + .map_err(TransactionExecutorError::FeeAssetRetrievalFailed)?; + match fee_asset { + Some(Asset::Fungible(fee_asset)) => fee_asset.amount(), + Some(Asset::NonFungible(_)) => { + return Err(TransactionExecutorError::FeeAssetMustBeFungible); + }, + // If the asset was not found, its balance is zero. + None => 0, + } + }; + let host = TransactionExecutorHost::new( + tx_inputs.account(), + input_notes.clone(), + self.data_store, + script_mast_store, + account_procedure_index_map, + self.authenticator, + tx_inputs.block_header().block_num(), + initial_fee_asset_balance, + self.source_manager.clone(), + ); + + let advice_inputs = tx_advice_inputs.into_advice_inputs(); + + Ok((host, stack_inputs, advice_inputs)) + } +} + +// HELPER FUNCTIONS +// ================================================================================================ + +/// Creates a new [ExecutedTransaction] from the provided data. +fn build_executed_transaction( + mut advice_inputs: AdviceInputs, + tx_inputs: TransactionInputs, + stack_outputs: StackOutputs, + host: TransactionExecutorHost, +) -> Result { + // Note that the account delta does not contain the removed transaction fee, so it is the + // "pre-fee" delta of the transaction. + + let ( + pre_fee_account_delta, + _input_notes, + output_notes, + accessed_foreign_account_code, + generated_signatures, + tx_progress, + foreign_account_slot_names, + ) = host.into_parts(); + + let tx_outputs = + TransactionKernel::from_transaction_parts(&stack_outputs, &advice_inputs, output_notes) + .map_err(TransactionExecutorError::TransactionOutputConstructionFailed)?; + + let pre_fee_delta_commitment = pre_fee_account_delta.to_commitment(); + if tx_outputs.account_delta_commitment() != pre_fee_delta_commitment { + return Err(TransactionExecutorError::InconsistentAccountDeltaCommitment { + in_kernel_commitment: tx_outputs.account_delta_commitment(), + host_commitment: pre_fee_delta_commitment, + }); + } + + // The full transaction delta is the pre fee delta with the fee asset removed. + let mut post_fee_account_delta = pre_fee_account_delta; + post_fee_account_delta + .vault_mut() + .remove_asset(Asset::from(tx_outputs.fee())) + .map_err(TransactionExecutorError::RemoveFeeAssetFromDelta)?; + + let initial_account = tx_inputs.account(); + let final_account = tx_outputs.account(); + + if initial_account.id() != final_account.id() { + return Err(TransactionExecutorError::InconsistentAccountId { + input_id: initial_account.id(), + output_id: final_account.id(), + }); + } + + // Make sure nonce delta was computed correctly. + let nonce_delta = final_account.nonce() - initial_account.nonce(); + if nonce_delta != post_fee_account_delta.nonce_delta() { + return Err(TransactionExecutorError::InconsistentAccountNonceDelta { + expected: nonce_delta, + actual: post_fee_account_delta.nonce_delta(), + }); + } + + // Introduce generated signatures into the witness inputs. + advice_inputs.map.extend(generated_signatures); + + // Overwrite advice inputs from after the execution on the transaction inputs. This is + // guaranteed to be a superset of the original advice inputs. + let tx_inputs = tx_inputs + .with_foreign_account_code(accessed_foreign_account_code) + .with_foreign_account_slot_names(foreign_account_slot_names) + .with_advice_inputs(advice_inputs); + + Ok(ExecutedTransaction::new( + tx_inputs, + tx_outputs, + post_fee_account_delta, + tx_progress.into(), + )) +} + +/// Validates that input notes were not created after the reference block. +/// +/// Returns the set of block numbers required to execute the provided notes and the set of asset +/// vault keys that will be needed in the transaction prologue. +/// +/// The transaction input vault is a copy of the account vault and to mutate the input vault (during +/// the prologue, for asset preservation), witnesses for the note assets against the account vault +/// must be requested. +fn validate_input_notes( + notes: &InputNotes, + block_ref: BlockNumber, +) -> Result<(BTreeSet, BTreeSet), TransactionExecutorError> { + let mut ref_blocks: BTreeSet = BTreeSet::new(); + let mut asset_vault_keys: BTreeSet = BTreeSet::new(); + + for input_note in notes.iter() { + // Validate that notes were not created after the reference, and build the set of required + // block numbers + if let Some(location) = input_note.location() { + if location.block_num() > block_ref { + return Err(TransactionExecutorError::NoteBlockPastReferenceBlock( + input_note.id(), + block_ref, + )); + } + ref_blocks.insert(location.block_num()); + } + + asset_vault_keys.extend(input_note.note().assets().iter().map(Asset::vault_key)); + } + + Ok((asset_vault_keys, ref_blocks)) +} + +/// Validates that the number of cycles specified is within the allowed range. +fn validate_num_cycles(num_cycles: u32) -> Result<(), TransactionExecutorError> { + if !(MIN_TX_EXECUTION_CYCLES..=MAX_TX_EXECUTION_CYCLES).contains(&num_cycles) { + Err(TransactionExecutorError::InvalidExecutionOptionsCycles { + min_cycles: MIN_TX_EXECUTION_CYCLES, + max_cycles: MAX_TX_EXECUTION_CYCLES, + actual: num_cycles, + }) + } else { + Ok(()) + } +} + +/// Remaps an execution error to a transaction executor error. +/// +/// - If the inner error is [`TransactionKernelError::Unauthorized`], it is remapped to +/// [`TransactionExecutorError::Unauthorized`]. +/// - Otherwise, the execution error is wrapped in +/// [`TransactionExecutorError::TransactionProgramExecutionFailed`]. +fn map_execution_error(exec_err: ExecutionError) -> TransactionExecutorError { + match exec_err { + ExecutionError::EventError { ref error, .. } => { + match error.downcast_ref::() { + Some(TransactionKernelError::Unauthorized(summary)) => { + TransactionExecutorError::Unauthorized(summary.clone()) + }, + Some(TransactionKernelError::InsufficientFee { account_balance, tx_fee }) => { + TransactionExecutorError::InsufficientFee { + account_balance: *account_balance, + tx_fee: *tx_fee, + } + }, + Some(TransactionKernelError::MissingAuthenticator) => { + TransactionExecutorError::MissingAuthenticator + }, + _ => TransactionExecutorError::TransactionProgramExecutionFailed(exec_err), + } + }, + _ => TransactionExecutorError::TransactionProgramExecutionFailed(exec_err), + } +} diff --git a/vendor/miden-tx/src/executor/notes_checker.rs b/vendor/miden-tx/src/executor/notes_checker.rs new file mode 100644 index 000000000..69b71869e --- /dev/null +++ b/vendor/miden-tx/src/executor/notes_checker.rs @@ -0,0 +1,410 @@ +use alloc::collections::BTreeMap; +use alloc::vec::Vec; + +use miden_processor::advice::AdviceInputs; +use miden_protocol::account::AccountId; +use miden_protocol::block::BlockNumber; +use miden_protocol::note::Note; +use miden_protocol::transaction::{ + InputNote, + InputNotes, + TransactionArgs, + TransactionInputs, + TransactionKernel, +}; +use miden_standards::note::{NoteConsumptionStatus, StandardNote}; + +use super::{ProgramExecutor, TransactionExecutor}; +use crate::auth::TransactionAuthenticator; +use crate::errors::TransactionCheckerError; +use crate::executor::map_execution_error; +use crate::{DataStore, NoteCheckerError, TransactionExecutorError}; + +// CONSTANTS +// ================================================================================================ + +/// Maximum number of notes that can be checked at once. +/// +/// Fixed at an amount that should keep each run of note consumption checking to a maximum of ~50ms. +pub const MAX_NUM_CHECKER_NOTES: usize = 20; + +// NOTE CONSUMPTION INFO +// ================================================================================================ + +/// Represents a failed note consumption. +#[derive(Debug)] +pub struct FailedNote { + pub note: Note, + pub error: TransactionExecutorError, +} + +impl FailedNote { + /// Constructs a new `FailedNote`. + pub fn new(note: Note, error: TransactionExecutorError) -> Self { + Self { note, error } + } +} + +/// Contains information about the successful and failed consumption of notes. +#[derive(Default, Debug)] +pub struct NoteConsumptionInfo { + pub successful: Vec, + pub failed: Vec, +} + +impl NoteConsumptionInfo { + /// Creates a new [`NoteConsumptionInfo`] instance with the given successful notes. + pub fn new_successful(successful: Vec) -> Self { + Self { successful, ..Default::default() } + } + + /// Creates a new [`NoteConsumptionInfo`] instance with the given successful and failed notes. + pub fn new(successful: Vec, failed: Vec) -> Self { + Self { successful, failed } + } +} + +// NOTE CONSUMPTION CHECKER +// ================================================================================================ + +/// This struct performs input notes check against provided target account. +/// +/// The check is performed using the [NoteConsumptionChecker::check_notes_consumability] procedure. +/// Essentially runs the transaction to make sure that provided input notes could be consumed by the +/// account. +pub struct NoteConsumptionChecker<'a, STORE, AUTH, EXEC: ProgramExecutor>( + &'a TransactionExecutor<'a, 'a, STORE, AUTH, EXEC>, +); + +impl<'a, STORE, AUTH, EXEC> NoteConsumptionChecker<'a, STORE, AUTH, EXEC> +where + STORE: DataStore + Sync, + AUTH: TransactionAuthenticator + Sync, + EXEC: ProgramExecutor, +{ + /// Creates a new [`NoteConsumptionChecker`] instance with the given transaction executor. + pub fn new(tx_executor: &'a TransactionExecutor<'a, 'a, STORE, AUTH, EXEC>) -> Self { + NoteConsumptionChecker(tx_executor) + } + + /// Checks whether some set of the provided input notes could be consumed by the provided + /// account by executing the transaction with varying combination of notes. + /// + /// This function attempts to find the maximum set of notes that can be successfully executed + /// together by the target account. + /// + /// Because of the runtime complexity involved in this function, a limited range of + /// [`MAX_NUM_CHECKER_NOTES`] input notes is allowed. + /// + /// If some notes succeed and others fail, the failed notes are removed from the candidate set + /// and the remaining notes (successful + unattempted) are retried in the next iteration. This + /// process continues until either all remaining notes succeed or no notes can be successfully + /// executed + /// + /// For example, given notes A, B, C, D, E, the execution flow would be as follows: + /// - Try [A, B, C, D, E] → A, B succeed, C fails → Remove C, try again. + /// - Try [A, B, D, E] → A, B, D succeed, E fails → Remove E, try again. + /// - Try [A, B, D] → All succeed → Return successful=[A, B, D], failed=[C, E]. + /// + /// If a failure occurs at the epilogue phase of the transaction execution, the relevant set of + /// otherwise-successful notes are retried in various combinations in an attempt to find a + /// combination that passes the epilogue phase successfully. + /// + /// Returns a list of successfully consumed notes and a list of failed notes. + pub async fn check_notes_consumability( + &self, + target_account_id: AccountId, + block_ref: BlockNumber, + mut notes: Vec, + tx_args: TransactionArgs, + ) -> Result { + let num_notes = notes.len(); + if num_notes == 0 || num_notes > MAX_NUM_CHECKER_NOTES { + return Err(NoteCheckerError::InputNoteCountOutOfRange(num_notes)); + } + // Ensure standard notes are ordered first. + notes.sort_unstable_by_key(|note| { + StandardNote::from_script_root(note.script().root()).is_none() + }); + + let notes = InputNotes::from(notes); + let tx_inputs = self + .0 + .prepare_tx_inputs(target_account_id, block_ref, notes, tx_args) + .await + .map_err(NoteCheckerError::TransactionPreparation)?; + + // Attempt to find an executable set of notes. + self.find_executable_notes_by_elimination(tx_inputs).await + } + + /// Checks whether the provided input note could be consumed by the provided account by + /// executing a transaction at the specified block height. + /// + /// This function takes into account the possibility that the signatures may not be loaded into + /// the transaction context and returns the [`NoteConsumptionStatus`] result accordingly. + /// + /// This function first applies the static analysis of the provided note, and if it doesn't + /// reveal any errors next it tries to execute the transaction. Based on the execution result, + /// it either returns a [`NoteCheckerError`] or the [`NoteConsumptionStatus`]: depending on + /// whether the execution succeeded, failed in the prologue, during the note execution process + /// or in the epilogue. + pub async fn can_consume( + &self, + target_account_id: AccountId, + block_ref: BlockNumber, + note: InputNote, + tx_args: TransactionArgs, + ) -> Result { + // Return the consumption status if we manage to determine it from the standard note + if let Some(standard_note) = StandardNote::from_script_root(note.note().script().root()) + && let Some(consumption_status) = + standard_note.is_consumable(note.note(), target_account_id, block_ref) + { + return Ok(consumption_status); + } + + // Prepare transaction inputs. + let mut tx_inputs = self + .0 + .prepare_tx_inputs( + target_account_id, + block_ref, + InputNotes::new_unchecked(vec![note]), + tx_args, + ) + .await + .map_err(NoteCheckerError::TransactionPreparation)?; + + // try to consume the provided note + match self.try_execute_notes(&mut tx_inputs).await { + // execution succeeded + Ok(()) => Ok(NoteConsumptionStatus::Consumable), + Err(tx_checker_error) => { + match tx_checker_error { + // execution failed on the preparation stage, before we actually executed the tx + TransactionCheckerError::TransactionPreparation(e) => { + Err(NoteCheckerError::TransactionPreparation(e)) + }, + // execution failed during the prologue + TransactionCheckerError::PrologueExecution(e) => { + Err(NoteCheckerError::PrologueExecution(e)) + }, + // execution failed during the note processing + TransactionCheckerError::NoteExecution { .. } => { + Ok(NoteConsumptionStatus::UnconsumableConditions) + }, + // execution failed during the epilogue + TransactionCheckerError::EpilogueExecution(epilogue_error) => { + Ok(handle_epilogue_error(epilogue_error)) + }, + } + }, + } + } + + // HELPER METHODS + // -------------------------------------------------------------------------------------------- + + /// Finds a set of executable notes and eliminates failed notes from the list in the process. + /// + /// The result contains some combination of the input notes partitioned by whether they + /// succeeded or failed to execute. + async fn find_executable_notes_by_elimination( + &self, + mut tx_inputs: TransactionInputs, + ) -> Result { + let mut candidate_notes = tx_inputs + .input_notes() + .iter() + .map(|note| note.clone().into_note()) + .collect::>(); + let mut failed_notes = Vec::new(); + + // Attempt to execute notes in a loop. Reduce the set of notes based on failures until + // either a set of notes executes without failure or the set of notes cannot be + // further reduced. + loop { + // Execute the candidate notes. + tx_inputs.set_input_notes(candidate_notes.clone()); + match self.try_execute_notes(&mut tx_inputs).await { + Ok(()) => { + // A full set of successful notes has been found. + let successful = candidate_notes; + return Ok(NoteConsumptionInfo::new(successful, failed_notes)); + }, + Err(TransactionCheckerError::NoteExecution { failed_note_index, error }) => { + // SAFETY: Failed note index is in bounds of the candidate notes. + let failed_note = candidate_notes.remove(failed_note_index); + failed_notes.push(FailedNote::new(failed_note, error)); + + // All possible candidate combinations have been attempted. + if candidate_notes.is_empty() { + return Ok(NoteConsumptionInfo::new(Vec::new(), failed_notes)); + } + // Continue and process the next set of candidates. + }, + Err(TransactionCheckerError::EpilogueExecution(_)) => { + let consumption_info = self + .find_largest_executable_combination( + candidate_notes, + failed_notes, + tx_inputs, + ) + .await; + return Ok(consumption_info); + }, + Err(TransactionCheckerError::PrologueExecution(err)) => { + return Err(NoteCheckerError::PrologueExecution(err)); + }, + Err(TransactionCheckerError::TransactionPreparation(err)) => { + return Err(NoteCheckerError::TransactionPreparation(err)); + }, + } + } + } + + /// Attempts to find the largest possible combination of notes that can execute successfully + /// together. + /// + /// This method incrementally tries combinations of increasing size (1 note, 2 notes, 3 notes, + /// etc.) and builds upon previously successful combinations to find the maximum executable + /// set. + async fn find_largest_executable_combination( + &self, + mut remaining_notes: Vec, + mut failed_notes: Vec, + mut tx_inputs: TransactionInputs, + ) -> NoteConsumptionInfo { + let mut successful_notes = Vec::new(); + let mut failed_note_index = BTreeMap::new(); + + // Iterate by note count: try 1 note, then 2, then 3, etc. + for size in 1..=remaining_notes.len() { + // Can't build a combination of size N without at least N-1 successful notes. + if successful_notes.len() < size - 1 { + break; + } + + // Try adding each remaining note to the current successful combination. + for (idx, note) in remaining_notes.iter().enumerate() { + successful_notes.push(note.clone()); + + tx_inputs.set_input_notes(successful_notes.clone()); + match self.try_execute_notes(&mut tx_inputs).await { + Ok(()) => { + // The successfully added note might have failed earlier. Remove it from the + // failed list. + failed_note_index.remove(¬e.id()); + // This combination succeeded; remove the most recently added note from + // the remaining set. + remaining_notes.remove(idx); + break; + }, + Err(error) => { + // This combination failed; remove the last note from the test set and + // continue to next note. + let failed_note = + successful_notes.pop().expect("successful notes should not be empty"); + // Record the failed note (overwrite previous failures for the relevant + // note). + failed_note_index + .insert(failed_note.id(), FailedNote::new(failed_note, error.into())); + }, + } + } + } + + // Append failed notes to the list of failed notes provided as input. + failed_notes.extend(failed_note_index.into_values()); + NoteConsumptionInfo::new(successful_notes, failed_notes) + } + + /// Attempts to execute a transaction with the provided input notes. + /// + /// This method executes the full transaction pipeline including prologue, note execution, + /// and epilogue phases. It returns `Ok(())` if all notes are successfully consumed, + /// or a specific [`NoteExecutionError`] indicating where and why the execution failed. + async fn try_execute_notes( + &self, + tx_inputs: &mut TransactionInputs, + ) -> Result<(), TransactionCheckerError> { + if tx_inputs.input_notes().is_empty() { + return Ok(()); + } + + let (mut host, stack_inputs, advice_inputs) = + self.0 + .prepare_transaction(tx_inputs) + .await + .map_err(TransactionCheckerError::TransactionPreparation)?; + + let processor = EXEC::new(stack_inputs, advice_inputs, self.0.exec_options); + let result = processor + .execute(&TransactionKernel::main(), &mut host) + .await + .map_err(map_execution_error); + + match result { + Ok(execution_output) => { + // Set the advice inputs from the successful execution as advice inputs for + // reexecution. This avoids calls to the data store (to load data lazily) that have + // already been done as part of this execution. + let (_, advice_map, merkle_store, _) = execution_output.advice.into_parts(); + let advice_inputs = AdviceInputs { + map: advice_map, + store: merkle_store, + ..Default::default() + }; + tx_inputs.set_advice_inputs(advice_inputs); + Ok(()) + }, + Err(error) => { + let notes = host.tx_progress().note_execution(); + + // Empty notes vector means that we didn't process the notes, so an error + // occurred. + if notes.is_empty() { + return Err(TransactionCheckerError::PrologueExecution(error)); + } + + let ((_, last_note_interval), success_notes) = + notes.split_last().expect("notes vector is not empty because of earlier check"); + + // If the interval end of the last note is specified, then an error occurred after + // notes processing. + if last_note_interval.end().is_some() { + Err(TransactionCheckerError::EpilogueExecution(error)) + } else { + // Return the index of the failed note. + let failed_note_index = success_notes.len(); + Err(TransactionCheckerError::NoteExecution { failed_note_index, error }) + } + }, + } + } +} + +// HELPER FUNCTIONS +// ================================================================================================ + +/// Handle the epilogue error during the note consumption check in the `can_consume` method. +/// +/// The goal of this helper function is to handle the cases where the account couldn't consume the +/// note because of some epilogue check failure, e.g. absence of the authenticator. +fn handle_epilogue_error(epilogue_error: TransactionExecutorError) -> NoteConsumptionStatus { + match epilogue_error { + // `Unauthorized` is returned for the multisig accounts if the transaction doesn't have + // enough signatures. + TransactionExecutorError::Unauthorized(_) + // `MissingAuthenticator` is returned for the account with the basic auth if the + // authenticator was not provided to the executor (UnreachableAuth). + | TransactionExecutorError::MissingAuthenticator => { + // Both these cases signal that there is a probability that the provided note could be + // consumed if the authentication is provided. + NoteConsumptionStatus::ConsumableWithAuthorization + }, + // TODO: apply additional checks to get the verbose error reason + _ => NoteConsumptionStatus::UnconsumableConditions, + } +} diff --git a/vendor/miden-tx/src/executor/program_executor.rs b/vendor/miden-tx/src/executor/program_executor.rs new file mode 100644 index 000000000..f4dc8eaa1 --- /dev/null +++ b/vendor/miden-tx/src/executor/program_executor.rs @@ -0,0 +1,52 @@ +use miden_processor::advice::AdviceInputs; +use miden_processor::{ + ExecutionError, + ExecutionOptions, + ExecutionOutput, + FastProcessor, + FutureMaybeSend, + Host, + Program, + StackInputs, +}; + +/// A transaction-scoped program executor used by +/// [`TransactionExecutor`](super::TransactionExecutor). +/// +/// TODO: Move this trait into `miden-vm` once the executor boundary is +/// consolidated there. +pub trait ProgramExecutor { + /// Create a new executor configured with the provided transaction inputs and options. + fn new( + stack_inputs: StackInputs, + advice_inputs: AdviceInputs, + options: ExecutionOptions, + ) -> Self + where + Self: Sized; + + /// Execute the provided program against the given host. + fn execute( + self, + program: &Program, + host: &mut H, + ) -> impl FutureMaybeSend>; +} + +impl ProgramExecutor for FastProcessor { + fn new( + stack_inputs: StackInputs, + advice_inputs: AdviceInputs, + options: ExecutionOptions, + ) -> Self { + FastProcessor::new_with_options(stack_inputs, advice_inputs, options) + } + + fn execute( + self, + program: &Program, + host: &mut H, + ) -> impl FutureMaybeSend> { + FastProcessor::execute(self, program, host) + } +} diff --git a/vendor/miden-tx/src/host/account_delta_tracker.rs b/vendor/miden-tx/src/host/account_delta_tracker.rs new file mode 100644 index 000000000..889470b73 --- /dev/null +++ b/vendor/miden-tx/src/host/account_delta_tracker.rs @@ -0,0 +1,91 @@ +use miden_protocol::Felt; +use miden_protocol::account::{ + AccountCode, + AccountDelta, + AccountId, + AccountVaultDelta, + PartialAccount, +}; + +use crate::host::storage_delta_tracker::StorageDeltaTracker; + +// ACCOUNT DELTA TRACKER +// ================================================================================================ + +/// Keeps track of changes made to the account during transaction execution. +/// +/// Currently, this tracks: +/// - Changes to the account storage, slots and maps. +/// - Changes to the account vault. +/// - Changes to the account nonce. +/// +/// TODO: implement tracking of: +/// - account code changes. +#[derive(Debug, Clone)] +pub struct AccountDeltaTracker { + account_id: AccountId, + storage: StorageDeltaTracker, + vault: AccountVaultDelta, + code: Option, + nonce_delta: Felt, +} + +impl AccountDeltaTracker { + /// Returns a new [AccountDeltaTracker] instantiated for the specified account. + pub fn new(account: &PartialAccount) -> Self { + let code = if account.is_new() { + Some(account.code().clone()) + } else { + None + }; + + Self { + account_id: account.id(), + storage: StorageDeltaTracker::new(account), + vault: AccountVaultDelta::default(), + code, + nonce_delta: Felt::ZERO, + } + } + + /// Returns true if the nonce delta is non-zero. + pub fn was_nonce_incremented(&self) -> bool { + self.nonce_delta != Felt::ZERO + } + + /// Increments the nonce delta by one. + pub fn increment_nonce(&mut self) { + self.nonce_delta += Felt::ONE; + } + + /// Returns a reference to the vault delta. + pub fn vault_delta(&self) -> &AccountVaultDelta { + &self.vault + } + + /// Returns a mutable reference to the vault delta. + pub fn vault_delta_mut(&mut self) -> &mut AccountVaultDelta { + &mut self.vault + } + + /// Returns a mutable reference to the current storage delta tracker. + pub fn storage(&mut self) -> &mut StorageDeltaTracker { + &mut self.storage + } + + /// Consumes `self` and returns the resulting [AccountDelta]. + /// + /// Normalizes the delta by removing entries for storage slots where the initial and new + /// value are equal. + pub fn into_delta(self) -> AccountDelta { + let account_id = self.account_id; + let nonce_delta = self.nonce_delta; + + let storage_delta = self.storage.into_delta(); + let vault_delta = self.vault; + + AccountDelta::new(account_id, storage_delta, vault_delta, nonce_delta) + .expect("account delta created in delta tracker should be valid") + .with_code(self.code) + } +} diff --git a/vendor/miden-tx/src/host/account_procedures.rs b/vendor/miden-tx/src/host/account_procedures.rs new file mode 100644 index 000000000..0a74d232e --- /dev/null +++ b/vendor/miden-tx/src/host/account_procedures.rs @@ -0,0 +1,69 @@ +use miden_protocol::account::AccountCode; + +use super::{BTreeMap, Word}; +use crate::errors::TransactionKernelError; + +// ACCOUNT PROCEDURE INDEX MAP +// ================================================================================================ + +/// A map of maps { acct_code_commitment |-> { proc_root |-> proc_index } } for all known +/// procedures of account interfaces for all accounts expected to be invoked during transaction +/// execution. +#[derive(Debug, Clone, Default)] +pub struct AccountProcedureIndexMap(BTreeMap>); + +impl AccountProcedureIndexMap { + /// Returns a new [`AccountProcedureIndexMap`] instantiated with account procedures from the + /// provided iterator of [`AccountCode`]. + pub fn new<'code>(account_codes: impl IntoIterator) -> Self { + let mut index_map = Self::default(); + + for account_code in account_codes { + // Insert each account procedures only once. + if !index_map.0.contains_key(&account_code.commitment()) { + index_map.insert_code(account_code); + } + } + + index_map + } + + /// Inserts the procedures from the provided [`AccountCode`] into the advice inputs, using + /// [`AccountCode::commitment`] as the key. + /// + /// The resulting instance will map the account code commitment to a mapping of + /// `proc_root |-> proc_index` for any account that is expected to be involved in the + /// transaction, enabling fast procedure index lookups at runtime. + pub fn insert_code(&mut self, code: &AccountCode) { + let mut procedure_map = BTreeMap::new(); + for (proc_idx, proc_root) in code.procedures().iter().enumerate() { + // SAFETY: AccountCode::MAX_NUM_PROCEDURES is 256 and so the highest possible index is + // 255. + let proc_idx = + u8::try_from(proc_idx).expect("account code should contain at most 256 procedures"); + procedure_map.insert(*proc_root.mast_root(), proc_idx); + } + + self.0.insert(code.commitment(), procedure_map); + } + + /// Returns the index of the requested procedure root in the account code identified by the + /// provided commitment. + /// + /// # Errors + /// + /// Returns an error if: + /// - the requested procedure is not present in this map. + pub fn get_proc_index( + &self, + code_commitment: Word, + procedure_root: Word, + ) -> Result { + self.0 + .get(&code_commitment) + .ok_or(TransactionKernelError::UnknownCodeCommitment(code_commitment))? + .get(&procedure_root) + .cloned() + .ok_or(TransactionKernelError::UnknownAccountProcedure(procedure_root)) + } +} diff --git a/vendor/miden-tx/src/host/kernel_process.rs b/vendor/miden-tx/src/host/kernel_process.rs new file mode 100644 index 000000000..dc8ec218a --- /dev/null +++ b/vendor/miden-tx/src/host/kernel_process.rs @@ -0,0 +1,344 @@ +use miden_processor::{ExecutionError, Felt, ProcessorState}; +use miden_protocol::Word; +use miden_protocol::account::{AccountId, StorageSlotId, StorageSlotType}; +use miden_protocol::note::{NoteId, NoteStorage}; +use miden_protocol::transaction::memory::{ + ACCOUNT_STACK_TOP_PTR, + ACCT_CODE_COMMITMENT_OFFSET, + ACCT_ID_PREFIX_IDX, + ACCT_ID_SUFFIX_IDX, + ACCT_STORAGE_SLOT_ID_PREFIX_OFFSET, + ACCT_STORAGE_SLOT_ID_SUFFIX_OFFSET, + ACCT_STORAGE_SLOT_TYPE_OFFSET, + ACCT_STORAGE_SLOT_VALUE_OFFSET, + ACTIVE_INPUT_NOTE_PTR, + NATIVE_NUM_ACCT_STORAGE_SLOTS_PTR, + NUM_OUTPUT_NOTES_PTR, +}; + +use crate::errors::TransactionKernelError; + +// TRANSACTION KERNEL PROCESS +// ================================================================================================ + +pub(super) trait TransactionKernelProcess { + /// Returns the pointer to the active account. + fn get_active_account_ptr(&self) -> Result; + + /// Returns the [`AccountId`] of the active account. + fn get_active_account_id(&self) -> Result; + + /// Returns the account code commitment of the active account. + fn get_active_account_code_commitment(&self) -> Result; + + #[allow(dead_code)] + fn get_num_storage_slots(&self) -> Result; + + /// Returns the current number of output notes. + fn get_num_output_notes(&self) -> u64; + + fn get_vault_root(&self, vault_root_ptr: Felt) -> Result; + + fn get_active_note_id(&self) -> Result, TransactionKernelError>; + + fn get_storage_slot( + &self, + slot_ptr: Felt, + ) -> Result<(StorageSlotId, StorageSlotType, Word), TransactionKernelError>; + + fn read_note_recipient_info_from_adv_map( + &self, + recipient_digest: Word, + ) -> Result<(NoteStorage, Word, Word), TransactionKernelError>; + + fn read_note_storage_from_adv_map( + &self, + storage_commitment: &Word, + ) -> Result; + + fn has_advice_map_entry(&self, key: Word) -> bool; + + /// Returns `true` if the advice provider has a merkle path for the provided root and leaf + /// index, `false` otherwise. + fn has_merkle_path( + &self, + root: Word, + leaf_index: Felt, + ) -> Result; +} + +impl<'a> TransactionKernelProcess for ProcessorState<'a> { + fn get_active_account_ptr(&self) -> Result { + let account_stack_top_ptr = + self.get_mem_value(self.ctx(), ACCOUNT_STACK_TOP_PTR).ok_or_else(|| { + TransactionKernelError::other("account stack top ptr should be initialized") + })?; + let account_stack_top_ptr = u32::try_from(account_stack_top_ptr.as_canonical_u64()) + .map_err(|_| { + TransactionKernelError::other("account stack top ptr should fit into a u32") + })?; + + let active_account_ptr = self + .get_mem_value(self.ctx(), account_stack_top_ptr) + .ok_or_else(|| TransactionKernelError::other("account id should be initialized"))?; + u32::try_from(active_account_ptr.as_canonical_u64()) + .map_err(|_| TransactionKernelError::other("active account ptr should fit into a u32")) + } + + fn get_active_account_id(&self) -> Result { + let active_account_ptr = self.get_active_account_ptr()?; + let active_account_id_and_nonce = self + .get_mem_word(self.ctx(), active_account_ptr) + .map_err(|_| { + TransactionKernelError::other("active account ptr should be word-aligned") + })? + .ok_or_else(|| { + TransactionKernelError::other("active account id should be initialized") + })?; + + AccountId::try_from_elements( + active_account_id_and_nonce[ACCT_ID_SUFFIX_IDX], + active_account_id_and_nonce[ACCT_ID_PREFIX_IDX], + ) + .map_err(|_| { + TransactionKernelError::other( + "active account id ptr should point to a valid account ID", + ) + }) + } + + fn get_active_account_code_commitment(&self) -> Result { + let active_account_ptr = self.get_active_account_ptr()?; + let code_commitment = self + .get_mem_word(self.ctx(), active_account_ptr + ACCT_CODE_COMMITMENT_OFFSET) + .map_err(|err| { + TransactionKernelError::other_with_source( + "failed to read code commitment from memory", + err, + ) + })? + .ok_or_else(|| { + TransactionKernelError::other("active account code commitment was not initialized") + })?; + + Ok(code_commitment) + } + + /// Returns the number of storage slots initialized for the active account. + /// + /// # Errors + /// Returns an error if the memory location supposed to contain the account storage slot number + /// has not been initialized. + fn get_num_storage_slots(&self) -> Result { + let num_storage_slots_felt = self + .get_mem_value(self.ctx(), NATIVE_NUM_ACCT_STORAGE_SLOTS_PTR) + .ok_or(TransactionKernelError::AccountStorageSlotsNumMissing( + NATIVE_NUM_ACCT_STORAGE_SLOTS_PTR, + ))?; + + Ok(num_storage_slots_felt.as_canonical_u64()) + } + + fn get_num_output_notes(&self) -> u64 { + // Read the number from memory or default to 0 if the location hasn't been accessed + // previously (e.g. when no notes have been created yet). + self.get_mem_value(self.ctx(), NUM_OUTPUT_NOTES_PTR) + .map(|num_output_notes| num_output_notes.as_canonical_u64()) + .unwrap_or(0) + } + + /// Returns the ID of the active note, or None if the note execution hasn't started yet or has + /// already ended. + /// + /// # Errors + /// Returns an error if the address of the active note is invalid (e.g., greater than + /// `u32::MAX`). + fn get_active_note_id(&self) -> Result, TransactionKernelError> { + // get the note address in `Felt` or return `None` if the address hasn't been accessed + // previously. + let note_address_felt = match self.get_mem_value(self.ctx(), ACTIVE_INPUT_NOTE_PTR) { + Some(addr) => addr, + None => return Ok(None), + }; + // convert note address into u32 + let note_address = u32::try_from(note_address_felt.as_canonical_u64()).map_err(|_| { + TransactionKernelError::other(format!( + "failed to convert {note_address_felt} into a memory address (u32)" + )) + })?; + // if `note_address` == 0 note execution has ended and there is no valid note address + if note_address == 0 { + Ok(None) + } else { + Ok(self + .get_mem_word(self.ctx(), note_address) + .map_err(|err| { + TransactionKernelError::other_with_source( + "failed to read note address", + ExecutionError::MemoryErrorNoCtx(err), + ) + })? + .map(NoteId::from_raw)) + } + } + + /// Returns the vault root at the provided pointer. + fn get_vault_root(&self, vault_root_ptr: Felt) -> Result { + let vault_root_ptr = u32::try_from(vault_root_ptr.as_canonical_u64()).map_err(|_err| { + TransactionKernelError::other(format!( + "vault root ptr should fit into a u32, but was {vault_root_ptr}" + )) + })?; + self.get_mem_word(self.ctx(), vault_root_ptr) + .map_err(|_err| { + TransactionKernelError::other(format!( + "vault root ptr {vault_root_ptr} is not word-aligned" + )) + })? + .ok_or_else(|| { + TransactionKernelError::other(format!( + "vault root ptr {vault_root_ptr} was not initialized" + )) + }) + } + + fn get_storage_slot( + &self, + slot_ptr: Felt, + ) -> Result<(StorageSlotId, StorageSlotType, Word), TransactionKernelError> { + let slot_ptr = u32::try_from(slot_ptr.as_canonical_u64()).map_err(|_err| { + TransactionKernelError::other(format!( + "slot ptr should fit into a u32, but was {slot_ptr}" + )) + })?; + + let slot_metadata = self + .get_mem_word(self.ctx(), slot_ptr) + .map_err(|err| { + TransactionKernelError::other_with_source( + format!("misaligned slot ptr {slot_ptr}"), + err, + ) + })? + .ok_or_else(|| { + TransactionKernelError::other(format!("slot ptr {slot_ptr} is uninitialized")) + })?; + + let slot_value_ptr = slot_ptr + ACCT_STORAGE_SLOT_VALUE_OFFSET as u32; + let slot_value = self + .get_mem_word(self.ctx(), slot_value_ptr) + .map_err(|err| { + TransactionKernelError::other_with_source( + format!("misaligned slot value ptr {slot_value_ptr}"), + err, + ) + })? + .ok_or_else(|| { + TransactionKernelError::other(format!( + "slot value ptr {slot_value_ptr} is uninitialized" + )) + })?; + + let slot_type = slot_metadata[ACCT_STORAGE_SLOT_TYPE_OFFSET as usize]; + let slot_type = u8::try_from(slot_type.as_canonical_u64()).map_err(|err| { + TransactionKernelError::other(format!("failed to convert {slot_type} into u8: {err}")) + })?; + let slot_type = StorageSlotType::try_from(slot_type).map_err(|err| { + TransactionKernelError::other_with_source( + format!("failed to convert {slot_type} into storage slot type",), + err, + ) + })?; + + let suffix = slot_metadata[ACCT_STORAGE_SLOT_ID_SUFFIX_OFFSET as usize]; + let prefix = slot_metadata[ACCT_STORAGE_SLOT_ID_PREFIX_OFFSET as usize]; + let slot_id = StorageSlotId::new(suffix, prefix); + + Ok((slot_id, slot_type, slot_value)) + } + + fn read_note_recipient_info_from_adv_map( + &self, + recipient_digest: Word, + ) -> Result<(NoteStorage, Word, Word), TransactionKernelError> { + let (sn_script_hash, storage_commitment) = + read_double_word_from_adv_map(self, recipient_digest)?; + let (sn_hash, script_root) = read_double_word_from_adv_map(self, sn_script_hash)?; + let (serial_num, _) = read_double_word_from_adv_map(self, sn_hash)?; + + let inputs = self.read_note_storage_from_adv_map(&storage_commitment)?; + + Ok((inputs, script_root, serial_num)) + } + + /// Extracts and validates note storage from the advice provider. + fn read_note_storage_from_adv_map( + &self, + storage_commitment: &Word, + ) -> Result { + let inputs_data = self.advice_provider().get_mapped_values(storage_commitment); + + match inputs_data { + None => Ok(NoteStorage::default()), + Some(storage_items) => { + let note_storage = NoteStorage::new(storage_items.to_vec()) + .map_err(TransactionKernelError::MalformedNoteStorage)?; + + if ¬e_storage.commitment() == storage_commitment { + Ok(note_storage) + } else { + Err(TransactionKernelError::InvalidNoteStorage { + expected: *storage_commitment, + actual: note_storage.commitment(), + }) + } + }, + } + } + + fn has_advice_map_entry(&self, key: Word) -> bool { + self.advice_provider().get_mapped_values(&key).is_some() + } + + fn has_merkle_path( + &self, + root: Word, + leaf_index: Felt, + ) -> Result { + self.advice_provider() + .has_merkle_path(root, Felt::from(TREE_DEPTH), leaf_index) + .map_err(|err| { + TransactionKernelError::other_with_source( + "failed to check for merkle path presence in advice provider", + err, + ) + }) + } +} + +// HELPER FUNCTIONS +// ================================================================================================ + +/// Reads a double word (two [`Word`]s, 8 [`Felt`]s total) from the advice map. +/// +/// # Errors +/// Returns an error if the key is not present in the advice map or if the data is malformed +/// (not exactly 8 elements). +fn read_double_word_from_adv_map( + process: &ProcessorState, + key: Word, +) -> Result<(Word, Word), TransactionKernelError> { + let data = process + .advice_provider() + .get_mapped_values(&key) + .ok_or_else(|| TransactionKernelError::MalformedRecipientData(vec![]))?; + + if data.len() != 8 { + return Err(TransactionKernelError::MalformedRecipientData(data.to_vec())); + } + + let first_word = Word::new([data[0], data[1], data[2], data[3]]); + let second_word = Word::new([data[4], data[5], data[6], data[7]]); + + Ok((first_word, second_word)) +} diff --git a/vendor/miden-tx/src/host/link_map.rs b/vendor/miden-tx/src/host/link_map.rs new file mode 100644 index 000000000..675696981 --- /dev/null +++ b/vendor/miden-tx/src/host/link_map.rs @@ -0,0 +1,349 @@ +use alloc::vec::Vec; +use core::cmp::Ordering; + +use miden_processor::advice::AdviceMutation; +use miden_processor::{ContextId, ExecutionOutput, ProcessorState}; +use miden_protocol::{Felt, Word, ZERO}; + +// LINK MAP +// ================================================================================================ + +/// A map based on a sorted linked list. +/// +/// This type enables access to the list in kernel memory. +/// +/// See link_map.masm for docs. +/// +/// # Warning +/// +/// The functions on this type assume that the provided map_ptr points to a valid map in the +/// provided memory viewer. If those assumptions are violated, the functions may panic. +#[derive(Clone, Copy)] +pub struct LinkMap<'process> { + map_ptr: u32, + mem: &'process MemoryViewer<'process>, +} + +impl<'process> LinkMap<'process> { + // CONSTRUCTOR + // -------------------------------------------------------------------------------------------- + + /// Creates a new link map from the provided map_ptr in the provided process. + pub fn new(map_ptr: Felt, mem: &'process MemoryViewer<'process>) -> Self { + let map_ptr: u32 = + u32::try_from(map_ptr.as_canonical_u64()).expect("map_ptr must be a valid u32"); + + Self { map_ptr, mem } + } + + // PUBLIC METHODS + // -------------------------------------------------------------------------------------------- + + /// Handles a `LINK_MAP_SET_EVENT` emitted from a VM. + /// + /// Expected operand stack state before: [map_ptr, KEY, NEW_VALUE] + /// Advice stack state after: [entry_ptr, set_operation] + pub fn handle_set_event(process: &ProcessorState<'_>) -> Vec { + let map_ptr = process.get_stack_item(1); + let map_key = process.get_stack_word(2); + + let mem_viewer = MemoryViewer::ProcessState(process); + let link_map = LinkMap::new(map_ptr, &mem_viewer); + + let (set_op, entry_ptr) = link_map.compute_set_operation(map_key); + + vec![AdviceMutation::extend_stack([Felt::from(entry_ptr), Felt::from(set_op as u8)])] + } + + /// Handles a `LINK_MAP_GET_EVENT` emitted from a VM. + /// + /// Expected operand stack state before: [map_ptr, KEY] + /// Advice stack state after: [entry_ptr, get_operation] + pub fn handle_get_event(process: &ProcessorState<'_>) -> Vec { + let map_ptr = process.get_stack_item(1); + let map_key = process.get_stack_word(2); + + let mem_viewer = MemoryViewer::ProcessState(process); + let link_map = LinkMap::new(map_ptr, &mem_viewer); + let (get_op, entry_ptr) = link_map.compute_get_operation(map_key); + + vec![AdviceMutation::extend_stack([Felt::from(entry_ptr), Felt::from(get_op as u8)])] + } + + /// Returns `true` if the map is empty, `false` otherwise. + pub fn is_empty(&self) -> bool { + self.head().is_none() + } + + /// Returns an iterator over the link map entries. + pub fn iter(&self) -> impl Iterator { + LinkMapIter { + current_entry_ptr: self.head().unwrap_or(0), + map: *self, + } + } + + // PRIVATE METHODS + // -------------------------------------------------------------------------------------------- + + /// Returns the entry pointer at the head of the map or `None` if the map is empty. + fn head(&self) -> Option { + // Returns None if the value was either not yet initialized or points to 0. + // It can point to 0 for example if a get operation is executed before a set operation, + // which initializes the value in memory to 0 but does not change it. + self.mem.get_kernel_mem_element(self.map_ptr).and_then(|head_ptr| { + if head_ptr == ZERO { + None + } else { + Some( + u32::try_from(head_ptr.as_canonical_u64()) + .expect("head ptr should be a valid ptr"), + ) + } + }) + } + + /// Returns the [`Entry`] at the given pointer. + fn entry(&self, entry_ptr: u32) -> Entry { + let key = self.key(entry_ptr); + let (value0, value1) = self.value(entry_ptr); + let metadata = self.metadata(entry_ptr); + + Entry { + ptr: entry_ptr, + metadata, + key, + value0, + value1, + } + } + + /// Returns the key of the entry at the given pointer. + fn key(&self, entry_ptr: u32) -> Word { + self.mem + .get_kernel_mem_word(entry_ptr + 4) + .expect("entry pointer should be valid") + } + + /// Returns the values of the entry at the given pointer. + fn value(&self, entry_ptr: u32) -> (Word, Word) { + let value0 = self + .mem + .get_kernel_mem_word(entry_ptr + 8) + .expect("entry pointer should be valid"); + let value1 = self + .mem + .get_kernel_mem_word(entry_ptr + 12) + .expect("entry pointer should be valid"); + (value0, value1) + } + + /// Returns the metadata of the entry at the given pointer. + fn metadata(&self, entry_ptr: u32) -> EntryMetadata { + let entry_metadata = + self.mem.get_kernel_mem_word(entry_ptr).expect("entry pointer should be valid"); + + let map_ptr = entry_metadata[0]; + let map_ptr = u32::try_from(map_ptr.as_canonical_u64()) + .expect("entry_ptr should point to a u32 map_ptr"); + + let prev_entry_ptr = entry_metadata[1]; + let prev_entry_ptr = u32::try_from(prev_entry_ptr.as_canonical_u64()) + .expect("entry_ptr should point to a u32 prev_entry_ptr"); + + let next_entry_ptr = entry_metadata[2]; + let next_entry_ptr = u32::try_from(next_entry_ptr.as_canonical_u64()) + .expect("entry_ptr should point to a u32 next_entry_ptr"); + + EntryMetadata { map_ptr, prev_entry_ptr, next_entry_ptr } + } + + /// Computes what needs to be done to insert the given key into the link map. + /// + /// If the key already exists in the map, then its value must be updated and + /// [`SetOperation::Update`] and the pointer to the existing entry are returned. + /// + /// If the key does not exist in the map, find the place where it has to be inserted. This can + /// be at the head of the list ([`SetOperation::InsertAtHead`]) if the key is smaller than all + /// existing keys or if the map is empty. Otherwise it is after an existing entry + /// ([`SetOperation::InsertAfterEntry`]) in which case the key must be greater than the entry's + /// key after which it is inserted and smaller than the entry before which it is inserted + /// (unless it is the end of the map). + fn compute_set_operation(&self, key: Word) -> (SetOperation, u32) { + let Some(current_head) = self.head() else { + return (SetOperation::InsertAtHead, 0); + }; + + let mut last_entry_ptr: u32 = current_head; + + for entry in self.iter() { + match key.cmp(&entry.key) { + Ordering::Equal => { + return (SetOperation::Update, entry.ptr); + }, + Ordering::Less => { + if entry.ptr == current_head { + return (SetOperation::InsertAtHead, entry.ptr); + } + + break; + }, + Ordering::Greater => { + last_entry_ptr = entry.ptr; + }, + } + } + + (SetOperation::InsertAfterEntry, last_entry_ptr) + } + + /// Computes a get operation for a key in a link map. + /// + /// If the key exists, then [`GetOperation::Found`] is returned and the pointer to it. + /// + /// If it does not exist, its absence must be proven, otherwise the host could lie. To do that, + /// the in-kernel link map validates that the key is not in the list, so this function returns + /// information pointing to the entry where the key would be if it existed. + /// + /// The way to compute this is the same as a set operation, so this function simply remaps its + /// output. + fn compute_get_operation(&self, key: Word) -> (GetOperation, u32) { + let (set_op, entry_ptr) = self.compute_set_operation(key); + let get_op = match set_op { + SetOperation::Update => GetOperation::Found, + SetOperation::InsertAtHead => GetOperation::AbsentAtHead, + SetOperation::InsertAfterEntry => GetOperation::AbsentAfterEntry, + }; + (get_op, entry_ptr) + } +} + +// LINK MAP ITER +// ================================================================================================ + +/// An iterator over a [`LinkMap`]. +struct LinkMapIter<'process> { + current_entry_ptr: u32, + map: LinkMap<'process>, +} + +impl<'process> Iterator for LinkMapIter<'process> { + type Item = Entry; + + fn next(&mut self) -> Option { + if self.current_entry_ptr == 0 { + return None; + } + + let current_entry = self.map.entry(self.current_entry_ptr); + + self.current_entry_ptr = current_entry.metadata.next_entry_ptr; + + Some(current_entry) + } +} + +// LINK MAP TYPES +// ================================================================================================ + +/// An entry in a [`LinkMap`]. +/// +/// Exposed for testing purposes only. +#[derive(Debug, Clone, Copy)] +pub struct Entry { + pub ptr: u32, + pub metadata: EntryMetadata, + pub key: Word, + pub value0: Word, + pub value1: Word, +} + +/// An entry's metadata in a [`LinkMap`]. +/// +/// Exposed for testing purposes only. +#[derive(Debug, Clone, Copy)] +pub struct EntryMetadata { + pub map_ptr: u32, + pub prev_entry_ptr: u32, + pub next_entry_ptr: u32, +} + +// HELPER TYPES +// ================================================================================================ + +/// The operation needed to get a key or prove its absence. +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +enum GetOperation { + Found = 0, + AbsentAtHead = 1, + AbsentAfterEntry = 2, +} + +/// The operation needed to set a key. +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +enum SetOperation { + Update = 0, + InsertAtHead = 1, + InsertAfterEntry = 2, +} + +// MEMORY VIEWER +// ================================================================================================ + +/// A abstraction over ways to view a process' memory. +/// +/// More specifically, it allows using a [`LinkMap`] both with a [`ProcessorState`], i.e. a process +/// that is actively executing and also an [`ExecutionOutput`], i.e. a process that has finished +/// execution. +/// +/// This should all go away again once we change a LinkMap's implementation to be based on an actual +/// map type instead of viewing a process' memory directly. +pub enum MemoryViewer<'mem> { + ProcessState(&'mem ProcessorState<'mem>), + ExecutionOutputs(&'mem ExecutionOutput), +} + +impl<'mem> MemoryViewer<'mem> { + /// Reads an element from transaction kernel memory. + fn get_kernel_mem_element(&self, addr: u32) -> Option { + match self { + MemoryViewer::ProcessState(process_state) => { + process_state.get_mem_value(ContextId::root(), addr) + }, + MemoryViewer::ExecutionOutputs(_execution_output) => { + // TODO: Use Memory::read_element once it no longer requires &mut self. + // https://github.com/0xMiden/miden-vm/issues/2237 + + // Copy of how Memory::read_element is implemented in Miden VM. + let idx = addr % miden_protocol::WORD_SIZE as u32; + let word_addr = addr - idx; + + Some(self.get_kernel_mem_word(word_addr)?[idx as usize]) + }, + } + } + + /// Reads a word from transaction kernel memory. + fn get_kernel_mem_word(&self, addr: u32) -> Option { + match self { + MemoryViewer::ProcessState(process_state) => process_state + .get_mem_word(ContextId::root(), addr) + .expect("address should be word-aligned"), + MemoryViewer::ExecutionOutputs(execution_output) => { + let tx_kernel_context = ContextId::root(); + let clk = 0u32; + + // Note that this never returns None even if the location is uninitialized, but the + // link map does not rely on this. + Some( + execution_output + .memory + .read_word(tx_kernel_context, Felt::from(addr), clk.into()) + .expect("expected address to be word-aligned"), + ) + }, + } + } +} diff --git a/vendor/miden-tx/src/host/mod.rs b/vendor/miden-tx/src/host/mod.rs new file mode 100644 index 000000000..b0f40dac3 --- /dev/null +++ b/vendor/miden-tx/src/host/mod.rs @@ -0,0 +1,483 @@ +mod account_delta_tracker; + +use account_delta_tracker::AccountDeltaTracker; +mod storage_delta_tracker; + +mod link_map; +pub use link_map::{LinkMap, MemoryViewer}; + +mod account_procedures; +pub use account_procedures::AccountProcedureIndexMap; + +pub(crate) mod note_builder; +use miden_protocol::CoreLibrary; +use miden_protocol::transaction::TransactionEventId; +use miden_protocol::vm::{EventId, EventName}; +use note_builder::OutputNoteBuilder; + +mod kernel_process; +use kernel_process::TransactionKernelProcess; + +mod script_mast_forest_store; +pub use script_mast_forest_store::ScriptMastForestStore; + +mod tx_progress; + +mod tx_event; +use alloc::boxed::Box; +use alloc::collections::BTreeMap; +use alloc::sync::Arc; +use alloc::vec::Vec; + +use miden_processor::advice::AdviceMutation; +use miden_processor::event::{EventError, EventHandlerRegistry}; +use miden_processor::mast::MastForest; +use miden_processor::trace::RowIndex; +use miden_processor::{Felt, MastForestStore, ProcessorState}; +use miden_protocol::Word; +use miden_protocol::account::{ + AccountCode, + AccountDelta, + AccountHeader, + AccountId, + AccountStorageHeader, + PartialAccount, + StorageMapKey, + StorageSlotHeader, + StorageSlotId, + StorageSlotName, +}; +use miden_protocol::asset::Asset; +use miden_protocol::note::{NoteAttachment, NoteId, NoteMetadata, NoteRecipient}; +use miden_protocol::transaction::{ + InputNote, + InputNotes, + RawOutputNote, + RawOutputNotes, + TransactionMeasurements, + TransactionSummary, +}; +pub(crate) use tx_event::{RecipientData, TransactionEvent, TransactionProgressEvent}; +pub use tx_progress::TransactionProgress; + +use crate::errors::TransactionKernelError; + +// TRANSACTION BASE HOST +// ================================================================================================ + +/// The base transaction host that implements shared behavior of all transaction host +/// implementations. +pub struct TransactionBaseHost<'store, STORE> { + /// MAST store which contains the code required to execute account code functions. + mast_store: &'store STORE, + + /// MAST store which contains the forests of all scripts involved in the transaction. These + /// include input note scripts and the transaction script, but not account code. + scripts_mast_store: ScriptMastForestStore, + + /// The header of the account at the beginning of transaction execution. + initial_account_header: AccountHeader, + + /// The storage header of the native account at the beginning of transaction execution. + initial_account_storage_header: AccountStorageHeader, + + /// Account state changes accumulated during transaction execution. + /// + /// The delta is updated by event handlers. + account_delta: AccountDeltaTracker, + + /// A map of the procedure MAST roots to the corresponding procedure indices for all the + /// account codes involved in the transaction (for native and foreign accounts alike). + acct_procedure_index_map: AccountProcedureIndexMap, + + /// Input notes consumed by the transaction. + input_notes: InputNotes, + + /// The list of notes created while executing a transaction stored as note_ptr |-> note_builder + /// map. + output_notes: BTreeMap, + + /// Handle the VM default events _before_ passing it to user defined ones. + core_lib_handlers: EventHandlerRegistry, +} + +impl<'store, STORE> TransactionBaseHost<'store, STORE> { + // CONSTRUCTORS + // -------------------------------------------------------------------------------------------- + + /// Creates a new [`TransactionBaseHost`] instance from the provided inputs. + pub fn new( + account: &PartialAccount, + input_notes: InputNotes, + mast_store: &'store STORE, + scripts_mast_store: ScriptMastForestStore, + acct_procedure_index_map: AccountProcedureIndexMap, + ) -> Self { + let core_lib_handlers = { + let mut registry = EventHandlerRegistry::new(); + + let core_lib = CoreLibrary::default(); + for (event_id, handler) in core_lib.handlers() { + registry + .register(event_id, handler) + .expect("There are no duplicates in the core library handlers"); + } + registry + }; + Self { + mast_store, + scripts_mast_store, + initial_account_header: account.into(), + initial_account_storage_header: account.storage().header().clone(), + account_delta: AccountDeltaTracker::new(account), + acct_procedure_index_map, + output_notes: BTreeMap::default(), + input_notes, + core_lib_handlers, + } + } + + // PUBLIC ACCESSORS + // -------------------------------------------------------------------------------------------- + + /// Returns the ID of the native account. + pub fn native_account_id(&self) -> AccountId { + self.initial_account_header().id() + } + + /// Returns a reference to the initial account header of the native account, which represents + /// the state at the beginning of the transaction. + pub fn initial_account_header(&self) -> &AccountHeader { + &self.initial_account_header + } + + /// Returns a reference to the initial storage header of the native account, which represents + /// the state at the beginning of the transaction. + pub fn initial_account_storage_header(&self) -> &AccountStorageHeader { + &self.initial_account_storage_header + } + + /// Returns the initial storage slot of the native account identified by [`StorageSlotId`], + /// which represents the state at the beginning of the transaction. + pub fn initial_account_storage_slot( + &self, + slot_id: StorageSlotId, + ) -> Result<&StorageSlotHeader, TransactionKernelError> { + self.initial_account_storage_header() + .find_slot_header_by_id(slot_id) + .ok_or_else(|| { + TransactionKernelError::other(format!( + "failed to find storage map with name {slot_id} in storage header" + )) + }) + } + + /// Returns a reference to the account delta tracker of this transaction host. + pub fn account_delta_tracker(&self) -> &AccountDeltaTracker { + &self.account_delta + } + + /// Clones the inner [`AccountDeltaTracker`] and converts it into an [`AccountDelta`]. + pub fn build_account_delta(&self) -> AccountDelta { + self.account_delta_tracker().clone().into_delta() + } + + /// Returns the input notes consumed in this transaction. + pub fn input_notes(&self) -> InputNotes { + self.input_notes.clone() + } + + /// Clones the inner [`OutputNoteBuilder`]s and returns the vector of created output notes that + /// are tracked by this host. + pub fn build_output_notes(&self) -> Vec { + self.output_notes.values().cloned().map(|builder| builder.build()).collect() + } + + /// Consumes `self` and returns the account delta, input and output notes. + pub fn into_parts(self) -> (AccountDelta, InputNotes, Vec) { + let output_notes = self.output_notes.into_values().map(|builder| builder.build()).collect(); + + (self.account_delta.into_delta(), self.input_notes, output_notes) + } + + // MUTATORS + // -------------------------------------------------------------------------------------------- + + /// Inserts an output note builder at the specified index. + /// + /// # Errors + /// + /// Returns an error if: + /// - a note builder already exists at the given index. + pub(super) fn insert_output_note_builder( + &mut self, + note_idx: usize, + note_builder: OutputNoteBuilder, + ) -> Result<(), TransactionKernelError> { + if self.output_notes.contains_key(¬e_idx) { + return Err(TransactionKernelError::other(format!( + "Attempted to create note builder for note index {} twice", + note_idx + ))); + } + self.output_notes.insert(note_idx, note_builder); + Ok(()) + } + + /// Inserts an [`OutputNoteBuilder`] into the output notes created from only the recipient + /// digest. + pub(super) fn output_note_from_recipient_digest( + &mut self, + note_idx: usize, + metadata: NoteMetadata, + recipient_digest: Word, + ) -> Result, TransactionKernelError> { + let note_builder = OutputNoteBuilder::from_recipient_digest(metadata, recipient_digest)?; + self.insert_output_note_builder(note_idx, note_builder)?; + + Ok(Vec::new()) + } + + /// Inserts an [`OutputNoteBuilder`] into the output notes created from the full + /// [`NoteRecipient`] object. + pub(super) fn output_note_from_recipient( + &mut self, + note_idx: usize, + metadata: NoteMetadata, + recipient: NoteRecipient, + ) -> Result, TransactionKernelError> { + let note_builder = OutputNoteBuilder::from_recipient(metadata, recipient); + self.insert_output_note_builder(note_idx, note_builder)?; + + Ok(Vec::new()) + } + + /// Loads the provided [`AccountCode`] into the host's [`AccountProcedureIndexMap`]. + pub fn load_foreign_account_code(&mut self, account_code: &AccountCode) { + self.acct_procedure_index_map.insert_code(account_code) + } + + // EVENT HANDLERS + // -------------------------------------------------------------------------------------------- + + /// Handles the event if the core lib event handler registry contains a handler with the emitted + /// event ID. + /// + /// Returns `Some` if the event was handled, `None` otherwise. + pub fn handle_core_lib_events( + &self, + process: &ProcessorState, + ) -> Result>, EventError> { + let event_id = EventId::from_felt(process.get_stack_item(0)); + if let Some(mutations) = self.core_lib_handlers.handle_event(event_id, process)? { + Ok(Some(mutations)) + } else { + Ok(None) + } + } + + /// Resolves an [`EventId`] to its corresponding [`EventName`], if known. + /// + /// First checks if the event is a core library event, then checks if it is a transaction + /// kernel event. + pub fn resolve_event(&self, event_id: EventId) -> Option<&EventName> { + if let Some(name) = self.core_lib_handlers.resolve_event(event_id) { + return Some(name); + } + + TransactionEventId::try_from(event_id) + .ok() + .map(|event_id| event_id.event_name()) + } + + /// Converts the provided signature into an advice mutation that pushes it onto the advice stack + /// as a response to an `AuthRequest` event. + pub fn on_auth_requested(&self, signature: Vec) -> Vec { + vec![AdviceMutation::extend_stack(signature)] + } + + /// Adds an asset to the output note identified by the note index. + pub fn on_note_before_add_asset( + &mut self, + note_idx: usize, + asset: Asset, + ) -> Result, TransactionKernelError> { + let note_builder = self.output_notes.get_mut(¬e_idx).ok_or_else(|| { + TransactionKernelError::other(format!("failed to find output note {note_idx}")) + })?; + + note_builder.add_asset(asset)?; + + Ok(Vec::new()) + } + + /// Sets the attachment on the output note identified by the note index. + pub fn on_note_before_set_attachment( + &mut self, + note_idx: usize, + attachment: NoteAttachment, + ) -> Result, TransactionKernelError> { + let note_builder = self.output_notes.get_mut(¬e_idx).ok_or_else(|| { + TransactionKernelError::other(format!("failed to find output note {note_idx}")) + })?; + + note_builder.set_attachment(attachment); + + Ok(Vec::new()) + } + + /// Pushes the index of the procedure root in the code identified by the commitment onto the + /// advice stack. + pub fn on_account_push_procedure_index( + &mut self, + code_commitment: Word, + procedure_root: Word, + ) -> Result, TransactionKernelError> { + let proc_idx = + self.acct_procedure_index_map.get_proc_index(code_commitment, procedure_root)?; + Ok(vec![AdviceMutation::extend_stack([Felt::from(proc_idx)])]) + } + + /// Handles the increment nonce event by incrementing the nonce delta by one. + pub fn on_account_after_increment_nonce( + &mut self, + ) -> Result, TransactionKernelError> { + if self.account_delta.was_nonce_incremented() { + return Err(TransactionKernelError::NonceCanOnlyIncrementOnce); + } + + self.account_delta.increment_nonce(); + + Ok(Vec::new()) + } + + // ACCOUNT STORAGE UPDATE HANDLERS + // -------------------------------------------------------------------------------------------- + + /// Tracks the insertion of an item in the account delta. + pub fn on_account_storage_after_set_item( + &mut self, + slot_name: StorageSlotName, + new_value: Word, + ) -> Result, TransactionKernelError> { + self.account_delta.storage().set_item(slot_name, new_value); + + Ok(Vec::new()) + } + + /// Tracks the insertion of a storage map item in the account delta. + pub fn on_account_storage_after_set_map_item( + &mut self, + slot_name: StorageSlotName, + key: StorageMapKey, + old_map_value: Word, + new_map_value: Word, + ) -> Result, TransactionKernelError> { + self.account_delta + .storage() + .set_map_item(slot_name, key, old_map_value, new_map_value); + + Ok(Vec::new()) + } + + // ACCOUNT VAULT UPDATE HANDLERS + // -------------------------------------------------------------------------------------------- + + /// Tracks the addition of an asset to the account vault in the account delta. + pub fn on_account_vault_after_add_asset( + &mut self, + asset: Asset, + ) -> Result, TransactionKernelError> { + self.account_delta + .vault_delta_mut() + .add_asset(asset) + .map_err(TransactionKernelError::AccountDeltaAddAssetFailed)?; + + Ok(Vec::new()) + } + + /// Tracks the removal of an asset from the account vault in the account delta. + pub fn on_account_vault_after_remove_asset( + &mut self, + asset: Asset, + ) -> Result, TransactionKernelError> { + self.account_delta + .vault_delta_mut() + .remove_asset(asset) + .map_err(TransactionKernelError::AccountDeltaRemoveAssetFailed)?; + + Ok(Vec::new()) + } + + // HELPER FUNCTIONS + // -------------------------------------------------------------------------------------------- + + /// Builds a [`TransactionSummary`] from the current host's state and validates it against the + /// provided commitments. + pub(crate) fn build_tx_summary( + &self, + account_delta_commitment: Word, + input_notes_commitment: Word, + output_notes_commitment: Word, + salt: Word, + ) -> Result { + let account_delta = self.build_account_delta(); + let input_notes = self.input_notes(); + let output_notes_vec = self.build_output_notes(); + let output_notes = RawOutputNotes::new(output_notes_vec).map_err(|err| { + TransactionKernelError::TransactionSummaryConstructionFailed(Box::new(err)) + })?; + + // Validate commitments + let actual_account_delta_commitment = account_delta.to_commitment(); + if actual_account_delta_commitment != account_delta_commitment { + return Err(TransactionKernelError::TransactionSummaryCommitmentMismatch( + format!( + "expected account delta commitment to be {actual_account_delta_commitment} but was {account_delta_commitment}" + ) + .into(), + )); + } + + let actual_input_notes_commitment = input_notes.commitment(); + if actual_input_notes_commitment != input_notes_commitment { + return Err(TransactionKernelError::TransactionSummaryCommitmentMismatch( + format!( + "expected input notes commitment to be {actual_input_notes_commitment} but was {input_notes_commitment}" + ) + .into(), + )); + } + + let actual_output_notes_commitment = output_notes.commitment(); + if actual_output_notes_commitment != output_notes_commitment { + return Err(TransactionKernelError::TransactionSummaryCommitmentMismatch( + format!( + "expected output notes commitment to be {actual_output_notes_commitment} but was {output_notes_commitment}" + ) + .into(), + )); + } + + Ok(TransactionSummary::new(account_delta, input_notes, output_notes, salt)) + } + + /// Returns the underlying store of the base host. + pub fn store(&self) -> &'store STORE { + self.mast_store + } +} + +impl<'store, STORE> TransactionBaseHost<'store, STORE> +where + STORE: MastForestStore, +{ + /// Returns the [`MastForest`] that contains the procedure with the given `procedure_root`. + pub fn get_mast_forest(&self, procedure_root: &Word) -> Option> { + // Search in the note MAST forest store, otherwise fall back to the user-provided store + match self.scripts_mast_store.get(procedure_root) { + Some(forest) => Some(forest), + None => self.mast_store.get(procedure_root), + } + } +} diff --git a/vendor/miden-tx/src/host/note_builder.rs b/vendor/miden-tx/src/host/note_builder.rs new file mode 100644 index 000000000..d392c16b5 --- /dev/null +++ b/vendor/miden-tx/src/host/note_builder.rs @@ -0,0 +1,143 @@ +use alloc::vec::Vec; + +use miden_protocol::asset::Asset; +use miden_protocol::errors::NoteError; +use miden_protocol::note::{ + Note, + NoteAssets, + NoteAttachment, + NoteMetadata, + NoteRecipient, + PartialNote, +}; + +use super::{RawOutputNote, Word}; +use crate::errors::TransactionKernelError; + +// OUTPUT NOTE BUILDER +// ================================================================================================ + +/// Builder of an output note, provided primarily to enable adding assets to a note incrementally. +/// +/// Assets are accumulated in a `Vec` and the final `NoteAssets` is only constructed when +/// [`build`](Self::build) is called. This avoids recomputing the commitment hash on every asset +/// addition. +#[derive(Debug, Clone)] +pub struct OutputNoteBuilder { + metadata: NoteMetadata, + assets: Vec, + recipient_digest: Word, + recipient: Option, +} + +impl OutputNoteBuilder { + // CONSTRUCTOR + // -------------------------------------------------------------------------------------------- + + /// Returns a new [OutputNoteBuilder] from the provided metadata, recipient digest, and optional + /// recipient. + /// + /// # Errors + /// + /// Returns an error if: + /// - the note is public. + pub fn from_recipient_digest( + metadata: NoteMetadata, + recipient_digest: Word, + ) -> Result { + // For public notes, we must have a recipient. + if !metadata.is_private() { + return Err(TransactionKernelError::PublicNoteMissingDetails( + metadata, + recipient_digest, + )); + } + + Ok(Self { + metadata, + recipient_digest, + recipient: None, + assets: Vec::new(), + }) + } + + /// Returns a new [`OutputNoteBuilder`] from the provided metadata and recipient. + pub fn from_recipient(metadata: NoteMetadata, recipient: NoteRecipient) -> Self { + Self { + metadata, + recipient_digest: recipient.digest(), + recipient: Some(recipient), + assets: Vec::new(), + } + } + + // STATE MUTATORS + // -------------------------------------------------------------------------------------------- + + /// Adds the specified asset to the note. + /// + /// # Errors + /// Returns an error if adding the asset to the note fails. This can happen for the following + /// reasons: + /// - The same non-fungible asset is already added to the note. + /// - A fungible asset issued by the same faucet is already added to the note and adding both + /// assets together results in an invalid asset. + /// - Adding the asset to the note will push the list beyond the [NoteAssets::MAX_NUM_ASSETS] + /// limit. + pub fn add_asset(&mut self, asset: Asset) -> Result<(), TransactionKernelError> { + // Check if an asset issued by the same faucet already exists in the list of assets. + if let Some(own_asset) = self.assets.iter_mut().find(|a| a.is_same(&asset)) { + match own_asset { + Asset::Fungible(f_own_asset) => { + // If a fungible asset issued by the same faucet is found, try to add the + // provided asset to it. + let new_asset = f_own_asset + .add(asset.unwrap_fungible()) + .map_err(NoteError::AddFungibleAssetBalanceError) + .map_err(TransactionKernelError::FailedToAddAssetToNote)?; + *own_asset = Asset::Fungible(new_asset); + }, + Asset::NonFungible(nf_asset) => { + return Err(TransactionKernelError::FailedToAddAssetToNote( + NoteError::DuplicateNonFungibleAsset(*nf_asset), + )); + }, + } + } else { + // If the asset is not in the list, add it to the list. + self.assets.push(asset); + if self.assets.len() > NoteAssets::MAX_NUM_ASSETS { + return Err(TransactionKernelError::FailedToAddAssetToNote( + NoteError::TooManyAssets(self.assets.len()), + )); + } + } + + Ok(()) + } + + /// Overwrites the attachment in the note's metadata. + pub fn set_attachment(&mut self, attachment: NoteAttachment) { + self.metadata.set_attachment(attachment); + } + + /// Converts this builder to an [OutputNote]. + /// + /// Depending on the available information, this may result in [`OutputNote::Full`] or + /// [`OutputNote::Partial`] notes. + pub fn build(self) -> RawOutputNote { + let assets = NoteAssets::new(self.assets) + .expect("assets should be valid since add_asset validates them"); + + match self.recipient { + Some(recipient) => { + let note = Note::new(assets, self.metadata, recipient); + RawOutputNote::Full(note) + }, + None => { + let note = PartialNote::new(self.metadata, self.recipient_digest, assets); + RawOutputNote::Partial(note) + }, + } + } +} diff --git a/vendor/miden-tx/src/host/script_mast_forest_store.rs b/vendor/miden-tx/src/host/script_mast_forest_store.rs new file mode 100644 index 000000000..6a8e82054 --- /dev/null +++ b/vendor/miden-tx/src/host/script_mast_forest_store.rs @@ -0,0 +1,68 @@ +use alloc::collections::BTreeMap; +use alloc::sync::Arc; + +use miden_processor::MastForestStore; +use miden_protocol::Word; +use miden_protocol::assembly::mast::MastForest; +use miden_protocol::note::NoteScript; +use miden_protocol::transaction::TransactionScript; +use miden_protocol::vm::AdviceMap; + +/// Stores the MAST forests for a set of scripts (both note scripts and transaction scripts). +/// +/// A [ScriptMastForestStore] is meant to exclusively store MAST forests related to both +/// transaction and input note scripts. +#[derive(Debug, Clone, Default)] +pub struct ScriptMastForestStore { + mast_forests: BTreeMap>, + advice_map: AdviceMap, +} + +impl ScriptMastForestStore { + /// Creates a new [ScriptMastForestStore]. + pub fn new( + tx_script: Option<&TransactionScript>, + note_scripts: impl Iterator>, + ) -> Self { + let mut mast_store = ScriptMastForestStore { + mast_forests: BTreeMap::new(), + advice_map: AdviceMap::default(), + }; + + for note_script in note_scripts { + mast_store.insert(note_script.as_ref().mast()); + } + + if let Some(tx_script) = tx_script { + mast_store.insert(tx_script.mast()); + } + mast_store + } + + /// Registers all procedures of the provided [MastForest] with this store. + fn insert(&mut self, mast_forest: Arc) { + // only register procedures that are local to this forest + for proc_digest in mast_forest.local_procedure_digests() { + self.mast_forests.insert(proc_digest, mast_forest.clone()); + } + + // collect advice data from the forest + for (key, values) in mast_forest.advice_map().clone() { + self.advice_map.insert((*key).into(), values); + } + } + + /// Returns a reference to the advice data collected from all forests. + pub fn advice_map(&self) -> &AdviceMap { + &self.advice_map + } +} + +// MAST FOREST STORE IMPLEMENTATION +// ================================================================================================ + +impl MastForestStore for ScriptMastForestStore { + fn get(&self, procedure_root: &Word) -> Option> { + self.mast_forests.get(procedure_root).cloned() + } +} diff --git a/vendor/miden-tx/src/host/storage_delta_tracker.rs b/vendor/miden-tx/src/host/storage_delta_tracker.rs new file mode 100644 index 000000000..a86ff85ab --- /dev/null +++ b/vendor/miden-tx/src/host/storage_delta_tracker.rs @@ -0,0 +1,221 @@ +use alloc::collections::BTreeMap; +use alloc::vec::Vec; + +use miden_protocol::Word; +use miden_protocol::account::{ + AccountStorageDelta, + AccountStorageHeader, + PartialAccount, + StorageMapKey, + StorageSlotDelta, + StorageSlotHeader, + StorageSlotName, + StorageSlotType, +}; + +/// Keeps track of the initial storage of an account during transaction execution. +/// +/// For storage value slots this can be simply inspected by looking in to the +/// [`AccountStorageHeader`]. +/// +/// For map slots, to avoid making a copy of the entire storage map or even requiring that it is +/// fully accessible in the first place, the initial values are tracked lazily. That is, whenever +/// `set_map_item` is called, the previous value is extracted from the stack and if that is the +/// first time the key is written to, then the previous value is the initial value of that key in +/// that slot. +#[derive(Debug, Clone)] +pub struct StorageDeltaTracker { + /// Flag indicating whether this delta is for a new account. + is_account_new: bool, + /// The _initial_ storage header of the native account against which the transaction is + /// executed. This is only used to look up the initial values of storage _value_ slots, while + /// the map slots are unused. + storage_header: AccountStorageHeader, + /// A map from slot name to a map of key-value pairs where the key is a storage map key and + /// the value represents the value of that key at the beginning of transaction execution. + init_maps: BTreeMap>, + /// The account storage delta. + delta: AccountStorageDelta, +} + +impl StorageDeltaTracker { + // CONSTRUCTORS + // -------------------------------------------------------------------------------------------- + + /// Constructs a new initial storage delta from the provided account. + /// + /// If the account is new, inserts the storage entries into the delta analogously to the + /// transaction kernel delta. + pub fn new(account: &PartialAccount) -> Self { + let initial_storage_header = if account.is_new() { + empty_storage_header_from_account(account) + } else { + account.storage().header().clone() + }; + + let mut storage_delta_tracker = Self { + is_account_new: account.is_new(), + storage_header: initial_storage_header, + init_maps: BTreeMap::new(), + delta: AccountStorageDelta::new(), + }; + + // Insert account storage into delta if it is new to match the kernel behavior. + if account.is_new() { + account.storage().header().slots().for_each(|slot_header| { + match slot_header.slot_type() { + StorageSlotType::Value => { + // For new accounts, all values should be added to the delta, even empty + // words, so that the final delta includes the storage slot. + storage_delta_tracker + .set_item(slot_header.name().clone(), slot_header.value()); + }, + StorageSlotType::Map => { + let storage_map = account + .storage() + .maps() + .find(|map| map.root() == slot_header.value()) + .expect("storage map should be present in partial storage"); + + // Make sure each map is represented by at least an empty storage map delta. + storage_delta_tracker + .delta + .insert_empty_map_delta(slot_header.name().clone()); + + storage_map.entries().for_each(|(key, value)| { + storage_delta_tracker.set_map_item( + slot_header.name().clone(), + *key, + Word::empty(), + *value, + ); + }); + }, + } + }); + } + + storage_delta_tracker + } + + // PUBLIC MUTATORS + // -------------------------------------------------------------------------------------------- + + /// Updates a value slot. + pub fn set_item(&mut self, slot_name: StorageSlotName, new_value: Word) { + self.delta + .set_item(slot_name, new_value) + .expect("transaction kernel should not change slot types"); + } + + /// Updates a map slot. + pub fn set_map_item( + &mut self, + slot_name: StorageSlotName, + key: StorageMapKey, + prev_value: Word, + new_value: Word, + ) { + // Don't update the delta if the new value matches the old one. + if prev_value != new_value { + self.set_init_map_item(slot_name.clone(), key, prev_value); + self.delta + .set_map_item(slot_name, key, new_value) + .expect("transaction kernel should not change slot types"); + } + } + + /// Consumes `self` and returns the resulting, normalized [`AccountStorageDelta`]. + pub fn into_delta(self) -> AccountStorageDelta { + self.normalize() + } + + // HELPERS + // -------------------------------------------------------------------------------------------- + + /// Sets the initial value of the given key in the given slot to the given value, if no value is + /// already tracked for that key. + fn set_init_map_item( + &mut self, + slot_name: StorageSlotName, + key: StorageMapKey, + prev_value: Word, + ) { + let slot_map = self.init_maps.entry(slot_name).or_default(); + slot_map.entry(key).or_insert(prev_value); + } + + /// Normalizes the storage delta by: + /// + /// - removing entries for value slot updates whose new value is equal to the initial value at + /// the beginning of transaction execution. + /// - removing entries for map slot updates where for a given key, the new value is equal to the + /// initial value at the beginning of transaction execution. + fn normalize(self) -> AccountStorageDelta { + let Self { + is_account_new, + storage_header, + init_maps, + delta, + } = self; + let mut deltas = delta.into_map(); + + deltas.retain(|slot_name, slot_delta| { + match slot_delta { + StorageSlotDelta::Value(new_value) => { + // SAFETY: The header in the initial storage is the one from the account + // against which the transaction is executed, so accessing that slot name + // should be fine. + let slot_header = storage_header + .find_slot_header_by_name(slot_name) + .expect("slot name should exist"); + + // Only retain the value if the account is new or if it has changed. + // New accounts must contain all slots, even empty ones, to represent the full + // storage state. + is_account_new || *new_value != slot_header.value() + }, + + // On the key-value level: Keep only the key-value pairs whose new value is + // different from the initial value. + // On the map level: Keep only the maps that are non-empty after its key-value + // pairs have been normalized, or if the account is new. + StorageSlotDelta::Map(map_delta) => { + let init_map = init_maps.get(slot_name); + + if let Some(init_map) = init_map { + map_delta.as_map_mut().retain(|key, new_value| { + let initial_value = init_map.get(key).expect( + "the initial value should be present for every value that was updated", + ); + new_value != initial_value + }); + } + + // Only retain the map delta if the account is new or if it still contains + // values after normalization. + is_account_new || !map_delta.is_empty() + }, + } + }); + + AccountStorageDelta::from_raw(deltas) + } +} + +/// Creates empty slots of the same slot types as the to-be-created account. +fn empty_storage_header_from_account(account: &PartialAccount) -> AccountStorageHeader { + let slots: Vec = account + .storage() + .header() + .slots() + .map(|slot_header| match slot_header.slot_type() { + StorageSlotType::Value => { + StorageSlotHeader::with_empty_value(slot_header.name().clone()) + }, + StorageSlotType::Map => StorageSlotHeader::with_empty_map(slot_header.name().clone()), + }) + .collect(); + + AccountStorageHeader::new(slots).expect("storage header should be valid") +} diff --git a/vendor/miden-tx/src/host/tx_event.rs b/vendor/miden-tx/src/host/tx_event.rs new file mode 100644 index 000000000..93aab405c --- /dev/null +++ b/vendor/miden-tx/src/host/tx_event.rs @@ -0,0 +1,827 @@ +use alloc::vec::Vec; + +use miden_processor::ProcessorState; +use miden_processor::advice::{AdviceMutation, AdviceProvider}; +use miden_processor::trace::RowIndex; +use miden_protocol::account::{ + AccountId, + StorageMap, + StorageMapKey, + StorageSlotName, + StorageSlotType, +}; +use miden_protocol::asset::{Asset, AssetVault, AssetVaultKey, FungibleAsset}; +use miden_protocol::note::{ + NoteAttachment, + NoteAttachmentArray, + NoteAttachmentContent, + NoteAttachmentKind, + NoteAttachmentScheme, + NoteId, + NoteMetadata, + NoteRecipient, + NoteScript, + NoteStorage, + NoteTag, + NoteType, +}; +use miden_protocol::transaction::memory::{NOTE_MEM_SIZE, OUTPUT_NOTE_SECTION_OFFSET}; +use miden_protocol::transaction::{TransactionEventId, TransactionSummary}; +use miden_protocol::vm::EventId; +use miden_protocol::{Felt, Hasher, Word}; + +use crate::host::{TransactionBaseHost, TransactionKernelProcess}; +use crate::{LinkMap, TransactionKernelError}; + +// TRANSACTION PROGRESS EVENT +// ================================================================================================ +#[derive(Debug)] +pub(crate) enum TransactionProgressEvent { + PrologueStart(RowIndex), + PrologueEnd(RowIndex), + + NotesProcessingStart(RowIndex), + NotesProcessingEnd(RowIndex), + + NoteExecutionStart { note_id: NoteId, clk: RowIndex }, + NoteExecutionEnd(RowIndex), + + TxScriptProcessingStart(RowIndex), + TxScriptProcessingEnd(RowIndex), + + EpilogueStart(RowIndex), + EpilogueEnd(RowIndex), + + EpilogueAuthProcStart(RowIndex), + EpilogueAuthProcEnd(RowIndex), + + EpilogueAfterTxCyclesObtained(RowIndex), +} + +// TRANSACTION EVENT +// ================================================================================================ + +/// The data necessary to handle a [`TransactionEventId`]. +#[derive(Debug)] +pub(crate) enum TransactionEvent { + /// The data necessary to request a foreign account's data from the data store. + AccountBeforeForeignLoad { + /// The foreign account's ID. + foreign_account_id: AccountId, + }, + + AccountVaultAfterRemoveAsset { + asset: Asset, + }, + + AccountVaultAfterAddAsset { + asset: Asset, + }, + + AccountStorageAfterSetItem { + slot_name: StorageSlotName, + new_value: Word, + }, + + AccountStorageAfterSetMapItem { + slot_name: StorageSlotName, + key: StorageMapKey, + old_value: Word, + new_value: Word, + }, + + /// The data necessary to request a storage map witness from the data store. + AccountStorageBeforeMapItemAccess { + /// The account ID for whose storage a witness is requested. + active_account_id: AccountId, + /// The root of the storage map for which a witness is requested. + map_root: Word, + /// The raw map key for which a witness is requested. + map_key: StorageMapKey, + }, + + /// The data necessary to request an asset witness from the data store. + AccountVaultBeforeAssetAccess { + /// The account ID for whose vault a witness is requested. + active_account_id: AccountId, + /// The vault root identifying the asset vault from which a witness is requested. + vault_root: Word, + /// The asset for which a witness is requested. + asset_key: AssetVaultKey, + }, + + AccountAfterIncrementNonce, + + AccountPushProcedureIndex { + /// The code commitment of the active account. + code_commitment: Word, + /// The procedure root whose index is requested. + procedure_root: Word, + }, + + NoteBeforeCreated { + /// The note index extracted from the stack. + note_idx: usize, + /// The note metadata extracted from the stack. + metadata: NoteMetadata, + /// The recipient data extracted from the advice inputs. + recipient_data: RecipientData, + }, + + NoteBeforeAddAsset { + /// The note index to which the asset is added. + note_idx: usize, + /// The asset that is added to the output note. + asset: Asset, + }, + + NoteBeforeSetAttachment { + /// The note index on which the attachment is set. + note_idx: usize, + /// The attachment that is set. + attachment: NoteAttachment, + }, + + /// The data necessary to handle an auth request. + AuthRequest { + pub_key_hash: Word, + tx_summary: TransactionSummary, + signature: Option>, + }, + + Unauthorized { + tx_summary: TransactionSummary, + }, + + EpilogueBeforeTxFeeRemovedFromAccount { + fee_asset: FungibleAsset, + }, + + LinkMapSet { + advice_mutation: Vec, + }, + LinkMapGet { + advice_mutation: Vec, + }, + + Progress(TransactionProgressEvent), +} + +impl TransactionEvent { + /// Extracts the [`TransactionEventId`] from the stack as well as the data necessary to handle + /// it. + /// + /// Returns `Some` if the extracted [`TransactionEventId`] resulted in an event that needs to be + /// handled, `None` otherwise. + pub fn extract<'store, STORE>( + base_host: &TransactionBaseHost<'store, STORE>, + process: &ProcessorState, + ) -> Result, TransactionKernelError> { + let event_id = EventId::from_felt(process.get_stack_item(0)); + let tx_event_id = TransactionEventId::try_from(event_id).map_err(|err| { + TransactionKernelError::other_with_source( + "failed to convert event ID into transaction event ID", + err, + ) + })?; + + let tx_event = match tx_event_id { + TransactionEventId::AccountBeforeForeignLoad => { + // Expected stack state: [event, account_id_suffix, account_id_prefix] + let account_id_suffix = process.get_stack_item(1); + let account_id_prefix = process.get_stack_item(2); + let account_id = AccountId::try_from_elements(account_id_suffix, account_id_prefix) + .map_err(|err| { + TransactionKernelError::other_with_source( + "failed to convert account ID word into account ID", + err, + ) + })?; + + Some(TransactionEvent::AccountBeforeForeignLoad { foreign_account_id: account_id }) + }, + TransactionEventId::AccountVaultBeforeAddAsset + | TransactionEventId::AccountVaultBeforeRemoveAsset => { + // Expected stack state: [event, ASSET_KEY, ASSET_VALUE, account_vault_root_ptr] + let asset_vault_key = process.get_stack_word(1); + let vault_root_ptr = process.get_stack_item(9); + + let asset_vault_key = + AssetVaultKey::try_from(asset_vault_key).map_err(|source| { + TransactionKernelError::MalformedAssetInEventHandler { + handler: "AccountVaultBefore{Add,Remove}Asset", + source, + } + })?; + let current_vault_root = process.get_vault_root(vault_root_ptr)?; + + on_account_vault_asset_accessed( + base_host, + process, + asset_vault_key, + current_vault_root, + )? + }, + TransactionEventId::AccountVaultAfterRemoveAsset => { + // Expected stack state: [event, ASSET_KEY, ASSET_VALUE] + let asset_key = process.get_stack_word(1); + let asset_value = process.get_stack_word(5); + + let asset = + Asset::from_key_value_words(asset_key, asset_value).map_err(|source| { + TransactionKernelError::MalformedAssetInEventHandler { + handler: "AccountVaultAfterRemoveAsset", + source, + } + })?; + + Some(TransactionEvent::AccountVaultAfterRemoveAsset { asset }) + }, + TransactionEventId::AccountVaultAfterAddAsset => { + // Expected stack state: [event, ASSET_KEY, ASSET_VALUE] + let asset_key = process.get_stack_word(1); + let asset_value = process.get_stack_word(5); + + let asset = + Asset::from_key_value_words(asset_key, asset_value).map_err(|source| { + TransactionKernelError::MalformedAssetInEventHandler { + handler: "AccountVaultAfterAddAsset", + source, + } + })?; + + Some(TransactionEvent::AccountVaultAfterAddAsset { asset }) + }, + TransactionEventId::AccountVaultBeforeGetAsset => { + // Expected stack state: + // [event, ASSET_KEY, vault_root_ptr] + let asset_key = process.get_stack_word(1); + let vault_root_ptr = process.get_stack_item(5); + + let asset_key = AssetVaultKey::try_from(asset_key).map_err(|source| { + TransactionKernelError::MalformedAssetInEventHandler { + handler: "AccountVaultBeforeGetAsset", + source, + } + })?; + let vault_root = process.get_vault_root(vault_root_ptr)?; + + on_account_vault_asset_accessed(base_host, process, asset_key, vault_root)? + }, + + TransactionEventId::AccountStorageBeforeSetItem => None, + + TransactionEventId::AccountStorageAfterSetItem => { + // Expected stack state: [event, slot_ptr, VALUE] + let slot_ptr = process.get_stack_item(1); + let new_value = process.get_stack_word(2); + + let (slot_id, slot_type, _old_value) = process.get_storage_slot(slot_ptr)?; + + let slot_header = base_host.initial_account_storage_slot(slot_id)?; + let slot_name = slot_header.name().clone(); + + if !slot_type.is_value() { + return Err(TransactionKernelError::other(format!( + "expected slot to be of type value, found {slot_type}" + ))); + } + + Some(TransactionEvent::AccountStorageAfterSetItem { slot_name, new_value }) + }, + + TransactionEventId::AccountStorageBeforeGetMapItem => { + // Expected stack state: [event, slot_ptr, KEY] + let slot_ptr = process.get_stack_item(1); + let map_key = process.get_stack_word(2); + let map_key = StorageMapKey::from_raw(map_key); + + on_account_storage_map_item_accessed(base_host, process, slot_ptr, map_key)? + }, + + TransactionEventId::AccountStorageBeforeSetMapItem => { + // Expected stack state: [event, slot_ptr, KEY] + let slot_ptr = process.get_stack_item(1); + let map_key = process.get_stack_word(2); + let map_key = StorageMapKey::from_raw(map_key); + + on_account_storage_map_item_accessed(base_host, process, slot_ptr, map_key)? + }, + + TransactionEventId::AccountStorageAfterSetMapItem => { + // Expected stack state: [event, slot_ptr, KEY, OLD_VALUE, NEW_VALUE] + let slot_ptr = process.get_stack_item(1); + let key = process.get_stack_word(2); + let old_value = process.get_stack_word(6); + let new_value = process.get_stack_word(10); + + let key = StorageMapKey::from_raw(key); + // Resolve slot ID to slot name. + let (slot_id, ..) = process.get_storage_slot(slot_ptr)?; + let slot_header = base_host.initial_account_storage_slot(slot_id)?; + let slot_name = slot_header.name().clone(); + + Some(TransactionEvent::AccountStorageAfterSetMapItem { + slot_name, + key, + old_value, + new_value, + }) + }, + + TransactionEventId::AccountBeforeIncrementNonce => None, + + TransactionEventId::AccountAfterIncrementNonce => { + Some(TransactionEvent::AccountAfterIncrementNonce) + }, + + TransactionEventId::AccountPushProcedureIndex => { + // Expected stack state: [event, PROC_ROOT] + let procedure_root = process.get_stack_word(1); + let code_commitment = process.get_active_account_code_commitment()?; + + Some(TransactionEvent::AccountPushProcedureIndex { + code_commitment, + procedure_root, + }) + }, + + TransactionEventId::NoteBeforeCreated => { + // Expected stack state: [event, tag, note_type, RECIPIENT] + let tag = process.get_stack_item(1); + let note_type = process.get_stack_item(2); + let recipient_digest = process.get_stack_word(3); + + let sender = base_host.native_account_id(); + let metadata = build_note_metadata(sender, note_type, tag)?; + + let note_idx = process.get_num_output_notes() as usize; + + // try to read the full recipient from the advice provider + let recipient_data = if process.has_advice_map_entry(recipient_digest) { + let (note_storage, script_root, serial_num) = + process.read_note_recipient_info_from_adv_map(recipient_digest)?; + + let note_script = process + .advice_provider() + .get_mapped_values(&script_root) + .map(|script_data| { + NoteScript::try_from(script_data).map_err(|source| { + TransactionKernelError::MalformedNoteScript { + data: script_data.to_vec(), + source, + } + }) + }) + .transpose()?; + + match note_script { + Some(note_script) => { + let recipient = + NoteRecipient::new(serial_num, note_script, note_storage); + + if recipient.digest() != recipient_digest { + return Err(TransactionKernelError::other(format!( + "recipient digest is {recipient_digest}, but recipient constructed from raw inputs has digest {}", + recipient.digest() + ))); + } + + RecipientData::Recipient(recipient) + }, + None => RecipientData::ScriptMissing { + recipient_digest, + serial_num, + script_root, + note_storage, + }, + } + } else { + RecipientData::Digest(recipient_digest) + }; + + Some(TransactionEvent::NoteBeforeCreated { note_idx, metadata, recipient_data }) + }, + + TransactionEventId::NoteAfterCreated => None, + + TransactionEventId::NoteBeforeAddAsset => { + // Expected stack state: [event, ASSET_KEY, ASSET_VALUE, note_idx] + let asset_key = process.get_stack_word(1); + let asset_value = process.get_stack_word(5); + let note_idx = process.get_stack_item(9); + + let asset = + Asset::from_key_value_words(asset_key, asset_value).map_err(|source| { + TransactionKernelError::MalformedAssetInEventHandler { + handler: "NoteBeforeAddAsset", + source, + } + })?; + let note_idx = note_idx.as_canonical_u64() as usize; + + Some(TransactionEvent::NoteBeforeAddAsset { note_idx, asset }) + }, + + TransactionEventId::NoteAfterAddAsset => None, + + TransactionEventId::NoteBeforeSetAttachment => { + // Expected stack state: [ + // event, attachment_scheme, attachment_kind, + // note_ptr, note_ptr, ATTACHMENT + // ] + + let attachment_scheme = process.get_stack_item(1); + let attachment_kind = process.get_stack_item(2); + let note_ptr = process.get_stack_item(3); + let attachment = process.get_stack_word(5); + + let (note_idx, attachment) = extract_note_attachment( + attachment_scheme, + attachment_kind, + attachment, + note_ptr, + process.advice_provider(), + )?; + + Some(TransactionEvent::NoteBeforeSetAttachment { note_idx, attachment }) + }, + + TransactionEventId::AuthRequest => { + // Expected stack state: [event, MESSAGE, PUB_KEY] + let message = process.get_stack_word(1); + let pub_key_hash = process.get_stack_word(5); + let signature_key = Hasher::merge(&[pub_key_hash, message]); + + let signature = process + .advice_provider() + .get_mapped_values(&signature_key) + .map(|slice| slice.to_vec()); + + let tx_summary = extract_tx_summary(base_host, process, message)?; + + Some(TransactionEvent::AuthRequest { pub_key_hash, tx_summary, signature }) + }, + + TransactionEventId::Unauthorized => { + // Expected stack state: [event, MESSAGE] + let message = process.get_stack_word(1); + let tx_summary = extract_tx_summary(base_host, process, message)?; + + Some(TransactionEvent::Unauthorized { tx_summary }) + }, + + TransactionEventId::EpilogueBeforeTxFeeRemovedFromAccount => { + // Expected stack state: [event, FEE_ASSET_KEY, FEE_ASSET_VALUE] + let fee_asset_key = process.get_stack_word(1); + let fee_asset_value = process.get_stack_word(5); + + let fee_asset = FungibleAsset::from_key_value_words(fee_asset_key, fee_asset_value) + .map_err(TransactionKernelError::FailedToConvertFeeAsset)?; + + Some(TransactionEvent::EpilogueBeforeTxFeeRemovedFromAccount { fee_asset }) + }, + + TransactionEventId::LinkMapSet => Some(TransactionEvent::LinkMapSet { + advice_mutation: LinkMap::handle_set_event(process), + }), + TransactionEventId::LinkMapGet => Some(TransactionEvent::LinkMapGet { + advice_mutation: LinkMap::handle_get_event(process), + }), + + TransactionEventId::PrologueStart => Some(TransactionEvent::Progress( + TransactionProgressEvent::PrologueStart(process.clock()), + )), + TransactionEventId::PrologueEnd => Some(TransactionEvent::Progress( + TransactionProgressEvent::PrologueEnd(process.clock()), + )), + + TransactionEventId::NotesProcessingStart => Some(TransactionEvent::Progress( + TransactionProgressEvent::NotesProcessingStart(process.clock()), + )), + TransactionEventId::NotesProcessingEnd => Some(TransactionEvent::Progress( + TransactionProgressEvent::NotesProcessingEnd(process.clock()), + )), + + TransactionEventId::NoteExecutionStart => { + let note_id = process.get_active_note_id()?.ok_or_else(|| TransactionKernelError::other( + "note execution interval measurement is incorrect: check the placement of the start and the end of the interval", + ))?; + + Some(TransactionEvent::Progress(TransactionProgressEvent::NoteExecutionStart { + note_id, + clk: process.clock(), + })) + }, + TransactionEventId::NoteExecutionEnd => Some(TransactionEvent::Progress( + TransactionProgressEvent::NoteExecutionEnd(process.clock()), + )), + + TransactionEventId::TxScriptProcessingStart => Some(TransactionEvent::Progress( + TransactionProgressEvent::TxScriptProcessingStart(process.clock()), + )), + TransactionEventId::TxScriptProcessingEnd => Some(TransactionEvent::Progress( + TransactionProgressEvent::TxScriptProcessingEnd(process.clock()), + )), + + TransactionEventId::EpilogueStart => Some(TransactionEvent::Progress( + TransactionProgressEvent::EpilogueStart(process.clock()), + )), + TransactionEventId::EpilogueEnd => Some(TransactionEvent::Progress( + TransactionProgressEvent::EpilogueEnd(process.clock()), + )), + + TransactionEventId::EpilogueAuthProcStart => Some(TransactionEvent::Progress( + TransactionProgressEvent::EpilogueAuthProcStart(process.clock()), + )), + TransactionEventId::EpilogueAuthProcEnd => Some(TransactionEvent::Progress( + TransactionProgressEvent::EpilogueAuthProcEnd(process.clock()), + )), + + TransactionEventId::EpilogueAfterTxCyclesObtained => Some(TransactionEvent::Progress( + TransactionProgressEvent::EpilogueAfterTxCyclesObtained(process.clock()), + )), + }; + + Ok(tx_event) + } +} + +// RECIPIENT DATA +// ================================================================================================ + +/// The partial data to construct a note recipient. +#[derive(Debug)] +pub(crate) enum RecipientData { + /// Only the recipient digest is available. + Digest(Word), + /// The full [`NoteRecipient`] is available. + Recipient(NoteRecipient), + /// Everything but the note script is available. + ScriptMissing { + recipient_digest: Word, + serial_num: Word, + script_root: Word, + note_storage: NoteStorage, + }, +} + +/// Checks if the necessary witness for accessing the asset identified by the vault key is already +/// in the merkle store, and: +/// - If so, returns `None`. +/// - If not, returns `Some` with all necessary data for requesting it. +fn on_account_vault_asset_accessed<'store, STORE>( + base_host: &TransactionBaseHost<'store, STORE>, + process: &ProcessorState, + vault_key: AssetVaultKey, + vault_root: Word, +) -> Result, TransactionKernelError> { + let leaf_index = Felt::try_from(vault_key.to_leaf_index().position()) + .expect("expected key index to be a felt"); + let active_account_id = process.get_active_account_id()?; + + // For the native account we need to explicitly request the initial vault root, while for + // foreign accounts the current vault root is always the initial one. + let vault_root = if active_account_id == base_host.native_account_id() { + base_host.initial_account_header().vault_root() + } else { + vault_root + }; + + // Note that we check whether a merkle path for the current vault root is present, not + // necessarily for the root we are going to request. This is because the end goal is to + // enable access to an asset against the current vault root, and so if this + // condition is already satisfied, there is nothing to request. + if process.has_merkle_path::<{ AssetVault::DEPTH }>(vault_root, leaf_index)? { + // If the witness already exists, the event does not need to be handled. + Ok(None) + } else { + Ok(Some(TransactionEvent::AccountVaultBeforeAssetAccess { + active_account_id, + vault_root, + asset_key: vault_key, + })) + } +} + +/// Checks if the necessary witness for accessing the map item identified by the map key is already +/// in the merkle store, and: +/// - If so, returns `None`. +/// - If not, returns `Some` with all necessary data for requesting it. +fn on_account_storage_map_item_accessed<'store, STORE>( + base_host: &TransactionBaseHost<'store, STORE>, + process: &ProcessorState, + slot_ptr: Felt, + map_key: StorageMapKey, +) -> Result, TransactionKernelError> { + let (slot_id, slot_type, current_map_root) = process.get_storage_slot(slot_ptr)?; + + if !slot_type.is_map() { + return Err(TransactionKernelError::other(format!( + "expected slot to be of type map, found {slot_type}" + ))); + } + + let active_account_id = process.get_active_account_id()?; + let leaf_index: Felt = map_key + .hash() + .to_leaf_index() + .position() + .try_into() + .expect("expected key index to be a felt"); + + // For the native account we need to explicitly request the initial map root, + // while for foreign accounts the current map root is always the initial one. + let map_root = if active_account_id == base_host.native_account_id() { + // For native accounts, we have to request witnesses against the initial + // root instead of the _current_ one, since the data + // store only has witnesses for initial one. + let slot_header = base_host.initial_account_storage_slot(slot_id)?; + + if slot_header.slot_type() != StorageSlotType::Map { + return Err(TransactionKernelError::other(format!( + "expected slot {slot_id} to be of type map" + ))); + } + slot_header.value() + } else { + current_map_root + }; + + if process.has_merkle_path::<{ StorageMap::DEPTH }>(current_map_root, leaf_index)? { + // If the witness already exists, the event does not need to be handled. + Ok(None) + } else { + Ok(Some(TransactionEvent::AccountStorageBeforeMapItemAccess { + active_account_id, + map_root, + map_key, + })) + } +} + +/// Extracts the transaction summary from the advice map using the provided `message` as the +/// key. +/// +/// ```text +/// Expected advice map state: { +/// MESSAGE: [ +/// SALT, OUTPUT_NOTES_COMMITMENT, INPUT_NOTES_COMMITMENT, ACCOUNT_DELTA_COMMITMENT +/// ] +/// } +/// ``` +fn extract_tx_summary<'store, STORE>( + base_host: &TransactionBaseHost<'store, STORE>, + process: &ProcessorState, + message: Word, +) -> Result { + let Some(commitments) = process.advice_provider().get_mapped_values(&message) else { + return Err(TransactionKernelError::TransactionSummaryConstructionFailed( + "expected message to exist in advice provider".into(), + )); + }; + + if commitments.len() != 16 { + return Err(TransactionKernelError::TransactionSummaryConstructionFailed( + "expected 4 words for transaction summary commitments".into(), + )); + } + + let account_delta_commitment = extract_word(commitments, 0); + let input_notes_commitment = extract_word(commitments, 4); + let output_notes_commitment = extract_word(commitments, 8); + let salt = extract_word(commitments, 12); + + let tx_summary = base_host.build_tx_summary( + account_delta_commitment, + input_notes_commitment, + output_notes_commitment, + salt, + )?; + + if tx_summary.to_commitment() != message { + return Err(TransactionKernelError::TransactionSummaryConstructionFailed( + "transaction summary doesn't commit to the expected message".into(), + )); + } + + Ok(tx_summary) +} + +// HELPER FUNCTIONS +// ================================================================================================ + +/// Builds the note metadata from sender, note type and tag if all inputs are valid. +fn build_note_metadata( + sender: AccountId, + note_type: Felt, + tag: Felt, +) -> Result { + let note_type = u8::try_from(note_type.as_canonical_u64()) + .map_err(|_| TransactionKernelError::other("failed to decode note_type into u8")) + .and_then(|note_type_byte| { + NoteType::try_from(note_type_byte).map_err(|source| { + TransactionKernelError::other_with_source( + "failed to decode note_type from u8", + source, + ) + }) + })?; + + let tag = u32::try_from(tag.as_canonical_u64()) + .map_err(|_| TransactionKernelError::other("failed to decode note tag into u32")) + .map(NoteTag::new)?; + + Ok(NoteMetadata::new(sender, note_type).with_tag(tag)) +} + +fn extract_note_attachment( + attachment_scheme: Felt, + attachment_kind: Felt, + attachment: Word, + note_ptr: Felt, + advice_provider: &AdviceProvider, +) -> Result<(usize, NoteAttachment), TransactionKernelError> { + let note_idx = note_ptr_to_idx(note_ptr)?; + + let attachment_kind = u8::try_from(attachment_kind.as_canonical_u64()) + .map_err(|_| TransactionKernelError::other("failed to convert attachment kind to u8")) + .and_then(|attachment_kind| { + NoteAttachmentKind::try_from(attachment_kind).map_err(|source| { + TransactionKernelError::other_with_source( + "failed to convert u8 to attachment kind", + source, + ) + }) + })?; + + let attachment_scheme = u32::try_from(attachment_scheme.as_canonical_u64()) + .map_err(|_| TransactionKernelError::other("failed to convert attachment scheme to u32")) + .map(NoteAttachmentScheme::new)?; + + let attachment_content = match attachment_kind { + NoteAttachmentKind::None => { + if !attachment.is_empty() { + return Err(TransactionKernelError::NoteAttachmentNoneIsNotEmpty); + } + NoteAttachmentContent::None + }, + NoteAttachmentKind::Word => NoteAttachmentContent::Word(attachment), + NoteAttachmentKind::Array => { + let elements = advice_provider.get_mapped_values(&attachment).ok_or_else(|| { + TransactionKernelError::other( + "elements of a note attachment commitment must be present in the advice provider", + ) + })?; + + let commitment_attachment = + NoteAttachmentArray::new(elements.to_vec()).map_err(|source| { + TransactionKernelError::other_with_source( + "failed to construct note attachment commitment", + source, + ) + })?; + + if commitment_attachment.commitment() != attachment { + return Err(TransactionKernelError::NoteAttachmentArrayMismatch { + actual: commitment_attachment.commitment(), + provided: attachment, + }); + } + + NoteAttachmentContent::Array(commitment_attachment) + }, + }; + + let attachment = + NoteAttachment::new(attachment_scheme, attachment_content).map_err(|source| { + TransactionKernelError::other_with_source("failed to extract note attachment", source) + })?; + + Ok((note_idx as usize, attachment)) +} + +/// Extracts a word from a slice of field elements. +#[inline(always)] +fn extract_word(commitments: &[Felt], start: usize) -> Word { + Word::from([ + commitments[start], + commitments[start + 1], + commitments[start + 2], + commitments[start + 3], + ]) +} + +/// Converts the provided note ptr into the corresponding note index. +fn note_ptr_to_idx(note_ptr: Felt) -> Result { + u32::try_from(note_ptr.as_canonical_u64()) + .map_err(|_| TransactionKernelError::other("failed to convert note_ptr to u32")) + .and_then(|note_ptr| { + note_ptr + .checked_sub(OUTPUT_NOTE_SECTION_OFFSET) + .ok_or_else(|| { + TransactionKernelError::other("failed to calculate note_idx from note_ptr") + }) + .map(|note_ptr| note_ptr / NOTE_MEM_SIZE) + }) +} diff --git a/vendor/miden-tx/src/host/tx_progress.rs b/vendor/miden-tx/src/host/tx_progress.rs new file mode 100644 index 000000000..fc8be54d6 --- /dev/null +++ b/vendor/miden-tx/src/host/tx_progress.rs @@ -0,0 +1,213 @@ +pub use alloc::vec::Vec; + +use super::{NoteId, RowIndex, TransactionMeasurements}; + +// TRANSACTION PROGRESS +// ================================================================================================ + +/// Contains the information about the number of cycles for each of the transaction execution +/// stages. +#[derive(Clone, Debug)] +pub struct TransactionProgress { + prologue: CycleInterval, + notes_processing: CycleInterval, + note_execution: Vec<(NoteId, CycleInterval)>, + tx_script_processing: CycleInterval, + epilogue: CycleInterval, + auth_procedure: CycleInterval, + /// The cycle count of the processor at the point where compute_fee called clk to obtain the + /// transaction's cycle count. + /// + /// This is used to get the total number of cycles the transaction takes for use in + /// compute_fee itself. + epilogue_after_tx_cycles_obtained: Option, +} + +impl TransactionProgress { + // CONSTRUCTORS + // -------------------------------------------------------------------------------------------- + + /// Initializes a new [`TransactionProgress`] with all values set to their defaults. + pub fn new() -> Self { + Self { + prologue: CycleInterval::default(), + notes_processing: CycleInterval::default(), + note_execution: Vec::new(), + tx_script_processing: CycleInterval::default(), + epilogue: CycleInterval::default(), + auth_procedure: CycleInterval::default(), + epilogue_after_tx_cycles_obtained: None, + } + } + + // STATE ACCESSORS + // -------------------------------------------------------------------------------------------- + + pub fn prologue(&self) -> &CycleInterval { + &self.prologue + } + + pub fn notes_processing(&self) -> &CycleInterval { + &self.notes_processing + } + + pub fn note_execution(&self) -> &Vec<(NoteId, CycleInterval)> { + &self.note_execution + } + + pub fn tx_script_processing(&self) -> &CycleInterval { + &self.tx_script_processing + } + + pub fn epilogue(&self) -> &CycleInterval { + &self.epilogue + } + + pub fn auth_procedure(&self) -> &CycleInterval { + &self.auth_procedure + } + + // STATE MUTATORS + // -------------------------------------------------------------------------------------------- + + pub fn start_prologue(&mut self, cycle: RowIndex) { + self.prologue.set_start(cycle); + } + + pub fn end_prologue(&mut self, cycle: RowIndex) { + self.prologue.set_end(cycle); + } + + pub fn start_notes_processing(&mut self, cycle: RowIndex) { + self.notes_processing.set_start(cycle); + } + + pub fn end_notes_processing(&mut self, cycle: RowIndex) { + self.notes_processing.set_end(cycle); + } + + pub fn start_note_execution(&mut self, cycle: RowIndex, note_id: NoteId) { + self.note_execution.push((note_id, CycleInterval::new(cycle))); + } + + pub fn end_note_execution(&mut self, cycle: RowIndex) { + if let Some((_, interval)) = self.note_execution.last_mut() { + interval.set_end(cycle) + } + } + + pub fn start_tx_script_processing(&mut self, cycle: RowIndex) { + self.tx_script_processing.set_start(cycle); + } + + pub fn end_tx_script_processing(&mut self, cycle: RowIndex) { + self.tx_script_processing.set_end(cycle); + } + + pub fn start_epilogue(&mut self, cycle: RowIndex) { + self.epilogue.set_start(cycle); + } + + pub fn start_auth_procedure(&mut self, cycle: RowIndex) { + self.auth_procedure.set_start(cycle); + } + + pub fn end_auth_procedure(&mut self, cycle: RowIndex) { + self.auth_procedure.set_end(cycle); + } + + pub fn epilogue_after_tx_cycles_obtained(&mut self, cycle: RowIndex) { + self.epilogue_after_tx_cycles_obtained = Some(cycle); + } + + pub fn end_epilogue(&mut self, cycle: RowIndex) { + self.epilogue.set_end(cycle); + } +} + +impl Default for TransactionProgress { + fn default() -> Self { + Self::new() + } +} + +impl From for TransactionMeasurements { + fn from(tx_progress: TransactionProgress) -> Self { + let prologue = tx_progress.prologue().len(); + + let notes_processing = tx_progress.notes_processing().len(); + + let note_execution = tx_progress + .note_execution() + .iter() + .map(|(note_id, interval)| (*note_id, interval.len())) + .collect(); + + let tx_script_processing = tx_progress.tx_script_processing().len(); + + let epilogue = tx_progress.epilogue().len(); + + let auth_procedure = tx_progress.auth_procedure().len(); + + // Compute the number of cycles that where not captured by the call to clk. + let after_tx_cycles_obtained = if let Some(epilogue_after_tx_cycles_obtained) = + tx_progress.epilogue_after_tx_cycles_obtained + { + tx_progress.epilogue().end().expect("epilogue end should be set") + - epilogue_after_tx_cycles_obtained + } else { + 0 + }; + + Self { + prologue, + notes_processing, + note_execution, + tx_script_processing, + epilogue, + auth_procedure, + after_tx_cycles_obtained, + } + } +} + +/// Stores the cycles corresponding to the start and the end of an interval. +#[derive(Clone, Default, Debug)] +pub struct CycleInterval { + start: Option, + end: Option, +} + +impl CycleInterval { + pub fn new(start: RowIndex) -> Self { + Self { start: Some(start), end: None } + } + + pub fn set_start(&mut self, s: RowIndex) { + self.start = Some(s); + } + + pub fn set_end(&mut self, e: RowIndex) { + self.end = Some(e); + } + + pub fn start(&self) -> Option { + self.start + } + + pub fn end(&self) -> Option { + self.end + } + + /// Calculate the length of the interval + pub fn len(&self) -> usize { + if let Some(start) = self.start + && let Some(end) = self.end + && end >= start + { + return end - start; + } + + 0 + } +} diff --git a/vendor/miden-tx/src/lib.rs b/vendor/miden-tx/src/lib.rs new file mode 100644 index 000000000..3dd06bdcc --- /dev/null +++ b/vendor/miden-tx/src/lib.rs @@ -0,0 +1,52 @@ +#![no_std] + +#[macro_use] +extern crate alloc; + +#[cfg(feature = "std")] +extern crate std; + +mod executor; +pub use executor::{ + DataStore, + ExecutionOptions, + FailedNote, + MAX_NUM_CHECKER_NOTES, + MastForestStore, + NoteConsumptionChecker, + NoteConsumptionInfo, + ProgramExecutor, + TransactionExecutor, + TransactionExecutorHost, +}; + +mod host; +pub use host::{AccountProcedureIndexMap, LinkMap, MemoryViewer, ScriptMastForestStore}; + +mod prover; +pub use prover::{ + LocalTransactionProver, + ProvingOptions, + TransactionMastStore, + TransactionProverHost, +}; + +mod verifier; +pub use verifier::TransactionVerifier; + +mod errors; +pub use errors::{ + AuthenticationError, + DataStoreError, + NoteCheckerError, + TransactionExecutorError, + TransactionKernelError, + TransactionProverError, + TransactionVerifierError, +}; + +pub mod auth; + +// RE-EXPORTS +// ================================================================================================ +pub use miden_protocol::utils; diff --git a/vendor/miden-tx/src/prover/mast_store.rs b/vendor/miden-tx/src/prover/mast_store.rs new file mode 100644 index 000000000..e92984cc6 --- /dev/null +++ b/vendor/miden-tx/src/prover/mast_store.rs @@ -0,0 +1,81 @@ +use alloc::collections::BTreeMap; +use alloc::sync::Arc; + +use miden_processor::MastForestStore; +use miden_protocol::account::AccountCode; +use miden_protocol::assembly::mast::MastForest; +use miden_protocol::transaction::TransactionKernel; +use miden_protocol::utils::sync::RwLock; +use miden_protocol::{CoreLibrary, ProtocolLib, Word}; +use miden_standards::StandardsLib; + +// TRANSACTION MAST STORE +// ================================================================================================ + +/// A store for the code available during transaction execution. +/// +/// Transaction MAST store contains a map between procedure MAST roots and [MastForest]s containing +/// MASTs for these procedures. The VM will request [MastForest]s from the store when it encounters +/// a procedure which it doesn't have the code for. Thus, to execute a program which makes +/// references to external procedures, the store must be loaded with [MastForest]s containing these +/// procedures. +pub struct TransactionMastStore { + mast_forests: RwLock>>, +} + +#[allow(clippy::new_without_default)] +impl TransactionMastStore { + /// Returns a new [TransactionMastStore] instantiated with the default libraries. + /// + /// The default libraries include: + /// - Miden core library [`CoreLibrary`]. + /// - Miden protocol library [`ProtocolLib`]. + /// - Miden standards library [`StandardsLib`]. + /// - Transaction kernel [`TransactionKernel::kernel`]. + pub fn new() -> Self { + let mast_forests = RwLock::new(BTreeMap::new()); + let store = Self { mast_forests }; + + // load transaction kernel MAST forest + let kernels_forest = TransactionKernel::kernel().mast_forest().clone(); + store.insert(kernels_forest); + + // load miden-core-lib MAST forest + let miden_core_lib_forest = CoreLibrary::default().mast_forest().clone(); + store.insert(miden_core_lib_forest); + + // load protocol lib MAST forest + let protocol_lib_forest = ProtocolLib::default().mast_forest().clone(); + store.insert(protocol_lib_forest); + + // load standards lib MAST forest + let standards_lib_forest = StandardsLib::default().mast_forest().clone(); + store.insert(standards_lib_forest); + + store + } + + /// Registers all procedures of the provided [MastForest] with this store. + pub fn insert(&self, mast_forest: Arc) { + let mut mast_forests = self.mast_forests.write(); + + // only register procedures that are local to this forest + for proc_digest in mast_forest.local_procedure_digests() { + mast_forests.insert(proc_digest, mast_forest.clone()); + } + } + + /// Loads the provided account code into this store. + pub fn load_account_code(&self, code: &AccountCode) { + self.insert(code.mast().clone()); + } +} + +// MAST FOREST STORE IMPLEMENTATION +// ================================================================================================ + +impl MastForestStore for TransactionMastStore { + fn get(&self, procedure_root: &Word) -> Option> { + self.mast_forests.read().get(procedure_root).cloned() + } +} diff --git a/vendor/miden-tx/src/prover/mod.rs b/vendor/miden-tx/src/prover/mod.rs new file mode 100644 index 000000000..82c297c80 --- /dev/null +++ b/vendor/miden-tx/src/prover/mod.rs @@ -0,0 +1,204 @@ +use alloc::sync::Arc; +use alloc::vec::Vec; + +use miden_protocol::account::delta::AccountUpdateDetails; +use miden_protocol::account::{AccountDelta, PartialAccount}; +use miden_protocol::asset::Asset; +use miden_protocol::block::BlockNumber; +use miden_protocol::transaction::{ + InputNote, + InputNotes, + ProvenTransaction, + TransactionInputs, + TransactionKernel, + TransactionOutputs, + TxAccountUpdate, +}; +pub use miden_prover::ProvingOptions; +use miden_prover::{ExecutionProof, Word, prove_dyn_host}; + +use super::TransactionProverError; +use crate::host::{AccountProcedureIndexMap, ScriptMastForestStore}; + +mod prover_host; +pub use prover_host::TransactionProverHost; + +mod mast_store; +pub use mast_store::TransactionMastStore; + +// LOCAL TRANSACTION PROVER +// ------------------------------------------------------------------------------------------------ + +/// Local Transaction prover is a stateless component which is responsible for proving transactions. +pub struct LocalTransactionProver { + mast_store: Arc, + proof_options: ProvingOptions, +} + +impl LocalTransactionProver { + /// Creates a new [LocalTransactionProver] instance. + pub fn new(proof_options: ProvingOptions) -> Self { + Self { + mast_store: Arc::new(TransactionMastStore::new()), + proof_options, + } + } + + fn build_proven_transaction( + &self, + input_notes: &InputNotes, + tx_outputs: TransactionOutputs, + pre_fee_account_delta: AccountDelta, + account: PartialAccount, + ref_block_num: BlockNumber, + ref_block_commitment: Word, + proof: ExecutionProof, + ) -> Result { + let fee = tx_outputs.fee(); + let expiration_block_num = tx_outputs.expiration_block_num(); + let (account_header, output_notes) = tx_outputs.into_parts(); + + // erase private note information (convert private full notes to just headers) + let output_notes: Vec<_> = output_notes + .into_iter() + .map(|note| note.into_output_note()) + .collect::, _>>() + .map_err(TransactionProverError::OutputNoteShrinkFailed)?; + + // Compute the commitment of the pre-fee delta, which goes into the proven transaction, + // since it is the output of the transaction and so is needed for proof verification. + let pre_fee_delta_commitment: Word = pre_fee_account_delta.to_commitment(); + + // The full transaction delta is the pre fee delta with the fee asset removed. + let mut post_fee_account_delta = pre_fee_account_delta; + post_fee_account_delta + .vault_mut() + .remove_asset(Asset::from(fee)) + .map_err(TransactionProverError::RemoveFeeAssetFromDelta)?; + + let account_update_details = if account.has_public_state() { + AccountUpdateDetails::Delta(post_fee_account_delta) + } else { + AccountUpdateDetails::Private + }; + + let account_update = TxAccountUpdate::new( + account.id(), + account.initial_commitment(), + account_header.to_commitment(), + pre_fee_delta_commitment, + account_update_details, + ) + .map_err(TransactionProverError::ProvenTransactionBuildFailed)?; + + ProvenTransaction::new( + account_update, + input_notes.iter(), + output_notes, + ref_block_num, + ref_block_commitment, + fee, + expiration_block_num, + proof, + ) + .map_err(TransactionProverError::ProvenTransactionBuildFailed) + } + + pub async fn prove_transaction_inputs( + &self, + tx_inputs: TransactionInputs, + ) -> Result { + let (stack_inputs, advice_inputs) = TransactionKernel::prepare_inputs(&tx_inputs); + + self.mast_store.load_account_code(tx_inputs.account().code()); + for account_code in tx_inputs.foreign_account_code() { + self.mast_store.load_account_code(account_code); + } + + let script_mast_store = ScriptMastForestStore::new( + tx_inputs.tx_script(), + tx_inputs.input_notes().iter().map(|n| n.note().script()), + ); + + let account_procedure_index_map = AccountProcedureIndexMap::new( + tx_inputs.foreign_account_code().iter().chain([tx_inputs.account().code()]), + ); + + let (partial_account, ref_block, _, input_notes, _) = tx_inputs.into_parts(); + let mut host = TransactionProverHost::new( + &partial_account, + input_notes, + self.mast_store.as_ref(), + script_mast_store, + account_procedure_index_map, + ); + + let advice_inputs = advice_inputs.into_advice_inputs(); + + let (stack_outputs, proof) = prove_dyn_host( + &TransactionKernel::main(), + stack_inputs, + advice_inputs.clone(), + &mut host, + self.proof_options.clone(), + ) + .await + .map_err(TransactionProverError::TransactionProgramExecutionFailed)?; + + // Extract transaction outputs and process transaction data. + // Note that the account delta does not contain the removed transaction fee, so it is the + // "pre-fee" delta of the transaction. + let (pre_fee_account_delta, input_notes, output_notes) = host.into_parts(); + let tx_outputs = + TransactionKernel::from_transaction_parts(&stack_outputs, &advice_inputs, output_notes) + .map_err(TransactionProverError::TransactionOutputConstructionFailed)?; + + self.build_proven_transaction( + &input_notes, + tx_outputs, + pre_fee_account_delta, + partial_account, + ref_block.block_num(), + ref_block.commitment(), + proof, + ) + } + + pub async fn prove( + &self, + tx_inputs: impl Into, + ) -> Result { + self.prove_transaction_inputs(tx_inputs.into()).await + } +} + +impl Default for LocalTransactionProver { + fn default() -> Self { + Self { + mast_store: Arc::new(TransactionMastStore::new()), + proof_options: Default::default(), + } + } +} + +#[cfg(any(feature = "testing", test))] +impl LocalTransactionProver { + pub fn prove_dummy( + &self, + executed_transaction: miden_protocol::transaction::ExecutedTransaction, + ) -> Result { + let (tx_inputs, tx_outputs, account_delta, _) = executed_transaction.into_parts(); + + let (partial_account, ref_block, _, input_notes, _) = tx_inputs.into_parts(); + + self.build_proven_transaction( + &input_notes, + tx_outputs, + account_delta, + partial_account, + ref_block.block_num(), + ref_block.commitment(), + ExecutionProof::new_dummy(), + ) + } +} diff --git a/vendor/miden-tx/src/prover/prover_host.rs b/vendor/miden-tx/src/prover/prover_host.rs new file mode 100644 index 000000000..ab375ed10 --- /dev/null +++ b/vendor/miden-tx/src/prover/prover_host.rs @@ -0,0 +1,237 @@ +use alloc::sync::Arc; +use alloc::vec::Vec; + +use miden_processor::advice::AdviceMutation; +use miden_processor::event::EventError; +use miden_processor::mast::MastForest; +use miden_processor::{FutureMaybeSend, Host, MastForestStore, ProcessorState}; +use miden_prover::HostDyn; +use miden_protocol::Word; +use miden_protocol::account::{AccountDelta, PartialAccount}; +use miden_protocol::assembly::debuginfo::Location; +use miden_protocol::assembly::{SourceFile, SourceSpan}; +use miden_protocol::transaction::{InputNote, InputNotes, RawOutputNote}; +use miden_protocol::vm::{EventId, EventName}; + +use crate::host::{RecipientData, ScriptMastForestStore, TransactionBaseHost, TransactionEvent}; +use crate::{AccountProcedureIndexMap, TransactionKernelError}; + +/// The transaction prover host is responsible for handling [`Host`] requests made by the +/// transaction kernel during proving. +pub struct TransactionProverHost<'store, STORE> +where + STORE: MastForestStore, +{ + /// The underlying base transaction host. + base_host: TransactionBaseHost<'store, STORE>, +} + +impl<'store, STORE> TransactionProverHost<'store, STORE> +where + STORE: MastForestStore, +{ + // CONSTRUCTORS + // -------------------------------------------------------------------------------------------- + + /// Creates a new [`TransactionProverHost`] instance from the provided inputs. + pub fn new( + account: &PartialAccount, + input_notes: InputNotes, + mast_store: &'store STORE, + scripts_mast_store: ScriptMastForestStore, + acct_procedure_index_map: AccountProcedureIndexMap, + ) -> Self { + let base_host = TransactionBaseHost::new( + account, + input_notes, + mast_store, + scripts_mast_store, + acct_procedure_index_map, + ); + + Self { base_host } + } + + // PUBLIC ACCESSORS + // -------------------------------------------------------------------------------------------- + + /// Consumes `self` and returns the account delta, input and output notes. + pub fn into_parts(self) -> (AccountDelta, InputNotes, Vec) { + self.base_host.into_parts() + } +} + +// HOST IMPLEMENTATION +// ================================================================================================ + +impl Host for TransactionProverHost<'_, STORE> +where + STORE: MastForestStore, +{ + fn get_label_and_source_file( + &self, + _location: &Location, + ) -> (SourceSpan, Option>) { + // For the prover, we assume that the transaction witness is a successfully executed + // transaction and so there should be no need to provide the actual source manager, as it + // is only used to improve error message quality which we shouldn't run into here. + (SourceSpan::UNKNOWN, None) + } + + fn get_mast_forest(&self, node_digest: &Word) -> impl FutureMaybeSend>> { + let result = self.base_host.get_mast_forest(node_digest); + async move { result } + } + + fn on_event( + &mut self, + process: &ProcessorState, + ) -> impl FutureMaybeSend, EventError>> { + let result = self.on_event_sync(process); + async move { result } + } + + fn resolve_event(&self, event_id: EventId) -> Option<&EventName> { + self.base_host.resolve_event(event_id) + } +} + +impl HostDyn for TransactionProverHost<'_, STORE> +where + STORE: MastForestStore + Sync, +{ + fn get_label_and_source_file( + &self, + _location: &Location, + ) -> (SourceSpan, Option>) { + (SourceSpan::UNKNOWN, None) + } + + fn get_mast_forest_sync(&self, node_digest: &Word) -> Option> { + self.base_host.get_mast_forest(node_digest) + } + + fn on_event_sync_dyn( + &mut self, + process: &ProcessorState<'_>, + ) -> Result, EventError> { + TransactionProverHost::on_event_sync(self, process) + } + + fn resolve_event(&self, event_id: EventId) -> Option<&EventName> { + self.base_host.resolve_event(event_id) + } +} + +impl TransactionProverHost<'_, STORE> +where + STORE: MastForestStore, +{ + fn on_event_sync( + &mut self, + process: &ProcessorState, + ) -> Result, EventError> { + if let Some(advice_mutations) = self.base_host.handle_core_lib_events(process)? { + return Ok(advice_mutations); + } + + let tx_event = + TransactionEvent::extract(&self.base_host, process).map_err(EventError::from)?; + + // None means the event ID does not need to be handled. + let Some(tx_event) = tx_event else { + return Ok(Vec::new()); + }; + + let result = match tx_event { + // Foreign account data and witnesses should be in the advice provider at + // proving time, so there is nothing to do. + TransactionEvent::AccountBeforeForeignLoad { .. } => Ok(Vec::new()), + + TransactionEvent::AccountVaultAfterRemoveAsset { asset } => { + self.base_host.on_account_vault_after_remove_asset(asset) + }, + TransactionEvent::AccountVaultAfterAddAsset { asset } => { + self.base_host.on_account_vault_after_add_asset(asset) + }, + + TransactionEvent::AccountStorageAfterSetItem { slot_name, new_value } => { + self.base_host.on_account_storage_after_set_item(slot_name, new_value) + }, + + TransactionEvent::AccountStorageAfterSetMapItem { + slot_name, + key, + old_value, + new_value, + } => self + .base_host + .on_account_storage_after_set_map_item(slot_name, key, old_value, new_value), + + // Access witnesses should be in the advice provider at proving time. + TransactionEvent::AccountVaultBeforeAssetAccess { .. } => Ok(Vec::new()), + TransactionEvent::AccountStorageBeforeMapItemAccess { .. } => Ok(Vec::new()), + + TransactionEvent::AccountAfterIncrementNonce => { + self.base_host.on_account_after_increment_nonce() + }, + + TransactionEvent::AccountPushProcedureIndex { code_commitment, procedure_root } => { + self.base_host.on_account_push_procedure_index(code_commitment, procedure_root) + }, + + TransactionEvent::NoteBeforeCreated { note_idx, metadata, recipient_data } => { + match recipient_data { + RecipientData::Digest(recipient_digest) => self + .base_host + .output_note_from_recipient_digest(note_idx, metadata, recipient_digest), + RecipientData::Recipient(note_recipient) => self + .base_host + .output_note_from_recipient(note_idx, metadata, note_recipient), + RecipientData::ScriptMissing { .. } => Err(TransactionKernelError::other( + "note script should be in the advice provider at proving time", + )), + } + }, + + TransactionEvent::NoteBeforeAddAsset { note_idx, asset } => { + self.base_host.on_note_before_add_asset(note_idx, asset).map(|_| Vec::new()) + }, + + TransactionEvent::NoteBeforeSetAttachment { note_idx, attachment } => self + .base_host + .on_note_before_set_attachment(note_idx, attachment) + .map(|_| Vec::new()), + + TransactionEvent::AuthRequest { signature, .. } => { + if let Some(signature) = signature { + Ok(self.base_host.on_auth_requested(signature)) + } else { + Err(TransactionKernelError::other( + "signatures should be in the advice provider at proving time", + )) + } + }, + + TransactionEvent::Unauthorized { tx_summary } => { + Err(TransactionKernelError::other(format!( + "unexpected unauthorized event during proving with tx summary commitment {}", + tx_summary.to_commitment() + ))) + }, + + // We don't track enough information to handle this event. Since this just improves + // error messages for users and the error should not be relevant during proving, we + // ignore it. + TransactionEvent::EpilogueBeforeTxFeeRemovedFromAccount { .. } => Ok(Vec::new()), + + TransactionEvent::LinkMapSet { advice_mutation } => Ok(advice_mutation), + TransactionEvent::LinkMapGet { advice_mutation } => Ok(advice_mutation), + + // We do not track tx progress during proving. + TransactionEvent::Progress(_) => Ok(Vec::new()), + }; + + result.map_err(EventError::from) + } +} diff --git a/vendor/miden-tx/src/verifier/mod.rs b/vendor/miden-tx/src/verifier/mod.rs new file mode 100644 index 000000000..9855151de --- /dev/null +++ b/vendor/miden-tx/src/verifier/mod.rs @@ -0,0 +1,73 @@ +use miden_protocol::CoreLibrary; +use miden_protocol::transaction::{ProvenTransaction, TransactionKernel}; +use miden_protocol::vm::ProgramInfo; +use miden_verifier::verify_with_precompiles; + +use super::TransactionVerifierError; + +// TRANSACTION VERIFIER +// ================================================================================================ + +/// The [TransactionVerifier] is used to verify [ProvenTransaction]s. +/// +/// The [TransactionVerifier] contains a [ProgramInfo] object which is associated with the +/// transaction kernel program. The `proof_security_level` specifies the minimum security +/// level that the transaction proof must have in order to be considered valid. +pub struct TransactionVerifier { + tx_program_info: ProgramInfo, + proof_security_level: u32, +} + +impl TransactionVerifier { + /// Returns a new [TransactionVerifier] instantiated with the specified security level. + pub fn new(proof_security_level: u32) -> Self { + let tx_program_info = TransactionKernel::program_info(); + Self { tx_program_info, proof_security_level } + } + + /// Verifies the provided [`ProvenTransaction`] against the transaction kernel. + /// + /// # Errors + /// Returns an error if: + /// - Transaction verification fails. + /// - The security level of the verified proof is insufficient. + pub fn verify(&self, transaction: &ProvenTransaction) -> Result<(), TransactionVerifierError> { + // build stack inputs and outputs + let stack_inputs = TransactionKernel::build_input_stack( + transaction.account_id(), + transaction.account_update().initial_state_commitment(), + transaction.input_notes().commitment(), + transaction.ref_block_commitment(), + transaction.ref_block_num(), + ); + let stack_outputs = TransactionKernel::build_output_stack( + transaction.account_update().final_state_commitment(), + transaction.account_update().account_delta_commitment(), + transaction.output_notes().commitment(), + transaction.fee(), + transaction.expiration_block_num(), + ); + + // verify transaction proof + let precompile_verifiers = CoreLibrary::default().verifier_registry(); + let proof_security_level = verify_with_precompiles( + self.tx_program_info.clone(), + stack_inputs, + stack_outputs, + transaction.proof().clone(), + &precompile_verifiers, + ) + .map_err(TransactionVerifierError::TransactionVerificationFailed)? + .0; + + // check security level + if proof_security_level < self.proof_security_level { + return Err(TransactionVerifierError::InsufficientProofSecurityLevel { + actual: proof_security_level, + expected_minimum: self.proof_security_level, + }); + } + + Ok(()) + } +}