zkFMI
日本語

DeFMI: a settlement layer that never reads the trade

DeFMI is a research implementation for verifying asset transfers without publishing amounts. It checks note spending authority, value conservation, reservation remainders and atomic delivery of securities and cash. It runs on a dedicated non-EVM Avalanche L1; it is not an independently operated FMI with established legal settlement finality.

Updated 2026-09-12. Current note and confidential-asset evidence is revision-pinned below. The earlier tables and charts are historical measurements from defmi/DEFMI.md, not performance results for the new path. Original environment: host-c, rustc 1.97.1, ed25519; scalar multiplication 38.4 ± 4.1 µs, 40-bit range proof 20.88 ms.

How notes settle while values stay committed

A note is the unit of asset management; a Pedersen commitment hides its quantity. Non-PQC notes still use additive homomorphism. With quantities expressed under the same generators, C(v;r) = vG + rH gives C(a;r1) + C(b;r2) = C(a+b;r1+r2). The asset-confidential path also verifies links between asset identities and values.

For example, consume a note worth 100 and create a recipient note worth 30 plus change worth 70. These are the parties' explanatory values, not public amounts. Validators check public commitments and proofs for conservation, non-negative ranges, ownership and asset consistency. A venue settlement also verifies quantity times price, reduces reservations and atomically creates securities and cash claims. Redeeming a claim produces a note that can be spent again.

Preventing reuse without naming the spent note

Every valid spend of the same input note must carry the same nullifier. The current Triptych-based proof binds ownership, value and serial_point to the same hidden input position. Validators check the proof and reject a previously used serial. A spender cannot choose another serial and produce a valid proof to spend that input again. Newly created output notes have their own spending tags.

Public records and wallet secrets

PurposePublicPrivate
QuantityCommitment and consistency proofsQuantity, asset opening and blinding randomness
ReceiptOne-time destination point and ciphertextDecryption key and decrypted opening
SpendNullifier and spending proofSpending key and private proof inputs

Copying a public note does not grant spending authority. Decryption and spending are separate capabilities. Outputs created in one transaction, timing, output count, venue and reservation relationships remain observable. A sender knows what it sent. A cryptographic public format does not guarantee transaction unlinkability or resistance to metadata inference. See self-custody and client-asset management in Japan for the legal distinction.

Native confidential asset identities: 2026-09-12

A randomized commitment replaces the asset identifier, with a proof linking it to the same asset in a public eligible cohort. Cohorts contain 2–64 assets; recipient-encrypted openings identify even zero-quantity notes. Asset definitions and cohorts remain public, and a facility retains its randomized identity through reservation and settlement.

Five AvalancheGo validators on one Linux host executed issuance, reservation, partial fills, cancellation and expiry refunds, claim redemption and reuse of a delivered note. Wallet reads after restart recovered seller balances of 57 securities and 600 currency units, buyer balances of 53 and 410, and untouched refunded balances of 25 and 25. All validator state roots agreed.

Verdict: smoke_only.Assets were synthetic; proof inputs and committee keys were constructed in one process. The run did not exercise an actual DeKYX approval service, existing QOMM/OCLOB callers connected to this confidential path, independent operators or an external cryptographic review. This Ristretto path is refused in PQC On mode.

Execution record and limits · Outcome · Restart readback

What is guaranteed, and what is not

DeFMI can check only what arithmetic settles without opening anything.

GuaranteeWhat makes it hold
Value is neither created nor destroyedper-asset commitment conservation, opening-knowledge and range proofs, canonical inputs and issuance authority
No balance goes negativea range proof on the difference
The two legs move togetherboth are checked before either is applied
One instruction settles oncenullifier registration
Cash leg = quantity × pricea product proof over three commitments
The securities leg is the instructed quantityan equality proof across generators

DeFMI verifies asset linkage and consistency with the signed instruction. Economic fairness and legal validity are not consequences of arithmetic alone. Existing venue paths also retain trust in their authorized committee and application rules.

Historical balance-width benchmarks

