diff --git a/CHANGELOG.md b/CHANGELOG.md index fed1c0d877..1ac405b7c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,16 @@ # Changelog -## v0.15.0 (TBD) +## 0.14.2 (2026-03-31) ### Changes -- [BREAKING] Renamed `ProvenBatch::new` to `new_unchecked` ([#2687](https://github.com/0xMiden/miden-base/issues/2687)). -- Added shared `ProcedurePolicy` for AuthMultisig ([#2670](https://github.com/0xMiden/protocol/pull/2670)). -- [BREAKING] Changed `NoteType` encoding from 2 bits to 1 and makes `NoteType::Private` the default ([#2691](https://github.com/0xMiden/miden-base/issues/2691)). +- Changed felt-to-word layout in the type registry from `[0, 0, 0, felt]` to `[felt, 0, 0, 0]` to match the actual MASM storage layout ([#2711](https://github.com/0xMiden/protocol/pull/2711)). + +## 0.14.1 (2026-03-30) + +### Changes + +- Integrated various AggLayer-related cleanups ([#2695](https://github.com/0xMiden/protocol/pull/2695)). ## 0.14.0 (2026-03-23) diff --git a/Cargo.lock b/Cargo.lock index 10b8e860d3..9226a9ebb1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -329,16 +329,16 @@ checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "blake3" -version = "1.8.3" +version = "1.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" +checksum = "4d2d5991425dfd0785aed03aedcf0b321d61975c9b5b3689c774a2610ae0b51e" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", - "cpufeatures", + "cpufeatures 0.3.0", ] [[package]] @@ -382,9 +382,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.57" +version = "1.2.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" +checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1" dependencies = [ "find-msvc-tools", "jobserver", @@ -406,7 +406,7 @@ checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ "cfg-if", "cipher", - "cpufeatures", + "cpufeatures 0.2.17", ] [[package]] @@ -525,7 +525,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "531185e432bb31db1ecda541e9e7ab21468d4d844ad7505e0546a49b4945d49b" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "proptest", "serde_core", ] @@ -560,6 +560,15 @@ 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 = "criterion" version = "0.5.1" @@ -681,7 +690,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "curve25519-dalek-derive", "digest", "fiat-crypto", @@ -1358,9 +1367,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.91" +version = "0.3.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" dependencies = [ "once_cell", "wasm-bindgen", @@ -1386,7 +1395,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" dependencies = [ - "cpufeatures", + "cpufeatures 0.2.17", ] [[package]] @@ -1514,7 +1523,7 @@ dependencies = [ [[package]] name = "miden-agglayer" -version = "0.15.0" +version = "0.14.2" dependencies = [ "alloy-sol-types", "fs-err", @@ -1588,7 +1597,7 @@ dependencies = [ [[package]] name = "miden-block-prover" -version = "0.15.0" +version = "0.14.2" dependencies = [ "miden-protocol", "thiserror", @@ -1799,7 +1808,7 @@ dependencies = [ [[package]] name = "miden-protocol" -version = "0.15.0" +version = "0.14.2" dependencies = [ "anyhow", "assert_matches", @@ -1835,7 +1844,7 @@ dependencies = [ [[package]] name = "miden-protocol-macros" -version = "0.15.0" +version = "0.14.2" dependencies = [ "miden-protocol", "proc-macro2", @@ -1873,7 +1882,7 @@ dependencies = [ [[package]] name = "miden-standards" -version = "0.15.0" +version = "0.14.2" dependencies = [ "anyhow", "assert_matches", @@ -1892,7 +1901,7 @@ dependencies = [ [[package]] name = "miden-testing" -version = "0.15.0" +version = "0.14.2" dependencies = [ "anyhow", "assert_matches", @@ -1920,7 +1929,7 @@ dependencies = [ [[package]] name = "miden-tx" -version = "0.15.0" +version = "0.14.2" dependencies = [ "anyhow", "assert_matches", @@ -1937,7 +1946,7 @@ dependencies = [ [[package]] name = "miden-tx-batch-prover" -version = "0.15.0" +version = "0.14.2" dependencies = [ "miden-protocol", "miden-tx", @@ -2204,9 +2213,9 @@ checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" [[package]] name = "p3-air" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ebc58ec27a174420348b3f04dba836fa2e5b5fe8df74601087417352757c643" +checksum = "9f2ec9cbfc642fc5173817287c3f8b789d07743b5f7e812d058b7a03e344f9ab" dependencies = [ "p3-field", "p3-matrix", @@ -2215,9 +2224,9 @@ dependencies = [ [[package]] name = "p3-blake3" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3cacb38c29fbee71fe3e5c6c0a1073632e46dc3e93fbdc50ab4e4fac137b525" +checksum = "b667f43b19499dd939c9e2553aa95688936a88360d50117dae3c8848d07dbc70" dependencies = [ "blake3", "p3-symmetric", @@ -2226,9 +2235,9 @@ dependencies = [ [[package]] name = "p3-challenger" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af9bbcb18fe90271668259aacfc43455e328673c2b5c926cff0663edc8653e4d" +checksum = "4a0b490c745a7d2adeeafff06411814c8078c432740162332b3cd71be0158a76" dependencies = [ "p3-field", "p3-maybe-rayon", @@ -2240,9 +2249,9 @@ dependencies = [ [[package]] name = "p3-commit" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14d07b50c6f6d3bc89ed7c54ae0c569fb4caaa58263fd389dc02fb1b0a6378fa" +checksum = "916ae7989d5c3b49f887f5c55b2f9826bdbb81aaebf834503c4145d8b267c829" dependencies = [ "itertools 0.14.0", "p3-field", @@ -2253,9 +2262,9 @@ dependencies = [ [[package]] name = "p3-dft" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17e7ba0dc20be075eab3f88f0cb820a0901f86218a1c46134e7c817d41597989" +checksum = "55301e91544440254977108b85c32c09d7ea05f2f0dd61092a2825339906a4a7" dependencies = [ "itertools 0.14.0", "p3-field", @@ -2268,9 +2277,9 @@ dependencies = [ [[package]] name = "p3-field" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b8533e6c2f4d0cc61fd2ae5299bb83316898e535f47291808d37e4d666ba088" +checksum = "85affca7fc983889f260655c4cf74163eebb94605f702e4b6809ead707cba54f" dependencies = [ "itertools 0.14.0", "num-bigint", @@ -2284,9 +2293,9 @@ dependencies = [ [[package]] name = "p3-goldilocks" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8102a8c85acee1f896c3764bef5fac908e6026dadfc557c185294970cce0746" +checksum = "0ca1081f5c47b940f2d75a11c04f62ea1cc58a5d480dd465fef3861c045c63cd" dependencies = [ "num-bigint", "p3-challenger", @@ -2304,9 +2313,9 @@ dependencies = [ [[package]] name = "p3-keccak" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65d30dd586d2855906a01c3414c155c2d564f6677d1b51f04186dcac080f757" +checksum = "ebcf27615ece1995e4fcf4c69740f1cf515d1481367a20b4b3ce7f4f1b8d70f7" dependencies = [ "p3-symmetric", "p3-util", @@ -2315,9 +2324,9 @@ dependencies = [ [[package]] name = "p3-matrix" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72bb78444459155c2e4711d71abbfef7b04cc2ba1fa83751ccab241b01957095" +checksum = "53428126b009071563d1d07305a9de8be0d21de00b57d2475289ee32ffca6577" dependencies = [ "itertools 0.14.0", "p3-field", @@ -2330,18 +2339,18 @@ dependencies = [ [[package]] name = "p3-maybe-rayon" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a0a54345917f500130a9986fa5ff9ecbc26f0c6313080b35b713e26ddc8053" +checksum = "082bf467011c06c768c579ec6eb9accb5e1e62108891634cc770396e917f978a" dependencies = [ "rayon", ] [[package]] name = "p3-mds" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd514bf3e9bf9f1b7db2db96e5bd2972d9963dd62430de1e193d74522ae96a6" +checksum = "35209e6214102ea6ec6b8cb1b9c15a9b8e597a39f9173597c957f123bced81b3" dependencies = [ "p3-dft", "p3-field", @@ -2448,9 +2457,9 @@ dependencies = [ [[package]] name = "p3-monty-31" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d9340a650f07a6cd42a4e877017ba7b206df87fe50dfc3cf110f01a3c370bd1" +checksum = "ffa8c99ec50c035020bbf5457c6a729ba6a975719c1a8dd3f16421081e4f650c" dependencies = [ "itertools 0.14.0", "num-bigint", @@ -2472,9 +2481,9 @@ dependencies = [ [[package]] name = "p3-poseidon1" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd56ae3a51ded1b77f7b1b21d0b157ae82b9d5ca8f2cba347c0b821fe771a79" +checksum = "6a018b618e3fa0aec8be933b1d8e404edd23f46991f6bf3f5c2f3f95e9413fe9" dependencies = [ "p3-field", "p3-symmetric", @@ -2483,9 +2492,9 @@ dependencies = [ [[package]] name = "p3-poseidon2" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "858aa1c33ec983dfbb8cfc553a213de19d8fde96485e54e6e952b9ac5e70bd4e" +checksum = "256a668a9ba916f8767552f13d0ba50d18968bc74a623bfdafa41e2970c944d0" dependencies = [ "p3-field", "p3-mds", @@ -2496,9 +2505,9 @@ dependencies = [ [[package]] name = "p3-symmetric" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9a3b20bb8104e52d45219a78d80654c8ac6a4781be0eaa3f3e999f5ae4b9b2" +checksum = "6c60a71a1507c13611b0f2b0b6e83669fd5b76f8e3115bcbced5ccfdf3ca7807" dependencies = [ "itertools 0.14.0", "p3-field", @@ -2508,9 +2517,9 @@ dependencies = [ [[package]] name = "p3-util" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f24495d9cd64693165a9f1b3da0758395ad6d25d2d44dd740bdb34c2bce0c53" +checksum = "f8b766b9e9254bf3fa98d76e42cf8a5b30628c182dfd5272d270076ee12f0fc0" dependencies = [ "rayon", "serde", @@ -2615,7 +2624,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" dependencies = [ - "cpufeatures", + "cpufeatures 0.2.17", "opaque-debug", "universal-hash", ] @@ -2735,9 +2744,9 @@ dependencies = [ [[package]] name = "proptest" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37566cb3fdacef14c0737f9546df7cfeadbfbc9fef10991038bf5015d0c80532" +checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ "bitflags 2.11.0", "num-traits", @@ -3019,9 +3028,9 @@ checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" [[package]] name = "rustc-hash" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" [[package]] name = "rustc_version" @@ -3177,9 +3186,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876ac351060d4f882bb1032b6369eb0aef79ad9df1ea8bc404874d8cc3d0cd98" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ "serde_core", ] @@ -3191,7 +3200,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest", ] @@ -3533,9 +3542,9 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8195ca05e4eb728f4ba94f3e3291661320af739c4e43779cbdfae82ab239fcc" +checksum = "994b95d9e7bae62b34bab0e2a4510b801fa466066a6a8b2b57361fa1eba068ee" dependencies = [ "indexmap", "serde_core", @@ -3548,18 +3557,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.1.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.25.8+spec-1.1.0" +version = "0.25.9+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16bff38f1d86c47f9ff0647e6838d7bb362522bdf44006c7068c2b1e606f1f3c" +checksum = "da053d28fe57e2c9d21b48261e14e7b4c8b670b54d2c684847b91feaf4c7dac5" dependencies = [ "indexmap", "toml_datetime", @@ -3569,18 +3578,18 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.1.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011" +checksum = "39ca317ebc49f06bd748bfba29533eac9485569dc9bf80b849024b025e814fb9" dependencies = [ "winnow", ] [[package]] name = "toml_writer" -version = "1.1.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d282ade6016312faf3e41e57ebbba0c073e4056dab1232ab1cb624199648f8ed" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" [[package]] name = "tracing" @@ -3728,9 +3737,9 @@ checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" [[package]] name = "unicode-segmentation" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" [[package]] name = "unicode-width" @@ -3768,9 +3777,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.22.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" +checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" dependencies = [ "js-sys", "wasm-bindgen", @@ -3833,9 +3842,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.114" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" dependencies = [ "cfg-if", "once_cell", @@ -3846,9 +3855,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.114" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3856,9 +3865,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.114" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" dependencies = [ "bumpalo", "proc-macro2", @@ -3869,9 +3878,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.114" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" dependencies = [ "unicode-ident", ] @@ -3912,9 +3921,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.91" +version = "0.3.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" +checksum = "cd70027e39b12f0849461e08ffc50b9cd7688d942c1c8e3c7b22273236b4dd0a" dependencies = [ "js-sys", "wasm-bindgen", @@ -3977,9 +3986,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8" +checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" dependencies = [ "memchr", ] @@ -4084,18 +4093,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.47" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.47" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 932913e36b..aace353325 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ homepage = "https://miden.xyz" license = "MIT" repository = "https://github.com/0xMiden/protocol" rust-version = "1.90" -version = "0.15.0" +version = "0.14.2" [profile.release] codegen-units = 1 @@ -37,14 +37,14 @@ lto = true [workspace.dependencies] # Workspace crates -miden-agglayer = { default-features = false, path = "crates/miden-agglayer", version = "0.15" } -miden-block-prover = { default-features = false, path = "crates/miden-block-prover", version = "0.15" } -miden-protocol = { default-features = false, path = "crates/miden-protocol", version = "0.15" } -miden-protocol-macros = { default-features = false, path = "crates/miden-protocol-macros", version = "0.15" } -miden-standards = { default-features = false, path = "crates/miden-standards", version = "0.15" } -miden-testing = { default-features = false, path = "crates/miden-testing", version = "0.15" } -miden-tx = { default-features = false, path = "crates/miden-tx", version = "0.15" } -miden-tx-batch-prover = { default-features = false, path = "crates/miden-tx-batch-prover", version = "0.15" } +miden-agglayer = { default-features = false, path = "crates/miden-agglayer", version = "0.14" } +miden-block-prover = { default-features = false, path = "crates/miden-block-prover", version = "0.14" } +miden-protocol = { default-features = false, path = "crates/miden-protocol", version = "0.14" } +miden-protocol-macros = { default-features = false, path = "crates/miden-protocol-macros", version = "0.14" } +miden-standards = { default-features = false, path = "crates/miden-standards", version = "0.14" } +miden-testing = { default-features = false, path = "crates/miden-testing", version = "0.14" } +miden-tx = { default-features = false, path = "crates/miden-tx", version = "0.14" } +miden-tx-batch-prover = { default-features = false, path = "crates/miden-tx-batch-prover", version = "0.14" } # Miden dependencies miden-assembly = { default-features = false, version = "0.22" } diff --git a/Makefile b/Makefile index 03d5211d73..72f6eac962 100644 --- a/Makefile +++ b/Makefile @@ -189,3 +189,16 @@ install-tools: ## Installs development tools required by the Makefile (mdbook, t cargo install taplo-cli --locked cargo install cargo-machete --locked @echo "Development tools installation complete!" + +# -- documentation --------------------------------------------------------------------------------- + +AGGLAYER_DIAGRAMS_DIR := crates/miden-agglayer/diagrams +EXCALIDRAW_SOURCES := $(wildcard $(AGGLAYER_DIAGRAMS_DIR)/*.excalidraw) +EXCALIDRAW_PNGS := $(EXCALIDRAW_SOURCES:.excalidraw=.png) + +.PHONY: agglayer-spec +agglayer-spec: $(EXCALIDRAW_PNGS) ## Exports AggLayer spec diagrams from .excalidraw to .png + +$(AGGLAYER_DIAGRAMS_DIR)/%.png: $(AGGLAYER_DIAGRAMS_DIR)/%.excalidraw + @command -v npx >/dev/null 2>&1 || { echo "npx not found. Install Node.js first."; exit 1; } + npx excalidraw-brute-export-cli -i $< --format png -o $@ --scale 2 diff --git a/crates/miden-agglayer/SPEC.md b/crates/miden-agglayer/SPEC.md index e80ab2d82c..cddf6461fd 100644 --- a/crates/miden-agglayer/SPEC.md +++ b/crates/miden-agglayer/SPEC.md @@ -25,22 +25,132 @@ implementation are called out inline with `TODO (Future)` markers. | **Integration Service** (offchain) | Observes L1 events (deposits, GER updates) and creates UPDATE_GER and CLAIM notes on Miden. Trusted to provide correct proofs and data. | Not an onchain entity; creates notes targeting bridge/faucet | | **Bridge Operator** (offchain) | Deploys bridge and faucet accounts. Creates CONFIG_AGG_BRIDGE notes to register faucets. Must use the bridge admin account. | Not an onchain entity; creates config notes | -### Current permissions +--- + +## 2. Protocol Description + +The crate `miden-agglayer` implements the AggLayer bridging protocol on the Miden blockchain. This section provides a high-level description of the implementation on Miden, covering the main operational flows. + +### 2.1 Bridge-out (Miden to AggLayer) + +![Bridge-out flow](diagrams/bridge-out.png) + +A user initiates a bridge-out by creating a [`B2AGG`](#41-b2agg) note containing a single fungible +asset and the destination network/address. The bridge account consumes this note: + +1. Validates that the asset's faucet is registered in the faucet registry. +2. FPIs to the faucet (`agglayer_faucet::asset_to_origin_asset`) to obtain the scaled + U256 amount, origin token address, and origin network. +3. FPIs to the faucet (`agglayer_faucet::get_metadata_hash`) to obtain the metadata hash. +4. Constructs a leaf-data structure (leaf type, origin network, origin token address, + destination network, destination address, amount, metadata hash). +5. Computes the Keccak-256 leaf value and appends it to the Local Exit Tree (LET). +6. Creates a public [`BURN`](#45-burn-generated) note targeting the faucet, which burns the asset and + decreases the faucet's token supply. + +The leaf appended to the LET can later be included in a Merkle proof on any +AggLayer-connected chain to claim the bridged asset. + +TODO: The bridge currently has no emergency pause mechanism to halt operations +([#2696](https://github.com/0xMiden/protocol/issues/2696)). + +### 2.2 Bridge-in (AggLayer to Miden) + +![Bridge-in flow](diagrams/bridge-in.png) + +When a new deposit into Miden is made on an AggLayer-connected chain, Miden needs to be "informed" of the updated AggLayer state by having a new Global Exit Root (GER) injected - see [Section 2.3](#23-ger-injection). + +Once the GER is injected, any user can initiate the claim process by creating a [`CLAIM`](#42-claim) note on Miden containing Merkle proofs and leaf data (by monitoring updates to the AggLayer contract on Ethereum L1). This will typically be done by a claim manager service for convenience, but is permissionless and open to any user. +The `CLAIM` note is consumed by the bridge account: + +1. Validates the Global Exit Root (GER) is known in the bridge's `ger_map`. +2. Parses the global index to determine whether this is a mainnet or rollup deposit, + and extracts the leaf index and source bridge network. +3. Verifies the Merkle proof: for mainnet deposits, a single proof against + `mainnet_exit_root`; for rollup deposits, a two-level proof (leaf against + `local_exit_root`, then `local_exit_root` against `rollup_exit_root`). +4. Updates the claimed global index (CGI) chain hash: + `NEW_CGI = Keccak256(OLD_CGI, Keccak256(GLOBAL_INDEX, LEAF_VALUE))`. +5. Checks and sets the claim nullifier to prevent double-claiming. +6. Looks up the faucet from the origin token address via the token registry. +7. Verifies the claim amount against the leaf's U256 amount and the faucet's scale factor. +8. Creates a [`MINT`](#47-mint-generated) note targeting the faucet. + +The faucet consumes the `MINT` note, mints the specified amount, and creates a [`P2ID`](#46-p2id-generated) note +that delivers the minted assets to the recipient's Miden account. + +TODO: Destination network from the leaf data is not validated against Miden's own network +ID ([#2698](https://github.com/0xMiden/protocol/issues/2698)). + +TODO: The leaf type field is not validated to be `LEAF_TYPE_ASSET` (0) +([#2699](https://github.com/0xMiden/protocol/issues/2699)). + +TODO: Claims cannot be reversed once the nullifier is set +([#2703](https://github.com/0xMiden/protocol/issues/2703)). + +### 2.3 GER Injection + +![GER injection flow](diagrams/ger-injection.png) + +Global Exit Roots represent a snapshot of exit tree roots across all AggLayer-connected +chains. A GER Manager observes L1 GER updates and creates [`UPDATE_GER`](#44-update_ger) notes +on Miden. The bridge consumes these notes: + +1. Asserts the note sender is the designated GER manager. +2. Computes `KEY = poseidon2::merge(GER_LOWER, GER_UPPER)`. +3. Stores `KEY -> [1, 0, 0, 0]` in the `ger_map`, marking the GER as known. + +Subsequent CLAIM notes reference a GER that must be present in this map for the claim +to be valid. + +TODO: GERs cannot be removed once inserted +([#2702](https://github.com/0xMiden/protocol/issues/2702)). + +TODO: No hash chain tracks GER insertions for proof generation +([#2707](https://github.com/0xMiden/protocol/issues/2707)). + +TODO: Duplicate GER insertions are silently accepted +([#2708](https://github.com/0xMiden/protocol/issues/2708)). -| Note type | Issuer (sender check) | Consumer (consuming-account check) | -|-----------|----------------------|-----------------------------------| -| B2AGG (bridge-out) | Any user -- not restricted | Bridge account -- **enforced** via `NetworkAccountTarget` attachment | -| B2AGG (reclaim) | Any user -- not restricted | Original sender only -- **enforced**: script checks `sender == consuming account` | -| CONFIG_AGG_BRIDGE | Bridge admin only -- **enforced** by `bridge_config::register_faucet` procedure | Bridge account -- **enforced** via `NetworkAccountTarget` attachment | -| UPDATE_GER | GER manager only -- **enforced** by `bridge_config::update_ger` procedure | Bridge account -- **enforced** via `NetworkAccountTarget` attachment | -| CLAIM | Anyone -- not restricted | Bridge account -- **enforced** via `NetworkAccountTarget` attachment | -| MINT | Bridge account only -- **enforced** by faucet's `owner_only` mint policy via `Ownable2Step` (asserts note sender is the faucet's owner, i.e. the bridge) | Target faucet only -- **enforced** via `NetworkAccountTarget` attachment | +### 2.4 Faucet Registration + +![Faucet registration flow](diagrams/faucet-registration.png) + +Each bridged token requires a dedicated AggLayer faucet on Miden. The Bridge Operator +creates [`CONFIG_AGG_BRIDGE`](#43-config_agg_bridge) notes to register faucets. The bridge consumes these notes, +asserting the sender is the bridge admin, then registers the faucet in both the faucet +registry and the token registry. For a detailed description of the faucet and token +registries, see [Section 7](#7-faucet-registry). + +TODO: Faucet registrations are permanent; no remapping or deregistration is supported +([#2704](https://github.com/0xMiden/protocol/issues/2704), +[#2705](https://github.com/0xMiden/protocol/issues/2705)). + +TODO: Faucet existence and code commitment are not validated during registration +([#2709](https://github.com/0xMiden/protocol/issues/2709)). + +### 2.5 Administration + +The bridge has two administrative roles set at account creation time: + +- **Bridge admin** (`admin_account_id`): authorizes faucet registration via + [`CONFIG_AGG_BRIDGE`](#43-config_agg_bridge) notes. +- **GER manager** (`ger_manager_account_id`): authorizes GER updates via [`UPDATE_GER`](#44-update_ger) + notes. + +Both roles are verified by checking the note sender against the stored account ID. + +TODO: Administrative roles cannot be transferred after account creation +([#2706](https://github.com/0xMiden/protocol/issues/2706)). + +TODO: No emergency pause mechanism exists +([#2696](https://github.com/0xMiden/protocol/issues/2696)). --- -## 2. Contracts and Public Interfaces +## 3. Contracts and Public Interfaces -### 2.1 Bridge Account Component +### 3.1 Bridge Account Component The bridge account has a single unified `bridge` component (`components/bridge.masm`), which is a thin wrapper that re-exports procedures from the `agglayer` library modules: @@ -137,7 +247,7 @@ Validates a bridge-in claim and creates a MINT note targeting the faucet: 7. Verifies the `faucet_mint_amount` against the leaf data's U256 amount and the faucet's scale factor (via FPI to `agglayer_faucet::get_scale`), using `asset_conversion::verify_u256_to_native_amount_conversion`. -8. Builds a MINT output note targeting the faucet (see [Section 3.7](#37-mint-generated)). +8. Builds a MINT output note targeting the faucet (see [Section 4.7](#47-mint-generated)). #### Bridge Account Storage @@ -159,7 +269,7 @@ Validates a bridge-in claim and creates a MINT note targeting the faucet: Initial state: all map slots empty, all value slots `[0, 0, 0, 0]` except `admin_account_id` and `ger_manager_account_id` which are set at account creation time. -### 2.2 Faucet Account Component +### 3.2 Faucet Account Component The faucet account has the `agglayer_faucet` component (`components/faucet.masm`), which is a thin wrapper that re-exports procedures from the `agglayer` library: @@ -257,17 +367,17 @@ companion components required by `network_fungible::mint_and_send`: --- -## 3. Note Types and Storage Layouts +## 4. Note Types and Storage Layouts **Encoding conventions:** All multi-byte values in note storage (addresses, U256 integers, Keccak-256 hashes) are encoded as arrays of u32 felts via `bytes_to_packed_u32_felts`: big-endian limb order with **little-endian byte order** -within each 4-byte limb (see [Section 5.5](#55-endianness-summary)). Scalar u32 fields +within each 4-byte limb (see [Section 6.5](#65-endianness-summary)). Scalar u32 fields (network IDs) are byte-reversed at storage time so their in-memory bytes align with the Keccak preimage format directly — the felt value does **not** equal the numeric value (e.g., chain ID `1` = `0x00000001` is stored as felt `0x01000000`). -### 3.1 B2AGG +### 4.1 B2AGG (Bridge-to-AggLayer) **Purpose:** User bridges an asset from Miden to the AggLayer. @@ -309,7 +419,23 @@ Keccak preimage format directly — the felt value does **not** equal the numeri - **Reclaim:** Consuming account is the original sender -> assets are added back to the account via `basic_wallet::add_assets_to_account`. No output notes. -### 3.2 CLAIM +#### Permissions + +**Bridge-out:** + +| Role | Enforcement | +|------|------------| +| **Issuer** | Any user -- not restricted | +| **Consumer** | Bridge account -- **enforced** via `NetworkAccountTarget` attachment | + +**Reclaim:** + +| Role | Enforcement | +|------|------------| +| **Issuer** | Any user -- not restricted | +| **Consumer** | Original sender only -- **enforced**: script checks `sender == consuming account` | + +### 4.2 CLAIM **Purpose:** Claim assets, which were deposited on any AggLayer-connected rollup, on Miden. Consumed by the bridge account, which validates the proof, looks up the faucet via the @@ -373,7 +499,14 @@ The storage is divided into three logical regions: proof data (felts 0-535), lea faucet via the token registry, verifies the amount conversion, then builds a MINT output note targeting the faucet. -### 3.3 CONFIG_AGG_BRIDGE +#### Permissions + +| Role | Enforcement | +|------|------------| +| **Issuer** | Anyone -- not restricted | +| **Consumer** | Bridge account -- **enforced** via `NetworkAccountTarget` attachment | + +### 4.3 CONFIG_AGG_BRIDGE **Purpose:** Registers a faucet in the bridge's faucet registry. @@ -412,7 +545,14 @@ The storage is divided into three logical regions: proof data (felts 0-535), lea `bridge_config::register_faucet` (which asserts sender is bridge admin and performs two-step registration into `faucet_registry_map` and `token_registry_map`). -### 3.4 UPDATE_GER +#### Permissions + +| Role | Enforcement | +|------|------------| +| **Issuer** | Bridge admin only -- **enforced** by `bridge_config::register_faucet` procedure | +| **Consumer** | Bridge account -- **enforced** via `NetworkAccountTarget` attachment | + +### 4.4 UPDATE_GER **Purpose:** Stores a new Global Exit Root (GER) in the bridge account so that subsequent CLAIM notes can be verified against it. @@ -451,7 +591,14 @@ CLAIM notes can be verified against it. `bridge_config::update_ger` (which asserts sender is GER manager), which computes `poseidon2::merge(GER_LOWER, GER_UPPER)` and stores the result in the GER map. -### 3.5 BURN (generated) +#### Permissions + +| Role | Enforcement | +|------|------------| +| **Issuer** | GER manager only -- **enforced** by `bridge_config::update_ger` procedure | +| **Consumer** | Bridge account -- **enforced** via `NetworkAccountTarget` attachment | + +### 4.5 BURN (generated) **Purpose:** Created by `bridge_out::bridge_out` to burn the bridged asset on the faucet. @@ -488,7 +635,14 @@ The standard BURN script calls `faucets::burn` on the consuming faucet account. validates that the note contains exactly one fungible asset issued by that faucet and decreases the faucet's total token supply by the burned amount. -### 3.6 P2ID (generated) +#### Permissions + +| Role | Enforcement | +|------|------------| +| **Issuer** | Bridge account (created by `bridge_out::bridge_out`) | +| **Consumer** | Target faucet only -- **enforced** via `NetworkAccountTarget` attachment | + +### 4.6 P2ID (generated) **Purpose:** Created by the faucet (via `mint_and_send`) when consuming a MINT note, to deliver minted assets to the recipient. @@ -529,7 +683,14 @@ Consuming account must match `target_account_id` from note storage (enforced by script). All note assets are added to the consuming account via `basic_wallet::add_assets_to_account`. -### 3.7 MINT (generated) +#### Permissions + +| Role | Enforcement | +|------|------------| +| **Issuer** | Faucet account (created by `mint_and_send`) | +| **Consumer** | Destination account only -- **enforced** by P2ID script (checks `target_account_id`) | + +### 4.7 MINT (generated) **Purpose:** Created by `bridge_in::claim` on the bridge account. Consumed by the faucet to mint and distribute assets to the recipient. @@ -587,22 +748,29 @@ After the policy check passes, `mint_and_send` mints the specified amount and cr P2ID output note for the recipient using the storage items (script root, serial number, destination account ID, tag). +#### Permissions + +| Role | Enforcement | +|------|------------| +| **Issuer** | Bridge account only -- **enforced** by faucet's `owner_only` mint policy via `Ownable2Step` (asserts note sender is the faucet's owner, i.e. the bridge) | +| **Consumer** | Target faucet only -- **enforced** via `NetworkAccountTarget` attachment | + --- -## 4. Amount Conversion +## 5. Amount Conversion *This section is a placeholder. Content to be added.* --- -## 5. Ethereum ↔ Miden Address Conversion +## 6. Ethereum ↔ Miden Address Conversion The AggLayer bridge operates across two address spaces: Ethereum's 20-byte addresses and Miden's `AccountId` (two field elements). This section specifies the encoding that maps between them, as implemented in Rust (`eth_types/address.rs`) and MASM (`agglayer/common/eth_address.masm`). -### 5.1 Background +### 6.1 Background Miden's `AccountId` (version 0) consists of two Goldilocks field elements: @@ -620,7 +788,7 @@ Ethereum addresses are 20-byte (160-bit) values. Because every valid `AccountId` 16 bytes (prefix: 8 bytes, suffix: 8 bytes), it can be embedded into the lower 16 bytes of an Ethereum address with 4 zero-padding bytes at the top. -### 5.2 Embedded Format +### 6.2 Embedded Format An `AccountId` is embedded in a 20-byte Ethereum address as follows: @@ -655,7 +823,7 @@ non-zero, if the packed `u64` values exceed the field modulus, or if the resulti don't form a valid `AccountId`. Arbitrary Ethereum addresses (e.g., from EOAs or contracts on L1) cannot generally be decoded into `AccountId` values. -### 5.3 MASM Limb Representation +### 6.3 MASM Limb Representation Inside the Miden VM, a 20-byte Ethereum address is represented as 5 field elements, each holding a `u32` value. This layout uses **big-endian limb @@ -676,9 +844,9 @@ Keccak-256 precompile. The Rust function `EthAddressFormat::to_elements()` produces exactly this 5-felt array from a 20-byte address. -### 5.4 Conversion Procedures +### 6.4 Conversion Procedures -#### 5.4.1 `AccountId` → Ethereum Address (Rust) +#### 6.4.1 `AccountId` → Ethereum Address (Rust) `EthAddressFormat::from_account_id(account_id: AccountId) -> EthAddressFormat` @@ -694,7 +862,7 @@ This is the **external API** used by the bridge interface. It lets a user conver This conversion is **infallible**: every valid `AccountId` produces a valid 20-byte address. -#### 5.4.2 Ethereum Address → `AccountId` (Rust) +#### 6.4.2 Ethereum Address → `AccountId` (Rust) `EthAddressFormat::to_account_id(&self) -> Result` @@ -723,7 +891,7 @@ extract the recipient's `AccountId` from the embedded Ethereum address and e.g. | `FeltOutOfField` | A `u64` value ≥ the Goldilocks prime `p` | | `InvalidAccountId` | The resulting felts don't form a valid `AccountId` | -#### 5.4.3 Ethereum Address → `AccountId` (MASM) +#### 6.4.3 Ethereum Address → `AccountId` (MASM) `eth_address::to_account_id` — Module: `agglayer::common::eth_address` @@ -743,7 +911,7 @@ Invocation: exec 1. `assertz limb0` — the most-significant limb must be zero (error: `ERR_MSB_NONZERO`). 2. Build `suffix` from `(limb4, limb3)`: - a. Validate both values are `u32` (error: `ERR_NOT_U32`). - - b. Byte-swap each limb from little-endian to big-endian via `utils::swap_u32_bytes` (see [Section 5.5](#55-endianness-summary)). + - b. Byte-swap each limb from little-endian to big-endian via `utils::swap_u32_bytes` (see [Section 6.5](#65-endianness-summary)). - c. Pack into a felt: `suffix = bswap(limb3) × 2^32 + bswap(limb4)`. - d. Verify no mod-p reduction: split the felt back via `u32split` and assert equality with the original limbs (error: `ERR_FELT_OUT_OF_FIELD @@ -772,17 +940,17 @@ Outputs: [swapped] Reverses the byte order of a `u32`: `[b0, b1, b2, b3] → [b3, b2, b1, b0]`. -#### 5.4.4 Ethereum Address → Field Elements (Rust) +#### 6.4.4 Ethereum Address → Field Elements (Rust) `EthAddressFormat::to_elements(&self) -> Vec` Converts the 20-byte address into a field element array for use in the Miden VM. Each 4-byte chunk is interpreted as a **little-endian** `u32` and stored as a `Felt`. -The output order matches the big-endian limb order described in [Section 5.3](#53-masm-limb-representation). +The output order matches the big-endian limb order described in [Section 6.3](#63-masm-limb-representation). -This is used when constructing `NoteStorage` for B2AGG notes (see [Section 3.1](#31-b2agg)) and CLAIM notes (see [Section 3.2](#32-claim)). +This is used when constructing `NoteStorage` for B2AGG notes (see [Section 4.1](#41-b2agg)) and CLAIM notes (see [Section 4.2](#42-claim)). -### 5.5 Endianness Summary +### 6.5 Endianness Summary The conversion involves multiple levels of byte ordering: this table clarifies the different conventions used. @@ -795,7 +963,7 @@ The conversion involves multiple levels of byte ordering: this table clarifies t The byte swap (`swap_u32_bytes`) in the MASM `build_felt` procedure bridges between the little-endian bytes within each limb in `NoteStorage` and the big-endian-bytes within the `u32` pairs needed to construct the prefix and suffix in the MASM `build_felt` procedure. -### 5.6 Roundtrip Guarantee +### 6.6 Roundtrip Guarantee The encoding is a bijection over the set of valid `AccountId` values: for every valid `AccountId`, `from_account_id` followed by `to_account_id` (or the MASM equivalent) @@ -803,7 +971,7 @@ recovers the original. --- -## 6. Faucet Registry +## 7. Faucet Registry The AggLayer bridge connects multiple chains, each with its own native token ecosystem. When tokens move between chains, they need a representation on the destination chain. @@ -819,7 +987,7 @@ Terminology: AggLayer faucet. On EVM chains, each non-native Miden token would be represented by a deployed wrapped ERC20 contract. -A faucet must be registered in the [Bridge Contract](#21-bridge-account-component) before it can participate in bridging. The +A faucet must be registered in the [Bridge Contract](#31-bridge-account-component) before it can participate in bridging. The bridge maintains two registry maps: - **Faucet registry** (`agglayer::bridge::faucet_registry_map`): maps faucet account IDs @@ -830,9 +998,9 @@ bridge maintains two registry maps: correct faucet for a given origin token (see `bridge_config::lookup_faucet_by_token_address`). -Both registries are populated atomically by `bridge_config::register_faucet` during the [`CONFIG_AGG_BRIDGE`](#33-config_agg_bridge) note consumption. +Both registries are populated atomically by `bridge_config::register_faucet` during the [`CONFIG_AGG_BRIDGE`](#43-config_agg_bridge) note consumption. -### 6.1 Bridging-in: Registering non-native faucets on Miden +### 7.1 Bridging-in: Registering non-native faucets on Miden When a new ERC20 token is bridged to Miden for the first time, a corresponding AggLayer faucet account must be created and registered. The faucet serves as the mint/burn @@ -847,7 +1015,7 @@ configuration: - Scale factor: the exponent used to convert between EVM U256 amounts and Field elements on Miden - Metadata hash: `keccak256(abi.encode(name, symbol, decimals))`. This is precomputed by the bridge admin at faucet creation time and is currently not verified onchain (TODO Verify metadata hash onchain ([#2586](https://github.com/0xMiden/protocol/issues/2586))) -Registration is performed via [`CONFIG_AGG_BRIDGE`](#33-config_agg_bridge) notes. The bridge +Registration is performed via [`CONFIG_AGG_BRIDGE`](#43-config_agg_bridge) notes. The bridge operator creates a `CONFIG_AGG_BRIDGE` note containing the faucet's account ID and the origin token address, then sends it to the bridge account. On consumption, the note script calls `bridge_config::register_faucet`, which performs a two-step registration: @@ -860,7 +1028,7 @@ script calls `bridge_config::register_faucet`, which performs a two-step registr The token registry enables the bridge to resolve which Miden-side faucet corresponds to a given origin token address during CLAIM note processing. When the bridge -processes a [`CLAIM`](#32-claim) note, it reads the origin token address from the leaf data and calls +processes a [`CLAIM`](#42-claim) note, it reads the origin token address from the leaf data and calls `bridge_config::lookup_faucet_by_token_address` to find the registered faucet. This lookup hashes the address with Poseidon2 and retrieves the faucet ID from the token registry map. If the token address is not registered, the `CLAIM` note consumption will fail. @@ -869,7 +1037,7 @@ This means that the bridge admin must register the faucet on the Miden side befo The bridge admin is a trusted role, and is the sole entity that can register faucets on the Miden side (due to the caller restriction on [`bridge_config::register_faucet`](#bridge_configregister_faucet)). -### 6.2 Bridging-out: How Miden-native tokens are registered on other chains +### 7.2 Bridging-out: How Miden-native tokens are registered on other chains When an asset is bridged out from Miden, [`bridge_out::bridge_out`](#bridge_outbridge_out) constructs a leaf for the Local Exit Tree. The leaf includes the metadata hash, which the bridge fetches from @@ -889,7 +1057,7 @@ A Miden-native faucet uses the same storage layout and registration flow as a wrapped faucet. The key difference is what values are stored in the conversion metadata: -- `origin_token_address`: the faucet's own `AccountId` as per the [Embedded Format](#52-embedded-format). +- `origin_token_address`: the faucet's own `AccountId` as per the [Embedded Format](#62-embedded-format). - `origin_network`: Miden's network ID as assigned by AggLayer (currently unassigned). - `metadata_hash`: `keccak256(abi.encode(name, symbol, decimals))` - same as for wrapped faucets. diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm index 7c486135e0..4df7db2aac 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm @@ -8,41 +8,42 @@ use miden::protocol::native_account # ================================================================================================= const ERR_GER_NOT_FOUND = "GER not found in storage" -const ERR_FAUCET_NOT_REGISTERED="faucet is not registered in the bridge's faucet registry" -const ERR_TOKEN_NOT_REGISTERED="token address is not registered in the bridge's token registry" -const ERR_SENDER_NOT_BRIDGE_ADMIN="note sender is not the bridge admin" -const ERR_SENDER_NOT_GER_MANAGER="note sender is not the global exit root manager" +const ERR_FAUCET_NOT_REGISTERED = "faucet is not registered in the bridge's faucet registry" +const ERR_TOKEN_NOT_REGISTERED = "token address is not registered in the bridge's token registry" +const ERR_SENDER_NOT_BRIDGE_ADMIN = "note sender is not the bridge admin" +const ERR_SENDER_NOT_GER_MANAGER = "note sender is not the global exit root manager" # CONSTANTS # ================================================================================================= # Storage slots -const BRIDGE_ADMIN_SLOT=word("agglayer::bridge::admin_account_id") -const GER_MANAGER_SLOT=word("agglayer::bridge::ger_manager_account_id") -const GER_MAP_STORAGE_SLOT=word("agglayer::bridge::ger_map") -const FAUCET_REGISTRY_MAP_SLOT=word("agglayer::bridge::faucet_registry_map") -const TOKEN_REGISTRY_MAP_SLOT=word("agglayer::bridge::token_registry_map") +const BRIDGE_ADMIN_SLOT = word("agglayer::bridge::admin_account_id") +const GER_MANAGER_SLOT = word("agglayer::bridge::ger_manager_account_id") +const GER_MAP_STORAGE_SLOT = word("agglayer::bridge::ger_map") +const FAUCET_REGISTRY_MAP_SLOT = word("agglayer::bridge::faucet_registry_map") +const TOKEN_REGISTRY_MAP_SLOT = word("agglayer::bridge::token_registry_map") # Flags -const GER_KNOWN_FLAG=1 -const IS_FAUCET_REGISTERED_FLAG=1 +const GER_KNOWN_FLAG = 1 +const IS_FAUCET_REGISTERED_FLAG = 1 # Offset in the local memory of the `hash_token_address` procedure -const TOKEN_ADDR_HASH_PTR=0 +const TOKEN_ADDR_HASH_PTR = 0 # PUBLIC INTERFACE # ================================================================================================= #! Updates the Global Exit Root (GER) in the bridge account storage. #! -#! Computes hash(GER) = poseidon2::merge(GER_LOWER, GER_UPPER) and stores it in a map -#! with value [GER_KNOWN_FLAG, 0, 0, 0] to indicate the GER is known. -#! -#! Panics if the note sender is not the global exit root manager. +#! Computes hash(GER) = poseidon2::merge(GER_LOWER, GER_UPPER) and stores it in a map with value +#! [GER_KNOWN_FLAG, 0, 0, 0] to indicate the GER is known. #! #! Inputs: [GER_LOWER[4], GER_UPPER[4], pad(8)] #! Outputs: [pad(16)] #! +#! Panics if: +#! - the note sender is not the global exit root manager. +#! #! Invocation: call pub proc update_ger # assert the note sender is the global exit root manager. @@ -65,14 +66,15 @@ pub proc update_ger exec.native_account::set_map_item # => [OLD_VALUE, pad(12)] + dropw # => [pad(16)] end #! Asserts that the provided GER is valid (exists in storage). #! -#! Computes hash(GER) = poseidon2::merge(GER_LOWER, GER_UPPER) and looks up the hash in -#! the GER storage map. Panics if the GER has never been stored. +#! Computes hash(GER) = poseidon2::merge(GER_LOWER, GER_UPPER) and looks up the hash in the GER +#! storage map. Panics if the GER has never been stored. #! #! Inputs: [GER_ROOT[8]] #! Outputs: [] @@ -81,7 +83,7 @@ end #! - the GER is not found in storage. #! #! Invocation: exec -pub proc assert_valid_ger +proc assert_valid_ger # compute hash(GER) exec.poseidon2::merge # => [GER_HASH] @@ -104,14 +106,15 @@ end #! #! 1. Writes `KEY -> [1, 0, 0, 0]` into the `faucet_registry` map, where #! `KEY = [0, 0, faucet_id_suffix, faucet_id_prefix]`. -#! 2. Writes `hash(tokenAddress[5]) -> [faucet_id_suffix, faucet_id_prefix, 0, 0]` -#! into the `token_registry` map. -#! -#! Panics if the note sender is not the bridge admin. +#! 2. Writes `hash(tokenAddress[5]) -> [faucet_id_suffix, faucet_id_prefix, 0, 0]` into the +#! `token_registry` map. #! #! Inputs: [origin_token_addr(5), faucet_id_suffix, faucet_id_prefix, pad(9)] #! Outputs: [pad(16)] #! +#! Panics if: +#! - the note sender is not the bridge admin. +#! #! Invocation: call pub proc register_faucet # assert the note sender is the bridge admin. @@ -120,19 +123,24 @@ pub proc register_faucet # Save faucet ID for later use in token_registry dup.6 dup.6 - # => [faucet_id_suffix, faucet_id_prefix, origin_token_addr(5), faucet_id_suffix, faucet_id_prefix, pad(9)] + # => [faucet_id_suffix, faucet_id_prefix, origin_token_addr(5), + # faucet_id_suffix, faucet_id_prefix, pad(9)] # --- 1. Register faucet in faucet_registry --- + # set_map_item expects [slot_id(2), KEY, VALUE] and returns [OLD_VALUE]. # Build KEY = [0, 0, suffix, prefix] and VALUE = [IS_FAUCET_REGISTERED_FLAG, 0, 0, 0] push.0.0.0.IS_FAUCET_REGISTERED_FLAG - # => [IS_FAUCET_REGISTERED_FLAG, 0, 0, 0, faucet_id_suffix, faucet_id_prefix, origin_token_addr(5), faucet_id_suffix, faucet_id_prefix, pad(9)] + # => [IS_FAUCET_REGISTERED_FLAG, 0, 0, 0, + # faucet_id_suffix, faucet_id_prefix, origin_token_addr(5), + # faucet_id_suffix, faucet_id_prefix, pad(9)] - movup.5 movup.5 - # => [faucet_id_suffix, faucet_id_prefix, IS_FAUCET_REGISTERED_FLAG, 0, 0, 0, origin_token_addr(5), faucet_id_suffix, faucet_id_prefix, pad(9)] - - push.0.0 - # => [[0, 0, faucet_id_suffix, faucet_id_prefix], [IS_FAUCET_REGISTERED_FLAG, 0, 0, 0], origin_token_addr(5), faucet_id_suffix, faucet_id_prefix, pad(9)] + movup.5 movup.5 push.0.0 + # => [ + # [0, 0, faucet_id_suffix, faucet_id_prefix], + # [IS_FAUCET_REGISTERED_FLAG, 0, 0, 0], + # origin_token_addr(5), faucet_id_suffix, faucet_id_prefix, pad(9) + # ] push.FAUCET_REGISTRY_MAP_SLOT[0..2] exec.native_account::set_map_item @@ -148,10 +156,7 @@ pub proc register_faucet # => [TOKEN_ADDR_HASH, faucet_id_suffix, faucet_id_prefix, pad(10)] # Build VALUE = [0, 0, faucet_id_suffix, faucet_id_prefix] - movup.5 movup.5 - # => [faucet_id_suffix, faucet_id_prefix, TOKEN_ADDR_HASH, pad(10)] - - push.0.0 + movup.5 movup.5 push.0.0 # => [0, 0, faucet_id_suffix, faucet_id_prefix, TOKEN_ADDR_HASH, pad(10)] swapw @@ -167,8 +172,7 @@ end #! Asserts that a faucet is registered in the bridge's faucet registry. #! -#! Looks up the faucet ID in the faucet registry map and asserts the registration -#! flag is set. +#! Looks up the faucet ID in the faucet registry map and asserts the registration flag is set. #! #! Inputs: [faucet_id_suffix, faucet_id_prefix] #! Outputs: [] @@ -177,7 +181,7 @@ end #! - the faucet is not registered in the faucet registry. #! #! Invocation: exec -pub proc assert_faucet_registered +proc assert_faucet_registered # Build KEY = [0, 0, faucet_id_suffix, faucet_id_prefix] push.0.0 # => [0, 0, faucet_id_suffix, faucet_id_prefix] @@ -202,7 +206,7 @@ end #! - the token address is not registered in the token registry. #! #! Invocation: exec -pub proc lookup_faucet_by_token_address +proc lookup_faucet_by_token_address # Hash the token address exec.hash_token_address # => [TOKEN_ADDR_HASH] @@ -213,7 +217,7 @@ pub proc lookup_faucet_by_token_address # Assert the token is registered: faucet_id_prefix is always non-zero for valid account IDs. dup.3 dup.3 push.0.0 - # => [0, 0, faucet_id_suffix, faucet_id_prefix, 0, 0, faucet_id_suffix, faucet_id_prefix] + # => [0, 0, faucet_id_suffix, faucet_id_prefix, 0, 0, faucet_id_suffix, faucet_id_prefix] exec.account_id::is_equal # => [is_id_zero, 0, 0, faucet_id_suffix, faucet_id_prefix] @@ -250,8 +254,8 @@ end #! Asserts that the note sender matches the bridge admin stored in account storage. #! -#! Reads the bridge admin account ID from BRIDGE_ADMIN_SLOT and compares it against -#! the sender of the currently executing note. Panics if they do not match. +#! Reads the bridge admin account ID from BRIDGE_ADMIN_SLOT and compares it against the sender of +#! the currently executing note. #! #! Inputs: [pad(16)] #! Outputs: [pad(16)] @@ -260,9 +264,7 @@ end #! - the note sender does not match the bridge admin account ID. #! #! Invocation: exec -pub proc assert_sender_is_bridge_admin - # => [pad(16)] - +proc assert_sender_is_bridge_admin push.BRIDGE_ADMIN_SLOT[0..2] exec.active_account::get_item # => [0, 0, admin_suffix, admin_prefix, pad(16)] @@ -280,8 +282,8 @@ end #! Asserts that the note sender matches the global exit root manager stored in account storage. #! -#! Reads the GER manager account ID from GER_MANAGER_SLOT and compares it against -#! the sender of the currently executing note. Panics if they do not match. +#! Reads the GER manager account ID from GER_MANAGER_SLOT and compares it against the sender of the +#! currently executing note. #! #! Inputs: [pad(16)] #! Outputs: [pad(16)] @@ -290,9 +292,7 @@ end #! - the note sender does not match the GER manager account ID. #! #! Invocation: exec -pub proc assert_sender_is_ger_manager - # => [pad(16)] - +proc assert_sender_is_ger_manager push.GER_MANAGER_SLOT[0..2] exec.active_account::get_item # => [0, 0, mgr_suffix, mgr_prefix, pad(16)] diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm index 146531502f..f101c90b40 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_in.masm @@ -9,7 +9,6 @@ use miden::core::crypto::hashes::poseidon2 use miden::core::mem use miden::core::word use miden::protocol::note -use miden::protocol::note::NOTE_TYPE_PUBLIC use miden::protocol::output_note use miden::protocol::output_note::ATTACHMENT_KIND_NONE use miden::protocol::active_account @@ -44,7 +43,7 @@ const IS_CLAIMED_FLAG = [1, 0, 0, 0] # ------------------------------------------------------------------------------------------------- # The slot in this component's storage layout where claim nullifiers are stored. -# Map entries: RPO(leaf_index, source_bridge_network) => [1, 0, 0, 0] +# Map entries: Poseidon2(leaf_index, source_bridge_network) => [1, 0, 0, 0] const CLAIM_NULLIFIERS_SLOT = word("agglayer::bridge::claim_nullifiers") # Storage slot constants for the CGI (claimed global index) chain hash. @@ -59,12 +58,21 @@ const CGI_CHAIN_HASH_HI_SLOT_NAME = word("agglayer::bridge::cgi_chain_hash_hi") const CLAIM_PROOF_DATA_WORD_LEN = 134 const CLAIM_LEAF_DATA_WORD_LEN = 8 -# MINT note storage layout (public mode, 18 items): -# [0]: tag, [1]: amount, [2]: attachment_kind, [3]: attachment_scheme, -# [4-7]: ATTACHMENT, [8-11]: P2ID_SCRIPT_ROOT, [12-15]: SERIAL_NUM, -# [16]: account_id_suffix, [17]: account_id_prefix +# MINT note storage layout (public mode, 18 felts total): +# - tag [0] : 1 felt +# - amount [1] : 1 felt +# - attachment_kind [2] : 1 felt +# - attachment_scheme [3] : 1 felt +# - ATTACHMENT [4..7] : 4 felts +# - P2ID_SCRIPT_ROOT [8..11] : 4 felts +# - SERIAL_NUM [12..15] : 4 felts +# - account_id_suffix [16] : 1 felt +# - account_id_prefix [17] : 1 felt const MINT_NOTE_NUM_STORAGE_ITEMS = 18 +# P2ID output note constants +const OUTPUT_NOTE_TYPE_PUBLIC = 1 + # P2ID attachment constants (the P2ID note created by the faucet has no attachment) const P2ID_ATTACHMENT_SCHEME_NONE = 0 @@ -151,11 +159,11 @@ const CLAIM_DEST_ID_SUFFIX_LOCAL = 1 # PUBLIC INTERFACE # ================================================================================================= -#! Validates a claim against the AggLayer bridge and creates a MINT note for the aggfaucet. +#! Validates a claim against the AggLayer bridge and creates a MINT note for the AggLayer faucet. #! #! This procedure is called by the CLAIM note script. It validates the Merkle proof and then #! looks up the faucet account ID from the token registry using the origin token address from -#! the leaf data, and creates a MINT note targeting the aggfaucet. +#! the leaf data, and creates a MINT note targeting the AggLayer Faucet. #! #! The MINT note uses the standard MINT note pattern (public mode) with 18 storage items. #! See `write_mint_note_storage` for the full storage layout. @@ -227,7 +235,7 @@ pub proc claim exec.verify_claim_amount # => [faucet_id_suffix, faucet_id_prefix, pad(16)] - # Build MINT output note targeting the aggfaucet + # Build MINT output note targeting the AggLayer faucet loc_load.CLAIM_DEST_ID_PREFIX_LOCAL loc_load.CLAIM_DEST_ID_SUFFIX_LOCAL # => [destination_id_suffix, destination_id_prefix, faucet_id_suffix, faucet_id_prefix, pad(16)] @@ -348,12 +356,13 @@ pub proc process_global_index_rollup repeat.5 assertz.err=ERR_LEADING_BITS_NON_ZERO end + # => [mainnet_flag_le, rollup_index_le, leaf_index_le] # the next element is the mainnet flag (LE-packed u32) # for a rollup deposit it must be exactly 0; zero is byte-order-independent, # so no swap is needed before asserting - # => [mainnet_flag_le, rollup_index_le, leaf_index_le] assertz.err=ERR_BRIDGE_NOT_ROLLUP + # => [rollup_index_le, leaf_index_le] # byte-swap rollup_index from LE to BE exec.utils::swap_u32_bytes @@ -366,8 +375,8 @@ end #! Computes the Global Exit Tree (GET) root from the mainnet and rollup exit roots. #! -#! The mainnet exit root is expected at `exit_roots_ptr` and -#! the rollup exit root is expected at `exit_roots_ptr + 8`. +#! The mainnet exit root is expected at `exit_roots_ptr` and the rollup exit root is expected at +#! `exit_roots_ptr + 8`. #! #! Inputs: [exit_roots_ptr] #! Outputs: [GER_ROOT[8]] @@ -376,6 +385,7 @@ end pub proc compute_ger(exit_roots_ptr: MemoryAddress) -> DoubleWord push.64 swap # => [exit_roots_ptr, len_bytes] + exec.keccak256::hash_bytes # => [GER_ROOT[8]] end @@ -417,14 +427,15 @@ pub proc verify_merkle_proof( movdn.8 exec.word::eq and # => [verification_flag] end -#! Verifies that the faucet_mint_amount matches the raw U256 amount from the leaf data, -#! scaled down by the faucet's scale factor. + +#! Verifies that the faucet_mint_amount matches the raw U256 amount from the leaf data, scaled down +#! by the faucet's scale factor. #! #! This procedure: #! 1. Performs an FPI call to the faucet's `get_scale` procedure to retrieve the scale factor. #! 2. Loads the raw U256 amount from the leaf data in memory. -#! 3. Calls `verify_u256_to_native_amount_conversion` to assert that: -#! faucet_mint_amount == floor(raw_amount / 10^scale) +#! 3. Calls `verify_u256_to_native_amount_conversion` to assert that +#! `faucet_mint_amount == floor(raw_amount / 10^scale)`. #! #! Inputs: [faucet_id_suffix, faucet_id_prefix] #! Outputs: [] @@ -520,16 +531,20 @@ end #! Invocation: exec proc verify_leaf movupw.2 + # => [PROOF_DATA_KEY, LEAF_VALUE[8]] + # load proof data from the advice map into memory adv.push_mapval # => [PROOF_DATA_KEY, LEAF_VALUE[8]] push.SMT_PROOF_LOCAL_EXIT_ROOT_PTR push.CLAIM_PROOF_DATA_WORD_LEN exec.mem::pipe_preimage_to_memory drop + # => [LEAF_VALUE[8]] # 1. compute GER from mainnet + rollup exit roots push.EXIT_ROOTS_PTR # => [exit_roots_ptr, LEAF_VALUE[8]] + exec.compute_ger # => [GER[8], LEAF_VALUE[8]] @@ -546,8 +561,8 @@ proc verify_leaf # [gi0, gi1, gi2, gi3, gi4, mainnet_flag_le, rollup_index_le, leaf_index_le] # gi0 is on top (position 0). The mainnet flag is at stack position 5. - # Duplicate the mainnet flag element, byte-swap from LE to BE, - # assert it is a valid boolean (0 or 1), then use it to branch. + # Duplicate the mainnet flag element, byte-swap from LE to BE, assert it is a valid boolean + # (0 or 1), then use it to branch. dup.5 exec.utils::swap_u32_bytes dup # => [mainnet_flag, mainnet_flag, GLOBAL_INDEX[8], LEAF_VALUE[8]] @@ -556,6 +571,7 @@ proc verify_leaf if.true # ==================== MAINNET DEPOSIT ==================== + exec.process_global_index_mainnet # => [leaf_index, LEAF_VALUE[8]] @@ -583,8 +599,9 @@ proc verify_leaf # => [] else # ==================== ROLLUP DEPOSIT ==================== - # mainnet_flag = 0; extract rollup_index and leaf_index via helper, - # then do two-level verification + + # mainnet_flag = 0; extract rollup_index and leaf_index via helper, then do two-level + # verification exec.process_global_index_rollup # => [leaf_index, rollup_index, LEAF_VALUE[8]] @@ -614,7 +631,6 @@ proc verify_leaf # => [LOCAL_EXIT_ROOT_LO, LOCAL_EXIT_ROOT_HI, rollup_index, rollup_exit_root_ptr] push.SMT_PROOF_ROLLUP_EXIT_ROOT_PTR movdn.8 - # => [LOCAL_EXIT_ROOT[8], smt_proof_rollup_ptr, rollup_index, rollup_exit_root_ptr] exec.verify_merkle_proof @@ -642,8 +658,8 @@ proc verify_leaf # => [] end -#! Computes the claim nullifier as RPO(leaf_index, source_bridge_network), then checks -#! that the claim has not been spent and marks it as spent. +#! Computes the claim nullifier as Poseidon2(leaf_index, source_bridge_network), then checks that +#! the claim has not been spent and marks it as spent. #! #! This mimics the Solidity `_setAndCheckClaimed(leafIndex, sourceBridgeNetwork)` function. #! See: https://github.com/agglayer/agglayer-contracts/blob/60d06fc3224792ce55dc2690d66b6719a73398e7/contracts/v2/PolygonZkEVMBridgeV2.sol#L987 @@ -674,10 +690,10 @@ proc set_and_check_claimed # => [] end -#! Checks that the CLAIM note has not already been spent, and marks it as spent -#! by storing [1, 0, 0, 0] in the CLAIM_NULLIFIERS_SLOT map. +#! Checks that the CLAIM note has not already been spent, and marks it as spent by storing +#! [1, 0, 0, 0] in the CLAIM_NULLIFIERS_SLOT map. #! -#! The nullifier is computed as RPO(leaf_index, source_bridge_network), which uniquely +#! The nullifier is computed as Poseidon2(leaf_index, source_bridge_network), which uniquely #! identifies a claim in the Global Exit Root (GER) as per the AggLayer protocol. #! #! Inputs: [NULLIFIER] @@ -711,10 +727,11 @@ proc claim_batch_pipe_double_words # 1) Verify PROOF_DATA_KEY mem_storew_be.CLAIM_PROOF_DATA_KEY_MEM_ADDR adv.push_mapval - # => [PROOF_DATA_KEY] + # => [PROOF_DATA_KEY, LEAF_DATA_KEY] push.CLAIM_PROOF_DATA_START_PTR push.CLAIM_PROOF_DATA_WORD_LEN exec.mem::pipe_double_words_preimage_to_memory drop + # => [LEAF_DATA_KEY] # 2) Verify LEAF_DATA_KEY mem_storew_be.CLAIM_LEAF_DATA_KEY_MEM_ADDR @@ -723,6 +740,7 @@ proc claim_batch_pipe_double_words push.CLAIM_LEAF_DATA_START_PTR push.CLAIM_LEAF_DATA_WORD_LEN exec.mem::pipe_double_words_preimage_to_memory drop + # => [] end #! Extracts the destination account ID as address[5] from memory. @@ -742,8 +760,12 @@ proc load_destination_address # => [address[5]] end -# Inputs: [] -# Outputs: [U256[0], U256[1]] +#! Loads the claim note asset amount onto the stack from memory. +#! +#! Inputs: [] +#! Outputs: [U256_LO, U256_HI] +#! +#! Invocation: exec proc load_raw_claim_amount mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_7 mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_6 @@ -753,6 +775,7 @@ proc load_raw_claim_amount mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_2 mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_1 mem_load.OUTPUT_NOTE_ASSET_AMOUNT_MEM_ADDR_0 + # => [U256_LO, U256_HI] end #! Reads the origin token address (5 felts) from the leaf data in memory. @@ -770,10 +793,10 @@ proc load_origin_token_address # => [origin_token_addr(5)] end -#! Builds a PUBLIC MINT output note targeting the aggfaucet. +#! Builds a PUBLIC MINT output note targeting the AggLayer Faucet. #! -#! The MINT note uses public mode (18 storage items) so the AggFaucet creates -#! a PUBLIC P2ID note on consumption. This procedure orchestrates three steps: +#! The MINT note uses public mode (18 storage items) so the AggLayer Faucet creates a PUBLIC P2ID +#! note on consumption. This procedure orchestrates three steps: #! 1. Write all 18 MINT note storage items to global memory. #! 2. Build the MINT note recipient digest from the storage. #! 3. Create the output note, and set the attachment. @@ -785,11 +808,11 @@ end proc build_mint_output_note # Step 1: Write all 18 MINT note storage items to global memory exec.write_mint_note_storage - # => [faucet_id_prefix, faucet_id_suffix] + # => [faucet_id_suffix, faucet_id_prefix] # Step 2: Build the MINT note recipient digest exec.build_mint_recipient - # => [MINT_RECIPIENT, faucet_id_prefix, faucet_id_suffix] + # => [MINT_RECIPIENT, faucet_id_suffix, faucet_id_prefix] # Step 3: Create the output note and set the faucet attachment exec.create_mint_note_with_attachment @@ -902,40 +925,38 @@ proc build_mint_recipient # => [MINT_RECIPIENT] end - - #! Creates the MINT output note and sets the NetworkAccountTarget attachment on it. #! -#! Creates a public output note with no assets, and sets the attachment so only the -#! target faucet can consume the note. +#! Creates a public output note with no assets, and sets the attachment so only the target faucet +#! can consume the note. #! -#! Inputs: [MINT_RECIPIENT, faucet_id_prefix, faucet_id_suffix] +#! Inputs: [MINT_RECIPIENT, faucet_id_suffix, faucet_id_prefix] #! Outputs: [] #! #! Invocation: exec proc create_mint_note_with_attachment # Create the MINT output note targeting the faucet - push.NOTE_TYPE_PUBLIC - # => [note_type, MINT_RECIPIENT, faucet_id_prefix, faucet_id_suffix] + push.OUTPUT_NOTE_TYPE_PUBLIC + # => [note_type, MINT_RECIPIENT, faucet_id_suffix, faucet_id_prefix] # Set tag to DEFAULT push.DEFAULT_TAG - # => [note_type, MINT_RECIPIENT, faucet_id_prefix, faucet_id_suffix] + # => [tag, note_type, MINT_RECIPIENT, faucet_id_suffix, faucet_id_prefix] # Create the output note (no assets - MINT notes carry no assets) exec.output_note::create - # => [note_idx, faucet_id_prefix, faucet_id_suffix] + # => [note_idx, faucet_id_suffix, faucet_id_prefix] movdn.2 - # => [faucet_id_prefix, faucet_id_suffix, note_idx] + # => [faucet_id_suffix, faucet_id_prefix, note_idx] # Set the attachment on the MINT note to target the faucet account # NetworkAccountTarget attachment: targets the faucet so only it can consume the note - # network_account_target::new expects [prefix, suffix, exec_hint] + # network_account_target::new expects [suffix, prefix, exec_hint] # and returns [attachment_scheme, attachment_kind, ATTACHMENT] push.ALWAYS # exec_hint = ALWAYS movdn.2 - # => [faucet_id_prefix, faucet_id_suffix, exec_hint, note_idx] + # => [faucet_id_suffix, faucet_id_prefix, exec_hint, note_idx] exec.network_account_target::new # => [attachment_scheme, attachment_kind, ATTACHMENT, note_idx] @@ -1091,4 +1112,4 @@ proc store_cgi_chain_hash push.CGI_CHAIN_HASH_HI_SLOT_NAME[0..2] exec.native_account::set_item dropw # => [] -end \ No newline at end of file +end diff --git a/crates/miden-agglayer/asm/agglayer/bridge/bridge_out.masm b/crates/miden-agglayer/asm/agglayer/bridge/bridge_out.masm index ef96e970d6..824ad3da19 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/bridge_out.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/bridge_out.masm @@ -10,15 +10,13 @@ use miden::standards::note_tag::DEFAULT_TAG use miden::standards::note::execution_hint::ALWAYS use miden::protocol::types::MemoryAddress use miden::protocol::output_note -use miden::core::crypto::hashes::keccak256 use miden::core::crypto::hashes::poseidon2 -use miden::core::word use agglayer::common::utils use agglayer::faucet -> agglayer_faucet use agglayer::bridge::bridge_config use agglayer::bridge::leaf_utils use agglayer::bridge::merkle_tree_frontier -use agglayer::common::utils::EthereumAddressFormat +use agglayer::common::eth_address::EthereumAddressFormat # CONSTANTS # ================================================================================================= @@ -78,7 +76,7 @@ const ATTACHMENT_KIND_LOC=13 # ------------------------------------------------------------------------------------------------- const LEAF_TYPE_ASSET=0 -use miden::protocol::note::NOTE_TYPE_PUBLIC +const PUBLIC_NOTE=1 const BURN_NOTE_NUM_STORAGE_ITEMS=0 # PUBLIC INTERFACE @@ -176,7 +174,7 @@ pub proc bridge_out exec.asset::load swapw dropw # => [ASSET_KEY, PROC_MAST_ROOT, pad(16)] - # ASSET layout: [0, 0, faucet_id_suffix, faucet_id_prefix] + # ASSET_KEY layout: [0, 0, faucet_id_suffix, faucet_id_prefix] # Extract faucet ID, drop padding and amount drop drop @@ -188,6 +186,7 @@ pub proc bridge_out push.LEAF_DATA_START_PTR push.METADATA_HASH_OFFSET add movdn.8 # => [METADATA_HASH_LO, METADATA_HASH_HI, metadata_hash_ptr, pad(8)] + exec.utils::mem_store_double_word_unaligned # => [pad(16)] @@ -215,6 +214,7 @@ pub proc bridge_out locaddr.BRIDGE_OUT_BURN_ASSET_LOC exec.asset::load # => [ASSET_KEY, ASSET_VALUE, pad(16)] + exec.create_burn_note # => [pad(16)] end @@ -222,19 +222,19 @@ end # HELPER PROCEDURES # ================================================================================================= -#! Validates that a faucet is registered in the bridge's faucet registry, then performs -#! an FPI call to the faucet's `asset_to_origin_asset` procedure to obtain the scaled -#! amount, origin token address, and origin network. +#! Validates that a faucet is registered in the bridge's faucet registry, then performs an FPI call +#! to the faucet's `asset_to_origin_asset` procedure to obtain the scaled amount, origin token +#! address, and origin network. #! #! Inputs: [ASSET_KEY, ASSET_VALUE] -#! Outputs: [AMOUNT_U256[0](4), AMOUNT_U256[1](4), origin_addr(5), origin_network] +#! Outputs: [AMOUNT_U256_LO, AMOUNT_U256_HI, origin_addr(5), origin_network] #! #! Where: #! - ASSET_KEY is the vault key of the asset to be bridged out. #! - ASSET_VALUE is the value of the asset to be bridged out. -#! - AMOUNT_U256: scaled amount as 8 u32 limbs (little-endian) -#! - origin_addr: origin token address (5 u32 felts) -#! - origin_network: origin network identifier +#! - AMOUNT_U256: scaled amount as 8 u32 limbs (little-endian). +#! - origin_addr: origin token address (5 u32 felts). +#! - origin_network: origin network identifier. #! #! Panics if: #! - The faucet is not registered in the faucet registry. @@ -248,9 +248,8 @@ proc convert_asset padw padw swapdw end # => [ASSET_KEY, ASSET_VALUE, pad(16)] - swapw - exec.asset::fungible_value_into_amount - movdn.4 + + swapw exec.asset::fungible_value_into_amount movdn.4 # => [ASSET_KEY, amount, pad(16)] exec.asset::key_into_faucet_id @@ -270,13 +269,13 @@ proc convert_asset # => [faucet_id_suffix, faucet_id_prefix, PROC_MAST_ROOT, amount, pad(15), pad(1)] exec.tx::execute_foreign_procedure - # => [AMOUNT_U256[0](4), AMOUNT_U256[1](4), origin_addr(5), origin_network, pad(2), pad(1)] + # => [AMOUNT_U256_LO, AMOUNT_U256_HI, origin_addr(5), origin_network, pad(2), pad(1)] # drop the 3 trailing padding elements repeat.3 movup.14 drop end - # => [AMOUNT_U256[0](4), AMOUNT_U256[1](4), origin_addr(5), origin_network] + # => [AMOUNT_U256_LO, AMOUNT_U256_HI, origin_addr(5), origin_network] end #! Computes the leaf value from the leaf data in memory and appends it to the Local Exit Tree. @@ -324,8 +323,8 @@ end #! Loads the LET (Local Exit Tree) frontier from account storage into memory. #! -#! The num_leaves is read from its dedicated value slot, and the 32 frontier entries are read -#! from the LET map slot (double-word array, indices 0..31). The data is placed into memory at +#! The num_leaves is read from its dedicated value slot, and the 32 frontier entries are read from +#! the LET map slot (double-word array, indices 0..31). The data is placed into memory at #! LET_FRONTIER_MEM_PTR, matching the layout expected by append_and_update_frontier: #! [num_leaves, 0, 0, 0, [[FRONTIER_NODE_LO, FRONTIER_NODE_HI]; 32]] #! @@ -394,6 +393,7 @@ proc save_let_root_and_num_leaves # 3. Save new_leaf_count to its value slot as [new_leaf_count, 0, 0, 0] push.0.0.0 movup.3 # => [new_leaf_count, 0, 0, 0] + push.LET_NUM_LEAVES_SLOT[0..2] exec.native_account::set_item dropw @@ -455,6 +455,7 @@ proc write_address_to_memory(mem_ptr: MemoryAddress, address: EthereumAddressFor # => [mem_ptr+4, address(1)] mem_store + # => [] end #! Computes the SERIAL_NUM of the outputted BURN note. @@ -527,7 +528,7 @@ proc create_burn_note exec.note::build_recipient # => [RECIPIENT] - push.NOTE_TYPE_PUBLIC + push.PUBLIC_NOTE push.DEFAULT_TAG # => [tag, note_type, RECIPIENT] @@ -539,8 +540,7 @@ proc create_burn_note # => [note_idx, pad(15)] # duplicate note_idx: one for set_attachment, one for add_asset - dup - swapw loc_loadw_le.ATTACHMENT_LOC + dup swapw loc_loadw_le.ATTACHMENT_LOC # => [NOTE_ATTACHMENT, note_idx, note_idx, pad(11)] loc_load.ATTACHMENT_KIND_LOC diff --git a/crates/miden-agglayer/asm/agglayer/bridge/leaf_utils.masm b/crates/miden-agglayer/asm/agglayer/bridge/leaf_utils.masm index f9d697c66d..2ef1cbf4a2 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/leaf_utils.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/leaf_utils.masm @@ -20,13 +20,14 @@ const PACKED_DATA_NUM_ELEMENTS = 29 #! Given a memory address where the unpacked leaf data starts, packs the leaf data in-place, and #! computes the leaf value by hashing the packed bytes. #! -#! Inputs: [LEAF_DATA_START_PTR] +#! Inputs: [leaf_data_start_ptr] #! Outputs: [LEAF_VALUE[8]] #! #! Invocation: exec pub proc compute_leaf_value(leaf_data_start_ptr: MemoryAddress) -> DoubleWord dup # => [leaf_data_start_ptr, leaf_data_start_ptr] + exec.pack_leaf_data # => [leaf_data_start_ptr] @@ -39,17 +40,17 @@ end #! Packs the raw leaf data by shifting left 3 bytes to match Solidity's abi.encodePacked format. #! -#! The raw data has leafType occupying 4 bytes (as a u32 felt) but Solidity's abi.encodePacked -#! only uses 1 byte for uint8 leafType. This procedure shifts all data left by 3 bytes so that: +#! The raw data has leafType occupying 4 bytes (as a u32 felt) but Solidity's abi.encodePacked only +#! uses 1 byte for uint8 leafType. This procedure shifts all data left by 3 bytes so that: #! - Byte 0: leafType (1 byte) #! - Bytes 1-4: originNetwork (4 bytes) #! - etc. #! #! The Keccak precompile expects u32 values packed in little-endian byte order. -#! For each packed element, we drop the leading 3 bytes and rebuild the u32 so that -#! bytes [b0, b1, b2, b3] map to u32::from_le_bytes([b0, b1, b2, b3]). -#! With little-endian input limbs, the first byte comes from the MSB of `curr` and -#! the next three bytes come from the LSBs of `next`: +#! For each packed element, we drop the leading 3 bytes and rebuild the u32 so that bytes +#! [b0, b1, b2, b3] map to u32::from_le_bytes([b0, b1, b2, b3]). +#! With little-endian input limbs, the first byte comes from the MSB of `curr` and the next three +#! bytes come from the LSBs of `next`: #! packed = ((curr >> 24) & 0xFF) #! | (next & 0xFF) << 8 #! | ((next >> 8) & 0xFF) << 16 @@ -57,6 +58,7 @@ end #! #! To help visualize the packing process, consider that each field element represents a 4-byte #! value [u8; 4] (LE). +#! #! Memory before is: #! ptr+0: 1 felt: [a, b, c, d] #! ptr+1: 1 felt: [e, f, g, h] @@ -100,11 +102,11 @@ pub proc pack_leaf_data(leaf_data_start_ptr: MemoryAddress) # compute source address for next element (counter + 1) dup.2 loc_load.PACKING_START_PTR_LOCAL add add.1 - # => [next_src_addr, curr_lsb, curr_elem, counter] + # => [next_src_addr, curr_msb, curr_elem, counter] # load next element mem_load - # => [next_elem, curr_lsb, curr_elem, counter] + # => [next_elem, curr_msb, curr_elem, counter] # keep curr_msb on top for combination swap diff --git a/crates/miden-agglayer/asm/agglayer/bridge/merkle_tree_frontier.masm b/crates/miden-agglayer/asm/agglayer/bridge/merkle_tree_frontier.masm index ed613d1bf9..26add7c242 100644 --- a/crates/miden-agglayer/asm/agglayer/bridge/merkle_tree_frontier.masm +++ b/crates/miden-agglayer/asm/agglayer/bridge/merkle_tree_frontier.masm @@ -306,27 +306,3 @@ pub proc append_and_update_frontier padw loc_loadw_le.CUR_HASH_LO_LOCAL # => [NEW_ROOT_LO, NEW_ROOT_HI, new_leaf_count] end - -# HELPER PROCEDURES -# ================================================================================================= - -#! Stores the canonical zeros from the advice map to the memory at the provided address. -#! -#! Inputs: [zeros_ptr] -#! Outputs: [] -proc store_canonical_zeros - # prepare the stack for the adv_pipe instruction - padw padw padw - # => [PAD, PAD, PAD, zeros_ptr] - - # TODO: use constant once constant usage will be implemented - repeat.32 - adv_pipe - # => [ZERO_I_L, ZERO_I_R, PAD, zeros_ptr+8] - end - # => [ZERO_31_L, ZERO_31_R, PAD, zeros_ptr+256] - - # clean the stack - dropw dropw dropw drop - # => [] -end diff --git a/crates/miden-agglayer/asm/agglayer/common/asset_conversion.masm b/crates/miden-agglayer/asm/agglayer/common/asset_conversion.masm index 298d85eccc..832f1a770b 100644 --- a/crates/miden-agglayer/asm/agglayer/common/asset_conversion.masm +++ b/crates/miden-agglayer/asm/agglayer/common/asset_conversion.masm @@ -72,12 +72,12 @@ end #! returns the result as 8 u32 limbs in little-endian order (U256 format). #! #! Inputs: [amount, target_scale] -#! Outputs: [[RESULT_U256[0], RESULT_U256[1]]] +#! Outputs: [[RESULT_U256_LO, RESULT_U256_HI]] #! #! Where: #! - amount: The asset amount to be converted (range: 0 to 2^63 - 2^31) #! - target_scale: Exponent for scaling factor (10^target_scale) -#! - [RESULT_U256[0], RESULT_U256[1]]: U256 value as 8 u32 limbs in little-endian order +#! - [RESULT_U256_LO, RESULT_U256_HI]: U256 value as 8 u32 limbs in little-endian order #! (least significant limb at the top of the stack, each limb stored in little-endian format) #! #! Examples: @@ -106,7 +106,7 @@ pub proc scale_native_amount_to_u256 # convert to U256 & little endian padw swapw - # => [RESULT_U256[0], RESULT_U256[1]] + # => [RESULT_U256_LO, RESULT_U256_HI] end #! Reverse the limbs and change the byte endianness of the result. @@ -123,7 +123,7 @@ pub proc reverse_limbs_and_change_byte_endianness movdn.7 end - # => [RESULT_U256[0], RESULT_U256[1]] + # => [RESULT_U256_LO, RESULT_U256_HI] end #! Subtract two 128-bit integers (little-endian u32 limbs) and assert no underflow. diff --git a/crates/miden-agglayer/asm/agglayer/common/eth_address.masm b/crates/miden-agglayer/asm/agglayer/common/eth_address.masm index 5a038f07fe..b9cc4a9c33 100644 --- a/crates/miden-agglayer/asm/agglayer/common/eth_address.masm +++ b/crates/miden-agglayer/asm/agglayer/common/eth_address.masm @@ -2,6 +2,11 @@ use agglayer::common::utils use miden::core::crypto::hashes::keccak256 use miden::core::word +# TYPE ALIASES +# ================================================================================================= + +pub type EthereumAddressFormat = struct { a: felt, b: felt, c: felt, d: felt, e: felt } + # ERRORS # ================================================================================================= diff --git a/crates/miden-agglayer/asm/agglayer/common/utils.masm b/crates/miden-agglayer/asm/agglayer/common/utils.masm index ac4e5fbbae..45bed28405 100644 --- a/crates/miden-agglayer/asm/agglayer/common/utils.masm +++ b/crates/miden-agglayer/asm/agglayer/common/utils.masm @@ -3,11 +3,6 @@ use miden::protocol::types::DoubleWord use miden::protocol::types::MemoryAddress -# TYPE ALIASES -# ================================================================================================= - -pub type EthereumAddressFormat = struct { a: felt, b: felt, c: felt, d: felt, e: felt } - # BYTE MANIPULATION # ================================================================================================= @@ -15,7 +10,7 @@ pub type EthereumAddressFormat = struct { a: felt, b: felt, c: felt, d: felt, e: #! #! Inputs: [value] #! Outputs: [swapped] -pub proc swap_u32_bytes +proc swap_u32_bytes # part0 = (value & 0xFF) << 24 dup u32and.0xFF u32shl.24 # => [part0, value] @@ -43,7 +38,9 @@ end #! #! Inputs: [WORD_1, WORD_2, ptr] #! Outputs: [WORD_1, WORD_2, ptr] -pub proc mem_store_double_word( +#! +#! Total cycles: 28 +proc mem_store_double_word( double_word_to_store: DoubleWord, mem_ptr: MemoryAddress ) -> (DoubleWord, MemoryAddress) @@ -58,7 +55,7 @@ end #! #! Inputs: [WORD_1, WORD_2, ptr] #! Outputs: [] -pub proc mem_store_double_word_unaligned( +proc mem_store_double_word_unaligned( double_word_to_store: DoubleWord, mem_ptr: MemoryAddress ) @@ -86,7 +83,7 @@ end #! #! Inputs: [ptr] #! Outputs: [WORD_1, WORD_2] -pub proc mem_load_double_word(mem_ptr: MemoryAddress) -> DoubleWord +proc mem_load_double_word(mem_ptr: MemoryAddress) -> DoubleWord padw dup.4 add.4 mem_loadw_le # => [WORD_2, ptr] diff --git a/crates/miden-agglayer/asm/agglayer/faucet/mod.masm b/crates/miden-agglayer/asm/agglayer/faucet/mod.masm index 0b9c00db78..408c0e93e1 100644 --- a/crates/miden-agglayer/asm/agglayer/faucet/mod.masm +++ b/crates/miden-agglayer/asm/agglayer/faucet/mod.masm @@ -1,11 +1,7 @@ use miden::core::sys use agglayer::common::utils use agglayer::common::asset_conversion -use agglayer::common::eth_address use miden::protocol::active_account -use miden::protocol::active_note -use miden::standards::faucets -use miden::standards::faucets::network_fungible # CONSTANTS # ================================================================================================= @@ -26,8 +22,7 @@ const METADATA_HASH_HI_SLOT = word("agglayer::faucet::metadata_hash_hi") #! Returns the origin token address (5 felts) from faucet conversion storage. #! -#! Reads conversion_info_1 (first 4 felts of address) and conversion_info_2 (5th felt) -#! from storage. +#! Reads conversion_info_1 (first 4 felts of address) and conversion_info_2 (5th felt) from storage. #! #! Inputs: [] #! Outputs: [addr0, addr1, addr2, addr3, addr4] @@ -80,8 +75,8 @@ end #! Returns the pre-computed metadata hash (8 u32 felts) from faucet storage. #! -#! The metadata hash is `keccak256(abi.encode(name, symbol, decimals))` and is stored -#! across two value slots (lo and hi, 4 felts each). +#! The metadata hash is `keccak256(abi.encode(name, symbol, decimals))` and is stored across two +#! value slots (lo and hi, 4 felts each). #! #! Inputs: [pad(16)] #! Outputs: [METADATA_HASH_LO(4), METADATA_HASH_HI(4), pad(8)] @@ -121,15 +116,15 @@ pub proc get_scale # => [scale, pad(15)] end -#! Converts a native Miden asset amount to origin asset data using the stored -#! conversion metadata (origin_token_address, origin_network, and scale). +#! Converts a native Miden asset amount to origin asset data using the stored conversion metadata +#! (origin_token_address, origin_network, and scale). #! #! This procedure is intended to be called via FPI from the bridge account. -#! It reads the faucet's conversion metadata from storage, scales the native amount -#! to U256 format, and returns the result along with origin token address and network. +#! It reads the faucet's conversion metadata from storage, scales the native amount to U256 format, +#! and returns the result along with origin token address and network. #! #! Inputs: [amount, pad(15)] -#! Outputs: [AMOUNT_U256[0], AMOUNT_U256[1], addr0, addr1, addr2, addr3, addr4, origin_network, pad(2)] +#! Outputs: [AMOUNT_U256_LO, AMOUNT_U256_HI, addr0, addr1, addr2, addr3, addr4, origin_network, pad(2)] #! #! Where: #! - amount: The native Miden asset amount @@ -142,9 +137,7 @@ pub proc asset_to_origin_asset # => [amount, pad(15)] # Step 1: Get scale from storage - exec.get_scale_inner - # => [scale, amount, pad(15)] - swap + exec.get_scale_inner swap # => [amount, scale, pad(15)] # Step 2: Scale amount to U256 @@ -170,6 +163,7 @@ pub proc asset_to_origin_asset # => [U256_LO, U256_HI, addr0, addr1, addr2, addr3, addr4, origin_network, pad(15)] exec.sys::truncate_stack + # => [U256_LO, U256_HI, addr0, addr1, addr2, addr3, addr4, origin_network, pad(2)] end #! Burns the fungible asset from the active note. @@ -190,7 +184,7 @@ end #! Invocation: call pub use ::miden::standards::faucets::basic_fungible::burn -#! Re-export the network fungible faucet's mint_and_send procedure. +#! Re-export the network fungible faucet's `mint_and_send` procedure. #! #! See `miden::standards::faucets::network_fungible::mint_and_send` for more details. #! diff --git a/crates/miden-agglayer/asm/components/bridge.masm b/crates/miden-agglayer/asm/components/bridge.masm index 15f10fd4b6..4c38d5a019 100644 --- a/crates/miden-agglayer/asm/components/bridge.masm +++ b/crates/miden-agglayer/asm/components/bridge.masm @@ -1,7 +1,12 @@ # The MASM code of the AggLayer Bridge Account Component. # -# This is a thin wrapper that re-exports bridge-related procedures from the -# agglayer library. +# This is a thin wrapper that re-exports bridge-related procedures from the agglayer library. +# +# The bridge exposes: +# - `register_faucet` from the bridge_config module +# - `update_ger` from the bridge_config module +# - `claim` for bridge-in +# - `bridge_out` for bridge-out pub use ::agglayer::bridge::bridge_config::register_faucet pub use ::agglayer::bridge::bridge_config::update_ger diff --git a/crates/miden-agglayer/asm/components/faucet.masm b/crates/miden-agglayer/asm/components/faucet.masm index ffa33f399e..71927c63d9 100644 --- a/crates/miden-agglayer/asm/components/faucet.masm +++ b/crates/miden-agglayer/asm/components/faucet.masm @@ -1,8 +1,10 @@ # The MASM code of the AggLayer Faucet Account Component. # -# This is a thin wrapper that re-exports faucet-related procedures from the -# agglayer library. The faucet exposes: -# - `mint_and_send` from the network fungible faucet (for MINT note consumption, with owner verification) +# This is a thin wrapper that re-exports faucet-related procedures from the agglayer library. +# +# The faucet exposes: +# - `mint_and_send` from the network fungible faucet (for MINT note consumption, with owner +# verification) # - `asset_to_origin_asset` for bridge-out FPI # - `get_metadata_hash` for bridge-out FPI (metadata hash retrieval) # - `get_scale` for bridge-in FPI (amount verification) diff --git a/crates/miden-agglayer/asm/note_scripts/B2AGG.masm b/crates/miden-agglayer/asm/note_scripts/B2AGG.masm index bc5ee0931b..0ae42e52e8 100644 --- a/crates/miden-agglayer/asm/note_scripts/B2AGG.masm +++ b/crates/miden-agglayer/asm/note_scripts/B2AGG.masm @@ -3,19 +3,17 @@ use miden::protocol::account_id use miden::protocol::active_account use miden::protocol::active_note use miden::protocol::asset -use miden::protocol::asset::ASSET_VALUE_MEMORY_OFFSET -use miden::protocol::note use miden::standards::attachments::network_account_target use miden::standards::wallets::basic->basic_wallet # CONSTANTS # ================================================================================================= -const ASSET_PTR=0 -const B2AGG_NOTE_NUM_STORAGE_ITEMS=6 +const B2AGG_NOTE_ASSETS_PTR = 0 +const B2AGG_NOTE_NUM_ASSETS = 1 -const STORAGE_START_PTR=8 -const STORAGE_END_PTR=STORAGE_START_PTR + 8 +const B2AGG_NOTE_STORAGE_PTR = 8 +const B2AGG_NOTE_NUM_STORAGE_ITEMS = 6 # ERRORS # ================================================================================================= @@ -29,7 +27,8 @@ const ERR_B2AGG_TARGET_ACCOUNT_MISMATCH="B2AGG note attachment target account do #! Bridge-to-AggLayer (B2AGG) note script: bridges assets from Miden to an AggLayer-connected chain. #! #! This note can be consumed in two ways: -#! - If the consuming account is the sender (reclaim): the note's assets are added back to the consuming account. +#! - If the consuming account is the sender (reclaim case): the note's assets are added back to the +#! consuming account. #! - If the consuming account is the Agglayer Bridge: the note's assets are moved to a BURN note, #! and the note details are hashed into a leaf and appended to the Local Exit Tree. #! @@ -37,8 +36,8 @@ const ERR_B2AGG_TARGET_ACCOUNT_MISMATCH="B2AGG note attachment target account do #! Outputs: [] #! #! Note storage layout (6 felts total): -#! - destination_network [0] : 1 felt -#! - destination_address [1..5] : 5 felts +#! - destination_network [0] : 1 felt +#! - destination_address [1..5] : 5 felts #! #! Where: #! - destination_network: Destination network identifier (uint32) @@ -51,7 +50,6 @@ const ERR_B2AGG_TARGET_ACCOUNT_MISMATCH="B2AGG note attachment target account do #! - The note does not contain exactly 6 storage items. #! - The note does not contain exactly 1 asset. #! - The note attachment does not target the consuming account. -#! begin dropw # => [pad(16)] @@ -77,26 +75,27 @@ begin # => [pad(16)] # Store note storage -> mem[8..14] - push.STORAGE_START_PTR exec.active_note::get_storage - # => [num_storage_items, dest_ptr, pad(16)] + push.B2AGG_NOTE_STORAGE_PTR exec.active_note::get_storage + # => [num_storage_items, storage_ptr, pad(16)] - push.B2AGG_NOTE_NUM_STORAGE_ITEMS assert_eq.err=ERR_B2AGG_UNEXPECTED_NUMBER_OF_STORAGE_ITEMS drop - # => [pad(16)] + # Validate the number of storage items + push.B2AGG_NOTE_NUM_STORAGE_ITEMS assert_eq.err=ERR_B2AGG_UNEXPECTED_NUMBER_OF_STORAGE_ITEMS + # => [storage_ptr, pad(16)] + + # load the 6 B2AGG felts from B2AGG_NOTE_STORAGE_PTR as two words + add.4 mem_loadw_le swapw mem_loadw_le.B2AGG_NOTE_STORAGE_PTR + # => [dest_network, dest_address(5), pad(10)] # Store note assets -> mem[0..8] - push.ASSET_PTR exec.active_note::get_assets - # => [num_assets, ptr, pad(16)] + push.B2AGG_NOTE_ASSETS_PTR exec.active_note::get_assets + # => [num_assets, assets_ptr, dest_network, dest_address(5), pad(10)] # Must be exactly 1 asset - push.1 assert_eq.err=ERR_B2AGG_WRONG_NUMBER_OF_ASSETS drop - # => [pad(16)] - - # load the 6 B2AGG felts from note storage as two words - push.STORAGE_START_PTR add.4 mem_loadw_le swapw mem_loadw_le.STORAGE_START_PTR - # => [dest_network, dest_address(5), pad(10)] + push.B2AGG_NOTE_NUM_ASSETS assert_eq.err=ERR_B2AGG_WRONG_NUMBER_OF_ASSETS + # => [assets_ptr, dest_network, dest_address(5), pad(10)] - # Load asset onto the stack from ASSET_PTR - push.ASSET_PTR exec.asset::load + # Load asset onto the stack from B2AGG_NOTE_ASSETS_PTR + exec.asset::load # => [ASSET_KEY, ASSET_VALUE, dest_network, dest_address(5), pad(10)] call.bridge_out::bridge_out diff --git a/crates/miden-agglayer/asm/note_scripts/CLAIM.masm b/crates/miden-agglayer/asm/note_scripts/CLAIM.masm index 036b34298e..715ab6d7f9 100644 --- a/crates/miden-agglayer/asm/note_scripts/CLAIM.masm +++ b/crates/miden-agglayer/asm/note_scripts/CLAIM.masm @@ -1,9 +1,6 @@ use agglayer::bridge::bridge_in -> bridge use miden::protocol::active_note -use miden::protocol::note -use miden::core::crypto::hashes::keccak256 use miden::core::crypto::hashes::poseidon2 -use miden::core::mem use miden::standards::attachments::network_account_target # CONSTANTS @@ -13,7 +10,8 @@ const PROOF_DATA_SIZE = 536 const LEAF_DATA_SIZE = 32 const OUTPUT_NOTE_SIZE = 8 -const PROOF_DATA_START_PTR = 0 +const CLAIM_NOTE_STORAGE_PTR = 0 +const PROOF_DATA_START_PTR = CLAIM_NOTE_STORAGE_PTR const LEAF_DATA_START_PTR = 536 const FAUCET_MINT_AMOUNT = 568 @@ -22,74 +20,15 @@ const FAUCET_MINT_AMOUNT = 568 const ERR_CLAIM_TARGET_ACCT_MISMATCH = "CLAIM note attachment target account does not match consuming account" -#! Reads claim data from memory and inserts it into the advice map under two separate keys. -#! -#! This procedure organizes the claim note data into two logical groups and inserts them -#! into the advice map under separate keys for easier access. -#! -#! Inputs: [] -#! Outputs: [PROOF_DATA_KEY, LEAF_DATA_KEY] -#! -#! Advice map entries created: -#! PROOF_DATA_KEY => [ -#! smtProofLocalExitRoot[256], // SMT proof for local exit root (256 felts, bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH]) -#! smtProofRollupExitRoot[256], // SMT proof for rollup exit root (256 felts, bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH]) -#! globalIndex[8], // Global index (8 felts, uint256 as 8 u32 felts) -#! mainnetExitRoot[8], // Mainnet exit root hash (8 felts, bytes32 as 8 u32 felts) -#! rollupExitRoot[8], // Rollup exit root hash (8 felts, bytes32 as 8 u32 felts) -#! ] -#! -#! LEAF_DATA_KEY => [ -#! leafType[1], // Leaf type (1 felt, uint32) -#! originNetwork[1], // Origin network identifier (1 felt, uint32) -#! originTokenAddress[5], // Origin token address (5 felts, address as 5 u32 felts) -#! destinationNetwork[1], // Destination network identifier (1 felt, uint32) -#! destinationAddress[5], // Destination address (5 felts, address as 5 u32 felts) -#! amount[8], // Amount of tokens (8 felts, uint256 as 8 u32 felts) -#! metadata[8], // ABI encoded metadata (8 felts, fixed size) -#! padding[3], // padding (3 felts) -#! ] -#! -#! Invocation: exec -proc write_claim_data_into_advice_map_by_key - # 1) Get LEAF_DATA_KEY - push.LEAF_DATA_SIZE push.LEAF_DATA_START_PTR - exec.poseidon2::hash_elements - # => [LEAF_DATA_KEY] - - push.LEAF_DATA_SIZE add.LEAF_DATA_START_PTR push.LEAF_DATA_START_PTR - movdn.5 movdn.5 - # => [LEAF_DATA_KEY, start_ptr, end_ptr] - - adv.insert_mem - # OS => [LEAF_DATA_KEY, start_ptr, end_ptr] - # AM => {LEAF_DATA_KEY: mem[start_ptr..end_ptr] } - movup.4 drop movup.4 drop - # => [LEAF_DATA_KEY] - - # 2) Get PROOF_DATA_KEY - push.PROOF_DATA_SIZE push.PROOF_DATA_START_PTR - exec.poseidon2::hash_elements - # => [PROOF_DATA_KEY, LEAF_DATA_KEY] - - push.PROOF_DATA_SIZE push.PROOF_DATA_START_PTR - movdn.5 movdn.5 - # => [PROOF_DATA_KEY, start_ptr, end_ptr, LEAF_DATA_KEY] - - adv.insert_mem - # OS => [PROOF_DATA_KEY, start_ptr, end_ptr, LEAF_DATA_KEY] - # AM => {PROOF_DATA_KEY: mem[start_ptr..end_ptr] } - - movup.4 drop movup.4 drop - # => [PROOF_DATA_KEY, LEAF_DATA_KEY] -end +# NOTE SCRIPT +# ================================================================================================= #! Agglayer Bridge CLAIM script: claims assets by calling the bridge's claim function. #! #! This note is consumed by the agglayer bridge account whose ID is provided #! in the note attachment (NetworkAccountTarget). Upon consumption, the bridge validates #! the Merkle proof, looks up the faucet from the token registry, and creates a MINT note -#! targeting the aggfaucet. +#! targeting the Agglayer Faucet. #! #! Requires that the account exposes: #! - agglayer::bridge::bridge_in::claim procedure. @@ -98,20 +37,20 @@ end #! Outputs: [pad(16)] #! #! NoteStorage layout (569 felts total): -#! - smtProofLocalExitRoot [0..255] : 256 felts -#! - smtProofRollupExitRoot [256..511]: 256 felts -#! - globalIndex [512..519]: 8 felts -#! - mainnetExitRoot [520..527]: 8 felts -#! - rollupExitRoot [528..535]: 8 felts -#! - leafType [536] : 1 felt -#! - originNetwork [537] : 1 felt -#! - originTokenAddress [538..542]: 5 felts -#! - destinationNetwork [543] : 1 felt -#! - destinationAddress [544..548]: 5 felts -#! - amount [549..556]: 8 felts -#! - metadata [557..564]: 8 felts -#! - padding [565..567]: 3 felts -#! - miden_claim_amount [568] : 1 felt +#! - smtProofLocalExitRoot [0..255] : 256 felts +#! - smtProofRollupExitRoot [256..511] : 256 felts +#! - globalIndex [512..519] : 8 felts +#! - mainnetExitRoot [520..527] : 8 felts +#! - rollupExitRoot [528..535] : 8 felts +#! - leafType [536] : 1 felt +#! - originNetwork [537] : 1 felt +#! - originTokenAddress [538..542] : 5 felts +#! - destinationNetwork [543] : 1 felt +#! - destinationAddress [544..548] : 5 felts +#! - amount [549..556] : 8 felts +#! - metadata [557..564] : 8 felts +#! - padding [565..567] : 3 felts +#! - miden_claim_amount [568] : 1 felt #! #! Where: #! - smtProofLocalExitRoot: SMT proof for local exit root (bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH]) @@ -146,7 +85,7 @@ begin # => [pad(16)] # Load CLAIM note storage into memory, starting at address 0 - push.0 exec.active_note::get_storage drop drop + push.CLAIM_NOTE_STORAGE_PTR exec.active_note::get_storage drop drop # => [pad(16)] exec.write_claim_data_into_advice_map_by_key @@ -167,3 +106,68 @@ begin # => [pad(16)] end + +# HELPER PROCEDURES +# ================================================================================================= + +#! Reads claim data from memory and inserts it into the advice map under two separate keys. +#! +#! This procedure organizes the claim note data into two logical groups and inserts them +#! into the advice map under separate keys for easier access. +#! +#! Inputs: [] +#! Outputs: [PROOF_DATA_KEY, LEAF_DATA_KEY] +#! +#! Advice map entries created: +#! PROOF_DATA_KEY => [ +#! smtProofLocalExitRoot[256], // SMT proof for local exit root (256 felts, bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH]) +#! smtProofRollupExitRoot[256], // SMT proof for rollup exit root (256 felts, bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH]) +#! globalIndex[8], // Global index (8 felts, uint256 as 8 u32 felts) +#! mainnetExitRoot[8], // Mainnet exit root hash (8 felts, bytes32 as 8 u32 felts) +#! rollupExitRoot[8], // Rollup exit root hash (8 felts, bytes32 as 8 u32 felts) +#! ] +#! +#! LEAF_DATA_KEY => [ +#! leafType[1], // Leaf type (1 felt, uint32) +#! originNetwork[1], // Origin network identifier (1 felt, uint32) +#! originTokenAddress[5], // Origin token address (5 felts, address as 5 u32 felts) +#! destinationNetwork[1], // Destination network identifier (1 felt, uint32) +#! destinationAddress[5], // Destination address (5 felts, address as 5 u32 felts) +#! amount[8], // Amount of tokens (8 felts, uint256 as 8 u32 felts) +#! metadata[8], // ABI encoded metadata (8 felts, fixed size) +#! padding[3], // padding (3 felts) +#! ] +#! +#! Invocation: exec +proc write_claim_data_into_advice_map_by_key + # 1) Get LEAF_DATA_KEY + push.LEAF_DATA_SIZE push.LEAF_DATA_START_PTR + exec.poseidon2::hash_elements + # => [LEAF_DATA_KEY] + + push.LEAF_DATA_SIZE add.LEAF_DATA_START_PTR push.LEAF_DATA_START_PTR + movdn.5 movdn.5 + # => [LEAF_DATA_KEY, start_ptr, end_ptr] + + adv.insert_mem + # OS => [LEAF_DATA_KEY, start_ptr, end_ptr] + # AM => {LEAF_DATA_KEY: mem[start_ptr..end_ptr] } + movup.4 drop movup.4 drop + # => [LEAF_DATA_KEY] + + # 2) Get PROOF_DATA_KEY + push.PROOF_DATA_SIZE push.PROOF_DATA_START_PTR + exec.poseidon2::hash_elements + # => [PROOF_DATA_KEY, LEAF_DATA_KEY] + + push.PROOF_DATA_SIZE push.PROOF_DATA_START_PTR + movdn.5 movdn.5 + # => [PROOF_DATA_KEY, start_ptr, end_ptr, LEAF_DATA_KEY] + + adv.insert_mem + # OS => [PROOF_DATA_KEY, start_ptr, end_ptr, LEAF_DATA_KEY] + # AM => {PROOF_DATA_KEY: mem[start_ptr..end_ptr] } + + movup.4 drop movup.4 drop + # => [PROOF_DATA_KEY, LEAF_DATA_KEY] +end diff --git a/crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm b/crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm index 12259b36f7..98df2690fd 100644 --- a/crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm +++ b/crates/miden-agglayer/asm/note_scripts/CONFIG_AGG_BRIDGE.masm @@ -1,17 +1,14 @@ use agglayer::bridge::bridge_config use miden::protocol::active_note -use miden::protocol::active_account -use miden::protocol::account_id use miden::standards::attachments::network_account_target # CONSTANTS # ================================================================================================= -const STORAGE_START_PTR = 0 const CONFIG_AGG_BRIDGE_NUM_STORAGE_ITEMS = 7 -const FAUCET_ID_SUFFIX = 5 -const FAUCET_ID_PREFIX = 6 +const STORAGE_START_PTR = 0 +const ORIGIN_TOKEN_ADDR_0 = STORAGE_START_PTR const ORIGIN_TOKEN_ADDR_4 = 4 # ERRORS @@ -20,7 +17,8 @@ const ORIGIN_TOKEN_ADDR_4 = 4 const ERR_CONFIG_AGG_BRIDGE_UNEXPECTED_STORAGE_ITEMS = "CONFIG_AGG_BRIDGE expects exactly 7 note storage items" const ERR_CONFIG_AGG_BRIDGE_TARGET_ACCOUNT_MISMATCH = "CONFIG_AGG_BRIDGE note attachment target account does not match consuming account" -#! Registers a faucet in the bridge's faucet registry and token registry. +#! Agglayer Bridge CONFIG_AGG_BRIDGE script: registers a faucet in the bridge's faucet registry and +#! token registry. #! #! This note can only be consumed by the Agglayer Bridge account that is targeted by the note #! attachment, and only if the note was sent by the bridge admin. @@ -33,14 +31,13 @@ const ERR_CONFIG_AGG_BRIDGE_TARGET_ACCOUNT_MISMATCH = "CONFIG_AGG_BRIDGE note at #! Outputs: [pad(16)] #! #! NoteStorage layout (7 felts total): -#! - origin_token_addr_0 [0]: 1 felt -#! - origin_token_addr_1 [1]: 1 felt -#! - origin_token_addr_2 [2]: 1 felt -#! - origin_token_addr_3 [3]: 1 felt -#! - origin_token_addr_4 [4]: 1 felt -#! - faucet_id_suffix [5]: 1 felt -#! - faucet_id_prefix [6]: 1 felt - +#! - origin_token_addr_0 [0] : 1 felt +#! - origin_token_addr_1 [1] : 1 felt +#! - origin_token_addr_2 [2] : 1 felt +#! - origin_token_addr_3 [3] : 1 felt +#! - origin_token_addr_4 [4] : 1 felt +#! - faucet_id_suffix [5] : 1 felt +#! - faucet_id_prefix [6] : 1 felt #! #! Where: #! - faucet_id_suffix: Suffix felt of the faucet account ID to register. @@ -63,24 +60,30 @@ begin push.STORAGE_START_PTR exec.active_note::get_storage # => [num_storage_items, dest_ptr, pad(16)] + # Validate the number of storage items push.CONFIG_AGG_BRIDGE_NUM_STORAGE_ITEMS assert_eq.err=ERR_CONFIG_AGG_BRIDGE_UNEXPECTED_STORAGE_ITEMS drop # => [pad(16)] # Load origin_token_addr(5) and faucet_id from memory # register_faucet expects: [origin_token_addr(5), faucet_id_suffix, faucet_id_prefix, pad(9)] - # Load all 7 values individually in the correct order - mem_load.FAUCET_ID_PREFIX mem_load.FAUCET_ID_SUFFIX mem_load.ORIGIN_TOKEN_ADDR_4 - # => [addr4, faucet_id_suffix, faucet_id_prefix, pad(16)] + # Load origin_token_addr_4, faucet_id_suffix, and faucet_id_prefix onto the sack. Notice that we + # can use `mem_loadw_le` here: that allows us to reuse the existing zeros on the stack, and + # since note memory was not initialized, fourth element on the stack will be equal ZERO, which + # is what we want. + mem_loadw_le.ORIGIN_TOKEN_ADDR_4 + # => [addr4, faucet_id_suffix, faucet_id_prefix, pad(13)] - padw mem_loadw_le.STORAGE_START_PTR - # => [addr4, addr3, addr2, addr1, addr0, faucet_id_suffix, faucet_id_prefix, pad(16)] + # Load remaining origin_token_addr_[0..3] onto the stack + padw mem_loadw_le.ORIGIN_TOKEN_ADDR_0 + # => [addr0, addr1, addr2, addr3, addr4, faucet_id_suffix, faucet_id_prefix, pad(13)] # Register the faucet in the bridge - # => [addr4, addr3, addr2, addr1, addr0, faucet_id_suffix, faucet_id_prefix, pad(9), pad(7)] + # => [addr0, addr1, addr2, addr3, addr4, faucet_id_suffix, faucet_id_prefix, pad(9), pad(4)] + call.bridge_config::register_faucet - # => [pad(16), pad(7)] + # => [pad(16), pad(4)] - dropw drop drop drop + dropw # => [pad(16)] end diff --git a/crates/miden-agglayer/asm/note_scripts/UPDATE_GER.masm b/crates/miden-agglayer/asm/note_scripts/UPDATE_GER.masm index c11d609db0..070d181ef1 100644 --- a/crates/miden-agglayer/asm/note_scripts/UPDATE_GER.masm +++ b/crates/miden-agglayer/asm/note_scripts/UPDATE_GER.masm @@ -1,38 +1,39 @@ use agglayer::bridge::bridge_config use miden::protocol::active_note -use miden::protocol::active_account -use miden::protocol::account_id -use miden::protocol::note use miden::standards::attachments::network_account_target # CONSTANTS # ================================================================================================= + const UPDATE_GER_NOTE_NUM_STORAGE_ITEMS = 8 const STORAGE_PTR_GER_LOWER = 0 const STORAGE_PTR_GER_UPPER = 4 # ERRORS # ================================================================================================= + const ERR_UPDATE_GER_UNEXPECTED_NUMBER_OF_STORAGE_ITEMS = "UPDATE_GER script expects exactly 8 note storage items" const ERR_UPDATE_GER_TARGET_ACCOUNT_MISMATCH = "UPDATE_GER note attachment target account does not match consuming account" # NOTE SCRIPT # ================================================================================================= -#! Agglayer Bridge UPDATE_GER script: updates the GER by calling the bridge_config::update_ger function. +#! Agglayer Bridge UPDATE_GER script: updates the GER by calling the bridge_config::update_ger +#! function. #! #! This note can only be consumed by the specific agglayer bridge account whose ID is provided -#! in the note attachment (target_account_id), and only if the note was sent by the -#! global exit root manager. +#! in the note attachment (target_account_id), and only if the note was sent by the global exit root +#! manager. #! #! Requires that the account exposes: #! - agglayer::bridge_config::update_ger procedure. #! #! Inputs: [ARGS, pad(12)] #! Outputs: [pad(16)] +#! #! NoteStorage layout (8 felts total): -#! - GER_LOWER [0..3] -#! - GER_UPPER [4..7] +#! - GER_LOWER [0..3] : 4 felts +#! - GER_UPPER [4..7] : 4 felts #! #! Panics if: #! - account does not expose update_ger procedure. @@ -47,22 +48,21 @@ begin assert.err=ERR_UPDATE_GER_TARGET_ACCOUNT_MISMATCH # => [pad(16)] - # proceed with the GER update logic - + # Load note storage to memory push.STORAGE_PTR_GER_LOWER exec.active_note::get_storage # => [num_storage_items, dest_ptr, pad(16)] + # Validate the number of storage items push.UPDATE_GER_NOTE_NUM_STORAGE_ITEMS assert_eq.err=ERR_UPDATE_GER_UNEXPECTED_NUMBER_OF_STORAGE_ITEMS drop # => [pad(16)] # Load GER_LOWER and GER_UPPER from note storage mem_loadw_le.STORAGE_PTR_GER_UPPER # => [GER_UPPER[4], pad(12)] - swapw - mem_loadw_le.STORAGE_PTR_GER_LOWER + + swapw mem_loadw_le.STORAGE_PTR_GER_LOWER # => [GER_LOWER[4], GER_UPPER[4], pad(8)] call.bridge_config::update_ger # => [pad(16)] - end diff --git a/crates/miden-agglayer/diagrams/bridge-in.excalidraw b/crates/miden-agglayer/diagrams/bridge-in.excalidraw new file mode 100644 index 0000000000..94eb00a2fd --- /dev/null +++ b/crates/miden-agglayer/diagrams/bridge-in.excalidraw @@ -0,0 +1,470 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "miden-agglayer", + "elements": [ + { + "id": "title", + "type": "text", + "x": 400, + "y": 30, + "width": 500, + "height": 35, + "text": "Bridge-in (AggLayer to Miden)", + "fontSize": 28, + "fontFamily": 1, + "textAlign": "center" + }, + { + "id": "l1", + "type": "rectangle", + "x": 40, + "y": 320, + "width": 160, + "height": 50, + "strokeColor": "#868e96", + "backgroundColor": "#e9ecef", + "fillStyle": "solid", + "strokeWidth": 2, + "roundness": { "type": 3 }, + "boundElements": [{"id": "arrow-l1-user", "type": "arrow"}] + }, + { + "id": "l1-label", + "type": "text", + "x": 60, + "y": 332, + "width": 100, + "height": 25, + "text": "AggLayer L1", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "center", + "containerId": "l1" + }, + { + "id": "user", + "type": "diamond", + "x": 30, + "y": 120, + "width": 180, + "height": 100, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "boundElements": [ + {"id": "arrow-l1-user", "type": "arrow"}, + {"id": "arrow1", "type": "arrow"} + ] + }, + { + "id": "user-label", + "type": "text", + "x": 60, + "y": 150, + "width": 120, + "height": 40, + "text": "User / Claim\nManager", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "center", + "containerId": "user" + }, + { + "id": "arrow-l1-user", + "type": "arrow", + "x": 110, + "y": 320, + "width": 10, + "height": 100, + "strokeColor": "#868e96", + "strokeWidth": 2, + "strokeStyle": "dashed", + "points": [[0, 0], [10, -100]], + "startBinding": {"elementId": "l1", "focus": 0, "gap": 5}, + "endBinding": {"elementId": "user", "focus": 0, "gap": 5} + }, + { + "id": "al1-label", + "type": "text", + "x": 125, + "y": 260, + "width": 80, + "height": 20, + "text": "reads proofs", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left" + }, + { + "id": "claim", + "type": "rectangle", + "x": 300, + "y": 140, + "width": 120, + "height": 60, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "fillStyle": "solid", + "strokeWidth": 2, + "roundness": { "type": 3 }, + "boundElements": [ + {"id": "arrow1", "type": "arrow"}, + {"id": "arrow2", "type": "arrow"} + ] + }, + { + "id": "claim-label", + "type": "text", + "x": 322, + "y": 158, + "width": 76, + "height": 25, + "text": "CLAIM", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "containerId": "claim" + }, + { + "id": "bridge", + "type": "rectangle", + "x": 510, + "y": 90, + "width": 230, + "height": 210, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "boundElements": [ + {"id": "arrow2", "type": "arrow"}, + {"id": "arrow3", "type": "arrow"}, + {"id": "arrow-ger", "type": "arrow"} + ] + }, + { + "id": "bridge-name", + "type": "text", + "x": 570, + "y": 98, + "width": 110, + "height": 35, + "text": "Bridge", + "fontSize": 28, + "fontFamily": 1, + "textAlign": "center" + }, + { + "id": "bridge-desc", + "type": "text", + "x": 525, + "y": 140, + "width": 200, + "height": 150, + "text": "1. Validate GER\n2. Parse global index\n3. Verify Merkle proof\n4. Update CGI chain\n5. Check nullifier\n6. Lookup faucet\n7. Verify amount\n8. Create MINT note", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left" + }, + { + "id": "mint", + "type": "rectangle", + "x": 830, + "y": 140, + "width": 110, + "height": 60, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "fillStyle": "solid", + "strokeWidth": 2, + "roundness": { "type": 3 }, + "boundElements": [ + {"id": "arrow3", "type": "arrow"}, + {"id": "arrow4", "type": "arrow"} + ] + }, + { + "id": "mint-label", + "type": "text", + "x": 855, + "y": 158, + "width": 60, + "height": 25, + "text": "MINT", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "containerId": "mint" + }, + { + "id": "faucet", + "type": "rectangle", + "x": 1030, + "y": 120, + "width": 170, + "height": 100, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "boundElements": [ + {"id": "arrow4", "type": "arrow"}, + {"id": "arrow5", "type": "arrow"} + ] + }, + { + "id": "faucet-name", + "type": "text", + "x": 1068, + "y": 128, + "width": 94, + "height": 35, + "text": "Faucet", + "fontSize": 28, + "fontFamily": 1, + "textAlign": "center" + }, + { + "id": "faucet-desc", + "type": "text", + "x": 1070, + "y": 173, + "width": 90, + "height": 25, + "text": "Mint & send", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center" + }, + { + "id": "p2id", + "type": "rectangle", + "x": 1290, + "y": 140, + "width": 100, + "height": 60, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "fillStyle": "solid", + "strokeWidth": 2, + "roundness": { "type": 3 }, + "boundElements": [ + {"id": "arrow5", "type": "arrow"}, + {"id": "arrow6", "type": "arrow"} + ] + }, + { + "id": "p2id-label", + "type": "text", + "x": 1312, + "y": 158, + "width": 56, + "height": 25, + "text": "P2ID", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "containerId": "p2id" + }, + { + "id": "recipient", + "type": "diamond", + "x": 1480, + "y": 120, + "width": 160, + "height": 100, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "boundElements": [{"id": "arrow6", "type": "arrow"}] + }, + { + "id": "recipient-label", + "type": "text", + "x": 1512, + "y": 155, + "width": 96, + "height": 25, + "text": "Recipient", + "fontSize": 18, + "fontFamily": 1, + "textAlign": "center", + "containerId": "recipient" + }, + { + "id": "ger-box", + "type": "rectangle", + "x": 555, + "y": 370, + "width": 140, + "height": 50, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "roundness": { "type": 3 }, + "boundElements": [{"id": "arrow-ger", "type": "arrow"}] + }, + { + "id": "ger-label", + "type": "text", + "x": 580, + "y": 382, + "width": 90, + "height": 25, + "text": "GER Map", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "center", + "containerId": "ger-box" + }, + { + "id": "arrow1", + "type": "arrow", + "x": 210, + "y": 170, + "width": 90, + "height": 0, + "strokeColor": "#1e1e1e", + "strokeWidth": 2, + "points": [[0, 0], [90, 0]], + "startBinding": {"elementId": "user", "focus": 0, "gap": 5}, + "endBinding": {"elementId": "claim", "focus": 0, "gap": 5} + }, + { + "id": "a1-label", + "type": "text", + "x": 220, + "y": 148, + "width": 70, + "height": 20, + "text": "creates", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center" + }, + { + "id": "arrow2", + "type": "arrow", + "x": 420, + "y": 170, + "width": 90, + "height": 0, + "strokeColor": "#1e1e1e", + "strokeWidth": 2, + "points": [[0, 0], [90, 0]], + "startBinding": {"elementId": "claim", "focus": 0, "gap": 5}, + "endBinding": {"elementId": "bridge", "focus": 0, "gap": 5} + }, + { + "id": "arrow3", + "type": "arrow", + "x": 740, + "y": 170, + "width": 90, + "height": 0, + "strokeColor": "#1e1e1e", + "strokeWidth": 2, + "points": [[0, 0], [90, 0]], + "startBinding": {"elementId": "bridge", "focus": 0, "gap": 5}, + "endBinding": {"elementId": "mint", "focus": 0, "gap": 5} + }, + { + "id": "a3-label", + "type": "text", + "x": 750, + "y": 148, + "width": 70, + "height": 20, + "text": "creates", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center" + }, + { + "id": "arrow4", + "type": "arrow", + "x": 940, + "y": 170, + "width": 90, + "height": 0, + "strokeColor": "#1e1e1e", + "strokeWidth": 2, + "points": [[0, 0], [90, 0]], + "startBinding": {"elementId": "mint", "focus": 0, "gap": 5}, + "endBinding": {"elementId": "faucet", "focus": 0, "gap": 5} + }, + { + "id": "arrow5", + "type": "arrow", + "x": 1200, + "y": 170, + "width": 90, + "height": 0, + "strokeColor": "#1e1e1e", + "strokeWidth": 2, + "points": [[0, 0], [90, 0]], + "startBinding": {"elementId": "faucet", "focus": 0, "gap": 5}, + "endBinding": {"elementId": "p2id", "focus": 0, "gap": 5} + }, + { + "id": "a5-label", + "type": "text", + "x": 1210, + "y": 148, + "width": 70, + "height": 20, + "text": "creates", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center" + }, + { + "id": "arrow6", + "type": "arrow", + "x": 1390, + "y": 170, + "width": 90, + "height": 0, + "strokeColor": "#1e1e1e", + "strokeWidth": 2, + "points": [[0, 0], [90, 0]], + "startBinding": {"elementId": "p2id", "focus": 0, "gap": 5}, + "endBinding": {"elementId": "recipient", "focus": 0, "gap": 5} + }, + { + "id": "arrow-ger", + "type": "arrow", + "x": 625, + "y": 300, + "width": 0, + "height": 70, + "strokeColor": "#1e1e1e", + "strokeWidth": 2, + "strokeStyle": "dashed", + "points": [[0, 0], [0, 70]], + "startBinding": {"elementId": "bridge", "focus": 0, "gap": 5}, + "endBinding": {"elementId": "ger-box", "focus": 0, "gap": 5} + }, + { + "id": "ager-label", + "type": "text", + "x": 635, + "y": 328, + "width": 60, + "height": 20, + "text": "validate", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left" + } + ], + "appState": { + "viewBackgroundColor": "#ffffff", + "gridSize": 20 + }, + "files": {} +} diff --git a/crates/miden-agglayer/diagrams/bridge-in.png b/crates/miden-agglayer/diagrams/bridge-in.png new file mode 100644 index 0000000000..00efefc85c Binary files /dev/null and b/crates/miden-agglayer/diagrams/bridge-in.png differ diff --git a/crates/miden-agglayer/diagrams/bridge-out.excalidraw b/crates/miden-agglayer/diagrams/bridge-out.excalidraw new file mode 100644 index 0000000000..5301ab97ce --- /dev/null +++ b/crates/miden-agglayer/diagrams/bridge-out.excalidraw @@ -0,0 +1,373 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "miden-agglayer", + "elements": [ + { + "id": "title", + "type": "text", + "x": 350, + "y": 30, + "width": 500, + "height": 35, + "text": "Bridge-out (Miden to AggLayer)", + "fontSize": 28, + "fontFamily": 1, + "textAlign": "center" + }, + { + "id": "user", + "type": "diamond", + "x": 50, + "y": 130, + "width": 140, + "height": 100, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "boundElements": [{"id": "arrow1", "type": "arrow"}] + }, + { + "id": "user-label", + "type": "text", + "x": 88, + "y": 167, + "width": 64, + "height": 25, + "text": "User", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "containerId": "user" + }, + { + "id": "b2agg", + "type": "rectangle", + "x": 280, + "y": 150, + "width": 140, + "height": 60, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "fillStyle": "solid", + "strokeWidth": 2, + "roundness": { "type": 3 }, + "boundElements": [ + {"id": "arrow1", "type": "arrow"}, + {"id": "arrow2", "type": "arrow"} + ] + }, + { + "id": "b2agg-label", + "type": "text", + "x": 310, + "y": 168, + "width": 80, + "height": 25, + "text": "B2AGG", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "containerId": "b2agg" + }, + { + "id": "bridge", + "type": "rectangle", + "x": 510, + "y": 100, + "width": 220, + "height": 180, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "boundElements": [ + {"id": "arrow2", "type": "arrow"}, + {"id": "arrow3", "type": "arrow"}, + {"id": "arrow5", "type": "arrow"} + ] + }, + { + "id": "bridge-name", + "type": "text", + "x": 565, + "y": 108, + "width": 110, + "height": 35, + "text": "Bridge", + "fontSize": 28, + "fontFamily": 1, + "textAlign": "center" + }, + { + "id": "bridge-desc", + "type": "text", + "x": 525, + "y": 150, + "width": 190, + "height": 120, + "text": "1. Validate faucet\n2. FPI: get amount\n3. FPI: get metadata\n4. Build leaf data\n5. Append to LET\n6. Create BURN note", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left" + }, + { + "id": "burn", + "type": "rectangle", + "x": 820, + "y": 150, + "width": 120, + "height": 60, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "fillStyle": "solid", + "strokeWidth": 2, + "roundness": { "type": 3 }, + "boundElements": [ + {"id": "arrow3", "type": "arrow"}, + {"id": "arrow4", "type": "arrow"} + ] + }, + { + "id": "burn-label", + "type": "text", + "x": 845, + "y": 168, + "width": 70, + "height": 25, + "text": "BURN", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "containerId": "burn" + }, + { + "id": "faucet", + "type": "rectangle", + "x": 1030, + "y": 130, + "width": 160, + "height": 100, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "boundElements": [{"id": "arrow4", "type": "arrow"}] + }, + { + "id": "faucet-name", + "type": "text", + "x": 1063, + "y": 140, + "width": 94, + "height": 35, + "text": "Faucet", + "fontSize": 28, + "fontFamily": 1, + "textAlign": "center" + }, + { + "id": "faucet-desc", + "type": "text", + "x": 1070, + "y": 185, + "width": 80, + "height": 25, + "text": "Burn asset", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center" + }, + { + "id": "let-box", + "type": "rectangle", + "x": 540, + "y": 350, + "width": 160, + "height": 50, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "roundness": { "type": 3 }, + "boundElements": [ + {"id": "arrow5", "type": "arrow"}, + {"id": "arrow-l1", "type": "arrow"} + ] + }, + { + "id": "let-label", + "type": "text", + "x": 555, + "y": 362, + "width": 130, + "height": 25, + "text": "Local Exit Tree", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "center", + "containerId": "let-box" + }, + { + "id": "l1", + "type": "rectangle", + "x": 900, + "y": 350, + "width": 160, + "height": 50, + "strokeColor": "#868e96", + "backgroundColor": "#e9ecef", + "fillStyle": "solid", + "strokeWidth": 2, + "roundness": { "type": 3 }, + "boundElements": [{"id": "arrow-l1", "type": "arrow"}] + }, + { + "id": "l1-label", + "type": "text", + "x": 930, + "y": 362, + "width": 100, + "height": 25, + "text": "AggLayer L1", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "center", + "containerId": "l1" + }, + { + "id": "arrow1", + "type": "arrow", + "x": 190, + "y": 180, + "width": 90, + "height": 0, + "strokeColor": "#1e1e1e", + "strokeWidth": 2, + "points": [[0, 0], [90, 0]], + "startBinding": {"elementId": "user", "focus": 0, "gap": 5}, + "endBinding": {"elementId": "b2agg", "focus": 0, "gap": 5} + }, + { + "id": "a1-label", + "type": "text", + "x": 200, + "y": 157, + "width": 70, + "height": 20, + "text": "creates", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center" + }, + { + "id": "arrow2", + "type": "arrow", + "x": 420, + "y": 180, + "width": 90, + "height": 0, + "strokeColor": "#1e1e1e", + "strokeWidth": 2, + "points": [[0, 0], [90, 0]], + "startBinding": {"elementId": "b2agg", "focus": 0, "gap": 5}, + "endBinding": {"elementId": "bridge", "focus": 0, "gap": 5} + }, + { + "id": "arrow3", + "type": "arrow", + "x": 730, + "y": 180, + "width": 90, + "height": 0, + "strokeColor": "#1e1e1e", + "strokeWidth": 2, + "points": [[0, 0], [90, 0]], + "startBinding": {"elementId": "bridge", "focus": 0, "gap": 5}, + "endBinding": {"elementId": "burn", "focus": 0, "gap": 5} + }, + { + "id": "a3-label", + "type": "text", + "x": 740, + "y": 157, + "width": 70, + "height": 20, + "text": "creates", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center" + }, + { + "id": "arrow4", + "type": "arrow", + "x": 940, + "y": 180, + "width": 90, + "height": 0, + "strokeColor": "#1e1e1e", + "strokeWidth": 2, + "points": [[0, 0], [90, 0]], + "startBinding": {"elementId": "burn", "focus": 0, "gap": 5}, + "endBinding": {"elementId": "faucet", "focus": 0, "gap": 5} + }, + { + "id": "arrow5", + "type": "arrow", + "x": 620, + "y": 280, + "width": 0, + "height": 70, + "strokeColor": "#1e1e1e", + "strokeWidth": 2, + "strokeStyle": "dashed", + "points": [[0, 0], [0, 70]], + "startBinding": {"elementId": "bridge", "focus": 0, "gap": 5}, + "endBinding": {"elementId": "let-box", "focus": 0, "gap": 5} + }, + { + "id": "a5-label", + "type": "text", + "x": 630, + "y": 308, + "width": 80, + "height": 20, + "text": "append leaf", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left" + }, + { + "id": "arrow-l1", + "type": "arrow", + "x": 700, + "y": 375, + "width": 200, + "height": 0, + "strokeColor": "#868e96", + "strokeWidth": 2, + "strokeStyle": "dashed", + "points": [[0, 0], [200, 0]], + "startBinding": {"elementId": "let-box", "focus": 0, "gap": 5}, + "endBinding": {"elementId": "l1", "focus": 0, "gap": 5} + }, + { + "id": "al1-label", + "type": "text", + "x": 745, + "y": 353, + "width": 110, + "height": 20, + "text": "LET root posted", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "center" + } + ], + "appState": { + "viewBackgroundColor": "#ffffff", + "gridSize": 20 + }, + "files": {} +} diff --git a/crates/miden-agglayer/diagrams/bridge-out.png b/crates/miden-agglayer/diagrams/bridge-out.png new file mode 100644 index 0000000000..19b8665f70 Binary files /dev/null and b/crates/miden-agglayer/diagrams/bridge-out.png differ diff --git a/crates/miden-agglayer/diagrams/faucet-registration.excalidraw b/crates/miden-agglayer/diagrams/faucet-registration.excalidraw new file mode 100644 index 0000000000..c5a235074e --- /dev/null +++ b/crates/miden-agglayer/diagrams/faucet-registration.excalidraw @@ -0,0 +1,317 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "miden-agglayer", + "elements": [ + { + "id": "title", + "type": "text", + "x": 280, + "y": 30, + "width": 400, + "height": 35, + "text": "Faucet Registration", + "fontSize": 28, + "fontFamily": 1, + "textAlign": "center" + }, + { + "id": "operator", + "type": "diamond", + "x": 30, + "y": 120, + "width": 180, + "height": 100, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "boundElements": [{"id": "arrow1", "type": "arrow"}] + }, + { + "id": "operator-label", + "type": "text", + "x": 55, + "y": 150, + "width": 130, + "height": 40, + "text": "Bridge\nOperator", + "fontSize": 18, + "fontFamily": 1, + "textAlign": "center", + "containerId": "operator" + }, + { + "id": "config-note", + "type": "rectangle", + "x": 270, + "y": 120, + "width": 280, + "height": 60, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "fillStyle": "solid", + "strokeWidth": 2, + "roundness": { "type": 3 }, + "boundElements": [ + {"id": "arrow1", "type": "arrow"}, + {"id": "arrow2", "type": "arrow"} + ] + }, + { + "id": "config-label", + "type": "text", + "x": 285, + "y": 133, + "width": 250, + "height": 35, + "text": "CONFIG_AGG_BRIDGE", + "fontSize": 22, + "fontFamily": 1, + "textAlign": "center", + "containerId": "config-note" + }, + { + "id": "bridge", + "type": "rectangle", + "x": 620, + "y": 100, + "width": 220, + "height": 150, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "boundElements": [ + {"id": "arrow2", "type": "arrow"}, + {"id": "arrow3", "type": "arrow"}, + {"id": "arrow4", "type": "arrow"}, + {"id": "arrow-ref", "type": "arrow"} + ] + }, + { + "id": "bridge-name", + "type": "text", + "x": 675, + "y": 108, + "width": 110, + "height": 35, + "text": "Bridge", + "fontSize": 28, + "fontFamily": 1, + "textAlign": "center" + }, + { + "id": "bridge-desc", + "type": "text", + "x": 635, + "y": 150, + "width": 190, + "height": 85, + "text": "1. Assert sender\n2. Register in faucet reg\n3. Register in token reg", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left" + }, + { + "id": "faucet-reg", + "type": "rectangle", + "x": 580, + "y": 320, + "width": 140, + "height": 50, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "roundness": { "type": 3 }, + "boundElements": [{"id": "arrow3", "type": "arrow"}] + }, + { + "id": "faucet-reg-label", + "type": "text", + "x": 595, + "y": 332, + "width": 110, + "height": 25, + "text": "Faucet Registry", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "containerId": "faucet-reg" + }, + { + "id": "token-reg", + "type": "rectangle", + "x": 760, + "y": 320, + "width": 140, + "height": 50, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "roundness": { "type": 3 }, + "boundElements": [{"id": "arrow4", "type": "arrow"}] + }, + { + "id": "token-reg-label", + "type": "text", + "x": 775, + "y": 332, + "width": 110, + "height": 25, + "text": "Token Registry", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center", + "containerId": "token-reg" + }, + { + "id": "faucet-acct", + "type": "rectangle", + "x": 940, + "y": 120, + "width": 170, + "height": 80, + "strokeColor": "#868e96", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "boundElements": [{"id": "arrow-ref", "type": "arrow"}] + }, + { + "id": "faucet-name", + "type": "text", + "x": 978, + "y": 140, + "width": 94, + "height": 35, + "text": "Faucet", + "fontSize": 28, + "fontFamily": 1, + "textAlign": "center" + }, + { + "id": "arrow1", + "type": "arrow", + "x": 210, + "y": 170, + "width": 60, + "height": 0, + "strokeColor": "#1e1e1e", + "strokeWidth": 2, + "points": [[0, 0], [60, 0]], + "startBinding": {"elementId": "operator", "focus": 0, "gap": 5}, + "endBinding": {"elementId": "config-note", "focus": 0, "gap": 5} + }, + { + "id": "a1-label", + "type": "text", + "x": 215, + "y": 145, + "width": 50, + "height": 20, + "text": "creates", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center" + }, + { + "id": "arrow2", + "type": "arrow", + "x": 550, + "y": 150, + "width": 70, + "height": 0, + "strokeColor": "#1e1e1e", + "strokeWidth": 2, + "points": [[0, 0], [70, 0]], + "startBinding": {"elementId": "config-note", "focus": 0, "gap": 5}, + "endBinding": {"elementId": "bridge", "focus": 0, "gap": 5} + }, + { + "id": "arrow3", + "type": "arrow", + "x": 680, + "y": 250, + "width": -30, + "height": 70, + "strokeColor": "#1e1e1e", + "strokeWidth": 2, + "strokeStyle": "dashed", + "points": [[0, 0], [-30, 70]], + "startBinding": {"elementId": "bridge", "focus": -0.5, "gap": 5}, + "endBinding": {"elementId": "faucet-reg", "focus": 0, "gap": 5} + }, + { + "id": "a3-label", + "type": "text", + "x": 615, + "y": 278, + "width": 40, + "height": 20, + "text": "write", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left" + }, + { + "id": "arrow4", + "type": "arrow", + "x": 780, + "y": 250, + "width": 50, + "height": 70, + "strokeColor": "#1e1e1e", + "strokeWidth": 2, + "strokeStyle": "dashed", + "points": [[0, 0], [50, 70]], + "startBinding": {"elementId": "bridge", "focus": 0.5, "gap": 5}, + "endBinding": {"elementId": "token-reg", "focus": 0, "gap": 5} + }, + { + "id": "a4-label", + "type": "text", + "x": 810, + "y": 278, + "width": 40, + "height": 20, + "text": "write", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left" + }, + { + "id": "arrow-ref", + "type": "arrow", + "x": 840, + "y": 160, + "width": 100, + "height": 0, + "strokeColor": "#868e96", + "strokeWidth": 2, + "strokeStyle": "dashed", + "points": [[0, 0], [100, 0]], + "startBinding": {"elementId": "bridge", "focus": 0, "gap": 5}, + "endBinding": {"elementId": "faucet-acct", "focus": 0, "gap": 5} + }, + { + "id": "aref-label", + "type": "text", + "x": 855, + "y": 138, + "width": 80, + "height": 20, + "text": "references", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "center" + } + ], + "appState": { + "viewBackgroundColor": "#ffffff", + "gridSize": 20 + }, + "files": {} +} diff --git a/crates/miden-agglayer/diagrams/faucet-registration.png b/crates/miden-agglayer/diagrams/faucet-registration.png new file mode 100644 index 0000000000..7f43fa2bee Binary files /dev/null and b/crates/miden-agglayer/diagrams/faucet-registration.png differ diff --git a/crates/miden-agglayer/diagrams/ger-injection.excalidraw b/crates/miden-agglayer/diagrams/ger-injection.excalidraw new file mode 100644 index 0000000000..4899e06b26 --- /dev/null +++ b/crates/miden-agglayer/diagrams/ger-injection.excalidraw @@ -0,0 +1,267 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "miden-agglayer", + "elements": [ + { + "id": "title", + "type": "text", + "x": 250, + "y": 30, + "width": 400, + "height": 35, + "text": "GER Injection", + "fontSize": 28, + "fontFamily": 1, + "textAlign": "center" + }, + { + "id": "ger-mgr", + "type": "diamond", + "x": 30, + "y": 120, + "width": 180, + "height": 100, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "boundElements": [ + {"id": "arrow1", "type": "arrow"}, + {"id": "arrow-observe", "type": "arrow"} + ] + }, + { + "id": "ger-mgr-label", + "type": "text", + "x": 55, + "y": 150, + "width": 130, + "height": 40, + "text": "GER\nManager", + "fontSize": 18, + "fontFamily": 1, + "textAlign": "center", + "containerId": "ger-mgr" + }, + { + "id": "l1", + "type": "rectangle", + "x": 50, + "y": 280, + "width": 140, + "height": 50, + "strokeColor": "#868e96", + "backgroundColor": "#e9ecef", + "fillStyle": "solid", + "strokeWidth": 2, + "roundness": { "type": 3 }, + "boundElements": [{"id": "arrow-observe", "type": "arrow"}] + }, + { + "id": "l1-label", + "type": "text", + "x": 65, + "y": 292, + "width": 110, + "height": 25, + "text": "AggLayer L1", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "center", + "containerId": "l1" + }, + { + "id": "arrow-observe", + "type": "arrow", + "x": 120, + "y": 280, + "width": 0, + "height": -60, + "strokeColor": "#868e96", + "strokeWidth": 2, + "strokeStyle": "dashed", + "points": [[0, 0], [0, -60]], + "startBinding": {"elementId": "l1", "focus": 0, "gap": 5}, + "endBinding": {"elementId": "ger-mgr", "focus": 0, "gap": 5} + }, + { + "id": "observe-label", + "type": "text", + "x": 130, + "y": 240, + "width": 90, + "height": 20, + "text": "observes GER", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left" + }, + { + "id": "update-ger", + "type": "rectangle", + "x": 310, + "y": 140, + "width": 160, + "height": 60, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "fillStyle": "solid", + "strokeWidth": 2, + "roundness": { "type": 3 }, + "boundElements": [ + {"id": "arrow1", "type": "arrow"}, + {"id": "arrow2", "type": "arrow"} + ] + }, + { + "id": "update-ger-label", + "type": "text", + "x": 325, + "y": 150, + "width": 130, + "height": 40, + "text": "UPDATE_GER", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "containerId": "update-ger" + }, + { + "id": "bridge", + "type": "rectangle", + "x": 570, + "y": 100, + "width": 220, + "height": 150, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "boundElements": [ + {"id": "arrow2", "type": "arrow"}, + {"id": "arrow3", "type": "arrow"} + ] + }, + { + "id": "bridge-name", + "type": "text", + "x": 625, + "y": 108, + "width": 110, + "height": 35, + "text": "Bridge", + "fontSize": 28, + "fontFamily": 1, + "textAlign": "center" + }, + { + "id": "bridge-desc", + "type": "text", + "x": 585, + "y": 150, + "width": 190, + "height": 85, + "text": "1. Assert sender\n2. Compute GER hash\n3. Store in ger_map", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "left" + }, + { + "id": "ger-map", + "type": "rectangle", + "x": 610, + "y": 320, + "width": 140, + "height": 50, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "roundness": { "type": 3 }, + "boundElements": [{"id": "arrow3", "type": "arrow"}] + }, + { + "id": "ger-map-label", + "type": "text", + "x": 635, + "y": 332, + "width": 90, + "height": 25, + "text": "GER Map", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "center", + "containerId": "ger-map" + }, + { + "id": "arrow1", + "type": "arrow", + "x": 210, + "y": 170, + "width": 100, + "height": 0, + "strokeColor": "#1e1e1e", + "strokeWidth": 2, + "points": [[0, 0], [100, 0]], + "startBinding": {"elementId": "ger-mgr", "focus": 0, "gap": 5}, + "endBinding": {"elementId": "update-ger", "focus": 0, "gap": 5} + }, + { + "id": "a1-label", + "type": "text", + "x": 225, + "y": 148, + "width": 70, + "height": 20, + "text": "creates", + "fontSize": 14, + "fontFamily": 1, + "textAlign": "center" + }, + { + "id": "arrow2", + "type": "arrow", + "x": 470, + "y": 170, + "width": 100, + "height": 0, + "strokeColor": "#1e1e1e", + "strokeWidth": 2, + "points": [[0, 0], [100, 0]], + "startBinding": {"elementId": "update-ger", "focus": 0, "gap": 5}, + "endBinding": {"elementId": "bridge", "focus": 0, "gap": 5} + }, + { + "id": "arrow3", + "type": "arrow", + "x": 680, + "y": 250, + "width": 0, + "height": 70, + "strokeColor": "#1e1e1e", + "strokeWidth": 2, + "strokeStyle": "dashed", + "points": [[0, 0], [0, 70]], + "startBinding": {"elementId": "bridge", "focus": 0, "gap": 5}, + "endBinding": {"elementId": "ger-map", "focus": 0, "gap": 5} + }, + { + "id": "a3-label", + "type": "text", + "x": 690, + "y": 278, + "width": 40, + "height": 20, + "text": "store", + "fontSize": 12, + "fontFamily": 1, + "textAlign": "left" + } + ], + "appState": { + "viewBackgroundColor": "#ffffff", + "gridSize": 20 + }, + "files": {} +} diff --git a/crates/miden-agglayer/diagrams/ger-injection.png b/crates/miden-agglayer/diagrams/ger-injection.png new file mode 100644 index 0000000000..31bee22110 Binary files /dev/null and b/crates/miden-agglayer/diagrams/ger-injection.png differ diff --git a/crates/miden-agglayer/src/bridge.rs b/crates/miden-agglayer/src/bridge.rs index 08b493f947..2ec155232b 100644 --- a/crates/miden-agglayer/src/bridge.rs +++ b/crates/miden-agglayer/src/bridge.rs @@ -116,7 +116,7 @@ static LET_NUM_LEAVES_SLOT_NAME: LazyLock = LazyLock::new(|| { /// - `update_ger`, which injects a new GER into the storage map. /// - `bridge_out`, which bridges an asset out of Miden to the destination network. /// - `claim`, which validates a claim against the AggLayer bridge and creates a MINT note for the -/// AggFaucet. +/// AggLayer Faucet. /// /// ## Storage Layout /// diff --git a/crates/miden-agglayer/src/claim_note.rs b/crates/miden-agglayer/src/claim_note.rs index 5cd5b9f17f..a3c5702bae 100644 --- a/crates/miden-agglayer/src/claim_note.rs +++ b/crates/miden-agglayer/src/claim_note.rs @@ -162,7 +162,7 @@ impl TryFrom for NoteStorage { // ================================================================================================ /// Generates a CLAIM note - a note that instructs the bridge to validate a claim and create -/// a MINT note for the aggfaucet. +/// a MINT note for the AggLayer Faucet. /// /// # Parameters /// - `storage`: The core storage for creating the CLAIM note diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/note.masm b/crates/miden-protocol/asm/kernels/transaction/lib/note.masm index 835f4eff48..a2fa70b19b 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/note.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/note.masm @@ -4,9 +4,6 @@ use $kernel::asset::ASSET_SIZE use $kernel::constants::NOTE_MEM_SIZE use $kernel::memory -pub use $kernel::util::note::NOTE_TYPE_PUBLIC -pub use $kernel::util::note::NOTE_TYPE_PRIVATE - # ERRORS # ================================================================================================= diff --git a/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm b/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm index f4bbcc7d36..c8a80d5f06 100644 --- a/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm +++ b/crates/miden-protocol/asm/kernels/transaction/lib/output_note.masm @@ -4,7 +4,6 @@ use $kernel::callbacks use $kernel::fungible_asset use $kernel::memory use $kernel::note -use $kernel::note::NOTE_TYPE_PUBLIC use $kernel::constants::MAX_OUTPUT_NOTES_PER_TX use $kernel::util::note::ATTACHMENT_KIND_NONE use $kernel::util::note::ATTACHMENT_KIND_ARRAY @@ -15,6 +14,10 @@ use miden::core::word # CONSTANTS # ================================================================================================= +# Constants for different note types +const PUBLIC_NOTE=1 # 0b01 +const PRIVATE_NOTE=2 # 0b10 + # The default value of the felt at index 3 in the note metadata header when a new note is created. # All zeros sets the attachment kind to None and the user-defined attachment scheme to "none". const ATTACHMENT_DEFAULT_KIND_AND_SCHEME=0 @@ -315,12 +318,10 @@ end #! or off-chain). #! - NOTE_METADATA_HEADER is the metadata associated with a note. pub proc build_metadata_header - # Validate that note type is private (0) or public (1). + # Validate that note type is private or public. # -------------------------------------------------------------------------------------------- - dup.1 - u32assert.err=ERR_NOTE_INVALID_TYPE u32lte.NOTE_TYPE_PUBLIC - assert.err=ERR_NOTE_INVALID_TYPE + dup.1 eq.PRIVATE_NOTE dup.2 eq.PUBLIC_NOTE or assert.err=ERR_NOTE_INVALID_TYPE # => [tag, note_type] # Validate the note tag fits into a u32. @@ -329,23 +330,22 @@ pub proc build_metadata_header u32assert.err=ERR_NOTE_TAG_MUST_BE_U32 # => [tag, note_type] - # Merge note type, version, and sender ID suffix. + # Merge note type and sender ID suffix. # -------------------------------------------------------------------------------------------- exec.account::get_id # => [sender_id_suffix, sender_id_prefix, tag, note_type] - # The lower 8 bits of the account ID suffix are guaranteed to be zero by construction. - # Encode note_type at bit 4, leaving version at 0 (in bits 0..=3). - # Shifting note_type left by 4 is equivalent to multiplying by 16. - movup.3 mul.16 add - # => [sender_id_suffix_type_version, sender_id_prefix, tag] + # the lower bits of an account ID suffix are guaranteed to be zero, so we can safely use that + # space to encode the note type + movup.3 add + # => [sender_id_suffix_and_note_type, sender_id_prefix, tag] # Build metadata header. # -------------------------------------------------------------------------------------------- push.ATTACHMENT_DEFAULT_KIND_AND_SCHEME movdn.3 - # => [sender_id_suffix_type_version, sender_id_prefix, tag, attachment_kind_scheme] + # => [sender_id_suffix_and_note_type, sender_id_prefix, tag, attachment_kind_scheme] # => [NOTE_METADATA_HEADER] end diff --git a/crates/miden-protocol/asm/protocol/note.masm b/crates/miden-protocol/asm/protocol/note.masm index c5f3c37526..8962a86263 100644 --- a/crates/miden-protocol/asm/protocol/note.masm +++ b/crates/miden-protocol/asm/protocol/note.masm @@ -4,8 +4,6 @@ use miden::core::mem # Re-export the max inputs per note constant. pub use miden::protocol::util::note::MAX_NOTE_STORAGE_ITEMS -pub use miden::protocol::util::note::NOTE_TYPE_PUBLIC -pub use miden::protocol::util::note::NOTE_TYPE_PRIVATE # ERRORS # ================================================================================================= @@ -186,11 +184,11 @@ end #! - sender_{suffix,prefix} are the suffix and prefix felts of the sender ID of the note which #! metadata was provided. pub proc extract_sender_from_metadata - # => [sender_id_suffix_type_version, sender_id_prefix, tag, attachment_kind_scheme] + # => [sender_id_suffix_and_note_type, sender_id_prefix, tag, attachment_kind_scheme] # drop tag and attachment_kind_scheme movup.3 drop movup.2 drop - # => [sender_id_suffix_type_version, sender_id_prefix] + # => [sender_id_suffix_and_note_type, sender_id_prefix] # extract suffix of sender from merged layout, which means clearing the least significant byte exec.account_id::shape_suffix @@ -209,7 +207,7 @@ end #! #! Invocation: exec pub proc extract_attachment_info_from_metadata - # => [sender_id_suffix_type_version, sender_id_prefix, tag, attachment_kind_scheme] + # => [sender_id_suffix_and_note_type, sender_id_prefix, tag, attachment_kind_scheme] drop drop drop # => [attachment_kind_scheme] diff --git a/crates/miden-protocol/asm/shared_utils/util/note.masm b/crates/miden-protocol/asm/shared_utils/util/note.masm index d0f474b692..066dfcd2fb 100644 --- a/crates/miden-protocol/asm/shared_utils/util/note.masm +++ b/crates/miden-protocol/asm/shared_utils/util/note.masm @@ -10,12 +10,3 @@ pub const ATTACHMENT_KIND_NONE=0 pub const ATTACHMENT_KIND_WORD=1 #! A note attachment consisting of the commitment to a set of felts. pub const ATTACHMENT_KIND_ARRAY=2 - -# Note type constants. These encode the note type in the lower byte of the metadata header. -# See NoteType in the Rust protocol crate for details. - -#! The note type of private notes. -pub const NOTE_TYPE_PRIVATE=0 - -#! The note type of public notes. -pub const NOTE_TYPE_PUBLIC=1 diff --git a/crates/miden-protocol/src/account/component/storage/init_storage_data.rs b/crates/miden-protocol/src/account/component/storage/init_storage_data.rs index 6a0f560d55..8eaa214894 100644 --- a/crates/miden-protocol/src/account/component/storage/init_storage_data.rs +++ b/crates/miden-protocol/src/account/component/storage/init_storage_data.rs @@ -47,9 +47,9 @@ impl From<&str> for WordValue { } impl From for WordValue { - /// Converts a [`Felt`] to a [`WordValue`] as a Word in the form `[0, 0, 0, felt]`. + /// Converts a [`Felt`] to a [`WordValue`] as a Word in the form `[felt, 0, 0, 0]`. fn from(value: Felt) -> Self { - WordValue::FullyTyped(Word::from([Felt::ZERO, Felt::ZERO, Felt::ZERO, value])) + WordValue::FullyTyped(Word::from([value, Felt::ZERO, Felt::ZERO, Felt::ZERO])) } } diff --git a/crates/miden-protocol/src/account/component/storage/schema/tests.rs b/crates/miden-protocol/src/account/component/storage/schema/tests.rs index 99161c5994..5ec22680ea 100644 --- a/crates/miden-protocol/src/account/component/storage/schema/tests.rs +++ b/crates/miden-protocol/src/account/component/storage/schema/tests.rs @@ -97,7 +97,7 @@ fn value_slot_schema_accepts_felt_typed_word_init_value() { init_data.set_value("demo::u8_word", 6u8).unwrap(); let built = slot.try_build_word(&init_data, &slot_name).unwrap(); - assert_eq!(built, Word::from([Felt::new(0), Felt::new(0), Felt::new(0), Felt::new(6)])); + assert_eq!(built, Word::from([Felt::new(6), Felt::new(0), Felt::new(0), Felt::new(0)])); } #[test] diff --git a/crates/miden-protocol/src/account/component/storage/toml/tests.rs b/crates/miden-protocol/src/account/component/storage/toml/tests.rs index 221c18c0a6..f445fad344 100644 --- a/crates/miden-protocol/src/account/component/storage/toml/tests.rs +++ b/crates/miden-protocol/src/account/component/storage/toml/tests.rs @@ -752,7 +752,7 @@ fn extensive_schema_metadata_and_init_toml_example() { }; assert_eq!( protocol_version_word, - &Word::from([Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::from(7u8)]) + &Word::from([Felt::from(7u8), Felt::ZERO, Felt::ZERO, Felt::ZERO]) ); let static_word_name = StorageSlotName::new("demo::static_word").unwrap(); @@ -831,7 +831,7 @@ fn extensive_schema_metadata_and_init_toml_example() { assert_eq!( typed_map_new_contents.get(&StorageMapKey::from_array([1, 2, 0, 0])), - Word::from([Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::new(16)]) + Word::from([Felt::new(16), Felt::ZERO, Felt::ZERO, Felt::ZERO]) ); let token_metadata_slot = @@ -941,6 +941,6 @@ fn typed_map_supports_non_numeric_value_types() { let key = Word::parse("0x1").unwrap(); let symbol_felt: Felt = TokenSymbol::new("BTC").unwrap().into(); - let expected_value = Word::from([Felt::ZERO, Felt::ZERO, Felt::ZERO, symbol_felt]); + let expected_value = Word::from([symbol_felt, Felt::ZERO, Felt::ZERO, Felt::ZERO]); assert_eq!(map.get(&StorageMapKey::from_raw(key)), expected_value); } diff --git a/crates/miden-protocol/src/account/component/storage/type_registry.rs b/crates/miden-protocol/src/account/component/storage/type_registry.rs index 6db8c9716c..88c884e9ec 100644 --- a/crates/miden-protocol/src/account/component/storage/type_registry.rs +++ b/crates/miden-protocol/src/account/component/storage/type_registry.rs @@ -280,17 +280,17 @@ where fn parse_str(input: &str) -> Result { let felt = ::parse_str(input)?; - Ok(Word::from([Felt::new(0), Felt::new(0), Felt::new(0), felt])) + Ok(Word::from([felt, Felt::new(0), Felt::new(0), Felt::new(0)])) } fn display_word(value: Word) -> Result { - if value[0] != Felt::new(0) || value[1] != Felt::new(0) || value[2] != Felt::new(0) { + if value[1] != Felt::new(0) || value[2] != Felt::new(0) || value[3] != Felt::new(0) { return Err(SchemaTypeError::ConversionError(format!( - "expected a word of the form [0, 0, 0, ] for type `{}`", + "expected a word of the form [, 0, 0, 0] for type `{}`", Self::type_name() ))); } - ::display_felt(value[3]) + ::display_felt(value[0]) } } @@ -676,13 +676,13 @@ impl SchemaTypeRegistry { match self.type_kind(type_name) { TypeKind::Word => Ok(()), TypeKind::Felt => { - // Felt types stored as words must have the form [0, 0, 0, ] - if word[0] != Felt::ZERO || word[1] != Felt::ZERO || word[2] != Felt::ZERO { + // Felt types stored as words must have the form [, 0, 0, 0] + if word[1] != Felt::ZERO || word[2] != Felt::ZERO || word[3] != Felt::ZERO { return Err(SchemaTypeError::ConversionError(format!( - "expected a word of the form [0, 0, 0, ] for type `{type_name}`" + "expected a word of the form [, 0, 0, 0] for type `{type_name}`" ))); } - self.validate_felt_value(type_name, word[3]) + self.validate_felt_value(type_name, word[0]) }, } } @@ -707,7 +707,7 @@ impl SchemaTypeRegistry { // Treat any registered felt type as a word type by zero-padding the remaining felts. if self.contains_felt_type(type_name) { - let value = self.display_felt(type_name, word[3]); + let value = self.display_felt(type_name, word[0]); return WordDisplay::Felt(value); } @@ -737,7 +737,7 @@ impl SchemaTypeRegistry { // Treat any registered felt type as a word type by zero-padding the remaining felts. if let Some(converter) = self.felt.get(type_name) { let felt = converter(value)?; - return Ok(Word::from([Felt::new(0), Felt::new(0), Felt::new(0), felt])); + return Ok(Word::from([felt, Felt::new(0), Felt::new(0), Felt::new(0)])); } Err(SchemaTypeError::WordTypeNotFound(type_name.clone())) @@ -776,12 +776,12 @@ mod tests { let numeric_word = SCHEMA_TYPE_REGISTRY .try_parse_word(&auth_scheme_type, "2") .expect("numeric auth scheme id should parse"); - assert_eq!(numeric_word, Word::from([Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::from(2u8)])); + assert_eq!(numeric_word, Word::from([Felt::from(2u8), Felt::ZERO, Felt::ZERO, Felt::ZERO])); let named_word = SCHEMA_TYPE_REGISTRY .try_parse_word(&auth_scheme_type, "EcdsaK256Keccak") .expect("named auth scheme should parse"); - assert_eq!(named_word, Word::from([Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::from(1u8)])); + assert_eq!(named_word, Word::from([Felt::from(1u8), Felt::ZERO, Felt::ZERO, Felt::ZERO])); let displayed = SCHEMA_TYPE_REGISTRY.display_word(&auth_scheme_type, numeric_word); assert!( @@ -798,7 +798,7 @@ mod tests { assert!(SCHEMA_TYPE_REGISTRY.try_parse_word(&auth_scheme_type, "9").is_err()); assert!(SCHEMA_TYPE_REGISTRY.try_parse_word(&auth_scheme_type, "invalid").is_err()); - let invalid_word = Word::from([Felt::ZERO, Felt::ZERO, Felt::ZERO, Felt::from(9u8)]); + let invalid_word = Word::from([Felt::from(9u8), Felt::ZERO, Felt::ZERO, Felt::ZERO]); assert!( SCHEMA_TYPE_REGISTRY .validate_word_value(&auth_scheme_type, invalid_word) diff --git a/crates/miden-protocol/src/batch/proven_batch.rs b/crates/miden-protocol/src/batch/proven_batch.rs index 32a0bf9d18..eb8aae5495 100644 --- a/crates/miden-protocol/src/batch/proven_batch.rs +++ b/crates/miden-protocol/src/batch/proven_batch.rs @@ -36,16 +36,13 @@ impl ProvenBatch { // CONSTRUCTORS // -------------------------------------------------------------------------------------------- - /// Creates a new [`ProvenBatch`] from the provided parts without checking any constraints - /// except the ones listed in the errors section below. - /// - /// This should essentially never be called by users. + /// Creates a new [`ProvenBatch`] from the provided parts. /// /// # Errors /// /// Returns an error if the batch expiration block number is not greater than the reference /// block number. - pub fn new_unchecked( + pub fn new( id: BatchId, reference_block_commitment: Word, reference_block_num: BlockNumber, @@ -180,7 +177,7 @@ impl Deserializable for ProvenBatch { let batch_expiration_block_num = BlockNumber::read_from(source)?; let transactions = OrderedTransactionHeaders::read_from(source)?; - Self::new_unchecked( + Self::new( id, reference_block_commitment, reference_block_num, diff --git a/crates/miden-protocol/src/note/metadata.rs b/crates/miden-protocol/src/note/metadata.rs index 535ff99e4d..04c36b9c08 100644 --- a/crates/miden-protocol/src/note/metadata.rs +++ b/crates/miden-protocol/src/note/metadata.rs @@ -14,12 +14,6 @@ use crate::Hasher; use crate::errors::NoteError; use crate::note::{NoteAttachment, NoteAttachmentKind, NoteAttachmentScheme}; -// CONSTANTS -// ================================================================================================ - -/// The number of bits by which the note type is offset in the first felt of the note metadata. -const NOTE_TYPE_SHIFT: u64 = 4; - // NOTE METADATA // ================================================================================================ @@ -40,7 +34,7 @@ const NOTE_TYPE_SHIFT: u64 = 4; /// The header word has the following layout: /// /// ```text -/// 0th felt: [sender_id_suffix (56 bits) | reserved (3 bits) | note_type (1 bit) | version (4 bits)] +/// 0th felt: [sender_id_suffix (56 bits) | 6 zero bits | note_type (2 bit)] /// 1st felt: [sender_id_prefix (64 bits)] /// 2nd felt: [32 zero bits | note_tag (32 bits)] /// 3rd felt: [30 zero bits | attachment_kind (2 bits) | attachment_scheme (32 bits)] @@ -50,13 +44,11 @@ const NOTE_TYPE_SHIFT: u64 = 4; /// - 1st felt: The lower 8 bits of the account ID suffix are `0` by construction, so that they can /// be overwritten with other data. The suffix' most significant bit must be zero such that the /// entire felt retains its validity even if all of its lower 8 bits are set to `1`. So the note -/// type and version can be comfortably encoded. +/// type can be comfortably encoded. /// - 2nd felt: Is equivalent to the prefix of the account ID so it inherits its validity. /// - 3rd felt: The upper 32 bits are always zero. /// - 4th felt: The upper 30 bits are always zero. /// -/// The version is hardcoded to 0 and is reserved to make it easier to introduce another version. -/// /// The value of the attachment word depends on the /// [`NoteAttachmentKind`](crate::note::NoteAttachmentKind): /// - [`NoteAttachmentKind::None`](crate::note::NoteAttachmentKind::None): Empty word. @@ -81,12 +73,6 @@ pub struct NoteMetadata { } impl NoteMetadata { - /// Version 0 of the note metadata encoding. - /// - /// If we make this public, we may want to instead consider introducing a `NoteMetadataVersion` - /// struct, similar to `AccountIdVersion`. - const VERSION_0: u8 = 0; - // CONSTRUCTORS // -------------------------------------------------------------------------------------------- @@ -351,12 +337,12 @@ impl TryFrom for NoteMetadataHeader { // HELPER FUNCTIONS // ================================================================================================ -/// Merges the suffix of an [`AccountId`] and note metadata into a single [`Felt`]. +/// Merges the suffix of an [`AccountId`] and the [`NoteType`] into a single [`Felt`]. /// /// The layout is as follows: /// /// ```text -/// [sender_id_suffix (56 bits) | reserved (3 bits) | note_type (1 bit) | version (4 bits)] +/// [sender_id_suffix (56 bits) | 6 zero bits | note_type (2 bits)] /// ``` /// /// The most significant bit of the suffix is guaranteed to be zero, so the felt retains its @@ -367,44 +353,28 @@ fn merge_sender_suffix_and_note_type(sender_id_suffix: Felt, note_type: NoteType let mut merged = sender_id_suffix.as_canonical_u64(); let note_type_byte = note_type as u8; - debug_assert!(note_type_byte < 2, "note type must not contain values >= 2"); - // note_type at bit 4, version at bits 0..=3 (hardcoded to NoteMetadata::VERSION_0_NUMBER) - merged |= (note_type_byte as u64) << NOTE_TYPE_SHIFT; - merged |= NoteMetadata::VERSION_0 as u64; + debug_assert!(note_type_byte < 4, "note type must not contain values >= 4"); + merged |= note_type_byte as u64; // SAFETY: The most significant bit of the suffix is zero by construction so the u64 will be a // valid felt. Felt::try_from(merged).expect("encoded value should be a valid felt") } -/// Unmerges the sender ID suffix and note metadata (note type and version). +/// Unmerges the sender ID suffix and note type. fn unmerge_sender_suffix_and_note_type(element: Felt) -> Result<(Felt, NoteType), NoteError> { - // The mask that clears out the lower 8 bits to recover the sender suffix. - const SENDER_SUFFIX_MASK: u64 = 0xffff_ffff_ffff_ff00; - - let raw = element.as_canonical_u64(); - let version = (raw & 0b1111) as u8; - let note_type_bit = ((raw >> NOTE_TYPE_SHIFT) & 0b1) as u8; - let reserved = ((raw >> 5) & 0b111) as u8; - - if reserved != 0 { - return Err(NoteError::other("reserved bits in note metadata header must be zero")); - } - - if version != NoteMetadata::VERSION_0 { - return Err(NoteError::other(format!( - "unsupported note metadata version {version}, expected {}", - NoteMetadata::VERSION_0 - ))); - } + const NOTE_TYPE_MASK: u8 = 0b11; + // Inverts the note type mask. + const SENDER_SUFFIX_MASK: u64 = !(NOTE_TYPE_MASK as u64); - let note_type = NoteType::try_from(note_type_bit).map_err(|source| { + let note_type_byte = element.as_canonical_u64() as u8 & NOTE_TYPE_MASK; + let note_type = NoteType::try_from(note_type_byte).map_err(|source| { NoteError::other_with_source("failed to decode note type from metadata header", source) })?; // No bits were set so felt should still be valid. - let sender_suffix = - Felt::try_from(raw & SENDER_SUFFIX_MASK).expect("felt should still be valid"); + let sender_suffix = Felt::try_from(element.as_canonical_u64() & SENDER_SUFFIX_MASK) + .expect("felt should still be valid"); Ok((sender_suffix, note_type)) } diff --git a/crates/miden-protocol/src/note/note_type.rs b/crates/miden-protocol/src/note/note_type.rs index 204746d336..d72b953f86 100644 --- a/crates/miden-protocol/src/note/note_type.rs +++ b/crates/miden-protocol/src/note/note_type.rs @@ -14,10 +14,9 @@ use crate::utils::serde::{ // NOTE TYPE // ================================================================================================ -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(u8)] pub enum NoteType { - #[default] /// Notes with this type have only their hash published to the network. Private = Self::PRIVATE, @@ -26,21 +25,17 @@ pub enum NoteType { } impl NoteType { - const PRIVATE: u8 = 0; - const PUBLIC: u8 = 1; - - /// Returns the note type encoded to a 1-bit flag, where private is 0 and public is 1. - pub const fn as_u8(self) -> u8 { - self as u8 - } + // Keep these masks in sync with `miden-lib/asm/miden/kernels/tx/tx.masm` + pub const PUBLIC: u8 = 0b01; + pub const PRIVATE: u8 = 0b10; } // CONVERSIONS FROM NOTE TYPE // ================================================================================================ impl From for Felt { - fn from(note_type: NoteType) -> Self { - Felt::from(note_type.as_u8()) + fn from(id: NoteType) -> Self { + Felt::new(id as u64) } } @@ -59,27 +54,38 @@ impl TryFrom for NoteType { } } -impl TryFrom for NoteType { +impl TryFrom for NoteType { type Error = NoteError; - fn try_from(value: Felt) -> Result { - let byte = value.as_canonical_u64(); - Self::try_from( - u8::try_from(byte) - .map_err(|_| NoteError::UnknownNoteType(format!("0b{byte:b}").into()))?, - ) + fn try_from(value: u16) -> Result { + Self::try_from(value as u64) } } -// STRING CONVERSION -// ================================================================================================ +impl TryFrom for NoteType { + type Error = NoteError; -impl Display for NoteType { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - NoteType::Private => write!(f, "private"), - NoteType::Public => write!(f, "public"), - } + fn try_from(value: u32) -> Result { + Self::try_from(value as u64) + } +} + +impl TryFrom for NoteType { + type Error = NoteError; + + fn try_from(value: u64) -> Result { + let value: u8 = value + .try_into() + .map_err(|_| NoteError::UnknownNoteType(format!("0b{value:b}").into()))?; + value.try_into() + } +} + +impl TryFrom for NoteType { + type Error = NoteError; + + fn try_from(value: Felt) -> Result { + value.as_canonical_u64().try_into() } } @@ -126,47 +132,32 @@ impl Deserializable for NoteType { } } -// TESTS +// DISPLAY // ================================================================================================ -#[cfg(test)] -mod tests { +impl Display for NoteType { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + NoteType::Private => write!(f, "private"), + NoteType::Public => write!(f, "public"), + } + } +} + +#[test] +fn test_from_str_note_type() { use assert_matches::assert_matches; - use super::*; use crate::alloc::string::ToString; - #[rstest::rstest] - #[case::private(NoteType::Private)] - #[case::public(NoteType::Public)] - #[test] - fn test_note_type_roundtrip(#[case] note_type: NoteType) -> anyhow::Result<()> { - // String roundtrip - assert_eq!(note_type, note_type.to_string().parse()?); - - // Serialization roundtrip - assert_eq!(note_type, NoteType::read_from_bytes(¬e_type.to_bytes())?); - - // Byte conversion roundtrip - assert_eq!(note_type, NoteType::try_from(note_type.as_u8())?); - - // Felt conversion roundtrip - assert_eq!(note_type, NoteType::try_from(Felt::from(note_type))?); - - Ok(()) + for string in ["private", "public"] { + let parsed_note_type = NoteType::from_str(string).unwrap(); + assert_eq!(parsed_note_type.to_string(), string); } - #[test] - fn test_from_str_note_type() { - for string in ["private", "public"] { - let parsed_note_type = NoteType::from_str(string).unwrap(); - assert_eq!(parsed_note_type.to_string(), string); - } + let public_type_invalid_err = NoteType::from_str("puBlIc").unwrap_err(); + assert_matches!(public_type_invalid_err, NoteError::UnknownNoteType(_)); - let public_type_invalid_err = NoteType::from_str("puBlIc").unwrap_err(); - assert_matches!(public_type_invalid_err, NoteError::UnknownNoteType(_)); - - let invalid_type = NoteType::from_str("invalid").unwrap_err(); - assert_matches!(invalid_type, NoteError::UnknownNoteType(_)); - } + let invalid_type = NoteType::from_str("invalid").unwrap_err(); + assert_matches!(invalid_type, NoteError::UnknownNoteType(_)); } diff --git a/crates/miden-standards/asm/standards/auth/tx_policy.masm b/crates/miden-standards/asm/standards/auth/tx_policy.masm index 8ad6264183..76da300070 100644 --- a/crates/miden-standards/asm/standards/auth/tx_policy.masm +++ b/crates/miden-standards/asm/standards/auth/tx_policy.masm @@ -3,8 +3,6 @@ use miden::protocol::native_account use miden::protocol::tx const ERR_AUTH_PROCEDURE_MUST_BE_CALLED_ALONE = "procedure must be called alone" -const ERR_AUTH_TRANSACTION_MUST_NOT_INCLUDE_INPUT_NOTES = "transaction must not include input notes" -const ERR_AUTH_TRANSACTION_MUST_NOT_INCLUDE_OUTPUT_NOTES = "transaction must not include output notes" const ERR_AUTH_TRANSACTION_MUST_NOT_INCLUDE_INPUT_OR_OUTPUT_NOTES = "transaction must not include input or output notes" #! Asserts that exactly one non-auth account procedure was called in the current transaction. @@ -61,34 +59,6 @@ pub proc assert_only_one_non_auth_procedure_called # => [] end -#! Asserts that the current transaction does not consume input notes. -#! -#! Inputs: [] -#! Outputs: [] -#! -#! Invocation: exec -pub proc assert_no_input_notes - exec.tx::get_num_input_notes - # => [num_input_notes] - - assertz.err=ERR_AUTH_TRANSACTION_MUST_NOT_INCLUDE_INPUT_NOTES - # => [] -end - -#! Asserts that the current transaction does not create output notes. -#! -#! Inputs: [] -#! Outputs: [] -#! -#! Invocation: exec -pub proc assert_no_output_notes - exec.tx::get_num_output_notes - # => [num_output_notes] - - assertz.err=ERR_AUTH_TRANSACTION_MUST_NOT_INCLUDE_OUTPUT_NOTES - # => [] -end - #! Asserts that the current transaction does not consume input notes or create output notes. #! #! Inputs: [] diff --git a/crates/miden-standards/asm/standards/faucets/mod.masm b/crates/miden-standards/asm/standards/faucets/mod.masm index ab2ed1c2e5..57eaf2b416 100644 --- a/crates/miden-standards/asm/standards/faucets/mod.masm +++ b/crates/miden-standards/asm/standards/faucets/mod.masm @@ -10,6 +10,7 @@ use miden::protocol::asset::FUNGIBLE_ASSET_MAX_AMOUNT # ================================================================================================= const ASSET_PTR=0 +const PRIVATE_NOTE=2 # ERRORS # ================================================================================================= diff --git a/crates/miden-standards/asm/standards/notes/mint.masm b/crates/miden-standards/asm/standards/notes/mint.masm index 7780a67052..ca287daf48 100644 --- a/crates/miden-standards/asm/standards/notes/mint.masm +++ b/crates/miden-standards/asm/standards/notes/mint.masm @@ -9,8 +9,8 @@ use miden::standards::faucets::network_fungible->network_faucet const MINT_NOTE_NUM_STORAGE_ITEMS_PRIVATE=12 const MINT_NOTE_MIN_NUM_STORAGE_ITEMS_PUBLIC=16 -use miden::protocol::note::NOTE_TYPE_PUBLIC -use miden::protocol::note::NOTE_TYPE_PRIVATE +const OUTPUT_NOTE_TYPE_PUBLIC=1 +const OUTPUT_NOTE_TYPE_PRIVATE=2 # Memory Addresses of MINT note storage # The attachment is at the same memory address for both private and public storage. @@ -98,7 +98,7 @@ pub proc main # => [RECIPIENT, pad(12)] # push note_type, and load tag and amount - push.NOTE_TYPE_PUBLIC + push.OUTPUT_NOTE_TYPE_PUBLIC mem_load.0 mem_load.1 # => [amount, tag, note_type, RECIPIENT, pad(12)] else @@ -114,7 +114,7 @@ pub proc main # => [RECIPIENT, pad(12)] # push note_type, and load tag and amount - push.NOTE_TYPE_PRIVATE + push.OUTPUT_NOTE_TYPE_PRIVATE mem_load.0 mem_load.1 # => [amount, tag, note_type, RECIPIENT, pad(12)] end diff --git a/crates/miden-standards/asm/standards/wallets/basic.masm b/crates/miden-standards/asm/standards/wallets/basic.masm index 9656713977..57f72ec600 100644 --- a/crates/miden-standards/asm/standards/wallets/basic.masm +++ b/crates/miden-standards/asm/standards/wallets/basic.masm @@ -6,6 +6,7 @@ use miden::protocol::active_note # CONSTANTS # ================================================================================================= +const PUBLIC_NOTE=1 #! Adds the provided asset to the active account. #! diff --git a/crates/miden-standards/src/account/auth/multisig/mod.rs b/crates/miden-standards/src/account/auth/multisig.rs similarity index 99% rename from crates/miden-standards/src/account/auth/multisig/mod.rs rename to crates/miden-standards/src/account/auth/multisig.rs index ddbd9e99dd..196bb3de0c 100644 --- a/crates/miden-standards/src/account/auth/multisig/mod.rs +++ b/crates/miden-standards/src/account/auth/multisig.rs @@ -1,6 +1,3 @@ -#[allow(dead_code)] -pub(crate) mod procedure_policies; - use alloc::collections::BTreeSet; use alloc::vec::Vec; diff --git a/crates/miden-standards/src/account/auth/multisig/procedure_policies.rs b/crates/miden-standards/src/account/auth/multisig/procedure_policies.rs deleted file mode 100644 index c83245cc0a..0000000000 --- a/crates/miden-standards/src/account/auth/multisig/procedure_policies.rs +++ /dev/null @@ -1,246 +0,0 @@ -use miden_protocol::Word; -use miden_protocol::errors::AccountError; - -/// Defines which execution modes a procedure policy supports and the corresponding threshold -/// values for each mode. -/// -/// A procedure can require the immediate threshold, the delayed threshold, or support both. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum ProcedurePolicyExecutionMode { - ImmediateOnly { - immediate_threshold: u32, - }, - DelayOnly { - delay_threshold: u32, - }, - ImmediateOrDelay { - immediate_threshold: u32, - delay_threshold: u32, - }, -} - -/// Note Restrictions on whether transactions that call a procedure may consume input notes -/// or create output notes. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -#[repr(u8)] -pub enum ProcedurePolicyNoteRestriction { - #[default] - None = 0, - NoInputNotes = 1, - NoOutputNotes = 2, - NoInputOrOutputNotes = 3, -} - -/// Defines a per-procedure multisig policy. -/// -/// A procedure policy can override the default multisig requirements for a specific procedure. -/// It specifies: -/// - an execution mode, which determines whether the procedure can be executed immediately, after a -/// delay, or both -/// - note restrictions, which limit whether a transaction invoking the procedure may consume input -/// notes or create output notes -/// -/// Execution modes: -/// - Immediate execution: the action is authorized and executed within the current transaction. -/// - Delayed execution: the action is proposed first, and can only be executed after a required -/// time delay has elapsed. -/// -/// Thresholds: -/// - Immediate threshold: the number of signatures required to authorize immediate execution. -/// - Delayed threshold: the number of signatures required to authorize a delayed action. -/// -/// The thresholds for immediate and delayed execution may differ. -/// -/// The policy is encoded into the procedure-policy storage word as: -/// `[immediate_threshold, delayed_threshold, note_restrictions, 0]`. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct ProcedurePolicy { - execution_mode: ProcedurePolicyExecutionMode, - note_restrictions: ProcedurePolicyNoteRestriction, -} - -impl ProcedurePolicy { - /// Creates an explicit procedure policy from an execution mode and note restriction pair. - /// - /// Common multisig cases should generally prefer the `with_*_threshold...` helpers and - /// configure note restrictions afterwards via [`ProcedurePolicy::with_note_restriction`]. - pub fn new( - execution_mode: ProcedurePolicyExecutionMode, - note_restrictions: ProcedurePolicyNoteRestriction, - ) -> Result { - Self::validate_execution_mode(execution_mode)?; - Ok(Self { execution_mode, note_restrictions }) - } - - pub fn with_immediate_threshold(immediate_threshold: u32) -> Result { - Self::new( - ProcedurePolicyExecutionMode::ImmediateOnly { immediate_threshold }, - ProcedurePolicyNoteRestriction::None, - ) - } - - pub fn with_delay_threshold(delay_threshold: u32) -> Result { - Self::new( - ProcedurePolicyExecutionMode::DelayOnly { delay_threshold }, - ProcedurePolicyNoteRestriction::None, - ) - } - - pub fn with_immediate_and_delay_thresholds( - immediate_threshold: u32, - delay_threshold: u32, - ) -> Result { - Self::new( - ProcedurePolicyExecutionMode::ImmediateOrDelay { immediate_threshold, delay_threshold }, - ProcedurePolicyNoteRestriction::None, - ) - } - - pub const fn with_note_restriction( - mut self, - note_restrictions: ProcedurePolicyNoteRestriction, - ) -> Self { - self.note_restrictions = note_restrictions; - self - } - - pub const fn execution_mode(&self) -> ProcedurePolicyExecutionMode { - self.execution_mode - } - - pub const fn note_restrictions(&self) -> ProcedurePolicyNoteRestriction { - self.note_restrictions - } - - pub const fn immediate_threshold(&self) -> Option { - match self.execution_mode { - ProcedurePolicyExecutionMode::ImmediateOnly { immediate_threshold } => { - Some(immediate_threshold) - }, - ProcedurePolicyExecutionMode::DelayOnly { .. } => None, - ProcedurePolicyExecutionMode::ImmediateOrDelay { immediate_threshold, .. } => { - Some(immediate_threshold) - }, - } - } - - pub const fn delay_threshold(&self) -> Option { - match self.execution_mode { - ProcedurePolicyExecutionMode::ImmediateOnly { .. } => None, - ProcedurePolicyExecutionMode::DelayOnly { delay_threshold } => Some(delay_threshold), - ProcedurePolicyExecutionMode::ImmediateOrDelay { delay_threshold, .. } => { - Some(delay_threshold) - }, - } - } - - fn validate_execution_mode( - execution_mode: ProcedurePolicyExecutionMode, - ) -> Result<(), AccountError> { - match execution_mode { - ProcedurePolicyExecutionMode::ImmediateOnly { immediate_threshold } => { - if immediate_threshold == 0 { - return Err(AccountError::other( - "procedure policy immediate threshold must be at least 1", - )); - } - }, - ProcedurePolicyExecutionMode::DelayOnly { delay_threshold } => { - if delay_threshold == 0 { - return Err(AccountError::other( - "procedure policy delay threshold must be at least 1", - )); - } - }, - ProcedurePolicyExecutionMode::ImmediateOrDelay { - immediate_threshold, - delay_threshold, - } => { - if immediate_threshold == 0 || delay_threshold == 0 { - return Err(AccountError::other( - "immediate and delayed thresholds must both be at least 1", - )); - } - // Delayed execution is the lower-quorum option while immediate execution is - // higher-quorum path. If the delay threshold were greater than the - // immediate threshold, the "fast" path would be easier to satisfy - // than the delayed path, which contradicts that model. - if delay_threshold > immediate_threshold { - return Err(AccountError::other( - "delay threshold cannot exceed immediate threshold", - )); - } - }, - } - - Ok(()) - } - - pub fn to_word(self) -> Word { - let immediate_threshold = self.immediate_threshold().unwrap_or(0); - let delay_threshold = self.delay_threshold().unwrap_or(0); - - Word::from([immediate_threshold, delay_threshold, self.note_restrictions as u32, 0]) - } -} - -#[cfg(test)] -mod tests { - use alloc::string::ToString; - - use super::{ProcedurePolicy, ProcedurePolicyNoteRestriction}; - - #[test] - fn procedure_policy_word_encoding_matches_storage_layout() { - let policy = ProcedurePolicy::with_immediate_and_delay_thresholds(4, 3) - .unwrap() - .with_note_restriction(ProcedurePolicyNoteRestriction::NoInputOrOutputNotes); - - assert_eq!(policy.to_word(), [4u32, 3, 3, 0].into()); - } - - #[test] - fn procedure_policy_construction_rejects_invalid_combinations() { - assert!( - ProcedurePolicy::with_immediate_threshold(0) - .unwrap_err() - .to_string() - .contains("procedure policy immediate threshold must be at least 1") - ); - - assert!( - ProcedurePolicy::with_immediate_and_delay_thresholds(1, 0) - .unwrap_err() - .to_string() - .contains("immediate and delayed thresholds must both be at least 1") - ); - - assert!( - ProcedurePolicy::with_immediate_and_delay_thresholds(1, 2) - .unwrap_err() - .to_string() - .contains("delay threshold cannot exceed immediate threshold") - ); - } - - #[test] - fn procedure_policy_thresholds_are_exposed_with_getters() { - let procedure_policy = ProcedurePolicy::with_delay_threshold(2).unwrap(); - - assert_eq!(procedure_policy.immediate_threshold(), None); - assert_eq!(procedure_policy.delay_threshold(), Some(2)); - } - - #[test] - fn procedure_policy_note_restrictions_are_exposed_with_getters() { - let procedure_policy = ProcedurePolicy::with_immediate_threshold(2) - .unwrap() - .with_note_restriction(ProcedurePolicyNoteRestriction::NoInputNotes); - - assert_eq!(ProcedurePolicyNoteRestriction::default(), ProcedurePolicyNoteRestriction::None); - assert_eq!( - procedure_policy.note_restrictions(), - ProcedurePolicyNoteRestriction::NoInputNotes - ); - } -} diff --git a/crates/miden-standards/src/testing/mock_util_lib.rs b/crates/miden-standards/src/testing/mock_util_lib.rs index 09fd850aa5..211feed5d9 100644 --- a/crates/miden-standards/src/testing/mock_util_lib.rs +++ b/crates/miden-standards/src/testing/mock_util_lib.rs @@ -7,14 +7,13 @@ use crate::StandardsLib; const MOCK_UTIL_LIBRARY_CODE: &str = " use miden::protocol::output_note - use miden::protocol::note::NOTE_TYPE_PRIVATE use miden::standards::wallets::basic->wallet #! Inputs: [] #! Outputs: [note_idx] pub proc create_default_note push.1.2.3.4 # = RECIPIENT - push.NOTE_TYPE_PRIVATE # = NoteType::Private + push.2 # = NoteType::Private push.0 # = NoteTag # => [tag, note_type, RECIPIENT] diff --git a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs index a2ff0c722d..fbcc0cd51d 100644 --- a/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs +++ b/crates/miden-testing/src/kernel_tests/tx/test_output_note.rs @@ -78,7 +78,7 @@ async fn test_create_note() -> anyhow::Result<()> { exec.prologue::prepare_transaction push.{recipient} - push.{NOTE_TYPE_PUBLIC} + push.{PUBLIC_NOTE} push.{tag} exec.output_note::create @@ -88,7 +88,7 @@ async fn test_create_note() -> anyhow::Result<()> { end ", recipient = recipient, - NOTE_TYPE_PUBLIC = NoteType::Public as u8, + PUBLIC_NOTE = NoteType::Public as u8, tag = tag, ); @@ -157,7 +157,7 @@ fn note_creation_script(tag: Felt) -> String { exec.prologue::prepare_transaction push.{recipient} - push.{NOTE_TYPE_PUBLIC} + push.{PUBLIC_NOTE} push.{tag} exec.output_note::create @@ -167,7 +167,7 @@ fn note_creation_script(tag: Felt) -> String { end ", recipient = Word::from([0, 1, 2, 3u32]), - NOTE_TYPE_PUBLIC = NoteType::Public as u8, + PUBLIC_NOTE = NoteType::Public as u8, ) } @@ -188,7 +188,7 @@ async fn test_create_note_too_many_notes() -> anyhow::Result<()> { exec.prologue::prepare_transaction push.{recipient} - push.{NOTE_TYPE_PUBLIC} + push.{PUBLIC_NOTE} push.{tag} exec.output_note::create @@ -196,7 +196,7 @@ async fn test_create_note_too_many_notes() -> anyhow::Result<()> { ", tag = NoteTag::new(1234 << 16 | 5678), recipient = Word::from([0, 1, 2, 3u32]), - NOTE_TYPE_PUBLIC = NoteType::Public as u8, + PUBLIC_NOTE = NoteType::Public as u8, ); let exec_output = tx_context.execute_code(&code).await; @@ -264,7 +264,7 @@ async fn test_get_output_notes_commitment() -> anyhow::Result<()> { # create output note 1 push.{recipient_1} - push.{NOTE_TYPE_PUBLIC} + push.{PUBLIC_NOTE} push.{tag_1} exec.output_note::create # => [note_idx] @@ -276,7 +276,7 @@ async fn test_get_output_notes_commitment() -> anyhow::Result<()> { # create output note 2 push.{recipient_2} - push.{NOTE_TYPE_PUBLIC} + push.{PUBLIC_NOTE} push.{tag_2} exec.output_note::create # => [note_idx] @@ -303,7 +303,7 @@ async fn test_get_output_notes_commitment() -> anyhow::Result<()> { # => [OUTPUT_NOTES_COMMITMENT] end ", - NOTE_TYPE_PUBLIC = NoteType::Public as u8, + PUBLIC_NOTE = NoteType::Public as u8, recipient_1 = output_note_1.recipient().digest(), tag_1 = output_note_1.metadata().tag(), ASSET_1_KEY = asset_1.to_key_word(), @@ -373,7 +373,7 @@ async fn test_create_note_and_add_asset() -> anyhow::Result<()> { exec.prologue::prepare_transaction push.{recipient} - push.{NOTE_TYPE_PUBLIC} + push.{PUBLIC_NOTE} push.{tag} exec.output_note::create @@ -395,7 +395,7 @@ async fn test_create_note_and_add_asset() -> anyhow::Result<()> { end ", recipient = recipient, - NOTE_TYPE_PUBLIC = NoteType::Public as u8, + PUBLIC_NOTE = NoteType::Public as u8, tag = tag, ASSET_KEY = asset.to_key_word(), ASSET_VALUE = asset.to_value_word(), @@ -443,7 +443,7 @@ async fn test_create_note_and_add_multiple_assets() -> anyhow::Result<()> { exec.prologue::prepare_transaction push.{recipient} - push.{NOTE_TYPE_PUBLIC} + push.{PUBLIC_NOTE} push.{tag} exec.output_note::create # => [note_idx] @@ -480,7 +480,7 @@ async fn test_create_note_and_add_multiple_assets() -> anyhow::Result<()> { end ", recipient = recipient, - NOTE_TYPE_PUBLIC = NoteType::Public as u8, + PUBLIC_NOTE = NoteType::Public as u8, tag = tag, ASSET_KEY = asset.to_key_word(), ASSET_VALUE = asset.to_value_word(), @@ -572,7 +572,7 @@ async fn test_create_note_and_add_same_nft_twice() -> anyhow::Result<()> { # => [] push.{recipient} - push.{NOTE_TYPE_PUBLIC} + push.{PUBLIC_NOTE} push.{tag} exec.output_note::create # => [note_idx] @@ -592,7 +592,7 @@ async fn test_create_note_and_add_same_nft_twice() -> anyhow::Result<()> { end ", recipient = recipient, - NOTE_TYPE_PUBLIC = NoteType::Public as u8, + PUBLIC_NOTE = NoteType::Public as u8, tag = tag, ASSET_KEY = non_fungible_asset.to_key_word(), ASSET_VALUE = non_fungible_asset.to_value_word(), @@ -672,7 +672,7 @@ async fn test_build_recipient_hash() -> anyhow::Result<()> { exec.note::build_recipient_hash # => [RECIPIENT, pad(12)] - push.{NOTE_TYPE_PUBLIC} + push.{PUBLIC_NOTE} push.{tag} # => [tag, note_type, RECIPIENT] @@ -685,7 +685,7 @@ async fn test_build_recipient_hash() -> anyhow::Result<()> { ", script_root = input_note_1.script().clone().root(), output_serial_no = output_serial_no, - NOTE_TYPE_PUBLIC = NoteType::Public as u8, + PUBLIC_NOTE = NoteType::Public as u8, tag = tag, ); diff --git a/crates/miden-testing/tests/agglayer/bridge_out.rs b/crates/miden-testing/tests/agglayer/bridge_out.rs index 52c11a6a73..e0a61d3e47 100644 --- a/crates/miden-testing/tests/agglayer/bridge_out.rs +++ b/crates/miden-testing/tests/agglayer/bridge_out.rs @@ -16,7 +16,7 @@ use miden_protocol::Felt; use miden_protocol::account::auth::AuthScheme; use miden_protocol::account::{AccountId, AccountIdVersion, AccountStorageMode, AccountType}; use miden_protocol::asset::{Asset, FungibleAsset}; -use miden_protocol::note::{NoteAssets, NoteScript, NoteType}; +use miden_protocol::note::{NoteAssets, NoteType}; use miden_protocol::transaction::RawOutputNote; use miden_standards::account::faucets::TokenMetadata; use miden_standards::account::mint_policies::OwnerControlledInitConfig; @@ -156,7 +156,6 @@ async fn bridge_out_consecutive() -> anyhow::Result<()> { // STEP 2: CONSUME 32 B2AGG NOTES AND VERIFY FRONTIER EVOLUTION // -------------------------------------------------------------------------------------------- - let burn_note_script: NoteScript = StandardNote::BURN.script(); let mut burn_note_ids = Vec::with_capacity(note_count); for (i, note) in notes.iter().enumerate() { @@ -164,7 +163,6 @@ async fn bridge_out_consecutive() -> anyhow::Result<()> { let executed_tx = mock_chain .build_tx_context(bridge_account.clone(), &[note.id()], &[])? - .add_note_script(burn_note_script.clone()) .foreign_accounts(vec![foreign_account_inputs]) .build()? .execute() diff --git a/crates/miden-tx-batch-prover/src/local_batch_prover.rs b/crates/miden-tx-batch-prover/src/local_batch_prover.rs index 09a5dc3fce..4e7ccfffc7 100644 --- a/crates/miden-tx-batch-prover/src/local_batch_prover.rs +++ b/crates/miden-tx-batch-prover/src/local_batch_prover.rs @@ -74,7 +74,7 @@ impl LocalBatchProver { batch_expiration_block_num, ) = proposed_batch.into_parts(); - ProvenBatch::new_unchecked( + ProvenBatch::new( id, block_header.commitment(), block_header.block_num(), diff --git a/deny.toml b/deny.toml index 29b7c11c46..af17464860 100644 --- a/deny.toml +++ b/deny.toml @@ -45,6 +45,7 @@ highlight = "all" multiple-versions = "deny" skip = [ #{ name = "ansi_term", version = "=0.11.0" }, + { name = "cpufeatures" }, # Allow duplicate rand versions - miden-field uses 0.10, miden-vm uses 0.9 { name = "rand" }, { name = "rand_core" }, diff --git a/docs/src/account/components.md b/docs/src/account/components.md index 4adcbcb6e3..769e0c5925 100644 --- a/docs/src/account/components.md +++ b/docs/src/account/components.md @@ -98,7 +98,7 @@ In TOML, these are declared using dotted array keys: **Value-slot** entries describe their schema via `WordSchema`. A value type can be either: -- **Simple**: defined through the `type = ""` field, indicating the expected `SchemaType` for the entire word. The value is supplied at instantiation time via `InitStorageData`. Felt types are stored as full words in the following layout: `[0, 0, 0, ]`. +- **Simple**: defined through the `type = ""` field, indicating the expected `SchemaType` for the entire word. The value is supplied at instantiation time via `InitStorageData`. Felt types are stored as full words in the following layout: `[, 0, 0, 0]`. - **Composite**: provided through `type = [ ... ]`, which contains exactly four `FeltSchema` descriptors. Each element is either a named typed field (optionally with `default-value`) or a `void` element for reserved/padding zeros. Composite schema entries reuse the existing TOML structure for four-element words, while simple schemas rely on `type`. In our example, the `token_metadata` slot uses a composite schema (`type = [...]`) mixing typed fields (`max_supply`, `decimals`) with defaults (`symbol`) and a reserved/padding `void` element.