Skip to content

Commit bddf559

Browse files
committed
Add test for aggregated revoked HTLC claim on anchors channel
1 parent 0d1fb02 commit bddf559

File tree

1 file changed

+303
-2
lines changed

1 file changed

+303
-2
lines changed

lightning/src/ln/monitor_tests.rs

Lines changed: 303 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,34 @@ use crate::chain::chaininterface::LowerBoundedFeeEstimator;
1919
use crate::ln::channel;
2020
#[cfg(anchors)]
2121
use crate::ln::chan_utils;
22+
#[cfg(anchors)]
23+
use crate::ln::channelmanager::ChannelManager;
2224
use crate::ln::channelmanager::{BREAKDOWN_TIMEOUT, PaymentId};
2325
use crate::ln::msgs::ChannelMessageHandler;
2426
#[cfg(anchors)]
2527
use crate::util::config::UserConfig;
2628
#[cfg(anchors)]
29+
use crate::util::crypto::sign;
30+
#[cfg(anchors)]
2731
use crate::util::events::BumpTransactionEvent;
2832
use crate::util::events::{Event, MessageSendEvent, MessageSendEventsProvider, ClosureReason, HTLCDestination};
33+
#[cfg(anchors)]
34+
use crate::util::ser::Writeable;
35+
#[cfg(anchors)]
36+
use crate::util::test_utils;
2937

38+
#[cfg(anchors)]
39+
use bitcoin::blockdata::transaction::EcdsaSighashType;
3040
use bitcoin::blockdata::script::Builder;
3141
use bitcoin::blockdata::opcodes;
3242
use bitcoin::secp256k1::Secp256k1;
3343
#[cfg(anchors)]
34-
use bitcoin::{Amount, Script, TxIn, TxOut, PackedLockTime};
44+
use bitcoin::secp256k1::SecretKey;
45+
#[cfg(anchors)]
46+
use bitcoin::{Amount, PublicKey, Script, TxIn, TxOut, PackedLockTime, Witness};
3547
use bitcoin::Transaction;
48+
#[cfg(anchors)]
49+
use bitcoin::util::sighash::SighashCache;
3650

3751
use crate::prelude::*;
3852

