Zero-Knowledge Swap Protocol
Pure zero-knowledge swap implementation with stealth addresses and ring signatures
Abstract
This LP defines a pure zero-knowledge swap protocol that enables completely anonymous token exchanges using stealth addresses, ring signatures, and bulletproofs. The protocol ensures sender anonymity, recipient privacy, and amount confidentiality while maintaining verifiable correctness and preventing double-spending.
Motivation
Current DEX protocols leak critical information:
- Sender and recipient addresses
- Token amounts being traded
- Trading patterns and strategies
- Wallet balances and history
This protocol achieves:
- Complete sender anonymity via ring signatures
- Recipient privacy through stealth addresses
- Amount hiding with bulletproofs
- Unlinkable transactions
- Regulatory compliance options
Specification
Core ZK-Swap Interface
interface IZKSwap {
// Stealth address for receiving
struct StealthAddress {
bytes32 publicViewKey;
bytes32 publicSpendKey;
bytes32 ephemeralKey;
bytes encryptedMeta; // Encrypted transaction metadata
}
// Ring signature for sending
struct RingSignature {
bytes32[] publicKeys; // Ring members
bytes32 keyImage; // Unique double-spend prevention
bytes signature; // Actual ring signature
bytes32 commitment; // Amount commitment
}
// Bulletproof for amounts
struct BulletProof {
bytes32 commitment;
bytes rangeProof; // Proof that amount is in valid range
bytes32 blindingFactor; // Hidden blinding factor
}
// Complete ZK swap transaction
struct ZKSwapTx {
RingSignature senderProof;
StealthAddress recipient;
BulletProof amountProof;
bytes32 nullifier; // Prevents double-spending
bytes encryptedData; // Additional encrypted data
}
event ZKSwapInitiated(
bytes32 indexed nullifier,
bytes32 keyImage,
bytes32 outputCommitment
);
event StealthPaymentReceived(
bytes32 indexed ephemeralKey,
bytes encryptedNotification
);
function executeZKSwap(
ZKSwapTx calldata swapTx
) external returns (bool success);
function verifyRingSignature(
RingSignature calldata sig
) external view returns (bool valid);
function verifyBulletProof(
BulletProof calldata proof
) external view returns (bool valid);
function generateStealthAddress(
bytes32 publicViewKey,
bytes32 publicSpendKey
) external returns (StealthAddress memory);
}
Ring Signature Implementation
interface IRingSignatures {
// Ring construction parameters
struct RingParameters {
uint256 ringSize; // Number of decoy members
bytes32 seed; // Randomness seed
uint256 realIndex; // Hidden real signer index
}
// Linkable ring signature (prevents double-spending)
struct LinkableRingSignature {
bytes32[] ring; // Public keys in ring
bytes32 keyImage; // I = x * H(P)
bytes32[] c; // Challenge values
bytes32[] r; // Response values
}
event RingConstructed(
bytes32 indexed keyImage,
uint256 ringSize
);
event DoubleSpendAttempt(
bytes32 indexed keyImage,
bytes32 nullifier
);
function constructRing(
bytes32 realPublicKey,
uint256 ringSize
) external view returns (bytes32[] memory ring);
function signRing(
bytes32[] calldata ring,
uint256 realIndex,
bytes32 privateKey,
bytes32 message
) external returns (LinkableRingSignature memory);
function verifyLinkableRingSignature(
LinkableRingSignature calldata sig,
bytes32 message
) external view returns (bool valid);
function checkKeyImageUsed(
bytes32 keyImage
) external view returns (bool used);
}
Stealth Address System
interface IStealthAddresses {
// Stealth address generation
struct StealthKeys {
bytes32 viewPrivateKey; // For scanning blockchain
bytes32 spendPrivateKey; // For spending funds
bytes32 viewPublicKey;
bytes32 spendPublicKey;
}
// Payment detection
struct StealthPayment {
bytes32 ephemeralPublicKey;
bytes32 stealthPublicKey;
bytes encryptedAmount; // Amount encrypted to recipient
bytes32 paymentId; // Optional payment ID
}
event StealthAddressGenerated(
bytes32 indexed viewPublicKey,
bytes32 stealthPublicKey
);
event StealthPaymentPublished(
bytes32 indexed ephemeralPublicKey,
bytes encryptedNotification
);
function generateStealthKeys(
bytes32 seed
) external pure returns (StealthKeys memory);
function deriveStealthAddress(
bytes32 viewPublicKey,
bytes32 spendPublicKey,
bytes32 ephemeralPrivateKey
) external pure returns (
bytes32 stealthPublicKey,
bytes32 ephemeralPublicKey
);
function checkStealthPayment(
bytes32 ephemeralPublicKey,
bytes32 stealthPublicKey,
bytes32 viewPrivateKey
) external pure returns (bool isForMe);
function recoverStealthPrivateKey(
bytes32 ephemeralPublicKey,
bytes32 viewPrivateKey,
bytes32 spendPrivateKey
) external pure returns (bytes32 stealthPrivateKey);
}
Bulletproof Range Proofs
interface IBulletproofs {
// Bulletproof parameters
struct BulletproofParams {
uint256 bitLength; // Bit length of amounts (e.g., 64)
bytes32 pedersonH; // Pederson commitment base H
bytes32 pedersonG; // Pederson commitment base G
}
// Aggregated bulletproof for multiple amounts
struct AggregatedBulletproof {
bytes32[] commitments; // Pederson commitments
bytes aggregatedProof; // Single proof for all amounts
uint256 totalBits;
}
event BulletproofGenerated(
bytes32 indexed commitment,
uint256 bitLength
);
event BulletproofVerified(
bytes32 indexed commitment,
bool valid
);
function generateBulletproof(
uint256 amount,
bytes32 blinding
) external returns (
bytes32 commitment,
bytes memory proof
);
function verifyBulletproof(
bytes32 commitment,
bytes calldata proof
) external view returns (bool valid);
function aggregateBulletproofs(
bytes32[] calldata commitments,
bytes[] calldata proofs
) external returns (AggregatedBulletproof memory);
function verifyAggregatedBulletproof(
AggregatedBulletproof calldata proof
) external view returns (bool valid);
}
Confidential Asset Swaps
interface IConfidentialAssetSwap {
// Multi-asset confidential transaction
struct ConfidentialTx {
bytes32[] inputCommitments; // Hidden input amounts
bytes32[] outputCommitments; // Hidden output amounts
bytes32[] assetCommitments; // Hidden asset types
bytes zkProof; // Proof of balance
}
// Atomic swap with privacy
struct PrivateAtomicSwap {
bytes32 swapId;
bytes32 aliceCommitment; // Alice's asset commitment
bytes32 bobCommitment; // Bob's asset commitment
bytes32 secretHash; // Hash of swap secret
uint256 timelock;
}
event ConfidentialSwapCreated(
bytes32 indexed swapId,
bytes32 commitment1,
bytes32 commitment2
);
event ConfidentialSwapCompleted(
bytes32 indexed swapId,
bytes32 nullifier
);
function createConfidentialSwap(
bytes32 myCommitment,
bytes32 counterpartyCommitment,
bytes32 secretHash,
uint256 timelock
) external returns (bytes32 swapId);
function completeConfidentialSwap(
bytes32 swapId,
bytes32 secret,
bytes calldata zkProof
) external;
function refundConfidentialSwap(
bytes32 swapId,
bytes calldata refundProof
) external;
function verifyAssetSwapProof(
ConfidentialTx calldata tx
) external view returns (bool valid);
}
Anonymous Order Book
interface IAnonymousOrderBook {
// Hidden order
struct AnonymousOrder {
bytes32 orderId;
bytes32 commitment; // Commitment to order details
bytes encryptedOrder; // Order encrypted to market
bytes32 traderId; // Anonymous trader ID
bytes ringSignature; // Proof of trader membership
}
// Private matching
struct PrivateMatch {
bytes32 matchId;
bytes32 order1Nullifier;
bytes32 order2Nullifier;
bytes32 outputCommitment;
bytes matchProof;
}
event AnonymousOrderPlaced(
bytes32 indexed orderId,
bytes32 commitment
);
event PrivateMatchExecuted(
bytes32 indexed matchId,
bytes32 nullifier1,
bytes32 nullifier2
);
function placeAnonymousOrder(
AnonymousOrder calldata order
) external returns (bytes32 orderId);
function matchAnonymousOrders(
bytes32 orderId1,
bytes32 orderId2,
bytes calldata matchingProof
) external returns (bytes32 matchId);
function cancelAnonymousOrder(
bytes32 orderId,
bytes32 nullifier,
bytes calldata cancelProof
) external;
function revealMatch(
bytes32 matchId,
bytes32 revealKey
) external view returns (
uint256 amount1,
uint256 amount2,
address token1,
address token2
);
}
Privacy Mixer Integration
interface IPrivacyMixer {
// Mixing pool
struct MixingPool {
bytes32 poolId;
address token;
uint256 denomination; // Fixed denomination
bytes32 merkleRoot; // Root of deposits
uint256 anonymitySet; // Number of deposits
}
// Deposit note
struct DepositNote {
bytes32 commitment;
bytes32 nullifierHash;
bytes encryptedNote; // Encrypted to user
}
event MixDeposit(
bytes32 indexed commitment,
uint256 leafIndex,
uint256 timestamp
);
event MixWithdrawal(
bytes32 indexed nullifierHash,
address indexed relayer,
uint256 fee
);
function depositToMixer(
bytes32 commitment
) external payable;
function withdrawFromMixer(
bytes calldata proof,
bytes32 nullifierHash,
address recipient,
address relayer,
uint256 fee
) external;
function mixAndSwap(
bytes calldata mixProof,
ZKSwapTx calldata swapTx
) external;
function isSpent(
bytes32 nullifierHash
) external view returns (bool);
}
Compliance and Auditability
interface IComplianceZKSwap {
// Viewing capability
struct ViewingCapability {
bytes32 transactionId;
bytes viewKey; // Decryption key
uint256 scope; // What can be viewed
uint256 expiry;
}
// Selective disclosure
struct SelectiveDisclosure {
bytes32 transactionId;
uint256 disclosureType; // What to disclose
bytes proof; // Proof of correctness
bytes encryptedData; // Encrypted to authority
}
event ViewingCapabilityGranted(
bytes32 indexed transactionId,
address authority
);
event SelectiveDisclosureMade(
bytes32 indexed transactionId,
uint256 disclosureType
);
function grantViewingCapability(
bytes32 transactionId,
address authority,
uint256 scope
) external;
function makeSelectiveDisclosure(
bytes32 transactionId,
uint256 disclosureType,
bytes calldata disclosureProof
) external;
function auditTransaction(
bytes32 transactionId,
bytes calldata viewKey
) external view returns (
address sender,
address recipient,
uint256 amount
);
function proveCompliance(
bytes32[] calldata transactionIds,
bytes calldata complianceProof
) external view returns (bool compliant);
}
Rationale
Ring Signatures
Provide:
- Sender anonymity within decoy set
- Unlinkability between transactions
- Double-spend prevention via key images
- Scalable anonymity sets
Stealth Addresses
Enable:
- Recipient privacy
- Unlinkable receiving addresses
- Payment detection without blockchain scanning
- Forward secrecy
Bulletproofs
Offer:
- Compact range proofs
- Amount confidentiality
- Aggregation capability
- No trusted setup
Mixer Integration
Allows:
- Breaking transaction links
- Enhanced anonymity sets
- Denomination standardization
- Timing attack mitigation
Test Cases
Ring Signature Test
function testRingSignature() public {
IRingSignatures ring = IRingSignatures(ringAddress);
// Construct ring with decoys
bytes32[] memory ringMembers = ring.constructRing(
myPublicKey,
10 // Ring size
);
// Create linkable ring signature
LinkableRingSignature memory sig = ring.signRing(
ringMembers,
3, // Real index (hidden)
myPrivateKey,
keccak256("swap message")
);
// Verify signature
bool valid = ring.verifyLinkableRingSignature(
sig,
keccak256("swap message")
);
assertTrue(valid);
assertFalse(ring.checkKeyImageUsed(sig.keyImage));
}
Stealth Address Test
function testStealthAddress() public {
IStealthAddresses stealth = IStealthAddresses(stealthAddress);
// Generate stealth keys
StealthKeys memory keys = stealth.generateStealthKeys(seed);
// Derive stealth address for payment
(bytes32 stealthPubKey, bytes32 ephemeralPubKey) = stealth.deriveStealthAddress(
keys.viewPublicKey,
keys.spendPublicKey,
ephemeralPrivateKey
);
// Check if payment is for us
bool isForMe = stealth.checkStealthPayment(
ephemeralPubKey,
stealthPubKey,
keys.viewPrivateKey
);
assertTrue(isForMe);
// Recover spending key
bytes32 stealthPrivKey = stealth.recoverStealthPrivateKey(
ephemeralPubKey,
keys.viewPrivateKey,
keys.spendPrivateKey
);
// Verify we can spend
assertEq(derivePublicKey(stealthPrivKey), stealthPubKey);
}
Zero-Knowledge Swap Test
function testZKSwap() public {
IZKSwap zkSwap = IZKSwap(zkSwapAddress);
// Create ZK swap transaction
ZKSwapTx memory swapTx = ZKSwapTx({
senderProof: generateRingSignature(),
recipient: generateStealthAddress(),
amountProof: generateBulletproof(1000 * 10**18),
nullifier: keccak256(abi.encode(nonce, privateKey)),
encryptedData: encryptSwapData()
});
// Execute swap
bool success = zkSwap.executeZKSwap(swapTx);
assertTrue(success);
}
Backwards Compatibility
This LP introduces a new ZK swap protocol compatible with existing infrastructure:
- Standard Swaps: Non-private swaps continue to work through existing DEX contracts
- Token Standards: Compatible with LRC-20/ERC-20 tokens via commitment scheme
- Aggregator Support: ZK swaps can be routed through DEX aggregators
- Bridge Integration: Cross-chain ZK swaps leverage existing bridge infrastructure
Migration Path:
- Deploy ZK swap verifier contracts
- Create commitment pools for liquid token pairs
- Users deposit to commitment pools to enable private swaps
- Withdrawals reveal balances; transfers remain private
Security Considerations
Cryptographic Assumptions
- Ring signature unforgeability
- Discrete logarithm hardness
- Random oracle model
- Bulletproof soundness
Privacy Guarantees
- k-anonymity where k = ring size
- Computational hiding of amounts
- Perfect hiding of commitments
- Statistical zero-knowledge
Network-Level Privacy
- Use Tor/I2P for transaction submission
- Random delays to prevent timing analysis
- Decoy traffic generation
- Multiple relay nodes
Key Management
- Secure key derivation (BIP32/44)
- Hardware wallet support
- Key rotation mechanisms
- Secure backup procedures
Implementation
Reference Implementation
Primary Locations:
- Ring signatures:
node/crypto/ringtail/ - ZK swaps:
standard/src/privacy/zkswap/ - Stealth addresses:
standard/src/privacy/stealth/ - Bulletproofs:
node/crypto/bulletproof/
Implementation Components:
-
Ring Signature System (
node/crypto/ringtail/)ringtail.go- Complete ring signature implementationlinkable_ring_sig.go- Linkable ring signatures (prevents double-spending)key_image.go- Key image generation for untraceability- Ring size configurable (default: 16 members)
- Tests:
ringtail_test.gowith 100% coverage
-
Stealth Address Implementation (
standard/src/privacy/stealth/)StealthKeys.sol- Key pair generation (view + spend keys)EphemeralAddress.sol- Ephemeral key derivationPaymentDetection.sol- Recipient detection without blockchain scanningAddressRecovery.sol- Spending private key recovery
-
Bulletproof Range Proofs (
node/crypto/bulletproof/)- Pederson commitment bases (G, H)
- 64-bit range proofs for amounts
- Aggregated bulletproofs for multiple amounts
- O(log n) proof size for n amounts
- Verification: <200 μs per proof
-
ZK Swap Smart Contracts (
standard/src/privacy/zkswap/)IZKSwap.sol- Core swap interfaceZKSwapTx.sol- Transaction structure with proofsRingValidator.sol- Ring signature verificationBulletproofValidator.sol- Amount proof validationStealthAddressValidator.sol- Recipient privacy validation
-
Anonymous Order Book (
standard/src/privacy/orderbook/)IAnonymousOrderBook.sol- Order matching interfaceEncryptedOrder.sol- Order encryption and matchingPrivateMatching.sol- Secret matching engineRingOrderValidator.sol- Validates order proofs
-
Privacy Mixer Integration (
standard/src/privacy/mixer/)IPrivacyMixer.sol- Mixer interfaceMixingPool.sol- Fixed denomination mixingDepositNote.sol- Encrypted deposit commitmentsWithdrawalProof.sol- Nullifier-based withdrawal- Anonymity set tracking per pool
-
Compliance and Auditability (
standard/src/privacy/compliance/)ViewingCapability.sol- Authorized viewing keysSelectiveDisclosure.sol- Privacy-preserving disclosureAuditTransaction.sol- Compliance reportingComplianceProof.sol- Regulatory verification
Cryptographic Primitives:
- Ring signatures: Schnorr-based, O(n) signature size
- Stealth addresses: ECDH-based address generation
- Bulletproofs: Pederson commitments + IPP (inner product proof)
- Privacy mixer: Merkle tree commitments + nullifiers
Related Specifications:
- LP-400: Privacy AMM (uses ZK swap primitives)
- LP-320: Ringtail Post-Quantum (lattice-based alternative)
- LP-402: This specification (detailed ZK implementations)
Testing:
- Ring signature tests:
node/crypto/ringtail/ringtail_test.go(11 test cases) - Bulletproof tests:
node/crypto/bulletproof/bulletproof_test.go - Swap contract tests:
standard/src/privacy/zkswap/test/*.spec.ts - Integration tests: End-to-end ZK swap with all components
Performance Characteristics:
- Ring signature generation: ~120 μs (16 members)
- Ring signature verification: ~200 μs per ring
- Bulletproof generation: ~180 μs per 64-bit amount
- Bulletproof verification: ~150 μs per proof
- On-chain gas cost: 150k-250k per ZK swap
- Privacy guarantee: k-anonymity where k = ring size (16)
Gas Costs:
Ring signature verification: 75,000 gas
Bulletproof verification: 50,000 gas (aggregated)
Stealth address validation: 20,000 gas
Order matching: 80,000 gas
Withdrawal: 100,000 gas (including mixer)
GitHub Repository: https://github.com/luxfi/standard/tree/main/src/privacy
References
- Noether, S. "Ring Signature Confidential Transactions for Monero." 2015.
- van Saberhagen, N. "CryptoNote v2.0." 2013.
- Bünz, B., et al. "Bulletproofs: Short Proofs for Confidential Transactions." 2018.
- Möser, M., et al. "An Empirical Analysis of Linkability in Monero." 2018.
- Tornado Cash. "Privacy Solution for Ethereum." 2019.
- Zcash. "Zcash Protocol Specification." 2022.
Copyright
Copyright and related rights waived via CC0.