Skip to Content
ReferenceMeasurements

Copied from docs/measurements.md  at build time. Edit that file.

zx402: Measurements

Fill this file during M0. It replaces the estimates in SPEC.md section 10. Measured values that are already known are in research/2026-10-06-measurements.md .

1. Circuits

CircuitConstraintsSetup powerProving timeProving key size
spend16,828151.5 to 1.9 s14,285,885 bytes
insert62,950164.3 to 4.8 s47,067,202 bytes
ragequit8,423141.0 s7,141,180 bytes

The setup power is the smallest one that fits the circuit. snarkjs needs one row per constraint, one per public signal, and one more. One shared powers of tau file of power 16 serves all three circuits. Its size is 113,248,170 bytes. The proving key sizes are for the development keys. Proving times were measured on an Apple M2 Max with Node 24.10.0 and snarkjs 0.7.6, while other builds ran. Each time covers the witness, the proof, and a check of the proof.

2. Scripts

ScriptSize in bytesSize with parameters appliedScript hash
pool10,07212,659
deposit484518
config1,7601,794
asp1,2191,254
nft456499

Measured on 2026-10-06 with Aiken 1.1.24 and the development verification keys. The pool script takes the three verification keys as parameters, which adds about 2,600 bytes. The pool script fits one publication transaction (limit 16,384 bytes). Its reference output locks 55.5 ADA at the current price per byte. Script hashes depend on the deployment and are filled in after the live run.

3. Transactions on mainnet

TransactionCPU stepsMemory unitsSize in bytesFee in ADATransaction hash
Deposit
Refund
Insert, 1 deposit
Insert, 1 note
Insert, 4 slots
Settle, 1 payout
Settle, 4 payouts
Ragequit
Stealth leg 2
ASP update
Config update

Transactions on the local test chain

Measured on 2026-10-06 in packages/txlib/test/flow.test.ts, with the real validators, real proofs, and Preprod parameters. Execution units include the 10 percent margin that the builders add. Sizes include signatures.

TransactionCPU stepsMemory unitsSize in bytesFee in ADA
Depositnonenone3500.170781
Refund9,549,35130,0494780.186606
Insert, 2 deposits3,561,283,6451,062,7499900.714686
Insert, 1 note2,882,288,665775,1508850.636746
Settle, 1 payout3,223,990,487886,9821,2430.683587
Settle, 4 payouts3,354,760,3721,219,4931,5550.725929
Ragequit2,854,798,448676,0891,3120.647836
Stealth leg 2nonenone2030.165961
ASP update61,407,529178,5376000.215321

So one private payment in stealth mode costs about 0.85 ADA in network fees for its two legs, before the relayer fee.

Transactions on Preprod

The Preprod pool was deployed on 2026-10-06. Its record is deployments/preprod.json .

TransactionCPU stepsMemory unitsSize in bytesFee in ADATransaction hash
Plain x402 payment, no poolnonenone2810.169021a185ee503b7a43e9faf67ea112f3adc05ffe3df5825b4b8512ad8d6a2c839a76
Fund the role keysnonenone5710.180549b99438ad408efecccffc77e9cf866e4fd013ee165aa0fe0060fceda939a0ca42
Publish the pool scriptnonenone12,9470.725093a9b1c5863c47a4bbf40448914192f893be139901a7f9c34c095f1073c89d1215
Publish the deposit, config, and ASP scriptsnonenone3,9540.3294010a57eac8b52fea26a1ebd71c82b34c0d1b91da824017a066a9389d45485dadbb
Init34,513,558107,8261,4390.227451db87709baae198b5de44a8115b8f2a931fe0ca4d94142943615b7ba142ac523f
Depositnonenone3490.170781ea0c386124bc7a621850dbcc6604da672e9ea2f56244ea09d6582647e6a6380c
Refund9,549,35130,0494770.1866066cc784d3785f8a41b44119f0cbd9016b16125f10f81dce35d175a0e98a6c6e58
Insert, 1 deposit3,096,717,864972,8319370.6737148f6fa4b314995a5094a87ad5622fd2d85561d9305fdf946654cd8f567dfbc72f
ASP update64,242,754182,8205990.2157724411baa60102d65d4d267469fdc7d24751278c2af79cc72cc95c68335058d1ff
Settle, 1 payout to a one-time address3,224,181,642887,4901,2420.683630a65f39df751aa7843906799080d9520bbafb6a123e1730df354d9d06782632e4
Stealth leg 2, one-time address to the sellernonenone1960.1656976043b7ec6e7fe3d98082081878ca66352ec0506405e36f1f9d33bce321bc9246
Insert, 1 note2,887,382,131792,5748840.6381181e7c759350fef8ae0f4b74ce74eb34ad97057c7ac2b74c96ea25467fbfa571f0
Ragequit2,864,233,950699,9481,1740.64386531e91180001495a39c500c4c29aba3a8d0bbe54237365ff41a7880ca760ec763

