Skip to main content

Note Disclosure

A note disclosure key is a compact, shareable string that lets you prove the exact contents of a shielded note — value, asset, and commitment — to any third party, without granting them any ability to spend the note.

This is Orbinum's mechanism for voluntary transparency: you remain private by default, but you can selectively reveal a note when you choose to.


Background: what a note is

A note commits to four fields — value, asset_id, owner_pk_ax and blinding — hashed into the single value stored on-chain:

commitment = Poseidon4(value, asset_id, owner_pk_ax, blinding)

See Notes & the Merkle Forest for what each field holds.

Because the commitment is a one-way hash, anyone can see it on-chain but no one can determine what it contains. A disclosure key solves exactly that: it encodes the four inputs so any holder can verify the hash and confirm the note's contents.


The disclosure key format

A disclosure key is a string with the prefix orbdisc: followed by a base64url-encoded JSON payload:

orbdisc:<base64url(JSON)>

The JSON payload (version 1):

{
"v": 1,
"c": "0x2d0aba56a9fb579a007a1980a6164f248682546aa460ea46956be66fabeb8ea7",
"val": "0x152d02c7e14af6800000",
"aid": "0x0",
"opk": "0x1d4a09a1ab9ffff52fbef32c393617d70c03c92835bc6331fbb4a45e381d9735",
"bld": "0x2dcbb6f4d229a19c91e1a4ee057bafa3a96573fa36559644864c811b13ae8d62"
}

All numeric values use little-endian hex — the canonical representation of BN254 field elements as used by the ZK circuit.

Commitment endianness

The same commitment appears in two forms in the system. On-chain (Substrate, block explorers) it is stored as big-endian H256. Inside the circuit and in the disclosure key it is stored as a little-endian BN254 field element. They are byte-reversed versions of each other.

Example:

On-chain (big-endian):   0xa78eebab6fe66b9546ea60a46a548286244f16a680197a009a57fba956ba0a2d
Disclosure key (LE): 0x2d0aba56a9fb579a007a1980a6164f248682546aa460ea46956be66fabeb8ea7

Both represent the same commitment.


Two separate key systems

The owner public key is not your EVM wallet address. Notes are owned by a Baby JubJub keypair derived from your wallet signature, because BJJ scalar multiplication costs ~3,000 R1CS constraints inside a circuit where secp256k1 costs hundreds of thousands.

That derivation — and why a viewing key can be shared without sharing spending authority — is documented in Keys & Identity.

The key in a commitment is a stealth key

The opk in a disclosure key is not the recipient's published identity. Each received note carries a distinct stealth owner key derived from the sender's ECDH shared secret.

Consequence for disclosure: two keys issued by the same person for two received notes show two unrelated opk values, and an auditor cannot link them to each other or to that person's global key. A disclosure key proves this note's contents — not "these notes belong to the same person". See Note Discovery.


Cryptographic verification

When a recipient decodes a disclosure key, they recompute the commitment from the revealed inputs and compare it against the embedded "c" field:

recomputed = Poseidon4(value, asset_id, owner_pk_ax, blinding)
valid = (recomputed === commitment)

This is a cryptographic proof of knowledge of the preimage. A forged key with an incorrect value will produce a different hash and fail verification. The decodeNoteDisclosureKey function in @orbinum/protocol returns null for any key that fails this check — verification needs no custody access.


How to generate and verify a disclosure key

Generating a key (note owner)

import { createNoteDisclosureKey } from '@orbinum/protocol';

const key = createNoteDisclosureKey(note);
// "orbdisc:eyJ2IjoxLCJjIjoi..."

The note must be a ZkNote — a decrypted note object held by the owner. The function extracts value, assetId, ownerPk, and blinding from the note and serialises them as a disclosure key. The spending key is never included.

Verifying a key (any third party)

import { decodeNoteDisclosureKey } from '@orbinum/protocol';

const disclosure = decodeNoteDisclosureKey(key);

if (disclosure === null) {
// key is malformed, tampered, or contains an incorrect preimage
} else {
console.log('value :', disclosure.value); // bigint
console.log('assetId :', disclosure.assetId); // bigint
console.log('commitment verified ✓');
}

No network call is required. Verification is a local Poseidon hash computation.