@@ -227,18 +227,27 @@ impl ChannelState {
227227 Ok ( Ok ( ( ) ) )
228228 }
229229
230- /// Checks whether the proposed HTLC can be added to the channel as an outgoing HTLC. This requires that we have
231- /// sufficient liquidity, and that the restrictions on our in flight htlc balance and count are not violated by
232- /// the addition of the HTLC. Specification sanity checks (such as reasonable CLTV) are also included, as this
233- /// is where we'd check it in real life.
234- fn check_outgoing_addition ( & self , htlc : & Htlc ) -> Result < ( ) , ForwardingError > {
230+ /// Checks whether the proposed HTLC can be added to the channel as an outgoing HTLC. Each limit is read from the
231+ /// party that owns it:
232+ ///
233+ /// * `htlc_minimum_msat` / `htlc_maximum_msat` are forwarder-advertised: they are part of the gossiped
234+ /// `channel_update` for the sending node's own direction (populated into the graph from `self.policy`), so they
235+ /// are enforced against `self.policy`.
236+ /// * `max_accepted_htlcs` / `max_htlc_value_in_flight_msat` are receiver-negotiated (BOLT-2 channel open, never
237+ /// gossiped): they bound the HTLCs the counterparty will accept in flight towards it, so they are enforced
238+ /// against `counterparty_policy`.
239+ fn check_outgoing_addition (
240+ & self ,
241+ htlc : & Htlc ,
242+ counterparty_policy : & ChannelPolicy ,
243+ ) -> Result < ( ) , ForwardingError > {
235244 fail_forwarding_inequality ! ( htlc. amount_msat, >, self . policy. max_htlc_size_msat, MoreThanMaximum ) ;
236245 fail_forwarding_inequality ! ( htlc. amount_msat, <, self . policy. min_htlc_size_msat, LessThanMinimum ) ;
237246 fail_forwarding_inequality ! (
238- self . in_flight. len( ) as u64 + 1 , >, self . policy . max_htlc_count, ExceedsInFlightCount
247+ self . in_flight. len( ) as u64 + 1 , >, counterparty_policy . max_htlc_count, ExceedsInFlightCount
239248 ) ;
240249 fail_forwarding_inequality ! (
241- self . in_flight_total( ) + htlc. amount_msat, >, self . policy . max_in_flight_msat, ExceedsInFlightTotal
250+ self . in_flight_total( ) + htlc. amount_msat, >, counterparty_policy . max_in_flight_msat, ExceedsInFlightTotal
242251 ) ;
243252 fail_forwarding_inequality ! ( htlc. amount_msat, >, self . local_balance_msat, InsufficientBalance ) ;
244253 fail_forwarding_inequality ! ( htlc. cltv_expiry, >, 500000000 , ExpiryInSeconds ) ;
@@ -256,8 +265,9 @@ impl ChannelState {
256265 & mut self ,
257266 hash : PaymentHash ,
258267 htlc : Htlc ,
268+ counterparty_policy : & ChannelPolicy ,
259269 ) -> Result < Result < u64 , ForwardingError > , CriticalError > {
260- if let Err ( fwd_err) = self . check_outgoing_addition ( & htlc) {
270+ if let Err ( fwd_err) = self . check_outgoing_addition ( & htlc, counterparty_policy ) {
261271 return Ok ( Err ( fwd_err) ) ;
262272 }
263273
@@ -393,8 +403,15 @@ impl SimulatedChannel {
393403 }
394404
395405 self . sanity_check ( ) ?;
396- self . get_node_mut ( sending_node) ?
397- . add_outgoing_htlc ( hash, htlc)
406+ let ( sender, counterparty) = if sending_node == & self . node_1 . policy . pubkey {
407+ ( & mut self . node_1 , & self . node_2 )
408+ } else if sending_node == & self . node_2 . policy . pubkey {
409+ ( & mut self . node_2 , & self . node_1 )
410+ } else {
411+ return Err ( CriticalError :: NodeNotFound ( * sending_node) ) ;
412+ } ;
413+
414+ sender. add_outgoing_htlc ( hash, htlc, & counterparty. policy )
398415 }
399416
400417 /// Performs a sanity check on the total balances in a channel. Note that we do not currently include on-chain
@@ -1718,6 +1735,8 @@ mod tests {
17181735 let mut channel_state =
17191736 ChannelState :: new ( create_test_policy ( local_balance / 2 ) , local_balance) ;
17201737
1738+ let policy = channel_state. policy . clone ( ) ;
1739+
17211740 // Basic sanity check that we Initialize the channel correctly.
17221741 assert_channel_balances ! ( channel_state, local_balance, 0 , 0 ) ;
17231742
@@ -1730,7 +1749,9 @@ mod tests {
17301749 cltv_expiry : 40 ,
17311750 } ;
17321751
1733- assert ! ( channel_state. add_outgoing_htlc( hash_1, htlc_1) . is_ok( ) ) ;
1752+ assert ! ( channel_state
1753+ . add_outgoing_htlc( hash_1, htlc_1, & policy)
1754+ . is_ok( ) ) ;
17341755 assert_channel_balances ! (
17351756 channel_state,
17361757 local_balance - htlc_1. amount_msat,
@@ -1741,7 +1762,7 @@ mod tests {
17411762 // Try to add a htlc with the same payment hash and assert that we fail because we enforce one htlc per hash
17421763 // at present.
17431764 assert ! ( matches!(
1744- channel_state. add_outgoing_htlc( hash_1, htlc_1) ,
1765+ channel_state. add_outgoing_htlc( hash_1, htlc_1, & policy ) ,
17451766 Err ( CriticalError :: PaymentHashExists ( _) )
17461767 ) ) ;
17471768
@@ -1752,7 +1773,9 @@ mod tests {
17521773 cltv_expiry : 40 ,
17531774 } ;
17541775
1755- assert ! ( channel_state. add_outgoing_htlc( hash_2, htlc_2) . is_ok( ) ) ;
1776+ assert ! ( channel_state
1777+ . add_outgoing_htlc( hash_2, htlc_2, & policy)
1778+ . is_ok( ) ) ;
17561779 assert_channel_balances ! (
17571780 channel_state,
17581781 local_balance - htlc_1. amount_msat - htlc_2. amount_msat,
@@ -1834,46 +1857,56 @@ mod tests {
18341857 let mut channel_state =
18351858 ChannelState :: new ( create_test_policy ( local_balance / 2 ) , local_balance) ;
18361859
1860+ // Size limits (min/max htlc size) are read from our own policy, while the in-flight limits (count and total)
1861+ // are read from the counterparty's.
1862+ let mut counterparty = channel_state. policy . clone ( ) ;
1863+ counterparty. max_in_flight_msat = 30_000 ;
1864+ counterparty. max_htlc_count = 4 ;
1865+
18371866 let mut htlc = Htlc {
18381867 amount_msat : channel_state. policy . max_htlc_size_msat + 1 ,
18391868 cltv_expiry : channel_state. policy . cltv_expiry_delta ,
18401869 } ;
1841- // HTLC maximum size exceeded.
18421870 assert ! ( matches!(
1843- channel_state. check_outgoing_addition( & htlc) ,
1871+ channel_state. check_outgoing_addition( & htlc, & counterparty ) ,
18441872 Err ( ForwardingError :: MoreThanMaximum ( _, _) )
18451873 ) ) ;
18461874
1847- // Beneath HTLC minimum size.
18481875 htlc. amount_msat = channel_state. policy . min_htlc_size_msat - 1 ;
18491876 assert ! ( matches!(
1850- channel_state. check_outgoing_addition( & htlc) ,
1877+ channel_state. check_outgoing_addition( & htlc, & counterparty ) ,
18511878 Err ( ForwardingError :: LessThanMinimum ( _, _) )
18521879 ) ) ;
18531880
1854- // Add two large htlcs so that we will start to run into our in-flight total amount limit.
18551881 let hash_1 = PaymentHash ( [ 1 ; 32 ] ) ;
18561882 let htlc_1 = Htlc {
1857- amount_msat : channel_state . policy . max_in_flight_msat / 2 ,
1883+ amount_msat : counterparty . max_in_flight_msat / 2 ,
18581884 cltv_expiry : channel_state. policy . cltv_expiry_delta ,
18591885 } ;
18601886
1861- assert ! ( channel_state. check_outgoing_addition( & htlc_1) . is_ok( ) ) ;
1862- assert ! ( channel_state. add_outgoing_htlc( hash_1, htlc_1) . is_ok( ) ) ;
1887+ assert ! ( channel_state
1888+ . check_outgoing_addition( & htlc_1, & counterparty)
1889+ . is_ok( ) ) ;
1890+ assert ! ( channel_state
1891+ . add_outgoing_htlc( hash_1, htlc_1, & counterparty)
1892+ . is_ok( ) ) ;
18631893
18641894 let hash_2 = PaymentHash ( [ 2 ; 32 ] ) ;
18651895 let htlc_2 = Htlc {
1866- amount_msat : channel_state . policy . max_in_flight_msat / 2 ,
1896+ amount_msat : counterparty . max_in_flight_msat / 2 ,
18671897 cltv_expiry : channel_state. policy . cltv_expiry_delta ,
18681898 } ;
18691899
1870- assert ! ( channel_state. check_outgoing_addition( & htlc_2) . is_ok( ) ) ;
1871- assert ! ( channel_state. add_outgoing_htlc( hash_2, htlc_2) . is_ok( ) ) ;
1900+ assert ! ( channel_state
1901+ . check_outgoing_addition( & htlc_2, & counterparty)
1902+ . is_ok( ) ) ;
1903+ assert ! ( channel_state
1904+ . add_outgoing_htlc( hash_2, htlc_2, & counterparty)
1905+ . is_ok( ) ) ;
18721906
1873- // Now, assert that we can't add even our smallest htlc size, because we're hit our in-flight amount limit.
18741907 htlc. amount_msat = channel_state. policy . min_htlc_size_msat ;
18751908 assert ! ( matches!(
1876- channel_state. check_outgoing_addition( & htlc) ,
1909+ channel_state. check_outgoing_addition( & htlc, & counterparty ) ,
18771910 Err ( ForwardingError :: ExceedsInFlightTotal ( _, _) )
18781911 ) ) ;
18791912
@@ -1884,11 +1917,14 @@ mod tests {
18841917 assert ! ( channel_state. remove_outgoing_htlc( & hash_2) . is_ok( ) ) ;
18851918 channel_state. settle_outgoing_htlc ( htlc_2. amount_msat , true ) ;
18861919
1887- // Now we're going to add many htlcs so that we hit our in-flight count limit (unique payment hash per htlc).
1888- for i in 0 ..channel_state. policy . max_htlc_count {
1920+ for i in 0 ..counterparty. max_htlc_count {
18891921 let hash = PaymentHash ( [ i. try_into ( ) . unwrap ( ) ; 32 ] ) ;
1890- assert ! ( channel_state. check_outgoing_addition( & htlc) . is_ok( ) ) ;
1891- assert ! ( channel_state. add_outgoing_htlc( hash, htlc) . is_ok( ) ) ;
1922+ assert ! ( channel_state
1923+ . check_outgoing_addition( & htlc, & counterparty)
1924+ . is_ok( ) ) ;
1925+ assert ! ( channel_state
1926+ . add_outgoing_htlc( hash, htlc, & counterparty)
1927+ . is_ok( ) ) ;
18921928 }
18931929
18941930 // Try to add one more htlc and we should be rejected.
@@ -1898,34 +1934,35 @@ mod tests {
18981934 } ;
18991935
19001936 assert ! ( matches!(
1901- channel_state. check_outgoing_addition( & htlc_3) ,
1937+ channel_state. check_outgoing_addition( & htlc_3, & counterparty ) ,
19021938 Err ( ForwardingError :: ExceedsInFlightCount ( _, _) )
19031939 ) ) ;
19041940
19051941 // Resolve all in-flight htlcs.
1906- for i in 0 ..channel_state . policy . max_htlc_count {
1942+ for i in 0 ..counterparty . max_htlc_count {
19071943 let hash = PaymentHash ( [ i. try_into ( ) . unwrap ( ) ; 32 ] ) ;
19081944 assert ! ( channel_state. remove_outgoing_htlc( & hash) . is_ok( ) ) ;
19091945 channel_state. settle_outgoing_htlc ( htlc. amount_msat , true )
19101946 }
19111947
1912- // Add and settle another htlc to move more liquidity away from our local balance.
1913- let hash_4 = PaymentHash ( [ 1 ; 32 ] ) ;
19141948 let htlc_4 = Htlc {
19151949 amount_msat : channel_state. policy . max_htlc_size_msat ,
19161950 cltv_expiry : channel_state. policy . cltv_expiry_delta ,
19171951 } ;
1918- assert ! ( channel_state. check_outgoing_addition( & htlc_4) . is_ok( ) ) ;
1919- assert ! ( channel_state. add_outgoing_htlc( hash_4, htlc_4) . is_ok( ) ) ;
1920- assert ! ( channel_state. remove_outgoing_htlc( & hash_4) . is_ok( ) ) ;
1921- channel_state. settle_outgoing_htlc ( htlc_4. amount_msat , true ) ;
1922-
1923- // Finally, assert that we don't have enough balance to forward our largest possible htlc (because of all the
1924- // htlcs that we've settled) and assert that we fail to a large htlc. The balance assertion here is just a
1925- // sanity check for the test, which will fail if we change the amounts settled/failed in the test.
1952+ for hash in [ PaymentHash ( [ 10 ; 32 ] ) , PaymentHash ( [ 11 ; 32 ] ) ] {
1953+ assert ! ( channel_state
1954+ . add_outgoing_htlc( hash, htlc_4, & counterparty)
1955+ . is_ok( ) ) ;
1956+ assert ! ( channel_state. remove_outgoing_htlc( & hash) . is_ok( ) ) ;
1957+ channel_state. settle_outgoing_htlc ( htlc_4. amount_msat , true ) ;
1958+ }
1959+
1960+ // Finally, assert that we don't have enough balance to forward our largest possible htlc. The balance
1961+ // assertion here is just a sanity check for the test, which will fail if we change the amounts settled in the
1962+ // test.
19261963 assert ! ( channel_state. local_balance_msat < channel_state. policy. max_htlc_size_msat) ;
19271964 assert ! ( matches!(
1928- channel_state. check_outgoing_addition( & htlc_4) ,
1965+ channel_state. check_outgoing_addition( & htlc_4, & counterparty ) ,
19291966 Err ( ForwardingError :: InsufficientBalance ( _, _) )
19301967 ) ) ;
19311968 }
@@ -2073,6 +2110,104 @@ mod tests {
20732110 ) ) ;
20742111 }
20752112
2113+ #[ test]
2114+ fn test_add_htlc_policy_ownership ( ) {
2115+ let capacity_msat = 500_000_000 ;
2116+
2117+ // A policy that trips none of the limits under test, so only the party deliberately made strict can reject.
2118+ let permissive = || {
2119+ let mut policy = create_test_policy ( capacity_msat / 2 ) ;
2120+ policy. min_htlc_size_msat = 1 ;
2121+ policy. max_htlc_size_msat = capacity_msat;
2122+ policy. max_htlc_count = 483 ;
2123+ policy. max_in_flight_msat = capacity_msat;
2124+ policy
2125+ } ;
2126+
2127+ // Builds a channel with node_1 as the sender and node_2 as the receiver under the given policies.
2128+ let build_channel = |sender_policy : ChannelPolicy , receiver_policy : ChannelPolicy | {
2129+ let node_1 = ChannelState :: new ( sender_policy, capacity_msat) ;
2130+ let node_2 = ChannelState :: new ( receiver_policy, 0 ) ;
2131+ (
2132+ node_1. policy . pubkey ,
2133+ SimulatedChannel {
2134+ capacity_msat,
2135+ short_channel_id : ShortChannelID :: from ( 123 ) ,
2136+ node_1,
2137+ node_2,
2138+ exclude_capacity : false ,
2139+ } ,
2140+ )
2141+ } ;
2142+
2143+ let htlc = Htlc {
2144+ amount_msat : 1000 ,
2145+ cltv_expiry : 40 ,
2146+ } ;
2147+
2148+ // An HTLC above the sender's advertised maximum is rejected, even though the receiver's maximum is higher.
2149+ let mut sender_policy = permissive ( ) ;
2150+ sender_policy. max_htlc_size_msat = 2000 ;
2151+ let ( sender, mut channel) = build_channel ( sender_policy, permissive ( ) ) ;
2152+ assert ! ( matches!(
2153+ channel. add_htlc(
2154+ & sender,
2155+ PaymentHash ( [ 1 ; 32 ] ) ,
2156+ Htlc {
2157+ amount_msat: 3000 ,
2158+ cltv_expiry: 40
2159+ }
2160+ ) ,
2161+ Ok ( Err ( ForwardingError :: MoreThanMaximum ( _, _) ) )
2162+ ) ) ;
2163+
2164+ // An HTLC below the sender's advertised minimum is rejected, even though the receiver's minimum is lower.
2165+ let mut sender_policy = permissive ( ) ;
2166+ sender_policy. min_htlc_size_msat = 10_000 ;
2167+ let ( sender, mut channel) = build_channel ( sender_policy, permissive ( ) ) ;
2168+ assert ! ( matches!(
2169+ channel. add_htlc(
2170+ & sender,
2171+ PaymentHash ( [ 2 ; 32 ] ) ,
2172+ Htlc {
2173+ amount_msat: 5000 ,
2174+ cltv_expiry: 40
2175+ }
2176+ ) ,
2177+ Ok ( Err ( ForwardingError :: LessThanMinimum ( _, _) ) )
2178+ ) ) ;
2179+
2180+ // The receiver's `max_accepted_htlcs` bounds the in-flight HTLC count: with a limit of 1 the second HTLC is
2181+ // rejected, even though the sender's own policy (483) would allow it.
2182+ let mut receiver_policy = permissive ( ) ;
2183+ receiver_policy. max_htlc_count = 1 ;
2184+ let ( sender, mut channel) = build_channel ( permissive ( ) , receiver_policy) ;
2185+ assert ! ( channel
2186+ . add_htlc( & sender, PaymentHash ( [ 3 ; 32 ] ) , htlc)
2187+ . is_ok( ) ) ;
2188+ assert ! ( matches!(
2189+ channel. add_htlc( & sender, PaymentHash ( [ 4 ; 32 ] ) , htlc) ,
2190+ Ok ( Err ( ForwardingError :: ExceedsInFlightCount ( _, _) ) )
2191+ ) ) ;
2192+
2193+ // The receiver's `max_htlc_value_in_flight_msat` bounds the in-flight value: a 6000 msat HTLC exceeds the
2194+ // receiver's 5000 msat limit, even though the sender permits the full channel capacity in flight.
2195+ let mut receiver_policy = permissive ( ) ;
2196+ receiver_policy. max_in_flight_msat = 5000 ;
2197+ let ( sender, mut channel) = build_channel ( permissive ( ) , receiver_policy) ;
2198+ assert ! ( matches!(
2199+ channel. add_htlc(
2200+ & sender,
2201+ PaymentHash ( [ 5 ; 32 ] ) ,
2202+ Htlc {
2203+ amount_msat: 6000 ,
2204+ cltv_expiry: 40
2205+ }
2206+ ) ,
2207+ Ok ( Err ( ForwardingError :: ExceedsInFlightTotal ( _, _) ) )
2208+ ) ) ;
2209+ }
2210+
20762211 mock ! {
20772212 Network { }
20782213
0 commit comments