KeyPack: A Transaction Protocol for Grin
WalletLayer Enhancement for MimbleWimble Transactions with Advanced Privacy Through Transaction Unlinking
Abstract
This paper presents KeyPack, a walletlayer protocol that enhances Grin’s transaction model without modifying the underlying MimbleWimble protocol. The proposed method splits transactions into two atomic parts while maintaining security through encrypted keys, creating unlinked transactions that improve privacy by breaking transaction graph analysis. The protocol reduces the standard threestep interactive process to a single step, enables offline receiving, and enhances MimbleWimble privacy properties through transaction unlinking. The approach creates distinct, temporally separated transactions that appear unrelated on the blockchain, making transaction graph analysis substantially more difficult. The paper provides comprehensive mathematical proofs of security, enhanced privacy preservation through unlinking, and proper fee handling.
Table of Contents
 [Introduction]
 [Background]
 [The KeyPack Protocol]
 [Security Analysis]
 [Privacy Considerations]
 [Fee Handling]
 [Compression Techniques]
 [Edge Cases and Mitigations]
 [Conclusions]
1. Introduction
1.1 Problem Statement
Grin’s current transaction model presents several significant challenges:

Complex Transaction Flow: The current threestep interactive process requires:
Sender (slate_v1) → Receiver (slate_v2) → Sender (slate_v3)
This necessitates simultaneous online presence and creates high coordination complexity.

Payment Proof Limitations: While supported, current payment proofs suffer from:
 Complex exchange processes
 Nonstandardized implementation
 Integration difficulties

User Experience Barriers: Current implementation creates friction through:
 Unfamiliar transaction models
 High cognitive load
 Poor mobile experience
1.2 Key Innovations
KeyPack protocol provides:
 Enhanced privacy through transaction unlinking, making transaction graph analysis more difficult
 Temporally separated, seemingly unrelated transactions that mask transaction flow
 Reduced transaction steps with maintained security
 Integrated robust payment proofs
 Enhanced MimbleWimble privacy properties
 No consensus changes required
The protocol’s primary innovation lies in creating two distinct, apparently unrelated transactions on the blockchain. This unlinking property improves privacy by:
 Breaking direct transaction graph connections
 Creating temporal separation between transaction components
 Increasing difficulty of correlation attacks
 Preventing formation of clear transaction patterns
 Masking senderreceiver relationships
2. Background
2.1 MimbleWimble Fundamentals
MimbleWimble transactions use Pedersen Commitments:
 C(r, v) = r·H + v·G
where: r is the blinding factor
 v is the value
 H, G are generator points
 · denotes scalar multiplication
2.2 Standard Grin Transaction Structure
A typical Grin transaction consists of:
 Inputs: C(r_in, v) = r_in·H + v·G
 Outputs: C(r_out, v) = r_out·H + v·G
 Kernel: K = (r_in  r_out)·H
 Kernel Signature: s = k + e*r where:
 k is the nonce
 e is the challenge
 r is the kernel excess (r_in  r_out)
3. The KeyPack Protocol
3.1 Core Concept
KeyPack decomposes a standard Grin transaction into two cryptographically linked but temporally separated transactions. This decomposition maintains atomic operation through encrypted key management while breaking transaction graph linkability.
3.1.1 Transaction Decomposition
Standard Grin transaction A → B is decomposed into:
TX1: A → T (Temporary output with encrypted key for B)
TX2: T → B (Automatic execution when B receives key)
3.1.2 Detailed Transaction Structure
First Transaction (A → T):
Components:
1. Input:
 C(r_a, v) = r_a·H + v·G
 Commitment from A's UTXO set
 Standard Pedersen commitment
2. Temporary Output:
 C(r_t, v) = r_t·H + v·G
 r_t is encrypted for B
 Uses temporary blinding factor
3. Kernel K1:
 K1 = (r_a  r_t)·H
 Standard kernel excess
 Includes encrypted payload
