@@ -212,7 +212,7 @@ impl<'a, Key : Send + cmp::Eq + hash::Hash> ChainListener for SimpleManyChannelM
212
212
let block_hash = header. bitcoin_hash ( ) ;
213
213
let mut monitors = self . monitors . lock ( ) . unwrap ( ) ;
214
214
for monitor in monitors. values_mut ( ) {
215
- monitor. block_disconnected ( disconnected_height, & block_hash) ;
215
+ monitor. block_disconnected ( disconnected_height, & block_hash, & * self . broadcaster , & * self . fee_estimator ) ;
216
216
}
217
217
}
218
218
}
@@ -502,6 +502,13 @@ enum OnchainEvent {
502
502
HTLCUpdate {
503
503
htlc_update : ( HTLCSource , PaymentHash ) ,
504
504
} ,
505
+ /// Claim tx aggregate multiple claimable outpoints. One of the outpoint may be claimed by a remote party tx.
506
+ /// In this case, we need to drop the outpoint and regenerate a new claim tx. By safety, we keep tracking
507
+ /// the outpoint to be sure to resurect it back to the claim tx if reorgs happen.
508
+ ContentiousOutpoint {
509
+ outpoint : BitcoinOutPoint ,
510
+ input_material : InputMaterial ,
511
+ }
505
512
}
506
513
507
514
/// Higher-level cache structure needed to re-generate bumped claim txn if needed
@@ -1321,6 +1328,11 @@ impl ChannelMonitor {
1321
1328
writer. write_all ( & [ 1 ; 1 ] ) ?;
1322
1329
htlc_update. 0 . write ( writer) ?;
1323
1330
htlc_update. 1 . write ( writer) ?;
1331
+ } ,
1332
+ OnchainEvent :: ContentiousOutpoint { ref outpoint, ref input_material } => {
1333
+ writer. write_all ( & [ 2 ; 1 ] ) ?;
1334
+ outpoint. write ( writer) ?;
1335
+ input_material. write ( writer) ?;
1324
1336
}
1325
1337
}
1326
1338
}
@@ -1545,6 +1557,10 @@ impl ChannelMonitor {
1545
1557
log_trace ! ( self , "Outpoint {}:{} is being being claimed, if it doesn't succeed, a bumped claiming txn is going to be broadcast at height {}" , single_htlc_tx. input[ 0 ] . previous_output. txid, single_htlc_tx. input[ 0 ] . previous_output. vout, height_timer) ;
1546
1558
let mut per_input_material = HashMap :: with_capacity ( 1 ) ;
1547
1559
per_input_material. insert ( single_htlc_tx. input [ 0 ] . previous_output , InputMaterial :: Revoked { script : redeemscript, pubkey : Some ( revocation_pubkey) , key : revocation_key, is_htlc : true , amount : htlc. amount_msat / 1000 } ) ;
1560
+ match self . claimable_outpoints . entry ( single_htlc_tx. input [ 0 ] . previous_output ) {
1561
+ hash_map:: Entry :: Occupied ( _) => { } ,
1562
+ hash_map:: Entry :: Vacant ( entry) => { entry. insert ( ( single_htlc_tx. txid ( ) , height) ) ; }
1563
+ }
1548
1564
match self . pending_claim_requests . entry ( single_htlc_tx. txid ( ) ) {
1549
1565
hash_map:: Entry :: Occupied ( _) => { } ,
1550
1566
hash_map:: Entry :: Vacant ( entry) => { entry. insert ( ClaimTxBumpMaterial { height_timer, feerate_previous : used_feerate, soonest_timelock : htlc. cltv_expiry , per_input_material } ) ; }
@@ -1630,15 +1646,17 @@ impl ChannelMonitor {
1630
1646
}
1631
1647
}
1632
1648
let height_timer = Self :: get_height_timer ( height, soonest_timelock) ;
1649
+ let spend_txid = spend_tx. txid ( ) ;
1633
1650
for ( input, info) in spend_tx. input . iter_mut ( ) . zip ( inputs_info. iter ( ) ) {
1634
1651
let ( redeemscript, revocation_key) = sign_input ! ( sighash_parts, input, info. 0 , info. 1 ) ;
1635
1652
log_trace ! ( self , "Outpoint {}:{} is being being claimed, if it doesn't succeed, a bumped claiming txn is going to be broadcast at height {}" , input. previous_output. txid, input. previous_output. vout, height_timer) ;
1636
1653
per_input_material. insert ( input. previous_output , InputMaterial :: Revoked { script : redeemscript, pubkey : if info. 0 . is_some ( ) { Some ( revocation_pubkey) } else { None } , key : revocation_key, is_htlc : if info. 0 . is_some ( ) { true } else { false } , amount : info. 1 } ) ;
1637
- if info. 2 < soonest_timelock {
1638
- soonest_timelock = info. 2 ;
1654
+ match self . claimable_outpoints . entry ( input. previous_output ) {
1655
+ hash_map:: Entry :: Occupied ( _) => { } ,
1656
+ hash_map:: Entry :: Vacant ( entry) => { entry. insert ( ( spend_txid, height) ) ; }
1639
1657
}
1640
1658
}
1641
- match self . pending_claim_requests . entry ( spend_tx . txid ( ) ) {
1659
+ match self . pending_claim_requests . entry ( spend_txid ) {
1642
1660
hash_map:: Entry :: Occupied ( _) => { } ,
1643
1661
hash_map:: Entry :: Vacant ( entry) => { entry. insert ( ClaimTxBumpMaterial { height_timer, feerate_previous : used_feerate, soonest_timelock, per_input_material } ) ; }
1644
1662
}
@@ -1831,6 +1849,10 @@ impl ChannelMonitor {
1831
1849
log_trace ! ( self , "Outpoint {}:{} is being being claimed, if it doesn't succeed, a bumped claiming txn is going to be broadcast at height {}" , single_htlc_tx. input[ 0 ] . previous_output. txid, single_htlc_tx. input[ 0 ] . previous_output. vout, height_timer) ;
1832
1850
let mut per_input_material = HashMap :: with_capacity ( 1 ) ;
1833
1851
per_input_material. insert ( single_htlc_tx. input [ 0 ] . previous_output , InputMaterial :: RemoteHTLC { script : redeemscript, key : htlc_key, preimage : Some ( * payment_preimage) , amount : htlc. amount_msat / 1000 , locktime : 0 } ) ;
1852
+ match self . claimable_outpoints . entry ( single_htlc_tx. input [ 0 ] . previous_output ) {
1853
+ hash_map:: Entry :: Occupied ( _) => { } ,
1854
+ hash_map:: Entry :: Vacant ( entry) => { entry. insert ( ( single_htlc_tx. txid ( ) , height) ) ; }
1855
+ }
1834
1856
match self . pending_claim_requests . entry ( single_htlc_tx. txid ( ) ) {
1835
1857
hash_map:: Entry :: Occupied ( _) => { } ,
1836
1858
hash_map:: Entry :: Vacant ( entry) => { entry. insert ( ClaimTxBumpMaterial { height_timer, feerate_previous : used_feerate, soonest_timelock : htlc. cltv_expiry , per_input_material} ) ; }
@@ -1872,6 +1894,10 @@ impl ChannelMonitor {
1872
1894
log_trace ! ( self , "Outpoint {}:{} is being being claimed, if it doesn't succeed, a bumped claiming txn is going to be broadcast at height {}" , timeout_tx. input[ 0 ] . previous_output. txid, timeout_tx. input[ 0 ] . previous_output. vout, height_timer) ;
1873
1895
let mut per_input_material = HashMap :: with_capacity ( 1 ) ;
1874
1896
per_input_material. insert ( timeout_tx. input [ 0 ] . previous_output , InputMaterial :: RemoteHTLC { script : redeemscript, key : htlc_key, preimage : None , amount : htlc. amount_msat / 1000 , locktime : htlc. cltv_expiry } ) ;
1897
+ match self . claimable_outpoints . entry ( timeout_tx. input [ 0 ] . previous_output ) {
1898
+ hash_map:: Entry :: Occupied ( _) => { } ,
1899
+ hash_map:: Entry :: Vacant ( entry) => { entry. insert ( ( timeout_tx. txid ( ) , height) ) ; }
1900
+ }
1875
1901
match self . pending_claim_requests . entry ( timeout_tx. txid ( ) ) {
1876
1902
hash_map:: Entry :: Occupied ( _) => { } ,
1877
1903
hash_map:: Entry :: Vacant ( entry) => { entry. insert ( ClaimTxBumpMaterial { height_timer, feerate_previous : used_feerate, soonest_timelock : htlc. cltv_expiry , per_input_material } ) ; }
@@ -1912,12 +1938,17 @@ impl ChannelMonitor {
1912
1938
}
1913
1939
}
1914
1940
let height_timer = Self :: get_height_timer ( height, soonest_timelock) ;
1941
+ let spend_txid = spend_tx. txid ( ) ;
1915
1942
for ( input, info) in spend_tx. input . iter_mut ( ) . zip ( inputs_info. iter ( ) ) {
1916
1943
let ( redeemscript, htlc_key) = sign_input ! ( sighash_parts, input, info. 1 , ( info. 0 ) . 0 . to_vec( ) ) ;
1917
1944
log_trace ! ( self , "Outpoint {}:{} is being being claimed, if it doesn't succeed, a bumped claiming txn is going to be broadcast at height {}" , input. previous_output. txid, input. previous_output. vout, height_timer) ;
1918
1945
per_input_material. insert ( input. previous_output , InputMaterial :: RemoteHTLC { script : redeemscript, key : htlc_key, preimage : Some ( * ( info. 0 ) ) , amount : info. 1 , locktime : 0 } ) ;
1946
+ match self . claimable_outpoints . entry ( input. previous_output ) {
1947
+ hash_map:: Entry :: Occupied ( _) => { } ,
1948
+ hash_map:: Entry :: Vacant ( entry) => { entry. insert ( ( spend_txid, height) ) ; }
1949
+ }
1919
1950
}
1920
- match self . pending_claim_requests . entry ( spend_tx . txid ( ) ) {
1951
+ match self . pending_claim_requests . entry ( spend_txid ) {
1921
1952
hash_map:: Entry :: Occupied ( _) => { } ,
1922
1953
hash_map:: Entry :: Vacant ( entry) => { entry. insert ( ClaimTxBumpMaterial { height_timer, feerate_previous : used_feerate, soonest_timelock, per_input_material } ) ; }
1923
1954
}
@@ -2036,6 +2067,10 @@ impl ChannelMonitor {
2036
2067
log_trace ! ( self , "Outpoint {}:{} is being being claimed, if it doesn't succeed, a bumped claiming txn is going to be broadcast at height {}" , spend_tx. input[ 0 ] . previous_output. txid, spend_tx. input[ 0 ] . previous_output. vout, height_timer) ;
2037
2068
let mut per_input_material = HashMap :: with_capacity ( 1 ) ;
2038
2069
per_input_material. insert ( spend_tx. input [ 0 ] . previous_output , InputMaterial :: Revoked { script : redeemscript, pubkey : None , key : revocation_key, is_htlc : false , amount : tx. output [ 0 ] . value } ) ;
2070
+ match self . claimable_outpoints . entry ( spend_tx. input [ 0 ] . previous_output ) {
2071
+ hash_map:: Entry :: Occupied ( _) => { } ,
2072
+ hash_map:: Entry :: Vacant ( entry) => { entry. insert ( ( spend_tx. txid ( ) , height) ) ; }
2073
+ }
2039
2074
match self . pending_claim_requests . entry ( spend_tx. txid ( ) ) {
2040
2075
hash_map:: Entry :: Occupied ( _) => { } ,
2041
2076
hash_map:: Entry :: Vacant ( entry) => { entry. insert ( ClaimTxBumpMaterial { height_timer, feerate_previous : used_feerate, soonest_timelock : height + self . our_to_self_delay as u32 , per_input_material } ) ; }
@@ -2099,6 +2134,7 @@ impl ChannelMonitor {
2099
2134
let height_timer = Self :: get_height_timer ( height, htlc. cltv_expiry ) ;
2100
2135
let mut per_input_material = HashMap :: with_capacity ( 1 ) ;
2101
2136
per_input_material. insert ( htlc_timeout_tx. input [ 0 ] . previous_output , InputMaterial :: LocalHTLC { script : htlc_script, sigs : ( * their_sig, * our_sig) , preimage : None , amount : htlc. amount_msat / 1000 } ) ;
2137
+ //TODO: with option_simplified_commitment track outpoint too
2102
2138
log_trace ! ( self , "Outpoint {}:{} is being being claimed, if it doesn't succeed, a bumped claiming txn is going to be broadcast at height {}" , htlc_timeout_tx. input[ 0 ] . previous_output. vout, htlc_timeout_tx. input[ 0 ] . previous_output. txid, height_timer) ;
2103
2139
pending_claims. push ( ( htlc_timeout_tx. txid ( ) , ClaimTxBumpMaterial { height_timer, feerate_previous : 0 , soonest_timelock : htlc. cltv_expiry , per_input_material } ) ) ;
2104
2140
res. push ( htlc_timeout_tx) ;
@@ -2122,6 +2158,7 @@ impl ChannelMonitor {
2122
2158
let height_timer = Self :: get_height_timer ( height, htlc. cltv_expiry ) ;
2123
2159
let mut per_input_material = HashMap :: with_capacity ( 1 ) ;
2124
2160
per_input_material. insert ( htlc_success_tx. input [ 0 ] . previous_output , InputMaterial :: LocalHTLC { script : htlc_script, sigs : ( * their_sig, * our_sig) , preimage : Some ( * payment_preimage) , amount : htlc. amount_msat / 1000 } ) ;
2161
+ //TODO: with option_simplified_commitment track outpoint too
2125
2162
log_trace ! ( self , "Outpoint {}:{} is being being claimed, if it doesn't succeed, a bumped claiming txn is going to be broadcast at height {}" , htlc_success_tx. input[ 0 ] . previous_output. vout, htlc_success_tx. input[ 0 ] . previous_output. txid, height_timer) ;
2126
2163
pending_claims. push ( ( htlc_success_tx. txid ( ) , ClaimTxBumpMaterial { height_timer, feerate_previous : 0 , soonest_timelock : htlc. cltv_expiry , per_input_material } ) ) ;
2127
2164
res. push ( htlc_success_tx) ;
@@ -2356,17 +2393,18 @@ impl ChannelMonitor {
2356
2393
}
2357
2394
2358
2395
// Scan all input to verify is one of the outpoint spent is of interest for us
2396
+ let mut claimed_outpoints = Vec :: new ( ) ;
2397
+ let mut claimed_input_material = Vec :: new ( ) ;
2359
2398
for inp in & tx. input {
2360
2399
if let Some ( ancestor_claimable_txid) = self . claimable_outpoints . get ( & inp. previous_output ) {
2361
2400
// If outpoint has claim request pending on it...
2362
2401
if let Some ( claim_material) = self . pending_claim_requests . get_mut ( & ancestor_claimable_txid. 0 ) {
2363
2402
//... we need to verify equality between transaction outpoints and claim request
2364
2403
// outpoints to know if transaction is the original claim or a bumped one issued
2365
2404
// by us.
2366
- let mut claimed_outpoints = Vec :: new ( ) ;
2367
- for ( claim_inp, tx_inp) in claim_material. per_input_material . keys ( ) . zip ( tx. input . iter ( ) ) {
2368
- if * claim_inp != tx_inp. previous_output {
2369
- claimed_outpoints. push ( tx_inp. previous_output . clone ( ) ) ;
2405
+ for claim_inp in claim_material. per_input_material . keys ( ) {
2406
+ if * claim_inp == inp. previous_output {
2407
+ claimed_outpoints. push ( inp. previous_output . clone ( ) ) ;
2370
2408
}
2371
2409
}
2372
2410
if claimed_outpoints. len ( ) == 0 && claim_material. per_input_material . len ( ) == tx. input . len ( ) { // If true, register claim request to be removed after reaching a block security height
@@ -2377,19 +2415,30 @@ impl ChannelMonitor {
2377
2415
}
2378
2416
}
2379
2417
} else { // If false, generate new claim request with update outpoint set
2380
- for already_claimed in claimed_outpoints {
2381
- claim_material. per_input_material . remove ( & already_claimed) ;
2418
+ for already_claimed in claimed_outpoints. iter ( ) {
2419
+ if let Some ( input_material) = claim_material. per_input_material . remove ( & already_claimed) {
2420
+ claimed_input_material. push ( input_material) ;
2421
+ }
2382
2422
}
2383
2423
// Avoid bump engine using inaccurate feerate due to new transaction size
2384
2424
claim_material. feerate_previous = 0 ;
2385
2425
//TODO: recompute soonest_timelock to avoid wasting a bit on fees
2386
2426
bump_candidates. push ( ( ancestor_claimable_txid. 0 . clone ( ) , claim_material. clone ( ) ) ) ;
2387
2427
}
2428
+ break ; //No need to iterate further, either tx is our or their
2388
2429
} else {
2389
2430
panic ! ( "Inconsistencies between pending_claim_requests map and claimable_outpoints map" ) ;
2390
2431
}
2391
2432
}
2392
2433
}
2434
+ for ( outpoint, input_material) in claimed_outpoints. iter ( ) . zip ( claimed_input_material. drain ( ..) ) {
2435
+ match self . onchain_events_waiting_threshold_conf . entry ( height + ANTI_REORG_DELAY - 1 ) {
2436
+ hash_map:: Entry :: Occupied ( _) => { } ,
2437
+ hash_map:: Entry :: Vacant ( entry) => {
2438
+ entry. insert ( vec ! [ OnchainEvent :: ContentiousOutpoint { outpoint: * outpoint, input_material: input_material } ] ) ;
2439
+ }
2440
+ }
2441
+ }
2393
2442
}
2394
2443
if let Some ( ref cur_local_tx) = self . current_local_signed_commitment_tx {
2395
2444
if self . would_broadcast_at_height ( height) {
@@ -2432,6 +2481,9 @@ impl ChannelMonitor {
2432
2481
log_trace ! ( self , "HTLC {} failure update has got enough confirmations to be passed upstream" , log_bytes!( ( htlc_update. 1 ) . 0 ) ) ;
2433
2482
htlc_updated. push ( ( htlc_update. 0 , None , htlc_update. 1 ) ) ;
2434
2483
} ,
2484
+ OnchainEvent :: ContentiousOutpoint { outpoint, .. } => {
2485
+ self . claimable_outpoints . remove ( & outpoint) ;
2486
+ }
2435
2487
}
2436
2488
}
2437
2489
}
@@ -2454,13 +2506,52 @@ impl ChannelMonitor {
2454
2506
( watch_outputs, spendable_outputs, htlc_updated)
2455
2507
}
2456
2508
2457
- fn block_disconnected ( & mut self , height : u32 , block_hash : & Sha256dHash ) {
2458
- if let Some ( _) = self . onchain_events_waiting_threshold_conf . remove ( & ( height + ANTI_REORG_DELAY - 1 ) ) {
2509
+ fn block_disconnected ( & mut self , height : u32 , block_hash : & Sha256dHash , broadcaster : & BroadcasterInterface , fee_estimator : & FeeEstimator ) {
2510
+ let mut bump_candidates = HashMap :: new ( ) ;
2511
+ if let Some ( events) = self . onchain_events_waiting_threshold_conf . remove ( & ( height + ANTI_REORG_DELAY - 1 ) ) {
2459
2512
//We may discard:
2460
2513
//- htlc update there as failure-trigger tx (revoked commitment tx, non-revoked commitment tx, HTLC-timeout tx) has been disconnected
2461
2514
//- our claim tx on a commitment tx output
2515
+ //- resurect outpoint back in its claimable set and regenerate tx
2516
+ for ev in events {
2517
+ match ev {
2518
+ OnchainEvent :: ContentiousOutpoint { outpoint, input_material } => {
2519
+ if let Some ( ancestor_claimable_txid) = self . claimable_outpoints . get ( & outpoint) {
2520
+ if let Some ( claim_material) = self . pending_claim_requests . get_mut ( & ancestor_claimable_txid. 0 ) {
2521
+ // Avoid bump engine using inaccurate feerate due to new transaction size
2522
+ claim_material. feerate_previous = 0 ;
2523
+ claim_material. per_input_material . insert ( outpoint, input_material) ;
2524
+ // Using a HashMap guarantee us than if we have multiple outpoints getting
2525
+ // resurrected only one bump claim tx is going to be broadcast
2526
+ bump_candidates. insert ( ancestor_claimable_txid. clone ( ) , claim_material. clone ( ) ) ;
2527
+ }
2528
+ }
2529
+ } ,
2530
+ _ => { } ,
2531
+ }
2532
+ }
2533
+ }
2534
+ for ( _, claim_material) in bump_candidates. iter_mut ( ) {
2535
+ if let Some ( ( new_timer, new_feerate, bump_tx) ) = self . bump_claim_tx ( height, & claim_material, fee_estimator) {
2536
+ claim_material. height_timer = new_timer;
2537
+ claim_material. feerate_previous = new_feerate;
2538
+ broadcaster. broadcast_transaction ( & bump_tx) ;
2539
+ }
2540
+ }
2541
+ for ( ancestor_claim_txid, claim_material) in bump_candidates. drain ( ) {
2542
+ self . pending_claim_requests . insert ( ancestor_claim_txid. 0 , claim_material) ;
2543
+ }
2544
+ //TODO: if we implement cross-block aggregated claim transaction we need to refresh set of outpoints and regenerate tx but
2545
+ // right now if one of the outpoint get disconnected, just erase whole pending claim request.
2546
+ let mut remove_request = Vec :: new ( ) ;
2547
+ self . claimable_outpoints . retain ( |_, ref v|
2548
+ if v. 1 == height {
2549
+ remove_request. push ( v. 0 . clone ( ) ) ;
2550
+ false
2551
+ } else { true } ) ;
2552
+ for req in remove_request {
2553
+ self . pending_claim_requests . remove ( & req) ;
2462
2554
}
2463
- self . claimable_outpoints . retain ( |_, ref v| if v. 1 == height { false } else { true } ) ;
2464
2555
self . last_block_hash = block_hash. clone ( ) ;
2465
2556
}
2466
2557
@@ -3060,6 +3151,14 @@ impl<R: ::std::io::Read> ReadableArgs<R, Arc<Logger>> for (Sha256dHash, ChannelM
3060
3151
htlc_update : ( htlc_source, hash)
3061
3152
}
3062
3153
} ,
3154
+ 2 => {
3155
+ let outpoint = Readable :: read ( reader) ?;
3156
+ let input_material = Readable :: read ( reader) ?;
3157
+ OnchainEvent :: ContentiousOutpoint {
3158
+ outpoint,
3159
+ input_material
3160
+ }
3161
+ }
3063
3162
_ => return Err ( DecodeError :: InvalidValue ) ,
3064
3163
} ;
3065
3164
events. push ( ev) ;
0 commit comments