Skip to content

Commit e3dc3f6

Browse files
Aditya SharmaAditya Sharma
Aditya Sharma
authored and
Aditya Sharma
committed
functional_tests: Add test_peer_storage to confirm if we recover from peer storage and sweep funds correctly.
1 parent bd9d4cc commit e3dc3f6

File tree

3 files changed

+175
-3
lines changed

3 files changed

+175
-3
lines changed

lightning/src/ln/functional_test_utils.rs

+23
Original file line numberDiff line numberDiff line change
@@ -1011,6 +1011,29 @@ macro_rules! get_channel_type_features {
10111011
}
10121012
}
10131013

1014+
/// Returns a Stub Channel Monitor given a channel Id, making some naive assumptions
1015+
#[macro_export]
1016+
macro_rules! get_stub {
1017+
($node: expr, $channel_id: expr) => {
1018+
{
1019+
use bitcoin::hashes::Hash;
1020+
let mut stub = None;
1021+
// Assume funding vout is either 0 or 1 blindly
1022+
for index in 0..2 {
1023+
if let Ok(mon) = $node.chain_monitor.chain_monitor.get_stub_monitor(
1024+
$crate::chain::transaction::OutPoint{
1025+
txid: bitcoin::Txid::from_slice(&$channel_id.0[..]).unwrap(), index
1026+
})
1027+
{
1028+
stub = Some(mon);
1029+
break;
1030+
}
1031+
}
1032+
stub.unwrap()
1033+
}
1034+
}
1035+
}
1036+
10141037
/// Returns a channel monitor given a channel id, making some naive assumptions
10151038
#[macro_export]
10161039
macro_rules! get_monitor {

lightning/src/ln/functional_tests.rs

+148-3
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,20 @@
1414
use crate::chain;
1515
use crate::chain::{ChannelMonitorUpdateStatus, Confirm, Listen, Watch};
1616
use crate::chain::chaininterface::LowerBoundedFeeEstimator;
17-
use crate::chain::channelmonitor;
17+
use crate::chain::chainmonitor::Persist;
18+
use crate::chain::{channelmonitor, BestBlock};
1819
use crate::chain::channelmonitor::{CLOSED_CHANNEL_UPDATE_ID, CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ANTI_REORG_DELAY};
1920
use crate::chain::transaction::OutPoint;
20-
use crate::sign::{ecdsa::EcdsaChannelSigner, EntropySource, OutputSpender, SignerProvider};
21+
use crate::sign::{ecdsa::EcdsaChannelSigner, EntropySource, OutputSpender, SignerProvider, SpendableOutputDescriptor};
2122
use crate::events::{Event, FundingInfo, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentPurpose, ClosureReason, HTLCDestination, PaymentFailureReason};
2223
use crate::ln::types::{ChannelId, PaymentPreimage, PaymentSecret, PaymentHash};
2324
use crate::ln::channel::{CONCURRENT_INBOUND_HTLC_FEE_BUFFER, FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE, MIN_AFFORDABLE_HTLC_COUNT, get_holder_selected_channel_reserve_satoshis, OutboundV1Channel, InboundV1Channel, COINBASE_MATURITY, ChannelPhase};
24-
use crate::ln::channelmanager::{self, PaymentId, RAACommitmentOrder, PaymentSendFailure, RecipientOnionFields, BREAKDOWN_TIMEOUT, ENABLE_GOSSIP_TICKS, DISABLE_GOSSIP_TICKS, MIN_CLTV_EXPIRY_DELTA};
25+
26+
use crate::ln::channelmanager::{self, ChainParameters, PaymentId, RAACommitmentOrder, PaymentSendFailure, RecipientOnionFields, BREAKDOWN_TIMEOUT, ENABLE_GOSSIP_TICKS, DISABLE_GOSSIP_TICKS, MIN_CLTV_EXPIRY_DELTA};
2527
use crate::ln::channel::{DISCONNECT_PEER_AWAITING_RESPONSE_TICKS, ChannelError};
2628
use crate::ln::{chan_utils, onion_utils};
2729
use crate::ln::chan_utils::{commitment_tx_base_weight, COMMITMENT_TX_WEIGHT_PER_HTLC, OFFERED_HTLC_SCRIPT_WEIGHT, htlc_success_tx_weight, htlc_timeout_tx_weight, HTLCOutputInCommitment};
30+
use crate::ln::fundrecoverer::FundRecoverer;
2831
use crate::routing::gossip::{NetworkGraph, NetworkUpdate};
2932
use crate::routing::router::{Path, PaymentParameters, Route, RouteHop, get_route, RouteParameters};
3033
use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, NodeFeatures};
@@ -174,6 +177,148 @@ fn test_funding_exceeds_no_wumbo_limit() {
174177
}
175178
}
176179

