Skip to content

Commit 5075faa

Browse files
Aditya SharmaAditya Sharma
Aditya Sharma
authored and
Aditya Sharma
committed
Add test for fund recovery using PeerStorage
Introduce test_peer_storage to verify fund recovery through PeerStorage. This test simulates a scenario where two nodes establish a channel, one node is formatted, and funds are recovered using FundRecoverer. - Add test_peer_storage in functional_tests.rs to validate fund recovery via PeerStorage. - Implement clear_watched_txn_and_output to reset watched_outputs and watched_txn for recovery testing. - Add handler for PeerStorage and YourPeerStorage in get_chan_reestablish_msgs!(), because we exchange these msgs just before exchanging ChannelReestablish.
1 parent 890e74a commit 5075faa

File tree

3 files changed

+176
-3
lines changed

3 files changed

+176
-3
lines changed

lightning/src/ln/functional_test_utils.rs

+6
Original file line numberDiff line numberDiff line change
@@ -3574,6 +3574,12 @@ macro_rules! get_chan_reestablish_msgs {
35743574
} else if let MessageSendEvent::SendChannelAnnouncement { ref node_id, ref msg, .. } = msg {
35753575
assert_eq!(*node_id, $dst_node.node.get_our_node_id());
35763576
announcements.insert(msg.contents.short_channel_id);
3577+
} else if let MessageSendEvent::SendPeerStorageMessage { ref node_id, ref msg } = msg {
3578+
$dst_node.node.handle_peer_storage($src_node.node.get_our_node_id(), msg);
3579+
assert_eq!(*node_id, $dst_node.node.get_our_node_id());
3580+
} else if let MessageSendEvent::SendYourPeerStorageMessage { ref node_id, ref msg } = msg {
3581+
$dst_node.node.handle_your_peer_storage($src_node.node.get_our_node_id(), msg);
3582+
assert_eq!(*node_id, $dst_node.node.get_our_node_id());
35773583
} else {
35783584
panic!("Unexpected event")
35793585
}

lightning/src/ln/functional_tests.rs

+165-3
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,19 @@
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+
use crate::ln::channelmanager::{self, ChainParameters, PaymentId, RAACommitmentOrder, PaymentSendFailure, RecipientOnionFields, BREAKDOWN_TIMEOUT, ENABLE_GOSSIP_TICKS, DISABLE_GOSSIP_TICKS, MIN_CLTV_EXPIRY_DELTA};
2526
use crate::ln::channel::{DISCONNECT_PEER_AWAITING_RESPONSE_TICKS, ChannelError};
2627
use crate::ln::{chan_utils, onion_utils};
2728
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};
29+
use crate::ln::fundrecoverer::{FundRecoverer, RecoveryEvent};
2830
use crate::routing::gossip::{NetworkGraph, NetworkUpdate};
2931
use crate::routing::router::{Path, PaymentParameters, Route, RouteHop, get_route, RouteParameters};
3032
use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, NodeFeatures};
@@ -174,6 +176,166 @@ fn test_funding_exceeds_no_wumbo_limit() {
174176
}
175177
}
176178

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

+5
Original file line numberDiff line numberDiff line change
@@ -1457,6 +1457,11 @@ impl TestChainSource {
14571457
self.watched_outputs.lock().unwrap().remove(&(outpoint, script_pubkey.clone()));
14581458
self.watched_txn.lock().unwrap().remove(&(outpoint.txid, script_pubkey));
14591459
}
1460+
1461+
pub fn clear_watched_txn_and_outputs(&self) {
1462+
self.watched_outputs.lock().unwrap().clear();
1463+
self.watched_txn.lock().unwrap().clear();
1464+
}
14601465
}
14611466

14621467
impl UtxoLookup for TestChainSource {

0 commit comments

Comments
 (0)