Skip to content

Commit 9911a29

Browse files
committed
Replace quiescence fuzz coverage with splicing
Splicing should also have fuzz coverage, and it depends on the quiescence protocol.
1 parent 323b03f commit 9911a29

File tree

1 file changed

+285
-13
lines changed

1 file changed

+285
-13
lines changed

fuzz/src/chanmon_consistency.rs

Lines changed: 285 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ use lightning::ln::channelmanager::{
5050
RecipientOnionFields,
5151
};
5252
use lightning::ln::functional_test_utils::*;
53+
use lightning::ln::funding::{FundingTxInput, SpliceContribution};
5354
use lightning::ln::inbound_payment::ExpandedKey;
5455
use lightning::ln::msgs::{
5556
BaseMessageHandler, ChannelMessageHandler, CommitmentUpdate, Init, MessageSendEvent,
@@ -68,6 +69,7 @@ use lightning::sign::{
6869
};
6970
use lightning::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};
7071
use lightning::util::config::UserConfig;
72+
use lightning::util::errors::APIError;
7173
use lightning::util::hash_tables::*;
7274
use lightning::util::logger::Logger;
7375
use lightning::util::ser::{LengthReadable, ReadableArgs, Writeable, Writer};
@@ -163,6 +165,63 @@ impl Writer for VecWriter {
163165
}
164166
}
165167

168+
pub struct TestWallet {
169+
secret_key: SecretKey,
170+
utxos: Mutex<Vec<lightning::events::bump_transaction::Utxo>>,
171+
secp: Secp256k1<bitcoin::secp256k1::All>,
172+
}
173+
174+
impl TestWallet {
175+
pub fn new(secret_key: SecretKey) -> Self {
176+
Self { secret_key, utxos: Mutex::new(Vec::new()), secp: Secp256k1::new() }
177+
}
178+
179+
fn get_change_script(&self) -> Result<ScriptBuf, ()> {
180+
let public_key = bitcoin::PublicKey::new(self.secret_key.public_key(&self.secp));
181+
Ok(ScriptBuf::new_p2wpkh(&public_key.wpubkey_hash().unwrap()))
182+
}
183+
184+
pub fn add_utxo(&self, outpoint: bitcoin::OutPoint, value: Amount) -> TxOut {
185+
let public_key = bitcoin::PublicKey::new(self.secret_key.public_key(&self.secp));
186+
let utxo = lightning::events::bump_transaction::Utxo::new_v0_p2wpkh(
187+
outpoint,
188+
value,
189+
&public_key.wpubkey_hash().unwrap(),
190+
);
191+
self.utxos.lock().unwrap().push(utxo.clone());
192+
utxo.output
193+
}
194+
195+
pub fn sign_tx(
196+
&self, mut tx: Transaction,
197+
) -> Result<Transaction, bitcoin::sighash::P2wpkhError> {
198+
let utxos = self.utxos.lock().unwrap();
199+
for i in 0..tx.input.len() {
200+
if let Some(utxo) =
201+
utxos.iter().find(|utxo| utxo.outpoint == tx.input[i].previous_output)
202+
{
203+
let sighash = bitcoin::sighash::SighashCache::new(&tx).p2wpkh_signature_hash(
204+
i,
205+
&utxo.output.script_pubkey,
206+
utxo.output.value,
207+
bitcoin::EcdsaSighashType::All,
208+
)?;
209+
let signature = self.secp.sign_ecdsa(
210+
&secp256k1::Message::from_digest(sighash.to_byte_array()),
211+
&self.secret_key,
212+
);
213+
let bitcoin_sig = bitcoin::ecdsa::Signature {
214+
signature,
215+
sighash_type: bitcoin::EcdsaSighashType::All,
216+
};
217+
tx.input[i].witness =
218+
bitcoin::Witness::p2wpkh(&bitcoin_sig, &self.secret_key.public_key(&self.secp));
219+
}
220+
}
221+
Ok(tx)
222+
}
223+
}
224+
166225
/// The LDK API requires that any time we tell it we're done persisting a `ChannelMonitor[Update]`
167226
/// we never pass it in as the "latest" `ChannelMonitor` on startup. However, we can pass
168227
/// out-of-date monitors as long as we never told LDK we finished persisting them, which we do by
@@ -671,6 +730,7 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
671730
let mut config = UserConfig::default();
672731
config.channel_config.forwarding_fee_proportional_millionths = 0;
673732
config.channel_handshake_config.announce_for_forwarding = true;
733+
config.reject_inbound_splices = false;
674734
if anchors {
675735
config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true;
676736
config.manually_accept_inbound_channels = true;
@@ -724,6 +784,7 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
724784
let mut config = UserConfig::default();
725785
config.channel_config.forwarding_fee_proportional_millionths = 0;
726786
config.channel_handshake_config.announce_for_forwarding = true;
787+
config.reject_inbound_splices = false;
727788
if anchors {
728789
config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true;
729790
config.manually_accept_inbound_channels = true;
@@ -984,6 +1045,30 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
9841045
}};
9851046
}
9861047