4. Transaction Data:
 Encrypted r_t for B
 Payment proof components
 Version information
 Metadata (timestamp, network)
Second Transaction (T → B):
Components:
1. Input:
 C(r_t, v) = r_t·H + v·G
 Uses decrypted r_t
 Spends temporary output
2. Final Output:
 C(r_b, vf) = r_b·H + (vf)·G
 B's receiving address
 Includes fee deduction
3. Kernel K2:
 K2 = (r_t  r_b)·H + f·G
 Commits to transaction fee
 Links to payment proof
4. Transaction Data:
 Complete payment proof
 Fee commitment
 Standard metadata
3.1.10 Nonrepudiation and Dispute Prevention
KeyPack’s encrypted mode provides cryptographic proof of transaction intent and execution, preventing disputes about coin transmission.
Cryptographic Evidence Chain:
1. Sender Proof Generation:
 Transaction kernel K1 signed by sender
 Encrypted r_t bound to recipient's key
 Payment proof σ₁ with amount commitment
 Timestamped blockchain record
2. Recipient Validation Chain:
recipient_access = ECDH(recipient_priv, sender_pub)
if decrypt(r_t, recipient_access) succeeds:
 Proves sender intended recipient
 Proves correct amount
 Proves sender authorization
 Provides temporal proof
Dispute Resolution Properties:
1. Sender Cannot Deny:
 Kernel signature proves sending intent
 Encrypted key proves intended recipient
 Blockchain timestamp proves timing
 Amount commitment proves value
2. Recipient Cannot Deny:
 Key decryption proves receipt ability
 Second transaction proves access
 Blockchain records prove timing
 Kernel linkage proves completeness
3. Third Party Verification:
 All proofs publicly verifiable
 Temporal sequence confirmed
 Amount commitments validated
 Cryptographic linkage verified
Protocol Evidence Trail:
Evidence Components:
1. First Transaction:
 Kernel commitment K1
 Sender signature σ₁
 Encrypted key data
 Amount commitment
2. Recipient Access:
 Successful key decryption
 Second transaction K2
 Linked payment proof
 Blockchain inclusion
Dispute Prevention Mechanisms:
1. Transaction Intent:
if (verify_kernel_signature(K1, sender_pub) &&
verify_key_encryption(r_t, recipient_pub)) {
// Proves sender intended this recipient
// Proves specific amount
UNDENIABLE_INTENT = true
}
2. Receipt Capability:
if (decrypt_temp_key(r_t, recipient_priv) &&
verify_second_tx(K2, r_t)) {
// Proves recipient could access funds
// Proves transaction completion
UNDENIABLE_RECEIPT = true
}
Comparison with Unencrypted Mode:
Encrypted Mode:
1. Proof Properties:
 Recipientspecific intent provable
 Access capability provable
 Temporal sequence verifiable
 Complete evidence chain
2. Dispute Resolution:
 Clear responsibility assignment
 Cryptographic evidence
 Thirdparty verifiable
 Temporal proof available
Unencrypted Mode:
1. Proof Properties:
 General sending intent only
 No specific recipient binding
 Temporal sequence only
 Incomplete evidence chain
2. Dispute Scenarios:
 Recipient uncertainty
 Intent ambiguity
 Claim race conditions
 Limited evidence
Forensic Analysis Capabilities:
1. Transaction Validation:
verify_complete_transaction(tx_data):
kernel1 = verify_first_transaction()
key_encryption = verify_recipient_binding()
kernel2 = verify_second_transaction()
return {
sender_intent: kernel1.valid,
recipient_bound: key_encryption.valid,
completion_proof: kernel2.valid,
temporal_proof: blockchain_timestamps
}
2. Evidence Collection:
collect_dispute_evidence(tx_id):
return {
sender_proofs: {
kernel_signature: K1.signature,
encrypted_key: r_t_encrypted,
payment_proof: σ₁,
blockchain_record: block_data
},
recipient_proofs: {
key_decryption: decrypt_proof,
second_tx: K2,
temporal_data: timestamps
}
}
This cryptographic evidence chain makes encrypted mode particularly suitable for:
 Commercial transactions requiring proof
 Exchange integrations needing audit
 Regulated environments
 Disputeprone scenarios
 Highvalue transfers