A deployment costs about 245 ADA: 160 ADA of role funding, 73.6 ADA locked in the four reference script outputs, 8.9 ADA in the three state outputs, and about 1.5 ADA of fees. The Init units are the evaluated units before the 10 percent margin.

The first Init on Preprod was finished by hand, after the node refused it for a wrong script integrity hash. That defect is fixed in packages/txlib (live cost models). To prove the fix, the complete deploy function ran once more on Preprod on 2026-10-06 with the final code, as a rehearsal for mainnet. All four transactions were accepted on the first try, and the run took 201 seconds. That second pool is a rehearsal and is not recorded in this repository.

Rehearsal transactionTransaction hash
Fund the role keys8706c1a3537f111999d2fb6d1c3c67d418a187c0c63a3a14a10c3bc079199c6a
Publish the pool script7be7af7699930f49b715e2f854f1a19a6a6e54524ef87c843ef762d6f1e285c0
Publish the deposit, config, and ASP scripts7e2c0d74c0b838eb604c352ae782825486efa710ecf0f8ef664682f4bb665180
Init7b156beead8160f683a932cb964d896bf5f7174f7a9decc56f04a6c5abcd7adc

The rows from Deposit to Ragequit are one complete pool story, run on 2026-10-06 with the code of this repository. The indexer, the crank, the association set provider service and the relayer ran in one process against Blockfrost. The sizes, fees and units in those rows are the values that the chain recorded. A deposit of 10 ADA was credited as 9.7 ADA. A private payment of 2 ADA reached the seller from a one-time address. The change note of 6.534303 ADA then left the pool through Ragequit. For the same shape of transaction, the live units of Refund, Settle, Insert of 1 note and Ragequit are within 0.4 percent of the local test chain. The association update is 5 percent higher on Preprod. So one private payment in stealth mode cost 0.849327 ADA in network fees for its two legs, before the relayer fee of 1 ADA.

The demo through the node and the stock x402 seller

On 2026-10-06 the node (npm run node -w ops), the example seller with the stock x402 server and facilitator, and the demo (npm run demo -w ops) ran as three processes against Preprod. The demo ran four times with the same note store. All four runs ended with HTTP 200 and the weather body. Two later runs are at the end of this section. The agent paid through the stock x402 client with the SDK’s stealth signer. The stock facilitator checked and submitted the second leg. The first and second run, between 15:44 and 15:57 UTC, used the first version of the node. The third run, at 18:00 UTC, used the hardened SDK and the first node that skips work while nothing changes. The fourth run, at 19:23 UTC, used the final code with the two latency fixes that section 4 describes.

