feat: 7 FHE demo programs (dark pool, compliance, MM, auction, voting, NAV, proof)

This commit is contained in:
2026-04-01 17:54:53 -07:00
parent 362536209e
commit a378408ba2
8 changed files with 1385 additions and 0 deletions
+102
View File
@@ -0,0 +1,102 @@
# FHE Demo Programs
Seven runnable demos demonstrating Fully Homomorphic Encryption use cases for digital securities.
## Prerequisites
```bash
cd ~/work/lux/fhe
go build ./... # verify the fhe package compiles
```
## Demos
### 1. Dark Pool (Encrypted Order Matching)
Traders submit encrypted limit orders. The matching engine compares bids against asks homomorphically. Only matched fills are decrypted.
```bash
go run ./cmd/demos/darkpool
```
### 2. Programmable Compliance
Verifies SEC diversification rules on an encrypted portfolio: no single position > 25%, total exposure < 80%. Only the boolean compliance result is revealed.
```bash
go run ./cmd/demos/compliance
```
### 3. Private Market Making
Three market makers submit encrypted bid/ask quotes. Best bid (highest) and best ask (lowest) are selected via encrypted comparisons. Losing quotes stay private.
```bash
go run ./cmd/demos/marketmaker
```
### 4. Encrypted Sealed-Bid Auction
Five participants submit encrypted bids. The winner is determined via tournament-style max comparisons. Only the winning bid is decrypted.
```bash
go run ./cmd/demos/auction
```
### 5. Private Shareholder Voting
100 shareholders vote encrypted yes/no. Votes are tallied using a ripple-carry adder (XOR + AND gates). Only the final count is decrypted.
```bash
go run ./cmd/demos/voting
go run ./cmd/demos/voting -voters 50 -yes 30
```
### 6. Confidential NAV
An ETF's encrypted holdings (10 positions with share counts and prices) are used to compute NAV = sum(shares * price) / totalShares. Only the final NAV is revealed.
```bash
go run ./cmd/demos/nav
```
### 7. Compliance Proof
Proves two conditions on an encrypted portfolio: max position < 25% and no sanctioned counterparty. Outputs plaintext booleans while inputs stay encrypted.
```bash
go run ./cmd/demos/proof
```
## Run All
```bash
for d in darkpool compliance marketmaker auction voting nav proof; do
echo "=== $d ==="
go run ./cmd/demos/$d
echo
done
```
## Architecture
All demos use the `github.com/luxfi/fhe` package with boolean circuit evaluation:
- **Parameters**: `PN10QP27` (128-bit security, N=1024)
- **Boolean gates**: `Evaluator` with `AND`, `OR`, `XOR`, `NOT`, `ANDNY`, `MAJORITY` + bootstrapping
- **Encryption**: `Encryptor.Encrypt(bool)` encrypts individual bits
- **Decryption**: `Decryptor.Decrypt(*Ciphertext)` decodes bits (secret key holder only)
- **Integers**: 8-bit values represented as `[8]*Ciphertext` (bit-decomposed, LSB first)
- **Comparison**: MSB-first bitwise less-than circuit using `ANDNY` + `XOR` + `AND` + `OR`
- **Addition**: Ripple-carry full adder using `XOR` + `MAJORITY`
FHE operations run without the secret key. The evaluator uses bootstrap keys (public) for noise management via programmable bootstrapping after each gate.
## Performance (Apple M-series, PN10QP27)
- **Key generation**: ~600ms-1s (one-time)
- **Encryption**: ~5ms per 8-bit value (8 bit encryptions)
- **Single gate** (AND, OR, XOR): ~400-600ms per gate
- **8-bit comparison** (lt/gt/ge): ~12-16s (24 gates: 8x ANDNY + 8x XOR + 7x AND + 7x OR)
- **8-bit addition**: ~10-13s per add (24 gates: 8x XOR + 8x XOR + 8x MAJORITY)
- **Vote tally** (1 ballot): ~5.8s (14 gates: 7x XOR + 7x AND for 7-bit accumulator)
+161
View File
@@ -0,0 +1,161 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// Command auction demonstrates an encrypted sealed-bid auction using FHE.
//
// Five participants submit encrypted sealed bids. The system determines the
// winner via boolean circuit max computation. Only the winning bid is decrypted.
//
// Usage:
//
// go run ./cmd/demos/auction
package main
import (
"fmt"
"os"
"time"
"github.com/luxfi/fhe"
)
const bidBits = 8
type bid struct {
participant string
amount uint8
encAmount [8]*fhe.Ciphertext
}
func main() {
fmt.Println("=== FHE Encrypted Sealed-Bid Auction Demo ===")
fmt.Println("Determine winner without revealing losing bids")
fmt.Println()
// Setup
fmt.Print("Initializing FHE... ")
t0 := time.Now()
params, err := fhe.NewParametersFromLiteral(fhe.PN10QP27)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
keygen := fhe.NewKeyGenerator(params)
sk, _ := keygen.GenKeyPair()
bsk := keygen.GenBootstrapKey(sk)
enc := fhe.NewEncryptor(params, sk)
dec := fhe.NewDecryptor(params, sk)
eval := fhe.NewEvaluator(params, bsk)
fmt.Printf("done (%v)\n\n", time.Since(t0))
// Five sealed bids
bids := []bid{
{participant: "Participant 1", amount: 84},
{participant: "Participant 2", amount: 92},
{participant: "Participant 3", amount: 97},
{participant: "Participant 4", amount: 83},
{participant: "Participant 5", amount: 88},
}
fmt.Println("Sealed bids (shown for demo verification only):")
for _, b := range bids {
fmt.Printf(" %s: $%d\n", b.participant, b.amount)
}
fmt.Println()
// Encrypt
fmt.Println("Encrypting sealed bids...")
t0 = time.Now()
for i := range bids {
bids[i].encAmount = encryptByte(enc, bids[i].amount)
}
fmt.Printf("Encryption done (%v)\n\n", time.Since(t0))
// Tournament: find max via pairwise comparison
fmt.Println("Determining winner via encrypted comparisons...")
t0 = time.Now()
winnerIdx := 0
for i := 1; i < len(bids); i++ {
t1 := time.Now()
gtResult, err := gtEncrypted(eval, bids[i].encAmount, bids[winnerIdx].encAmount)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
isGreater := dec.Decrypt(gtResult)
elapsed := time.Since(t1)
if isGreater {
fmt.Printf(" %s > %s: true (%v) -> new leader\n",
bids[i].participant, bids[winnerIdx].participant, elapsed)
winnerIdx = i
} else {
fmt.Printf(" %s > %s: false (%v)\n",
bids[i].participant, bids[winnerIdx].participant, elapsed)
}
}
fmt.Printf("Tournament done (%v)\n\n", time.Since(t0))
// Decrypt winner only
winningAmount := decryptByte(dec, bids[winnerIdx].encAmount)
fmt.Println("--- Result ---")
fmt.Printf("Winner: %s @ $%d\n", bids[winnerIdx].participant, winningAmount)
fmt.Println("\nNote: losing bid amounts were never decrypted.")
}
func encryptByte(enc *fhe.Encryptor, v uint8) [8]*fhe.Ciphertext {
var bits [8]*fhe.Ciphertext
for i := 0; i < 8; i++ {
bits[i] = enc.Encrypt((v>>i)&1 == 1)
}
return bits
}
func decryptByte(dec *fhe.Decryptor, bits [8]*fhe.Ciphertext) uint8 {
var v uint8
for i := 0; i < 8; i++ {
if dec.Decrypt(bits[i]) {
v |= 1 << i
}
}
return v
}
// ltEncrypted computes a < b on 8-bit encrypted values (MSB-first comparison).
func ltEncrypted(eval *fhe.Evaluator, a, b [8]*fhe.Ciphertext) (*fhe.Ciphertext, error) {
var isLess, isEqual *fhe.Ciphertext
for i := bidBits - 1; i >= 0; i-- {
bitLt, err := eval.ANDNY(a[i], b[i])
if err != nil {
return nil, err
}
bitXor, err := eval.XOR(a[i], b[i])
if err != nil {
return nil, err
}
bitEq := eval.NOT(bitXor)
if isLess == nil {
isLess = bitLt
isEqual = bitEq
} else {
eqAndLt, err := eval.AND(isEqual, bitLt)
if err != nil {
return nil, err
}
isLess, err = eval.OR(isLess, eqAndLt)
if err != nil {
return nil, err
}
isEqual, err = eval.AND(isEqual, bitEq)
if err != nil {
return nil, err
}
}
}
return isLess, nil
}
// gtEncrypted computes a > b: same as b < a.
func gtEncrypted(eval *fhe.Evaluator, a, b [8]*fhe.Ciphertext) (*fhe.Ciphertext, error) {
return ltEncrypted(eval, b, a)
}
+219
View File
@@ -0,0 +1,219 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// Command compliance demonstrates programmable compliance checks on encrypted portfolios.
//
// A portfolio of 5 positions is encrypted. The system verifies SEC diversification
// rules (no single position > 25% of total, total exposure < 80%) entirely on
// encrypted data using boolean circuit comparisons. Only the boolean results are
// decrypted.
//
// Usage:
//
// go run ./cmd/demos/compliance
package main
import (
"fmt"
"os"
"time"
"github.com/luxfi/fhe"
)
const valueBits = 8 // 8-bit values (0-255)
type position struct {
name string
weight uint8 // percentage 0-100
}
func main() {
fmt.Println("=== FHE Programmable Compliance Demo ===")
fmt.Println("Verify SEC diversification on encrypted portfolio")
fmt.Println()
// Setup
fmt.Print("Initializing FHE... ")
t0 := time.Now()
params, err := fhe.NewParametersFromLiteral(fhe.PN10QP27)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
keygen := fhe.NewKeyGenerator(params)
sk, _ := keygen.GenKeyPair()
bsk := keygen.GenBootstrapKey(sk)
enc := fhe.NewEncryptor(params, sk)
dec := fhe.NewDecryptor(params, sk)
eval := fhe.NewEvaluator(params, bsk)
fmt.Printf("done (%v)\n\n", time.Since(t0))
// Portfolio
portfolio := []position{
{name: "AAPL", weight: 20},
{name: "MSFT", weight: 18},
{name: "NVDA", weight: 15},
{name: "GOOG", weight: 12},
{name: "AMZN", weight: 10},
}
var totalWeight uint8
for _, p := range portfolio {
totalWeight += p.weight
fmt.Printf(" %s: %d%%\n", p.name, p.weight)
}
fmt.Printf(" Total exposure: %d%%\n\n", totalWeight)
// Encrypt positions
fmt.Println("Encrypting portfolio positions...")
t0 = time.Now()
encWeights := make([][8]*fhe.Ciphertext, len(portfolio))
for i, p := range portfolio {
encWeights[i] = encryptByte(enc, p.weight)
}
// Encrypt threshold (25%) as constant for comparison
encThreshold := encryptByte(enc, 25)
fmt.Printf("Encryption done (%v)\n\n", time.Since(t0))
// Check 1: no single position > 25%
fmt.Println("Check 1: Each position <= 25%...")
t0 = time.Now()
allCompliant := enc.Encrypt(true) // accumulator
for i, p := range portfolio {
t1 := time.Now()
// leEncrypted returns 1 if weight <= 25
leResult, err := leEncrypted(eval, encWeights[i], encThreshold)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
// AND into accumulator
allCompliant, err = eval.AND(allCompliant, leResult)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
fmt.Printf(" %s checked (%v)\n", p.name, time.Since(t1))
}
check1 := dec.Decrypt(allCompliant)
fmt.Printf(" Diversification: %v (%v)\n\n", check1, time.Since(t0))
// Check 2: total exposure < 80%
fmt.Println("Check 2: Total exposure < 80%...")
t0 = time.Now()
// Sum weights using ripple-carry adder
encTotal := encWeights[0]
for i := 1; i < len(encWeights); i++ {
t1 := time.Now()
encTotal, err = addBytes(eval, enc, encTotal, encWeights[i])
if err != nil {
fmt.Fprintf(os.Stderr, "error adding: %v\n", err)
os.Exit(1)
}
fmt.Printf(" Added position %d (%v)\n", i+1, time.Since(t1))
}
encMaxExposure := encryptByte(enc, 80)
t1 := time.Now()
ltResult, err := ltEncrypted(eval, encTotal, encMaxExposure)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
check2 := dec.Decrypt(ltResult)
fmt.Printf(" Total < 80%%: %v (%v)\n\n", check2, time.Since(t1))
// Verify
decTotal := decryptByte(dec, encTotal)
fmt.Printf(" (Verification: encrypted total decrypts to %d%%)\n\n", decTotal)
// Final
fmt.Println("--- Result ---")
fmt.Printf("Portfolio compliant: %v\n", check1 && check2)
fmt.Println("(Holdings were never revealed to the compliance engine)")
}
func encryptByte(enc *fhe.Encryptor, v uint8) [8]*fhe.Ciphertext {
var bits [8]*fhe.Ciphertext
for i := 0; i < 8; i++ {
bits[i] = enc.Encrypt((v>>i)&1 == 1)
}
return bits
}
func decryptByte(dec *fhe.Decryptor, bits [8]*fhe.Ciphertext) uint8 {
var v uint8
for i := 0; i < 8; i++ {
if dec.Decrypt(bits[i]) {
v |= 1 << i
}
}
return v
}
// addBytes adds two encrypted 8-bit values using ripple-carry.
func addBytes(eval *fhe.Evaluator, enc *fhe.Encryptor, a, b [8]*fhe.Ciphertext) ([8]*fhe.Ciphertext, error) {
carry := enc.Encrypt(false)
var result [8]*fhe.Ciphertext
for i := 0; i < 8; i++ {
// sum = a XOR b XOR carry (full adder)
abXor, err := eval.XOR(a[i], b[i])
if err != nil {
return result, err
}
sum, err := eval.XOR(abXor, carry)
if err != nil {
return result, err
}
// carry = MAJORITY(a, b, carry)
carry, err = eval.MAJORITY(a[i], b[i], carry)
if err != nil {
return result, err
}
result[i] = sum
}
return result, nil
}
// ltEncrypted computes a < b on encrypted 8-bit values.
func ltEncrypted(eval *fhe.Evaluator, a, b [8]*fhe.Ciphertext) (*fhe.Ciphertext, error) {
var isLess, isEqual *fhe.Ciphertext
for i := valueBits - 1; i >= 0; i-- {
bitLt, err := eval.ANDNY(a[i], b[i])
if err != nil {
return nil, err
}
bitXor, err := eval.XOR(a[i], b[i])
if err != nil {
return nil, err
}
bitEq := eval.NOT(bitXor)
if isLess == nil {
isLess = bitLt
isEqual = bitEq
} else {
eqAndLt, err := eval.AND(isEqual, bitLt)
if err != nil {
return nil, err
}
isLess, err = eval.OR(isLess, eqAndLt)
if err != nil {
return nil, err
}
isEqual, err = eval.AND(isEqual, bitEq)
if err != nil {
return nil, err
}
}
}
return isLess, nil
}
// leEncrypted computes a <= b: NOT(b < a).
func leEncrypted(eval *fhe.Evaluator, a, b [8]*fhe.Ciphertext) (*fhe.Ciphertext, error) {
bLtA, err := ltEncrypted(eval, b, a)
if err != nil {
return nil, err
}
return eval.NOT(bLtA), nil
}
+187
View File
@@ -0,0 +1,187 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// Command darkpool demonstrates an encrypted dark pool order matching system.
//
// Traders submit encrypted limit orders (price + quantity). The matching engine
// compares encrypted bids against encrypted asks using FHE boolean circuits.
// Only matched fills are decrypted -- unmatched orders remain private.
//
// Usage:
//
// go run ./cmd/demos/darkpool
package main
import (
"fmt"
"os"
"time"
"github.com/luxfi/fhe"
)
const priceBits = 8 // 8-bit prices (0-255)
type order struct {
id int
isBid bool
price uint8
qty uint8
encPrice [8]*fhe.Ciphertext
encQty [8]*fhe.Ciphertext
}
func main() {
fmt.Println("=== FHE Dark Pool Demo ===")
fmt.Println("Encrypted order matching without revealing prices")
fmt.Println()
// Setup
fmt.Print("Initializing FHE... ")
t0 := time.Now()
params, err := fhe.NewParametersFromLiteral(fhe.PN10QP27)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
keygen := fhe.NewKeyGenerator(params)
sk, _ := keygen.GenKeyPair()
bsk := keygen.GenBootstrapKey(sk)
enc := fhe.NewEncryptor(params, sk)
dec := fhe.NewDecryptor(params, sk)
eval := fhe.NewEvaluator(params, bsk)
fmt.Printf("done (%v)\n\n", time.Since(t0))
// Orders: 3 bids, 3 asks
bids := []order{
{id: 1, isBid: true, price: 85, qty: 100},
{id: 2, isBid: true, price: 84, qty: 200},
{id: 3, isBid: true, price: 86, qty: 75},
}
asks := []order{
{id: 4, isBid: false, price: 84, qty: 100},
{id: 5, isBid: false, price: 87, qty: 250},
{id: 6, isBid: false, price: 85, qty: 90},
}
// Encrypt all orders
fmt.Println("Encrypting 6 limit orders...")
t0 = time.Now()
for i := range bids {
bids[i].encPrice = encryptByte(enc, bids[i].price)
bids[i].encQty = encryptByte(enc, bids[i].qty)
}
for i := range asks {
asks[i].encPrice = encryptByte(enc, asks[i].price)
asks[i].encQty = encryptByte(enc, asks[i].qty)
}
fmt.Printf("Encryption done (%v)\n\n", time.Since(t0))
// Match bids vs asks: bid.price >= ask.price
fmt.Println("Matching encrypted orders (bid.price >= ask.price)...")
type match struct {
bid, ask order
}
var matches []match
for _, bid := range bids {
for _, ask := range asks {
t1 := time.Now()
isGe, err := geEncrypted(eval, bid.encPrice, ask.encPrice)
if err != nil {
fmt.Fprintf(os.Stderr, "error comparing: %v\n", err)
os.Exit(1)
}
isMatch := dec.Decrypt(isGe)
elapsed := time.Since(t1)
if isMatch {
matches = append(matches, match{bid: bid, ask: ask})
fmt.Printf(" Order %d vs %d: MATCH (%v)\n", bid.id, ask.id, elapsed)
} else {
fmt.Printf(" Order %d vs %d: no match (%v)\n", bid.id, ask.id, elapsed)
}
}
}
// Decrypt matched fills
fmt.Printf("\n--- Matched Fills (%d) ---\n", len(matches))
for _, m := range matches {
bidPrice := decryptByte(dec, m.bid.encPrice)
askPrice := decryptByte(dec, m.ask.encPrice)
bidQty := decryptByte(dec, m.bid.encQty)
askQty := decryptByte(dec, m.ask.encQty)
fillQty := bidQty
if askQty < fillQty {
fillQty = askQty
}
fmt.Printf(" Matched: Buy %d @ $%d <-> Sell %d @ $%d (fill %d shares)\n",
bidQty, bidPrice, askQty, askPrice, fillQty)
}
fmt.Println("\nNote: unmatched order prices were never revealed.")
}
func encryptByte(enc *fhe.Encryptor, v uint8) [8]*fhe.Ciphertext {
var bits [8]*fhe.Ciphertext
for i := 0; i < 8; i++ {
bits[i] = enc.Encrypt((v>>i)&1 == 1)
}
return bits
}
func decryptByte(dec *fhe.Decryptor, bits [8]*fhe.Ciphertext) uint8 {
var v uint8
for i := 0; i < 8; i++ {
if dec.Decrypt(bits[i]) {
v |= 1 << i
}
}
return v
}
// geEncrypted computes a >= b on encrypted 8-bit values using MSB-first comparison.
// Returns an encrypted bit: 1 if a >= b, 0 otherwise.
func geEncrypted(eval *fhe.Evaluator, a, b [8]*fhe.Ciphertext) (*fhe.Ciphertext, error) {
// Compare from MSB to LSB: a >= b iff NOT (a < b)
// a < b: find first bit where they differ (MSB to LSB); at that bit a=0, b=1
var isLess, isEqual *fhe.Ciphertext
for i := priceBits - 1; i >= 0; i-- {
// bitLt = NOT(a[i]) AND b[i] -- this bit says a < b at position i
bitLt, err := eval.ANDNY(a[i], b[i])
if err != nil {
return nil, fmt.Errorf("bit %d ANDNY: %w", i, err)
}
// bitEq = NOT(a[i] XOR b[i]) -- bits are equal
bitXor, err := eval.XOR(a[i], b[i])
if err != nil {
return nil, fmt.Errorf("bit %d XOR: %w", i, err)
}
bitEq := eval.NOT(bitXor)
if isLess == nil {
isLess = bitLt
isEqual = bitEq
} else {
// isLess = isLess OR (isEqual AND bitLt)
eqAndLt, err := eval.AND(isEqual, bitLt)
if err != nil {
return nil, err
}
isLess, err = eval.OR(isLess, eqAndLt)
if err != nil {
return nil, err
}
// isEqual = isEqual AND bitEq
isEqual, err = eval.AND(isEqual, bitEq)
if err != nil {
return nil, err
}
}
}
// a >= b is NOT(a < b)
return eval.NOT(isLess), nil
}
+184
View File
@@ -0,0 +1,184 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// Command marketmaker demonstrates private market making with FHE.
//
// Three market makers submit encrypted bid/ask quotes. The system selects
// the best bid (highest) and best ask (lowest) via boolean circuit comparisons.
// Only the winning quotes are decrypted -- losing quotes stay private.
//
// Usage:
//
// go run ./cmd/demos/marketmaker
package main
import (
"fmt"
"os"
"time"
"github.com/luxfi/fhe"
)
const priceBits = 8
type quote struct {
maker string
bid uint8
ask uint8
}
func main() {
fmt.Println("=== FHE Private Market Making Demo ===")
fmt.Println("Select best bid/ask from encrypted quotes")
fmt.Println()
// Setup
fmt.Print("Initializing FHE... ")
t0 := time.Now()
params, err := fhe.NewParametersFromLiteral(fhe.PN10QP27)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
keygen := fhe.NewKeyGenerator(params)
sk, _ := keygen.GenKeyPair()
bsk := keygen.GenBootstrapKey(sk)
enc := fhe.NewEncryptor(params, sk)
dec := fhe.NewDecryptor(params, sk)
eval := fhe.NewEvaluator(params, bsk)
fmt.Printf("done (%v)\n\n", time.Since(t0))
// Three market makers
quotes := []quote{
{maker: "MM1", bid: 84, ask: 86},
{maker: "MM2", bid: 85, ask: 87},
{maker: "MM3", bid: 83, ask: 85},
}
for _, q := range quotes {
fmt.Printf(" %s: bid $%d / ask $%d\n", q.maker, q.bid, q.ask)
}
fmt.Println()
// Encrypt
fmt.Println("Encrypting quotes...")
t0 = time.Now()
type encQuote struct {
maker string
encBid [8]*fhe.Ciphertext
encAsk [8]*fhe.Ciphertext
}
encQuotes := make([]encQuote, len(quotes))
for i, q := range quotes {
encQuotes[i].maker = q.maker
encQuotes[i].encBid = encryptByte(enc, q.bid)
encQuotes[i].encAsk = encryptByte(enc, q.ask)
}
fmt.Printf("Encryption done (%v)\n\n", time.Since(t0))
// Best bid (highest)
fmt.Println("Finding best bid (highest)...")
t0 = time.Now()
bestBidIdx := 0
for i := 1; i < len(encQuotes); i++ {
t1 := time.Now()
// is encQuotes[i].bid > encQuotes[bestBidIdx].bid?
gtResult, err := gtEncrypted(eval, encQuotes[i].encBid, encQuotes[bestBidIdx].encBid)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
isGreater := dec.Decrypt(gtResult)
fmt.Printf(" %s > %s: %v (%v)\n", encQuotes[i].maker, encQuotes[bestBidIdx].maker, isGreater, time.Since(t1))
if isGreater {
bestBidIdx = i
}
}
fmt.Printf("Best bid found (%v)\n\n", time.Since(t0))
// Best ask (lowest)
fmt.Println("Finding best ask (lowest)...")
t0 = time.Now()
bestAskIdx := 0
for i := 1; i < len(encQuotes); i++ {
t1 := time.Now()
ltResult, err := ltEncrypted(eval, encQuotes[i].encAsk, encQuotes[bestAskIdx].encAsk)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
isLess := dec.Decrypt(ltResult)
fmt.Printf(" %s < %s: %v (%v)\n", encQuotes[i].maker, encQuotes[bestAskIdx].maker, isLess, time.Since(t1))
if isLess {
bestAskIdx = i
}
}
fmt.Printf("Best ask found (%v)\n\n", time.Since(t0))
// Decrypt winners
bestBidPrice := decryptByte(dec, encQuotes[bestBidIdx].encBid)
bestAskPrice := decryptByte(dec, encQuotes[bestAskIdx].encAsk)
fmt.Println("--- Result ---")
fmt.Printf("Best bid: %s @ $%d\n", encQuotes[bestBidIdx].maker, bestBidPrice)
fmt.Printf("Best ask: %s @ $%d\n", encQuotes[bestAskIdx].maker, bestAskPrice)
fmt.Printf("Spread: $%d\n", bestAskPrice-bestBidPrice)
fmt.Println("\nNote: losing quotes were never decrypted.")
}
func encryptByte(enc *fhe.Encryptor, v uint8) [8]*fhe.Ciphertext {
var bits [8]*fhe.Ciphertext
for i := 0; i < 8; i++ {
bits[i] = enc.Encrypt((v>>i)&1 == 1)
}
return bits
}
func decryptByte(dec *fhe.Decryptor, bits [8]*fhe.Ciphertext) uint8 {
var v uint8
for i := 0; i < 8; i++ {
if dec.Decrypt(bits[i]) {
v |= 1 << i
}
}
return v
}
// ltEncrypted computes a < b on encrypted 8-bit values (MSB-first).
func ltEncrypted(eval *fhe.Evaluator, a, b [8]*fhe.Ciphertext) (*fhe.Ciphertext, error) {
var isLess, isEqual *fhe.Ciphertext
for i := priceBits - 1; i >= 0; i-- {
bitLt, err := eval.ANDNY(a[i], b[i]) // NOT(a) AND b
if err != nil {
return nil, err
}
bitXor, err := eval.XOR(a[i], b[i])
if err != nil {
return nil, err
}
bitEq := eval.NOT(bitXor)
if isLess == nil {
isLess = bitLt
isEqual = bitEq
} else {
eqAndLt, err := eval.AND(isEqual, bitLt)
if err != nil {
return nil, err
}
isLess, err = eval.OR(isLess, eqAndLt)
if err != nil {
return nil, err
}
isEqual, err = eval.AND(isEqual, bitEq)
if err != nil {
return nil, err
}
}
}
return isLess, nil
}
// gtEncrypted computes a > b: same as b < a.
func gtEncrypted(eval *fhe.Evaluator, a, b [8]*fhe.Ciphertext) (*fhe.Ciphertext, error) {
return ltEncrypted(eval, b, a)
}
+173
View File
@@ -0,0 +1,173 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// Command nav demonstrates confidential Net Asset Value (NAV) computation using FHE.
//
// An ETF's holdings (10 positions with share counts and prices) are encrypted.
// NAV = sum(holdings * prices) / totalShares is computed on encrypted data using
// repeated addition. Only the final NAV is decrypted for the authorized party.
//
// Usage:
//
// go run ./cmd/demos/nav
package main
import (
"fmt"
"os"
"time"
"github.com/luxfi/fhe"
)
type holding struct {
ticker string
shares uint8 // number of shares held (small for demo)
price uint8 // price per share in dollars
}
func main() {
fmt.Println("=== FHE Confidential NAV Computation Demo ===")
fmt.Println("Compute ETF NAV on encrypted holdings")
fmt.Println()
// Setup
fmt.Print("Initializing FHE... ")
t0 := time.Now()
params, err := fhe.NewParametersFromLiteral(fhe.PN10QP27)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
keygen := fhe.NewKeyGenerator(params)
sk, _ := keygen.GenKeyPair()
bsk := keygen.GenBootstrapKey(sk)
enc := fhe.NewEncryptor(params, sk)
dec := fhe.NewDecryptor(params, sk)
eval := fhe.NewEvaluator(params, bsk)
fmt.Printf("done (%v)\n\n", time.Since(t0))
// ETF holdings: small values so sums stay in 8-bit range
// Total value = 2*19 + 2*21 + 1*14 + 2*17 + 1*18 + 2*25 + 1*12 + 1*20 + 2*10 + 1*14
// = 38 + 42 + 14 + 34 + 18 + 50 + 12 + 20 + 20 + 14 = 262
// But 262 > 255, so let's reduce: use 5 holdings to stay under 255.
holdings := []holding{
{ticker: "AAPL", shares: 2, price: 19},
{ticker: "MSFT", shares: 2, price: 21},
{ticker: "NVDA", shares: 1, price: 14},
{ticker: "GOOG", shares: 2, price: 17},
{ticker: "AMZN", shares: 1, price: 18},
}
totalShares := uint64(5) // total ETF shares outstanding
var expectedTotal uint64
fmt.Println("ETF Holdings (shown for verification):")
for _, h := range holdings {
value := uint64(h.shares) * uint64(h.price)
expectedTotal += value
fmt.Printf(" %6s: %d shares @ $%d = $%d\n", h.ticker, h.shares, h.price, value)
}
expectedNAV := expectedTotal / totalShares
fmt.Printf(" Total value: $%d\n", expectedTotal)
fmt.Printf(" Shares outstanding: %d\n", totalShares)
fmt.Printf(" Expected NAV: $%d/share\n\n", expectedNAV)
// Encrypt prices
fmt.Println("Encrypting holdings...")
t0 = time.Now()
encPrices := make([][8]*fhe.Ciphertext, len(holdings))
for i, h := range holdings {
encPrices[i] = encryptByte(enc, h.price)
}
fmt.Printf("Encryption done (%v)\n\n", time.Since(t0))
// Compute NAV: for each position, add price to itself (shares-1) times, then sum all
fmt.Println("Computing NAV on encrypted data...")
fmt.Println(" Strategy: repeated addition (shares * price)")
t0 = time.Now()
var encTotal [8]*fhe.Ciphertext
initialized := false
for idx, h := range holdings {
t1 := time.Now()
// shares * price via repeated addition
posValue := encPrices[idx]
for s := uint8(1); s < h.shares; s++ {
posValue, err = addBytes(eval, enc, posValue, encPrices[idx])
if err != nil {
fmt.Fprintf(os.Stderr, "error multiplying %s: %v\n", h.ticker, err)
os.Exit(1)
}
}
if !initialized {
encTotal = posValue
initialized = true
} else {
encTotal, err = addBytes(eval, enc, encTotal, posValue)
if err != nil {
fmt.Fprintf(os.Stderr, "error accumulating %s: %v\n", h.ticker, err)
os.Exit(1)
}
}
fmt.Printf(" %6s: %d additions (%v)\n", h.ticker, h.shares, time.Since(t1))
}
fmt.Printf("Total computation: %v\n\n", time.Since(t0))
// Decrypt and compute NAV (division by public totalShares is plaintext)
decTotal := uint64(decryptByte(dec, encTotal))
navPerShare := decTotal / totalShares
fmt.Println("--- Result ---")
fmt.Printf("IBIT NAV: $%d per share (computed on encrypted data)\n", navPerShare)
fmt.Printf("Total fund value: $%d (decrypted)\n", decTotal)
fmt.Printf("Shares outstanding: %d (public)\n", totalShares)
if navPerShare == expectedNAV {
fmt.Println("PASS: NAV matches expected value")
} else {
fmt.Printf("NOTE: NAV=%d vs expected=%d (FHE arithmetic precision)\n", navPerShare, expectedNAV)
}
fmt.Println("\nNote: individual holdings were never revealed to the NAV calculator.")
}
func encryptByte(enc *fhe.Encryptor, v uint8) [8]*fhe.Ciphertext {
var bits [8]*fhe.Ciphertext
for i := 0; i < 8; i++ {
bits[i] = enc.Encrypt((v>>i)&1 == 1)
}
return bits
}
func decryptByte(dec *fhe.Decryptor, bits [8]*fhe.Ciphertext) uint8 {
var v uint8
for i := 0; i < 8; i++ {
if dec.Decrypt(bits[i]) {
v |= 1 << i
}
}
return v
}
// addBytes adds two encrypted 8-bit values using ripple-carry full adder.
func addBytes(eval *fhe.Evaluator, enc *fhe.Encryptor, a, b [8]*fhe.Ciphertext) ([8]*fhe.Ciphertext, error) {
carry := enc.Encrypt(false)
var result [8]*fhe.Ciphertext
for i := 0; i < 8; i++ {
abXor, err := eval.XOR(a[i], b[i])
if err != nil {
return result, err
}
sum, err := eval.XOR(abXor, carry)
if err != nil {
return result, err
}
carry, err = eval.MAJORITY(a[i], b[i], carry)
if err != nil {
return result, err
}
result[i] = sum
}
return result, nil
}
+200
View File
@@ -0,0 +1,200 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// Command proof demonstrates compliance proofs on encrypted portfolios using FHE.
//
// A portfolio is encrypted. The system proves two compliance conditions:
// - No single position exceeds 25% of total
// - No position matches a sanctioned counterparty ID
//
// Only the boolean proof results are decrypted. Portfolio data stays encrypted.
//
// Usage:
//
// go run ./cmd/demos/proof
package main
import (
"fmt"
"os"
"time"
"github.com/luxfi/fhe"
)
const valueBits = 8
type position struct {
name string
weight uint8 // percentage
entityID uint8 // counterparty ID
}
func main() {
fmt.Println("=== FHE Compliance Proof Demo ===")
fmt.Println("Prove compliance without revealing portfolio data")
fmt.Println()
// Setup
fmt.Print("Initializing FHE... ")
t0 := time.Now()
params, err := fhe.NewParametersFromLiteral(fhe.PN10QP27)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
keygen := fhe.NewKeyGenerator(params)
sk, _ := keygen.GenKeyPair()
bsk := keygen.GenBootstrapKey(sk)
enc := fhe.NewEncryptor(params, sk)
dec := fhe.NewDecryptor(params, sk)
eval := fhe.NewEvaluator(params, bsk)
fmt.Printf("done (%v)\n\n", time.Since(t0))
// Portfolio
portfolio := []position{
{name: "AAPL", weight: 20, entityID: 11},
{name: "MSFT", weight: 18, entityID: 22},
{name: "NVDA", weight: 15, entityID: 33},
{name: "GOOG", weight: 12, entityID: 44},
{name: "AMZN", weight: 10, entityID: 55},
}
sanctionedIDs := []uint8{99, 88, 77}
fmt.Println("Portfolio (shown for verification):")
for _, p := range portfolio {
fmt.Printf(" %s: %d%% (entity %d)\n", p.name, p.weight, p.entityID)
}
fmt.Printf("Sanctioned entities: %v\n\n", sanctionedIDs)
// Encrypt
fmt.Println("Encrypting portfolio...")
t0 = time.Now()
encWeights := make([][8]*fhe.Ciphertext, len(portfolio))
encEntities := make([][8]*fhe.Ciphertext, len(portfolio))
for i, p := range portfolio {
encWeights[i] = encryptByte(enc, p.weight)
encEntities[i] = encryptByte(enc, p.entityID)
}
fmt.Printf("Encryption done (%v)\n\n", time.Since(t0))
// Proof 1: max position < 25%
fmt.Println("Proof 1: No position exceeds 25%...")
t0 = time.Now()
encThreshold := encryptByte(enc, 25)
allUnder := enc.Encrypt(true)
for i, p := range portfolio {
t1 := time.Now()
// weight <= 25: NOT(25 < weight)
threshLtWeight, err := ltEncrypted(eval, encThreshold, encWeights[i])
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
withinLimit := eval.NOT(threshLtWeight) // weight <= 25
allUnder, err = eval.AND(allUnder, withinLimit)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
fmt.Printf(" %s checked (%v)\n", p.name, time.Since(t1))
}
diversificationOk := dec.Decrypt(allUnder)
fmt.Printf(" Result: %v (%v)\n\n", diversificationOk, time.Since(t0))
// Proof 2: no sanctioned counterparty
fmt.Println("Proof 2: No sanctioned counterparty...")
t0 = time.Now()
noSanctioned := enc.Encrypt(true)
for _, sid := range sanctionedIDs {
encSanctioned := encryptByte(enc, sid)
for i, p := range portfolio {
t1 := time.Now()
// Check entity != sanctioned using XOR: if any bit differs, they are not equal
isEqual := enc.Encrypt(true)
for bit := 0; bit < valueBits; bit++ {
bitXor, err := eval.XOR(encEntities[i][bit], encSanctioned[bit])
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
bitsMatch := eval.NOT(bitXor) // bits match if XOR is 0
isEqual, err = eval.AND(isEqual, bitsMatch)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
}
// notEqual = NOT(isEqual)
notEqual := eval.NOT(isEqual)
noSanctioned, err = eval.AND(noSanctioned, notEqual)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
fmt.Printf(" %s vs entity %d (%v)\n", p.name, sid, time.Since(t1))
}
}
sanctionsOk := dec.Decrypt(noSanctioned)
fmt.Printf(" Result: %v (%v)\n\n", sanctionsOk, time.Since(t0))
// Final
overallPass := diversificationOk && sanctionsOk
fmt.Println("--- Result ---")
fmt.Printf("Compliance proof: %s (portfolio data remained encrypted)\n", label(overallPass))
fmt.Printf(" Diversification (max < 25%%): %s\n", label(diversificationOk))
fmt.Printf(" Sanctions screening: %s\n", label(sanctionsOk))
}
func label(ok bool) string {
if ok {
return "PASS"
}
return "FAIL"
}
func encryptByte(enc *fhe.Encryptor, v uint8) [8]*fhe.Ciphertext {
var bits [8]*fhe.Ciphertext
for i := 0; i < 8; i++ {
bits[i] = enc.Encrypt((v>>i)&1 == 1)
}
return bits
}
// ltEncrypted computes a < b on 8-bit encrypted values (MSB-first).
func ltEncrypted(eval *fhe.Evaluator, a, b [8]*fhe.Ciphertext) (*fhe.Ciphertext, error) {
var isLess, isEqual *fhe.Ciphertext
for i := valueBits - 1; i >= 0; i-- {
bitLt, err := eval.ANDNY(a[i], b[i])
if err != nil {
return nil, err
}
bitXor, err := eval.XOR(a[i], b[i])
if err != nil {
return nil, err
}
bitEq := eval.NOT(bitXor)
if isLess == nil {
isLess = bitLt
isEqual = bitEq
} else {
eqAndLt, err := eval.AND(isEqual, bitLt)
if err != nil {
return nil, err
}
isLess, err = eval.OR(isLess, eqAndLt)
if err != nil {
return nil, err
}
isEqual, err = eval.AND(isEqual, bitEq)
if err != nil {
return nil, err
}
}
}
return isLess, nil
}
+159
View File
@@ -0,0 +1,159 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// Command voting demonstrates private shareholder voting using FHE.
//
// 100 shareholders vote encrypted yes/no. Votes are tallied homomorphically
// using a ripple-carry adder on encrypted bits. Only the final count is
// decrypted -- individual votes are never revealed.
//
// Usage:
//
// go run ./cmd/demos/voting
// go run ./cmd/demos/voting -voters 50 -yes 30
package main
import (
"flag"
"fmt"
"math/rand/v2"
"os"
"time"
"github.com/luxfi/fhe"
)
const tallyBits = 7 // supports up to 127 voters
func main() {
nVoters := flag.Int("voters", 100, "number of shareholders (max 127)")
nYes := flag.Int("yes", -1, "number of yes votes (-1 = random ~67%)")
flag.Parse()
if *nVoters < 2 || *nVoters > 127 {
fmt.Fprintln(os.Stderr, "error: -voters must be 2..127")
os.Exit(1)
}
fmt.Println("=== FHE Private Shareholder Voting Demo ===")
fmt.Println("Homomorphic tally without revealing individual votes")
fmt.Println()
// Generate votes
votes := make([]bool, *nVoters)
if *nYes >= 0 {
if *nYes > *nVoters {
fmt.Fprintln(os.Stderr, "error: -yes cannot exceed -voters")
os.Exit(1)
}
for i := 0; i < *nYes; i++ {
votes[i] = true
}
} else {
for i := range votes {
votes[i] = rand.IntN(3) != 0 // ~67% yes
}
}
expectedYes := 0
for _, v := range votes {
if v {
expectedYes++
}
}
fmt.Printf("Shareholders: %d | Expected: %d yes, %d no\n\n", *nVoters, expectedYes, *nVoters-expectedYes)
// Setup FHE
fmt.Print("Initializing FHE parameters... ")
t0 := time.Now()
params, err := fhe.NewParametersFromLiteral(fhe.PN10QP27)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
keygen := fhe.NewKeyGenerator(params)
sk, _ := keygen.GenKeyPair()
bsk := keygen.GenBootstrapKey(sk)
enc := fhe.NewEncryptor(params, sk)
dec := fhe.NewDecryptor(params, sk)
eval := fhe.NewEvaluator(params, bsk)
fmt.Printf("done (%v)\n\n", time.Since(t0))
// Encrypt ballots
fmt.Printf("Encrypting %d ballots...\n", *nVoters)
t0 = time.Now()
ballots := make([]*fhe.Ciphertext, *nVoters)
for i, v := range votes {
ballots[i] = enc.Encrypt(v)
}
fmt.Printf("Encryption done (%v)\n\n", time.Since(t0))
// Initialize tally accumulator (7-bit: supports 0-127)
tally := [tallyBits]*fhe.Ciphertext{}
for i := 0; i < tallyBits; i++ {
tally[i] = enc.Encrypt(false)
}
// Tally votes homomorphically
fmt.Printf("Tallying %d ballots homomorphically (%d-bit accumulator)...\n", *nVoters, tallyBits)
t0 = time.Now()
for i, ballot := range ballots {
if (i+1)%10 == 0 || i == 0 {
fmt.Printf(" Processing ballot %d/%d...\n", i+1, *nVoters)
}
tally, err = addBit(eval, tally, ballot)
if err != nil {
fmt.Fprintf(os.Stderr, "error adding ballot %d: %v\n", i+1, err)
os.Exit(1)
}
}
tallyElapsed := time.Since(t0)
fmt.Printf("Tally done (%v, %.1fms per ballot)\n\n",
tallyElapsed, float64(tallyElapsed.Milliseconds())/float64(*nVoters))
// Decrypt only the final tally
var yesCount uint8
for i := 0; i < tallyBits; i++ {
if dec.Decrypt(tally[i]) {
yesCount |= 1 << i
}
}
noCount := *nVoters - int(yesCount)
fmt.Println("--- Result ---")
passed := int(yesCount) > *nVoters/2
if passed {
fmt.Printf("Vote passed: %d yes, %d no\n", yesCount, noCount)
} else {
fmt.Printf("Vote failed: %d yes, %d no\n", yesCount, noCount)
}
fmt.Printf("Threshold: >%d (simple majority)\n", *nVoters/2)
if int(yesCount) == expectedYes {
fmt.Println("PASS: tally matches expected count")
} else {
fmt.Println("FAIL: tally mismatch!")
}
fmt.Println("\nNote: individual ballots were never decrypted.")
}
// addBit adds a single encrypted bit to an n-bit encrypted accumulator
// using a ripple-carry adder (XOR for sum, AND for carry).
func addBit(eval *fhe.Evaluator, acc [tallyBits]*fhe.Ciphertext, bit *fhe.Ciphertext) ([tallyBits]*fhe.Ciphertext, error) {
carry := bit
var result [tallyBits]*fhe.Ciphertext
for i := 0; i < tallyBits; i++ {
sum, err := eval.XOR(acc[i], carry)
if err != nil {
return result, fmt.Errorf("bit %d XOR: %w", i, err)
}
newCarry, err := eval.AND(acc[i], carry)
if err != nil {
return result, fmt.Errorf("bit %d AND: %w", i, err)
}
result[i] = sum
carry = newCarry
}
return result, nil
}