The undeniable cryptographic proof of sending intent to a specific recipient, combined with proof of recipient access capability, creates a complete evidence chain that prevents disputes about whether coins were actually sent and to whom.
In contrast, unencrypted mode lacks these dispute resolution properties as it cannot prove specific recipient intent, making it unsuitable for scenarios where transaction nonrepudiation is required.
Implementation Differences:
Encrypted Mode:
1. Key Distribution:
encrypted_r_t = ChaCha20Poly1305(
encryption_key,
r_t,
associated_data
)
2. Access Control:
 Only intended recipient can decrypt
 Transaction bound to recipient
 Failed attempts traceable
Unencrypted Mode:
1. Key Distribution:
clear_r_t = r_t // Direct publication
2. Access Control:
 Any party can use r_t
 Firsttoclaim model
 Racing condition possible
Use Case Analysis:
Encrypted Mode Optimal For:
1. Direct payments
2. Exchange transactions
3. Guaranteed delivery
4. Privacycritical transfers
5. Known recipient scenarios
Unencrypted Mode Enables:
1. Atomic swaps
2. Payment channels
3. Lightningstyle networks
4. Prize/reward distributions
5. Firsttoclaim games
Security Implications:
Encrypted Mode:
1. Privacy Properties:
 Recipient privacy preserved
 Transaction graph obscured
 Full unlinkability maintained
2. Security Guarantees:
 Guaranteed recipient
 No frontrunning possible
 Replay protection builtin
Unencrypted Mode:
1. Privacy Properties:
 Recipient privacy optional
 Transaction graph partially visible
 Conditional unlinkability
2. Security Considerations:
 Frontrunning possible
 Race conditions exist
 Requires additional protections
Protocol Modifications:
Transaction Header:
struct KeyPackHeader {
version: u8,
flags: u8,
encryption_mode: EncryptionMode,
...
}
enum EncryptionMode {
Encrypted {
recipient_pubkey: PublicKey,
encrypted_key: Vec<u8>
},
Unencrypted {
clear_key: Vec<u8>
}
}
Mode Selection Logic:
fn determine_encryption_mode(
transaction_type: TransactionType,
recipient_known: bool,
privacy_level: PrivacyLevel
) > EncryptionMode {
match (transaction_type, recipient_known, privacy_level) {
// Standard payment to known recipient
(TransactionType::Direct, true, _) =>
EncryptionMode::Encrypted,
// Atomic swap or similar
(TransactionType::Swap, _, _) =>
EncryptionMode::Unencrypted,
// High privacy requirement
(_, _, PrivacyLevel::Maximum) =>
EncryptionMode::Encrypted,
// Default to encrypted for safety
_ => EncryptionMode::Encrypted
}
}
Usage Considerations:

Default Behavior:
 Encrypted mode default  Explicit optout required  Warning for unencrypted use

Security Recommendations:
 Use encrypted mode for direct payments  Unencrypted only with additional protections  Consider privacy implications

Implementation Requirements:
Encrypted Mode:  Full KeyPack implementation  Key management system  Recipient public key Unencrypted Mode:  Simplified KeyPack  Additional safety checks  Race condition handling
The choice between encrypted and unencrypted modes represents a tradeoff between security guarantees and protocol flexibility. While encrypted mode provides the strongest security and privacy properties, unencrypted mode enables advanced features and novel use cases.
For typical transactions, encrypted mode remains the recommended default. Unencrypted mode should be used only in specific scenarios where its unique properties are required, and additional security measures can be implemented.
3.1.4 Transaction Flow Detail
Sending Process:
1. Wallet Preparation:
 Select inputs
 Calculate change
 Generate r_t
 Create payment proof
