@@ -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
}
@@ -397,6 +397,96 @@ enum InputMaterial {
397
397
}
398
398
}
399
399
400
+ impl Writeable for InputMaterial {
401
+ fn write < W : Writer > ( & self , writer : & mut W ) -> Result < ( ) , :: std:: io:: Error > {
402
+ match self {
403
+ & InputMaterial :: Revoked { ref script, ref pubkey, ref key, ref is_htlc, ref amount} => {
404
+ writer. write_all ( & [ 0 ; 1 ] ) ?;
405
+ script. write ( writer) ?;
406
+ pubkey. write ( writer) ?;
407
+ writer. write_all ( & key[ ..] ) ?;
408
+ if * is_htlc {
409
+ writer. write_all ( & [ 0 ; 1 ] ) ?;
410
+ } else {
411
+ writer. write_all ( & [ 1 ; 1 ] ) ?;
412
+ }
413
+ writer. write_all ( & byte_utils:: be64_to_array ( * amount) ) ?;
414
+ } ,
415
+ & InputMaterial :: RemoteHTLC { ref script, ref key, ref preimage, ref amount, ref locktime } => {
416
+ writer. write_all ( & [ 1 ; 1 ] ) ?;
417
+ script. write ( writer) ?;
418
+ key. write ( writer) ?;
419
+ preimage. write ( writer) ?;
420
+ writer. write_all ( & byte_utils:: be64_to_array ( * amount) ) ?;
421
+ writer. write_all ( & byte_utils:: be32_to_array ( * locktime) ) ?;
422
+ } ,
423
+ & InputMaterial :: LocalHTLC { ref script, ref sigs, ref preimage, ref amount } => {
424
+ writer. write_all ( & [ 2 ; 1 ] ) ?;
425
+ script. write ( writer) ?;
426
+ sigs. 0 . write ( writer) ?;
427
+ sigs. 1 . write ( writer) ?;
428
+ preimage. write ( writer) ?;
429
+ writer. write_all ( & byte_utils:: be64_to_array ( * amount) ) ?;
430
+ }
431
+ }
432
+ Ok ( ( ) )
433
+ }
434
+ }
435
+
436
+ impl < R : std:: io:: Read > Readable < R > for InputMaterial {
437
+ fn read ( reader : & mut R ) -> Result < Self , DecodeError > {
438
+ let input_material = match <u8 as Readable < R > >:: read ( reader) ? {
439
+ 0 => {
440
+ let script = Readable :: read ( reader) ?;
441
+ let pubkey = Readable :: read ( reader) ?;
442
+ let key = Readable :: read ( reader) ?;
443
+ let is_htlc = match <u8 as Readable < R > >:: read ( reader) ? {
444
+ 0 => true ,
445
+ 1 => false ,
446
+ _ => return Err ( DecodeError :: InvalidValue ) ,
447
+ } ;
448
+ let amount = Readable :: read ( reader) ?;
449
+ InputMaterial :: Revoked {
450
+ script,
451
+ pubkey,
452
+ key,
453
+ is_htlc,
454
+ amount
455
+ }
456
+ } ,
457
+ 1 => {
458
+ let script = Readable :: read ( reader) ?;
459
+ let key = Readable :: read ( reader) ?;
460
+ let preimage = Readable :: read ( reader) ?;
461
+ let amount = Readable :: read ( reader) ?;
462
+ let locktime = Readable :: read ( reader) ?;
463
+ InputMaterial :: RemoteHTLC {
464
+ script,
465
+ key,
466
+ preimage,
467
+ amount,
468
+ locktime
469
+ }
470
+ } ,
471
+ 2 => {
472
+ let script = Readable :: read ( reader) ?;
473
+ let their_sig = Readable :: read ( reader) ?;
474
+ let our_sig = Readable :: read ( reader) ?;
475
+ let preimage = Readable :: read ( reader) ?;
476
+ let amount = Readable :: read ( reader) ?;
477
+ InputMaterial :: LocalHTLC {
478
+ script,
479
+ sigs : ( their_sig, our_sig) ,
480
+ preimage,
481
+ amount
482
+ }
483
+ }
484
+ _ => return Err ( DecodeError :: InvalidValue ) ,
485
+ } ;
486
+ Ok ( input_material)
487
+ }
488
+ }
489
+
400
490
/// Upon discovering of some classes of onchain tx by ChannelMonitor, we may have to take actions on it
401
491
/// once they mature to enough confirmations (ANTI_REORG_DELAY)
402
492
#[ derive( Clone , PartialEq ) ]
@@ -412,6 +502,13 @@ enum OnchainEvent {
412
502
HTLCUpdate {
413
503
htlc_update : ( HTLCSource , PaymentHash ) ,
414
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
+ }
415
512
}
416
513
417
514
/// Higher-level cache structure needed to re-generate bumped claim txn if needed
@@ -1170,36 +1267,7 @@ impl ChannelMonitor {
1170
1267
writer. write_all ( & byte_utils:: be64_to_array ( claim_tx_data. per_input_material . len ( ) as u64 ) ) ?;
1171
1268
for ( outp, tx_material) in claim_tx_data. per_input_material . iter ( ) {
1172
1269
outp. write ( writer) ?;
1173
- match tx_material {
1174
- & InputMaterial :: Revoked { ref script, ref pubkey, ref key, ref is_htlc, ref amount} => {
1175
- writer. write_all ( & [ 0 ; 1 ] ) ?;
1176
- script. write ( writer) ?;
1177
- pubkey. write ( writer) ?;
1178
- writer. write_all ( & key[ ..] ) ?;
1179
- if * is_htlc {
1180
- writer. write_all ( & [ 0 ; 1 ] ) ?;
1181
- } else {
1182
- writer. write_all ( & [ 1 ; 1 ] ) ?;
1183
- }
1184
- writer. write_all ( & byte_utils:: be64_to_array ( * amount) ) ?;
1185
- } ,
1186
- & InputMaterial :: RemoteHTLC { ref script, ref key, ref preimage, ref amount, ref locktime } => {
1187
- writer. write_all ( & [ 1 ; 1 ] ) ?;
1188
- script. write ( writer) ?;
1189
- key. write ( writer) ?;
1190
- preimage. write ( writer) ?;
1191
- writer. write_all ( & byte_utils:: be64_to_array ( * amount) ) ?;
1192
- writer. write_all ( & byte_utils:: be32_to_array ( * locktime) ) ?;
1193
- } ,
1194
- & InputMaterial :: LocalHTLC { ref script, ref sigs, ref preimage, ref amount } => {
1195
- writer. write_all ( & [ 2 ; 1 ] ) ?;
1196
- script. write ( writer) ?;
1197
- sigs. 0 . write ( writer) ?;
1198
- sigs. 1 . write ( writer) ?;
1199
- preimage. write ( writer) ?;
1200
- writer. write_all ( & byte_utils:: be64_to_array ( * amount) ) ?;
1201
- }
1202
- }
1270
+ tx_material. write ( writer) ?;
1203
1271
}
1204
1272
}
1205
1273
@@ -1224,6 +1292,11 @@ impl ChannelMonitor {
1224
1292
writer. write_all ( & [ 1 ; 1 ] ) ?;
1225
1293
htlc_update. 0 . write ( writer) ?;
1226
1294
htlc_update. 1 . write ( writer) ?;
1295
+ } ,
1296
+ OnchainEvent :: ContentiousOutpoint { ref outpoint, ref input_material } => {
1297
+ writer. write_all ( & [ 2 ; 1 ] ) ?;
1298
+ outpoint. write ( writer) ?;
1299
+ input_material. write ( writer) ?;
1227
1300
}
1228
1301
}
1229
1302
}
@@ -2250,14 +2323,15 @@ impl ChannelMonitor {
2250
2323
}
2251
2324
2252
2325
// Scan all input to verify is one of the outpoint spent is of interest for us
2326
+ let mut claimed_outpoints = Vec :: new ( ) ;
2327
+ let mut claimed_input_material = Vec :: new ( ) ;
2253
2328
for inp in & tx. input {
2254
2329
if let Some ( ancestor_claimable_txid) = self . claimable_outpoints . get ( & inp. previous_output ) {
2255
2330
// If outpoint has claim request pending on it...
2256
2331
if let Some ( claim_material) = self . pending_claim_requests . get_mut ( & ancestor_claimable_txid. 0 ) {
2257
2332
//... we need to verify equality between transaction outpoints and claim request
2258
2333
// outpoints to know if transaction is the original claim or a bumped one issued
2259
2334
// by us.
2260
- let mut claimed_outpoints = Vec :: new ( ) ;
2261
2335
for ( claim_inp, tx_inp) in claim_material. per_input_material . keys ( ) . zip ( tx. input . iter ( ) ) {
2262
2336
if * claim_inp != tx_inp. previous_output {
2263
2337
claimed_outpoints. push ( tx_inp. previous_output . clone ( ) ) ;
@@ -2271,19 +2345,30 @@ impl ChannelMonitor {
2271
2345
}
2272
2346
}
2273
2347
} else { // If false, generate new claim request with update outpoint set
2274
- for already_claimed in claimed_outpoints {
2275
- claim_material. per_input_material . remove ( & already_claimed) ;
2348
+ for already_claimed in claimed_outpoints. iter ( ) {
2349
+ if let Some ( input_material) = claim_material. per_input_material . remove ( & already_claimed) {
2350
+ claimed_input_material. push ( input_material) ;
2351
+ }
2276
2352
}
2277
2353
// Avoid bump engine using inaccurate feerate due to new transaction size
2278
2354
claim_material. feerate_previous = 0 ;
2279
2355
//TODO: recompute soonest_timelock to avoid wasting a bit on fees
2280
2356
bump_candidates. push ( ( ancestor_claimable_txid. 0 . clone ( ) , claim_material. clone ( ) ) ) ;
2281
2357
}
2358
+ break ; //No need to iterate further, either tx is our or their
2282
2359
} else {
2283
2360
panic ! ( "Inconsistencies between pending_claim_requests map and claimable_outpoints map" ) ;
2284
2361
}
2285
2362
}
2286
2363
}
2364
+ for ( outpoint, input_material) in claimed_outpoints. iter ( ) . zip ( claimed_input_material. drain ( ..) ) {
2365
+ match self . onchain_events_waiting_threshold_conf . entry ( height + ANTI_REORG_DELAY - 1 ) {
2366
+ hash_map:: Entry :: Occupied ( _) => { } ,
2367
+ hash_map:: Entry :: Vacant ( entry) => {
2368
+ entry. insert ( vec ! [ OnchainEvent :: ContentiousOutpoint { outpoint: * outpoint, input_material: input_material } ] ) ;
2369
+ }
2370
+ }
2371
+ }
2287
2372
}
2288
2373
if let Some ( ref cur_local_tx) = self . current_local_signed_commitment_tx {
2289
2374
if self . would_broadcast_at_height ( height) {
@@ -2326,6 +2411,9 @@ impl ChannelMonitor {
2326
2411
log_trace ! ( self , "HTLC {} failure update has got enough confirmations to be passed upstream" , log_bytes!( ( htlc_update. 1 ) . 0 ) ) ;
2327
2412
htlc_updated. push ( ( htlc_update. 0 , None , htlc_update. 1 ) ) ;
2328
2413
} ,
2414
+ OnchainEvent :: ContentiousOutpoint { outpoint, .. } => {
2415
+ self . claimable_outpoints . remove ( & outpoint) ;
2416
+ }
2329
2417
}
2330
2418
}
2331
2419
}
@@ -2348,13 +2436,52 @@ impl ChannelMonitor {
2348
2436
( watch_outputs, spendable_outputs, htlc_updated)
2349
2437
}
2350
2438
2351
- fn block_disconnected ( & mut self , height : u32 , block_hash : & Sha256dHash ) {
2352
- if let Some ( _) = self . onchain_events_waiting_threshold_conf . remove ( & ( height + ANTI_REORG_DELAY - 1 ) ) {
2439
+ fn block_disconnected ( & mut self , height : u32 , block_hash : & Sha256dHash , broadcaster : & BroadcasterInterface , fee_estimator : & FeeEstimator ) {
2440
+ let mut bump_candidates = HashMap :: new ( ) ;
2441
+ if let Some ( events) = self . onchain_events_waiting_threshold_conf . remove ( & ( height + ANTI_REORG_DELAY - 1 ) ) {
2353
2442
//We may discard:
2354
2443
//- htlc update there as failure-trigger tx (revoked commitment tx, non-revoked commitment tx, HTLC-timeout tx) has been disconnected
2355
2444
//- our claim tx on a commitment tx output
2445
+ //- resurect outpoint back in its claimable set and regenerate tx
2446
+ for ev in events {
2447
+ match ev {
2448
+ OnchainEvent :: ContentiousOutpoint { outpoint, input_material } => {
2449
+ if let Some ( ancestor_claimable_txid) = self . claimable_outpoints . get ( & outpoint) {
2450
+ if let Some ( claim_material) = self . pending_claim_requests . get_mut ( & ancestor_claimable_txid. 0 ) {
2451
+ // Avoid bump engine using inaccurate feerate due to new transaction size
2452
+ claim_material. feerate_previous = 0 ;
2453
+ claim_material. per_input_material . insert ( outpoint, input_material) ;
2454
+ // Using a HashMap guarantee us than if we have multiple outpoints getting
2455
+ // resurrected only one bump claim tx is going to be broadcast
2456
+ bump_candidates. insert ( ancestor_claimable_txid. clone ( ) , claim_material. clone ( ) ) ;
2457
+ }
2458
+ }
2459
+ } ,
2460
+ _ => { } ,
2461
+ }
2462
+ }
2463
+ }
2464
+ for ( _, claim_material) in bump_candidates. iter_mut ( ) {
2465
+ if let Some ( ( new_timer, new_feerate, bump_tx) ) = self . bump_claim_tx ( height, & claim_material, fee_estimator) {
2466
+ claim_material. height_timer = new_timer;
2467
+ claim_material. feerate_previous = new_feerate;
2468
+ broadcaster. broadcast_transaction ( & bump_tx) ;
2469
+ }
2470
+ }
2471
+ for ( ancestor_claim_txid, claim_material) in bump_candidates. drain ( ) {
2472
+ self . pending_claim_requests . insert ( ancestor_claim_txid. 0 , claim_material) ;
2473
+ }
2474
+ //TODO: if we implement cross-block aggregated claim transaction we need to refresh set of outpoints and regenerate tx but
2475
+ // right now if one of the outpoint get disconnected, just erase whole pending claim request.
2476
+ let mut remove_request = Vec :: new ( ) ;
2477
+ self . claimable_outpoints . retain ( |_, ref v|
2478
+ if v. 1 == height {
2479
+ remove_request. push ( v. 0 . clone ( ) ) ;
2480
+ false
2481
+ } else { true } ) ;
2482
+ for req in remove_request {
2483
+ self . pending_claim_requests . remove ( & req) ;
2356
2484
}
2357
- self . claimable_outpoints . retain ( |_, ref v| if v. 1 == height { false } else { true } ) ;
2358
2485
self . last_block_hash = block_hash. clone ( ) ;
2359
2486
}
2360
2487
@@ -2927,55 +3054,8 @@ impl<R: ::std::io::Read> ReadableArgs<R, Arc<Logger>> for (Sha256dHash, ChannelM
2927
3054
let mut per_input_material = HashMap :: with_capacity ( cmp:: min ( per_input_material_len as usize , MAX_ALLOC_SIZE / 128 ) ) ;
2928
3055
for _ in 0 ..per_input_material_len {
2929
3056
let outpoint = Readable :: read ( reader) ?;
2930
- let tx_material = match <u8 as Readable < R > >:: read ( reader) ? {
2931
- 0 => {
2932
- let script = Readable :: read ( reader) ?;
2933
- let pubkey = Readable :: read ( reader) ?;
2934
- let key = Readable :: read ( reader) ?;
2935
- let is_htlc = match <u8 as Readable < R > >:: read ( reader) ? {
2936
- 0 => true ,
2937
- 1 => false ,
2938
- _ => return Err ( DecodeError :: InvalidValue ) ,
2939
- } ;
2940
- let amount = Readable :: read ( reader) ?;
2941
- InputMaterial :: Revoked {
2942
- script,
2943
- pubkey,
2944
- key,
2945
- is_htlc,
2946
- amount
2947
- }
2948
- } ,
2949
- 1 => {
2950
- let script = Readable :: read ( reader) ?;
2951
- let key = Readable :: read ( reader) ?;
2952
- let preimage = Readable :: read ( reader) ?;
2953
- let amount = Readable :: read ( reader) ?;
2954
- let locktime = Readable :: read ( reader) ?;
2955
- InputMaterial :: RemoteHTLC {
2956
- script,
2957
- key,
2958
- preimage,
2959
- amount,
2960
- locktime
2961
- }
2962
- } ,
2963
- 2 => {
2964
- let script = Readable :: read ( reader) ?;
2965
- let their_sig = Readable :: read ( reader) ?;
2966
- let our_sig = Readable :: read ( reader) ?;
2967
- let preimage = Readable :: read ( reader) ?;
2968
- let amount = Readable :: read ( reader) ?;
2969
- InputMaterial :: LocalHTLC {
2970
- script,
2971
- sigs : ( their_sig, our_sig) ,
2972
- preimage,
2973
- amount
2974
- }
2975
- }
2976
- _ => return Err ( DecodeError :: InvalidValue ) ,
2977
- } ;
2978
- per_input_material. insert ( outpoint, tx_material) ;
3057
+ let input_material = Readable :: read ( reader) ?;
3058
+ per_input_material. insert ( outpoint, input_material) ;
2979
3059
}
2980
3060
pending_claim_requests. insert ( ancestor_claim_txid, ClaimTxBumpMaterial { height_timer, feerate_previous, soonest_timelock, per_input_material } ) ;
2981
3061
}
@@ -3010,6 +3090,14 @@ impl<R: ::std::io::Read> ReadableArgs<R, Arc<Logger>> for (Sha256dHash, ChannelM
3010
3090
htlc_update : ( htlc_source, hash)
3011
3091
}
3012
3092
} ,
3093
+ 2 => {
3094
+ let outpoint = Readable :: read ( reader) ?;
3095
+ let input_material = Readable :: read ( reader) ?;
3096
+ OnchainEvent :: ContentiousOutpoint {
3097
+ outpoint,
3098
+ input_material
3099
+ }
3100
+ }
3013
3101
_ => return Err ( DecodeError :: InvalidValue ) ,
3014
3102
} ;
3015
3103
events. push ( ev) ;
0 commit comments