Transaction, first runCPU stepsMemory unitsSize in bytesFee in ADATransaction hash
Depositnonenone3490.170781188f2b64754228e1088f36cc68f63e35fca5b8cf42102791b8748471eefcd549
Insert, 1 deposit3,245,199,950989,1531,0070.688442f96c4e1ce6c990704fe952fe3dd48839d7449d2f19b9941a9588533a154bca1d
ASP update61,407,529178,5375990.215321c77106998cfd1b6f0aa00a04ad1476667566241e1d99b8021d1430b2a4cf2360
Settle, 1 payout to a one-time address3,258,408,716999,9721,4510.701784860faa892eff5205f6e1e6d60615837926c6eeb719d338163499a8a0b28f151e
Stealth leg 2, submitted by the stock facilitatornonenone2020.1659618295a36bb5111c94abc09d783552aac07ddd73769e5c9f42bca6b07f74fb39cf
Insert, 1 note2,892,116,632807,2269540.6423859d25ba585f7eb9ef06162ce420808122c540a3af9a039ce28be1686ca97e1426
Ragequit2,890,256,226786,8241,3830.6599503c9937b3ac020e23122c7d034640dd16afa13fd15242859818fbb1caf5020af4
Transaction, second runCPU stepsMemory unitsSize in bytesFee in ADATransaction hash
Depositnonenone3490.17078115d64d2341f166cecb5aa70e65949cd3accad4dbd825d2c06a6ec287c7eb2903
Insert, 1 deposit3,249,934,4521,003,8051,0770.6927089f36ba4c0f568f4596fe1478e66b8688699a6eba95fd6fe19533896431d29c07
ASP update61,407,529178,5375990.215321dc50a6fd31020cc8fcdf42398d7f84c8e8547db02b82f474250a1f3e2fb66eb8
Settle, 1 payout to a one-time address3,257,548,8581,001,6221,5220.704941fdf36cc2f51bdfd700df5dad651fe8c303807200df280b7e6659b81ec1a7d381
Stealth leg 2, submitted by the stock facilitatornonenone2020.16596125232740f5bb6b297895e65184fd25cb55478752bd7885a5b4dd9f4c35c453d7
Insert, 1 note2,899,686,358826,1611,0250.64714726c06f86d6d4b41d8a91b60d1b484ff360a9901d6e0e9a26362770d6e1d8a1c3
Ragequit2,890,173,640789,8711,4540.663244051fd479d1a35b6310cfc5314de9d5323add552f097f845ea7f3ee7c750308d3
Transaction, third runCPU stepsMemory unitsSize in bytesFee in ADATransaction hash
Depositnonenone3490.170781b262a1f060b453a514f83cf54f959d85a5b877c47e2f9d7a63ce53d2cb835140
Insert, 1 deposit3,247,424,320999,3101,1480.69539262edd14aa40b41a8037344f1b05c2d629bb90ef5dddea26e1882af7d6ef055bd
ASP update61,407,529178,5375990.215321f23b63ea87c93b93313c783eea9f8551cf28e9961a6ff6de80c20f61e1fd25ee
Settle, 1 payout to a one-time address3,286,928,8631,094,8841,6650.718733e62a60a14378487166372dfa6efdc482569fe027b6541e92341a0026481f2a31
Stealth leg 2, submitted by the stock facilitatornonenone2020.165961fb8f464241acae0d4832b16b05956f1f7d43231250f09db7025eda692ba0e90d
Insert, 1 note2,904,420,859840,8131,0950.651414c1594dbabed5182ce55244235503baab17b82f21f983858acf033a043ab8049a
Ragequit2,867,060,184710,6801,4570.657140660285f1a4163703d3d2d43b248069c3b46311f4189ad2f9573e91558e497bc1
Transaction, fourth runCPU stepsMemory unitsSize in bytesFee in ADATransaction hash
Depositnonenone3490.170781e4b11f9e9d49263391cd49571694daaa09b2a3e7e975553aa936bd6009b9e6fb
Insert, 1 deposit3,252,158,8211,013,9621,2180.699659e3f62b9280b0b049e41b9198bf57467876044b0bff9d649f88dde0b2eb2ad825
ASP update61,407,529178,5375990.215321627a689b9679212e4cf30e56025d4b441075be1fd8cfe2b4fc844bd93e1f02cd
Settle, 1 payout to a one-time address3,256,686,535997,1451,6620.710781fd2fd50d0979af7b04a99b0893342c5bb6ac52c8934e8470a20bb6786c722bf8
Stealth leg 2, submitted by the stock facilitatornonenone2020.16596139f629f21208881635660ee62d972f2c2d91da2eadebca7d2c42aabb11df13b0
Insert, 1 note2,909,155,361855,4651,1650.6556815093b3d3c8bca861c2cfa35f0ecdaf1be7c494bce20245f05154c4532d31e52f
Ragequit2,887,421,393788,9901,6660.672322eaa3659b37a0534151e7131e809c86811dce7046e572dcbbd4301c4bcbbc7034

Pool transactions change in size as the pool ages, for two reasons. Each Insert adds one root of 35 bytes to the root history in the pool datum, until the history holds its 16 roots. A run makes two Inserts, so each kind of pool transaction is about 70 bytes larger in the next run. A full history makes a pool transaction 525 bytes larger than in a new pool. The size alone then adds 0.0231 ADA to its fee. The proof for a new nullifier also changes with the place of that nullifier in the trie. It made a Settle or a Ragequit up to 139 bytes larger or smaller from one run to the next.

Two runs after the comparison with Base

A comparison with the Base implementation then led to five fixes in the SDK and the relayer. Two more demo runs checked them on Preprod. Both ended with HTTP 200 and the weather body.

The fifth run, at 20:50 UTC on 2026-10-06, started without the demo’s note store and with the same seed. Before the fix, that run would have used an old note secret again and locked its deposit. The SDK found the four old deposits on chain. It took the fifth deposit secret and the fifth change secret, paid the seller and exited the change. The sixth run, at 21:13 UTC, used the final code with all five fixes and the note store of the fifth run.

