commitment.rs
1 // Copyright (c) 2025 ADnet Contributors 2 // SPDX-License-Identifier: Apache-2.0 3 4 //! Token Commitment Scheme 5 //! 6 //! Implements hash-based commitments for hiding token amounts. 7 //! Uses SHA256 for a simple, portable implementation. 8 //! 9 //! Properties: 10 //! - Hiding: Cannot determine amount without randomness 11 //! - Binding: Cannot open to different amount 12 13 use anyhow::Result; 14 use serde::{Deserialize, Serialize}; 15 use sha2::{Digest, Sha256}; 16 17 // ============================================================================ 18 // Token Commitment 19 // ============================================================================ 20 21 /// A commitment to a DELTA token record 22 /// 23 /// Hides: owner address, amount 24 /// Reveals: nothing (commitment is random-looking hash) 25 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] 26 pub struct TokenCommitment { 27 /// The commitment hash 28 commitment: [u8; 32], 29 /// Commitment to owner address (for verification) 30 owner_hash: [u8; 32], 31 } 32 33 impl TokenCommitment { 34 /// Create a new token commitment 35 /// 36 /// Commits to both the owner and amount using hash-based commitment. 37 pub fn commit(owner: &[u8], amount: u64, randomness: u64) -> Result<Self> { 38 // Hash owner address 39 let owner_hash = Self::hash_owner(owner); 40 41 // Create commitment: H(owner || amount || randomness) 42 let mut hasher = Sha256::new(); 43 hasher.update(b"DELTA_TOKEN_COMMITMENT"); 44 hasher.update(owner_hash); 45 hasher.update(amount.to_le_bytes()); 46 hasher.update(randomness.to_le_bytes()); 47 let hash = hasher.finalize(); 48 49 let mut commitment = [0u8; 32]; 50 commitment.copy_from_slice(&hash); 51 52 Ok(Self { 53 commitment, 54 owner_hash, 55 }) 56 } 57 58 /// Hash an owner address 59 fn hash_owner(owner: &[u8]) -> [u8; 32] { 60 let mut hasher = Sha256::new(); 61 hasher.update(b"DELTA_OWNER_HASH"); 62 hasher.update(owner); 63 let hash = hasher.finalize(); 64 let mut result = [0u8; 32]; 65 result.copy_from_slice(&hash); 66 result 67 } 68 69 /// Get commitment as bytes 70 pub fn as_bytes(&self) -> &[u8; 32] { 71 &self.commitment 72 } 73 74 /// Get owner hash 75 pub fn owner_hash(&self) -> &[u8; 32] { 76 &self.owner_hash 77 } 78 79 /// Verify commitment is well-formed (non-zero) 80 pub fn verify(&self) -> bool { 81 self.commitment != [0u8; 32] 82 } 83 84 /// Create from raw bytes (for deserialization) 85 pub fn from_bytes(commitment: [u8; 32], owner_hash: [u8; 32]) -> Self { 86 Self { 87 commitment, 88 owner_hash, 89 } 90 } 91 } 92 93 // ============================================================================ 94 // Commitment Scheme (Placeholder for Pedersen) 95 // ============================================================================ 96 97 /// Hash-based commitment scheme for DELTA tokens 98 /// (Production would use Pedersen commitments) 99 pub struct CommitmentScheme; 100 101 impl CommitmentScheme { 102 /// Compute commitment: H(value || blinding) 103 pub fn commit(value: u64, blinding: u64) -> [u8; 32] { 104 let mut hasher = Sha256::new(); 105 hasher.update(b"DELTA_PEDERSEN_COMMIT"); 106 hasher.update(value.to_le_bytes()); 107 hasher.update(blinding.to_le_bytes()); 108 let hash = hasher.finalize(); 109 110 let mut result = [0u8; 32]; 111 result.copy_from_slice(&hash); 112 result 113 } 114 115 /// Verify a commitment is well-formed 116 pub fn verify_commitment(commitment: &[u8; 32]) -> bool { 117 *commitment != [0u8; 32] 118 } 119 } 120 121 // ============================================================================ 122 // Tests 123 // ============================================================================ 124 125 #[cfg(test)] 126 mod tests { 127 use super::*; 128 129 #[test] 130 fn test_commitment_creation() { 131 let owner = b"test_owner_address"; 132 let amount = 1_000_000u64; 133 let randomness = 12345u64; 134 135 let commitment = TokenCommitment::commit(owner, amount, randomness).unwrap(); 136 assert!(commitment.verify()); 137 } 138 139 #[test] 140 fn test_different_amounts_different_commitments() { 141 let owner = b"test_owner"; 142 let randomness = 12345u64; 143 144 let c1 = TokenCommitment::commit(owner, 100, randomness).unwrap(); 145 let c2 = TokenCommitment::commit(owner, 200, randomness).unwrap(); 146 147 assert_ne!(c1.as_bytes(), c2.as_bytes()); 148 } 149 150 #[test] 151 fn test_commitment_scheme() { 152 let c1 = CommitmentScheme::commit(100, 999); 153 let c2 = CommitmentScheme::commit(100, 1000); 154 155 assert!(CommitmentScheme::verify_commitment(&c1)); 156 assert_ne!(c1, c2); // Different blinding -> different commitment 157 } 158 }