Madison meets Satoshi

Auxiliary Precautions in Code:
Blockchain Governance for Sovereign Wealth Funds

Madison meets Satoshi

Return to Main Page

Provisions Protocol Calculator

Step-by-step verification of proof of reserves & solvency

⚠️ Educational Purpose Only This calculator uses simplified modular arithmetic to demonstrate concepts from the Provisions paper (Dagher et al., CCS 2015). Real implementations use elliptic curve cryptography (secp256k1). Do not use for production systems.

Protocol Overview

What is Provisions?

Provisions is a privacy-preserving proof of solvency protocol for cryptocurrency exchanges. It allows an exchange to prove they control enough assets to cover all customer liabilities without revealing individual account balances, the total holdings, or which blockchain addresses they control.

The Three Protocols

Protocol 1 - Proof of Assets: The exchange proves control of Bitcoin addresses using an anonymity set, without revealing which addresses are theirs.

Protocol 2 - Proof of Liabilities: The exchange commits to each customer's balance, proves each is positive, and sums them homomorphically.

Protocol 3 - Proof of Solvency: The exchange proves that total assets ≥ total liabilities using the commitments from Protocols 1 and 2.

Key Cryptographic Primitives

This calculator focuses on demonstrating:

// Pedersen Commitment
com = gm · hr // Commitment to message m with randomness r

// Homomorphic Property
com(a) · com(b) = com(a + b) // Multiply commitments = add messages

// Binary Decomposition for Range Proofs
Balance = Σ xk · 2k // Each bit committed separately
M

Maxwell's Merkle Tree Method

Historical Context

In 2014, Greg Maxwell proposed the first cryptographic proof of liabilities protocol, summarized by Zak Wilcox. This method uses a Merkle hash tree to allow customers to verify their balances are included in the exchange's total liabilities. While elegant and using familiar Bitcoin cryptography, it has significant privacy limitations that Provisions later addressed.

How It Works: The Merkle Tree Structure

The exchange builds a binary Merkle tree where each leaf represents a customer account:

// Leaf Node Structure
Balance = Customer's account balance
Hash = H(Balance | CID | Nonce)

// Internal Node Structure
Sum = leftChild.Balance + rightChild.Balance
Hash = H(Sum | leftChild.Hash | rightChild.Hash)

// Root Node
TotalLiabilities = Sum of all customer balances
RootHash = Published publicly
Try It: Build a Maxwell Merkle Tree

Enter customer balances (comma-separated) to see the Merkle tree structure.

Customer Verification in Maxwell

To verify their balance is included, a customer receives:

// Customer receives from exchange:
1. Their nonce (to open their leaf commitment)
2. Sibling nodes on the path from leaf to root

// Customer verification:
1. Recompute leaf hash: H(myBalance | myCID | nonce)
2. Walk up tree using sibling hashes
3. Compare final hash with published root
4. If match → balance is included ✓
Try It: Verify a Customer's Inclusion

Select a customer to see their verification path (Merkle proof).

⚠️ Privacy Limitations

The Maxwell method reveals significant information:

What Gets Leaked
1. Total Liabilities: The root node's sum is public, revealing the exchange's total customer deposits.

2. Sibling Balances: Each verification reveals the exact balance of the sibling leaf node.

3. Subtree Totals: Each internal sibling node reveals total holdings of all customers in that subtree.

4. Customer Count: Tree structure reveals approximate number of customers.

These privacy concerns led exchanges like Kraken to prefer trusted auditors over cryptographic proofs, which motivated the development of Provisions.

1

Pedersen Commitment Basics

Understanding Pedersen Commitments

A Pedersen commitment allows you to commit to a value m in a way that is:

Hiding: The commitment reveals nothing about m
Binding: You cannot change m after committing
Homomorphic: Operations on commitments correspond to operations on values

// The commitment formula
com = gm · hr mod p

// Where:
g, h = generators (public parameters)
m = message (the value being committed)
r = random blinding factor
p = prime modulus
Try It: Create a Pedersen Commitment

Enter a value to commit to and a random blinding factor. We use simplified parameters for demonstration.

// Simplified parameters (educational only)
p = 997 // Prime modulus
g = 5 // Generator 1
h = 7 // Generator 2 (unknown discrete log relationship to g)
Why Two Generators?

The security of Pedersen commitments requires that nobody knows the discrete logarithm relationship between g and h (i.e., no one knows x such that h = gx). In Provisions, g is the standard Bitcoin generator, and h is derived by hashing the string "Provisions" - ensuring no one can know the relationship.

2

Binary Decomposition for Range Proofs

Why Binary Decomposition?

To prove a balance is positive (not negative), Provisions decomposes each balance into its binary representation. By proving each bit is either 0 or 1, the protocol ensures the committed value is within a valid range [0, 2m-1].

Bitcoin uses satoshis (1 BTC = 108 satoshis), with a maximum supply of 21M BTC. This requires m = 51 bits to represent any valid balance.

// Binary representation
Balance = Σk=0m-1 xk · 2k

