-
Notifications
You must be signed in to change notification settings - Fork 106
feat(AggLayer): Solidity compatibility tests for MMR frontier code #2312
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: andrew-keccak-based-mmr-frontier
Are you sure you want to change the base?
Changes from all commits
d370ba6
3b1ff17
a551790
9ebf857
3f08e25
b4d80e7
32783f2
46f912e
db45701
d105a95
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| [submodule "crates/miden-agglayer/solidity-compat/lib/forge-std"] | ||
| path = crates/miden-agglayer/solidity-compat/lib/forge-std | ||
| url = https://github.com/foundry-rs/forge-std | ||
| [submodule "crates/miden-agglayer/solidity-compat/lib/agglayer-contracts"] | ||
| path = crates/miden-agglayer/solidity-compat/lib/agglayer-contracts | ||
| url = https://github.com/agglayer/agglayer-contracts |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,5 @@ | ||
| exclude = ["crates/miden-agglayer/solidity-compat/lib/*"] | ||
|
|
||
| [formatting] | ||
| align_entries = true | ||
| column_width = 120 | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,5 @@ | ||
| [default] | ||
| extend-ignore-identifiers-re = [".*1st.*", ".*2nd.*", ".*3rd.*"] | ||
|
|
||
| [files] | ||
| extend-exclude = ["crates/miden-agglayer/solidity-compat/lib"] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| # Foundry artifacts | ||
| /out/ | ||
| /cache/ | ||
|
|
||
| # Foundry broadcast files | ||
| /broadcast/ | ||
|
|
||
| # Environment | ||
| .env |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| # Solidity Compatibility Tests | ||
|
|
||
| This directory contains Foundry tests for generating test vectors to verify | ||
| that the Miden MMR Frontier implementation is compatible with the Solidity | ||
| `DepositContractBase.sol` from [agglayer-contracts v2](https://github.com/agglayer/agglayer-contracts). | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| Install [Foundry](https://book.getfoundry.sh/getting-started/installation): | ||
|
|
||
| ```bash | ||
| curl -L https://foundry.paradigm.xyz | bash | ||
| foundryup | ||
| ``` | ||
|
|
||
| ## Generating Test Vectors | ||
|
|
||
| From the repository root, you can regenerate both canonical zeros and MMR frontier test vectors with: | ||
|
|
||
| ```bash | ||
| make generate-solidity-test-vectors | ||
| ``` | ||
|
|
||
| Or from this directory: | ||
|
|
||
| ```bash | ||
| # Install dependencies (first time only) | ||
| forge install | ||
|
|
||
| # Generate canonical zeros (test-vectors/canonical_zeros.json) | ||
| forge test -vv --match-test test_generateCanonicalZeros | ||
|
|
||
| # Generate MMR frontier vectors (test-vectors/mmr_frontier_vectors.json) | ||
| forge test -vv --match-test test_generateVectors | ||
| ``` | ||
|
|
||
| ## Generated Files | ||
|
|
||
| - `test-vectors/canonical_zeros.json` - Canonical zeros for each tree height (ZERO_n = keccak256(ZERO_{n-1} || ZERO_{n-1})) | ||
| - `test-vectors/mmr_frontier_vectors.json` - Leaf-root pairs after adding leaves 0..31 | ||
|
|
||
| ### Canonical Zeros | ||
|
|
||
| The canonical zeros should match the constants in: | ||
| `crates/miden-agglayer/asm/bridge/canonical_zeros.masm` | ||
|
|
||
| ### MMR Frontier Vectors | ||
|
|
||
| The `test_generateVectors` adds leaves `0, 1, 2, ...` (as left-padded 32-byte values) | ||
| and outputs the root after each addition. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| { | ||
| "lib/agglayer-contracts": { | ||
| "rev": "e468f9b0967334403069aa650d9f1164b1731ebb" | ||
| }, | ||
| "lib/forge-std": { | ||
| "tag": { | ||
| "name": "v1.14.0", | ||
| "rev": "1801b0541f4fda118a10798fd3486bb7051c5dd6" | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| [profile.default] | ||
| libs = ["lib"] | ||
| out = "out" | ||
| solc = "0.8.20" | ||
| src = "src" | ||
|
|
||
| remappings = ["@agglayer/=lib/agglayer-contracts/contracts/"] | ||
|
|
||
| # Emit extra output for test vector generation | ||
| ffi = false | ||
| verbosity = 2 | ||
|
|
||
| # Allow writing test vectors to file | ||
| fs_permissions = [{ access = "read-write", path = "test-vectors" }] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| { | ||
| "canonical_zeros": [ | ||
| "0x0000000000000000000000000000000000000000000000000000000000000000", | ||
| "0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5", | ||
| "0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30", | ||
| "0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85", | ||
| "0xe58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a19344", | ||
| "0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d", | ||
| "0x887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968", | ||
| "0xffd70157e48063fc33c97a050f7f640233bf646cc98d9524c6b92bcf3ab56f83", | ||
| "0x9867cc5f7f196b93bae1e27e6320742445d290f2263827498b54fec539f756af", | ||
| "0xcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0", | ||
| "0xf9dc3e7fe016e050eff260334f18a5d4fe391d82092319f5964f2e2eb7c1c3a5", | ||
| "0xf8b13a49e282f609c317a833fb8d976d11517c571d1221a265d25af778ecf892", | ||
| "0x3490c6ceeb450aecdc82e28293031d10c7d73bf85e57bf041a97360aa2c5d99c", | ||
| "0xc1df82d9c4b87413eae2ef048f94b4d3554cea73d92b0f7af96e0271c691e2bb", | ||
| "0x5c67add7c6caf302256adedf7ab114da0acfe870d449a3a489f781d659e8becc", | ||
| "0xda7bce9f4e8618b6bd2f4132ce798cdc7a60e7e1460a7299e3c6342a579626d2", | ||
| "0x2733e50f526ec2fa19a22b31e8ed50f23cd1fdf94c9154ed3a7609a2f1ff981f", | ||
| "0xe1d3b5c807b281e4683cc6d6315cf95b9ade8641defcb32372f1c126e398ef7a", | ||
| "0x5a2dce0a8a7f68bb74560f8f71837c2c2ebbcbf7fffb42ae1896f13f7c7479a0", | ||
| "0xb46a28b6f55540f89444f63de0378e3d121be09e06cc9ded1c20e65876d36aa0", | ||
| "0xc65e9645644786b620e2dd2ad648ddfcbf4a7e5b1a3a4ecfe7f64667a3f0b7e2", | ||
| "0xf4418588ed35a2458cffeb39b93d26f18d2ab13bdce6aee58e7b99359ec2dfd9", | ||
| "0x5a9c16dc00d6ef18b7933a6f8dc65ccb55667138776f7dea101070dc8796e377", | ||
| "0x4df84f40ae0c8229d0d6069e5c8f39a7c299677a09d367fc7b05e3bc380ee652", | ||
| "0xcdc72595f74c7b1043d0e1ffbab734648c838dfb0527d971b602bc216c9619ef", | ||
| "0x0abf5ac974a1ed57f4050aa510dd9c74f508277b39d7973bb2dfccc5eeb0618d", | ||
| "0xb8cd74046ff337f0a7bf2c8e03e10f642c1886798d71806ab1e888d9e5ee87d0", | ||
| "0x838c5655cb21c6cb83313b5a631175dff4963772cce9108188b34ac87c81c41e", | ||
| "0x662ee4dd2dd7b2bc707961b1e646c4047669dcb6584f0d8d770daf5d7e7deb2e", | ||
| "0x388ab20e2573d171a88108e79d820e98f26c0b84aa8b2f4aa4968dbb818ea322", | ||
| "0x93237c50ba75ee485f4c22adf2f741400bdf8d6a9cc7df7ecae576221665d735", | ||
| "0x8448818bb4ae4562849e949e17ac16e0be16688e156b5cf15e098c627c0056a9" | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| { | ||
| "vectors": [ | ||
| {"leaf": "0x0000000000000000000000000000000000000000000000000000000000000000", "root": "0x27ae5ba08d7291c96c8cbddcc148bf48a6d68c7974b94356f53754ef6171d757", "count": 1}, | ||
| {"leaf": "0x0000000000000000000000000000000000000000000000000000000000000001", "root": "0x4a90a2c108a29b7755a0a915b9bb950233ce71f8a01859350d7b73cc56f57a62", "count": 2}, | ||
| {"leaf": "0x0000000000000000000000000000000000000000000000000000000000000002", "root": "0x2757cc260a62cc7c7708c387ea99f2a6bb5f034ed00da845734bec4d3fa3abfe", "count": 3}, | ||
| {"leaf": "0x0000000000000000000000000000000000000000000000000000000000000003", "root": "0xcb305ccda4331eb3fd9e17b81a5a0b336fb37a33f927698e9fb0604e534c6a01", "count": 4}, | ||
| {"leaf": "0x0000000000000000000000000000000000000000000000000000000000000004", "root": "0xa377a6262d3bae7be0ce09c2cc9f767b0f31848c268a4bdc12b63a451bb97281", "count": 5}, | ||
| {"leaf": "0x0000000000000000000000000000000000000000000000000000000000000005", "root": "0x440213f4dff167e3f5c655fbb6a3327af3512affed50ce3c1a3f139458a8a6d1", "count": 6}, | ||
| {"leaf": "0x0000000000000000000000000000000000000000000000000000000000000006", "root": "0xdd716d2905f2881005341ff1046ced5ee15cc63139716f56ed6be1d075c3f4a7", "count": 7}, | ||
| {"leaf": "0x0000000000000000000000000000000000000000000000000000000000000007", "root": "0xd6ebf96fcc3344fa755057b148162f95a93491bc6e8be756d06ec64df4df90fc", "count": 8}, | ||
| {"leaf": "0x0000000000000000000000000000000000000000000000000000000000000008", "root": "0x8b3bf2c95f3d0f941c109adfc3b652fadfeaf6f34be52524360a001cb151b5c9", "count": 9}, | ||
| {"leaf": "0x0000000000000000000000000000000000000000000000000000000000000009", "root": "0x74a5712654eccd015c44aca31817fd8bee8da400ada986a78384ef3594f2d459", "count": 10}, | ||
| {"leaf": "0x000000000000000000000000000000000000000000000000000000000000000a", "root": "0x95dd1209b92cce04311dfc8670b03428408c4ff62beb389e71847971f73702fa", "count": 11}, | ||
| {"leaf": "0x000000000000000000000000000000000000000000000000000000000000000b", "root": "0x0a83f3b2a75e19b7255b1de379ea9a71aef9716a3aef20a86abe625f088bbebf", "count": 12}, | ||
| {"leaf": "0x000000000000000000000000000000000000000000000000000000000000000c", "root": "0x601ba73b45858be76c8d02799fd70a5e1713e04031aa3be6746f95a17c343173", "count": 13}, | ||
| {"leaf": "0x000000000000000000000000000000000000000000000000000000000000000d", "root": "0x93d741c47aa73e36d3c7697758843d6af02b10ed38785f367d1602c8638adb0d", "count": 14}, | ||
| {"leaf": "0x000000000000000000000000000000000000000000000000000000000000000e", "root": "0x578f0d0a9b8ed5a4f86181b7e479da7ad72576ba7d3f36a1b72516aa0900c8ac", "count": 15}, | ||
| {"leaf": "0x000000000000000000000000000000000000000000000000000000000000000f", "root": "0x995c30e6b58c6e00e06faf4b5c94a21eb820b9db7ad30703f8e3370c2af10c11", "count": 16}, | ||
| {"leaf": "0x0000000000000000000000000000000000000000000000000000000000000010", "root": "0x49fb7257be1e954c377dc2557f5ca3f6fc7002d213f2772ab6899000e465236c", "count": 17}, | ||
| {"leaf": "0x0000000000000000000000000000000000000000000000000000000000000011", "root": "0x06fee72550896c50e28b894c60a3132bfe670e5c7a77ab4bb6a8ffb4abcf9446", "count": 18}, | ||
| {"leaf": "0x0000000000000000000000000000000000000000000000000000000000000012", "root": "0xbba3a807e79d33c6506cd5ecb5d50417360f8be58139f6dbe2f02c92e4d82491", "count": 19}, | ||
| {"leaf": "0x0000000000000000000000000000000000000000000000000000000000000013", "root": "0x1243fbd4d21287dbdaa542fa18a6a172b60d1af2c517b242914bdf8d82a98293", "count": 20}, | ||
| {"leaf": "0x0000000000000000000000000000000000000000000000000000000000000014", "root": "0x02b7b57e407fbccb506ed3199922d6d9bd0f703a1919d388c76867399ed44286", "count": 21}, | ||
| {"leaf": "0x0000000000000000000000000000000000000000000000000000000000000015", "root": "0xa15e7890d8f860a2ef391f9f58602dec7027c19e8f380980f140bbb92a3e00ba", "count": 22}, | ||
| {"leaf": "0x0000000000000000000000000000000000000000000000000000000000000016", "root": "0x2cb7eff4deb9bf6bbb906792bc152f1e63759b30e7829bfb5f3257ee600303f5", "count": 23}, | ||
| {"leaf": "0x0000000000000000000000000000000000000000000000000000000000000017", "root": "0xb1b034b4784411dc6858a0da771acef31be60216be0520a7950d29f66aee1fc5", "count": 24}, | ||
| {"leaf": "0x0000000000000000000000000000000000000000000000000000000000000018", "root": "0x3b17098f521ca0719e144a12bb79fdc51a3bc70385b5c2ee46b5762aae741f4f", "count": 25}, | ||
| {"leaf": "0x0000000000000000000000000000000000000000000000000000000000000019", "root": "0xd3e054489aa750d41938143011666a83e5e6b1477cce5ad612447059c2d8b939", "count": 26}, | ||
| {"leaf": "0x000000000000000000000000000000000000000000000000000000000000001a", "root": "0x6d15443ab2f39cce7fbe131843cdad6f27400eb179efb866569dd48baaf3ed4d", "count": 27}, | ||
| {"leaf": "0x000000000000000000000000000000000000000000000000000000000000001b", "root": "0xf9386ef40320c369185e48132f8fbf2f3e78d9598495dd342bcf4f41388d460d", "count": 28}, | ||
| {"leaf": "0x000000000000000000000000000000000000000000000000000000000000001c", "root": "0xb618ebe1f7675ef246a8cbb93519469076d5caacd4656330801537933e27b172", "count": 29}, | ||
| {"leaf": "0x000000000000000000000000000000000000000000000000000000000000001d", "root": "0x6c8c90b5aa967c98061a2dd09ea74dfb61fd9e86e308f14453e9e0ae991116de", "count": 30}, | ||
| {"leaf": "0x000000000000000000000000000000000000000000000000000000000000001e", "root": "0x06f51cfc733d71220d6e5b70a6b33a8d47a1ab55ac045fac75f26c762d7b29c9", "count": 31}, | ||
| {"leaf": "0x000000000000000000000000000000000000000000000000000000000000001f", "root": "0x82d1ddf8c6d986dee7fc6fa2d7120592d1dc5026b1bb349fcc9d5c73ac026f56", "count": 32} | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.20; | ||
|
|
||
| import "forge-std/Test.sol"; | ||
| import "@agglayer/v2/lib/DepositContractBase.sol"; | ||
|
|
||
| /** | ||
| * @title MMRTestVectors | ||
| * @notice Test contract that generates test vectors for verifying compatibility | ||
| * between Solidity's DepositContractBase and Miden's MMR Frontier implementation. | ||
| * | ||
| * Run with: forge test -vv --match-contract MMRTestVectors | ||
| * | ||
| * The output can be compared against the Rust KeccakMmrFrontier32 implementation | ||
| * in crates/miden-testing/tests/agglayer/mmr_frontier.rs | ||
| */ | ||
| contract MMRTestVectors is Test, DepositContractBase { | ||
|
|
||
| /** | ||
| * @notice Generates the canonical zeros and saves to JSON file. | ||
| * ZERO_0 = 0x0...0 (32 zero bytes) | ||
| * ZERO_n = keccak256(ZERO_{n-1} || ZERO_{n-1}) | ||
| * | ||
| * Output file: test-vectors/canonical_zeros.json | ||
| */ | ||
| function test_generateCanonicalZeros() public { | ||
| string memory json = "{\n"; | ||
| json = string.concat(json, ' "canonical_zeros": [\n'); | ||
|
|
||
| bytes32 zero = bytes32(0); | ||
| for (uint256 height = 0; height < 32; height++) { | ||
| if (height < 31) { | ||
| json = string.concat(json, ' "', vm.toString(zero), '",\n'); | ||
| } else { | ||
| json = string.concat(json, ' "', vm.toString(zero), '"\n'); | ||
| } | ||
| zero = keccak256(abi.encodePacked(zero, zero)); | ||
| } | ||
| json = string.concat(json, " ]\n}"); | ||
|
Comment on lines
+31
to
+39
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. writing a JSON in Solidity, nice :) I've done this before, but I this is not ideal, we can use a foundry "cheatcode" instead. I opened a PR on this branch to show a simpler way to do this, so we don't have to do string manipulation in solidity. Opened a PR here: #2314 |
||
|
|
||
| // Print to console | ||
| console.log(json); | ||
|
Comment on lines
+41
to
+42
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need this |
||
|
|
||
| // Save to file | ||
| string memory outputPath = "test-vectors/canonical_zeros.json"; | ||
| vm.writeFile(outputPath, json); | ||
| console.log("\nSaved to:", outputPath); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Generates MMR frontier vectors (leaf-root pairs) and saves to JSON file. | ||
| * Output file: test-vectors/mmr_frontier_vectors.json | ||
| */ | ||
| function test_generateVectors() public { | ||
| string memory json = "{\n"; | ||
| json = string.concat(json, ' "vectors": [\n'); | ||
|
|
||
| for (uint256 i = 0; i < 32; i++) { | ||
| bytes32 leaf = bytes32(i); | ||
| _addLeaf(leaf); | ||
| bytes32 root = getRoot(); | ||
|
|
||
| // Build JSON object for this vector | ||
| string memory vectorJson = string.concat( | ||
| ' {"leaf": "', vm.toString(leaf), | ||
| '", "root": "', vm.toString(root), | ||
| '", "count": ', vm.toString(depositCount), "}" | ||
| ); | ||
|
|
||
| if (i < 31) { | ||
| json = string.concat(json, vectorJson, ",\n"); | ||
| } else { | ||
| json = string.concat(json, vectorJson, "\n"); | ||
| } | ||
| } | ||
|
|
||
| json = string.concat(json, " ]\n}"); | ||
|
|
||
| // Print to console | ||
| console.log(json); | ||
|
Comment on lines
+79
to
+80
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above comment |
||
|
|
||
| // Save to file | ||
| string memory outputPath = "test-vectors/mmr_frontier_vectors.json"; | ||
| vm.writeFile(outputPath, json); | ||
| console.log("\nSaved to:", outputPath); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -45,5 +45,7 @@ miden-crypto = { workspace = true } | |
| miden-protocol = { features = ["std"], workspace = true } | ||
| primitive-types = { workspace = true } | ||
| rstest = { workspace = true } | ||
| serde = { features = ["derive"], version = "1.0" } | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: to be changed once #2313 lands |
||
| serde_json = { version = "1.0" } | ||
| tokio = { features = ["macros", "rt"], workspace = true } | ||
| winter-rand-utils = { version = "0.13" } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: if we don't need the
console.log()we can remove theTest.solimport (i think, need to double check)