Transaction, fifth runCPU stepsMemory unitsSize in bytesFee in ADATransaction hash
Depositnonenone3490.170781722906c19ba2caf5df038b9ca8a7bfa85f1113832e165ece38ad9f977878e1a3
Insert, 1 deposit3,271,382,5871,066,9071,2900.70726848723b508932dd269e9e7a5602759245499c6148ad7e6a46b3bf7d10ebf8e583
ASP update61,407,529178,5375990.215321b1a916d47182ffe574e8d65874796c3de5e37e8a03511675255da9accdfa2a3e
Settle, 1 payout to a one-time address3,291,160,0071,107,8311,8050.725945fc6254b4deb6130161a842e7731309e8653239f04a18035aa8d0a16a26f4fe79
Stealth leg 2, submitted by the stock facilitatornonenone2020.165961cb978fb4cf0e667cb4313056efed375433a1163b1a173ad595234cc7593320f4
Insert, 1 note2,913,889,862870,1171,2350.6599488b19e41de203c49668fd8df7d4387bd70e73e8bbe8c479533eb87643b498159d
Ragequit2,868,558,005719,2791,5970.663904e3b6b5c2744cf20d9f206b6a28f8d4f6fa0b4e4cd942dbda2eec621785a7e68d
Transaction, sixth runCPU stepsMemory unitsSize in bytesFee in ADATransaction hash
Depositnonenone3490.170781b0401cb76ee30e1f7f736a4880594b40bc8335eb95523c6341e6433913a49996
Insert, 1 deposit3,276,117,0881,081,5591,3600.711534bedb012c45c0cb8dd4b855126cd747bb9066b3da9f85075c82547a3daa299974
ASP update61,407,529178,5375990.21532175ce39638f893d2a6b2beb09a720dd15300df6d0d509f867ab4d9bb5e01518c9
Settle, 1 payout to a one-time address3,289,134,7821,099,9441,8750.7284248aa14376b07df1d1c5f1862c3981656e71451c0ecc0335606d7427302f463802
Stealth leg 2, submitted by the stock facilitatornonenone2020.16596131ecb799a2ecf591030562aa79940d2cb74071bedaf1819c54fe6d617b0ea5ff
Insert, 1 note2,915,789,138880,4861,3050.663763fcb2adb5066e8d76c434f22ef4abfed2cfe5ef788b70ccf628430b73698a5193
Ragequit2,901,528,426819,6981,7400.678367e71971072dd46b50c2b600790d1bce87ee1b44b1eef3cf2b09a7288671e4b0b9
Step, in secondsFifth runSixth run
Deposit submitted3.12.9
Note spendable, from the start194.8139.9
First leg confirmed, from the payment request40.234.6
Seller answered HTTP 200, from the payment request83.3178.2
Change note spendable, after the seller answered9.30.1
Exit submitted, after the change was spendable9.69.7
Exit confirmed, after the change was spendable23.319.9
Whole demo310.6338.0

During the fifth run the machine also ran two test suites that make proofs. So its time to a spendable note says nothing about the code. In the sixth run Preprod made no block for 132 seconds after the block of the Settle. The payment to the seller entered the next block, so the seller answered after 178 seconds. The code did not cause that wait. Since the fixes, a private payment makes two more requests to the agent’s provider: one for the fee rate and one for the tip. A deposit through the SDK makes one sync first.

The tUSDM pool

On 2026-10-07 the pool asset changed from ADA to tUSDM, the Preprod stablecoin of Masumi (policy 16a55b2a349361ff88c03788f93e1e966e5d689605d044fef722ddde, name 0014df10745553444d, 6 decimals). The same validators and the same proving keys serve the new pool; only the asset parameter of the pool script changed. The deploy sent four transactions and took about four minutes.

Deploy transactionTransaction hash
Role funding6e525fdf46419f7c587a3a164a4fb55bb2319c9d82ed95961d365ef58af6679c
Reference scripts 10200b471e4c1bc127566edc3521cc7dbca2f49fff95f5bee63b7184c613706a8
Reference scripts 2432ded4adfc56ad362858fe4ace3a73930c2cee593a7592cdc245d9983230391
Pool, config and ASP outputs291d5501d4ffa0eb97b63197e4a0424b51c71149fb5979cd9730ccce5d6e0dd9

The pool ID is d433ba1cc677f8771f22b0ecbbbdcda504022b96f34eeaed39fae78c. The ADA pool from 2026-10-06 is retired; its record is in deployments/retired/.

The demo ran once against the tUSDM pool, at 03:44 UTC on 2026-10-07. It deposited 10 tUSDM, paid the seller 2 tUSDM through the stock x402 client, and exited the change of 7 tUSDM in public. It ended with HTTP 200 and the weather body.

Transaction, tUSDM runCPU stepsMemory unitsSize in bytesFee in ADATransaction hash
Deposit, 10 tUSDM plus 1.392130 ADA minimumnonenone4450.1750057618ba9a3b33ae70ae9c23bcf7f4f5635def0c77a1ffd81d5ac8583b9fd01e8d
Insert, 1 deposit3,093,071,619968,0569830.6757703fe4adbde235b2d2657612666ac60229bcb3d3ae658cd8099d26e755285ff462
ASP update61,407,529178,5375990.21532154290a18e7917be5d6aca5b71131f89852346841ff58dfd52b34bde3b18e6c84
Settle, 1 payout of 2 tUSDM plus 2 ADA to a one-time address3,261,123,9101,015,1701,3840.700479ca9178e1f0768c05cef31f5f0565411190ce4b9d18ba25fb2e8d4140db9db5d7
Leg 2, one-time address to the seller, submitted by the stock facilitatornonenone2500.1680730f848755cd9e3309c15ea1a5622d4db53fed1ecea3f60fef9fe890873cc419ec
Insert, 1 change note2,900,511,156833,5239300.64402238f59cd274f4d1813744c8b2020d32ae175aa2f356c4d92789d71d3ece1942e3
Ragequit, 7 tUSDM2,865,602,217705,0561,2760.649316fb038a0197d25aeea3890ab5ccc7a9a990736256e4f44366e1b7484fe28760a2

