@@ -211,6 +211,55 @@ fn assign_slots_for_channel(
211211 Ok ( channel_slots)
212212}
213213
214+ struct BucketResources {
215+ slots_allocated : u16 ,
216+ slots_used : u16 ,
217+ liquidity_allocated : u64 ,
218+ liquidity_used : u64 ,
219+ }
220+
221+ impl BucketResources {
222+ fn new ( slots_allocated : u16 , liquidity_allocated : u64 ) -> Self {
223+ BucketResources { slots_allocated, slots_used : 0 , liquidity_allocated, liquidity_used : 0 }
224+ }
225+
226+ fn resources_available ( & self , htlc_amount_msat : u64 ) -> bool {
227+ return ( self . liquidity_used + htlc_amount_msat <= self . liquidity_allocated )
228+ && ( self . slots_used < self . slots_allocated ) ;
229+ }
230+
231+ fn add_htlc ( & mut self , htlc_amount_msat : u64 ) -> Result < ( ) , ( ) > {
232+ if !self . resources_available ( htlc_amount_msat) {
233+ return Err ( ( ) ) ;
234+ }
235+
236+ self . slots_used += 1 ;
237+ self . liquidity_used += htlc_amount_msat;
238+ debug_assert ! (
239+ self . slots_used <= self . slots_allocated,
240+ "slots_used {} exceeded slots_allocated {}" ,
241+ self . slots_used,
242+ self . slots_allocated
243+ ) ;
244+ debug_assert ! (
245+ self . liquidity_used <= self . liquidity_allocated,
246+ "liquidity_used {} exceeded liquidity_allocated {}" ,
247+ self . liquidity_used,
248+ self . liquidity_allocated
249+ ) ;
250+ Ok ( ( ) )
251+ }
252+
253+ fn remove_htlc ( & mut self , htlc_amount_msat : u64 ) -> Result < ( ) , ( ) > {
254+ if self . slots_used == 0 || self . liquidity_used < htlc_amount_msat {
255+ return Err ( ( ) ) ;
256+ }
257+ self . slots_used -= 1 ;
258+ self . liquidity_used -= htlc_amount_msat;
259+ Ok ( ( ) )
260+ }
261+ }
262+
214263/// A weighted average that decays over a specified window.
215264///
216265/// It enables tracking of historical behavior without storing individual data points.
@@ -303,7 +352,8 @@ mod tests {
303352 use crate :: {
304353 crypto:: chacha20:: ChaCha20 ,
305354 ln:: resource_manager:: {
306- assign_slots_for_channel, AggregatedWindowAverage , DecayingAverage , GeneralBucket ,
355+ assign_slots_for_channel, AggregatedWindowAverage , BucketResources , DecayingAverage ,
356+ GeneralBucket ,
307357 } ,
308358 sign:: EntropySource ,
309359 util:: test_utils:: TestKeysInterface ,
@@ -469,6 +519,60 @@ mod tests {
469519 assert ! ( general_bucket. slots_occupied[ slot_occupied as usize ] . is_none( ) ) ;
470520 }
471521
522+ fn test_bucket_resources ( ) -> BucketResources {
523+ BucketResources {
524+ slots_allocated : 10 ,
525+ slots_used : 0 ,
526+ liquidity_allocated : 100_000 ,
527+ liquidity_used : 0 ,
528+ }
529+ }
530+
531+ #[ test]
532+ fn test_bucket_resources_add_htlc ( ) {
533+ let mut bucket_resources = test_bucket_resources ( ) ;
534+ let available_liquidity = bucket_resources. liquidity_allocated ;
535+ assert ! ( bucket_resources. add_htlc( available_liquidity + 1000 ) . is_err( ) ) ;
536+
537+ assert ! ( bucket_resources. add_htlc( 21_000 ) . is_ok( ) ) ;
538+ assert ! ( bucket_resources. add_htlc( 42_000 ) . is_ok( ) ) ;
539+ assert_eq ! ( bucket_resources. slots_used, 2 ) ;
540+ assert_eq ! ( bucket_resources. liquidity_used, 63_000 ) ;
541+ }
542+
543+ #[ test]
544+ fn test_bucket_resources_add_htlc_over_resources_available ( ) {
545+ // Test trying to go over slot limit
546+ let mut bucket_resources = test_bucket_resources ( ) ;
547+ let slots_available = bucket_resources. slots_allocated ;
548+ for _ in 0 ..slots_available {
549+ assert ! ( bucket_resources. add_htlc( 10 ) . is_ok( ) ) ;
550+ }
551+ assert_eq ! ( bucket_resources. slots_used, slots_available) ;
552+ assert ! ( bucket_resources. add_htlc( 10 ) . is_err( ) ) ;
553+
554+ // Test trying to go over liquidity limit
555+ let mut bucket = test_bucket_resources ( ) ;
556+ assert ! ( bucket. add_htlc( bucket. liquidity_allocated - 1000 ) . is_ok( ) ) ;
557+ assert ! ( bucket. add_htlc( 2000 ) . is_err( ) ) ;
558+ }
559+
560+ #[ test]
561+ fn test_bucket_resources_remove_htlc ( ) {
562+ let mut bucket_resources = test_bucket_resources ( ) ;
563+
564+ // If no resources have been used, removing HTLC should fail
565+ assert ! ( bucket_resources. remove_htlc( 100 ) . is_err( ) ) ;
566+
567+ bucket_resources. add_htlc ( 1000 ) . unwrap ( ) ;
568+ // Test failure if it tries to remove amount over what is currently in use.
569+ assert ! ( bucket_resources. remove_htlc( 1001 ) . is_err( ) ) ;
570+
571+ assert ! ( bucket_resources. remove_htlc( 1000 ) . is_ok( ) ) ;
572+ assert_eq ! ( bucket_resources. slots_used, 0 ) ;
573+ assert_eq ! ( bucket_resources. liquidity_used, 0 ) ;
574+ }
575+
472576 #[ test]
473577 fn test_decaying_average_bounds ( ) {
474578 for ( start, bound) in [ ( 1000 , i64:: MAX ) , ( -1000 , i64:: MIN ) ] {
0 commit comments