@@ -1172,3 +1172,186 @@ fn test_revoked_counterparty_htlc_tx_balances() {
1172
1172
1173
1173
assert_eq ! ( nodes[ 0 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) , Vec :: new( ) ) ;
1174
1174
}
1175
+
1176
+ #[ test]
1177
+ fn test_revoked_counterparty_aggregated_claims ( ) {
1178
+ // Tests `get_claimable_balances` for revoked counterparty commitment transactions when
1179
+ // claiming with an aggregated claim transaction.
1180
+ let mut chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
1181
+ // We broadcast a second-to-latest commitment transaction, without providing the revocation
1182
+ // secret to the counterparty. However, because we always immediately take the revocation
1183
+ // secret from the keys_manager, we would panic at broadcast as we're trying to sign a
1184
+ // transaction which, from the point of view of our keys_manager, is revoked.
1185
+ chanmon_cfgs[ 1 ] . keys_manager . disable_revocation_policy_check = true ;
1186
+ let node_cfgs = create_node_cfgs ( 2 , & chanmon_cfgs) ;
1187
+ let node_chanmgrs = create_node_chanmgrs ( 2 , & node_cfgs, & [ None , None ] ) ;
1188
+ let nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
1189
+
1190
+ let ( _, _, chan_id, funding_tx) =
1191
+ create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 1_000_000 , 100_000_000 , InitFeatures :: known ( ) , InitFeatures :: known ( ) ) ;
1192
+ let funding_outpoint = OutPoint { txid : funding_tx. txid ( ) , index : 0 } ;
1193
+ assert_eq ! ( funding_outpoint. to_channel_id( ) , chan_id) ;
1194
+
1195
+ // We create two HTLCs, one which we will give A the preimage to to generate an HTLC-Success
1196
+ // transaction, and one which we will not, allowing B to claim the HTLC output in an aggregated
1197
+ // revocation-claim transaction.
1198
+
1199
+ let ( claimed_payment_preimage, claimed_payment_hash, ..) = route_payment ( & nodes[ 1 ] , & [ & nodes[ 0 ] ] , 3_000_000 ) ;
1200
+ let revoked_payment_hash = route_payment ( & nodes[ 1 ] , & [ & nodes[ 0 ] ] , 4_000_000 ) . 1 ;
1201
+
1202
+ let htlc_cltv_timeout = nodes[ 1 ] . best_block_info ( ) . 1 + TEST_FINAL_CLTV + 1 ; // Note ChannelManager adds one to CLTV timeouts for safety
1203
+
1204
+ // Cheat by giving A's ChannelMonitor the preimage to the to-be-claimed HTLC so that we have an
1205
+ // HTLC-claim transaction on the to-be-revoked state.
1206
+ get_monitor ! ( nodes[ 0 ] , chan_id) . provide_payment_preimage ( & claimed_payment_hash, & claimed_payment_preimage, & nodes[ 0 ] . tx_broadcaster , & node_cfgs[ 0 ] . fee_estimator , & nodes[ 0 ] . logger ) ;
1207
+
1208
+ // Now get the latest commitment transaction from A and then update the fee to revoke it
1209
+ let as_revoked_txn = get_local_commitment_txn ! ( nodes[ 0 ] , chan_id) ;
1210
+
1211
+ assert_eq ! ( as_revoked_txn. len( ) , 2 ) ;
1212
+ check_spends ! ( as_revoked_txn[ 0 ] , funding_tx) ;
1213
+ check_spends ! ( as_revoked_txn[ 1 ] , as_revoked_txn[ 0 ] ) ; // The HTLC-Claim transaction
1214
+
1215
+ let opt_anchors = get_opt_anchors ! ( nodes[ 0 ] , chan_id) ;
1216
+ let chan_feerate = get_feerate ! ( nodes[ 0 ] , chan_id) as u64 ;
1217
+
1218
+ {
1219
+ let mut feerate = chanmon_cfgs[ 0 ] . fee_estimator . sat_per_kw . lock ( ) . unwrap ( ) ;
1220
+ * feerate += 1 ;
1221
+ }
1222
+ nodes[ 0 ] . node . timer_tick_occurred ( ) ;
1223
+ check_added_monitors ! ( nodes[ 0 ] , 1 ) ;
1224
+
1225
+ let fee_update = get_htlc_update_msgs ! ( nodes[ 0 ] , nodes[ 1 ] . node. get_our_node_id( ) ) ;
1226
+ nodes[ 1 ] . node . handle_update_fee ( & nodes[ 0 ] . node . get_our_node_id ( ) , & fee_update. update_fee . unwrap ( ) ) ;
1227
+ commitment_signed_dance ! ( nodes[ 1 ] , nodes[ 0 ] , fee_update. commitment_signed, false ) ;
1228
+
1229
+ nodes[ 0 ] . node . claim_funds ( claimed_payment_preimage) ;
1230
+ expect_payment_claimed ! ( nodes[ 0 ] , claimed_payment_hash, 3_000_000 ) ;
1231
+ check_added_monitors ! ( nodes[ 0 ] , 1 ) ;
1232
+ let _a_htlc_msgs = get_htlc_update_msgs ! ( & nodes[ 0 ] , nodes[ 1 ] . node. get_our_node_id( ) ) ;
1233
+
1234
+ assert_eq ! ( sorted_vec( vec![ Balance :: ClaimableOnChannelClose {
1235
+ claimable_amount_satoshis: 100_000 - 4_000 - 3_000 ,
1236
+ } , Balance :: MaybeClaimableHTLCAwaitingTimeout {
1237
+ claimable_amount_satoshis: 4_000 ,
1238
+ claimable_height: htlc_cltv_timeout,
1239
+ } , Balance :: MaybeClaimableHTLCAwaitingTimeout {
1240
+ claimable_amount_satoshis: 3_000 ,
1241
+ claimable_height: htlc_cltv_timeout,
1242
+ } ] ) ,
1243
+ sorted_vec( nodes[ 1 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) ) ) ;
1244
+
1245
+ mine_transaction ( & nodes[ 1 ] , & as_revoked_txn[ 0 ] ) ;
1246
+ check_closed_broadcast ! ( nodes[ 1 ] , true ) ;
1247
+ check_closed_event ! ( nodes[ 1 ] , 1 , ClosureReason :: CommitmentTxConfirmed ) ;
1248
+ check_added_monitors ! ( nodes[ 1 ] , 1 ) ;
1249
+
1250
+ let mut claim_txn: Vec < _ > = nodes[ 1 ] . tx_broadcaster . txn_broadcasted . lock ( ) . unwrap ( ) . drain ( ..) . filter ( |tx| tx. input . iter ( ) . any ( |inp| inp. previous_output . txid == as_revoked_txn[ 0 ] . txid ( ) ) ) . collect ( ) ;
1251
+ // Currently the revoked commitment outputs are all claimed in one aggregated transaction
1252
+ assert_eq ! ( claim_txn. len( ) , 1 ) ;
1253
+ assert_eq ! ( claim_txn[ 0 ] . input. len( ) , 3 ) ;
1254
+ check_spends ! ( claim_txn[ 0 ] , as_revoked_txn[ 0 ] ) ;
1255
+
1256
+ let to_self_maturity = nodes[ 1 ] . best_block_info ( ) . 1 + ANTI_REORG_DELAY - 1 ;
1257
+
1258
+ assert_eq ! ( sorted_vec( vec![ Balance :: ClaimableAwaitingConfirmations { // to_remote output to A
1259
+ claimable_amount_satoshis: 100_000 - 4_000 - 3_000 ,
1260
+ confirmation_height: to_self_maturity,
1261
+ } , Balance :: CounterpartyRevokedOutputClaimable { // to_self output to A
1262
+ claimable_amount_satoshis: 1_000_000 - 100_000 - chan_feerate *
1263
+ ( channel:: commitment_tx_base_weight( opt_anchors) + 2 * channel:: COMMITMENT_TX_WEIGHT_PER_HTLC ) / 1000 ,
1264
+ } , Balance :: CounterpartyRevokedOutputClaimable { // HTLC 1
1265
+ claimable_amount_satoshis: 4_000 ,
1266
+ } , Balance :: CounterpartyRevokedOutputClaimable { // HTLC 2
1267
+ claimable_amount_satoshis: 3_000 ,
1268
+ } ] ) ,
1269
+ sorted_vec( nodes[ 1 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) ) ) ;
1270
+
1271
+ // Confirm A's HTLC-Success tranasction which presumably raced B's claim, causing B to create a
1272
+ // new claim.
1273
+ mine_transaction ( & nodes[ 1 ] , & as_revoked_txn[ 1 ] ) ;
1274
+ expect_payment_sent ! ( nodes[ 1 ] , claimed_payment_preimage) ;
1275
+ let mut claim_txn_2: Vec < _ > = nodes[ 1 ] . tx_broadcaster . txn_broadcasted . lock ( ) . unwrap ( ) . clone ( ) ;
1276
+ claim_txn_2. sort_unstable_by_key ( |tx| if tx. input . iter ( ) . any ( |inp| inp. previous_output . txid == as_revoked_txn[ 0 ] . txid ( ) ) { 0 } else { 1 } ) ;
1277
+ // Once B sees the HTLC-Success transaction it splits its claim transaction into two, though in
1278
+ // theory it could re-aggregate the claims as well.
1279
+ assert_eq ! ( claim_txn_2. len( ) , 2 ) ;
1280
+ assert_eq ! ( claim_txn_2[ 0 ] . input. len( ) , 2 ) ;
1281
+ check_spends ! ( claim_txn_2[ 0 ] , as_revoked_txn[ 0 ] ) ;
1282
+ assert_eq ! ( claim_txn_2[ 1 ] . input. len( ) , 1 ) ;
1283
+ check_spends ! ( claim_txn_2[ 1 ] , as_revoked_txn[ 1 ] ) ;
1284
+
1285
+ assert_eq ! ( sorted_vec( vec![ Balance :: ClaimableAwaitingConfirmations { // to_remote output to A
1286
+ claimable_amount_satoshis: 100_000 - 4_000 - 3_000 ,
1287
+ confirmation_height: to_self_maturity,
1288
+ } , Balance :: CounterpartyRevokedOutputClaimable { // to_self output to A
1289
+ claimable_amount_satoshis: 1_000_000 - 100_000 - chan_feerate *
1290
+ ( channel:: commitment_tx_base_weight( opt_anchors) + 2 * channel:: COMMITMENT_TX_WEIGHT_PER_HTLC ) / 1000 ,
1291
+ } , Balance :: CounterpartyRevokedOutputClaimable { // HTLC 1
1292
+ claimable_amount_satoshis: 4_000 ,
1293
+ } , Balance :: CounterpartyRevokedOutputClaimable { // HTLC 2
1294
+ // The amount here is a bit of a misnomer, really its been reduced by the HTLC
1295
+ // transaction fee, but the claimable amount is always a bit of an overshoot for HTLCs
1296
+ // anyway, so its not a big change.
1297
+ claimable_amount_satoshis: 3_000 ,
1298
+ } ] ) ,
1299
+ sorted_vec( nodes[ 1 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) ) ) ;
1300
+
1301
+ connect_blocks ( & nodes[ 1 ] , 5 ) ;
1302
+ test_spendable_output ( & nodes[ 1 ] , & as_revoked_txn[ 0 ] ) ;
1303
+
1304
+ assert_eq ! ( sorted_vec( vec![ Balance :: CounterpartyRevokedOutputClaimable { // to_self output to A
1305
+ claimable_amount_satoshis: 1_000_000 - 100_000 - chan_feerate *
1306
+ ( channel:: commitment_tx_base_weight( opt_anchors) + 2 * channel:: COMMITMENT_TX_WEIGHT_PER_HTLC ) / 1000 ,
1307
+ } , Balance :: CounterpartyRevokedOutputClaimable { // HTLC 1
1308
+ claimable_amount_satoshis: 4_000 ,
1309
+ } , Balance :: CounterpartyRevokedOutputClaimable { // HTLC 2
1310
+ // The amount here is a bit of a misnomer, really its been reduced by the HTLC
1311
+ // transaction fee, but the claimable amount is always a bit of an overshoot for HTLCs
1312
+ // anyway, so its not a big change.
1313
+ claimable_amount_satoshis: 3_000 ,
1314
+ } ] ) ,
1315
+ sorted_vec( nodes[ 1 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) ) ) ;
1316
+
1317
+ mine_transaction ( & nodes[ 1 ] , & claim_txn_2[ 1 ] ) ;
1318
+ let htlc_2_claim_maturity = nodes[ 1 ] . best_block_info ( ) . 1 + ANTI_REORG_DELAY - 1 ;
1319
+
1320
+ assert_eq ! ( sorted_vec( vec![ Balance :: CounterpartyRevokedOutputClaimable { // to_self output to A
1321
+ claimable_amount_satoshis: 1_000_000 - 100_000 - chan_feerate *
1322
+ ( channel:: commitment_tx_base_weight( opt_anchors) + 2 * channel:: COMMITMENT_TX_WEIGHT_PER_HTLC ) / 1000 ,
1323
+ } , Balance :: CounterpartyRevokedOutputClaimable { // HTLC 1
1324
+ claimable_amount_satoshis: 4_000 ,
1325
+ } , Balance :: ClaimableAwaitingConfirmations { // HTLC 2
1326
+ claimable_amount_satoshis: claim_txn_2[ 1 ] . output[ 0 ] . value,
1327
+ confirmation_height: htlc_2_claim_maturity,
1328
+ } ] ) ,
1329
+ sorted_vec( nodes[ 1 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) ) ) ;
1330
+
1331
+ connect_blocks ( & nodes[ 1 ] , 5 ) ;
1332
+ test_spendable_output ( & nodes[ 1 ] , & claim_txn_2[ 1 ] ) ;
1333
+
1334
+ assert_eq ! ( sorted_vec( vec![ Balance :: CounterpartyRevokedOutputClaimable { // to_self output to A
1335
+ claimable_amount_satoshis: 1_000_000 - 100_000 - chan_feerate *
1336
+ ( channel:: commitment_tx_base_weight( opt_anchors) + 2 * channel:: COMMITMENT_TX_WEIGHT_PER_HTLC ) / 1000 ,
1337
+ } , Balance :: CounterpartyRevokedOutputClaimable { // HTLC 1
1338
+ claimable_amount_satoshis: 4_000 ,
1339
+ } ] ) ,
1340
+ sorted_vec( nodes[ 1 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) ) ) ;
1341
+
1342
+ mine_transaction ( & nodes[ 1 ] , & claim_txn_2[ 0 ] ) ;
1343
+ let rest_claim_maturity = nodes[ 1 ] . best_block_info ( ) . 1 + ANTI_REORG_DELAY - 1 ;
1344
+
1345
+ assert_eq ! ( vec![ Balance :: ClaimableAwaitingConfirmations {
1346
+ claimable_amount_satoshis: claim_txn_2[ 0 ] . output[ 0 ] . value,
1347
+ confirmation_height: rest_claim_maturity,
1348
+ } ] ,
1349
+ nodes[ 1 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) ) ;
1350
+
1351
+ assert ! ( nodes[ 1 ] . node. get_and_clear_pending_events( ) . is_empty( ) ) ; // We shouldn't fail the payment until we spend the output
1352
+
1353
+ connect_blocks ( & nodes[ 1 ] , 5 ) ;
1354
+ expect_payment_failed ! ( nodes[ 1 ] , revoked_payment_hash, true ) ;
1355
+ test_spendable_output ( & nodes[ 1 ] , & claim_txn_2[ 0 ] ) ;
1356
+ assert ! ( nodes[ 1 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) . is_empty( ) ) ;
1357
+ }
0 commit comments