@@ -1646,6 +1646,7 @@ fn test_revoked_counterparty_htlc_tx_balances() {
1646
1646
do_test_revoked_counterparty_htlc_tx_balances ( true ) ;
1647
1647
}
1648
1648
1649
+ fn do_test_revoked_counterparty_aggregated_claims ( anchors : bool ) {
1649
1650
// Tests `get_claimable_balances` for revoked counterparty commitment transactions when
1650
1651
// claiming with an aggregated claim transaction.
1651
1652
let mut chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
@@ -1655,9 +1656,25 @@ fn test_revoked_counterparty_htlc_tx_balances() {
1655
1656
// transaction which, from the point of view of our keys_manager, is revoked.
1656
1657
chanmon_cfgs[ 1 ] . keys_manager . disable_revocation_policy_check = true ;
1657
1658
let node_cfgs = create_node_cfgs ( 2 , & chanmon_cfgs) ;
1658
- let node_chanmgrs = create_node_chanmgrs ( 2 , & node_cfgs, & [ None , None ] ) ;
1659
+ let mut user_config = test_default_channel_config ( ) ;
1660
+ if anchors {
1661
+ user_config. channel_handshake_config . negotiate_anchors_zero_fee_htlc_tx = true ;
1662
+ user_config. manually_accept_inbound_channels = true ;
1663
+ }
1664
+ let node_chanmgrs = create_node_chanmgrs ( 2 , & node_cfgs, & [ Some ( user_config) , Some ( user_config) ] ) ;
1659
1665
let nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
1660
1666
1667
+ let coinbase_tx = Transaction {
1668
+ version : 2 ,
1669
+ lock_time : PackedLockTime :: ZERO ,
1670
+ input : vec ! [ TxIn { ..Default :: default ( ) } ] ,
1671
+ output : vec ! [ TxOut {
1672
+ value: Amount :: ONE_BTC . to_sat( ) ,
1673
+ script_pubkey: nodes[ 0 ] . wallet_source. get_change_script( ) . unwrap( ) ,
1674
+ } ] ,
1675
+ } ;
1676
+ nodes[ 0 ] . wallet_source . add_utxo ( bitcoin:: OutPoint { txid : coinbase_tx. txid ( ) , vout : 0 } , coinbase_tx. output [ 0 ] . value ) ;
1677
+
1661
1678
let ( _, _, chan_id, funding_tx) =
1662
1679
create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 1_000_000 , 100_000_000 ) ;
1663
1680
let funding_outpoint = OutPoint { txid : funding_tx. txid ( ) , index : 0 } ;
@@ -1680,9 +1697,11 @@ fn test_revoked_counterparty_htlc_tx_balances() {
1680
1697
// Now get the latest commitment transaction from A and then update the fee to revoke it
1681
1698
let as_revoked_txn = get_local_commitment_txn ! ( nodes[ 0 ] , chan_id) ;
1682
1699
1683
- assert_eq ! ( as_revoked_txn. len( ) , 2 ) ;
1700
+ assert_eq ! ( as_revoked_txn. len( ) , if anchors { 1 } else { 2 } ) ;
1684
1701
check_spends ! ( as_revoked_txn[ 0 ] , funding_tx) ;
1685
- check_spends ! ( as_revoked_txn[ 1 ] , as_revoked_txn[ 0 ] ) ; // The HTLC-Claim transaction
1702
+ if !anchors {
1703
+ check_spends ! ( as_revoked_txn[ 1 ] , as_revoked_txn[ 0 ] ) ; // The HTLC-Claim transaction
1704
+ }
1686
1705
1687
1706
let channel_type_features = get_channel_type_features ! ( nodes[ 0 ] , nodes[ 1 ] , chan_id) ;
1688
1707
let chan_feerate = get_feerate ! ( nodes[ 0 ] , nodes[ 1 ] , chan_id) as u64 ;
@@ -1721,51 +1740,93 @@ fn test_revoked_counterparty_htlc_tx_balances() {
1721
1740
check_closed_event ! ( nodes[ 1 ] , 1 , ClosureReason :: CommitmentTxConfirmed , [ nodes[ 0 ] . node. get_our_node_id( ) ] , 1000000 ) ;
1722
1741
check_added_monitors ! ( nodes[ 1 ] , 1 ) ;
1723
1742
1724
- 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 ( ) ;
1725
- // Currently the revoked commitment outputs are all claimed in one aggregated transaction
1726
- assert_eq ! ( claim_txn. len( ) , 1 ) ;
1727
- assert_eq ! ( claim_txn[ 0 ] . input. len( ) , 3 ) ;
1728
- check_spends ! ( claim_txn[ 0 ] , as_revoked_txn[ 0 ] ) ;
1743
+ let mut claim_txn = nodes[ 1 ] . tx_broadcaster . txn_broadcast ( ) ;
1744
+ assert_eq ! ( claim_txn. len( ) , if anchors { 2 } else { 1 } ) ;
1745
+ let revoked_to_self_claim = if anchors {
1746
+ assert_eq ! ( claim_txn[ 0 ] . input. len( ) , 1 ) ;
1747
+ assert_eq ! ( claim_txn[ 0 ] . input[ 0 ] . previous_output. vout, 5 ) ; // Separate to_remote claim
1748
+ check_spends ! ( claim_txn[ 0 ] , as_revoked_txn[ 0 ] ) ;
1749
+ assert_eq ! ( claim_txn[ 1 ] . input. len( ) , 2 ) ;
1750
+ assert_eq ! ( claim_txn[ 1 ] . input[ 0 ] . previous_output. vout, 2 ) ;
1751
+ assert_eq ! ( claim_txn[ 1 ] . input[ 1 ] . previous_output. vout, 3 ) ;
1752
+ check_spends ! ( claim_txn[ 1 ] , as_revoked_txn[ 0 ] ) ;
1753
+ Some ( claim_txn. remove ( 0 ) )
1754
+ } else {
1755
+ assert_eq ! ( claim_txn[ 0 ] . input. len( ) , 3 ) ;
1756
+ assert_eq ! ( claim_txn[ 0 ] . input[ 0 ] . previous_output. vout, 3 ) ;
1757
+ assert_eq ! ( claim_txn[ 0 ] . input[ 1 ] . previous_output. vout, 0 ) ;
1758
+ assert_eq ! ( claim_txn[ 0 ] . input[ 2 ] . previous_output. vout, 1 ) ;
1759
+ check_spends ! ( claim_txn[ 0 ] , as_revoked_txn[ 0 ] ) ;
1760
+ None
1761
+ } ;
1729
1762
1730
1763
let to_remote_maturity = nodes[ 1 ] . best_block_info ( ) . 1 + ANTI_REORG_DELAY - 1 ;
1731
1764
1765
+ let commitment_tx_fee = chan_feerate *
1766
+ ( channel:: commitment_tx_base_weight ( & channel_type_features) + 2 * channel:: COMMITMENT_TX_WEIGHT_PER_HTLC ) / 1000 ;
1767
+ let anchor_outputs_value = if anchors { channel:: ANCHOR_OUTPUT_VALUE_SATOSHI * 2 } else { 0 } ;
1732
1768
assert_eq ! ( sorted_vec( vec![ Balance :: ClaimableAwaitingConfirmations {
1733
1769
// to_remote output in A's revoked commitment
1734
1770
amount_satoshis: 100_000 - 4_000 - 3_000 ,
1735
1771
confirmation_height: to_remote_maturity,
1736
1772
} , Balance :: CounterpartyRevokedOutputClaimable {
1737
1773
// to_self output in A's revoked commitment
1738
- amount_satoshis: 1_000_000 - 100_000 - chan_feerate *
1739
- ( channel:: commitment_tx_base_weight( & channel_type_features) + 2 * channel:: COMMITMENT_TX_WEIGHT_PER_HTLC ) / 1000 ,
1774
+ amount_satoshis: 1_000_000 - 100_000 - commitment_tx_fee - anchor_outputs_value,
1740
1775
} , Balance :: CounterpartyRevokedOutputClaimable { // HTLC 1
1741
1776
amount_satoshis: 4_000 ,
1742
1777
} , Balance :: CounterpartyRevokedOutputClaimable { // HTLC 2
1743
1778
amount_satoshis: 3_000 ,
1744
1779
} ] ) ,
1745
1780
sorted_vec( nodes[ 1 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) ) ) ;
1746
1781
1747
- // Confirm A's HTLC-Success tranasction which presumably raced B's claim, causing B to create a
1782
+ // Confirm A's HTLC-Success transaction which presumably raced B's claim, causing B to create a
1748
1783
// new claim.
1749
- mine_transaction ( & nodes[ 1 ] , & as_revoked_txn[ 1 ] ) ;
1784
+ if anchors {
1785
+ mine_transaction ( & nodes[ 0 ] , & as_revoked_txn[ 0 ] ) ;
1786
+ check_closed_broadcast ( & nodes[ 0 ] , 1 , true ) ;
1787
+ check_added_monitors ( & nodes[ 0 ] , 1 ) ;
1788
+ check_closed_event ! ( & nodes[ 0 ] , 1 , ClosureReason :: CommitmentTxConfirmed , false , [ nodes[ 1 ] . node. get_our_node_id( ) ] , 1_000_000 ) ;
1789
+ handle_bump_htlc_event ( & nodes[ 0 ] , 1 ) ;
1790
+ }
1791
+ let htlc_success_claim = if anchors {
1792
+ let mut txn = nodes[ 0 ] . tx_broadcaster . txn_broadcast ( ) ;
1793
+ assert_eq ! ( txn. len( ) , 1 ) ;
1794
+ check_spends ! ( txn[ 0 ] , as_revoked_txn[ 0 ] , coinbase_tx) ;
1795
+ txn. pop ( ) . unwrap ( )
1796
+ } else {
1797
+ as_revoked_txn[ 1 ] . clone ( )
1798
+ } ;
1799
+ mine_transaction ( & nodes[ 1 ] , & htlc_success_claim) ;
1750
1800
expect_payment_sent ( & nodes[ 1 ] , claimed_payment_preimage, None , true , false ) ;
1751
- let mut claim_txn_2 : Vec < _ > = nodes [ 1 ] . tx_broadcaster . txn_broadcasted . lock ( ) . unwrap ( ) . clone ( ) ;
1752
- 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 } ) ;
1801
+
1802
+ let mut claim_txn_2 = nodes [ 1 ] . tx_broadcaster . txn_broadcast ( ) ;
1753
1803
// Once B sees the HTLC-Success transaction it splits its claim transaction into two, though in
1754
1804
// theory it could re-aggregate the claims as well.
1755
1805
assert_eq ! ( claim_txn_2. len( ) , 2 ) ;
1756
- assert_eq ! ( claim_txn_2[ 0 ] . input. len( ) , 2 ) ;
1757
- check_spends ! ( claim_txn_2[ 0 ] , as_revoked_txn[ 0 ] ) ;
1758
- assert_eq ! ( claim_txn_2[ 1 ] . input. len( ) , 1 ) ;
1759
- check_spends ! ( claim_txn_2[ 1 ] , as_revoked_txn[ 1 ] ) ;
1806
+ if anchors {
1807
+ assert_eq ! ( claim_txn_2[ 0 ] . input. len( ) , 1 ) ;
1808
+ assert_eq ! ( claim_txn_2[ 0 ] . input[ 0 ] . previous_output. vout, 0 ) ;
1809
+ check_spends ! ( claim_txn_2[ 0 ] , & htlc_success_claim) ;
1810
+ assert_eq ! ( claim_txn_2[ 1 ] . input. len( ) , 1 ) ;
1811
+ assert_eq ! ( claim_txn_2[ 1 ] . input[ 0 ] . previous_output. vout, 3 ) ;
1812
+ check_spends ! ( claim_txn_2[ 1 ] , as_revoked_txn[ 0 ] ) ;
1813
+ } else {
1814
+ assert_eq ! ( claim_txn_2[ 0 ] . input. len( ) , 1 ) ;
1815
+ assert_eq ! ( claim_txn_2[ 0 ] . input[ 0 ] . previous_output. vout, 0 ) ;
1816
+ check_spends ! ( claim_txn_2[ 0 ] , as_revoked_txn[ 1 ] ) ;
1817
+ assert_eq ! ( claim_txn_2[ 1 ] . input. len( ) , 2 ) ;
1818
+ assert_eq ! ( claim_txn_2[ 1 ] . input[ 0 ] . previous_output. vout, 3 ) ;
1819
+ assert_eq ! ( claim_txn_2[ 1 ] . input[ 1 ] . previous_output. vout, 1 ) ;
1820
+ check_spends ! ( claim_txn_2[ 1 ] , as_revoked_txn[ 0 ] ) ;
1821
+ }
1760
1822
1761
1823
assert_eq ! ( sorted_vec( vec![ Balance :: ClaimableAwaitingConfirmations {
1762
1824
// to_remote output in A's revoked commitment
1763
1825
amount_satoshis: 100_000 - 4_000 - 3_000 ,
1764
1826
confirmation_height: to_remote_maturity,
1765
1827
} , Balance :: CounterpartyRevokedOutputClaimable {
1766
1828
// to_self output in A's revoked commitment
1767
- amount_satoshis: 1_000_000 - 100_000 - chan_feerate *
1768
- ( channel:: commitment_tx_base_weight( & channel_type_features) + 2 * channel:: COMMITMENT_TX_WEIGHT_PER_HTLC ) / 1000 ,
1829
+ amount_satoshis: 1_000_000 - 100_000 - commitment_tx_fee - anchor_outputs_value,
1769
1830
} , Balance :: CounterpartyRevokedOutputClaimable { // HTLC 1
1770
1831
amount_satoshis: 4_000 ,
1771
1832
} , Balance :: CounterpartyRevokedOutputClaimable { // HTLC 2
@@ -1781,8 +1842,7 @@ fn test_revoked_counterparty_htlc_tx_balances() {
1781
1842
1782
1843
assert_eq ! ( sorted_vec( vec![ Balance :: CounterpartyRevokedOutputClaimable {
1783
1844
// to_self output in A's revoked commitment
1784
- amount_satoshis: 1_000_000 - 100_000 - chan_feerate *
1785
- ( channel:: commitment_tx_base_weight( & channel_type_features) + 2 * channel:: COMMITMENT_TX_WEIGHT_PER_HTLC ) / 1000 ,
1845
+ amount_satoshis: 1_000_000 - 100_000 - commitment_tx_fee - anchor_outputs_value,
1786
1846
} , Balance :: CounterpartyRevokedOutputClaimable { // HTLC 1
1787
1847
amount_satoshis: 4_000 ,
1788
1848
} , Balance :: CounterpartyRevokedOutputClaimable { // HTLC 2
@@ -1793,47 +1853,75 @@ fn test_revoked_counterparty_htlc_tx_balances() {
1793
1853
} ] ) ,
1794
1854
sorted_vec( nodes[ 1 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) ) ) ;
1795
1855
1796
- mine_transaction ( & nodes[ 1 ] , & claim_txn_2[ 1 ] ) ;
1856
+ mine_transaction ( & nodes[ 1 ] , & claim_txn_2[ 0 ] ) ;
1797
1857
let htlc_2_claim_maturity = nodes[ 1 ] . best_block_info ( ) . 1 + ANTI_REORG_DELAY - 1 ;
1798
1858
1799
1859
assert_eq ! ( sorted_vec( vec![ Balance :: CounterpartyRevokedOutputClaimable {
1800
1860
// to_self output in A's revoked commitment
1801
- amount_satoshis: 1_000_000 - 100_000 - chan_feerate *
1802
- ( channel:: commitment_tx_base_weight( & channel_type_features) + 2 * channel:: COMMITMENT_TX_WEIGHT_PER_HTLC ) / 1000 ,
1861
+ amount_satoshis: 1_000_000 - 100_000 - commitment_tx_fee - anchor_outputs_value,
1803
1862
} , Balance :: CounterpartyRevokedOutputClaimable { // HTLC 1
1804
1863
amount_satoshis: 4_000 ,
1805
1864
} , Balance :: ClaimableAwaitingConfirmations { // HTLC 2
1806
- amount_satoshis: claim_txn_2[ 1 ] . output[ 0 ] . value,
1865
+ amount_satoshis: claim_txn_2[ 0 ] . output[ 0 ] . value,
1807
1866
confirmation_height: htlc_2_claim_maturity,
1808
1867
} ] ) ,
1809
1868
sorted_vec( nodes[ 1 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) ) ) ;
1810
1869
1811
1870
connect_blocks ( & nodes[ 1 ] , 5 ) ;
1812
- test_spendable_output ( & nodes[ 1 ] , & claim_txn_2[ 1 ] , false ) ;
1871
+ test_spendable_output ( & nodes[ 1 ] , & claim_txn_2[ 0 ] , false ) ;
1813
1872
1814
1873
assert_eq ! ( sorted_vec( vec![ Balance :: CounterpartyRevokedOutputClaimable {
1815
1874
// to_self output in A's revoked commitment
1816
- amount_satoshis: 1_000_000 - 100_000 - chan_feerate *
1817
- ( channel:: commitment_tx_base_weight( & channel_type_features) + 2 * channel:: COMMITMENT_TX_WEIGHT_PER_HTLC ) / 1000 ,
1875
+ amount_satoshis: 1_000_000 - 100_000 - commitment_tx_fee - anchor_outputs_value,
1818
1876
} , Balance :: CounterpartyRevokedOutputClaimable { // HTLC 1
1819
1877
amount_satoshis: 4_000 ,
1820
1878
} ] ) ,
1821
1879
sorted_vec( nodes[ 1 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) ) ) ;
1822
1880
1823
- mine_transaction ( & nodes[ 1 ] , & claim_txn_2[ 0 ] ) ;
1881
+ if anchors {
1882
+ mine_transactions ( & nodes[ 1 ] , & [ & claim_txn_2[ 1 ] , revoked_to_self_claim. as_ref ( ) . unwrap ( ) ] ) ;
1883
+ } else {
1884
+ mine_transaction ( & nodes[ 1 ] , & claim_txn_2[ 1 ] ) ;
1885
+ }
1824
1886
let rest_claim_maturity = nodes[ 1 ] . best_block_info ( ) . 1 + ANTI_REORG_DELAY - 1 ;
1825
1887
1826
- assert_eq ! ( vec![ Balance :: ClaimableAwaitingConfirmations {
1827
- amount_satoshis: claim_txn_2[ 0 ] . output[ 0 ] . value,
1828
- confirmation_height: rest_claim_maturity,
1829
- } ] ,
1830
- nodes[ 1 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) ) ;
1888
+ if anchors {
1889
+ assert_eq ! ( vec![ Balance :: ClaimableAwaitingConfirmations {
1890
+ amount_satoshis: claim_txn_2[ 1 ] . output[ 0 ] . value,
1891
+ confirmation_height: rest_claim_maturity,
1892
+ } , Balance :: ClaimableAwaitingConfirmations {
1893
+ amount_satoshis: revoked_to_self_claim. as_ref( ) . unwrap( ) . output[ 0 ] . value,
1894
+ confirmation_height: rest_claim_maturity,
1895
+ } ] ,
1896
+ nodes[ 1 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) ) ;
1897
+ } else {
1898
+ assert_eq ! ( vec![ Balance :: ClaimableAwaitingConfirmations {
1899
+ amount_satoshis: claim_txn_2[ 1 ] . output[ 0 ] . value,
1900
+ confirmation_height: rest_claim_maturity,
1901
+ } ] ,
1902
+ nodes[ 1 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) ) ;
1903
+ }
1831
1904
1832
1905
assert ! ( nodes[ 1 ] . node. get_and_clear_pending_events( ) . is_empty( ) ) ; // We shouldn't fail the payment until we spend the output
1833
1906
1834
1907
connect_blocks ( & nodes[ 1 ] , 5 ) ;
1835
1908
expect_payment_failed ! ( nodes[ 1 ] , revoked_payment_hash, false ) ;
1836
- test_spendable_output ( & nodes[ 1 ] , & claim_txn_2[ 0 ] , false ) ;
1909
+ if anchors {
1910
+ let events = nodes[ 1 ] . chain_monitor . chain_monitor . get_and_clear_pending_events ( ) ;
1911
+ assert_eq ! ( events. len( ) , 2 ) ;
1912
+ for ( i, event) in events. into_iter ( ) . enumerate ( ) {
1913
+ if let Event :: SpendableOutputs { outputs, .. } = event {
1914
+ assert_eq ! ( outputs. len( ) , 1 ) ;
1915
+ let spend_tx = nodes[ 1 ] . keys_manager . backing . spend_spendable_outputs (
1916
+ & [ & outputs[ 0 ] ] , Vec :: new ( ) , Builder :: new ( ) . push_opcode ( opcodes:: all:: OP_RETURN ) . into_script ( ) ,
1917
+ 253 , None , & Secp256k1 :: new ( )
1918
+ ) . unwrap ( ) ;
1919
+ check_spends ! ( spend_tx, if i == 0 { & claim_txn_2[ 1 ] } else { revoked_to_self_claim. as_ref( ) . unwrap( ) } ) ;
1920
+ } else { panic ! ( ) ; }
1921
+ }
1922
+ } else {
1923
+ test_spendable_output ( & nodes[ 1 ] , & claim_txn_2[ 1 ] , false ) ;
1924
+ }
1837
1925
assert ! ( nodes[ 1 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) . is_empty( ) ) ;
1838
1926
1839
1927
// Ensure that even if we connect more blocks, potentially replaying the entire chain if we're
@@ -1845,6 +1933,12 @@ fn test_revoked_counterparty_htlc_tx_balances() {
1845
1933
assert ! ( nodes[ 1 ] . chain_monitor. chain_monitor. get_monitor( funding_outpoint) . unwrap( ) . get_claimable_balances( ) . is_empty( ) ) ;
1846
1934
}
1847
1935
1936
+ #[ test]
1937
+ fn test_revoked_counterparty_aggregated_claims ( ) {
1938
+ do_test_revoked_counterparty_aggregated_claims ( false ) ;
1939
+ do_test_revoked_counterparty_aggregated_claims ( true ) ;
1940
+ }
1941
+
1848
1942
fn do_test_restored_packages_retry ( check_old_monitor_retries_after_upgrade : bool ) {
1849
1943
// Tests that we'll retry packages that were previously timelocked after we've restored them.
1850
1944
let chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
0 commit comments