2. First Transaction Creation:
 Build A → T transaction
 Encrypt r_t for B
 Sign transaction
 Include metadata
3. First Transaction Broadcast:
 Submit to network
 Wait for confirmation
 Monitor temporary output
Receiving Process:
1. Key Recovery:
 Detect transaction
 Derive shared secret
 Decrypt r_t
 Verify payment proof
2. Second Transaction Creation:
 Build T → B transaction
 Calculate fees
 Sign transaction
 Link payment proof
3. Second Transaction Broadcast:
 Submit to network
 Monitor confirmation
 Update wallet state
3.1.5 CutThrough and Blockchain State
Final Blockchain State:
1. Visible Components:
 Input: C(r_a, v)
 Output: C(r_b, vf)
 Kernels: K1, K2
2. Removed by CutThrough:
 Temporary output C(r_t, v)
 All intermediate states
3. Net Effect:
((r_a  r_b)·H) + f·G // Final kernel with fee
3.1.6 Payment Proof Integration
Proof Structure:
1. First Transaction Proof:
E1 = r_excess1·H
σ1 = k1 + e1·r_excess1
msg1 = hash(
amount 
kernel_features 
metadata
)
2. Second Transaction Proof:
E2 = r_excess2·H
σ2 = k2 + e2·r_excess2
msg2 = hash(
amount 
kernel_features 
metadata
)
3. Combined Proof:
P = {
E1, σ1, msg1, // First transaction
E2, σ2, msg2, // Second transaction
linking_data // Crosstransaction validation
}
3.1.7 Timelock Mechanism
1. Temporary Output Timelock:
 Minimum lock: current_height + min_conf
 Maximum lock: current_height + max_lock
 Default: current_height + 1440 (≈24 hours)
2. Recovery Conditions:
 Automatic return if not spent
 Sender can reclaim after timeout
 Configurable timeouts
3.1.8 Fee Handling Detail
1. First Transaction:
 No fee required
 Standard weight calculation
 Zero fee commitment
2. Second Transaction:
fee = weight * fee_base_rate
weight = kernel_weight + // Two kernels
input_weight + // Single input
output_weight // Final output
3. Fee Commitment in Kernel:
K2 = (r_t  r_b)·H + f·G // Explicit fee term
This expanded core concept provides a comprehensive technical foundation for understanding the complete KeyPack protocol implementation.
3.2 Temporary Key Encryption
The temporary key (r_t) is encrypted using:

Shared Secret Generation:
s = ECDH(Alice_priv, Bob_pub)

Key Derivation:
k = KDF(s  transaction_data)

Encryption:
c = ChaCha20(k, r_t)
4. Security Analysis
4.1 Value Conservation Proof
Theorem 1: KeyPack preserves value conservation.
Proof:

Initial state before cutthrough:
(r_a·H + v·G) + // Input from Alice (r_t·H + v·G)  // Output to Temp (r_t·H + v·G) + // Input from Temp (r_b·H + v·G) // Output to Bob

After combining terms:
(r_a  r_b)·H + 0·G

This proves:
 Value is conserved (0·G term)
 No coins created/destroyed
 Blinding factors balance
4.2 Enhanced Transaction Privacy Through Unlinking
Theorem 2: KeyPack enhances transaction privacy through temporal and structural unlinking.
Proof:

For any outputs A, B in a standard transaction:
P(link(A,B)) = 1/N
where N is the total number of outputs

For KeyPack’s split transactions A₁, A₂:
P(link(A₁,A₂)) = 1/N²
This quadratic improvement occurs because:
 Transactions appear at different times
 No structural links exist between A₁ and A₂
 Independent kernel commitments