What the outputs show, read from the chain:

  • The deposit output held 10 tUSDM and 1.392130 ADA, the measured minimum for that output. The Insert credited the whole 10 tUSDM, and the crank kept the deposit’s ADA.
  • The Settle left the pool with 7 tUSDM and its 6 ADA. The one-time address got exactly 2 tUSDM and 2 ADA. The relayer’s change got 1 tUSDM, its fee, and the relayer paid the 2 ADA and the network fee from its own ADA.
  • Leg 2 paid the seller 2 tUSDM and 1.831927 ADA, which is the attached 2 ADA minus the fee. No change output.
  • The exit paid the user 7 tUSDM and 1.055950 ADA, which is the measured minimum. The pool ended with no token entry and its 6 ADA.
Step, in secondstUSDM run
Deposit submitted3.2
Note spendable, from the start139.1
First leg confirmed, from the payment request30.6
Seller answered HTTP 200, from the payment request75.9
Change note spendable, after the seller answered24.2
Exit submitted, after the change was spendable9.8
Exit confirmed, after the change was spendable132.7
Whole demo371.9

Block times: deposit 03:44:53, Insert 03:45:42, ASP update 03:46:44, Settle 03:47:16, seller payment 03:47:37, change Insert 03:48:08, exit 03:50:42 UTC. The exit waited 154 seconds for a block, which is the chain, not the code.

Costs of the tUSDM run for each party: the depositor paid 0.175 ADA of fee plus 1.392 ADA attached to the deposit, which the crank kept; the agent paid 1 tUSDM to the relayer and nothing in ADA for the payment; the relayer spent 0.700 ADA of fee plus the 2 ADA it attached, and earned 1 tUSDM; the seller received 2 tUSDM plus 1.832 ADA; the exit cost the user 0.649 ADA of fee plus 1.056 ADA attached to the exit output, which stays in the user’s wallet.

The zx402 pool

On 2026-10-07 the five protocol byte strings took the name zx402 (Spec 1.0.7). Labels, contexts, note secrets and role keys changed with them, so a new tUSDM pool was deployed at 07:08 UTC from the same proving keys. The tUSDM pool d433ba1c… from earlier that day is retired; its record is in deployments/retired/.

Deploy transaction, zx402 poolSize in bytesFee in ADATransaction hash
Role funding5710.1805499ca39e3ac8e4febf71093ec837ba2edec5811f8545e30d2bbc96bf2693d2ace7
Reference scripts 112,9850.72676588b2f700b771a61982ecf8fba589efef718d87feb7fd20c711e9e86c32c51121
Reference scripts 23,9540.3294019459d503655fe9685bcb13ea2f7f082ea4d949eedb4b9586bdab4f712f32e282
Pool, config and ASP outputs1,4350.2272759f9509ea380c3db56b8c58c3a5e559bed290e717d07dab7e40acf127fe1dce09

The pool ID is 60279ebfb8db22bbe0cb2a1b7a61702ab36ade074a3866ac836df3ed. The deploy cost 245.04 ADA, of which 160 ADA funded the roles.

The demo ran once against the zx402 pool, at 07:15 UTC on 2026-10-07, with the same steps as the tUSDM run: a deposit of 10 tUSDM, a payment of 2 tUSDM to the stock x402 seller, and a public exit of the 7 tUSDM change. It ended with HTTP 200 and the weather body after 350.9 seconds.

Transaction, zx402 runCPU stepsMemory unitsSize in bytesFee in ADATransaction hash
Deposit, 10 tUSDM plus the minimum ADAnonenone4450.1750059a8e6bbf33926dd61b4e0f569459ebfad084f4fded2255915b5467d9ebadd9dd
Insert, 1 deposit3,113,944,8571,028,1319850.6808295c8c1f60bfdf3decc66172d7fb91bcfbf20511268369d0e9c831b5d53efe977e
ASP update64,242,754182,8205990.215772288f63b6494a82ea3bc63138594a4fccf1d8bd7807b1f8de7d3767bf5cf2e335
Settle, 1 payout of 2 tUSDM plus 2 ADA to a one-time address3,256,221,600998,2541,3840.6991499900d199882d60b84824bc12f422b6939296f5862bcb13e6f62252c46f654536
Leg 2, one-time address to the seller, submitted by the stock facilitatornonenone2500.168073b430fb489db83360df792fa5aebbacf7cde7283608085fc028ff92f71dbffc91
Insert, 1 change note2,897,675,931829,2409300.643570252de278de58ddc0b22dfb64346e4e54e9b40b2b607d7274493986889e926251
Ragequit, 7 tUSDM2,865,145,514703,3841,2760.6491875f383fcb185108118a4b6429e64383081bcafba875b0838fd2c078d03358f655

The sizes equal the tUSDM run’s, because the renamed strings keep their byte lengths. The execution units differ by less than one percent, which is the usual variation between runs with different inputs.

Step, in secondszx402 run
Deposit submitted3.4
Note spendable, from the start196.3
First leg confirmed, from the payment request32.5
Seller answered HTTP 200, from the payment request70.7
Change note spendable, after the seller answered60.7
Exit submitted, after the change was spendable9.6
Exit confirmed, after the change was spendable23.2
Whole demo350.9

