Skip to content
research
·4 min read

The Mathematics of Private Notes

A deep dive into Xythum's cryptographic note structure — the shielded UTXOs that power private trading on public blockchains.

The Mathematics of Private Notes

At the heart of every privacy-preserving protocol lies a fundamental challenge: how do you prove ownership and validity of assets without revealing what those assets are? In Xythum, the answer is the Note — our shielded UTXO primitive.

What is a Note?

A Note is the basic unit of value in Xythum's dark pool. It represents ownership of an asset amount but remains encrypted on-chain, revealing nothing about its contents.

The Note Structure

Every Note in Xythum contains the following components:

Note:={assetId,value,secret,recipientKey}\text{Note} := \{\text{assetId}, \text{value}, \text{secret}, \text{recipientKey}\}

Where:

  • assetId — The token contract address (hidden on-chain)
  • value — The amount of the asset (hidden on-chain)
  • secret — A random blinding factor
  • recipientKey — The owner's public spending key

Key Derivation

User keys in Xythum follow a hierarchical structure derived from a single root secret:

skroot:=H("xythum.root"signature)sk_{root} := H(\text{"xythum.root"} \mathbin{\|} \text{signature})

From this root, we derive the master spending and viewing keys:

skspend:=H("xythum.spend"skroot)sk_{spend} := H(\text{"xythum.spend"} \mathbin{\|} sk_{root}) skview:=H("xythum.view"skroot)sk_{view} := H(\text{"xythum.view"} \mathbin{\|} sk_{root})
key-derivation.ts
interface DarkAccount {
  skRoot: Uint8Array;
  skSpend: Uint8Array;
  skView: Uint8Array;
  shieldingKey: Uint8Array;
}
 
function deriveKeys(signature: Uint8Array): DarkAccount {
  const skRoot = hash("xythum.root", signature);
  const skSpend = hash("xythum.spend", skRoot);
  const skView = hash("xythum.view", skRoot);
  const shieldingKey = hash("xythum.shieldingKey", skView);
  
  return { skRoot, skSpend, skView, shieldingKey };
}

Nullifier Generation

To prevent double-spending, each Note has a unique nullifier that is revealed when spent. Critically, the nullifier reveals nothing about which Note it corresponds to.

For the ii-th UTXO, the nullifier is computed as:

nullifieri:=H("xythum.nullifier"skspendi)\text{nullifier}_i := H(\text{"xythum.nullifier"} \mathbin{\|} sk_{spend} \mathbin{\|} i)

Security Property

Given a nullifier, an adversary cannot determine which on-chain Note it corresponds to. This breaks the link between deposit and withdrawal.

Note Commitments

Rather than storing Notes on-chain directly, we publish commitments — cryptographic hashes that bind to the Note's contents without revealing them:

commitment:=H(assetIdvaluesecretrecipientKey)\text{commitment} := H(\text{assetId} \mathbin{\|} \text{value} \mathbin{\|} \text{secret} \mathbin{\|} \text{recipientKey})

The commitment is what gets stored in the on-chain Merkle tree. To spend a Note, the owner proves:

  1. They know a valid Note preimage for a commitment in the tree
  2. They possess the spending key for that Note
  3. The nullifier they're revealing corresponds to that Note

All without revealing the Note contents.

Balance Conservation

For any valid transaction consuming inputs and producing outputs:

ivalue(notein,i)+Δpublic=jvalue(noteout,j)\sum_{i} \text{value}(\text{note}_{in, i}) + \Delta_{\text{public}} = \sum_{j} \text{value}(\text{note}_{out, j})

Where Δpublic\Delta_{\text{public}} represents value entering from (positive) or exiting to (negative) the public chain.

ZK Enforced

This balance equation is verified inside the ZK circuit. The prover must demonstrate conservation without revealing any actual values.

Threshold Decryption

Notes are encrypted using a hybrid scheme that enables both personal and authorized decryption:

PartyCan Decrypt?Method
Note Owner✅ YesUsing their shielding key
Anyone Else❌ NoEncrypted
DAO Quorum✅ ConditionalThreshold decryption (m-of-n)

This enables compliant privacy — users have full privacy, but a quorum of authorized parties can decrypt notes under legal order.

Implementation

Here's a simplified implementation of Note creation:

note.rs
pub struct Note {
    pub asset_id: [u8; 32],
    pub value: u64,
    pub secret: [u8; 32],
    pub recipient_key: PublicKey,
}
 
impl Note {
    pub fn commitment(&self) -> [u8; 32] {
        hash(&[
            &self.asset_id,
            &self.value.to_le_bytes(),
            &self.secret,
            &self.recipient_key.as_bytes(),
        ])
    }
    
    pub fn nullifier(&self, sk_spend: &[u8], index: u64) -> [u8; 32] {
        hash(&[
            b"xythum.nullifier",
            sk_spend,
            &index.to_le_bytes(),
        ])
    }
}

What's Next?

In our next article, we'll explore how these Notes interact with the Solver Network to enable private order matching and settlement.


Xythum — Protocol-agnostic dark liquidity for the sovereign trader.

Xythum Research
Xythum Research@xythum

Building privacy infrastructure for the future of finance.

Related Articles

research

924 8E0 E707F78C9B 154AA530 CEBF 3CA6 761058C

Learn how Xythum derives cryptographic keys from a single root secret, separating spending, viewing, and shielding capabilities for maximum security.

·2 min read
research

41587B09 DEA311 C32D6 CF2 A5D6E

Understand the two types of shielded assets in Xythum—Notes for personal balance management and Memos for programmable payments.

·3 min read
engineering

ED 60C8D8AF7 9BE6DB82 A6EA58163 B97 EA454D88

A technical walkthrough of the zero-knowledge circuits that power Xythum's shielded transactions—from entry to exit.

·4 min read