The linear-backend proof is a bit decomposition of the ledger's balance range, so it should be linear in width. It is: 0.98 ms/bit to build, 0.80 ms/bit to settle, 896 B/bit on the wire. The settlement intercept, 2.1 ms, is the verification of the zkPI instruction itself. At 40 bits settlement costs 34.0 ms, of which about 6% is the instruction and the rest is the ledger's range proofs.

The consequence is a listing decision, not a cryptographic one: if settlement needs to be faster, reconsider the balance width. A quantity of securities and an amount of cash are orders of magnitude apart, so give each rail its own width. Securities at 24 bits and cash at 48 settles 24% faster than both at 48 and sends 10,752 B less, with no change to the cryptography.

Settlement cost against balance width 0 10 20 30 40 50 0 10 20 30 40 50 60 70 balance width, bits ms per DvP linear backend, settle (host-c): balance width, bits 8, 8.5 linear backend, settle (host-c): balance width, bits 16, 14.8 linear backend, settle (host-c): balance width, bits 24, 21.2 linear backend, settle (host-c): balance width, bits 32, 27.6 linear backend, settle (host-c): balance width, bits 40, 34 linear backend, settle (host-c): balance width, bits 48, 40.6 Bulletproof backend, settle (host-a): balance width, bits 8, 2.09 Bulletproof backend, settle (host-a): balance width, bits 16, 2.78 Bulletproof backend, settle (host-a): balance width, bits 32, 4.26 Bulletproof backend, settle (host-a): balance width, bits 64, 7.02 linear backend, settle (host-c) Bulletproof backend, settle (host-a)
Verification is linear in the width for the bit-decomposition backend (0.80 ms/bit, 2.1 ms intercept for the instruction itself) and roughly logarithmic for Bulletproofs. Hosts differ; see calibration. Artifacts: defmi.json, rust_bench.json.
Settlement package size against balance width 1,000 10,000 100,000 0 10 20 30 40 50 60 70 balance width, bits bytes linear backend: balance width, bits 8, 29,267 linear backend: balance width, bits 16, 36,435 linear backend: balance width, bits 24, 43,603 linear backend: balance width, bits 32, 50,771 linear backend: balance width, bits 40, 57,939 linear backend: balance width, bits 48, 65,107 Bulletproof backend: balance width, bits 8, 2,656 Bulletproof backend: balance width, bits 16, 2,912 Bulletproof backend: balance width, bits 32, 3,168 Bulletproof backend: balance width, bits 64, 3,424 linear backend Bulletproof backend
896 B per bit on the linear backend against a package that barely moves with Bulletproofs: 3,424 B at 64 bits.

The Bulletproof backend

The optimised backend uses the audited bulletproofs crate, changing proof size from linear to logarithmic. It loses fine-grained widths (power-of-two only), so the honest cross-width comparison is the 40-bit linear backend against the 64-bit Bulletproof one.

balance widthlinear settleBulletproof settlespeeduplinear packageBulletproof package
8 bit8.5 ms2.09 ms4.1×29,267 B2,656 B
16 bit14.8 ms2.78 ms5.3×36,435 B2,912 B
32 bit27.6 ms4.26 ms6.5×50,771 B3,168 B
40 bit linear vs 64 bit BP34.0 ms7.02 ms4.8×57,939 B3,424 B

artifacts: defmi.json, rust_bench.json · optimised backend on host-a (calibration 25.8 µs), baseline on host-c; rerun both on one host before using as promotion evidence

Per core that is 29.4 to 142.5 settlements per second. Verifications of independent packages share nothing, so they parallelise completely: one node measured 217 settlements/s on 8 workers (7.40× one worker). A settlement node's capacity is a procurement question.

Earlier asset-tag construction and measurements

The MPC hides which asset a request is for. Dropping the trade onto a per-instrument rail at settlement would give that back; putting every instrument on one rail makes conservation hold only across instruments, so asset A could be carried out as asset B.