180+
#[test]
181+
fn test_peer_storage() {
182+
let chanmon_cfgs = create_chanmon_cfgs(2);
183+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
184+
let (persister, chain_monitor);
185+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
186+
let nodes_0_deserialized;
187+
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
188+
let nodes_0_serialized = nodes[0].node.encode();
189+
190+
let (_a, _b, channel_id, funding_tx) = create_announced_chan_between_nodes(&nodes, 0, 1);
191+
192+
send_payment(&nodes[0], &vec!(&nodes[1])[..], 1000);
193+
send_payment(&nodes[0], &vec!(&nodes[1])[..], 10000);
194+
send_payment(&nodes[0], &vec!(&nodes[1])[..], 9999);
195+
196+
nodes[0].node.peer_disconnected(nodes[1].node.get_our_node_id());
197+
nodes[1].node.peer_disconnected(nodes[0].node.get_our_node_id());
198+
199+
// Reconnect peers
200+
nodes[0].node.peer_connected(nodes[1].node.get_our_node_id(), &msgs::Init {
201+
features: nodes[1].node.init_features(), networks: None, remote_network_address: None
202+
}, true).unwrap();
203+
let reestablish_1 = get_chan_reestablish_msgs!(nodes[0], nodes[1]);
204+
assert_eq!(reestablish_1.len(), 1);
205+
nodes[1].node.peer_connected(nodes[0].node.get_our_node_id(), &msgs::Init {
206+
features: nodes[0].node.init_features(), networks: None, remote_network_address: None
207+
}, false).unwrap();
208+
let reestablish_2 = get_chan_reestablish_msgs!(nodes[1], nodes[0]);
209+
assert_eq!(reestablish_2.len(), 1);
210+
211+
nodes[0].node.handle_channel_reestablish(nodes[1].node.get_our_node_id(), &reestablish_2[0]);
212+
handle_chan_reestablish_msgs!(nodes[0], nodes[1]);
213+
nodes[1].node.handle_channel_reestablish(nodes[0].node.get_our_node_id(), &reestablish_1[0]);
214+
handle_chan_reestablish_msgs!(nodes[1], nodes[0]);
215+
216+
nodes[0].node.peer_disconnected(nodes[1].node.get_our_node_id());
217+
nodes[1].node.peer_disconnected(nodes[0].node.get_our_node_id());
218+
219+
// // Lets drop the monitor and clear the chain_monitor as well.
220+
nodes[0].chain_source.clear_watched_txn_and_outputs();
221+
reload_node!(nodes[0], test_default_channel_config(), &nodes_0_serialized, &[], persister, chain_monitor, nodes_0_deserialized);
222+
let persister: &dyn Persist<TestChannelSigner> = &chanmon_cfgs[0].persister;
223+
224+
let fundrecoverer
225+
= FundRecoverer::new(node_cfgs[0].keys_manager, node_cfgs[0].logger,test_default_channel_config(), ChainParameters {network: Network::Testnet,
226+
best_block: BestBlock::from_network(Network::Testnet)}, node_cfgs[0].keys_manager, node_cfgs[0].keys_manager, Some(&chanmon_cfgs[0].chain_source),
227+
persister, node_cfgs[0].fee_estimator, node_cfgs[0].tx_broadcaster);
228+
229+
fundrecoverer.peer_connected(nodes[1].node.get_our_node_id(), &msgs::Init {
230+
features: nodes[0].node.init_features(), networks: None, remote_network_address: None
231+
}, true).unwrap();
232+
233+
nodes[1].node.peer_connected(nodes[0].node.get_our_node_id(), &msgs::Init {
234+
features: nodes[0].node.init_features(), networks: None, remote_network_address: None
235+
}, true).unwrap();
236+
let msg_events = nodes[1].node.get_and_clear_pending_msg_events();
237+
// 0th - SendYourPeerStorageMessage
238+
// 1st - SendPeerStorage
239+
// 2nd - SendChannelReestablish
240+
assert_eq!(msg_events.len(), 3);
241+
242+
// println!("events array is {:?}", msg_events);
243+
for msg in msg_events {
244+
if let MessageSendEvent::SendChannelReestablish { ref node_id, ref msg } = msg {
245+
fundrecoverer.handle_channel_reestablish(nodes[1].node.get_our_node_id(), msg);
246+
assert_eq!(*node_id, nodes[0].node.get_our_node_id());
247+
} else if let MessageSendEvent::SendPeerStorageMessage { ref node_id, ref msg } = msg {
248+
fundrecoverer.handle_peer_storage(nodes[1].node.get_our_node_id(), msg);
249+
assert_eq!(*node_id, nodes[0].node.get_our_node_id());
250+
} else if let MessageSendEvent::SendYourPeerStorageMessage { ref node_id, ref msg } = msg {
251+
fundrecoverer.handle_your_peer_storage(nodes[1].node.get_our_node_id(), msg);
252+
assert_eq!(*node_id, nodes[0].node.get_our_node_id());
253+
} else {
254+
panic!("Unexpected event")
255+
}
256+
}
257+
258+
let bogus_chan_reestablish = fundrecoverer.get_and_clear_pending_msg_events();
259+
assert_eq!(bogus_chan_reestablish.len(), 1);
260+
261+
match bogus_chan_reestablish[0] {
262+
MessageSendEvent::SendChannelReestablish {ref node_id, ref msg} => {
263+
assert_eq!(nodes[1].node.get_our_node_id(), *node_id);
264+
nodes[1].node.handle_channel_reestablish(nodes[0].node.get_our_node_id(), msg);
265+
},
266+
_ => panic!("Unexpected event"),
267+
}
268+
269+
let commitment_tx = {
270+
let mut node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
271+
assert_eq!(node_txn.len(), 1);
272+
node_txn.remove(0)
273+
};
274+
275+
let block = create_dummy_block(nodes[1].best_block_hash(), 42, vec![commitment_tx.clone()]);
276+
connect_block(&nodes[1], &block);
277+
// Since we are using fundrecoverer as Chain::watch.
278+
let txdata: Vec<_> = block.txdata.iter().enumerate().collect();
279+
let height = nodes[0].best_block_info().1 + 1;
280+
fundrecoverer.best_block_updated(&block.header, height);
281+
fundrecoverer.transactions_confirmed(&block.header, &txdata, height);
282+
283+
check_closed_broadcast!(nodes[1], true);
284+
285+
let events = nodes[1].node.get_and_clear_pending_events();
286+
assert_eq!(events.len(), 1);
287+
match events[0] {
288+
Event::ChannelClosed {..} => {}, // If we actually processed we'd receive the payment
289+
_ => panic!("Unexpected event"),
290+
}
291+
let mut dummy_block = create_dummy_block(nodes[1].best_block_hash(), height, Vec::new());
292+
for i in 1..CHAN_CONFIRM_DEPTH {
293+
let prev_blockhash = dummy_block.header.block_hash();
294+
let dummy_txdata: Vec<_> = dummy_block.txdata.iter().enumerate().collect();
295+
fundrecoverer.best_block_updated(&dummy_block.header, height + i + 1);
296+
fundrecoverer.transactions_confirmed(&dummy_block.header, &dummy_txdata, height + i + 1);
297+
dummy_block = create_dummy_block(prev_blockhash, height + i + 1, Vec::new());
298+
}
299+
300+
// Clearing chain source so that the `drop` doesn't panic.
301+
nodes[0].chain_source.clear_watched_txn_and_outputs();
302+
303+
check_added_monitors!(nodes[1], 1);
304+
305+
for event in fundrecoverer.get_and_clear_pending_events() {
306+
match event {
307+
Event::SpendableOutputs { mut outputs, channel_id: _ } => {
308+
for outp in outputs.drain(..) {
309+
match outp {
310+
SpendableOutputDescriptor::StaticPaymentOutput(static_payment) => {
311+
assert_eq!(static_payment.output.value.to_sat(), commitment_tx.output[0].value.to_sat());
312+
},
313+
_ => panic!("Unexpected event"),
314+
}
315+
}
316+
},
317+
_ => panic!("Unexpected event"),
318+
};
319+
}
320+
}
321+
177322
fn do_test_counterparty_no_reserve(send_from_initiator: bool) {
178323
// A peer providing a channel_reserve_satoshis of 0 (or less than our dust limit) is insecure,
179324
// but only for them. Because some LSPs do it with some level of trust of the clients (for a

lightning/src/util/test_utils.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1431,6 +1431,10 @@ impl TestChainSource {
14311431
self.watched_outputs.lock().unwrap().remove(&(outpoint, script_pubkey.clone()));
14321432
self.watched_txn.lock().unwrap().remove(&(outpoint.txid, script_pubkey));
14331433
}
1434+
pub fn clear_watched_txn_and_outputs(&self) {
1435+
self.watched_outputs.lock().unwrap().clear();
1436+
self.watched_txn.lock().unwrap().clear();
1437+
}
14341438
}
14351439

14361440
impl UtxoLookup for TestChainSource {

0 commit comments

Comments
 (0)