Block times: deposit 07:15:15, Settle 07:18:26, seller payment 07:18:48, exit 07:20:32 UTC. The note waited 196 seconds to become spendable because the deposit’s block, the Insert and the ASP update landed in three blocks 110 seconds apart, which is the chain, not the code.

The staged demo: a pool funded earlier, one payment

On 2026-10-07 at 07:54 UTC the demo ran with --reuse --no-exit on the zx402 pool: no deposit, one private payment from a note already in the pool, and the change left inside for the next run. This is the shape of the owner’s demo. The seller answered HTTP 200 after 96.6 seconds; the whole run took 99.7 seconds. The demo printed the explorer links, which the README repeats with screenshots from cexplorer (docs/evidence/).

Transaction, staged runCPU stepsMemory unitsSize in bytesFee in ADATransaction hash
Settle, 1 payout of 2 tUSDM plus 2 ADA to a one-time addresssee notesee note1,8300.746439e9c5eec05c2692995ab17c7164be38e068003ec832e21c51a1b1e427be6a
Leg 2, one-time address to the seller, submitted by the stock facilitatornonenone2400.17c493eeedb959bc091da233b0971bd1818faed68efca60ad324e263e3b5e57640

The sizes and fees are the explorer’s rounded figures; the execution units of a Settle are the same as in the runs above, because the validator and the circuit did not change.

Three earlier attempts of the same staged run failed at leg 2 with “Blockfrost submitTx failed” from the seller’s facilitator, while every run with a fresh deposit had worked. The cause was in our builder: the Conway set tag on the inputs of leg 2 was written only when a Mesh transaction builder had been constructed earlier in the same process, which a fresh-deposit run does and a --reuse run does not. The stock facilitator re-encodes every transaction with the tag before it submits, so the body hash changed and the node rejected the signature. Resubmitting the original bytes went through, which is how the cause was found. The builder now sets the tag itself (regression test TX-09). Those attempts left 2 tUSDM plus 2 ADA at each of three one-time addresses (indexes 1, 2 and 3), recoverable with the SDK’s recoverOneTimeFunds; the one-time funds of index 3 were spent by the resubmission, which paid the seller once more.

A Masumi agent paid from the pool