// Example: 42 in binary
42 = 0·20 + 1·21 + 0·22 + 1·23 + 0·24 + 1·25
42 = 0 + 2 + 0 + 8 + 0 + 32 = 101010
Try It: Decompose a Balance
3

Committing to Each Bit

Per-Bit Pedersen Commitments

Each bit xk of the balance gets its own Pedersen commitment with fresh randomness rk. The exchange then provides a zero-knowledge proof that each committed value is binary (0 or 1).

// Commitment to bit k
yk = gxk · hrk

// Where xk ∈ {0, 1}
If xk = 0: yk = g0 · hrk = hrk
If xk = 1: yk = g1 · hrk = g · hrk
Try It: Create Bit Commitments

Enter a small balance (0-255 for demonstration) and see the individual bit commitments created.

4

Computing the Balance Commitment

Homomorphic Balance Reconstruction

The magic of Pedersen commitments: we can compute a commitment to the full balance by raising each bit commitment to the appropriate power of 2 and multiplying them together.

// The balance commitment formula
y = Πk=0m-1 (yk)2k

// This equals:
y = gΣ xk·2k · hΣ rk·2k
y = gBalance · hr

// Where r = Σ rk·2k is the combined randomness
Try It: Compute Balance Commitment

Building on the bit commitments, let's compute the full balance commitment homomorphically.

5

Client-Side Verification

What the Client Receives

After the exchange publishes the proof of liabilities, each customer receives (privately):

// Private data given to each client:
- Their username
- Their combined randomness r = Σ rk·2k
- A nonce to open their CID commitment

// Public data (visible to everyone):
- List of customer ID commitments (CIDi)
- Bit commitments for each customer (yi,k)
- Zero-knowledge proofs (Πi)
- Total liabilities commitment (ZLiabilities)
Verification Steps

Each client performs the following verification:

// Step 1: Locate yourself in the list
Verify: username = open(CIDi, nonce)

// Step 2: Verify your balance commitment
Compute: y = Πk (yk)2k
Verify: y == gBalance · hr

// Step 3: (Optional) Verify total liabilities
Verify: ZLiabilities = Πi yi
Try It: Verify Your Balance

Simulate client verification. Enter your known balance and the randomness the exchange provided.

6

Homomorphic Liability Summation

Adding Commitments = Adding Values

The homomorphic property of Pedersen commitments allows computing a commitment to the sum of all liabilities by simply multiplying the individual balance commitments.

// Total liabilities commitment
ZLiabilities = Πi=1c yi

// This equals:
ZLiabilities = gΣ Balancei · hΣ ri
ZLiabilities = gTotalLiabilities · hR

// The exchange never reveals TotalLiabilities!
Try It: Sum Multiple Account Commitments

Enter multiple account balances (comma-separated) to see how the homomorphic sum works.

7

Full Protocol Demonstration

Complete Proof of Liabilities Flow

This demonstration shows the complete flow: an exchange creates commitments for multiple customers, computes the total liability commitment, and then we verify one customer's balance.

Configure the Exchange

Maxwell vs Provisions Comparison

Side-by-Side Comparison
Property Maxwell (Merkle Tree) Provisions (Pedersen)
Total Liabilities ❌ Public (in root) ✓ Hidden
Individual Balances ⚠️ Siblings revealed ✓ Fully hidden
Exchange Addresses ❌ Must be revealed ✓ Anonymity set
Customer Count ⚠️ Approximate (tree size) ✓ Hidden
Cryptography Used Hash functions (SHA-256) Elliptic curves + ZK proofs
Proof Size ✓ O(log n) per customer ⚠️ O(n) total
Verification Cost ✓ Fast (hash only) ⚠️ Slower (EC operations)
Range Proofs ❌ None (negative balances possible) ✓ Built-in (binary decomposition)
Collusion Detection ❌ Hard to detect ✓ Optional extension
Interactive Comparison Demo

See what information is leaked by each method for the same set of customers.

When to Use Each
Maxwell is Appropriate When:
• Privacy is not a primary concern
• Simpler cryptography is preferred
• Quick implementation is needed
• Users are technically sophisticated
• Exchange size is already public
Provisions is Appropriate When:
• Customer privacy is paramount
• Exchange addresses must stay hidden
• Competitive information (total deposits) is sensitive
• Negative balance attacks must be prevented
• Multiple exchanges need collusion detection
The Tradeoff Summary

Maxwell's approach represents the classic tradeoff: simplicity and efficiency vs privacy and security. The Merkle tree approach is elegant and uses familiar Bitcoin primitives, but reveals too much information for many exchanges to be comfortable with.

Provisions solves this by introducing homomorphic commitments and zero-knowledge proofs, enabling the same verification guarantees while keeping all sensitive data hidden. The cost is computational complexity and larger proofs, but the paper demonstrates this is practical even for the largest exchanges.

// The fundamental insight:

Maxwell: hash(balance) → verifiable but visible
Provisions: commit(balance) → verifiable AND hidden

// Both prove: "my balance is in the total"
// Only Provisions hides: what that balance is