The construction is an asset tag. Holding q units of asset a means holding A_a^q · h^r; each transfer publishes H = A_a · h^y under a fresh y, and every range proof is made against H. What binds the disguise to a real asset is the range proof on the difference: the payer's balance already sits under A_a, so no other tag can open it. Per settlement the addition is 32 B and one sigma proof across generators; the time cost is inside the measurement's 0.2 ms noise floor. The one-out-of-many membership proof (Groth–Kohlweiss) is needed once, when a balance is issued into an account, at 0.58 ms to verify for 4 instruments and 2.24 ms for 64.

Measured attack arms: carrying a balance out under a registered tag for another asset, and using a point that was never registered, are both rejected with "remainder does not equal balance minus amount". The package is byte-identical whichever of 64 instruments it is.

Netting: gross-gross, gross-net, net-net

These are BIS DvP models 1, 2 and 3, and at the same time a question of how many range proofs land and where. A gross rail proves at each order that the post-trade position is non-negative, so settlement failure cannot occur by construction; the price is order dependence. A net rail only accumulates homomorphically during the period and proves nothing until the close, where it shows coverage once per participant on the net. A commitment hides the sign as well as the magnitude, so an intermediate negative position leaks nothing.

NPmodeverify per orderat closetotalvs gross-gross
648gross-gross6.25 ms0400.0 ms1.00×
648gross-net4.84 ms10.3 ms320.1 ms1.25×
648net-net3.54 ms20.7 ms247.5 ms1.62×
648net-net + attested0.00 ms20.5 ms20.6 ms19.40×
Settlement verification, 64 trades, 8 participants 0 100 200 300 400 500 ms total gross-gross: 400 ms 400 ms gross-gross gross-net: 320 ms 320 ms gross-net net-net: 248 ms 248 ms net-net net-net|+ attested: 20.6 ms 20.6 ms net-net + attested
Netting removes range proofs but not the per-trade zkPI verification (3.5 ms each). Signing the cycle rather than each trade removes that, at the price of third-party verifiability of the allocation. Artifact: defmi.json.

The first prediction was wrong. Counting range proofs alone said net-net would be an eighth of the work; measured, it is 1.62×, because 3.5 ms per order is the verification of the zkPI instruction itself, needed once per trade, and netting does not remove it. What removes it is changing the granularity of the instruction: if the quorum signs a whole cycle, the settlement layer's work stops depending on the number of trades. The allocation then becomes the quorum's attestation rather than a proof. That is what a central counterparty has always been; this only makes it explicit, and DeCCP is where it is made defensible.

An intraday overdraft as a commitment

Practice does not refuse every order that would take a net position negative; it grants a limit against pledged collateral, which is exactly the Bank of Japan's intraday overdraft. Here the limit is a commitment, and coverage is proved about position + limit, so the proof never says which side of zero the position was on. The limit is essentially free: 9.4 ms without, 9.2 ms with, the same range proof width against a different commitment. Granting one costs 9.4 ms to build and 1.3 ms to verify, once.

Admission, pledge, overdraft and payment should be one event, and they are not one event in the current code: PositionBook::grant and Cycle::admit are separate calls, and there is no state that locks pledged collateral. The cost of the limit is measured; the atomicity is a written requirement that is not built.

The default waterfall

A net rail can fail at the close, and the order in which that failure is worked through is the substance of the arrangement, so it is enforced. The condition that tranche k may be drawn only once tranche k−1 is exhausted is written draw_k × remaining_{k−1} = 0, with the product's commitment pinned to the identity. Cost is linear at 11.2 ms per tranche to build and about 1.4 ms to verify, once per default.

Interposing a clearing house

Under novation there is no bilateral split left to verify: a trade between A and B becomes A against the house and the house against B. An obligation is a commitment, so replacing one edge with two is two multiplications and no proof. The house's book is flat by the same construction. Measured at 8 participants, net-net grows with the trades (66 ms at 16 to 794 ms at 256) and the cleared cycle does not (19 ms to 38 ms). Novation costs 0.53 µs a trade. What it buys is that the attested arm becomes defensible: a named house took the other side, its book is checked flat by anyone, its margin is a committed cap, and its capital sits in the waterfall where CPMI-IOSCO and EMIR put it.