The unlinking property holds because:
 Cutthrough removes C(r_t, v)
 Kernels K1, K2 are temporally separated
 Kernels are independently random and unlinkable
 Each transaction appears as a standard Grin transaction
 No observable correlation between parts exists

Transaction Graph Analysis Resistance:
For an adversary attempting to link transactions:P(identify_true_flow) = P(link(A₁,A₂)) * P(temporal_correlation)
where:
 P(temporal_correlation) ≤ 1/T for time window T
 Total linkability probability becomes 1/(N²T)
This demonstrates that KeyPack provides substantially stronger privacy than standard transactions by:
 Creating temporal separation
 Breaking structural links
 Generating independent kernels
 Masking transaction flow
 Preventing graph analysis
5. Privacy Considerations
5.1 Cryptographic Assumptions
Security relies on:

Discrete Logarithm Problem:
Given P = x·G, finding x is hard

ECDH Security:
s = ECDH(a, B) = ECDH(b, A) where: A = a·G, B = b·G

ChaCha20Poly1305 Security:
 INDCCA2 secure
 Authenticated encryption
5.2 Privacy Guarantees

Transaction Unlinkability:
∀ tx1, tx2: P(linked(tx1, tx2)) = P(random_guess)

Amount Confidentiality:
 Hidden by Pedersen commitments
 No leakage in proofs
6. Fee Handling
6.1 Mathematical Structure
Transaction 1 (Alice → Temp):
Inputs: C(r_a, v) = r_a·H + v·G
Outputs: C(r_t, v) = r_t·H + v·G
Kernel1: K1 = (r_a  r_t)·H
Fee1: 0
Transaction 2 (Temp → Bob):
Inputs: C(r_t, v) = r_t·H + v·G
Outputs: C(r_b, vf) = r_b·H + (vf)·G
Kernel2: K2 = (r_t  r_b)·H + f·G
Fee2: f
6.2 Fee Conservation Proof
Theorem 3: KeyPack preserves proper fee handling.
Proof:

Initial state (all components):
(r_a·H + v·G) + // Input from Alice (r_t·H + v·G)  // Output to Temp (r_t·H + v·G) + // Input from Temp (r_b·H + (vf)·G)  // Output to Bob (r_a  r_t)·H + // Kernel1 (r_t  r_b)·H + f·G // Kernel2 with fee

After combining terms:
((r_a  r_b)·H) + f·G
This proves:
 Value conservation including fee
 Single fee payment for entire operation
 Proper fee commitment in kernel
7. Compression Techniques
7.1 Key Compression
For public key P(x,y):
compressed = sign_bit(y)  x_coordinate
Compression ratio: ~50%
7.2 Nonce Derivation
nonce = blake2b(
kernel_commitment 
amount 
height_delta
)[0..8]
7.3 Size Analysis
Final compressed format:
struct CompressedKeyPack {
enc_key: [u8; 33], // Compressed key
nonce_data: [u8; 8], // Derived nonce
amount: u64, // Amount
height_delta: u16, // Block height difference
proof: [u8; 32], // Compact proof
}
Total size: ~83 bytes (39% reduction)
8. Edge Cases and Mitigations
8.1 Network Partitions
Protected by:
 Timelock prevents hanging
 Funds return to sender after timeout
 No permanent state changes
8.2 Frontrunning Protection
Secured through:
 Recipientspecific encryption
 Unique transaction data
 Timelock enforcement
9. Conclusions
KeyPack implements:
 Mathematically proven security
 Full MimbleWimble compatibility
 Strong payment proofs
 Enhanced privacy preservation
Requirements:
 No consensus changes
 No protocol modifications
 Walletlayer updates only
All proofs rely on standard cryptographic assumptions and protocol design principles.
References
[1] MimbleWimble Protocol Specification
[2] Pedersen Commitments in Cryptography
[3] ChaCha20Poly1305 AEAD Specification
[4] Schnorr Signature Scheme
[5] ECDH Key Exchange Protocol