rustywallet_lightning/
route.rs1use crate::channel::ShortChannelId;
7use crate::node::NodeId;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct RouteHint {
15 hops: Vec<RouteHintHop>,
17}
18
19impl RouteHint {
20 pub fn new() -> Self {
22 Self { hops: Vec::new() }
23 }
24
25 pub fn with_hops(hops: Vec<RouteHintHop>) -> Self {
27 Self { hops }
28 }
29
30 pub fn add_hop(&mut self, hop: RouteHintHop) {
32 self.hops.push(hop);
33 }
34
35 pub fn hops(&self) -> &[RouteHintHop] {
37 &self.hops
38 }
39
40 pub fn len(&self) -> usize {
42 self.hops.len()
43 }
44
45 pub fn is_empty(&self) -> bool {
47 self.hops.is_empty()
48 }
49}
50
51impl Default for RouteHint {
52 fn default() -> Self {
53 Self::new()
54 }
55}
56
57#[derive(Debug, Clone, PartialEq, Eq)]
59pub struct RouteHintHop {
60 pub src_node_id: NodeId,
62 pub short_channel_id: ShortChannelId,
64 pub fee_base_msat: u32,
66 pub fee_proportional_millionths: u32,
68 pub cltv_expiry_delta: u16,
70}
71
72impl RouteHintHop {
73 pub fn new(
75 src_node_id: NodeId,
76 short_channel_id: ShortChannelId,
77 fee_base_msat: u32,
78 fee_proportional_millionths: u32,
79 cltv_expiry_delta: u16,
80 ) -> Self {
81 Self {
82 src_node_id,
83 short_channel_id,
84 fee_base_msat,
85 fee_proportional_millionths,
86 cltv_expiry_delta,
87 }
88 }
89
90 pub fn fee_for_amount(&self, amount_msat: u64) -> u64 {
92 let base = self.fee_base_msat as u64;
93 let proportional = (amount_msat * self.fee_proportional_millionths as u64) / 1_000_000;
94 base + proportional
95 }
96}
97
98pub struct RouteHintBuilder {
100 hops: Vec<RouteHintHop>,
101}
102
103impl RouteHintBuilder {
104 pub fn new() -> Self {
106 Self { hops: Vec::new() }
107 }
108
109 pub fn hop(
111 mut self,
112 src_node_id: NodeId,
113 short_channel_id: ShortChannelId,
114 fee_base_msat: u32,
115 fee_proportional_millionths: u32,
116 cltv_expiry_delta: u16,
117 ) -> Self {
118 self.hops.push(RouteHintHop::new(
119 src_node_id,
120 short_channel_id,
121 fee_base_msat,
122 fee_proportional_millionths,
123 cltv_expiry_delta,
124 ));
125 self
126 }
127
128 pub fn build(self) -> RouteHint {
130 RouteHint::with_hops(self.hops)
131 }
132}
133
134impl Default for RouteHintBuilder {
135 fn default() -> Self {
136 Self::new()
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143
144 fn test_node_id() -> NodeId {
145 NodeId::from_bytes([2u8; 33])
146 }
147
148 #[test]
149 fn test_route_hint_creation() {
150 let hint = RouteHint::new();
151 assert!(hint.is_empty());
152 }
153
154 #[test]
155 fn test_route_hint_with_hops() {
156 let hop = RouteHintHop::new(
157 test_node_id(),
158 ShortChannelId::new(700000, 1, 0),
159 1000,
160 100,
161 144,
162 );
163
164 let hint = RouteHint::with_hops(vec![hop]);
165 assert_eq!(hint.len(), 1);
166 }
167
168 #[test]
169 fn test_fee_calculation() {
170 let hop = RouteHintHop::new(
171 test_node_id(),
172 ShortChannelId::new(700000, 1, 0),
173 1000, 1000, 144,
176 );
177
178 assert_eq!(hop.fee_for_amount(1_000_000), 2000);
183 }
184
185 #[test]
186 fn test_route_hint_builder() {
187 let hint = RouteHintBuilder::new()
188 .hop(
189 test_node_id(),
190 ShortChannelId::new(700000, 1, 0),
191 1000,
192 100,
193 144,
194 )
195 .hop(
196 test_node_id(),
197 ShortChannelId::new(700001, 2, 1),
198 500,
199 50,
200 40,
201 )
202 .build();
203
204 assert_eq!(hint.len(), 2);
205 }
206}