Four things it does not do: obligation graphs are per asset, so mixed-instrument novation is refused; positions at different providers are not cross-margined; arithmetic validity says nothing about legal validity; and the house can leave a trade out, though it can no longer invent one, because check_novation requires both counterparties' signatures on every edge of the before graph.

Hiding who paid whom: the note ledger

The current note format stores a one-time owner point, value commitment, ephemeral public key and recipient ciphertext separately. Version 2 uses a Triptych-based proof binding ownership and value to the same hidden input, publishing U/S as its nullifier rather than the public owner key. The following tables and charts describe the earlier implementation, not the performance or security of the current proof.

ring sizeprove (payer)verify (node)wire
210.9 ms1.8 ms37,024 B
1611.7 ms2.6 ms37,696 B
6414.5 ms3.7 ms38,144 B
51240.3 ms9.3 ms38,816 B
Note ring: who pays for the anonymity set 0 10 20 30 40 50 2 4 8 16 32 64 128 256 512 ring size (log scale) ms prove, payer's device: ring size (log scale) 2, 10.9 prove, payer's device: ring size (log scale) 4, 11.2 prove, payer's device: ring size (log scale) 8, 11.3 prove, payer's device: ring size (log scale) 16, 11.7 prove, payer's device: ring size (log scale) 32, 12.7 prove, payer's device: ring size (log scale) 64, 14.5 prove, payer's device: ring size (log scale) 128, 18.2 prove, payer's device: ring size (log scale) 256, 26.2 prove, payer's device: ring size (log scale) 512, 40.3 verify, settlement node: ring size (log scale) 2, 1.8 verify, settlement node: ring size (log scale) 4, 2.1 verify, settlement node: ring size (log scale) 8, 2.4 verify, settlement node: ring size (log scale) 16, 2.6 verify, settlement node: ring size (log scale) 32, 3.1 verify, settlement node: ring size (log scale) 64, 3.7 verify, settlement node: ring size (log scale) 128, 4.7 verify, settlement node: ring size (log scale) 256, 6.4 verify, settlement node: ring size (log scale) 512, 9.3 prove, payer's device verify, settlement node
The wire grows 224 B per doubling and verification stays under 10 ms to a ring of 512. Proving is what grows, so the payer caps the ring, not the node. Artifact: defmi.json.

The asymmetry is the point. The wire grows 224 B per doubling and verification stays under 10 ms up to a ring of 512; what grows is proving, on the payer's own device. So the payer caps the anonymity set, not the settlement node. A payee scans the pool at 0.063 ms per note, one scalar multiplication, and that is the only cost proportional to the pool. One-time points do not directly name a permanent address; metadata can still link payments.

In the Rust port the same DvP over two note rails settles in 51.9 ms at a ring of 8 with a 5,476 B package, against 50,827 B for the retired implementation, the Bulletproofs-against-bit-decomposition effect again. The build column there includes the whole 3-of-7 FROST ceremony, so it is an end-to-end construction cost.

What the ring is actually worth

A ring is an anonymity set only if the decoys are indistinguishable from the real note. A real spend is of a note the spender was just paid, because that is what settling is. Uniform decoys are mostly older, so an observer who guesses the newest member of the ring beats 1/R for free. The strategy was stated before the run.

decoysringtraffic 0traffic 4traffic 16nominal 1/R
uniform161.0000.5470.3590.062
recent161.0000.0620.0620.062

artifact: rings.json (host-a) · "traffic" is other settlements landing between being paid and paying

Observer's chance of naming the spent note, ring of 16 0 0.2 0.4 0.6 0.8 1 0 2.5 5 7.5 10 12.5 15 17.5 other settlements between being paid and paying probability uniform decoys: other settlements between being paid and paying 0, 1 uniform decoys: other settlements between being paid and paying 4, 0.55 uniform decoys: other settlements between being paid and paying 16, 0.36 recency-matched decoys: other settlements between being paid and paying 0, 1 recency-matched decoys: other settlements between being paid and paying 4, 0.06 recency-matched decoys: other settlements between being paid and paying 16, 0.06 nominal 1/16: other settlements between being paid and paying 0, 0.06 nominal 1/16: other settlements between being paid and paying 16, 0.06 uniform decoys recency-matched decoys nominal 1/16
With no traffic the ring is worth nothing under either rule. With sixteen settlements of traffic, recency-matched decoys reach the nominal figure exactly; uniform decoys leave the observer at 5.8× it. Artifact: rings.json (host-a).
An anonymity set on a note rail is other people's traffic, and the decoy rule only decides how much of it the ring can use.

