@@ -18,6 +18,9 @@ use ::serde::{Deserialize, Serialize};
1818use internals:: error:: InputString ;
1919use internals:: write_err;
2020
21+ #[ cfg( feature = "alloc" ) ]
22+ use crate :: { FeeRate , Weight } ;
23+
2124/// A set of denominations in which amounts can be expressed.
2225///
2326/// # Examples
@@ -1039,6 +1042,34 @@ impl Amount {
10391042 /// Returns [None] if overflow occurred.
10401043 pub fn checked_div ( self , rhs : u64 ) -> Option < Amount > { self . 0 . checked_div ( rhs) . map ( Amount ) }
10411044
1045+ /// Checked weight ceiling division.
1046+ ///
1047+ /// Be aware that integer division loses the remainder if no exact division
1048+ /// can be made. This method rounds up ensuring the transaction fee-rate is
1049+ /// sufficient. See also [`Amount::div_by_weight_floor`].
1050+ ///
1051+ /// [`None`] is returned if an overflow occurred.
1052+ #[ cfg( feature = "alloc" ) ]
1053+ pub fn div_by_weight_ceil ( self , rhs : Weight ) -> Option < FeeRate > {
1054+ let sats = self . 0 . checked_mul ( 1000 ) ?;
1055+ let wu = rhs. to_wu ( ) ;
1056+
1057+ let fee_rate = sats. checked_add ( wu. checked_sub ( 1 ) ?) ?. checked_div ( wu) ?;
1058+ Some ( FeeRate :: from_sat_per_kwu ( fee_rate) )
1059+ }
1060+
1061+ /// Checked weight floor division.
1062+ ///
1063+ /// Be aware that integer division loses the remainder if no exact division
1064+ /// can be made. See also [`Amount::div_by_weight_ceil`].
1065+ ///
1066+ /// Returns [`None`] if overflow occurred.
1067+ #[ cfg( feature = "alloc" ) ]
1068+ pub fn div_by_weight_floor ( self , rhs : Weight ) -> Option < FeeRate > {
1069+ let fee_rate = self . 0 . checked_mul ( 1_000 ) ?. checked_div ( rhs. to_wu ( ) ) ?;
1070+ Some ( FeeRate :: from_sat_per_kwu ( fee_rate) )
1071+ }
1072+
10421073 /// Checked remainder.
10431074 ///
10441075 /// Returns [None] if overflow occurred.
@@ -2117,6 +2148,48 @@ mod tests {
21172148 assert_eq ! ( ssat( -6 ) . checked_div( 2 ) , Some ( ssat( -3 ) ) ) ;
21182149 }
21192150
2151+ #[ cfg( feature = "alloc" ) ]
2152+ #[ test]
2153+ fn amount_checked_div_by_weight_ceil ( ) {
2154+ let weight = Weight :: from_kwu ( 1 ) . unwrap ( ) ;
2155+ let fee_rate = Amount :: from_sat ( 1 ) . div_by_weight_ceil ( weight) . unwrap ( ) ;
2156+ // 1 sats / 1,000 wu = 1 sats/kwu
2157+ assert_eq ! ( fee_rate, FeeRate :: from_sat_per_kwu( 1 ) ) ;
2158+
2159+ let weight = Weight :: from_wu ( 381 ) ;
2160+ let fee_rate = Amount :: from_sat ( 329 ) . div_by_weight_ceil ( weight) . unwrap ( ) ;
2161+ // 329 sats / 381 wu = 863.5 sats/kwu
2162+ // round up to 864
2163+ assert_eq ! ( fee_rate, FeeRate :: from_sat_per_kwu( 864 ) ) ;
2164+
2165+ let fee_rate = Amount :: MAX . div_by_weight_ceil ( weight) ;
2166+ assert ! ( fee_rate. is_none( ) ) ;
2167+
2168+ let fee_rate = Amount :: ONE_SAT . div_by_weight_ceil ( Weight :: ZERO ) ;
2169+ assert ! ( fee_rate. is_none( ) ) ;
2170+ }
2171+
2172+ #[ cfg( feature = "alloc" ) ]
2173+ #[ test]
2174+ fn amount_checked_div_by_weight_floor ( ) {
2175+ let weight = Weight :: from_kwu ( 1 ) . unwrap ( ) ;
2176+ let fee_rate = Amount :: from_sat ( 1 ) . div_by_weight_floor ( weight) . unwrap ( ) ;
2177+ // 1 sats / 1,000 wu = 1 sats/kwu
2178+ assert_eq ! ( fee_rate, FeeRate :: from_sat_per_kwu( 1 ) ) ;
2179+
2180+ let weight = Weight :: from_wu ( 381 ) ;
2181+ let fee_rate = Amount :: from_sat ( 329 ) . div_by_weight_floor ( weight) . unwrap ( ) ;
2182+ // 329 sats / 381 wu = 863.5 sats/kwu
2183+ // round down to 863
2184+ assert_eq ! ( fee_rate, FeeRate :: from_sat_per_kwu( 863 ) ) ;
2185+
2186+ let fee_rate = Amount :: MAX . div_by_weight_floor ( weight) ;
2187+ assert ! ( fee_rate. is_none( ) ) ;
2188+
2189+ let fee_rate = Amount :: ONE_SAT . div_by_weight_floor ( Weight :: ZERO ) ;
2190+ assert ! ( fee_rate. is_none( ) ) ;
2191+ }
2192+
21202193 #[ test]
21212194 #[ cfg( not( debug_assertions) ) ]
21222195 fn unchecked_amount_add ( ) {
0 commit comments