rustywallet_lightning/
channel.rs1use crate::error::LightningError;
7use std::fmt;
8use std::str::FromStr;
9
10#[derive(Debug, Clone, PartialEq, Eq, Hash)]
14pub struct ChannelPoint {
15 txid: [u8; 32],
17 output_index: u32,
19}
20
21impl ChannelPoint {
22 pub fn new(txid: [u8; 32], output_index: u32) -> Self {
24 Self { txid, output_index }
25 }
26
27 pub fn from_parts(txid_hex: &str, output_index: u32) -> Result<Self, LightningError> {
29 let bytes = hex::decode(txid_hex)
30 .map_err(|e| LightningError::InvalidChannelPoint(e.to_string()))?;
31
32 if bytes.len() != 32 {
33 return Err(LightningError::InvalidChannelPoint(format!(
34 "Expected 32 bytes for txid, got {}",
35 bytes.len()
36 )));
37 }
38
39 let mut txid = [0u8; 32];
40 txid.copy_from_slice(&bytes);
41
42 Ok(Self { txid, output_index })
43 }
44
45 pub fn parse(s: &str) -> Result<Self, LightningError> {
47 s.parse()
48 }
49
50 pub fn txid(&self) -> &[u8; 32] {
52 &self.txid
53 }
54
55 pub fn txid_hex(&self) -> String {
57 let mut reversed = self.txid;
59 reversed.reverse();
60 hex::encode(reversed)
61 }
62
63 pub fn output_index(&self) -> u32 {
65 self.output_index
66 }
67
68 pub fn to_string_format(&self) -> String {
70 format!("{}:{}", self.txid_hex(), self.output_index)
71 }
72
73 pub fn txid_bytes(&self) -> &[u8; 32] {
75 &self.txid
76 }
77}
78
79impl fmt::Display for ChannelPoint {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 write!(f, "{}", self.to_string_format())
82 }
83}
84
85impl FromStr for ChannelPoint {
86 type Err = LightningError;
87
88 fn from_str(s: &str) -> Result<Self, Self::Err> {
89 let parts: Vec<&str> = s.split(':').collect();
90 if parts.len() != 2 {
91 return Err(LightningError::InvalidChannelPoint(
92 "Expected format 'txid:index'".into(),
93 ));
94 }
95
96 let txid_hex = parts[0];
97 let output_index: u32 = parts[1]
98 .parse()
99 .map_err(|e| LightningError::InvalidChannelPoint(format!("Invalid index: {}", e)))?;
100
101 let bytes = hex::decode(txid_hex)
103 .map_err(|e| LightningError::InvalidChannelPoint(e.to_string()))?;
104
105 if bytes.len() != 32 {
106 return Err(LightningError::InvalidChannelPoint(format!(
107 "Expected 32 bytes for txid, got {}",
108 bytes.len()
109 )));
110 }
111
112 let mut txid = [0u8; 32];
113 txid.copy_from_slice(&bytes);
114 txid.reverse();
116
117 Ok(Self { txid, output_index })
118 }
119}
120
121#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
125pub struct ShortChannelId(u64);
126
127impl ShortChannelId {
128 pub fn new(block_height: u32, tx_index: u32, output_index: u16) -> Self {
130 let scid = ((block_height as u64) << 40)
131 | ((tx_index as u64) << 16)
132 | (output_index as u64);
133 Self(scid)
134 }
135
136 pub fn from_u64(value: u64) -> Self {
138 Self(value)
139 }
140
141 pub fn parse(s: &str) -> Result<Self, LightningError> {
143 let parts: Vec<&str> = s.split('x').collect();
144 if parts.len() != 3 {
145 return Err(LightningError::InvalidChannelPoint(
146 "Expected format 'blockxTxxoutput'".into(),
147 ));
148 }
149
150 let block: u32 = parts[0]
151 .parse()
152 .map_err(|e| LightningError::InvalidChannelPoint(format!("Invalid block: {}", e)))?;
153 let tx: u32 = parts[1]
154 .parse()
155 .map_err(|e| LightningError::InvalidChannelPoint(format!("Invalid tx: {}", e)))?;
156 let output: u16 = parts[2]
157 .parse()
158 .map_err(|e| LightningError::InvalidChannelPoint(format!("Invalid output: {}", e)))?;
159
160 Ok(Self::new(block, tx, output))
161 }
162
163 pub fn as_u64(&self) -> u64 {
165 self.0
166 }
167
168 pub fn block_height(&self) -> u32 {
170 (self.0 >> 40) as u32
171 }
172
173 pub fn tx_index(&self) -> u32 {
175 ((self.0 >> 16) & 0xFFFFFF) as u32
176 }
177
178 pub fn output_index(&self) -> u16 {
180 (self.0 & 0xFFFF) as u16
181 }
182}
183
184impl fmt::Display for ShortChannelId {
185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186 write!(
187 f,
188 "{}x{}x{}",
189 self.block_height(),
190 self.tx_index(),
191 self.output_index()
192 )
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199
200 #[test]
201 fn test_channel_point_parse() {
202 let cp_str = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef:1";
203 let cp = ChannelPoint::parse(cp_str).unwrap();
204
205 assert_eq!(cp.output_index(), 1);
206 assert_eq!(cp.to_string(), cp_str);
207 }
208
209 #[test]
210 fn test_channel_point_from_parts() {
211 let txid = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
212 let cp = ChannelPoint::from_parts(txid, 0).unwrap();
213
214 assert_eq!(cp.output_index(), 0);
215 }
216
217 #[test]
218 fn test_short_channel_id() {
219 let scid = ShortChannelId::new(700000, 1234, 0);
220
221 assert_eq!(scid.block_height(), 700000);
222 assert_eq!(scid.tx_index(), 1234);
223 assert_eq!(scid.output_index(), 0);
224 }
225
226 #[test]
227 fn test_short_channel_id_display() {
228 let scid = ShortChannelId::new(700000, 1234, 1);
229 assert_eq!(scid.to_string(), "700000x1234x1");
230 }
231
232 #[test]
233 fn test_short_channel_id_parse() {
234 let scid = ShortChannelId::parse("700000x1234x1").unwrap();
235
236 assert_eq!(scid.block_height(), 700000);
237 assert_eq!(scid.tx_index(), 1234);
238 assert_eq!(scid.output_index(), 1);
239 }
240}