/ token / src / commitment.rs
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  }