@@ -166,10 +166,26 @@ pub(super) fn construct_onion_keys<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T
166
166
}
167
167
168
168
/// returns the hop data, as well as the first-hop value_msat and CLTV value we should send.
169
+ // NOTE: This function is slightly different from the rust-lightning one, as we had to modify
170
+ // it in order to support RGB swaps. More details:
171
+ // We now split up the bitcoin amount into a fee part and a payment part: in non-RGB nodes the
172
+ // bitcoin amount is always monotonically increasing when constructing the route in reverse
173
+ // (notice the .rev() in the function): the receiver gets x, the previous hop gets
174
+ // `x + <fee to fwd to receiver>`, the previous one gets `x + <fee to fwd to next> +
175
+ // <fee to fwd to receiver>` and so on. So the original function could get away with only
176
+ // having one accumulator cur_value_msat that would always increase.
177
+ // Now with RGB, and specifically RGB swaps, we have scenarios where this assumption doesn't hold
178
+ // true: for example, when we send a payment that is initially RGB and then bitcoin (i.e. the
179
+ // user is buying an asset) this doesn't work. At some point in the route the bitcoin amount
180
+ // will suddenly need to decrease, because one node in the path (the user's node) is
181
+ // sending more bitcoin than it's actually receiving.
182
+ // So the new logic splits fees and payment_amount. We only accumulate fees, and for every node in the "bitcoin side"
183
+ // of the path we also set the payment_amount, while for the other nodes it will be 0.
169
184
pub ( super ) fn build_onion_payloads ( path : & Path , total_msat : u64 , mut recipient_onion : RecipientOnionFields , starting_htlc_offset : u32 , keysend_preimage : & Option < PaymentPreimage > ) -> Result < ( Vec < msgs:: OutboundOnionPayload > , u64 , u32 , Option < u64 > ) , APIError > {
170
- let mut cur_value_msat = 0u64 ;
171
185
// The rgb_amount at the last hop
172
186
let mut last_amount_rgb = None ;
187
+ let mut last_msat_amount = 0 ;
188
+ let mut cur_accumulated_fees = 0 ;
173
189
let mut cur_cltv = starting_htlc_offset;
174
190
let mut last_short_channel_id = 0 ;
175
191
let mut res: Vec < msgs:: OutboundOnionPayload > = Vec :: with_capacity (
@@ -180,8 +196,10 @@ pub(super) fn build_onion_payloads(path: &Path, total_msat: u64, mut recipient_o
180
196
// First hop gets special values so that it can check, on receipt, that everything is
181
197
// exactly as it should be (and the next hop isn't trying to probe to find out if we're
182
198
// the intended recipient).
183
- let value_msat = if cur_value_msat == 0 { hop. fee_msat } else { cur_value_msat } ;
184
- let value_rgb = hop. rgb_amount ;
199
+ let ( value_msat, value_rgb) = if last_msat_amount == 0 { ( hop. fee_msat , hop. rgb_amount ) } else {
200
+ cur_accumulated_fees += hop. fee_msat ;
201
+ ( last_msat_amount, last_amount_rgb)
202
+ } ;
185
203
let cltv = if cur_cltv == starting_htlc_offset { hop. cltv_expiry_delta + starting_htlc_offset } else { cur_cltv } ;
186
204
if idx == 0 {
187
205
if let Some ( BlindedTail {
@@ -190,7 +208,6 @@ pub(super) fn build_onion_payloads(path: &Path, total_msat: u64, mut recipient_o
190
208
let mut blinding_point = Some ( * blinding_point) ;
191
209
for ( i, blinded_hop) in hops. iter ( ) . enumerate ( ) {
192
210
if i == hops. len ( ) - 1 {
193
- cur_value_msat += final_value_msat;
194
211
cur_cltv += excess_final_cltv_expiry_delta;
195
212
res. push ( msgs:: OutboundOnionPayload :: BlindedReceive {
196
213
amt_msat : * final_value_msat,
@@ -231,18 +248,19 @@ pub(super) fn build_onion_payloads(path: &Path, total_msat: u64, mut recipient_o
231
248
rgb_amount_to_forward : value_rgb,
232
249
} ) ;
233
250
}
234
- cur_value_msat += hop. fee_msat ;
235
- if cur_value_msat >= 21000000 * 100000000 * 1000 {
251
+ last_amount_rgb = hop. rgb_amount ;
252
+ last_msat_amount = hop. payment_amount + cur_accumulated_fees;
253
+
254
+ if last_msat_amount >= 21000000 * 100000000 * 1000 {
236
255
return Err ( APIError :: InvalidRoute { err : "Channel fees overflowed?" . to_owned ( ) } ) ;
237
256
}
238
257
cur_cltv += hop. cltv_expiry_delta as u32 ;
239
258
if cur_cltv >= 500000000 {
240
259
return Err ( APIError :: InvalidRoute { err : "Channel CLTV overflowed?" . to_owned ( ) } ) ;
241
260
}
242
261
last_short_channel_id = hop. short_channel_id ;
243
- last_amount_rgb = hop. rgb_amount ;
244
262
}
245
- Ok ( ( res, cur_value_msat , cur_cltv, last_amount_rgb) )
263
+ Ok ( ( res, last_msat_amount , cur_cltv, last_amount_rgb) )
246
264
}
247
265
248
266
/// Length of the onion data packet. Before TLV-based onions this was 20 65-byte hops, though now
0 commit comments