@@ -1749,7 +1763,7 @@ fn test_yield_anchors_events() {
17491763

17501764
let mut holder_events = nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events();
17511765
// Certain block `ConnectStyle`s cause an extra `ChannelClose` event to be emitted since the
1752-
// best block is being updated prior to the confirmed transactions.
1766+
// best block is updated before the confirmed transactions are notified.
17531767
match *nodes[0].connect_style.borrow() {
17541768
ConnectStyle::BestBlockFirst|ConnectStyle::BestBlockFirstReorgsOnlyTip|ConnectStyle::BestBlockFirstSkippingBlocks => {
17551769
assert_eq!(holder_events.len(), 3);
@@ -1816,3 +1830,290 @@ fn test_yield_anchors_events() {
18161830
// Clear the remaining events as they're not relevant to what we're testing.
18171831
nodes[0].node.get_and_clear_pending_events();
18181832
}
1833+
1834+
#[cfg(anchors)]
1835+
#[test]
1836+
fn test_anchors_aggregated_revoked_htlc_tx() {
1837+
// Test that `ChannelMonitor`s can properly detect and claim funds from a counterparty claiming
1838+
// multiple HTLCs in a single transaction via the success path from a revoked commitment.
1839+
let secp = Secp256k1::new();
1840+
let mut chanmon_cfgs = create_chanmon_cfgs(2);
1841+
// Required to sign a revoked commitment transaction
1842+
chanmon_cfgs[1].keys_manager.disable_revocation_policy_check = true;
1843+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
1844+
let mut anchors_config = UserConfig::default();
1845+
anchors_config.channel_handshake_config.announced_channel = true;
1846+
anchors_config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true;
1847+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(anchors_config), Some(anchors_config)]);
1848+
1849+
let bob_persister: test_utils::TestPersister;
1850+
let bob_chain_monitor: test_utils::TestChainMonitor;
1851+
let bob_deserialized: ChannelManager<
1852+
&test_utils::TestChainMonitor, &test_utils::TestBroadcaster, &test_utils::TestKeysInterface,
1853+
&test_utils::TestKeysInterface, &test_utils::TestKeysInterface, &test_utils::TestFeeEstimator,
1854+
&test_utils::TestRouter, &test_utils::TestLogger,
1855+
>;
1856+
1857+
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
1858+
1859+
let chan_id = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000).2;
1860+
1861+
// Route two payments from Alice to Bob to lock in the HTLCs.
1862+
let (payment_preimage_a, payment_hash_a, _) = route_payment(&nodes[0], &[&nodes[1]], 100_000_000);
1863+
let (payment_preimage_b, payment_hash_b, _) = route_payment(&nodes[0], &[&nodes[1]], 100_000_000);
1864+
1865+
// Serialize Bob with the HTLCs locked in. We'll restart Bob later on with the state at this
1866+
// point such that he broadcasts a revoked commitment transaction.
1867+
let bob_serialized = nodes[1].node.encode();
1868+
let bob_serialized_monitor = get_monitor!(nodes[1], chan_id).encode();
1869+
1870+
// Bob claims both HTLCs...
1871+
claim_payment(&nodes[0], &[&nodes[1]], payment_preimage_a);
1872+
claim_payment(&nodes[0], &[&nodes[1]], payment_preimage_b);
1873+
1874+
// ...and sends one back such that he has a motive to broadcast his revoked state.
1875+
send_payment(&nodes[1], &[&nodes[0]], 30_000_000);
1876+
1877+
// Restart Bob with the revoked state and provide the HTLC preimages he claimed.
1878+
reload_node!(nodes[1], anchors_config, bob_serialized, &[&bob_serialized_monitor], bob_persister, bob_chain_monitor, bob_deserialized);
1879+
get_monitor!(nodes[1], chan_id).provide_payment_preimage(
1880+
&payment_hash_a, &payment_preimage_a, &node_cfgs[1].tx_broadcaster,
1881+
&LowerBoundedFeeEstimator::new(node_cfgs[1].fee_estimator), &nodes[1].logger
1882+
);
1883+
get_monitor!(nodes[1], chan_id).provide_payment_preimage(
1884+
&payment_hash_b, &payment_preimage_b, &node_cfgs[1].tx_broadcaster,
1885+
&LowerBoundedFeeEstimator::new(node_cfgs[1].fee_estimator), &nodes[1].logger
1886+
);
1887+
1888+
// Bob force closes by broadcasting his revoked state.
1889+
nodes[1].node.force_close_broadcasting_latest_txn(&chan_id, &nodes[0].node.get_our_node_id()).unwrap();
1890+
{
1891+
let mut txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
1892+
assert_eq!(txn.len(), 1);
1893+
let revoked_commitment = txn.pop().unwrap();
1894+
assert_eq!(revoked_commitment.output.len(), 6); // 2 HTLC outputs + 1 to_self output + 1 to_remote output + 2 anchor outputs
1895+
}
1896+
1897+
// Bob should now receive an event to bump his revoked commitment transaction's fee.
1898+
assert!(nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events().is_empty());
1899+
let events = nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events();
1900+
assert_eq!(events.len(), 1);
1901+
let (revoked_commitment, anchor_tx) = match &events[0] {
1902+
Event::BumpTransaction(BumpTransactionEvent::ChannelClose { commitment_tx, anchor_descriptor, .. }) => {
1903+
let secret_key = SecretKey::from_slice(&[1; 32]).unwrap();
1904+
let public_key = PublicKey::new(secret_key.public_key(&secp));
1905+
let fee_utxo_script = Script::new_v0_p2wpkh(&public_key.wpubkey_hash().unwrap());
1906+
let coinbase_tx = Transaction {
1907+
version: 2,
1908+
lock_time: PackedLockTime::ZERO,
1909+
input: vec![TxIn { ..Default::default() }],
1910+
output: vec![TxOut { // UTXO to attach fees to `anchor_tx`
1911+
value: Amount::ONE_BTC.to_sat(),
1912+
script_pubkey: fee_utxo_script.clone(),
1913+
}],
1914+
};
1915+
let mut anchor_tx = Transaction {
1916+
version: 2,
1917+
lock_time: PackedLockTime::ZERO,
1918+
input: vec![
1919+
TxIn { // Anchor input
1920+
previous_output: anchor_descriptor.outpoint,
1921+
..Default::default()
1922+
},
1923+
TxIn { // Fee input
1924+
previous_output: bitcoin::OutPoint { txid: coinbase_tx.txid(), vout: 0 },
1925+
..Default::default()
1926+
},
1927+
],
1928+
output: vec![TxOut { // Fee input change
1929+
value: coinbase_tx.output[0].value / 2 ,
1930+
script_pubkey: Script::new_op_return(&[]),
1931+
}],
1932+
};
1933+
let signer = nodes[1].keys_manager.derive_channel_keys(
1934+
anchor_descriptor.channel_value_satoshis, &anchor_descriptor.channel_keys_id,
1935+
);
1936+
let funding_sig = signer.sign_holder_anchor_input(&mut anchor_tx, 0, &secp).unwrap();
1937+
anchor_tx.input[0].witness = chan_utils::build_anchor_input_witness(
1938+
&signer.pubkeys().funding_pubkey, &funding_sig
1939+
);
1940+
anchor_tx.input[0].witness = chan_utils::build_anchor_input_witness(
1941+
&signer.pubkeys().funding_pubkey, &funding_sig
1942+
);
1943+
let fee_utxo_sig = {
1944+
let witness_script = Script::new_p2pkh(&public_key.pubkey_hash());
1945+
let sighash = hash_to_message!(&SighashCache::new(&anchor_tx).segwit_signature_hash(
1946+
1, &witness_script, coinbase_tx.output[0].value, EcdsaSighashType::All
1947+
).unwrap()[..]);
1948+
let sig = sign(&secp, &sighash, &secret_key);
1949+
let mut sig = sig.serialize_der().to_vec();
1950+
sig.push(EcdsaSighashType::All as u8);
1951+
sig
1952+
};
1953+
anchor_tx.input[1].witness = Witness::from_vec(vec![fee_utxo_sig, public_key.to_bytes()]);
1954+
check_spends!(anchor_tx, commitment_tx, coinbase_tx);
1955+
(commitment_tx, anchor_tx)
1956+
},
1957+
_ => panic!("Unexpected event"),
1958+
};
1959+
1960+
for node in &nodes {
1961+
mine_transactions(node, &[&revoked_commitment, &anchor_tx]);
1962+
check_added_monitors!(node, 1);
1963+
check_closed_broadcast!(node, true);
1964+
}
1965+
check_closed_event!(&nodes[0], 1, ClosureReason::CommitmentTxConfirmed);
1966+
check_closed_event!(&nodes[1], 1, ClosureReason::HolderForceClosed);
1967+
1968+
// Alice should detect the confirmed revoked commitment, and attempts to claim all of the
1969+
// revoked outputs.
1970+
{
1971+
let mut txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
1972+
assert_eq!(txn.len(), 1);
1973+
let tx = txn.pop().unwrap();
1974+
// TODO: to_self claim must be separate from HTLC claims
1975+
assert_eq!(tx.input.len(), 3); // Spends both HTLC outputs and to_self output
1976+
assert_eq!(tx.output.len(), 1);
1977+
check_spends!(tx, revoked_commitment);
1978+
}
1979+
1980+
// Since Bob was able to confirm his revoked commitment, he'll now try to claim the HTLCs
1981+
// through the success path.
1982+
assert!(nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events().is_empty());
1983+
let mut events = nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events();
1984+
// Certain block `ConnectStyle`s cause an extra `ChannelClose` event to be emitted since the
1985+
// best block is updated before the confirmed transactions are notified.
1986+
match *nodes[1].connect_style.borrow() {
1987+
ConnectStyle::BestBlockFirst|ConnectStyle::BestBlockFirstReorgsOnlyTip|ConnectStyle::BestBlockFirstSkippingBlocks => {
1988+
assert_eq!(events.len(), 2);
1989+
if let Event::BumpTransaction(BumpTransactionEvent::ChannelClose { .. }) = events.remove(0) {}
1990+
else { panic!("unexpected event"); }
1991+
1992+
},
1993+
_ => assert_eq!(events.len(), 1),
1994+
};
1995+
let htlc_tx = {
1996+
let secret_key = SecretKey::from_slice(&[1; 32]).unwrap();
1997+
let public_key = PublicKey::new(secret_key.public_key(&secp));
1998+
let fee_utxo_script = Script::new_v0_p2wpkh(&public_key.wpubkey_hash().unwrap());
1999+
let coinbase_tx = Transaction {
2000+
version: 2,
2001+
lock_time: PackedLockTime::ZERO,
2002+
input: vec![TxIn { ..Default::default() }],
2003+
output: vec![TxOut { // UTXO to attach fees to `htlc_tx`
2004+
value: Amount::ONE_BTC.to_sat(),
2005+
script_pubkey: fee_utxo_script.clone(),
2006+
}],
2007+
};
2008+
let mut htlc_tx = Transaction {
2009+
version: 2,
2010+
lock_time: PackedLockTime::ZERO,
2011+
input: vec![TxIn { // Fee input
2012+
previous_output: bitcoin::OutPoint { txid: coinbase_tx.txid(), vout: 0 },
2013+
..Default::default()
2014+
}],
2015+
output: vec![TxOut { // Fee input change
2016+
value: coinbase_tx.output[0].value / 2 ,
2017+
script_pubkey: Script::new_op_return(&[]),
2018+
}],
2019+
};
2020+
if let Event::BumpTransaction(BumpTransactionEvent::HTLCResolution { htlc_descriptors, .. }) = &events[0] {
2021+
assert_eq!(htlc_descriptors.len(), 2);
2022+
for htlc_descriptor in htlc_descriptors {
2023+
assert!(!htlc_descriptor.htlc.offered);
2024+
let signer = nodes[1].keys_manager.derive_channel_keys(
2025+
htlc_descriptor.channel_value_satoshis, &htlc_descriptor.channel_keys_id
2026+
);
2027+
let per_commitment_point = signer.get_per_commitment_point(htlc_descriptor.per_commitment_number, &secp);
2028+
htlc_tx.input.push(htlc_descriptor.unsigned_tx_input());
2029+
htlc_tx.output.push(htlc_descriptor.tx_output(&per_commitment_point, &secp));
2030+
}
2031+
for (idx, htlc_descriptor) in htlc_descriptors.iter().enumerate() {
2032+
let htlc_input_idx = idx + 1;
2033+
let signer = nodes[1].keys_manager.derive_channel_keys(
2034+
htlc_descriptor.channel_value_satoshis, &htlc_descriptor.channel_keys_id
2035+
);
2036+
let our_sig = signer.sign_holder_htlc_transaction(&htlc_tx, htlc_input_idx, htlc_descriptor, &secp).unwrap();
2037+
let per_commitment_point = signer.get_per_commitment_point(htlc_descriptor.per_commitment_number, &secp);
2038+
let witness_script = htlc_descriptor.witness_script(&per_commitment_point, &secp);
2039+
htlc_tx.input[htlc_input_idx].witness = htlc_descriptor.tx_input_witness(&our_sig, &witness_script);
2040+
}
2041+
} else {
2042+
panic!("Unexpected event");
2043+
}
2044+
let fee_utxo_sig = {
2045+
let witness_script = Script::new_p2pkh(&public_key.pubkey_hash());
2046+
let sighash = hash_to_message!(&SighashCache::new(&htlc_tx).segwit_signature_hash(
2047+
0, &witness_script, coinbase_tx.output[0].value, EcdsaSighashType::All
2048+
).unwrap()[..]);
2049+
let sig = sign(&secp, &sighash, &secret_key);
2050+
let mut sig = sig.serialize_der().to_vec();
2051+
sig.push(EcdsaSighashType::All as u8);
2052+
sig
2053+
};
2054+
htlc_tx.input[0].witness = Witness::from_vec(vec![fee_utxo_sig, public_key.to_bytes()]);
2055+
check_spends!(htlc_tx, coinbase_tx, revoked_commitment);
2056+
htlc_tx
2057+
};
2058+
2059+
for node in &nodes {
2060+
mine_transaction(node, &htlc_tx);
2061+
}
2062+
2063+
// Alice should see that Bob is trying to claim to HTLCs, so she should now try to claim them at
2064+
// the second level instead.
2065+
let (revoked_to_self_claim, revoked_htlc_claim) = {
2066+
let mut txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
2067+
assert_eq!(txn.len(), 2);
2068+
2069+
let revoked_to_self_claim = txn.pop().unwrap();
2070+
assert_eq!(revoked_to_self_claim.input.len(), 1);
2071+
assert_eq!(revoked_to_self_claim.output.len(), 1);
2072+
check_spends!(revoked_to_self_claim, revoked_commitment);
2073+
2074+
let revoked_htlc_claim = txn.pop().unwrap();
2075+
assert_eq!(revoked_htlc_claim.input.len(), 2);
2076+
assert_eq!(revoked_htlc_claim.output.len(), 1);
2077+
check_spends!(revoked_htlc_claim, htlc_tx);
2078+
2079+
(revoked_to_self_claim, revoked_htlc_claim)
2080+
};
2081+
for node in &nodes {
2082+
mine_transactions(node, &[&revoked_to_self_claim, &revoked_htlc_claim]);
2083+
}
2084+
2085+
2086+
// Connect one block to make sure the HTLC events are not yielded while ANTI_REORG_DELAY has not
2087+
// been reached.
2088+
connect_blocks(&nodes[0], 1);
2089+
connect_blocks(&nodes[1], 1);
2090+
2091+
assert!(nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events().is_empty());
2092+
assert!(nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events().is_empty());
2093+
2094+
// Connect the remaining blocks to reach ANTI_REORG_DELAY.
2095+
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 2);
2096+
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 2);
2097+
2098+
assert!(nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events().is_empty());
2099+
let spendable_output_events = nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events();
2100+
for (idx, event) in spendable_output_events.iter().enumerate() {
2101+
if let Event::SpendableOutputs { outputs } = event {
2102+
assert_eq!(outputs.len(), 1);
2103+
let spend_tx = nodes[0].keys_manager.backing.spend_spendable_outputs(
2104+
&[&outputs[0]], Vec::new(), Script::new_op_return(&[]), 253, &Secp256k1::new(),
2105+
).unwrap();
2106+
check_spends!(spend_tx, if idx == 0 { &revoked_to_self_claim } else { &revoked_htlc_claim });
2107+
} else {
2108+
panic!("unexpected event");
2109+
}
2110+
}
2111+
2112+
assert!(nodes[0].node.list_channels().is_empty());
2113+
assert!(nodes[1].node.list_channels().is_empty());
2114+
assert!(nodes[0].chain_monitor.chain_monitor.get_claimable_balances(&[]).is_empty());
2115+
// TODO: From Bob's PoV, he still thinks he can claim the outputs from his revoked commitment.
2116+
// This needs to be fixed before we enable pruning `ChannelMonitor`s once they don't have any
2117+
// balances to claim.
2118+
assert_eq!(nodes[1].chain_monitor.chain_monitor.get_claimable_balances(&[]).len(), 3);
2119+
}

0 commit comments

Comments
 (0)