A map key, a branch on a secret, a hash of a 4-digit PIN: on Midnight they all leak information, and none of it is obvious from reading the code. Compact X-Ray asks the official compiler, and shows every private value that reaches the chain, how much of it, and through which line.
X-Ray does not re-implement Compact's privacy rules, so it cannot drift from them. It uses the compiler as the oracle.
disclose()A copy of your contract with each disclose( blanked out, character for character, so every line and column stays where it was.
It now rejects each place a private value would reach the public side, naming the value, how much of it leaks, and the path it takes.
Private inputs on one side, public state and token operations on the other, one line per disclosure, coloured by how much leaks.
Run against the compiler (0.31.1) and a local Midnight network (ledger 8). Some of it contradicts the docs.
disclose() to return a witness value, but a probe contract showed the returned value is not in the transaction on chain; it goes back to the calling DApp.m.member(k) inside an assert must still be disclosed.if (secret > 5) {a = 1} else {a = 2} reveals the decision; the compiler tracks control flow.disclose() calls in the official examples.Mostly inside assert comparisons, which only reveal pass/fail. --deep finds them by removing one at a time.The same analysis behind every surface.
Report, init a privacy policy, check it, diff two versions. Text, JSON, Markdown, SARIF, HTML.
Fails a pull request that makes a private value newly public, with findings inline on the diff.
On save: each disclosing line marked, warnings for risky patterns, and what each circuit publishes above it.
# .github/workflows/privacy.yml
- uses: actions/checkout@v4
- uses: elaris-xyz/Compact-X-Ray@v0
with:
contracts: contracts/**/*.compact
compact-version: 0.31.1
- uses: github/codeql-action/upload-sarif@v3
if: always()
with: { sarif_file: compact-xray.sarif }
Every example contract from midnightntwrk/midnight-examples, analysed when this site was built.
When the contract is deployed, anyone watching the chain learns a fingerprint (hash) of the private value localSk in player1; and a fingerprint (hash) of the private value localSk, _x1 and _x2 in board1; and the exact private value localSetBoard in board1State.
When someone calls add, anyone watching the chain learns the result of an addition of num1 and num2 in result.
When the contract is deployed, anyone watching the chain learns a fingerprint (hash) of the private value localSk in organizer; and the exact value of candidateA in candidate0; and the exact value of candidateB in candidate1.
When someone calls storeMessage, anyone watching the chain learns the exact value of newMessage in message.
When the contract is deployed, anyone watching the chain learns the exact value of minimum in minimumBid.
When the contract is deployed, anyone watching the chain learns a fingerprint (hash) of _secret in organizer; and the exact value of fee in entryFee; and the exact value of partySize in maxListSize.
When someone calls publishMessageHash, anyone watching the chain learns a fingerprint (hash) of the private value secretMessage in messageHash.
When the contract is deployed, anyone watching the chain learns a fingerprint (hash) of the private value localSk in auctionOrganizer; and the exact value of maxBidCount in maxBids; and the exact value of address in organizerAddress.
When someone calls mintAndReceive, anyone watching the chain learns the exact value of amount through mintUnshieldedToken() (the amount of the unshielded token being minted).
When the contract is deployed, anyone watching the chain learns a fingerprint (hash) of the private value getUserSecret in contractAdmin.