3
3
//! claim outputs on-chain.
4
4
5
5
use chain:: transaction:: OutPoint ;
6
- use chain:: chaininterface:: { ChainListener , ChainWatchInterface } ;
6
+ use chain:: chaininterface:: { ChainListener , ChainWatchInterface , ChainWatchInterfaceUtil } ;
7
7
use chain:: keysinterface:: { KeysInterface , SpendableOutputDescriptor } ;
8
8
use chain:: keysinterface;
9
9
use ln:: channel:: { COMMITMENT_TX_BASE_WEIGHT , COMMITMENT_TX_WEIGHT_PER_HTLC , BREAKDOWN_TIMEOUT } ;
@@ -19,6 +19,7 @@ use util::events::{Event, EventsProvider, MessageSendEvent, MessageSendEventsPro
19
19
use util:: errors:: APIError ;
20
20
use util:: ser:: { Writeable , ReadableArgs } ;
21
21
use util:: config:: UserConfig ;
22
+ use util:: logger:: Logger ;
22
23
use util:: rng;
23
24
24
25
use bitcoin:: util:: hash:: BitcoinHash ;
@@ -41,7 +42,7 @@ use secp256k1::key::{PublicKey,SecretKey};
41
42
42
43
use std:: collections:: { BTreeSet , HashMap , HashSet } ;
43
44
use std:: default:: Default ;
44
- use std:: sync:: Arc ;
45
+ use std:: sync:: { Arc , Mutex } ;
45
46
use std:: sync:: atomic:: Ordering ;
46
47
use std:: time:: Instant ;
47
48
use std:: mem;
@@ -5709,3 +5710,252 @@ fn test_sweep_outbound_htlc_failure_update() {
5709
5710
do_test_sweep_outbound_htlc_failure_update ( false , false ) ;
5710
5711
do_test_sweep_outbound_htlc_failure_update ( true , false ) ;
5711
5712
}
5713
+
5714
+
5715
+ // aggregation
5716
+ // multiple bumping rounds
5717
+ // 2 different bumping heuristic
5718
+ // min relay fee isn't enough
5719
+
5720
+ #[ test]
5721
+ fn test_bump_penalty_txn_on_revoked_commitment ( ) {
5722
+ // In case of penalty txn with too low feerates for getting into mempools, RBF-bump them to be sure
5723
+ // we're able to claim outputs on revoked commitment transaction before timelocks expiration
5724
+
5725
+ let mut nodes = create_network ( 2 ) ;
5726
+
5727
+ let chan = create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 1000000 , 59000000 ) ;
5728
+ let payment_preimage = route_payment ( & nodes[ 0 ] , & vec ! ( & nodes[ 1 ] ) [ ..] , 3000000 ) . 0 ;
5729
+ route_payment ( & nodes[ 1 ] , & vec ! ( & nodes[ 0 ] ) [ ..] , 3000000 ) . 0 ;
5730
+ let revoked_local_txn = nodes[ 0 ] . node . channel_state . lock ( ) . unwrap ( ) . by_id . get ( & chan. 2 ) . unwrap ( ) . last_local_commitment_txn . clone ( ) ;
5731
+ assert_eq ! ( revoked_local_txn[ 0 ] . output. len( ) , 4 ) ;
5732
+ assert_eq ! ( revoked_local_txn[ 0 ] . input. len( ) , 1 ) ;
5733
+ assert_eq ! ( revoked_local_txn[ 0 ] . input[ 0 ] . previous_output. txid, chan. 3 . txid( ) ) ;
5734
+ let revoked_txid = revoked_local_txn[ 0 ] . txid ( ) ;
5735
+
5736
+ let mut penalty_amount = 0 ;
5737
+ for outp in revoked_local_txn[ 0 ] . output . iter ( ) {
5738
+ if outp. script_pubkey . is_v0_p2wsh ( ) {
5739
+ penalty_amount += outp. value ;
5740
+ }
5741
+ }
5742
+
5743
+ claim_payment ( & nodes[ 0 ] , & vec ! ( & nodes[ 1 ] ) [ ..] , payment_preimage) ;
5744
+ let header = BlockHeader { version : 0x20000000 , prev_blockhash : Default :: default ( ) , merkle_root : Default :: default ( ) , time : 42 , bits : 42 , nonce : 42 } ;
5745
+ nodes[ 1 ] . chain_monitor . block_connected_with_filtering ( & Block { header, txdata : vec ! [ revoked_local_txn[ 0 ] . clone( ) ] } , 1 ) ;
5746
+
5747
+ let previous_penalty_txid;
5748
+ let feerate_1;
5749
+ {
5750
+ let mut node_txn = nodes[ 1 ] . tx_broadcaster . txn_broadcasted . lock ( ) . unwrap ( ) ;
5751
+ assert_eq ! ( node_txn. len( ) , 2 ) ;
5752
+ assert_eq ! ( node_txn[ 0 ] , node_txn[ 1 ] ) ;
5753
+ assert_eq ! ( node_txn[ 0 ] . input. len( ) , 3 ) ; // Penalty txn claims to_local, offered_htlc and received_htlc outputs
5754
+ assert_eq ! ( node_txn[ 0 ] . output. len( ) , 1 ) ;
5755
+ check_spends ! ( node_txn[ 0 ] , revoked_local_txn[ 0 ] . clone( ) ) ;
5756
+ let fee_1 = penalty_amount - node_txn[ 0 ] . output [ 0 ] . value ;
5757
+ feerate_1 = fee_1 * 1000 / node_txn[ 0 ] . get_weight ( ) ;
5758
+ previous_penalty_txid = node_txn[ 0 ] . txid ( ) ;
5759
+ node_txn. clear ( ) ;
5760
+ } ;
5761
+
5762
+ let header = connect_blocks ( & nodes[ 1 ] . chain_monitor , 3 , 1 , true , header. bitcoin_hash ( ) ) ;
5763
+ let mut penalties_2 = HashMap :: new ( ) ;
5764
+ let mut feerate_2 = 0 ;
5765
+ {
5766
+ let mut node_txn = nodes[ 1 ] . tx_broadcaster . txn_broadcasted . lock ( ) . unwrap ( ) ;
5767
+ assert_eq ! ( node_txn. len( ) , 3 ) ;
5768
+ for tx in node_txn. drain ( ..) {
5769
+ if tx. input [ 0 ] . previous_output . txid == revoked_txid {
5770
+ let txid = tx. txid ( ) ;
5771
+ // Verify new bumped tx is different from last claiming transaction, we don't want spurrious rebroadcast
5772
+ assert_ne ! ( previous_penalty_txid, txid) ;
5773
+ check_spends ! ( tx, revoked_local_txn[ 0 ] . clone( ) ) ;
5774
+ let fee_2 = revoked_local_txn[ 0 ] . output [ tx. input [ 0 ] . previous_output . vout as usize ] . value - tx. output [ 0 ] . value ;
5775
+ feerate_2 = fee_2 * 1000 / tx. get_weight ( ) ;
5776
+ // Verify 25% bump heuristic
5777
+ assert ! ( feerate_2 * 100 >= feerate_1 * 125 ) ;
5778
+ penalties_2. insert ( tx. input [ 0 ] . previous_output , ( txid, fee_2) ) ;
5779
+ }
5780
+ }
5781
+ }
5782
+ assert_ne ! ( feerate_2, 0 ) ;
5783
+
5784
+ let header = connect_blocks ( & nodes[ 1 ] . chain_monitor , 3 , 4 , true , header. bitcoin_hash ( ) ) ;
5785
+ let mut penalties_3 = HashMap :: new ( ) ;
5786
+ let mut feerate_3 = 0 ;
5787
+ {
5788
+ let mut node_txn = nodes[ 1 ] . tx_broadcaster . txn_broadcasted . lock ( ) . unwrap ( ) ;
5789
+ assert_eq ! ( node_txn. len( ) , 5 ) ;
5790
+ for tx in node_txn. drain ( ..) {
5791
+ if let Some ( previous_data) = penalties_2. get ( & tx. input [ 0 ] . previous_output ) {
5792
+ let txid = tx. txid ( ) ;
5793
+ // Verify new bumped tx is different from any last claiming treansaction, we don't want spurrious rebroadcast
5794
+ assert_ne ! ( previous_data. 0 , txid) ;
5795
+ check_spends ! ( tx, revoked_local_txn[ 0 ] . clone( ) ) ;
5796
+ let fee_3 = revoked_local_txn[ 0 ] . output [ tx. input [ 0 ] . previous_output . vout as usize ] . value - tx. output [ 0 ] . value ;
5797
+ feerate_3 = fee_3 * 1000 / tx. get_weight ( ) ;
5798
+ // Verify 25% bump heuristic;
5799
+ assert ! ( feerate_3 * 100 >= feerate_2 * 125 ) ;
5800
+ penalties_3. insert ( tx. input [ 0 ] . previous_output , ( txid, fee_3) ) ;
5801
+ }
5802
+ }
5803
+ }
5804
+ assert_ne ! ( feerate_3, 0 ) ;
5805
+
5806
+ macro_rules! update_fee_estimator {
5807
+ ( $node: expr, $sat_per_kw: expr, $min_relay_sat_per_kw: expr) => {
5808
+ let funding_outpoint = OutPoint { txid: chan. 3 . txid( ) , index: 0 } ;
5809
+ let current_monitor = nodes[ 1 ] . chan_monitor. simple_monitor. monitors. lock( ) . unwrap( ) . remove( & funding_outpoint) . unwrap( ) ;
5810
+ let logger: Arc <Logger > = Arc :: new( test_utils:: TestLogger :: with_id( format!( "node {}" , 1 ) ) ) ;
5811
+ let chain_monitor = Arc :: new( ChainWatchInterfaceUtil :: new( Network :: Testnet , Arc :: clone( & logger) ) ) ;
5812
+ let new_fee_estimator = Arc :: new( test_utils:: TestFeeEstimator { sat_per_kw: $sat_per_kw, min_relay_sat_per_kw: $min_relay_sat_per_kw} ) ;
5813
+ let tx_broadcaster = Arc :: new( test_utils:: TestBroadcaster { txn_broadcasted: Mutex :: new( Vec :: new( ) ) } ) ;
5814
+ let chan_monitor = Arc :: new( test_utils:: TestChannelMonitor :: new( chain_monitor. clone( ) , tx_broadcaster. clone( ) , logger. clone( ) , new_fee_estimator. clone( ) ) ) ;
5815
+ assert!( chan_monitor. add_update_monitor( funding_outpoint, current_monitor) . is_ok( ) ) ;
5816
+ $node. chain_monitor = chain_monitor;
5817
+ $node. chan_monitor = chan_monitor;
5818
+ $node. tx_broadcaster = tx_broadcaster;
5819
+ }
5820
+ }
5821
+
5822
+ update_fee_estimator ! ( nodes[ 1 ] , 500 , 0 ) ;
5823
+ let header = connect_blocks ( & nodes[ 1 ] . chain_monitor , 3 , 7 , true , header. bitcoin_hash ( ) ) ;
5824
+ let mut penalties_4 = HashMap :: new ( ) ;
5825
+ let mut feerate_4 = 0 ;
5826
+ {
5827
+ let mut node_txn = nodes[ 1 ] . tx_broadcaster . txn_broadcasted . lock ( ) . unwrap ( ) ;
5828
+ assert_eq ! ( node_txn. len( ) , 3 ) ;
5829
+ for tx in node_txn. drain ( ..) {
5830
+ if let Some ( previous_data) = penalties_3. get ( & tx. input [ 0 ] . previous_output ) {
5831
+ let txid = tx. txid ( ) ;
5832
+ // Verify new bumped tx is different from any last claiming treansaction, we don't want spurrious rebroadcast
5833
+ assert_ne ! ( previous_data. 0 , txid) ;
5834
+ check_spends ! ( tx, revoked_local_txn[ 0 ] . clone( ) ) ;
5835
+ let fee_4 = revoked_local_txn[ 0 ] . output [ tx. input [ 0 ] . previous_output . vout as usize ] . value - tx. output [ 0 ] . value ;
5836
+ feerate_4 = fee_4 * 1000 / tx. get_weight ( ) ;
5837
+ // Verify HighPriority spike bump heuristic;
5838
+ assert ! ( feerate_4 > feerate_3) ;
5839
+ penalties_4. insert ( tx. input [ 0 ] . previous_output , ( txid, fee_4) ) ;
5840
+ }
5841
+ }
5842
+ }
5843
+ assert_ne ! ( feerate_4, 0 ) ;
5844
+ update_fee_estimator ! ( nodes[ 1 ] , 500 , 100 ) ;
5845
+ check_added_monitors ! ( nodes[ 1 ] , 1 ) ;
5846
+
5847
+ connect_blocks ( & nodes[ 1 ] . chain_monitor , 3 , 10 , true , header. bitcoin_hash ( ) ) ;
5848
+ let mut feerate_5 = 0 ;
5849
+ {
5850
+ let mut node_txn = nodes[ 1 ] . tx_broadcaster . txn_broadcasted . lock ( ) . unwrap ( ) ;
5851
+ assert_eq ! ( node_txn. len( ) , 3 ) ;
5852
+ for tx in node_txn. drain ( ..) {
5853
+ if let Some ( previous_data) = penalties_4. get ( & tx. input [ 0 ] . previous_output ) {
5854
+ let txid = tx. txid ( ) ;
5855
+ // Verify new bumped tx is different from any last claiming treansaction, we don't want spurrious rebroadcast
5856
+ assert_ne ! ( previous_data. 0 , txid) ;
5857
+ check_spends ! ( tx, revoked_local_txn[ 0 ] . clone( ) ) ;
5858
+ let fee_5 = revoked_local_txn[ 0 ] . output [ tx. input [ 0 ] . previous_output . vout as usize ] . value - tx. output [ 0 ] . value ;
5859
+ feerate_5 = fee_5 * 1000 / tx. get_weight ( ) ;
5860
+ // Verify min_relay_fee threshold force bump
5861
+ assert ! ( feerate_5 * 100 >= feerate_4 * 119 ) ;
5862
+ }
5863
+ }
5864
+ }
5865
+ assert_ne ! ( feerate_5, 0 ) ;
5866
+ nodes[ 0 ] . node . get_and_clear_pending_msg_events ( ) ;
5867
+ nodes[ 1 ] . node . get_and_clear_pending_msg_events ( ) ;
5868
+ nodes[ 0 ] . node . get_and_clear_pending_events ( ) ;
5869
+ nodes[ 1 ] . node . get_and_clear_pending_events ( ) ;
5870
+ }
5871
+
5872
+ #[ test]
5873
+ fn test_bump_penalty_txn_on_revoked_htlc ( ) {
5874
+ // In case of penalty txn with too low feerates for getting into mempools, RBF-bump them to be sure
5875
+ // we're able to claim outputs on revoked HTLC-Success/Timeout txn before timelock expiration
5876
+
5877
+ let nodes = create_network ( 2 ) ;
5878
+
5879
+ let chan = create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 1000000 , 59000000 ) ;
5880
+ let payment_preimage = route_payment ( & nodes[ 0 ] , & vec ! ( & nodes[ 1 ] ) [ ..] , 3000000 ) . 0 ;
5881
+ route_payment ( & nodes[ 1 ] , & vec ! ( & nodes[ 0 ] ) [ ..] , 3000000 ) . 0 ;
5882
+ let revoked_local_txn;
5883
+ {
5884
+ revoked_local_txn = nodes[ 1 ] . node . channel_state . lock ( ) . unwrap ( ) . by_id . get ( & chan. 2 ) . unwrap ( ) . last_local_commitment_txn . clone ( ) ;
5885
+ assert_eq ! ( revoked_local_txn[ 0 ] . output. len( ) , 4 ) ;
5886
+ assert_eq ! ( revoked_local_txn[ 0 ] . input. len( ) , 1 ) ;
5887
+ assert_eq ! ( revoked_local_txn[ 0 ] . input[ 0 ] . previous_output. txid, chan. 3 . txid( ) ) ;
5888
+ }
5889
+
5890
+ claim_payment ( & nodes[ 0 ] , & vec ! ( & nodes[ 1 ] ) [ ..] , payment_preimage) ;
5891
+ let header = BlockHeader { version : 0x20000000 , prev_blockhash : Default :: default ( ) , merkle_root : Default :: default ( ) , time : 42 , bits : 42 , nonce : 42 } ;
5892
+ nodes[ 1 ] . chain_monitor . block_connected_with_filtering ( & Block { header, txdata : vec ! [ revoked_local_txn[ 0 ] . clone( ) ] } , 1 ) ;
5893
+ let mut htlc_timeout = Transaction { version : 0 , lock_time : 0 , input : Vec :: new ( ) , output : Vec :: new ( ) } ;
5894
+ let mut htlc_success = Transaction { version : 0 , lock_time : 0 , input : Vec :: new ( ) , output : Vec :: new ( ) } ;
5895
+ {
5896
+ let mut revoked_htlc_txn = nodes[ 1 ] . tx_broadcaster . txn_broadcasted . lock ( ) . unwrap ( ) ;
5897
+ assert_eq ! ( revoked_htlc_txn. len( ) , 4 ) ;
5898
+ if revoked_htlc_txn[ 0 ] . input [ 0 ] . witness . last ( ) . unwrap ( ) . len ( ) == ACCEPTED_HTLC_SCRIPT_WEIGHT {
5899
+ assert_eq ! ( revoked_htlc_txn[ 1 ] . input[ 0 ] . witness. last( ) . unwrap( ) . len( ) , OFFERED_HTLC_SCRIPT_WEIGHT ) ;
5900
+ htlc_success = revoked_htlc_txn[ 0 ] . clone ( ) ;
5901
+ htlc_timeout = revoked_htlc_txn[ 1 ] . clone ( ) ;
5902
+ } else if revoked_htlc_txn[ 0 ] . input [ 0 ] . witness . last ( ) . unwrap ( ) . len ( ) == OFFERED_HTLC_SCRIPT_WEIGHT {
5903
+ assert_eq ! ( revoked_htlc_txn[ 1 ] . input[ 0 ] . witness. last( ) . unwrap( ) . len( ) , ACCEPTED_HTLC_SCRIPT_WEIGHT ) ;
5904
+ htlc_timeout = revoked_htlc_txn[ 0 ] . clone ( ) ;
5905
+ htlc_success = revoked_htlc_txn[ 1 ] . clone ( ) ;
5906
+ }
5907
+ assert_eq ! ( revoked_htlc_txn[ 0 ] , revoked_htlc_txn[ 2 ] ) ;
5908
+ assert_eq ! ( revoked_htlc_txn[ 1 ] , revoked_htlc_txn[ 3 ] ) ;
5909
+ revoked_htlc_txn. clear ( ) ;
5910
+ }
5911
+ nodes[ 0 ] . chain_monitor . block_connected_with_filtering ( & Block { header, txdata : vec ! [ revoked_local_txn[ 0 ] . clone( ) ] } , 1 ) ;
5912
+ {
5913
+ let mut node_txn = nodes[ 0 ] . tx_broadcaster . txn_broadcasted . lock ( ) . unwrap ( ) ;
5914
+ node_txn. clear ( ) ;
5915
+ }
5916
+ let header = BlockHeader { version : 0x20000000 , prev_blockhash : header. bitcoin_hash ( ) , merkle_root : Default :: default ( ) , time : 42 , bits : 42 , nonce : 42 } ;
5917
+ nodes[ 0 ] . chain_monitor . block_connected_with_filtering ( & Block { header, txdata : vec ! [ htlc_success. clone( ) , htlc_timeout. clone( ) ] } , 2 ) ;
5918
+ let mut penalties = HashMap :: new ( ) ;
5919
+ {
5920
+ let mut penalty_txn = nodes[ 0 ] . tx_broadcaster . txn_broadcasted . lock ( ) . unwrap ( ) ;
5921
+ assert_eq ! ( penalty_txn. len( ) , 2 ) ;
5922
+ if penalty_txn[ 0 ] . input [ 0 ] . previous_output . txid == htlc_timeout. txid ( ) {
5923
+ check_spends ! ( penalty_txn[ 0 ] , htlc_timeout. clone( ) ) ;
5924
+ penalties. insert ( penalty_txn[ 0 ] . input [ 0 ] . previous_output , ( penalty_txn[ 0 ] . txid ( ) , htlc_timeout. output [ 0 ] . value - penalty_txn[ 0 ] . output [ 0 ] . value ) ) ;
5925
+ check_spends ! ( penalty_txn[ 1 ] , htlc_success. clone( ) ) ;
5926
+ penalties. insert ( penalty_txn[ 1 ] . input [ 0 ] . previous_output , ( penalty_txn[ 1 ] . txid ( ) , htlc_success. output [ 0 ] . value - penalty_txn[ 1 ] . output [ 0 ] . value ) ) ;
5927
+ } else {
5928
+ check_spends ! ( penalty_txn[ 1 ] , htlc_timeout. clone( ) ) ;
5929
+ penalties. insert ( penalty_txn[ 1 ] . input [ 0 ] . previous_output , ( penalty_txn[ 0 ] . txid ( ) , htlc_timeout. output [ 0 ] . value - penalty_txn[ 1 ] . output [ 0 ] . value ) ) ;
5930
+ check_spends ! ( penalty_txn[ 0 ] , htlc_success. clone( ) ) ;
5931
+ penalties. insert ( penalty_txn[ 0 ] . input [ 0 ] . previous_output , ( penalty_txn[ 1 ] . txid ( ) , htlc_success. output [ 0 ] . value - penalty_txn[ 0 ] . output [ 0 ] . value ) ) ;
5932
+ }
5933
+ penalty_txn. clear ( ) ;
5934
+ }
5935
+ let header = BlockHeader { version : 0x20000000 , prev_blockhash : header. bitcoin_hash ( ) , merkle_root : Default :: default ( ) , time : 42 , bits : 42 , nonce : 42 } ;
5936
+ let header = connect_blocks ( & nodes[ 0 ] . chain_monitor , 2 , 2 , true , header. bitcoin_hash ( ) ) ;
5937
+ {
5938
+ let mut node_txn = nodes[ 0 ] . tx_broadcaster . txn_broadcasted . lock ( ) . unwrap ( ) ;
5939
+ node_txn. clear ( ) ;
5940
+ }
5941
+ connect_blocks ( & nodes[ 0 ] . chain_monitor , 1 , 4 , true , header. bitcoin_hash ( ) ) ;
5942
+ {
5943
+ let penalty_txn = nodes[ 0 ] . tx_broadcaster . txn_broadcasted . lock ( ) . unwrap ( ) ;
5944
+ for tx in penalty_txn. iter ( ) {
5945
+ if let Some ( previous_data) = penalties. get ( & tx. input [ 0 ] . previous_output ) {
5946
+ let txid = tx. txid ( ) ;
5947
+ assert_ne ! ( previous_data. 0 , txid) ;
5948
+ // Verify that fee has been increased
5949
+ if tx. input [ 0 ] . previous_output . txid == htlc_timeout. txid ( ) {
5950
+ check_spends ! ( tx, htlc_timeout. clone( ) ) ;
5951
+ let fee = htlc_timeout. output [ 0 ] . value - tx. output [ 0 ] . value ;
5952
+ assert ! ( fee > previous_data. 1 ) ;
5953
+ } else {
5954
+ check_spends ! ( tx, htlc_success. clone( ) ) ;
5955
+ let fee = htlc_timeout. output [ 0 ] . value - tx. output [ 0 ] . value ;
5956
+ assert ! ( fee > previous_data. 1 ) ;
5957
+ }
5958
+ }
5959
+ }
5960
+ }
5961
+ }
0 commit comments