With no other traffic the ring is worth nothing at any size under either rule. With sixteen settlements of traffic, recency-matched decoys (ring_recent, thirty lines) bring the observer to exactly the nominal figure where uniform decoys left it at 5.8× that.

A state root that stopped growing with history

The ring benchmark found verification climbing with the pool, and nothing in check_spend is proportional to the pool. The state root was: it re-compressed every note ever held and re-sorted every spent serial, four times a settlement. At 4,096 notes that was 61.3 ms of bookkeeping beside 8 ms of cryptography. Notes are only appended and serials only inserted, so the root is now a running hash extended once per change: 0.15 µs, flat, against 15.3 ms by walking.

Payment versus payment, across two ledgers

Two ledgers that share no state have no function that moves both legs, and fair exchange without a third party is impossible in general, so something has to pass between them. A hash lock passes a preimage that ends up in the clear on both ledgers, so anyone reading both can join the legs. What is used instead is an adaptor signature: the first mover's claim on one ledger hands the second mover a scalar, and what each ledger records is an ordinary signature with nothing in common with the other's.

1. Bob   -> Alice : Y = g^y
2. Alice           prepares leg A; her money leaves her account
   Alice -> Bob   : a pre-signature over "leg A", adapted to Y
3. Bob             prepares leg B, and sends his own pre-signature
4. Bob             claims on A  -> he is paid, and y becomes readable
5. Alice           reads A, recovers y, claims on B  -> she is paid

Bob draws the secret and moves first, so Bob is never at risk. Alice is exposed in exactly one window, between steps 4 and 5, and safe if and only if the gap between the two deadlines covers her reaction. That gap is this arrangement's Herstatt risk. Measured on 32-bit rails: prepare a leg 1.29 ms, the first mover's claim 0.05 ms, the second mover's reaction 0.06 ms, unwind an expired leg 0.55 µs. The cryptography is not what puts the money at risk; the deadline gap is set by the settlement finality of the two ledgers, three to five orders of magnitude larger. A deployment that wants a short window should shop for finality rather than for faster proofs.

On one chain, two DeFMI venues can also be called from a single transaction: atomic for free, zero exposure, and the transaction is the link. The adaptor arm costs four times the calls and 3.0% more verification, and buys unlinkability only when handles are derived per venue. The table on the zkPI page shows the observer at 1.000 with one name everywhere and at 1/k with per-venue handles, with the cryptography unchanged.

Agreeing with the book of record

Under Japan's book-entry regime this ledger cannot be the register: title rests on the record the transfer agent and account management institutions keep. So DeFMI is a mirror, and reconciliation is the price of that arrangement. It is one line of algebra: commitments multiply, so the product of the balances is a commitment to their sum; divide out the register's figure under the asset tag and what remains must be a pure power of h. Proving knowledge of that exponent proves the totals agree and says nothing else. The proof is 96 B whatever the ledger holds; checking 4,096 positions takes 0.65 ms. A 3-of-7 quorum assembles the same statement from shares of the aggregate blinding in 4.5 ms, so nobody holds the sum, which matters because whoever holds it could open the whole ledger.

A break is pass or fail. Finding where needs subtotals, and every subtotal claimed becomes public down to one position, which is a balance. This is the only operation in DeFMI that discloses on purpose, and it is the one you reach for on the day something is wrong. A register that keeps a figure per position localises for free instead.

Showing an auditor one slice

A note wallet is a view key and a spend key. The scoping is not in the key; it is in the address. A scope (an instrument, a quarter, a mandate) derives its own address from the wallet's seeds by hashing, and notes sent there are found by that scope's view key and by nothing else. A grant is 0.08 ms to issue and 0.07 ms to check; the holder of a scope reaches exactly the fifth of the pool it was granted and recovers no serials, because a serial needs the spend key.