On 2026-10-07 at 07:22 UTC the pool paid a Masumi agent on Preprod: “Expose: Web Single Answer” (registry asset 7e8bdaf2…dbc77, API https://staging.kodosumi.io/sumi/web_single_answer_cc), priced at 0.01 tUSDM in the pool’s unit. Masumi agents are paid through a Masumi payment service node, which locks the price in Masumi’s escrow contract from its own purchasing wallet. The command npm run masumi -w ops funded that wallet from the pool in private, started the job, created the purchase on a local Masumi node (payment source V1, the version every agent in the Preprod registry uses), and read the answer.

Transaction, Masumi purchaseCPU stepsMemory unitsSize in bytesFee in ADATransaction hash
Deposit, 10 tUSDMnonenone4450.17500508864108b15e833ca9a3ea82bcc3d5cd701c1d87c545fd32347ec7a4e8fc7c66
Settle, 1 payout of 0.01 tUSDM plus 2 ADA to the Masumi purchasing wallet3,290,328,2681,110,4491,6910.7215909d22cf8a3fd678be5019b11bc9bae450e751796e45acd972f970572b81ad3439
Masumi escrow lock, submitted by the Masumi node from its purchasing walletnonenone1,0560.20206521b0de05e35f0c1a5a8bd7dfadb666bc7fb4730fd2be2fc856d78eac328a0d6c

The run was split in two by a surprise: the agent’s start_job answer carries no amounts field, which the first version of the command required. The first run deposited, paid the purchasing wallet (134 seconds to a spendable note, 188 seconds to the funded wallet) and stopped at the job start. The fixed command found the wallet already funded, started job 6ac5f4b82173c6fa74f8fe68 after 15 seconds and created the purchase after 18 seconds. The Masumi node batches escrow locks every four minutes, so the lock entered the chain after 372 seconds; the agent answered 1 second later, with a one-sentence answer and three cited sources.

The agent’s seller never saw the pool. The purchasing wallet received its tUSDM from a Settle output, which looks like any other payout, and the escrow lock is a plain Masumi transaction from that wallet.

Two clean Masumi purchases, one of them end to end

After the fixes above, the command ran twice more against the same agent on 2026-10-07. The first run (09:24 UTC) paid from a note already in the pool: Settle 0d90e8a6…, escrow lock c078b060… after 302 seconds, job 6ac61036… completed. The second run (09:31 UTC) started with --deposit, so one command shows the whole trail: deposit, private payment, escrow lock, answer. It is Demo 2 in the README, with the terminal log in docs/evidence/masumi-demo-2026-10-07.log and cexplorer screenshots in docs/evidence/masumi-*.png.

Transaction, end-to-end Masumi runCPU stepsMemory unitsSize in bytesFee in ADATransaction hash
Deposit, 10 tUSDMnonenone4450.17500587f5283ea9541ff6f563f20b817864bd5683c12f13fe561a2dc8ed9f9895dad0
Settle, 1 payout of 0.01 tUSDM plus 2 ADA to the Masumi purchasing wallet3,309,382,6301,164,8092,0420.74154469c4014661a252ee35dd9cdf4f08d1a8b4799a6af7ac6767ef4f2a4b3ffb4144
Masumi escrow lock, submitted by the Masumi node from its purchasing walletnonenone1,0560.2020650b2d5095d6f04804c7de5663deec89c8c39559a0a214a70968190ab172a0c2c2
Step, in secondsend-to-end run
Deposit submitted6.9
Note spendable, from the start144.1
Settle submitted155.3
Purchasing wallet funded233.9
Escrow FundsLocked (the node batches locks every four minutes)515.8
Job completed, answer received517.2

The Settle spent one of the pool’s notes; the chain cannot tell which, and the fresh deposit of this run is one of several candidates. The agent’s answer, one sentence with three cited sources, came back through the Masumi job status.

Admin transactions on Preprod

Run on 2026-10-06 on the rehearsal pool with ops/src/admin.ts.

TransactionCPU stepsMemory unitsSize in bytesFee in ADATransaction hash
Config update, pause deposits82,889,536236,5487370.234389462a3a1c32b1c2c46b7de4dfe5855c39fd9925d3a4c81b21ecf47eebc871c437
Config update, unpause deposits82,889,536236,5487370.234389b8bca7aac78b8a24baa865e50d34ae22852639e38194abd9ff29c43d07ab2986
Sweep of 4 reference script outputsnonenone5080.4211521b7e85e2798319cdd4b54efa8aa09a1819f5b23c8c6fef9c6a9fac3e0a108c52

The sweep returned 73.55015 ADA to the operator. Its fee is higher than a plain payment because the ledger charges for the bytes of every reference script on a spent input.

4. End-to-end timings

FlowTime from request to one confirmation
Stealth payment, one-time key funded on demand116.0 s, 110.2 s, 68.8 s and 78.2 s on Preprod, four runs
Stealth payment, one-time key funded aheadnot built in M0

The time runs from the request to the seller until the seller answers HTTP 200. It covers the proof, the settlement by the relayer, one confirmation of the first leg, the second leg, and its check and one confirmation by the stock facilitator.

The demo on Preprod, step by step

The four runs of the demo command from section 3. Times are in seconds.

StepFirst runSecond runThird runFourth run
Deposit submitted3.62.93.02.8
Note spendable, from the start119.0163.7231.091.1
First leg confirmed, from the payment request28.926.930.140.3
Seller answered HTTP 200, from the payment request116.0110.268.878.2
Change note spendable, after the seller answered0.06.154.815.3
Exit submitted, after the change was spendable9.09.08.510.4
Exit confirmed, after the change was spendable19.575.041.621.0
Whole demo254.5355.0396.2205.5

Waiting for blocks dominates every step. The longest single wait, 75 seconds for the exit of the second run, was one slow block.

The third run was slow up to the spendable note. Two things in the node of that run caused it. The crank repeated an idle step only after 60 seconds, so it submitted the Insert 98 seconds after the block of the deposit. The indexer read each new block once. Right after the block of the association update, the provider still served the old association output. The next block came 67 seconds later, so the note became spendable 82 seconds after the block of the update. The final code repeats an idle step after 20 seconds. While the pool is active, its indexer reads each new block a second time 5 seconds later. In the fourth run the crank submitted the Insert 33 seconds after the block of the deposit. The note became spendable 13 seconds after the block of the association update.

In the first and second run the payment to the seller entered the second block after the Settle. In the third and fourth run it entered the next block. Four runs are too few to say whether the code or the block times made that difference.

Provider requests of the node

In the third and fourth run the node counted its HTTP requests to Blockfrost. The pool had about 20 transactions.

WhatRequests
Start of the node with a history of 13 pool transactions28
Start of the node with a history of 17 pool transactions33
Round that finds no new block1
Round after a new block8
One minute in which the node submitted nothing, seven samples12 to 27
Each of the first three minutes of the fourth run74, 96 and 43
Third run: 11.5 minutes of node time with one demo392
Fourth run: 4 minutes of node time with one demo282

A round comes every 10 seconds, and Preprod makes about three blocks a minute. That gives 27 requests a minute for a node that submits nothing, or about 1,600 an hour. The seven measured minutes average 19 requests, or about 1,100 an hour, because they had fewer blocks. For two minutes after a change, the second read of each new block adds 7 requests per block. A read of the history costs one more request for every 100 transactions at the pool address and at the deposit address. The demo and the seller’s facilitator make their own requests. This count does not include them.

Step timings on Preprod

One sample each, from the pool story in section 3, run step by step on a laptop that was busy with other work. Preprod makes a block about every 20 seconds.

StepWork before submissionWait for one confirmation
Deposit1.8 s to build27.8 s
Insert of 1 deposit by the crank14.3 s to read, prove, build and submit33.8 s
Association update6.3 s to read, build and submit49.1 s
Private settlement by the relayer1.9 s to prove, 7.0 s to verify, build and submit27.6 s
Stealth leg 2under 1 s to build39.4 s
Insert of 1 note by the crank19.3 s to read, prove, build and submit22.7 s
Ragequit4.2 s to prove, 5.6 s to build33.1 s

From the block of the deposit to the block of the second stealth leg, 224 seconds passed. That covers the deposit, its insertion, its approval, the private settlement and the payment to the seller, each started by hand after the previous one confirmed. An indexer that starts cold replayed this history of five pool transactions in about 10 seconds.

5. Differences from the Spec estimates

No estimate in SPEC section 10.3 was wrong by more than 20 percent.

TransactionEstimated CPUMeasured CPUEstimated fee in ADAMeasured fee in ADA
Depositnonenone0.17 to 0.200.170781 on Preprod
Insert, 4 notesabout 2.9B3.08B in the validator tests0.60 to 0.700.638118 for 1 note on Preprod
Insert, 4 depositsabout 4.1B4.26B in the validator tests0.75 to 0.900.714686 for 2 deposits on the local chain
Settleabout 2.9B3.22B on Preprod0.65 to 0.800.683630 on Preprod
Ragequitabout 2.6B2.86B on Preprod0.60 to 0.750.643865 on Preprod
Stealth leg 2nonenone0.17 to 0.200.165697 on Preprod

Settle and Ragequit use about 10 percent more CPU than estimated, and both stay under a third of the transaction limit. The Spec estimated 1.0 to 1.2 ADA for one private payment in stealth mode, counting a quarter of an Insert. The measured two legs cost 0.849327 ADA. A quarter of the Insert of one note adds 0.16 ADA, which gives 1.01 ADA. When a change note is inserted alone, the whole Insert of 0.638118 ADA belongs to that payment, which gives 1.49 ADA.

6. Contract building blocks

Measured in Aiken 1.1.24 unit tests on 2026-10-06. Each number includes the small cost of the test itself.

FunctionCaseCPU stepsMemory units
groth16.verify2 public inputs, both non-zero2,161,608,03390,539
groth16.verify2 public inputs, one of them 02,030,928,41089,021
encoding.labelone deposit5,815,53911,279
encoding.context1 payout20,442,21649,829
encoding.context4 payouts, each with a script credential, a stake credential, and a datum hash65,137,024171,572
encoding.nullifier_keyone key1,598,8551,105
groth16.verify, real insert proof15 public inputs, 9 of them 02,604,890,058270,047
groth16.verify, real spend proof6 public inputs2,700,200,044150,443
groth16.verify, real ragequit proof4 public inputs2,430,918,152120,491

Whole runs of the pool validator, measured in Aiken tests with real proofs. The limit for one transaction is 10,000,000,000 CPU steps.

ActionCaseCPU stepsMemory units
Insert4 deposits4,259,066,5601,303,951
Insert4 queued notes3,083,043,009959,424
Settle4 payouts, queue of 7, trie proof of 5 levels3,163,119,5241,525,372
Ragequita deposit note2,628,484,625731,595

A skipped zero input saves 130,679,623 CPU. So one proof check costs about 1.90B CPU plus 0.131B for each non-zero public input. That is close to the model in SPEC 10.2 (1.87B plus 0.13B).

7. Off-chain library

Measured on an Apple M2 Max with Node 24.10.0 on 2026-10-06.

OperationResult
One Poseidon255 h2 callabout 0.17 ms
Append 200 leaves, then read the root once226 hash calls
Set one leaf, then read the root32 hash calls

8. Development key setup

Measured on an Apple M2 Max with Node 24.10.0 and snarkjs 0.7.6 on 2026-10-06. The machine ran other builds at the same time, so read the long steps as upper bounds.

StepTime
Compile the three circuits, forcedabout 26 s
Compile the three circuits, nothing changedabout 1 s
Powers of tau, power 16: contribute44 to 51 s
Powers of tau, power 16: prepare phase 2923 to 1,145 s
Powers of tau, power 16: verify17 s
Groth16 setup for spend, insert, ragequit71 s, 96 s, 46 s
Full setup, first run1,663 s
Setup when every key is cachedunder 1 s

Two forced builds gave the same R1CS files, so the circuit build is reproducible.

CircuitR1CS SHA-256
spendf7cbe870ef2fa3897cc61f62ea3382617c4b5f7539421aee7c7809b96dae31cf
insertc1fec5a85f5e84ce270cb327d0548f7a9bfeeb144067fa13494f86b41007c7ca
ragequitec97e315ebf705e07913f7de0dec0c9fd79423fbfe1e5c73c48de580bffde91c

The keys are not reproducible. snarkjs mixes 64 random bytes from the operating system into every contribution, even when the caller passes a fixed text. So each machine gets its own development keys, and proof fixtures must carry the verification key they were made with.