1048+
let wallet_a = TestWallet::new(SecretKey::from_slice(&[1; 32]).unwrap());
1049+
let wallet_b = TestWallet::new(SecretKey::from_slice(&[2; 32]).unwrap());
1050+
let wallet_c = TestWallet::new(SecretKey::from_slice(&[3; 32]).unwrap());
1051+
let wallets = vec![wallet_a, wallet_b, wallet_c];
1052+
let coinbase_tx = bitcoin::Transaction {
1053+
version: bitcoin::transaction::Version::TWO,
1054+
lock_time: bitcoin::absolute::LockTime::ZERO,
1055+
input: vec![bitcoin::TxIn { ..Default::default() }],
1056+
output: wallets
1057+
.iter()
1058+
.map(|w| TxOut {
1059+
value: Amount::from_sat(100_000),
1060+
script_pubkey: w.get_change_script().unwrap(),
1061+
})
1062+
.collect(),
1063+
};
1064+
let coinbase_txid = coinbase_tx.compute_txid();
1065+
wallets.iter().enumerate().for_each(|(i, w)| {
1066+
w.add_utxo(
1067+
bitcoin::OutPoint { txid: coinbase_txid, vout: i as u32 },
1068+
Amount::from_sat(100_000),
1069+
);
1070+
});
1071+
9871072
let fee_est_a = Arc::new(FuzzEstimator { ret_val: atomic::AtomicU32::new(253) });
9881073
let mut last_htlc_clear_fee_a = 253;
9891074
let fee_est_b = Arc::new(FuzzEstimator { ret_val: atomic::AtomicU32::new(253) });
@@ -1073,6 +1158,34 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
10731158
if Some(*node_id) == expect_drop_id { panic!("peer_disconnected should drop msgs bound for the disconnected peer"); }
10741159
*node_id == a_id
10751160
},
1161+
MessageSendEvent::SendSpliceInit { ref node_id, .. } => {
1162+
if Some(*node_id) == expect_drop_id { panic!("peer_disconnected should drop msgs bound for the disconnected peer"); }
1163+
*node_id == a_id
1164+
},
1165+
MessageSendEvent::SendSpliceAck { ref node_id, .. } => {
1166+
if Some(*node_id) == expect_drop_id { panic!("peer_disconnected should drop msgs bound for the disconnected peer"); }
1167+
*node_id == a_id
1168+
},
1169+
MessageSendEvent::SendSpliceLocked { ref node_id, .. } => {
1170+
if Some(*node_id) == expect_drop_id { panic!("peer_disconnected should drop msgs bound for the disconnected peer"); }
1171+
*node_id == a_id
1172+
},
1173+
MessageSendEvent::SendTxAddInput { ref node_id, .. } => {
1174+
if Some(*node_id) == expect_drop_id { panic!("peer_disconnected should drop msgs bound for the disconnected peer"); }
1175+
*node_id == a_id
1176+
},
1177+
MessageSendEvent::SendTxAddOutput { ref node_id, .. } => {
1178+
if Some(*node_id) == expect_drop_id { panic!("peer_disconnected should drop msgs bound for the disconnected peer"); }
1179+
*node_id == a_id
1180+
},
1181+
MessageSendEvent::SendTxComplete { ref node_id, .. } => {
1182+
if Some(*node_id) == expect_drop_id { panic!("peer_disconnected should drop msgs bound for the disconnected peer"); }
1183+
*node_id == a_id
1184+
},
1185+
MessageSendEvent::SendTxAbort { ref node_id, .. } => {
1186+
if Some(*node_id) == expect_drop_id { panic!("peer_disconnected should drop msgs bound for the disconnected peer"); }
1187+
*node_id == a_id
1188+
},
10761189
MessageSendEvent::SendChannelReady { .. } => continue,
10771190
MessageSendEvent::SendAnnouncementSignatures { .. } => continue,
10781191
MessageSendEvent::SendChannelUpdate { ref node_id, ref msg } => {
@@ -1208,7 +1321,79 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
12081321
dest.handle_stfu(nodes[$node].get_our_node_id(), msg);
12091322
}
12101323
}
1211-
}
1324+
},
1325+
MessageSendEvent::SendTxAddInput { ref node_id, ref msg } => {
1326+
for (idx, dest) in nodes.iter().enumerate() {
1327+
if dest.get_our_node_id() == *node_id {
1328+
out.locked_write(format!("Delivering tx_add_input from node {} to node {}.\n", $node, idx).as_bytes());
1329+
dest.handle_tx_add_input(nodes[$node].get_our_node_id(), msg);
1330+
}
1331+
}
1332+
},
1333+
MessageSendEvent::SendTxAddOutput { ref node_id, ref msg } => {
1334+
for (idx, dest) in nodes.iter().enumerate() {
1335+
if dest.get_our_node_id() == *node_id {
1336+
out.locked_write(format!("Delivering tx_add_output from node {} to node {}.\n", $node, idx).as_bytes());
1337+
dest.handle_tx_add_output(nodes[$node].get_our_node_id(), msg);
1338+
}
1339+
}
1340+
},
1341+
MessageSendEvent::SendTxRemoveInput { ref node_id, ref msg } => {
1342+
for (idx, dest) in nodes.iter().enumerate() {
1343+
if dest.get_our_node_id() == *node_id {
1344+
out.locked_write(format!("Delivering tx_remove_input from node {} to node {}.\n", $node, idx).as_bytes());
1345+
dest.handle_tx_remove_input(nodes[$node].get_our_node_id(), msg);
1346+
}
1347+
}
1348+
},
1349+
MessageSendEvent::SendTxRemoveOutput { ref node_id, ref msg } => {
1350+
for (idx, dest) in nodes.iter().enumerate() {
1351+
if dest.get_our_node_id() == *node_id {
1352+
out.locked_write(format!("Delivering tx_remove_output from node {} to node {}.\n", $node, idx).as_bytes());
1353+
dest.handle_tx_remove_output(nodes[$node].get_our_node_id(), msg);
1354+
}
1355+
}
1356+
},
1357+
MessageSendEvent::SendTxComplete { ref node_id, ref msg } => {
1358+
for (idx, dest) in nodes.iter().enumerate() {
1359+
if dest.get_our_node_id() == *node_id {
1360+
out.locked_write(format!("Delivering tx_complete from node {} to node {}.\n", $node, idx).as_bytes());
1361+
dest.handle_tx_complete(nodes[$node].get_our_node_id(), msg);
1362+
}
1363+
}
1364+
},
1365+
MessageSendEvent::SendTxAbort { ref node_id, ref msg } => {
1366+
for (idx, dest) in nodes.iter().enumerate() {
1367+
if dest.get_our_node_id() == *node_id {
1368+
out.locked_write(format!("Delivering tx_abort from node {} to node {}.\n", $node, idx).as_bytes());
1369+
dest.handle_tx_abort(nodes[$node].get_our_node_id(), msg);
1370+
}
1371+
}
1372+
},
1373+
MessageSendEvent::SendSpliceInit { ref node_id, ref msg } => {
1374+
for (idx, dest) in nodes.iter().enumerate() {
1375+
if dest.get_our_node_id() == *node_id {
1376+
out.locked_write(format!("Delivering splice_init from node {} to node {}.\n", $node, idx).as_bytes());
1377+
dest.handle_splice_init(nodes[$node].get_our_node_id(), msg);
1378+
}
1379+
}
1380+
},
1381+
MessageSendEvent::SendSpliceAck { ref node_id, ref msg } => {
1382+
for (idx, dest) in nodes.iter().enumerate() {
1383+
if dest.get_our_node_id() == *node_id {
1384+
out.locked_write(format!("Delivering splice_ack from node {} to node {}.\n", $node, idx).as_bytes());
1385+
dest.handle_splice_ack(nodes[$node].get_our_node_id(), msg);
1386+
}
1387+
}
1388+
},
1389+
MessageSendEvent::SendSpliceLocked { ref node_id, ref msg } => {
1390+
for (idx, dest) in nodes.iter().enumerate() {
1391+
if dest.get_our_node_id() == *node_id {
1392+
out.locked_write(format!("Delivering splice_locked from node {} to node {}.\n", $node, idx).as_bytes());
1393+
dest.handle_splice_locked(nodes[$node].get_our_node_id(), msg);
1394+
}
1395+
}
1396+
},
12121397
MessageSendEvent::SendChannelReady { .. } => {
12131398
// Can be generated as a reestablish response
12141399
},
@@ -1347,6 +1532,25 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
13471532
events::Event::PaymentForwarded { .. } if $node == 1 => {},
13481533
events::Event::ChannelReady { .. } => {},
13491534
events::Event::HTLCHandlingFailed { .. } => {},
1535+
1536+
events::Event::FundingTransactionReadyForSigning {
1537+
channel_id,
1538+
counterparty_node_id,
1539+
unsigned_transaction,
1540+
..
1541+
} => {
1542+
let signed_tx = wallets[$node].sign_tx(unsigned_transaction).unwrap();
1543+
nodes[$node]
1544+
.funding_transaction_signed(
1545+
&channel_id,
1546+
&counterparty_node_id,
1547+
signed_tx,
1548+
)
1549+
.unwrap();
1550+
},
1551+
events::Event::SplicePending { .. } => {},
1552+
events::Event::SpliceFailed { .. } => {},
1553+
13501554
_ => {
13511555
if out.may_fail.load(atomic::Ordering::Acquire) {
13521556
return;
@@ -1652,16 +1856,92 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
16521856
},
16531857

16541858
0xa0 => {
1655-
nodes[0].maybe_propose_quiescence(&nodes[1].get_our_node_id(), &chan_a_id).unwrap()
1859+
let input = FundingTxInput::new_p2wpkh(coinbase_tx.clone(), 0).unwrap();
1860+
let contribution = SpliceContribution::SpliceIn {
1861+
value: Amount::from_sat(10_000),
1862+
inputs: vec![input],
1863+
change_script: None,
1864+
};
1865+
let funding_feerate_sat_per_kw = fee_est_a.ret_val.load(atomic::Ordering::Acquire);
1866+
if let Err(e) = nodes[0].splice_channel(
1867+
&chan_a_id,
1868+
&nodes[1].get_our_node_id(),
1869+
contribution,
1870+
funding_feerate_sat_per_kw,
1871+
None,
1872+
) {
1873+
assert!(
1874+
matches!(e, APIError::APIMisuseError { ref err } if err.contains("splice pending")),
1875+
"{:?}",
1876+
e
1877+
);
1878+
}
16561879
},
16571880
0xa1 => {
1658-
nodes[1].maybe_propose_quiescence(&nodes[0].get_our_node_id(), &chan_a_id).unwrap()
1881+
let input = FundingTxInput::new_p2wpkh(coinbase_tx.clone(), 1).unwrap();
1882+
let contribution = SpliceContribution::SpliceIn {
1883+
value: Amount::from_sat(10_000),
1884+
inputs: vec![input],
1885+
change_script: None,
1886+
};
1887+
let funding_feerate_sat_per_kw = fee_est_b.ret_val.load(atomic::Ordering::Acquire);
1888+
if let Err(e) = nodes[1].splice_channel(
1889+
&chan_a_id,
1890+
&nodes[0].get_our_node_id(),
1891+
contribution,
1892+
funding_feerate_sat_per_kw,
1893+
None,
1894+
) {
1895+
assert!(
1896+
matches!(e, APIError::APIMisuseError { ref err } if err.contains("splice pending")),
1897+
"{:?}",
1898+
e
1899+
);
1900+
}
16591901
},
16601902
0xa2 => {
1661-
nodes[1].maybe_propose_quiescence(&nodes[2].get_our_node_id(), &chan_b_id).unwrap()
1903+
let input = FundingTxInput::new_p2wpkh(coinbase_tx.clone(), 0).unwrap();
1904+
let contribution = SpliceContribution::SpliceIn {
1905+
value: Amount::from_sat(10_000),
1906+
inputs: vec![input],
1907+
change_script: None,
1908+
};
1909+
let funding_feerate_sat_per_kw = fee_est_b.ret_val.load(atomic::Ordering::Acquire);
1910+
if let Err(e) = nodes[1].splice_channel(
1911+
&chan_b_id,
1912+
&nodes[2].get_our_node_id(),
1913+
contribution,
1914+
funding_feerate_sat_per_kw,
1915+
None,
1916+
) {
1917+
assert!(
1918+
matches!(e, APIError::APIMisuseError { ref err } if err.contains("splice pending")),
1919+
"{:?}",
1920+
e
1921+
);
1922+
}
16621923
},
16631924
0xa3 => {
1664-
nodes[2].maybe_propose_quiescence(&nodes[1].get_our_node_id(), &chan_b_id).unwrap()
1925+
let input = FundingTxInput::new_p2wpkh(coinbase_tx.clone(), 1).unwrap();
1926+
let contribution = SpliceContribution::SpliceIn {
1927+
value: Amount::from_sat(10_000),
1928+
inputs: vec![input],
1929+
change_script: None,
1930+
};
1931+
let funding_feerate_sat_per_kw = fee_est_c.ret_val.load(atomic::Ordering::Acquire);
1932+
if let Err(e) = nodes[2].splice_channel(
1933+
&chan_b_id,
1934+
&nodes[1].get_our_node_id(),
1935+
contribution,
1936+
funding_feerate_sat_per_kw,
1937+
None,
1938+
) {
1939+
assert!(
1940+
matches!(e, APIError::APIMisuseError { ref err } if err.contains("splice pending")),
1941+
"{:?}",
1942+
e
1943+
);
1944+
}
16651945
},
16661946

16671947
0xb0 | 0xb1 | 0xb2 => {
@@ -1832,14 +2112,6 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
18322112
// complete the quiescence handshake.
18332113
process_all_events!();
18342114

1835-
// Then exit quiescence and process all messages again, to resolve any pending
1836-
// HTLCs (only irrevocably committed ones) before attempting to send more payments.
1837-
nodes[0].exit_quiescence(&nodes[1].get_our_node_id(), &chan_a_id).unwrap();
1838-
nodes[1].exit_quiescence(&nodes[0].get_our_node_id(), &chan_a_id).unwrap();
1839-
nodes[1].exit_quiescence(&nodes[2].get_our_node_id(), &chan_b_id).unwrap();
1840-
nodes[2].exit_quiescence(&nodes[1].get_our_node_id(), &chan_b_id).unwrap();
1841-
process_all_events!();
1842-
18432115
// Finally, make sure that at least one end of each channel can make a substantial payment
18442116
assert!(
18452117
send_payment(&nodes[0], &nodes[1], chan_a, 10_000_000, &mut p_id, &mut p_idx)

0 commit comments

Comments
 (0)