Three limits do not go away. A grant cannot be taken back, so revocation is moving to the next scope, which is an act of address management, and Rolling makes the schedule an object rather than a discipline. A view key is incoming only; outflows are a different disclosure. And scoping is only as fine as the payers cooperate: a payer using last quarter's address puts the note in last quarter's scope, which arrived_off_schedule detects and nothing prevents.

Who is allowed to hold a handle

Anyone can pick a handle. What needs permission is being vetted, and the vetting roll holds sealed envelopes C = a·G + r·h, not handles, so adding one reveals a uniformly random point and the timing of an onboarding says nothing about which entry is theirs. Membership is proved one-out-of-many over a fixed group; a fresh ring per proof would look more private and be less, because overlapping rings intersect. A Schnorr proof pins the handle to the one the envelope determines, so a vetting yields one handle and not a family. Roll::vetted() and Roll::crowd() are public so anyone can check the anonymity set's size; hiding the vetting event and proving the size of the crowd are the same information, and the design chooses the second.

Vetting proof against crowd size 0 2 4 6 8 10 4 8 16 32 64 128 crowd (log scale) ms verify, ms: crowd (log scale) 4, 0.66 verify, ms: crowd (log scale) 8, 0.96 verify, ms: crowd (log scale) 16, 1.33 verify, ms: crowd (log scale) 32, 1.82 verify, ms: crowd (log scale) 64, 2.61 verify, ms: crowd (log scale) 128, 3.84 prove, ms: crowd (log scale) 4, 0.81 prove, ms: crowd (log scale) 8, 1.25 prove, ms: crowd (log scale) 16, 1.88 prove, ms: crowd (log scale) 32, 2.91 prove, ms: crowd (log scale) 64, 4.9 prove, ms: crowd (log scale) 128, 9.02 verify, ms prove, ms
Predicted to double with the crowd; grew about 1.4× per doubling instead, the batched multi-scalar multiplication showing through. That miss moved the default crowd from 16 to 128. Artifact: vetting.json.

Verification was predicted to double with the crowd and grows about 1.4× instead, the batched multi-scalar multiplication showing through. At a crowd of 128 the check is 3.84 ms and the proof 1,676 B, so 128 is the default (vetting::CROWD). A crowd of thousands needs a Merkle tree inside a circuit, a different proof system from the sigma protocols and Bulletproofs here.

The Avalanche L1

The deployed execution path is a dedicated non-EVM custom VM in avalanche/defmivm/, launched by AvalancheGo over RPCChainVM protocol 45. Native transitions: asset registration, account opening, pre-authorised reserves, atomic multi-leg settlement. The full acceptance gate covers account-free notes, pre-authorised reserves, seven-party MPC zkPI, atomic multi-RFQ settlement, shared legal-entity caps, restart recovery and state-root agreement.

propertyobserved
local AvalancheGo processes5
accepted heights1, 2, 3, 4, 5
settlement acceptance207.3 ms
two account openings424.6 ms
node restart and state recovery1454.7 ms
same state root before and after restartyes

artifacts: avalanche_l1_acceptance.json, avalanche_qomm_full_acceptance.json (two RFQs in one atomic batch at height 53, claim materialisation at height 54, validator restart with identical roots)

The run uses five processes on one host. It proves native consensus acceptance, idempotent crash recovery and state-root agreement. It does not prove five independent organisations or public-network readiness.

What is still missing

  • Validators verify the complete submitted proof suite but do not re-run the private MP-SPDZ transcript or prove the share-to-proof handoff. Removing that committee trust needs a proof of the whole private computation and a separate consensus benchmark.
  • Admission, pledge, limit and payment are not one atomic event.
  • The current nullifier does not give the sender the spending secret. Timing, candidate sets and reservation metadata can still link activity.
  • Adding a second settlement currency is an account-opening decision, not a code change, because a tagged cash leg opens against the currency the account is denominated in.