diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ab88b5679..c3bc32afc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ ### Changes +- [BREAKING] Split `fee::estimate_fee` out of `fee::pay_fee`, pricing the FEE_SPONSORSHIP notes via the new `fees::estimate_network_note_sponsorships` without creating them, `fee::apply_cycle_margins` now also takes the sponsorship-note and output-note counts, and `fee::estimate_fee` and the `fees` sponsorship walks take the address of a price table of one word per output note in the caller's local memory ([#3784](https://github.com/0xMiden/protocol/pull/3784)). - Added a check that the guardian public key is not one of the approver public keys ([#3764](https://github.com/0xMiden/protocol/pull/3764)). - [BREAKING] Incremented the MSRV to 1.98. - [BREAKING] Updated the Miden VM and crypto crate family to v0.30.0 and `midenc-hir-type` to v0.12.0. `LocalTransactionProver::new` now takes `miden_prover::Prover`, `CoreLibrary` exposes one merged package, and `TransactionVerifier::verify` now returns `VerificationOutcome` so callers can handle outstanding precompile work ([#3782](https://github.com/0xMiden/protocol/pull/3782)). diff --git a/bin/bench-transaction/bench-tx.json b/bin/bench-transaction/bench-tx.json index 00e3277bcf..ea12dfcaa1 100644 --- a/bin/bench-transaction/bench-tx.json +++ b/bin/bench-transaction/bench-tx.json @@ -1,1073 +1,1073 @@ { "consume single P2ID note with Falcon signing": { - "prologue": 4702, + "prologue": 5329, "notes_processing": 2173, "note_execution": { - "0xd3d5bda9d758f4cef5e25bfe4c1e4855fbc43854e3843029fc82418b27c46111": 2131 + "0xeab5ee2cb049d9ceb37b8e6001a3d7b51b45cf499cc4f251da28087e47238b6b": 2131 }, "tx_script_processing": 41, "epilogue": { - "total": 73750, - "auth_procedure": 72613 + "total": 73858, + "auth_procedure": 72721 }, "trace": { - "core_rows": 80710, - "chiplets_rows": 11555, - "poseidon2_permutation_rows": 54864, - "range_rows": 20649, + "core_rows": 81445, + "chiplets_rows": 11504, + "poseidon2_permutation_rows": 55392, + "range_rows": 20645, "chiplets_shape": { - "hasher_rows": 8496, - "bitwise_rows": 648, - "memory_rows": 2409, + "hasher_rows": 8656, + "bitwise_rows": 656, + "memory_rows": 2190, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume single P2ID note with ECDSA signing": { - "prologue": 4702, + "prologue": 5329, "notes_processing": 2173, "note_execution": { - "0x3d8e909872ba6287f8bcabd40df2f2f2e1cfb72187fce1095f7937950c749d02": 2131 + "0x650abad4e79b5ca928a1370ffd726632d2ce41de366e91517650a5cf237c28c4": 2131 }, "tx_script_processing": 41, "epilogue": { - "total": 6112, - "auth_procedure": 4975 + "total": 6095, + "auth_procedure": 4958 }, "trace": { - "core_rows": 13072, - "chiplets_rows": 5812, - "poseidon2_permutation_rows": 19760, - "range_rows": 1899, + "core_rows": 13682, + "chiplets_rows": 5872, + "poseidon2_permutation_rows": 19888, + "range_rows": 1969, "chiplets_shape": { - "hasher_rows": 4144, - "bitwise_rows": 904, - "memory_rows": 762, + "hasher_rows": 4224, + "bitwise_rows": 848, + "memory_rows": 798, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume two P2ID notes with Falcon signing": { - "prologue": 5561, + "prologue": 5646, "notes_processing": 4528, "note_execution": { - "0x1334ee47271cac281b9ac957515d6d035aeaa0b773c0915f7f0645e2f04ef2e9": 2346, - "0x3f404f40d40f4aa97d9b67011bf67cbfce33d051a37ac61ed01e06bdf57a8331": 2131 + "0x7810979ea9cba8903202d8593afe04f967963d53c5b9c1c43a926b8be0572e80": 2131, + "0x7b934ca16878579b7f254b3c2fa8d27047b2d4e27d676f0b1b594501e15ab157": 2346 }, "tx_script_processing": 41, "epilogue": { - "total": 73678, - "auth_procedure": 72577 + "total": 73786, + "auth_procedure": 72685 }, "trace": { - "core_rows": 83852, - "chiplets_rows": 13588, - "poseidon2_permutation_rows": 55472, - "range_rows": 20387, + "core_rows": 84045, + "chiplets_rows": 13403, + "poseidon2_permutation_rows": 55648, + "range_rows": 20515, "chiplets_shape": { - "hasher_rows": 10024, - "bitwise_rows": 1016, - "memory_rows": 2546, + "hasher_rows": 10072, + "bitwise_rows": 1032, + "memory_rows": 2297, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume two P2ID notes with ECDSA signing": { - "prologue": 5561, + "prologue": 5646, "notes_processing": 4528, "note_execution": { - "0x1334ee47271cac281b9ac957515d6d035aeaa0b773c0915f7f0645e2f04ef2e9": 2346, - "0x3f404f40d40f4aa97d9b67011bf67cbfce33d051a37ac61ed01e06bdf57a8331": 2131 + "0x7810979ea9cba8903202d8593afe04f967963d53c5b9c1c43a926b8be0572e80": 2131, + "0x7b934ca16878579b7f254b3c2fa8d27047b2d4e27d676f0b1b594501e15ab157": 2346 }, "tx_script_processing": 41, "epilogue": { - "total": 6040, - "auth_procedure": 4939 + "total": 6023, + "auth_procedure": 4922 }, "trace": { - "core_rows": 16214, - "chiplets_rows": 7845, - "poseidon2_permutation_rows": 20384, - "range_rows": 1487, + "core_rows": 16282, + "chiplets_rows": 7771, + "poseidon2_permutation_rows": 20144, + "range_rows": 1413, "chiplets_shape": { - "hasher_rows": 5672, - "bitwise_rows": 1272, - "memory_rows": 899, + "hasher_rows": 5640, + "bitwise_rows": 1224, + "memory_rows": 905, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "create single P2ID note with Falcon signing": { - "prologue": 2065, + "prologue": 2080, "notes_processing": 35, "note_execution": {}, "tx_script_processing": 1883, "epilogue": { - "total": 75283, - "auth_procedure": 73197 + "total": 75593, + "auth_procedure": 73507 }, "trace": { - "core_rows": 79310, - "chiplets_rows": 11049, - "poseidon2_permutation_rows": 53152, - "range_rows": 20411, + "core_rows": 79635, + "chiplets_rows": 10916, + "poseidon2_permutation_rows": 53280, + "range_rows": 20351, "chiplets_shape": { - "hasher_rows": 8144, - "bitwise_rows": 600, - "memory_rows": 2303, + "hasher_rows": 8240, + "bitwise_rows": 616, + "memory_rows": 2058, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "create single P2ID note with ECDSA signing": { - "prologue": 2065, + "prologue": 2080, "notes_processing": 35, "note_execution": {}, "tx_script_processing": 1883, "epilogue": { - "total": 7645, - "auth_procedure": 5559 + "total": 7830, + "auth_procedure": 5744 }, "trace": { - "core_rows": 11672, - "chiplets_rows": 5314, - "poseidon2_permutation_rows": 18048, - "range_rows": 1273, + "core_rows": 11872, + "chiplets_rows": 5284, + "poseidon2_permutation_rows": 17728, + "range_rows": 1237, "chiplets_shape": { - "hasher_rows": 3800, - "bitwise_rows": 856, - "memory_rows": 656, + "hasher_rows": 3808, + "bitwise_rows": 808, + "memory_rows": 666, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume CLAIM note (L1 to Miden)": { - "prologue": 5010, - "notes_processing": 28303, + "prologue": 5060, + "notes_processing": 28215, "note_execution": { - "0xb92e6fd5ec3cb3d21fd8c207f67b385e1b06393b0476aff1dd17ad9bad1b8fbc": 28261 + "0x49cdb9cd15380c349a8fc22400ed05d402962d4eda56e9e3a2671030b8d60125": 28173 }, "tx_script_processing": 41, "epilogue": { - "total": 16614, - "auth_procedure": 11345 + "total": 17843, + "auth_procedure": 12574 }, "trace": { - "core_rows": 50012, - "chiplets_rows": 20291, - "poseidon2_permutation_rows": 41392, - "range_rows": 3469, + "core_rows": 51203, + "chiplets_rows": 20591, + "poseidon2_permutation_rows": 42032, + "range_rows": 3561, "chiplets_shape": { - "hasher_rows": 13248, - "bitwise_rows": 2816, - "memory_rows": 4225, + "hasher_rows": 13432, + "bitwise_rows": 2848, + "memory_rows": 4309, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume CLAIM note (L2 to Miden)": { - "prologue": 5010, - "notes_processing": 38465, + "prologue": 5060, + "notes_processing": 38185, "note_execution": { - "0x5ab9bf3e5894314d51d1224cbe74b44dde6ac77220c3686ac17faef2a25169ba": 38423 + "0x8f113a459b0a87ac90f3c184d1ebfd81cb09076fadb64bff39609eaa14209ce9": 38143 }, "tx_script_processing": 41, "epilogue": { - "total": 16614, - "auth_procedure": 11345 + "total": 17843, + "auth_procedure": 12574 }, "trace": { - "core_rows": 60174, - "chiplets_rows": 23041, - "poseidon2_permutation_rows": 43536, - "range_rows": 3693, + "core_rows": 61173, + "chiplets_rows": 23149, + "poseidon2_permutation_rows": 44176, + "range_rows": 3747, "chiplets_shape": { - "hasher_rows": 14800, - "bitwise_rows": 3072, - "memory_rows": 5167, + "hasher_rows": 14792, + "bitwise_rows": 3104, + "memory_rows": 5251, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume B2AGG note (bridge-out)": { - "prologue": 5317, - "notes_processing": 119280, + "prologue": 5367, + "notes_processing": 120008, "note_execution": { - "0x8045f362a182de06bb18d6a3d86fa86e2c379c5eb9cf379cf943f4d54d0eedae": 119238 + "0x71b6eb992761503187babb3bf70dcab321dc00465ecb077ab1a1b2289eedf4f6": 119966 }, "tx_script_processing": 41, "epilogue": { - "total": 25157, - "auth_procedure": 10184 + "total": 26299, + "auth_procedure": 11326 }, "trace": { - "core_rows": 149839, - "chiplets_rows": 71275, - "poseidon2_permutation_rows": 114000, - "range_rows": 4873, + "core_rows": 151759, + "chiplets_rows": 71628, + "poseidon2_permutation_rows": 114640, + "range_rows": 5943, "chiplets_shape": { - "hasher_rows": 57240, - "bitwise_rows": 3632, - "memory_rows": 10401, + "hasher_rows": 57472, + "bitwise_rows": 3664, + "memory_rows": 10490, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume B2AGG note (bridge-out, 2^31 leaves)": { - "prologue": 5317, - "notes_processing": 117589, + "prologue": 5367, + "notes_processing": 118317, "note_execution": { - "0x8045f362a182de06bb18d6a3d86fa86e2c379c5eb9cf379cf943f4d54d0eedae": 117547 + "0x71b6eb992761503187babb3bf70dcab321dc00465ecb077ab1a1b2289eedf4f6": 118275 }, "tx_script_processing": 41, "epilogue": { - "total": 24869, - "auth_procedure": 10184 + "total": 26011, + "auth_procedure": 11326 }, "trace": { - "core_rows": 147860, - "chiplets_rows": 70296, - "poseidon2_permutation_rows": 115392, - "range_rows": 4933, + "core_rows": 149780, + "chiplets_rows": 70657, + "poseidon2_permutation_rows": 116032, + "range_rows": 5917, "chiplets_shape": { - "hasher_rows": 56384, - "bitwise_rows": 3632, - "memory_rows": 10278, + "hasher_rows": 56624, + "bitwise_rows": 3664, + "memory_rows": 10367, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume B2AGG note (bridge-out, 2^31-1 leaves)": { - "prologue": 5317, - "notes_processing": 63420, + "prologue": 5367, + "notes_processing": 64148, "note_execution": { - "0x8045f362a182de06bb18d6a3d86fa86e2c379c5eb9cf379cf943f4d54d0eedae": 63378 + "0x71b6eb992761503187babb3bf70dcab321dc00465ecb077ab1a1b2289eedf4f6": 64106 }, "tx_script_processing": 41, "epilogue": { - "total": 16229, - "auth_procedure": 10184 + "total": 17371, + "auth_procedure": 11326 }, "trace": { - "core_rows": 85051, - "chiplets_rows": 40438, - "poseidon2_permutation_rows": 51824, - "range_rows": 3789, + "core_rows": 86971, + "chiplets_rows": 40799, + "poseidon2_permutation_rows": 52464, + "range_rows": 4913, "chiplets_shape": { - "hasher_rows": 30216, - "bitwise_rows": 3632, - "memory_rows": 6588, + "hasher_rows": 30456, + "bitwise_rows": 3664, + "memory_rows": 6677, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume P2ID note (network account)": { - "prologue": 4079, + "prologue": 4129, "notes_processing": 2173, "note_execution": { - "0x9c9eb8d97ddf61a38a3fdd6624d6dfe5db7ad6622862f0af7c912304e57b5a8a": 2131 + "0xa0711df1e3c6477a0edb404719c5b1215bcb15485541364be8e55d1ae6d3658d": 2131 }, "tx_script_processing": 41, "epilogue": { - "total": 12327, - "auth_procedure": 9064 + "total": 13049, + "auth_procedure": 9786 }, "trace": { - "core_rows": 18664, - "chiplets_rows": 8512, - "poseidon2_permutation_rows": 26080, - "range_rows": 1491, + "core_rows": 19436, + "chiplets_rows": 8797, + "poseidon2_permutation_rows": 26528, + "range_rows": 1643, "chiplets_shape": { - "hasher_rows": 6488, + "hasher_rows": 6728, "bitwise_rows": 920, - "memory_rows": 1102, + "memory_rows": 1147, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume P2ID note (16 assets, network account)": { - "prologue": 12524, + "prologue": 12574, "notes_processing": 29817, "note_execution": { - "0xcc4ef28945fffb42a774a2b037552c8fa5064b3f345ff8f6678ff8e4f64b4a97": 29775 + "0x4f3b199ab4df30bf543e1b4bc9db654b9f2fb552186c4122ceed28c6dfde8693": 29775 }, "tx_script_processing": 41, "epilogue": { - "total": 14806, - "auth_procedure": 9383 + "total": 15528, + "auth_procedure": 10105 }, "trace": { - "core_rows": 57232, - "chiplets_rows": 32968, - "poseidon2_permutation_rows": 42576, - "range_rows": 3571, + "core_rows": 58004, + "chiplets_rows": 33253, + "poseidon2_permutation_rows": 43024, + "range_rows": 3961, "chiplets_shape": { - "hasher_rows": 24944, + "hasher_rows": 25184, "bitwise_rows": 5120, - "memory_rows": 2902, + "memory_rows": 2947, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume P2IDE note (claim, network account)": { - "prologue": 4079, + "prologue": 4129, "notes_processing": 2281, "note_execution": { - "0xc8f84ad6166b2a1d26ed42023d715af78c2c04fcdd8b4713f87df85ba4fe580d": 2239 + "0x36fb7979b522f0f940eb1c56a26dea673042bb1305ed18114e1256985312d126": 2239 }, "tx_script_processing": 41, "epilogue": { - "total": 12327, - "auth_procedure": 9064 + "total": 13049, + "auth_procedure": 9786 }, "trace": { - "core_rows": 18772, - "chiplets_rows": 8548, - "poseidon2_permutation_rows": 26176, - "range_rows": 1509, + "core_rows": 19544, + "chiplets_rows": 8825, + "poseidon2_permutation_rows": 26624, + "range_rows": 1591, "chiplets_shape": { - "hasher_rows": 6520, + "hasher_rows": 6752, "bitwise_rows": 920, - "memory_rows": 1106, + "memory_rows": 1151, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume P2IDE note (claim, 16 assets, network account)": { - "prologue": 12524, + "prologue": 12574, "notes_processing": 29925, "note_execution": { - "0x796094cec656ed0e408b604733714f240f7b214079522c7e2fabc2fa72a9f635": 29883 + "0xbdb2ac27ff4e464791d0938f04f518681819ba3928a38f1cd72d994c43038f8d": 29883 }, "tx_script_processing": 41, "epilogue": { - "total": 14806, - "auth_procedure": 9383 + "total": 15528, + "auth_procedure": 10105 }, "trace": { - "core_rows": 57340, - "chiplets_rows": 33004, - "poseidon2_permutation_rows": 42672, - "range_rows": 3511, + "core_rows": 58112, + "chiplets_rows": 33281, + "poseidon2_permutation_rows": 43120, + "range_rows": 3979, "chiplets_shape": { - "hasher_rows": 24976, + "hasher_rows": 25208, "bitwise_rows": 5120, - "memory_rows": 2906, + "memory_rows": 2951, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume P2IDE note (reclaim, network account)": { - "prologue": 4182, + "prologue": 4232, "notes_processing": 2333, "note_execution": { - "0x4ef3f702e0697a657064ceddbd98308cc21bdc41ebb88383af34dbb1fe914097": 2291 + "0x3cd1c6d1cbff7db055d275341abcc500a42fd05d0adcee88d3653eaf10da3c62": 2291 }, "tx_script_processing": 41, "epilogue": { - "total": 12327, - "auth_procedure": 9064 + "total": 13049, + "auth_procedure": 9786 }, "trace": { - "core_rows": 18927, - "chiplets_rows": 8579, - "poseidon2_permutation_rows": 26368, - "range_rows": 1465, + "core_rows": 19699, + "chiplets_rows": 8856, + "poseidon2_permutation_rows": 26816, + "range_rows": 1601, "chiplets_shape": { - "hasher_rows": 6544, + "hasher_rows": 6776, "bitwise_rows": 920, - "memory_rows": 1113, + "memory_rows": 1158, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume SWAP note (public payback, network account)": { - "prologue": 4088, + "prologue": 4138, "notes_processing": 4537, "note_execution": { - "0xb3fdf5a2f247e8413f633bdafb05e1bdeb1171cb38b5e044b6c3a0de8303b468": 4495 + "0xe032d1b4e4b5b59927ca83c90a2d02389a3e4f0f14e4f9c5f4669b98acac5bfa": 4495 }, "tx_script_processing": 41, "epilogue": { - "total": 13989, - "auth_procedure": 9585 + "total": 14913, + "auth_procedure": 10509 }, "trace": { - "core_rows": 22699, - "chiplets_rows": 10586, - "poseidon2_permutation_rows": 28208, - "range_rows": 1779, + "core_rows": 23673, + "chiplets_rows": 10947, + "poseidon2_permutation_rows": 28656, + "range_rows": 1913, "chiplets_shape": { - "hasher_rows": 8072, - "bitwise_rows": 1256, - "memory_rows": 1256, + "hasher_rows": 8352, + "bitwise_rows": 1288, + "memory_rows": 1305, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume SWAP note (private payback, network account)": { - "prologue": 4088, + "prologue": 4138, "notes_processing": 4034, "note_execution": { - "0x1e47ad29927705f58e53bcec4c691a22d5f09909a28b3508a425a06a09d44d00": 3992 + "0xf81b87014953b8bfa7880c4ca404982c6acde8f1ab33241013a3eaacd2f7a015": 3992 }, "tx_script_processing": 41, "epilogue": { - "total": 13989, - "auth_procedure": 9585 + "total": 14913, + "auth_procedure": 10509 }, "trace": { - "core_rows": 22196, - "chiplets_rows": 10457, - "poseidon2_permutation_rows": 27888, - "range_rows": 1761, + "core_rows": 23170, + "chiplets_rows": 10826, + "poseidon2_permutation_rows": 28336, + "range_rows": 1897, "chiplets_shape": { - "hasher_rows": 7960, - "bitwise_rows": 1256, - "memory_rows": 1239, + "hasher_rows": 8248, + "bitwise_rows": 1288, + "memory_rows": 1288, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume PSWAP note (full fill, network account)": { - "prologue": 4079, - "notes_processing": 7336, + "prologue": 4129, + "notes_processing": 7352, "note_execution": { - "0x4f0fa61b2270c6cd3a193ed86d652c51335c0ab8271fb32aa2812ff540bd57cb": 7294 + "0x0b120c8f0057de9c9e75f96fcd8a6b534e97ffea43516a8cbfd696a6c5e91420": 7310 }, "tx_script_processing": 41, "epilogue": { - "total": 14029, - "auth_procedure": 9585 + "total": 14953, + "auth_procedure": 10509 }, "trace": { - "core_rows": 25529, - "chiplets_rows": 11423, - "poseidon2_permutation_rows": 30752, - "range_rows": 1861, + "core_rows": 26519, + "chiplets_rows": 11792, + "poseidon2_permutation_rows": 31184, + "range_rows": 2021, "chiplets_shape": { - "hasher_rows": 8592, - "bitwise_rows": 1384, - "memory_rows": 1445, + "hasher_rows": 8880, + "bitwise_rows": 1416, + "memory_rows": 1494, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume PSWAP note (partial fill, network account)": { - "prologue": 4079, - "notes_processing": 9836, + "prologue": 4129, + "notes_processing": 9868, "note_execution": { - "0x4f0fa61b2270c6cd3a193ed86d652c51335c0ab8271fb32aa2812ff540bd57cb": 9794 + "0x0b120c8f0057de9c9e75f96fcd8a6b534e97ffea43516a8cbfd696a6c5e91420": 9826 }, "tx_script_processing": 41, "epilogue": { - "total": 15446, - "auth_procedure": 9789 + "total": 16574, + "auth_procedure": 10917 }, "trace": { - "core_rows": 29446, - "chiplets_rows": 13195, - "poseidon2_permutation_rows": 32544, - "range_rows": 2011, + "core_rows": 30656, + "chiplets_rows": 13648, + "poseidon2_permutation_rows": 32976, + "range_rows": 2113, "chiplets_shape": { - "hasher_rows": 9808, - "bitwise_rows": 1776, - "memory_rows": 1609, + "hasher_rows": 10144, + "bitwise_rows": 1840, + "memory_rows": 1662, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume MINT note (fungible faucet, network account)": { - "prologue": 5803, - "notes_processing": 9183, + "prologue": 5853, + "notes_processing": 9406, "note_execution": { - "0xf89434d6652c33467586d94cb98ccae242e368d65778ac0c60212828a1eab15b": 9141 + "0xd0282fd8dff237bba5e5fe178497434fb02dfd64e9cb5e55427c848e998e22bc": 9364 }, "tx_script_processing": 41, "epilogue": { - "total": 19060, - "auth_procedure": 9340 + "total": 19984, + "auth_procedure": 10264 }, "trace": { - "core_rows": 34131, - "chiplets_rows": 13485, - "poseidon2_permutation_rows": 28704, - "range_rows": 2905, + "core_rows": 35328, + "chiplets_rows": 13881, + "poseidon2_permutation_rows": 29120, + "range_rows": 3001, "chiplets_shape": { - "hasher_rows": 10112, - "bitwise_rows": 1256, - "memory_rows": 2115, + "hasher_rows": 10424, + "bitwise_rows": 1288, + "memory_rows": 2167, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume MINT note (non-fungible faucet, network account)": { - "prologue": 5852, - "notes_processing": 12388, + "prologue": 5902, + "notes_processing": 12451, "note_execution": { - "0x5c71596a4bd644327b60fcdcd90d7bb4c7db41ecf64308169244af5f8ba5e11d": 12346 + "0x9d1c38fffad4aaf2042aca75bdd164f3571d6f391e39eb3325a408ed659ebe02": 12409 }, "tx_script_processing": 41, "epilogue": { - "total": 19330, - "auth_procedure": 9349 + "total": 20254, + "auth_procedure": 10273 }, "trace": { - "core_rows": 37655, - "chiplets_rows": 14662, - "poseidon2_permutation_rows": 30544, - "range_rows": 2963, + "core_rows": 38692, + "chiplets_rows": 15031, + "poseidon2_permutation_rows": 30960, + "range_rows": 3145, "chiplets_shape": { - "hasher_rows": 11248, - "bitwise_rows": 1160, - "memory_rows": 2252, + "hasher_rows": 11536, + "bitwise_rows": 1192, + "memory_rows": 2301, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume BURN note (network account)": { - "prologue": 6406, - "notes_processing": 4527, + "prologue": 6456, + "notes_processing": 4613, "note_execution": { - "0x3f1a82301028b2d9c24d939c0644cfba10d0036e9b6374b9e9dca3f9eaeecd6c": 4485 + "0xf23c5dbf34e0f5b7a07bb55740fee27cad486d762243d9db32bf7424e2136807": 4571 }, "tx_script_processing": 41, "epilogue": { - "total": 17799, - "auth_procedure": 9138 + "total": 18521, + "auth_procedure": 9860 }, "trace": { - "core_rows": 28817, - "chiplets_rows": 11785, - "poseidon2_permutation_rows": 27856, - "range_rows": 2621, + "core_rows": 29675, + "chiplets_rows": 12079, + "poseidon2_permutation_rows": 28288, + "range_rows": 2813, "chiplets_shape": { - "hasher_rows": 8920, + "hasher_rows": 9168, "bitwise_rows": 976, - "memory_rows": 1887, + "memory_rows": 1933, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume FAUCET_POLICY_CONFIG note (network account)": { - "prologue": 5738, - "notes_processing": 3592, + "prologue": 5788, + "notes_processing": 3762, "note_execution": { - "0x1f2ce020e34fbcc888d18a7dd727829d1f1795fbb5c90927db0c51cf26bcab0b": 3550 + "0xe01a79fc018f08426261e4a923b8f1f6c286ecd8e498128625f2a426da6f8820": 3720 }, "tx_script_processing": 41, "epilogue": { - "total": 17646, - "auth_procedure": 9129 + "total": 18368, + "auth_procedure": 9851 }, "trace": { - "core_rows": 27061, - "chiplets_rows": 10379, - "poseidon2_permutation_rows": 26480, - "range_rows": 2487, + "core_rows": 28003, + "chiplets_rows": 10717, + "poseidon2_permutation_rows": 26960, + "range_rows": 2619, "chiplets_shape": { - "hasher_rows": 7976, - "bitwise_rows": 640, - "memory_rows": 1761, + "hasher_rows": 8256, + "bitwise_rows": 648, + "memory_rows": 1811, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume FAUCET_METADATA_CONFIG note (network account)": { - "prologue": 5189, - "notes_processing": 6023, + "prologue": 5239, + "notes_processing": 6254, "note_execution": { - "0xc5d07af771df01abcd0fb395b32d26f7ba9205f09d4360ecca7876101017ecf4": 5981 + "0x8b054ac6467e35c53f3ffbf90e06ec27f8195b0129ba30679e1f012d7e3c0896": 6212 }, "tx_script_processing": 41, "epilogue": { - "total": 16235, - "auth_procedure": 9048 + "total": 16957, + "auth_procedure": 9770 }, "trace": { - "core_rows": 27532, - "chiplets_rows": 10472, - "poseidon2_permutation_rows": 25776, - "range_rows": 2395, + "core_rows": 28535, + "chiplets_rows": 10818, + "poseidon2_permutation_rows": 26256, + "range_rows": 2497, "chiplets_shape": { - "hasher_rows": 7992, - "bitwise_rows": 648, - "memory_rows": 1830, + "hasher_rows": 8280, + "bitwise_rows": 656, + "memory_rows": 1880, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume MIN_BURN_AMOUNT_CONFIG note (network account)": { - "prologue": 5795, - "notes_processing": 2418, + "prologue": 5845, + "notes_processing": 2581, "note_execution": { - "0x7aeb862ad5ae354ca222f42cb76f60de52b8cfc78a8c540bce55b8b6a2dc6e81": 2376 + "0x6aee9cc83c5ba20b828cd1a3f63cee7c57dc3231d98d3a7b41aab1f2fd570463": 2539 }, "tx_script_processing": 41, "epilogue": { - "total": 17799, - "auth_procedure": 9138 + "total": 18521, + "auth_procedure": 9860 }, "trace": { - "core_rows": 26097, - "chiplets_rows": 9989, - "poseidon2_permutation_rows": 25072, - "range_rows": 2469, + "core_rows": 27032, + "chiplets_rows": 10327, + "poseidon2_permutation_rows": 25552, + "range_rows": 2591, "chiplets_shape": { - "hasher_rows": 7632, - "bitwise_rows": 640, - "memory_rows": 1715, + "hasher_rows": 7912, + "bitwise_rows": 648, + "memory_rows": 1765, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume ALLOWLIST_CONFIG note (network account)": { - "prologue": 5795, - "notes_processing": 2985, + "prologue": 5845, + "notes_processing": 3148, "note_execution": { - "0xc27f85d59a55783f8405b4aaa258e2bbc0789f9b5d600a9fc6b0b648fbb08854": 2943 + "0xfa26c9aa1c21b5a79ea7c0726f17c1c0d64aa18f24ae4fa1e1a252f3dc2e0422": 3106 }, "tx_script_processing": 41, "epilogue": { - "total": 17978, - "auth_procedure": 9138 + "total": 18700, + "auth_procedure": 9860 }, "trace": { - "core_rows": 26843, - "chiplets_rows": 10615, - "poseidon2_permutation_rows": 27616, - "range_rows": 2477, + "core_rows": 27778, + "chiplets_rows": 10953, + "poseidon2_permutation_rows": 28096, + "range_rows": 2589, "chiplets_shape": { - "hasher_rows": 8184, - "bitwise_rows": 640, - "memory_rows": 1789, + "hasher_rows": 8464, + "bitwise_rows": 648, + "memory_rows": 1839, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume BLOCKLIST_CONFIG note (network account)": { - "prologue": 5795, - "notes_processing": 2985, + "prologue": 5845, + "notes_processing": 3148, "note_execution": { - "0x57e359b416c6054eaaee8ec6d101dfa7ccd1e8897014e6004cd8b87e1b82ba77": 2943 + "0xd0fe81015108191518f03d38bcd5d2b65ffc6263143bba5b032717104fe0f840": 3106 }, "tx_script_processing": 41, "epilogue": { - "total": 17978, - "auth_procedure": 9138 + "total": 18700, + "auth_procedure": 9860 }, "trace": { - "core_rows": 26843, - "chiplets_rows": 10615, - "poseidon2_permutation_rows": 27440, - "range_rows": 2493, + "core_rows": 27778, + "chiplets_rows": 10953, + "poseidon2_permutation_rows": 27920, + "range_rows": 2621, "chiplets_shape": { - "hasher_rows": 8184, - "bitwise_rows": 640, - "memory_rows": 1789, + "hasher_rows": 8464, + "bitwise_rows": 648, + "memory_rows": 1839, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume PAUSE_CONFIG note (network account)": { - "prologue": 3665, - "notes_processing": 2433, + "prologue": 3715, + "notes_processing": 2596, "note_execution": { - "0xebe32aa38938bfc16f277b8542e58ed4b6b7a4ea9be764972291b965299f59a6": 2391 + "0x8319881fb2093f5e4202542b56e2e4f28db21a4261fdef6e0a1e9b6e503d57e3": 2554 }, "tx_script_processing": 41, "epilogue": { - "total": 12392, - "auth_procedure": 8823 + "total": 13114, + "auth_procedure": 9545 }, "trace": { - "core_rows": 18575, - "chiplets_rows": 7665, - "poseidon2_permutation_rows": 23872, - "range_rows": 1581, + "core_rows": 19510, + "chiplets_rows": 8003, + "poseidon2_permutation_rows": 24352, + "range_rows": 1681, "chiplets_shape": { - "hasher_rows": 5872, - "bitwise_rows": 640, - "memory_rows": 1151, + "hasher_rows": 6152, + "bitwise_rows": 648, + "memory_rows": 1201, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume OWNER_CONFIG note (network account)": { - "prologue": 3524, - "notes_processing": 2461, + "prologue": 3574, + "notes_processing": 2624, "note_execution": { - "0x413694d4ebf2f4f4ad41eb749201f02c2d3e4c08da7f36eb957ccab987124fb7": 2419 + "0xea76aaf9623f2edea65fa01b923bd7817e2ee98e51cb885047a3134fb3e46829": 2582 }, "tx_script_processing": 41, "epilogue": { - "total": 12086, - "auth_procedure": 8805 + "total": 12808, + "auth_procedure": 9527 }, "trace": { - "core_rows": 18156, - "chiplets_rows": 7545, - "poseidon2_permutation_rows": 23728, - "range_rows": 1517, + "core_rows": 19091, + "chiplets_rows": 7883, + "poseidon2_permutation_rows": 24208, + "range_rows": 1641, "chiplets_shape": { - "hasher_rows": 5768, - "bitwise_rows": 656, - "memory_rows": 1119, + "hasher_rows": 6048, + "bitwise_rows": 664, + "memory_rows": 1169, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume RBAC_CONFIG note (network account)": { - "prologue": 3703, - "notes_processing": 5257, + "prologue": 3753, + "notes_processing": 5592, "note_execution": { - "0xe8b49ff8fb19d28da5c7e5266fe1a243b31cbb6e350636449d856e180797e81d": 5215 + "0x42f1d87c22e0b90f446f37474f19de44b4f24e6f1bb1552bb9390dddde1acd77": 5550 }, "tx_script_processing": 41, "epilogue": { - "total": 12934, - "auth_procedure": 8832 + "total": 13656, + "auth_procedure": 9554 }, "trace": { - "core_rows": 21979, - "chiplets_rows": 10003, - "poseidon2_permutation_rows": 30160, - "range_rows": 1719, + "core_rows": 23086, + "chiplets_rows": 10365, + "poseidon2_permutation_rows": 30800, + "range_rows": 1949, "chiplets_shape": { - "hasher_rows": 7912, - "bitwise_rows": 656, - "memory_rows": 1433, + "hasher_rows": 8216, + "bitwise_rows": 664, + "memory_rows": 1483, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume NETWORK_ACCOUNT_CONFIG note (network account)": { - "prologue": 3608, - "notes_processing": 2958, + "prologue": 3658, + "notes_processing": 3121, "note_execution": { - "0x89ace2ea72ffd23f3252bf317ed8155bf1e79fc769d166f538487c5b74d646c6": 2916 + "0x1f642466bd8f54d64c5b1670c001ccb7e01fd0d7fde2d0c3d46c7b0fa99e4763": 3079 }, "tx_script_processing": 41, "epilogue": { - "total": 12405, - "auth_procedure": 8814 + "total": 13127, + "auth_procedure": 9536 }, "trace": { - "core_rows": 19056, - "chiplets_rows": 8201, - "poseidon2_permutation_rows": 26416, - "range_rows": 1543, + "core_rows": 19991, + "chiplets_rows": 8539, + "poseidon2_permutation_rows": 26896, + "range_rows": 1687, "chiplets_shape": { - "hasher_rows": 6360, - "bitwise_rows": 640, - "memory_rows": 1199, + "hasher_rows": 6640, + "bitwise_rows": 648, + "memory_rows": 1249, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume CONSTANT_FEE_POLICY_CONFIG note (network account)": { - "prologue": 3664, - "notes_processing": 3643, + "prologue": 3714, + "notes_processing": 3820, "note_execution": { - "0x6420abd200a8e86e40db716e1a98aaf6c389047ae16b799030bd40a778b47b01": 3601 + "0x025c0149268d80d27e8b63ac04c78d5d301eb25fa010a0066209a1b53c299c16": 3778 }, "tx_script_processing": 41, "epilogue": { - "total": 12558, - "auth_procedure": 8823 + "total": 13280, + "auth_procedure": 9545 }, "trace": { - "core_rows": 19950, - "chiplets_rows": 8495, - "poseidon2_permutation_rows": 26528, - "range_rows": 1591, + "core_rows": 20899, + "chiplets_rows": 8833, + "poseidon2_permutation_rows": 27024, + "range_rows": 1767, "chiplets_shape": { - "hasher_rows": 6576, - "bitwise_rows": 640, - "memory_rows": 1277, + "hasher_rows": 6856, + "bitwise_rows": 648, + "memory_rows": 1327, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume FEE_SPONSORSHIP note with feature note (network account)": { - "prologue": 5282, + "prologue": 5367, "notes_processing": 1155, "note_execution": { - "0xaa511953232bfdd8bde965cf9b0cca4b57e4a9d9a83ba375ea8002caec40eff1": 456, - "0xcfad35a98d385c2cd4aa71e74125102cc4a475029d7d1c49f3a10bf430d0b37b": 648 + "0xa7995001442412a833ad5d36457a67963ddb41d71b07e2f44f5f2cdfef20bc84": 456, + "0xf7decae9953c23fcd430885e7de27b22cb8e406260d7683f190ed5a795a14b08": 648 }, "tx_script_processing": 41, "epilogue": { - "total": 14994, - "auth_procedure": 11875 + "total": 15723, + "auth_procedure": 12604 }, "trace": { - "core_rows": 21516, - "chiplets_rows": 9619, - "poseidon2_permutation_rows": 26320, - "range_rows": 1575, + "core_rows": 22330, + "chiplets_rows": 9920, + "poseidon2_permutation_rows": 26736, + "range_rows": 1693, "chiplets_shape": { - "hasher_rows": 7400, - "bitwise_rows": 992, - "memory_rows": 1225, + "hasher_rows": 7640, + "bitwise_rows": 1008, + "memory_rows": 1270, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume FEE_SPONSORSHIP note (reclaim)": { - "prologue": 3965, + "prologue": 4015, "notes_processing": 2865, "note_execution": { - "0xe32572b933d5de6c718bbc3c56e2b33574be853601d0577b2deb20a49be30539": 2823 + "0x12cb8a0d175a0a98c0b6ebc58ebb64cf529b0677be85bd7dd001601bb30b2b6b": 2823 }, "tx_script_processing": 41, "epilogue": { - "total": 10411, - "auth_procedure": 8149 + "total": 10394, + "auth_procedure": 8132 }, "trace": { - "core_rows": 17326, - "chiplets_rows": 7958, - "poseidon2_permutation_rows": 24016, - "range_rows": 1579, + "core_rows": 17359, + "chiplets_rows": 7868, + "poseidon2_permutation_rows": 23776, + "range_rows": 1517, "chiplets_shape": { - "hasher_rows": 5856, - "bitwise_rows": 1224, - "memory_rows": 876, + "hasher_rows": 5824, + "bitwise_rows": 1160, + "memory_rows": 882, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume CLAIM note (L1 to Miden, with fee payment)": { - "prologue": 5010, - "notes_processing": 28303, + "prologue": 5060, + "notes_processing": 28215, "note_execution": { - "0xb92e6fd5ec3cb3d21fd8c207f67b385e1b06393b0476aff1dd17ad9bad1b8fbc": 28261 + "0x49cdb9cd15380c349a8fc22400ed05d402962d4eda56e9e3a2671030b8d60125": 28173 }, "tx_script_processing": 41, "epilogue": { - "total": 20708, - "auth_procedure": 14034 + "total": 21937, + "auth_procedure": 15263 }, "trace": { - "core_rows": 54106, - "chiplets_rows": 22433, - "poseidon2_permutation_rows": 46672, - "range_rows": 3621, + "core_rows": 55297, + "chiplets_rows": 22733, + "poseidon2_permutation_rows": 47328, + "range_rows": 3761, "chiplets_shape": { - "hasher_rows": 14888, - "bitwise_rows": 3168, - "memory_rows": 4375, + "hasher_rows": 15072, + "bitwise_rows": 3200, + "memory_rows": 4459, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume CLAIM note (L2 to Miden, with fee payment)": { - "prologue": 5010, - "notes_processing": 38465, + "prologue": 5060, + "notes_processing": 38185, "note_execution": { - "0x5ab9bf3e5894314d51d1224cbe74b44dde6ac77220c3686ac17faef2a25169ba": 38423 + "0x8f113a459b0a87ac90f3c184d1ebfd81cb09076fadb64bff39609eaa14209ce9": 38143 }, "tx_script_processing": 41, "epilogue": { - "total": 20708, - "auth_procedure": 14034 + "total": 21937, + "auth_procedure": 15263 }, "trace": { - "core_rows": 64268, - "chiplets_rows": 25183, - "poseidon2_permutation_rows": 48816, - "range_rows": 3861, + "core_rows": 65267, + "chiplets_rows": 25291, + "poseidon2_permutation_rows": 49472, + "range_rows": 3963, "chiplets_shape": { - "hasher_rows": 16440, - "bitwise_rows": 3424, - "memory_rows": 5317, + "hasher_rows": 16432, + "bitwise_rows": 3456, + "memory_rows": 5401, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume B2AGG note (bridge-out, with fee payment)": { - "prologue": 5317, - "notes_processing": 119280, + "prologue": 5367, + "notes_processing": 120008, "note_execution": { - "0x8045f362a182de06bb18d6a3d86fa86e2c379c5eb9cf379cf943f4d54d0eedae": 119238 + "0x71b6eb992761503187babb3bf70dcab321dc00465ecb077ab1a1b2289eedf4f6": 119966 }, "tx_script_processing": 41, "epilogue": { - "total": 29251, - "auth_procedure": 12873 + "total": 30393, + "auth_procedure": 14015 }, "trace": { - "core_rows": 153933, - "chiplets_rows": 73417, - "poseidon2_permutation_rows": 118112, - "range_rows": 5047, + "core_rows": 155853, + "chiplets_rows": 73770, + "poseidon2_permutation_rows": 118768, + "range_rows": 6217, "chiplets_shape": { - "hasher_rows": 58880, - "bitwise_rows": 3984, - "memory_rows": 10551, + "hasher_rows": 59112, + "bitwise_rows": 4016, + "memory_rows": 10640, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume B2AGG note (bridge-out, 2^31-1 leaves, with fee payment)": { - "prologue": 5317, - "notes_processing": 63420, + "prologue": 5367, + "notes_processing": 64148, "note_execution": { - "0x8045f362a182de06bb18d6a3d86fa86e2c379c5eb9cf379cf943f4d54d0eedae": 63378 + "0x71b6eb992761503187babb3bf70dcab321dc00465ecb077ab1a1b2289eedf4f6": 64106 }, "tx_script_processing": 41, "epilogue": { - "total": 20323, - "auth_procedure": 12873 + "total": 21465, + "auth_procedure": 14015 }, "trace": { - "core_rows": 89145, - "chiplets_rows": 42580, - "poseidon2_permutation_rows": 55920, - "range_rows": 3969, + "core_rows": 91065, + "chiplets_rows": 42941, + "poseidon2_permutation_rows": 56576, + "range_rows": 5093, "chiplets_shape": { - "hasher_rows": 31856, - "bitwise_rows": 3984, - "memory_rows": 6738, + "hasher_rows": 32096, + "bitwise_rows": 4016, + "memory_rows": 6827, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume CONFIG_AGG_BRIDGE note (with fee payment)": { - "prologue": 4697, - "notes_processing": 13318, + "prologue": 4747, + "notes_processing": 13766, "note_execution": { - "0x4a95146177b6458432d5a1f60f8ade60686c30743fcc89271463bc1bf577ff97": 13276 + "0xd440b0cee85c6aef5c01291aa059ed14e6415fea85819c9ec7ee46f87acfbee8": 13724 }, "tx_script_processing": 41, "epilogue": { - "total": 16076, - "auth_procedure": 8976 + "total": 16798, + "auth_procedure": 9698 }, "trace": { - "core_rows": 34176, - "chiplets_rows": 15307, - "poseidon2_permutation_rows": 37536, - "range_rows": 2493, + "core_rows": 35396, + "chiplets_rows": 15656, + "poseidon2_permutation_rows": 38160, + "range_rows": 2739, "chiplets_shape": { - "hasher_rows": 12240, + "hasher_rows": 12544, "bitwise_rows": 696, - "memory_rows": 2369, + "memory_rows": 2414, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume DEREGISTER_AGG_FAUCET note (with fee payment)": { - "prologue": 4738, - "notes_processing": 12628, + "prologue": 4788, + "notes_processing": 12778, "note_execution": { - "0xe4cef1d192ed7353b3cdb747f5b6beec5419c36c49d96a6d954e3c694ecc9653": 12586 + "0x6b725938c4ac230b95043428503b438d09d1190117b878d60e98f7ba8313149d": 12736 }, "tx_script_processing": 41, "epilogue": { - "total": 16076, - "auth_procedure": 8976 + "total": 16798, + "auth_procedure": 9698 }, "trace": { - "core_rows": 33527, - "chiplets_rows": 14945, - "poseidon2_permutation_rows": 36320, - "range_rows": 2343, + "core_rows": 34449, + "chiplets_rows": 15311, + "poseidon2_permutation_rows": 36752, + "range_rows": 2643, "chiplets_shape": { - "hasher_rows": 12016, + "hasher_rows": 12360, "bitwise_rows": 688, - "memory_rows": 2239, + "memory_rows": 2261, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume UPDATE_GER note (with fee payment)": { - "prologue": 4679, - "notes_processing": 4347, + "prologue": 4729, + "notes_processing": 4546, "note_execution": { - "0x0f17499940516b9743f89ef52d6cb1d3bb002d1c196e033af53a792302987c9a": 4305 + "0x7d8f590e31efdb657004624314cd273c13ef1aec51e0c5f233e3fc2a8369ca99": 4504 }, "tx_script_processing": 41, "epilogue": { - "total": 15276, - "auth_procedure": 8976 + "total": 15998, + "auth_procedure": 9698 }, "trace": { - "core_rows": 24387, - "chiplets_rows": 10170, - "poseidon2_permutation_rows": 29760, - "range_rows": 2101, + "core_rows": 25358, + "chiplets_rows": 10487, + "poseidon2_permutation_rows": 30320, + "range_rows": 2281, "chiplets_shape": { - "hasher_rows": 7880, + "hasher_rows": 8152, "bitwise_rows": 640, - "memory_rows": 1648, + "memory_rows": 1693, "kernel_rom_rows": 1, "ace_rows": 0 } } }, "consume REMOVE_GER note (with fee payment)": { - "prologue": 4738, - "notes_processing": 5371, + "prologue": 4788, + "notes_processing": 5578, "note_execution": { - "0x7305f2b4fc451fa2cdb9c1799357f8b43637ea09330da85535339a02fc427395": 5329 + "0xae6a81d6163dd72e5a0d8c400b28e29a7aaedc506ccdf8e5967473b75f0dffc6": 5536 }, "tx_script_processing": 41, "epilogue": { - "total": 15312, - "auth_procedure": 8976 + "total": 16034, + "auth_procedure": 9698 }, "trace": { - "core_rows": 25506, - "chiplets_rows": 10486, - "poseidon2_permutation_rows": 30256, - "range_rows": 2141, + "core_rows": 26485, + "chiplets_rows": 10795, + "poseidon2_permutation_rows": 30784, + "range_rows": 2323, "chiplets_shape": { - "hasher_rows": 8112, + "hasher_rows": 8376, "bitwise_rows": 640, - "memory_rows": 1732, + "memory_rows": 1777, "kernel_rom_rows": 1, "ace_rows": 0 } diff --git a/crates/miden-agglayer/src/costs/table.rs b/crates/miden-agglayer/src/costs/table.rs index 4bba940ff3..cab0ead0e5 100644 --- a/crates/miden-agglayer/src/costs/table.rs +++ b/crates/miden-agglayer/src/costs/table.rs @@ -2,20 +2,20 @@ // Values are maxima across the benchmarked paths; see `miden_standards::note::costs` for the // caveats on what they do and do not cover. -/// Cycles of consuming a CLAIM note: L1 origin 54062, L2 origin 64224 (maximum). -pub const CLAIM_CONSUMPTION_CYCLES: u32 = 64224; +/// Cycles of consuming a CLAIM note: L1 origin 55253, L2 origin 65223 (maximum). +pub const CLAIM_CONSUMPTION_CYCLES: u32 = 65223; -/// Cycles of consuming a B2AGG note: empty frontier 153889 (maximum), 2^31-1 leaves 89101. -pub const B2AGG_CONSUMPTION_CYCLES: u32 = 153889; +/// Cycles of consuming a B2AGG note: empty frontier 155809 (maximum), 2^31-1 leaves 91021. +pub const B2AGG_CONSUMPTION_CYCLES: u32 = 155809; /// Cycles of consuming a CONFIG_AGG_BRIDGE note (single benchmarked path). -pub const CONFIG_AGG_BRIDGE_CONSUMPTION_CYCLES: u32 = 34132; +pub const CONFIG_AGG_BRIDGE_CONSUMPTION_CYCLES: u32 = 35352; /// Cycles of consuming a DEREGISTER_AGG_FAUCET note (single benchmarked path). -pub const DEREGISTER_AGG_FAUCET_CONSUMPTION_CYCLES: u32 = 33483; +pub const DEREGISTER_AGG_FAUCET_CONSUMPTION_CYCLES: u32 = 34405; /// Cycles of consuming an UPDATE_GER note (single benchmarked path). -pub const UPDATE_GER_CONSUMPTION_CYCLES: u32 = 24343; +pub const UPDATE_GER_CONSUMPTION_CYCLES: u32 = 25314; /// Cycles of consuming a REMOVE_GER note (single benchmarked path). -pub const REMOVE_GER_CONSUMPTION_CYCLES: u32 = 25462; +pub const REMOVE_GER_CONSUMPTION_CYCLES: u32 = 26441; diff --git a/crates/miden-standards/asm/standards/fee/mod.masm b/crates/miden-standards/asm/standards/fee/mod.masm index 83c76340da..630660db8b 100644 --- a/crates/miden-standards/asm/standards/fee/mod.masm +++ b/crates/miden-standards/asm/standards/fee/mod.masm @@ -7,7 +7,7 @@ use miden::protocol::asset use miden::protocol::native_account use miden::protocol::output_note use miden::protocol::tx -use {Asset} from miden::protocol::types +use {Asset, AssetId} from miden::protocol::types use miden::standards::assets::fungible_asset use miden::standards::fees use miden::standards::notes::tx_fee @@ -27,19 +27,35 @@ pub type ConversionInfo = word # CONSTANTS # ================================================================================================= -# Estimated upper bound on the number of cycles a pay_fee flow spends after the kernel's +# Estimated upper bound on the number of cycles the fee note payment spends after the kernel's # compute_fee call returns: rate conversion, fee-asset construction, serial-number derivation, # recipient computation, note creation and funding. Guarded by the fee-payment regression tests. const PAY_FEE_CYCLES = 8192 +# Estimated upper bounds on the number of cycles the sponsorship payment spends after the +# compute_fee call returns: a walk over every output note looking for network notes, then funding +# a FEE_SPONSORSHIP note for each one priced above zero, which covers reading the target +# attachment, the note creation and the vault withdrawal. The pricing itself runs during the +# estimate, before compute_fee, and is charged by the kernel. Measured by diffing the auth +# procedure cycles of transactions with one to three plain, zero-priced and sponsored network +# notes: about 800 cycles per walked note over both passes, of which the payment's walk is about +# half, and about 4300 per sponsorship note created. Guarded by the fee-payment regression tests. +const SPONSORSHIP_WALK_PER_OUTPUT_NOTE_CYCLES = 512 +const SPONSORSHIP_NOTE_CYCLES = 8192 + +# Local memory offset at which `pay_fee` keeps the sponsorship price table, sized for +# fees::SPONSORSHIP_PRICE_TABLE_NUM_ELEMENTS elements. +const SPONSORSHIP_PRICE_TABLE_LOC = 0 + # Estimated upper bounds on the number of kernel epilogue cycles spent after the auth procedure # returns: output vault build, output notes commitment, account patch and final account # commitments. The cost scales with the number of output notes (measured ~330 cycles per # asset-less note; ~1k-2k base for note-less transactions), so the estimate is computed as -# BASE + PER_NOTE * (num_output_notes + 1), counting the fee note itself. Note that the per-note -# figure is measured on asset-less notes; notes carrying many assets cost more, so asset-heavy -# transactions may slightly under-estimate the fee (which fails closed: the batch builder may -# reject the underpaying note). Guarded by the fee-payment regression tests. +# BASE + PER_NOTE * (num_output_notes + 1 + num_sponsorship_notes), counting the fee note and the +# sponsorship notes the payment has yet to create. Note that the per-note figure is measured on +# asset-less notes; notes carrying many assets cost more, so asset-heavy transactions may slightly +# under-estimate the fee (which fails closed: the batch builder may reject the underpaying note). +# Guarded by the fee-payment regression tests. const POST_AUTH_EPILOGUE_BASE_CYCLES = 4096 const POST_AUTH_EPILOGUE_PER_NOTE_CYCLES = 512 @@ -233,28 +249,44 @@ pub proc convert_amount(amount: felt, rate: ConversionRate) -> felt # => [converted_amount] end -#! Adds the internal cycle margins of a pay_fee flow to the caller's estimate of remaining +#! Adds the internal cycle margins of a fee flow to the caller's estimate of remaining #! authentication cycles. #! -#! The margins cover the pay_fee tail (everything after the compute_fee call) and the post-auth -#! kernel epilogue, whose cost scales with the number of output notes. All output notes except -#! the fee note itself exist by the time a pay_fee flow runs, so the estimate is scaled by their -#! count plus one. +#! The margins cover the payment — everything the fee flow does after the compute_fee call, +#! sponsorship notes included — and the post-auth kernel epilogue, whose cost scales with the +#! number of output notes. The fee note and the sponsorship notes are created after the fee is +#! computed, so the epilogue estimate is scaled by the count of the notes that already exist plus +#! those. #! -#! Inputs: [num_extra_cycles] +#! Inputs: [num_extra_cycles, num_sponsorship_notes, num_output_notes] #! Outputs: [num_estimated_extra_cycles] #! #! Where: #! - num_extra_cycles is the estimated number of cycles the authentication procedure spends after -#! the pay_fee flow returns. +#! the fee flow returns. +#! - num_sponsorship_notes is the number of FEE_SPONSORSHIP notes the payment will create. +#! - num_output_notes is the number of output notes that exist before the payment creates any. #! - num_estimated_extra_cycles is the total estimate to pass to compute_fee. #! #! Invocation: exec -pub proc apply_cycle_margins(num_extra_cycles: felt) -> felt +pub proc apply_cycle_margins( + num_extra_cycles: felt, + num_sponsorship_notes: felt, + num_output_notes: felt, +) -> felt add.PAY_FEE_CYCLES add.POST_AUTH_EPILOGUE_BASE_CYCLES - # => [num_partial_extra_cycles] + # => [num_estimated_extra_cycles, num_sponsorship_notes, num_output_notes] + + # the sponsorship payment walks every output note that exists + dup.2 mul.SPONSORSHIP_WALK_PER_OUTPUT_NOTE_CYCLES add + # => [num_estimated_extra_cycles, num_sponsorship_notes, num_output_notes] + + # and creates one sponsorship note per network note it finds + dup.1 mul.SPONSORSHIP_NOTE_CYCLES add + # => [num_estimated_extra_cycles, num_sponsorship_notes, num_output_notes] - exec.tx::get_num_output_notes add.1 mul.POST_AUTH_EPILOGUE_PER_NOTE_CYCLES add + # the epilogue processes the notes that exist, the sponsorship notes and the fee note + movup.2 movup.2 add add.1 mul.POST_AUTH_EPILOGUE_PER_NOTE_CYCLES add # => [num_estimated_extra_cycles] end @@ -316,6 +348,55 @@ pub proc create_and_fund_fee_note(asset: Asset) # => [] end +#! Estimates the fee of the current transaction. +#! +#! The fee covers the notes that will be created: the TX_FEE note, and one +#! FEE_SPONSORSHIP note per network output note. +#! +#! num_extra_cycles only needs to cover the number of cycles the caller spends after the fee flow +#! returns (e.g. transaction summary creation, hashing and signature verification, see +#! signature::estimate_authentication_cycles); this procedure adds the payment cycles and the +#! post-authentication kernel epilogue cycles internally. +#! +#! Inputs: [FEE_ASSET_ID, price_table_ptr, num_extra_cycles] +#! Outputs: [fee_amount] +#! +#! Where: +#! - FEE_ASSET_ID is the ID of the native fee asset (see tx::get_fee_asset_id), which funds the +#! sponsorship notes. +#! - price_table_ptr is the word-aligned address of the sponsorship price table the estimate fills +#! and the payment reads back: fees::SPONSORSHIP_PRICE_TABLE_NUM_ELEMENTS elements of the caller's +#! local memory (see fees::estimate_network_note_sponsorships). +#! - num_extra_cycles is the estimated number of cycles the caller spends after this call until +#! the end of the authentication procedure. +#! - fee_amount is the computed fee of the transaction, denominated in the native fee asset. +#! +#! Panics if: +#! - the sponsorship estimate fails (propagates the panics of +#! fees::estimate_network_note_sponsorships, e.g. a target's fee policy is unset or unreachable). +#! - num_extra_cycles (plus internal margins) is not a u32 or the total cycle count exceeds the +#! maximum cycle count. +#! +#! Invocation: exec +pub proc estimate_fee(fee_asset_id: AssetId, price_table_ptr: u32, num_extra_cycles: felt) -> felt + # only the note counts are needed: the fee prices the notes, not the amounts they carry + exec.fees::estimate_network_note_sponsorships + # => [num_sponsorship_notes, num_output_notes, num_extra_cycles] + + movup.2 + # => [num_extra_cycles, num_sponsorship_notes, num_output_notes] + + exec.apply_cycle_margins + # => [num_estimated_extra_cycles] + + # no output notes are excluded from the fee computation + padw movup.4 + # => [num_estimated_extra_cycles, EXCLUDE_NOTES_COMMITMENT] + + exec.tx::compute_fee + # => [fee_amount] +end + #! Computes the transaction fee and pays it by creating and funding a public TX_FEE note. #! #! This is the fee payment flow for all account types: the caller supplies the decoded @@ -327,23 +408,15 @@ end #! and obtain the conversion info via native_conversion_info, which needs no commitment since #! it is read from the reference block. #! -#! The fee amount is computed via the compute_fee kernel procedure. num_extra_cycles only needs -#! to cover the number of cycles the authentication procedure spends after this procedure -#! returns (e.g. transaction summary creation, hashing and signature verification, see -#! signature::estimate_authentication_cycles); this procedure adds its own tail cycles and the -#! post-authentication kernel epilogue cycles internally. -#! -#! The paid amount is ceil(fee_amount * rate_num / rate_den) of the asset issued by the faucet -#! in the conversion info. +#! The fee is computed by estimate_fee, which prices the network output notes once and records the +#! prices in this procedure's local memory. The payment then settles both of the transaction's fee +#! obligations, in order: the sponsorship notes of its network output notes, funded from those +#! prices, then the TX_FEE note carrying ceil(fee_amount * rate_num / rate_den) of the asset issued +#! by the faucet in the conversion info. #! #! If the computed fee is zero (e.g. the reference block's verification base fee is zero), no -#! fee note is created and no conversion info is required (the empty word is accepted). Note that -#! the sponsorship step above may still have created notes and withdrawn from the vault. -#! -#! Before computing the fee, this creates a FEE_SPONSORSHIP note for every network output note -#! of the transaction (see fees::create_network_note_sponsorships), so those notes are included -#! in the cycle estimate and the fee. This prices each network note through its target account's -#! fee policy via an FPI call, which requires that target to be provisioned as a foreign account. +#! fee note is created and no conversion info is required (the empty word is accepted). The +#! sponsorship notes are created regardless. #! #! Inputs: [num_extra_cycles, CONVERSION_INFO] #! Outputs: [total_sponsored_fee_amount] @@ -353,42 +426,41 @@ end #! the end of the authentication procedure. #! - CONVERSION_INFO is [faucet_id_suffix, faucet_id_prefix, rate_num, rate_den], or the empty #! word if no conversion info was committed (only accepted when the computed fee is zero). -#! - total_sponsored_fee_amount is the total amount the sponsorship step moved into sponsorship -#! notes. +#! - total_sponsored_fee_amount is the total amount moved into the sponsorship notes. #! #! Panics if: -#! - the sponsorship step fails (propagates the panics of -#! fees::create_network_note_sponsorships, e.g. a target's fee policy is unset/unreachable or -#! the vault underfunds a sponsored amount). -#! - num_extra_cycles (plus internal margins) is not a u32 or the total cycle count exceeds the -#! maximum cycle count. +#! - estimate_fee or fees::create_network_note_sponsorships panics. #! - the computed fee is non-zero and CONVERSION_INFO is the empty word. #! - the conversion rate is malformed or the converted amount overflows. #! - the account vault holds less of the payment asset than the amount to be paid. #! - the maximum number of output notes is exceeded. #! #! Invocation: exec +@locals(4096) pub proc pay_fee(num_extra_cycles: felt, conversion_info: ConversionInfo) -> felt - # sponsor any network output notes before the fee is computed, so the sponsorship notes - # this creates are counted by both the cycle estimate and compute_fee. + # the estimate fills the price table and the payment reads it back + locaddr.SPONSORSHIP_PRICE_TABLE_LOC dup + # => [price_table_ptr, price_table_ptr, num_extra_cycles, CONVERSION_INFO] + + # the native fee asset funds the sponsorship notes, and is read once for both passes exec.tx::get_fee_asset_id - # => [FEE_ASSET_ID, num_extra_cycles, CONVERSION_INFO] + # => [FEE_ASSET_ID, price_table_ptr, price_table_ptr, num_extra_cycles, CONVERSION_INFO] - exec.fees::create_network_note_sponsorships - # => [total_sponsored_fee_amount, num_extra_cycles, CONVERSION_INFO] + dupw movup.8 movdn.4 + # => [FEE_ASSET_ID, price_table_ptr, FEE_ASSET_ID, price_table_ptr, num_extra_cycles, + # CONVERSION_INFO] - movdn.5 - # => [num_extra_cycles, CONVERSION_INFO, total_sponsored_fee_amount] + movup.10 movdn.5 + # => [FEE_ASSET_ID, price_table_ptr, num_extra_cycles, FEE_ASSET_ID, price_table_ptr, + # CONVERSION_INFO] - exec.apply_cycle_margins - # => [num_estimated_extra_cycles, CONVERSION_INFO, total_sponsored_fee_amount] + exec.estimate_fee + # => [fee_amount, FEE_ASSET_ID, price_table_ptr, CONVERSION_INFO] - # no output notes are excluded from the fee computation - padw movup.4 - # => [num_estimated_extra_cycles, EXCLUDE_NOTES_COMMITMENT, CONVERSION_INFO, - # total_sponsored_fee_amount] + movdn.5 exec.fees::create_network_note_sponsorships + # => [total_sponsored_fee_amount, fee_amount, CONVERSION_INFO] - exec.tx::compute_fee + movdn.5 # => [fee_amount, CONVERSION_INFO, total_sponsored_fee_amount] dup eq.0 diff --git a/crates/miden-standards/asm/standards/fees/mod.masm b/crates/miden-standards/asm/standards/fees/mod.masm index df18852b3f..8ce2a36873 100644 --- a/crates/miden-standards/asm/standards/fees/mod.masm +++ b/crates/miden-standards/asm/standards/fees/mod.masm @@ -6,8 +6,13 @@ # FEE_SPONSORSHIP notes to the account vault, pricing each feature note through the active fee policy # owned by the `fee_manager` module. # -# `create_network_note_sponsorships` walks the transaction's output notes and, for every note targeted at a -# network account, creates a paired FEE_SPONSORSHIP note funded with the fee that account charges. +# `estimate_network_note_sponsorships` walks the transaction's output notes and, for every note +# targeted at a network account, asks that account's fee policy what the note costs, recording the +# fee asset value in a caller-provided table so a caller can charge for the FEE_SPONSORSHIP notes +# before it pays for them. +# +# `create_network_note_sponsorships` walks the same notes and creates the paired FEE_SPONSORSHIP note +# for every note the table prices above zero, funded from the native account's vault. use miden::core::word use miden::core::crypto::hashes::poseidon2 @@ -29,7 +34,7 @@ use {NUM_ASSETS as SPONSORSHIP_NUM_ASSETS, FEATURE_NOTE_ID_ITEM_OFFSET} from mid use {NETWORK_ACCOUNT_TARGET_ATTACHMENT_SCHEME, NETWORK_ACCOUNT_TARGET_ATTACHMENT_NUM_WORDS} from miden::standards::attachments::network_account_target -use {Asset, AssetId, Bool} from miden::protocol::types +use {AccountId, Asset, AssetId, Bool} from miden::protocol::types pub mod fee_manager pub mod policies @@ -62,14 +67,25 @@ const WORD_ALIGNMENT_MASK = 0xFFFFFFFC const SPONSORSHIP_STORAGE_LOC = 0 const SPONSORSHIP_FEATURE_NOTE_ID_LOC = SPONSORSHIP_STORAGE_LOC + FEATURE_NOTE_ID_ITEM_OFFSET -# Local memory offset at which `create_network_note_sponsorship` caches the expected fee asset ID. +# Number of elements in the sponsorship price table that `estimate_network_note_sponsorships` fills +# and `create_network_note_sponsorships` reads: one fee asset value word per output note, indexed +# by output note index. Sized for the transaction kernel's MAX_OUTPUT_NOTES_PER_TX limit; callers +# reserve this many elements of local memory for it. +pub const SPONSORSHIP_PRICE_TABLE_NUM_ELEMENTS = 1024 * WORD_NUM_ELEMENTS + +# Local memory offsets at which `process_network_note_sponsorship` caches the expected fee asset ID +# and the address of the note's price table entry. const EXPECTED_FEE_ASSET_ID_LOC = 0 +const PRICE_ENTRY_PTR_LOC = 4 + +# Local memory offset at which `read_network_target_id` stages the network account target +# attachment. +const READ_TARGET_ATTACHMENT_WORD_LOC = 0 # Local memory offsets used by `compute_sponsorship_fee` (in its own local frame). -const COMPUTE_ATTACHMENT_WORD_LOC = 0 -const COMPUTE_NOTE_IDX_LOC = 4 -const COMPUTE_TARGET_ID_SUFFIX_LOC = 5 -const COMPUTE_TARGET_ID_PREFIX_LOC = 6 +const COMPUTE_NOTE_IDX_LOC = 0 +const COMPUTE_TARGET_ID_SUFFIX_LOC = 1 +const COMPUTE_TARGET_ID_PREFIX_LOC = 2 # Local memory offsets used by `create_sponsorship_note` (in its own local frame). const CREATE_FEE_ASSET_ID_LOC = 0 @@ -251,12 +267,12 @@ end #! #! Walks the transaction's output notes as they stood when this procedure was called. For each note #! that carries a `NetworkAccountTarget` attachment (i.e. is targeted at a network account), it reads -#! the target account ID, asks that account's fee policy what the note costs via an FPI call to its -#! `estimate_note_fee` procedure, and, if the fee is non-zero, creates a FEE_SPONSORSHIP output note -#! bound to the network note (as its companion) carrying exactly the estimated fee asset. The fee -#! asset is removed from the native account's vault to fund the note. Until fee asset conversion -#! exists, the target's fee must use the expected fee asset supplied by the caller, which typically -#! reads it from this account's fee manager. +#! the price `estimate_network_note_sponsorships` recorded for it in the price table and, if the +#! price is non-zero, creates a FEE_SPONSORSHIP output note bound to the network note (as its +#! companion) carrying exactly that amount of the expected fee asset. The fee asset is removed from +#! the native account's vault to fund the note. The table must have been filled for the same output +#! notes by `estimate_network_note_sponsorships`, which is where the target's fee policy is queried +#! and its fee asset checked. #! #! The created sponsorship note is public, tagged for the target network account, names the native #! account as its reclaimer, and has reclaim disabled. Its serial number is derived from the @@ -266,76 +282,57 @@ end #! Must be invoked while the native (fee-paying) account is the active account, since it removes the #! fee asset from that account's vault and creates notes from it. #! -#! Inputs: [FEE_ASSET_ID] +#! Inputs: [FEE_ASSET_ID, price_table_ptr] #! Outputs: [total_sponsored_fee_amount] #! #! Where: #! - FEE_ASSET_ID is the ID of the asset sponsorship notes are funded with, typically read from #! the fee manager's fee asset ID slot. +#! - price_table_ptr is the address of the price table `estimate_network_note_sponsorships` filled. #! - total_sponsored_fee_amount is the total amount moved into the created sponsorship notes. #! #! Panics if: #! - a network output note's target ID attachment does not consist of exactly one word. +#! - the native account's vault does not hold enough of the fee asset. +#! +#! Invocation: exec +pub proc create_network_note_sponsorships(fee_asset_id: AssetId, price_table_ptr: u32) -> felt + push.1 movdn.5 exec.process_network_note_sponsorships movdn.2 drop drop + # => [total_sponsored_fee_amount] +end + +#! Estimates the FEE_SPONSORSHIP notes that this transaction's network output notes require. +#! +#! Asks each network note's target account what the note costs via an FPI call to its fee policy +#! and records the price in the table at price_table_ptr, indexed by output note index, for +#! `create_network_note_sponsorships` to fund the notes from. Creates no note and moves nothing out +#! of the vault, so a caller can charge for those notes before it pays for them. A note the target +#! prices to zero needs no sponsorship note and so is not counted. +#! +#! Inputs: [FEE_ASSET_ID, price_table_ptr] +#! Outputs: [num_sponsorship_notes, num_output_notes] +#! +#! Where: +#! - FEE_ASSET_ID is the ID of the asset the sponsorship notes are funded with, typically read +#! from the fee manager's fee asset ID slot. +#! - price_table_ptr is the word-aligned address of a table of SPONSORSHIP_PRICE_TABLE_NUM_ELEMENTS +#! elements in the caller's local memory, holding one fee asset value word per output note; only +#! the entries of network output notes are written. +#! - num_sponsorship_notes is the number of notes `create_network_note_sponsorships` will create. +#! - num_output_notes is the number of output notes the transaction has at this point, i.e. the +#! notes `create_network_note_sponsorships` will walk. +#! +#! Panics if: +#! - a network output note's target ID attachment does not consist of exactly one word. #! - the target account's fee policy requires a network output note's recipient preimage and it #! is unavailable or does not hash to the recipient. #! - the target account's active fee policy is not set. #! - a target account charges a non-zero fee in an asset other than the expected fee asset. -#! - the native account's vault does not hold enough of the fee asset. #! #! Invocation: exec -pub proc create_network_note_sponsorships(fee_asset_id: AssetId) -> felt - push.0 movdn.4 - # => [FEE_ASSET_ID, total_sponsored_fee_amount = 0] - - # get the output note count before any sponsorship notes were created, since creating them - # increments the count - exec.tx::get_num_output_notes - # => [num_output_notes, FEE_ASSET_ID, total_sponsored_fee_amount] - - push.0 - # => [note_idx = 0, num_output_notes, FEE_ASSET_ID, total_sponsored_fee_amount] - - dup.1 dup.1 neq - # => [should_loop, note_idx, num_output_notes, FEE_ASSET_ID, total_sponsored_fee_amount] - - while.true - # only network output notes are sponsored - dup exec.is_network_note - # => [is_network_note, attachment_idx, note_idx, num_output_notes, FEE_ASSET_ID, - # total_sponsored_fee_amount] - - if.true - dup.1 - # => [note_idx, attachment_idx, note_idx, num_output_notes, FEE_ASSET_ID, - # total_sponsored_fee_amount] - - # pass a copy of the expected fee asset ID along with the note parameters - dupw.1 movup.5 movup.5 - # => [note_idx, attachment_idx, FEE_ASSET_ID, note_idx, num_output_notes, FEE_ASSET_ID, - # total_sponsored_fee_amount] - - exec.create_network_note_sponsorship - # => [sponsored_fee_amount, note_idx, num_output_notes, FEE_ASSET_ID, - # total_sponsored_fee_amount] - - movup.7 add movdn.6 - # => [note_idx, num_output_notes, FEE_ASSET_ID, total_sponsored_fee_amount] - else - # do not sponsor non-network output notes - drop - # => [note_idx, num_output_notes, FEE_ASSET_ID, total_sponsored_fee_amount] - end - - add.1 - # => [note_idx+1, num_output_notes, FEE_ASSET_ID, total_sponsored_fee_amount] - - dup.1 dup.1 neq - # => [should_loop, note_idx, num_output_notes, FEE_ASSET_ID, total_sponsored_fee_amount] - end - # => [note_idx, num_output_notes, FEE_ASSET_ID, total_sponsored_fee_amount] - - drop drop dropw - # => [total_sponsored_fee_amount] +pub proc estimate_network_note_sponsorships(fee_asset_id: AssetId, price_table_ptr: u32) -> (felt, felt) + push.0 movdn.5 exec.process_network_note_sponsorships drop + # => [num_sponsorship_notes, num_output_notes] end # HELPER PROCEDURES @@ -602,67 +599,248 @@ proc is_network_note(note_idx: u16) -> (Bool, u8) # => [is_found, attachment_idx] end -#! Creates a FEE_SPONSORSHIP note for the network output note at the given index. +#! Walks the transaction's output notes and, for every network note, either prices it through its +#! target account's fee policy into the price table or funds its FEE_SPONSORSHIP note from the table. +#! +#! Backs `estimate_network_note_sponsorships` (create_notes = 0) and +#! `create_network_note_sponsorships` (create_notes = 1); see those for the full behaviour. +#! +#! Inputs: [FEE_ASSET_ID, price_table_ptr, create_notes] +#! Outputs: [total_sponsored_fee_amount, num_sponsorship_notes, num_output_notes] +#! +#! Where: +#! - FEE_ASSET_ID is the ID of the asset the sponsorship notes are funded with. +#! - price_table_ptr is the word-aligned address of the price table, one fee asset value word per +#! output note. +#! - create_notes is 1 to create the sponsorship notes from the table, 0 to fill the table. +#! - total_sponsored_fee_amount is the total amount the sponsorship notes carry. +#! - num_sponsorship_notes is the number of network notes priced above zero, i.e. of sponsorship +#! notes created or to be created. +#! - num_output_notes is the number of output notes walked, read before any note is created. +#! +#! Panics if: +#! - process_network_note_sponsorship panics for a network output note. +#! +#! Invocation: exec +proc process_network_note_sponsorships( + fee_asset_id: AssetId, + price_table_ptr: u32, + create_notes: Bool, +) -> (felt, felt, felt) + push.0 movdn.5 + # => [FEE_ASSET_ID, price_table_ptr, num_sponsorship_notes = 0, create_notes] + + push.0 movdn.5 + # => [FEE_ASSET_ID, price_table_ptr, total_sponsored_fee_amount = 0, num_sponsorship_notes, + # create_notes] + + # get the output note count before any sponsorship notes are created, since creating them + # increments the count + exec.tx::get_num_output_notes + # => [num_output_notes, FEE_ASSET_ID, price_table_ptr, total_sponsored_fee_amount, + # num_sponsorship_notes, create_notes] + + push.0 + # => [note_idx = 0, num_output_notes, FEE_ASSET_ID, price_table_ptr, total_sponsored_fee_amount, + # num_sponsorship_notes, create_notes] + + dup.1 dup.1 neq + # => [should_loop, note_idx, num_output_notes, FEE_ASSET_ID, price_table_ptr, + # total_sponsored_fee_amount, num_sponsorship_notes, create_notes] + + while.true + # only network output notes are sponsored + dup exec.is_network_note + # => [is_network_note, attachment_idx, note_idx, num_output_notes, FEE_ASSET_ID, + # price_table_ptr, total_sponsored_fee_amount, num_sponsorship_notes, create_notes] + + if.true + dup.1 + # => [note_idx, attachment_idx, note_idx, num_output_notes, FEE_ASSET_ID, + # price_table_ptr, total_sponsored_fee_amount, num_sponsorship_notes, create_notes] + + # pass copies of the expected fee asset ID, the table pointer and the create flag along + # with the note parameters + dupw.1 movup.5 movup.5 + # => [note_idx, attachment_idx, FEE_ASSET_ID, note_idx, num_output_notes, FEE_ASSET_ID, + # price_table_ptr, total_sponsored_fee_amount, num_sponsorship_notes, create_notes] + + dup.15 movdn.6 dup.13 movdn.6 + # => [note_idx, attachment_idx, FEE_ASSET_ID, price_table_ptr, create_notes, note_idx, + # num_output_notes, FEE_ASSET_ID, price_table_ptr, total_sponsored_fee_amount, + # num_sponsorship_notes, create_notes] + + exec.process_network_note_sponsorship + # => [sponsored_fee_amount, note_idx, num_output_notes, FEE_ASSET_ID, price_table_ptr, + # total_sponsored_fee_amount, num_sponsorship_notes, create_notes] + + # only increment `num_sponsorship_notes` if the amount is non-zero + dup neq.0 movup.10 add movdn.9 + # => [sponsored_fee_amount, note_idx, num_output_notes, FEE_ASSET_ID, price_table_ptr, + # total_sponsored_fee_amount, num_sponsorship_notes, create_notes] + + movup.8 add movdn.7 + # => [note_idx, num_output_notes, FEE_ASSET_ID, price_table_ptr, + # total_sponsored_fee_amount, num_sponsorship_notes, create_notes] + else + # do not sponsor non-network output notes + drop + # => [note_idx, num_output_notes, FEE_ASSET_ID, price_table_ptr, + # total_sponsored_fee_amount, num_sponsorship_notes, create_notes] + end + + add.1 + # => [note_idx+1, num_output_notes, FEE_ASSET_ID, price_table_ptr, + # total_sponsored_fee_amount, num_sponsorship_notes, create_notes] + + dup.1 dup.1 neq + # => [should_loop, note_idx, num_output_notes, FEE_ASSET_ID, price_table_ptr, + # total_sponsored_fee_amount, num_sponsorship_notes, create_notes] + end + # => [note_idx, num_output_notes, FEE_ASSET_ID, price_table_ptr, total_sponsored_fee_amount, + # num_sponsorship_notes, create_notes] + + drop movdn.7 dropw drop movup.3 drop + # => [total_sponsored_fee_amount, num_sponsorship_notes, num_output_notes] +end + +#! Prices the network output note at the given index into the price table or, optionally, creates +#! its FEE_SPONSORSHIP note from it. #! -#! See [`create_network_note_sponsorships`] for the full behaviour; this procedure handles a single network -#! note. A network note the target account prices to zero produces no sponsorship note. +#! With create_notes = 0 the note is priced through its target account's fee policy and the fee +#! asset value is recorded in the note's price table entry. With create_notes = 1 the recorded value +#! is read back and, if it is non-zero, the sponsorship note is created and funded with it in the +#! expected fee asset. A network note the target account prices to zero needs no sponsorship note, +#! so none is created for it. #! -#! Inputs: [note_idx, attachment_idx, EXPECTED_FEE_ASSET_ID] +#! Inputs: [note_idx, attachment_idx, EXPECTED_FEE_ASSET_ID, price_table_ptr, create_notes] #! Outputs: [sponsored_fee_amount] #! #! Where: #! - note_idx is the index of the network output note to sponsor. #! - attachment_idx is the index of the note's network account target attachment. #! - EXPECTED_FEE_ASSET_ID is the ID of the asset the sponsorship note must be funded with. -#! - sponsored_fee_amount is the amount moved into the sponsorship note, or zero if none was -#! created. +#! - price_table_ptr is the word-aligned address of the price table, one fee asset value word per +#! output note. +#! - create_notes is 1 to create the sponsorship note from the table, 0 to price the note into it. +#! - sponsored_fee_amount is the amount the sponsorship note carries, or zero if the target prices +#! the note to zero and so it needs none. #! #! Panics if: #! - the note's target ID attachment does not consist of exactly one word. -#! - the target account's fee policy requires the note's recipient preimage and it is unavailable -#! or does not hash to the recipient. -#! - the target account's active fee policy is not set. -#! - the target account charges a non-zero fee in an asset other than the expected fee asset. -#! - the native account's vault does not hold enough of the fee asset. +#! - create_notes is 0 and the target account's fee policy requires the note's recipient preimage +#! and it is unavailable or does not hash to the recipient. +#! - create_notes is 0 and the target account's active fee policy is not set. +#! - create_notes is 0 and the target account charges a non-zero fee in an asset other than the +#! expected fee asset. +#! - create_notes is 1 and the native account's vault does not hold enough of the fee asset. #! #! Invocation: exec -@locals(4) -proc create_network_note_sponsorship( +@locals(5) +proc process_network_note_sponsorship( note_idx: u16, attachment_idx: u8, - expected_fee_asset_id: AssetId + expected_fee_asset_id: AssetId, + price_table_ptr: u32, + create_notes: Bool ) -> felt - # cache the expected fee asset ID for the target fee asset check + # cache the expected fee asset ID for the target fee asset check and the note funding movdn.5 movdn.5 loc_storew_le.EXPECTED_FEE_ASSET_ID_LOC dropw - # => [note_idx, attachment_idx] + # => [note_idx, attachment_idx, price_table_ptr, create_notes] - dup movdn.2 - # => [note_idx, attachment_idx, note_idx] + # cache the address of the note's price table entry, one word per output note + movup.2 dup.1 mul.WORD_NUM_ELEMENTS add loc_store.PRICE_ENTRY_PTR_LOC + # => [note_idx, attachment_idx, create_notes] - exec.compute_sponsorship_fee - # => [FEE_ASSET_ID, FEE_ASSET_VALUE, target_id_prefix, note_idx] + movup.2 + # => [create_notes, note_idx, attachment_idx] - exec.fungible_asset::to_amount_unchecked - # => [fee_amount, FEE_ASSET_ID, FEE_ASSET_VALUE, target_id_prefix, note_idx] + if.true + padw loc_load.PRICE_ENTRY_PTR_LOC mem_loadw_le + # => [FEE_ASSET_VALUE, note_idx, attachment_idx] - movdn.10 dup.10 eq.0 - # => [is_zero_fee, FEE_ASSET_ID, FEE_ASSET_VALUE, target_id_prefix, note_idx, fee_amount] + dup eq.0 + if.true + # a note priced to zero needs no sponsorship note + dropw drop drop push.0 + # => [sponsored_fee_amount = 0] + else + movup.5 movup.5 dup movdn.6 + # => [note_idx, attachment_idx, FEE_ASSET_VALUE, note_idx] - if.true - # if the price of the note is zero, do not create a sponsorship note - dropw dropw drop drop - # => [fee_amount] + exec.read_network_target_id drop + # => [target_id_prefix, FEE_ASSET_VALUE, note_idx] + + movdn.4 dup movdn.6 + # => [FEE_ASSET_VALUE, target_id_prefix, note_idx, sponsored_fee_amount] + + padw loc_loadw_le.EXPECTED_FEE_ASSET_ID_LOC + # => [FEE_ASSET_ID, FEE_ASSET_VALUE, target_id_prefix, note_idx, sponsored_fee_amount] + + exec.create_sponsorship_note + # => [sponsored_fee_amount] + end else - # reject a target whose fee asset differs from the expected fee asset - dupw padw loc_loadw_le.EXPECTED_FEE_ASSET_ID_LOC exec.word::eq - assert.err=ERR_FEE_MANAGER_TARGET_FEE_ASSET_MISMATCH - # => [FEE_ASSET_ID, FEE_ASSET_VALUE, target_id_prefix, note_idx, fee_amount] + exec.compute_sponsorship_fee + # => [FEE_ASSET_ID, FEE_ASSET_VALUE, target_id_prefix] + + # record the fee asset value for the payment + swapw loc_load.PRICE_ENTRY_PTR_LOC mem_storew_le swapw + # => [FEE_ASSET_ID, FEE_ASSET_VALUE, target_id_prefix] + + exec.fungible_asset::to_amount_unchecked + # => [sponsored_fee_amount, FEE_ASSET_ID, FEE_ASSET_VALUE, target_id_prefix] + + movdn.9 swapw dropw movup.4 drop + # => [FEE_ASSET_ID, sponsored_fee_amount] + + dup.4 neq.0 + if.true + # reject a target whose fee asset differs from the expected fee asset + dupw padw loc_loadw_le.EXPECTED_FEE_ASSET_ID_LOC exec.word::eq + assert.err=ERR_FEE_MANAGER_TARGET_FEE_ASSET_MISMATCH + end + # => [FEE_ASSET_ID, sponsored_fee_amount] - exec.create_sponsorship_note - # => [fee_amount] + dropw + # => [sponsored_fee_amount] end end +#! Reads the target account ID of the network output note at the given index from its network +#! account target attachment. +#! +#! Inputs: [note_idx, attachment_idx] +#! Outputs: [target_id_suffix, target_id_prefix] +#! +#! Where: +#! - note_idx is the index of the network output note. +#! - attachment_idx is the index of the note's network account target attachment. +#! +#! Panics if: +#! - the attachment does not consist of exactly one word. +#! +#! Invocation: exec +@locals(4) +proc read_network_target_id(note_idx: u16, attachment_idx: u8) -> AccountId + # copy the attachment into local memory, then read the target ID from it + swap locaddr.READ_TARGET_ATTACHMENT_WORD_LOC + # => [dest_ptr, attachment_idx, note_idx] + + exec.output_note::write_attachment_to_memory + # => [num_words] + + eq.NETWORK_ACCOUNT_TARGET_ATTACHMENT_NUM_WORDS + assert.err=ERR_FEE_MANAGER_NETWORK_TARGET_WRONG_NUM_WORDS + # => [] + + padw loc_loadw_le.READ_TARGET_ATTACHMENT_WORD_LOC + # => [NETWORK_ACCOUNT_TARGET_ATTACHMENT] + + exec.network_account_target::into_target_id + # => [target_id_suffix, target_id_prefix] +end + #! Computes the fee the target network account charges for the output note at the given index. #! #! Reads the note's target account ID from its network account target attachment, gathers the @@ -688,27 +866,13 @@ end #! - the target account's active fee policy is not set. #! #! Invocation: exec -@locals(7) +@locals(3) proc compute_sponsorship_fee(note_idx: u16, attachment_idx: u8) -> (Asset, felt) # the note index is needed repeatedly below, so cache it dup loc_store.COMPUTE_NOTE_IDX_LOC # => [note_idx, attachment_idx] - # copy the network account target attachment into local memory, then read the target ID - swap locaddr.COMPUTE_ATTACHMENT_WORD_LOC - # => [dest_ptr, attachment_idx, note_idx] - - exec.output_note::write_attachment_to_memory - # => [num_words] - - eq.NETWORK_ACCOUNT_TARGET_ATTACHMENT_NUM_WORDS - assert.err=ERR_FEE_MANAGER_NETWORK_TARGET_WRONG_NUM_WORDS - # => [] - - padw loc_loadw_le.COMPUTE_ATTACHMENT_WORD_LOC - # => [NETWORK_ACCOUNT_TARGET_ATTACHMENT] - - exec.network_account_target::into_target_id + exec.read_network_target_id # => [target_id_suffix, target_id_prefix] loc_store.COMPUTE_TARGET_ID_SUFFIX_LOC diff --git a/crates/miden-standards/src/note/costs/table.rs b/crates/miden-standards/src/note/costs/table.rs index 9f763ca5e5..530e980979 100644 --- a/crates/miden-standards/src/note/costs/table.rs +++ b/crates/miden-standards/src/note/costs/table.rs @@ -2,54 +2,54 @@ // Values are maxima across the benchmarked paths; see `miden_standards::note::costs` for the // caveats on what they do and do not cover. -/// Cycles of consuming a P2ID note: 1 asset 18620, 16 assets 57188 (maximum). -pub const P2ID_CONSUMPTION_CYCLES: u32 = 57188; +/// Cycles of consuming a P2ID note: 1 asset 19392, 16 assets 57960 (maximum). +pub const P2ID_CONSUMPTION_CYCLES: u32 = 57960; -/// Cycles of consuming a P2IDE note: claim 18728, claim with 16 assets 57296 (maximum), reclaim -/// 18883. -pub const P2IDE_CONSUMPTION_CYCLES: u32 = 57296; +/// Cycles of consuming a P2IDE note: claim 19500, claim with 16 assets 58068 (maximum), reclaim +/// 19655. +pub const P2IDE_CONSUMPTION_CYCLES: u32 = 58068; -/// Cycles of consuming a SWAP note: public payback 22655 (maximum), private payback 22152. -pub const SWAP_CONSUMPTION_CYCLES: u32 = 22655; +/// Cycles of consuming a SWAP note: public payback 23629 (maximum), private payback 23126. +pub const SWAP_CONSUMPTION_CYCLES: u32 = 23629; -/// Cycles of consuming a PSWAP note: full fill 25485, partial fill 29402 (maximum). -pub const PSWAP_CONSUMPTION_CYCLES: u32 = 29402; +/// Cycles of consuming a PSWAP note: full fill 26475, partial fill 30612 (maximum). +pub const PSWAP_CONSUMPTION_CYCLES: u32 = 30612; -/// Cycles of consuming a MINT note: fungible faucet 34087, non-fungible faucet 37611 (maximum). -pub const MINT_CONSUMPTION_CYCLES: u32 = 37611; +/// Cycles of consuming a MINT note: fungible faucet 35284, non-fungible faucet 38648 (maximum). +pub const MINT_CONSUMPTION_CYCLES: u32 = 38648; /// Cycles of consuming a BURN note (single benchmarked path). -pub const BURN_CONSUMPTION_CYCLES: u32 = 28773; +pub const BURN_CONSUMPTION_CYCLES: u32 = 29631; /// Cycles of consuming a CONSTANT_FEE_POLICY_CONFIG note (single benchmarked path). -pub const CONSTANT_FEE_POLICY_CONFIG_CONSUMPTION_CYCLES: u32 = 19906; +pub const CONSTANT_FEE_POLICY_CONFIG_CONSUMPTION_CYCLES: u32 = 20855; /// Cycles of consuming a FAUCET_POLICY_CONFIG note (single benchmarked path). -pub const FAUCET_POLICY_CONFIG_CONSUMPTION_CYCLES: u32 = 27017; +pub const FAUCET_POLICY_CONFIG_CONSUMPTION_CYCLES: u32 = 27959; /// Cycles of consuming a FAUCET_METADATA_CONFIG note (single benchmarked path). -pub const FAUCET_METADATA_CONFIG_CONSUMPTION_CYCLES: u32 = 27488; +pub const FAUCET_METADATA_CONFIG_CONSUMPTION_CYCLES: u32 = 28491; /// Cycles of consuming a MIN_BURN_AMOUNT_CONFIG note (single benchmarked path). -pub const MIN_BURN_AMOUNT_CONFIG_CONSUMPTION_CYCLES: u32 = 26053; +pub const MIN_BURN_AMOUNT_CONFIG_CONSUMPTION_CYCLES: u32 = 26988; /// Cycles of consuming an ALLOWLIST_CONFIG note (single benchmarked path). -pub const ALLOWLIST_CONFIG_CONSUMPTION_CYCLES: u32 = 26799; +pub const ALLOWLIST_CONFIG_CONSUMPTION_CYCLES: u32 = 27734; /// Cycles of consuming a BLOCKLIST_CONFIG note (single benchmarked path). -pub const BLOCKLIST_CONFIG_CONSUMPTION_CYCLES: u32 = 26799; +pub const BLOCKLIST_CONFIG_CONSUMPTION_CYCLES: u32 = 27734; /// Cycles of consuming a PAUSE_CONFIG note (single benchmarked path). -pub const PAUSE_CONFIG_CONSUMPTION_CYCLES: u32 = 18531; +pub const PAUSE_CONFIG_CONSUMPTION_CYCLES: u32 = 19466; /// Cycles of consuming an OWNER_CONFIG note (single benchmarked path). -pub const OWNER_CONFIG_CONSUMPTION_CYCLES: u32 = 18112; +pub const OWNER_CONFIG_CONSUMPTION_CYCLES: u32 = 19047; /// Cycles of consuming an RBAC_CONFIG note (single benchmarked path). -pub const RBAC_CONFIG_CONSUMPTION_CYCLES: u32 = 21935; +pub const RBAC_CONFIG_CONSUMPTION_CYCLES: u32 = 23042; /// Cycles of consuming a NETWORK_ACCOUNT_CONFIG note (single benchmarked path). -pub const NETWORK_ACCOUNT_CONFIG_CONSUMPTION_CYCLES: u32 = 19012; +pub const NETWORK_ACCOUNT_CONFIG_CONSUMPTION_CYCLES: u32 = 19947; /// Cycles of consuming a FEE_SPONSORSHIP note (single benchmarked path). -pub const FEE_SPONSORSHIP_CONSUMPTION_CYCLES: u32 = 21472; +pub const FEE_SPONSORSHIP_CONSUMPTION_CYCLES: u32 = 22286; diff --git a/crates/miden-testing/tests/auth/fee_payment/mod.rs b/crates/miden-testing/tests/auth/fee_payment/mod.rs index e00ffb8b1e..81421cd4a4 100644 --- a/crates/miden-testing/tests/auth/fee_payment/mod.rs +++ b/crates/miden-testing/tests/auth/fee_payment/mod.rs @@ -25,12 +25,14 @@ const VERIFICATION_BASE_FEE: u32 = 500; // that matters. // // Must be kept in sync with `miden::standards::auth::signature` (per-scheme estimates and -// `MULTISIG_AUTH_BASE_CYCLES`) and `miden::standards::fee` (`PAY_FEE_CYCLES` and the epilogue -// margins). +// `MULTISIG_AUTH_BASE_CYCLES`) and `miden::standards::fee` (`PAY_FEE_CYCLES`, the sponsorship +// margins and the epilogue margins). const ECDSA_K256_KECCAK_AUTH_CYCLES: usize = 8000; const FALCON_512_POSEIDON2_AUTH_CYCLES: usize = 80000; const MULTISIG_AUTH_BASE_CYCLES: usize = 8192; const PAY_FEE_CYCLES: usize = 8192; +const SPONSORSHIP_WALK_PER_OUTPUT_NOTE_CYCLES: usize = 512; +const SPONSORSHIP_NOTE_CYCLES: usize = 8192; const POST_AUTH_EPILOGUE_BASE_CYCLES: usize = 4096; const POST_AUTH_EPILOGUE_PER_NOTE_CYCLES: usize = 512; diff --git a/crates/miden-testing/tests/auth/fee_payment/sponsorship.rs b/crates/miden-testing/tests/auth/fee_payment/sponsorship.rs index 0b402295fd..191c3b14dc 100644 --- a/crates/miden-testing/tests/auth/fee_payment/sponsorship.rs +++ b/crates/miden-testing/tests/auth/fee_payment/sponsorship.rs @@ -8,7 +8,7 @@ use miden_protocol::asset::{Asset, AssetAmount, AssetId, FungibleAsset}; use miden_protocol::crypto::rand::FeltRng; use miden_protocol::note::{Note, NoteScriptRoot, NoteTag, NoteType, PartialNote}; use miden_protocol::testing::account_id::ACCOUNT_ID_FEE_FAUCET; -use miden_protocol::transaction::RawOutputNote; +use miden_protocol::transaction::{ExecutedTransaction, RawOutputNote}; use miden_standards::account::auth::{ AuthNetworkAccount, FeeConversionInfo, @@ -28,7 +28,11 @@ use miden_standards::tx_script::SendNotesTransactionScript; use miden_testing::utils::create_spawn_note; use miden_testing::{Auth, MockChain}; -use super::VERIFICATION_BASE_FEE; +use super::{ + SPONSORSHIP_NOTE_CYCLES, + SPONSORSHIP_WALK_PER_OUTPUT_NOTE_CYCLES, + VERIFICATION_BASE_FEE, +}; // CONSTANTS // ================================================================================================ @@ -108,60 +112,141 @@ fn p2id_network_note( .into()) } -// TESTS -// ================================================================================================ +/// Returns whether the output note is a FEE_SPONSORSHIP note. +fn is_sponsorship_note(note: &RawOutputNote) -> bool { + note.recipient() + .is_some_and(|recipient| recipient.script().root() == FeeSponsorshipNote::script_root()) +} -/// When a fee-paying account creates a network output note, `pay_fee` sponsors it: a -/// FEE_SPONSORSHIP note funded from the creator's vault is emitted alongside the network note, -/// carrying exactly the fee the target account's policy prices the note at. -#[tokio::test] -async fn pay_fee_sponsors_network_output_note() -> anyhow::Result<()> { +/// Has a signing wallet send a P2ID network note to a network account that prices the P2ID script +/// root at `target_fee`, paying its fee through `pay_fee`. Returns the executed transaction, the +/// network note it created and the target's ID. +async fn send_network_note( + target_fee: u64, +) -> anyhow::Result<(ExecutedTransaction, Note, AccountId)> { + let (executed, mut notes) = send_network_notes(&[target_fee]).await?; + let (network_note, target_id) = notes.remove(0); + Ok((executed, network_note, target_id)) +} + +/// How a P2ID note sent by [`send_notes`] is targeted. +#[derive(Clone, Copy)] +enum NoteTarget { + /// A plain note to a network account, carrying no network account target attachment, so the + /// fee flow never prices it. + Plain, + /// A network note, priced by its target at the given fee. + Network(u64), +} + +/// Has a signing wallet send one P2ID network note per entry of `target_fees`, each to its own +/// network account pricing the P2ID script root at that fee, paying its fee through `pay_fee`. +/// Returns the executed transaction and, in creation order, each network note with its target's ID. +async fn send_network_notes( + target_fees: &[u64], +) -> anyhow::Result<(ExecutedTransaction, Vec<(Note, AccountId)>)> { + let targets: Vec = + target_fees.iter().map(|fee| NoteTarget::Network(*fee)).collect(); + send_notes(&targets).await +} + +/// Has a signing wallet send one P2ID note per entry of `note_targets`, each to its own network +/// account, paying its fee through `pay_fee`. Returns the executed transaction and, in creation +/// order, each note with its target's ID. +async fn send_notes( + note_targets: &[NoteTarget], +) -> anyhow::Result<(ExecutedTransaction, Vec<(Note, AccountId)>)> { let mut rng = RandomCoin::new(Word::from([1u32, 2, 3, 4])); - // a payload asset issued by a faucet other than the fee faucet, carried by the network note + // a payload asset issued by a faucet other than the fee faucet, carried by each note let payload_asset: Asset = FungibleAsset::mock(50); + let payload_total: Asset = FungibleAsset::mock(50 * note_targets.len() as u64); let mut builder = MockChain::builder().verification_base_fee(VERIFICATION_BASE_FEE); - // the sponsor is a signing wallet holding the fee asset (for its own fee and the sponsorship) - // and the payload asset + // the sponsor is a signing wallet holding the fee asset (for its own fee and the sponsorships) + // and the payload assets let sponsor = builder.add_existing_wallet_with_assets( Auth::basic_ecdsa(), - [fee_asset(1_000_000)?, payload_asset], + [fee_asset(1_000_000)?, payload_total], )?; - // the target network account prices the P2ID script root at FEE_AMOUNT - let target = network_account( - [2; 32], - [P2idNote::script_root(), FeeSponsorshipNote::script_root()], - &[(P2idNote::script_root(), FEE_AMOUNT)], - [], - SponsorshipPolicy::default(), - )?; - builder.add_account(target.clone())?; + // each target network account prices the P2ID script root at its own fee + let mut targets = Vec::new(); + for (i, note_target) in note_targets.iter().enumerate() { + let target_fee = match note_target { + NoteTarget::Plain => 0, + NoteTarget::Network(fee) => *fee, + }; + let target = network_account( + [2 + i as u8; 32], + [P2idNote::script_root(), FeeSponsorshipNote::script_root()], + &[(P2idNote::script_root(), target_fee)], + [], + SponsorshipPolicy::default(), + )?; + builder.add_account(target.clone())?; + targets.push(target); + } let mut mock_chain = builder.build()?; mock_chain.prove_next_block()?; - // the sponsor creates the P2ID network note via a send-notes transaction script - let network_note = p2id_network_note(sponsor.id(), target.id(), payload_asset, &mut rng)?; - let tx_script = SendNotesTransactionScript::new( - &sponsor.code().interface(sponsor.id()), - &[PartialNote::from(network_note.clone())], - )?; + // the sponsor creates the P2ID notes via a send-notes transaction script + let network_notes = targets + .iter() + .zip(note_targets) + .map(|(target, note_target)| match note_target { + NoteTarget::Plain => Ok(P2idNote::builder() + .sender(sponsor.id()) + .target(target.id()) + .asset(payload_asset) + .note_type(NoteType::Public) + .serial_number(rng.draw_word()) + .build()? + .into()), + NoteTarget::Network(_) => { + p2id_network_note(sponsor.id(), target.id(), payload_asset, &mut rng) + }, + }) + .collect::>>()?; + let partial_notes: Vec = + network_notes.iter().cloned().map(PartialNote::from).collect(); + let tx_script = + SendNotesTransactionScript::new(&sponsor.code().interface(sponsor.id()), &partial_notes)?; let (auth_args, advice_value) = native_conversion_info(); - let foreign_target = mock_chain.get_foreign_account_inputs(target.id())?; + let foreign_targets = targets + .iter() + .map(|target| mock_chain.get_foreign_account_inputs(target.id())) + .collect::, _>>()?; - let executed = mock_chain + let mut tx_builder = mock_chain .build_transaction(sponsor.id()) - .foreign_accounts([foreign_target]) + .foreign_accounts(foreign_targets) .send_notes_script(&tx_script) .auth_args(auth_args) - .add_advice_map_entry(auth_args, advice_value) - .expected_output_note(RawOutputNote::Full(network_note.clone())) - .build()? - .execute() - .await?; + .add_advice_map_entry(auth_args, advice_value); + for network_note in &network_notes { + tx_builder = tx_builder.expected_output_note(RawOutputNote::Full(network_note.clone())); + } + let executed = tx_builder.build()?.execute().await?; + + let notes = network_notes + .into_iter() + .zip(targets.iter().map(|target| target.id())) + .collect(); + Ok((executed, notes)) +} + +// TESTS +// ================================================================================================ + +/// When a fee-paying account creates a network output note, `pay_fee` sponsors it: a +/// FEE_SPONSORSHIP note funded from the creator's vault is emitted alongside the network note, +/// carrying exactly the fee the target account's policy prices the note at. +#[tokio::test] +async fn pay_fee_sponsors_network_output_note() -> anyhow::Result<()> { + let (executed, network_note, target_id) = send_network_note(FEE_AMOUNT).await?; // three output notes: the network note, its sponsorship note, and the sponsor's fee note let output_notes = executed.output_notes(); @@ -178,14 +263,11 @@ async fn pay_fee_sponsors_network_output_note() -> anyhow::Result<()> { // target network account let sponsorship = output_notes .iter() - .find(|note| { - note.recipient().map(|recipient| recipient.script().root()) - == Some(FeeSponsorshipNote::script_root()) - }) + .find(|note| is_sponsorship_note(note)) .expect("a sponsorship note should be created for the network note"); let sponsorship_assets: Vec = sponsorship.assets().iter().copied().collect(); assert_eq!(sponsorship_assets, vec![fee_asset(FEE_AMOUNT)?]); - assert_eq!(sponsorship.metadata().tag(), NoteTag::with_account_target(target.id())); + assert_eq!(sponsorship.metadata().tag(), NoteTag::with_account_target(target_id)); // the sponsor still pays its own fee, and the paid amount covers the fee required for the // transaction including the sponsorship note it just created @@ -205,6 +287,116 @@ async fn pay_fee_sponsors_network_output_note() -> anyhow::Result<()> { Ok(()) } +/// A network note priced to zero by its target needs no sponsorship: `pay_fee` creates no +/// FEE_SPONSORSHIP note for it and still pays the sponsor's own fee. +#[tokio::test] +async fn pay_fee_does_not_sponsor_network_note_priced_to_zero() -> anyhow::Result<()> { + let (executed, network_note, _) = send_network_note(0).await?; + + // two output notes: the network note and the sponsor's fee note + let output_notes = executed.output_notes(); + assert_eq!(output_notes.num_notes(), 2); + assert_eq!(output_notes.get_note(0).id(), network_note.id()); + assert!( + output_notes.iter().all(|note| !is_sponsorship_note(note)), + "a network note priced to zero should not be sponsored", + ); + // the sponsor's fee note still covers the fee required for the transaction + let fee_note = output_notes.get_note(1); + assert_eq!(fee_note.metadata().tag(), TxFeeNote::TAG); + let paid = fee_note + .assets() + .iter() + .next() + .expect("fee note carries one asset") + .unwrap_fungible(); + assert!( + paid.amount() >= executed.compute_fee(), + "paid fee {} should cover the required fee {}", + paid.amount(), + executed.compute_fee(), + ); + + Ok(()) +} + +/// Each sponsorship note carries the price its own network note's target charges: with two network +/// notes priced differently, the payment funds each from the price the estimate recorded for that +/// note, not from a single shared price. +#[tokio::test] +async fn pay_fee_sponsors_each_network_note_at_its_own_price() -> anyhow::Result<()> { + let target_fees = [FEE_AMOUNT, 3 * FEE_AMOUNT]; + let (executed, notes) = send_network_notes(&target_fees).await?; + + // five output notes: the two network notes, their sponsorship notes, and the sponsor's fee note + let output_notes = executed.output_notes(); + assert_eq!(output_notes.num_notes(), 5); + + for (i, ((network_note, target_id), target_fee)) in notes.iter().zip(target_fees).enumerate() { + assert_eq!(output_notes.get_note(i).id(), network_note.id()); + + let sponsorship = output_notes + .iter() + .find(|note| { + is_sponsorship_note(note) + && note.metadata().tag() == NoteTag::with_account_target(*target_id) + }) + .expect("each network note should have a sponsorship note tagged for its target"); + let sponsorship_assets: Vec = sponsorship.assets().iter().copied().collect(); + assert_eq!(sponsorship_assets, vec![fee_asset(target_fee)?]); + } + + let fee_note = output_notes + .iter() + .find(|note| note.metadata().tag() == TxFeeNote::TAG) + .expect("the sponsor should pay its own fee note"); + let paid = fee_note + .assets() + .iter() + .next() + .expect("fee note carries one asset") + .unwrap_fungible(); + assert!( + paid.amount() >= executed.compute_fee(), + "paid fee {} should cover the required fee {}", + paid.amount(), + executed.compute_fee(), + ); + + Ok(()) +} + +/// The sponsorship cycle margins are upper bounds of the measured cost of the payment pass, which +/// runs after `compute_fee` and so is estimated rather than charged by the kernel. +/// +/// A sponsored and a zero-priced network note are priced alike, so their difference isolates the +/// creation of one sponsorship note. One more plain output note costs the walk of both passes, of +/// which only the payment's is budgeted, so its bound is conservative. +#[tokio::test] +async fn sponsorship_cycle_margins_are_upper_bounds() -> anyhow::Result<()> { + let (zero_priced, _) = send_notes(&[NoteTarget::Network(0)]).await?; + let (sponsored, _) = send_notes(&[NoteTarget::Network(FEE_AMOUNT)]).await?; + let sponsorship_note_cycles = + sponsored.measurements().auth_procedure - zero_priced.measurements().auth_procedure; + assert!( + sponsorship_note_cycles <= SPONSORSHIP_NOTE_CYCLES, + "creating a sponsorship note took {sponsorship_note_cycles} cycles, exceeding the margin \ + of {SPONSORSHIP_NOTE_CYCLES}", + ); + + let (one_plain, _) = send_notes(&[NoteTarget::Plain]).await?; + let (two_plain, _) = send_notes(&[NoteTarget::Plain, NoteTarget::Plain]).await?; + let walk_cycles = + two_plain.measurements().auth_procedure - one_plain.measurements().auth_procedure; + assert!( + walk_cycles <= 2 * SPONSORSHIP_WALK_PER_OUTPUT_NOTE_CYCLES, + "walking one more output note took {walk_cycles} cycles over both passes, exceeding twice \ + the margin of {SPONSORSHIP_WALK_PER_OUTPUT_NOTE_CYCLES}", + ); + + Ok(()) +} + /// End-to-end single hop: a local wallet creates a P2ID network note (and, via `pay_fee`, its /// FEE_SPONSORSHIP note); the target network account then consumes both, and its /// `collect_sponsored_fees` credits the prepaid fee into its vault. @@ -254,10 +446,7 @@ async fn network_account_collects_sponsored_fee_single_hop() -> anyhow::Result<( let sponsorship_id = creation_tx .output_notes() .iter() - .find(|note| { - note.recipient().map(|recipient| recipient.script().root()) - == Some(FeeSponsorshipNote::script_root()) - }) + .find(|note| is_sponsorship_note(note)) .expect("a sponsorship note should be created") .id(); @@ -373,10 +562,7 @@ async fn spawned_network_note_sponsored_by_a_and_collected_by_b_multi_hop() -> a let sponsorship_id = spawn_tx .output_notes() .iter() - .find(|note| { - note.recipient().map(|recipient| recipient.script().root()) - == Some(FeeSponsorshipNote::script_root()) - }) + .find(|note| is_sponsorship_note(note)) .expect("A should sponsor the spawned note") .id(); diff --git a/crates/miden-testing/tests/scripts/fee_collection.rs b/crates/miden-testing/tests/scripts/fee_collection.rs index 693956838d..ca08ac628f 100644 --- a/crates/miden-testing/tests/scripts/fee_collection.rs +++ b/crates/miden-testing/tests/scripts/fee_collection.rs @@ -1259,11 +1259,23 @@ async fn sponsors_and_collects_multiple_notes() -> anyhow::Result<()> { // SPONSORSHIP POLICY // ================================================================================================ -/// Under [`SponsorshipPolicy::AtMostCollectedFees`], a sponsorship that no collected fee backs is -/// rejected. +/// Under [`SponsorshipPolicy::AtMostCollectedFees`], sponsorships the collected fees do not cover +/// are rejected: one that nothing backs, and two whose summed amount exceeds the one collected fee. +/// The latter pins that the cap compares the summed amount: a total that collapsed to the number +/// of sponsorship notes would pass it. +#[rstest] +// (network notes created, collected notes consumed) +#[case::nothing_collected(1, 0)] +#[case::one_of_two_covered(2, 1)] #[tokio::test] -async fn sponsoring_more_than_collected_is_rejected() -> anyhow::Result<()> { - let test = SponsorshipTest::builder().build()?; +async fn sponsoring_more_than_collected_is_rejected( + #[case] num_network_notes: u32, + #[case] num_collected_notes: u32, +) -> anyhow::Result<()> { + let test = SponsorshipTest::builder() + .num_network_notes(num_network_notes) + .num_collected_notes(num_collected_notes) + .build()?; let result = test.transaction()?.execute().await;