rustywallet_lightning/
payment.rs1use crate::error::LightningError;
7use sha2::{Digest, Sha256};
8use std::fmt;
9
10#[derive(Clone, PartialEq, Eq)]
16pub struct PaymentPreimage([u8; 32]);
17
18impl PaymentPreimage {
19 pub fn from_bytes(bytes: [u8; 32]) -> Self {
21 Self(bytes)
22 }
23
24 pub fn from_hex(hex: &str) -> Result<Self, LightningError> {
26 let bytes = hex::decode(hex)
27 .map_err(|e| LightningError::InvalidPreimage(e.to_string()))?;
28
29 if bytes.len() != 32 {
30 return Err(LightningError::InvalidPreimage(format!(
31 "Expected 32 bytes, got {}",
32 bytes.len()
33 )));
34 }
35
36 let mut arr = [0u8; 32];
37 arr.copy_from_slice(&bytes);
38 Ok(Self(arr))
39 }
40
41 pub fn random() -> Self {
43 use rand::RngCore;
44 let mut bytes = [0u8; 32];
45 rand::rngs::OsRng.fill_bytes(&mut bytes);
46 Self(bytes)
47 }
48
49 pub fn as_bytes(&self) -> &[u8; 32] {
51 &self.0
52 }
53
54 pub fn to_hex(&self) -> String {
56 hex::encode(self.0)
57 }
58
59 pub fn payment_hash(&self) -> PaymentHash {
61 let mut hasher = Sha256::new();
62 hasher.update(self.0);
63 let result = hasher.finalize();
64
65 let mut hash = [0u8; 32];
66 hash.copy_from_slice(&result);
67 PaymentHash(hash)
68 }
69}
70
71impl fmt::Debug for PaymentPreimage {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 write!(f, "PaymentPreimage([REDACTED])")
74 }
75}
76
77impl fmt::Display for PaymentPreimage {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 write!(f, "{}", self.to_hex())
80 }
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
88pub struct PaymentHash([u8; 32]);
89
90impl PaymentHash {
91 pub fn from_bytes(bytes: [u8; 32]) -> Self {
93 Self(bytes)
94 }
95
96 pub fn from_hex(hex: &str) -> Result<Self, LightningError> {
98 let bytes = hex::decode(hex)
99 .map_err(|e| LightningError::InvalidPaymentHash(e.to_string()))?;
100
101 if bytes.len() != 32 {
102 return Err(LightningError::InvalidPaymentHash(format!(
103 "Expected 32 bytes, got {}",
104 bytes.len()
105 )));
106 }
107
108 let mut arr = [0u8; 32];
109 arr.copy_from_slice(&bytes);
110 Ok(Self(arr))
111 }
112
113 pub fn as_bytes(&self) -> &[u8; 32] {
115 &self.0
116 }
117
118 pub fn to_hex(&self) -> String {
120 hex::encode(self.0)
121 }
122
123 pub fn verify(&self, preimage: &PaymentPreimage) -> bool {
125 preimage.payment_hash() == *self
126 }
127}
128
129impl fmt::Display for PaymentHash {
130 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131 write!(f, "{}", self.to_hex())
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn test_preimage_to_hash() {
141 let preimage = PaymentPreimage::from_bytes([0u8; 32]);
142 let hash = preimage.payment_hash();
143
144 assert_eq!(
146 hash.to_hex(),
147 "66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925"
148 );
149 }
150
151 #[test]
152 fn test_random_preimage() {
153 let preimage1 = PaymentPreimage::random();
154 let preimage2 = PaymentPreimage::random();
155
156 assert_ne!(preimage1.as_bytes(), preimage2.as_bytes());
157 }
158
159 #[test]
160 fn test_hash_verification() {
161 let preimage = PaymentPreimage::random();
162 let hash = preimage.payment_hash();
163
164 assert!(hash.verify(&preimage));
165
166 let wrong_preimage = PaymentPreimage::random();
167 assert!(!hash.verify(&wrong_preimage));
168 }
169
170 #[test]
171 fn test_hex_roundtrip() {
172 let preimage = PaymentPreimage::random();
173 let hex = preimage.to_hex();
174 let recovered = PaymentPreimage::from_hex(&hex).unwrap();
175
176 assert_eq!(preimage.as_bytes(), recovered.as_bytes());
177 }
178
179 #[test]
180 fn test_preimage_debug_redacted() {
181 let preimage = PaymentPreimage::random();
182 let debug = format!("{:?}", preimage);
183
184 assert!(debug.contains("REDACTED"));
185 assert!(!debug.contains(&preimage.to_hex()));
186 }
187}