Skip to content

Commit 8a8c75a

Browse files
authored
Merge pull request #846 from TheBlueMatt/2021-03-test-chains
Require syntactically-valid blockchains in functional and unit tests
2 parents 80ee1da + 4ebfa1d commit 8a8c75a

File tree

10 files changed

+378
-419
lines changed

10 files changed

+378
-419
lines changed

fuzz/src/chanmon_consistency.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,8 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
432432

433433
macro_rules! confirm_txn {
434434
($node: expr) => { {
435-
let mut header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
435+
let chain_hash = genesis_block(Network::Bitcoin).block_hash();
436+
let mut header = BlockHeader { version: 0x20000000, prev_blockhash: chain_hash, merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
436437
let txdata: Vec<_> = channel_txn.iter().enumerate().map(|(i, tx)| (i + 1, tx)).collect();
437438
$node.block_connected(&header, &txdata, 1);
438439
for i in 2..100 {

fuzz/src/full_stack.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ struct MoneyLossDetector<'a> {
161161
peers: &'a RefCell<[bool; 256]>,
162162
funding_txn: Vec<Transaction>,
163163
txids_confirmed: HashMap<Txid, usize>,
164-
header_hashes: Vec<BlockHash>,
164+
header_hashes: Vec<(BlockHash, u32)>,
165165
height: usize,
166166
max_height: usize,
167167
blocks_connected: u32,
@@ -179,7 +179,7 @@ impl<'a> MoneyLossDetector<'a> {
179179
peers,
180180
funding_txn: Vec::new(),
181181
txids_confirmed: HashMap::new(),
182-
header_hashes: vec![Default::default()],
182+
header_hashes: vec![(genesis_block(Network::Bitcoin).block_hash(), 0)],
183183
height: 0,
184184
max_height: 0,
185185
blocks_connected: 0,
@@ -199,23 +199,23 @@ impl<'a> MoneyLossDetector<'a> {
199199
}
200200
}
201201

202-
let header = BlockHeader { version: 0x20000000, prev_blockhash: self.header_hashes[self.height], merkle_root: Default::default(), time: self.blocks_connected, bits: 42, nonce: 42 };
203-
self.height += 1;
204202
self.blocks_connected += 1;
203+
let header = BlockHeader { version: 0x20000000, prev_blockhash: self.header_hashes[self.height].0, merkle_root: Default::default(), time: self.blocks_connected, bits: 42, nonce: 42 };
204+
self.height += 1;
205205
self.manager.block_connected(&header, &txdata, self.height as u32);
206206
(*self.monitor).block_connected(&header, &txdata, self.height as u32);
207207
if self.header_hashes.len() > self.height {
208-
self.header_hashes[self.height] = header.block_hash();
208+
self.header_hashes[self.height] = (header.block_hash(), self.blocks_connected);
209209
} else {
210210
assert_eq!(self.header_hashes.len(), self.height);
211-
self.header_hashes.push(header.block_hash());
211+
self.header_hashes.push((header.block_hash(), self.blocks_connected));
212212
}
213213
self.max_height = cmp::max(self.height, self.max_height);
214214
}
215215

216216
fn disconnect_block(&mut self) {
217217
if self.height > 0 && (self.max_height < 6 || self.height >= self.max_height - 6) {
218-
let header = BlockHeader { version: 0x20000000, prev_blockhash: self.header_hashes[self.height], merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
218+
let header = BlockHeader { version: 0x20000000, prev_blockhash: self.header_hashes[self.height - 1].0, merkle_root: Default::default(), time: self.header_hashes[self.height].1, bits: 42, nonce: 42 };
219219
self.manager.block_disconnected(&header);
220220
self.monitor.block_disconnected(&header, self.height as u32);
221221
self.height -= 1;

lightning-persister/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,8 @@ mod tests {
247247
let node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
248248
assert_eq!(node_txn.len(), 1);
249249

250-
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
251-
connect_block(&nodes[1], &Block { header, txdata: vec![node_txn[0].clone(), node_txn[0].clone()]}, 1);
250+
let header = BlockHeader { version: 0x20000000, prev_blockhash: nodes[0].best_block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
251+
connect_block(&nodes[1], &Block { header, txdata: vec![node_txn[0].clone(), node_txn[0].clone()]});
252252
check_closed_broadcast!(nodes[1], false);
253253
check_added_monitors!(nodes[1], 1);
254254

lightning/src/chain/channelmonitor.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1501,8 +1501,13 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
15011501
self.lockdown_from_offchain = true;
15021502
if *should_broadcast {
15031503
self.broadcast_latest_holder_commitment_txn(broadcaster, logger);
1504-
} else {
1504+
} else if !self.holder_tx_signed {
15051505
log_error!(logger, "You have a toxic holder commitment transaction avaible in channel monitor, read comment in ChannelMonitor::get_latest_holder_commitment_txn to be informed of manual action to take");
1506+
} else {
1507+
// If we generated a MonitorEvent::CommitmentTxBroadcasted, the ChannelManager
1508+
// will still give us a ChannelForceClosed event with !should_broadcast, but we
1509+
// shouldn't print the scary warning above.
1510+
log_info!(logger, "Channel off-chain state closed after we broadcasted our latest commitment transaction.");
15061511
}
15071512
}
15081513
}

lightning/src/ln/channelmanager.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3302,6 +3302,10 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
33023302

33033303
let _persistence_guard = PersistenceNotifierGuard::new(&self.total_consistency_lock, &self.persistence_notifier);
33043304

3305+
assert_eq!(*self.last_block_hash.read().unwrap(), header.prev_blockhash,
3306+
"Blocks must be connected in chain-order - the connected header must build on the last connected header");
3307+
assert_eq!(self.latest_block_height.load(Ordering::Acquire) as u64, height as u64 - 1,
3308+
"Blocks must be connected in chain-order - the connected header must build on the last connected header");
33053309
self.latest_block_height.store(height as usize, Ordering::Release);
33063310
*self.last_block_hash.write().unwrap() = block_hash;
33073311

@@ -3418,6 +3422,8 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
34183422
// See the docs for `ChannelManagerReadArgs` for more.
34193423
let _persistence_guard = PersistenceNotifierGuard::new(&self.total_consistency_lock, &self.persistence_notifier);
34203424

3425+
assert_eq!(*self.last_block_hash.read().unwrap(), header.block_hash(),
3426+
"Blocks must be disconnected in chain-order - the disconnected header must be the last connected header");
34213427
self.latest_block_height.fetch_sub(1, Ordering::AcqRel);
34223428
*self.last_block_hash.write().unwrap() = header.prev_blockhash;
34233429

lightning/src/ln/functional_test_utils.rs

Lines changed: 76 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -44,53 +44,81 @@ use std::sync::Mutex;
4444
use std::mem;
4545
use std::collections::HashMap;
4646

47-
pub const CHAN_CONFIRM_DEPTH: u32 = 100;
47+
pub const CHAN_CONFIRM_DEPTH: u32 = 10;
4848

49+
/// Mine the given transaction in the next block and then mine CHAN_CONFIRM_DEPTH - 1 blocks on
50+
/// top, giving the given transaction CHAN_CONFIRM_DEPTH confirmations.
4951
pub fn confirm_transaction<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &Transaction) {
50-
let dummy_tx = Transaction { version: 0, lock_time: 0, input: Vec::new(), output: Vec::new() };
51-
let dummy_tx_count = tx.version as usize;
52+
confirm_transaction_at(node, tx, node.best_block_info().1 + 1);
53+
connect_blocks(node, CHAN_CONFIRM_DEPTH - 1);
54+
}
55+
/// Mine a signle block containing the given transaction
56+
pub fn mine_transaction<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &Transaction) {
57+
let height = node.best_block_info().1 + 1;
58+
confirm_transaction_at(node, tx, height);
59+
}
60+
/// Mine the given transaction at the given height, mining blocks as required to build to that
61+
/// height
62+
pub fn confirm_transaction_at<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &Transaction, conf_height: u32) {
63+
let starting_block = node.best_block_info();
5264
let mut block = Block {
53-
header: BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 },
54-
txdata: vec![dummy_tx; dummy_tx_count],
65+
header: BlockHeader { version: 0x20000000, prev_blockhash: starting_block.0, merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 },
66+
txdata: Vec::new(),
5567
};
56-
block.txdata.push(tx.clone());
57-
connect_block(node, &block, 1);
58-
for i in 2..CHAN_CONFIRM_DEPTH {
68+
let height = starting_block.1 + 1;
69+
assert!(height <= conf_height);
70+
for _ in height..conf_height {
71+
connect_block(node, &block);
5972
block = Block {
6073
header: BlockHeader { version: 0x20000000, prev_blockhash: block.header.block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 },
6174
txdata: vec![],
6275
};
63-
connect_block(node, &block, i);
6476
}
77+
78+
for _ in 0..*node.network_chan_count.borrow() { // Make sure we don't end up with channels at the same short id by offsetting by chan_count
79+
block.txdata.push(Transaction { version: 0, lock_time: 0, input: Vec::new(), output: Vec::new() });
80+
}
81+
block.txdata.push(tx.clone());
82+
connect_block(node, &block);
6583
}
6684

67-
pub fn connect_blocks<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, depth: u32, height: u32, parent: bool, prev_blockhash: BlockHash) -> BlockHash {
85+
pub fn connect_blocks<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, depth: u32) -> BlockHash {
6886
let mut block = Block {
69-
header: BlockHeader { version: 0x2000000, prev_blockhash: if parent { prev_blockhash } else { Default::default() }, merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 },
87+
header: BlockHeader { version: 0x2000000, prev_blockhash: node.best_block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 },
7088
txdata: vec![],
7189
};
72-
connect_block(node, &block, height + 1);
73-
for i in 2..depth + 1 {
90+
connect_block(node, &block);
91+
for _ in 2..depth + 1 {
7492
block = Block {
7593
header: BlockHeader { version: 0x20000000, prev_blockhash: block.header.block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 },
7694
txdata: vec![],
7795
};
78-
connect_block(node, &block, height + i);
96+
connect_block(node, &block);
7997
}
8098
block.header.block_hash()
8199
}
82100

83-
pub fn connect_block<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, block: &Block, height: u32) {
101+
pub fn connect_block<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, block: &Block) {
84102
let txdata: Vec<_> = block.txdata.iter().enumerate().collect();
103+
let height = node.best_block_info().1 + 1;
85104
node.chain_monitor.chain_monitor.block_connected(&block.header, &txdata, height);
86105
node.node.block_connected(&block.header, &txdata, height);
87106
node.node.test_process_background_events();
107+
node.blocks.borrow_mut().push((block.header, height));
88108
}
89109

90-
pub fn disconnect_block<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, header: &BlockHeader, height: u32) {
91-
node.chain_monitor.chain_monitor.block_disconnected(header, height);
92-
node.node.block_disconnected(header);
93-
node.node.test_process_background_events();
110+
pub fn disconnect_blocks<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, count: u32) {
111+
for _ in 0..count {
112+
let orig_header = node.blocks.borrow_mut().pop().unwrap();
113+
assert!(orig_header.1 > 0); // Cannot disconnect genesis
114+
node.chain_monitor.chain_monitor.block_disconnected(&orig_header.0, orig_header.1);
115+
node.node.block_disconnected(&orig_header.0);
116+
}
117+
}
118+
119+
pub fn disconnect_all_blocks<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>) {
120+
let count = node.blocks.borrow_mut().len() as u32 - 1;
121+
disconnect_blocks(node, count);
94122
}
95123

96124
pub struct TestChanMonCfg {
@@ -123,6 +151,15 @@ pub struct Node<'a, 'b: 'a, 'c: 'b> {
123151
pub network_payment_count: Rc<RefCell<u8>>,
124152
pub network_chan_count: Rc<RefCell<u32>>,
125153
pub logger: &'c test_utils::TestLogger,
154+
pub blocks: RefCell<Vec<(BlockHeader, u32)>>,
155+
}
156+
impl<'a, 'b, 'c> Node<'a, 'b, 'c> {
157+
pub fn best_block_hash(&self) -> BlockHash {
158+
self.blocks.borrow_mut().last().unwrap().0.block_hash()
159+
}
160+
pub fn best_block_info(&self) -> (BlockHash, u32) {
161+
self.blocks.borrow_mut().last().map(|(a, b)| (a.block_hash(), *b)).unwrap()
162+
}
126163
}
127164

128165
impl<'a, 'b, 'c> Drop for Node<'a, 'b, 'c> {
@@ -416,8 +453,9 @@ pub fn create_chan_between_nodes_with_value_init<'a, 'b, 'c>(node_a: &Node<'a, '
416453
tx
417454
}
418455

419-
pub fn create_chan_between_nodes_with_value_confirm_first<'a, 'b, 'c, 'd>(node_recv: &'a Node<'b, 'c, 'c>, node_conf: &'a Node<'b, 'c, 'd>, tx: &Transaction) {
420-
confirm_transaction(node_conf, tx);
456+
pub fn create_chan_between_nodes_with_value_confirm_first<'a, 'b, 'c, 'd>(node_recv: &'a Node<'b, 'c, 'c>, node_conf: &'a Node<'b, 'c, 'd>, tx: &Transaction, conf_height: u32) {
457+
confirm_transaction_at(node_conf, tx, conf_height);
458+
connect_blocks(node_conf, CHAN_CONFIRM_DEPTH - 1);
421459
node_recv.node.handle_funding_locked(&node_conf.node.get_our_node_id(), &get_event_msg!(node_conf, MessageSendEvent::SendFundingLocked, node_recv.node.get_our_node_id()));
422460
}
423461

@@ -442,8 +480,10 @@ pub fn create_chan_between_nodes_with_value_confirm_second<'a, 'b, 'c>(node_recv
442480
}
443481

444482
pub fn create_chan_between_nodes_with_value_confirm<'a, 'b, 'c, 'd>(node_a: &'a Node<'b, 'c, 'd>, node_b: &'a Node<'b, 'c, 'd>, tx: &Transaction) -> ((msgs::FundingLocked, msgs::AnnouncementSignatures), [u8; 32]) {
445-
create_chan_between_nodes_with_value_confirm_first(node_a, node_b, tx);
446-
confirm_transaction(node_a, tx);
483+
let conf_height = std::cmp::max(node_a.best_block_info().1 + 1, node_b.best_block_info().1 + 1);
484+
create_chan_between_nodes_with_value_confirm_first(node_a, node_b, tx, conf_height);
485+
confirm_transaction_at(node_a, tx, conf_height);
486+
connect_blocks(node_a, CHAN_CONFIRM_DEPTH - 1);
447487
create_chan_between_nodes_with_value_confirm_second(node_b, node_a)
448488
}
449489

@@ -843,13 +883,13 @@ macro_rules! expect_payment_failed {
843883
assert_eq!(events.len(), 1);
844884
match events[0] {
845885
Event::PaymentFailed { ref payment_hash, rejected_by_dest, ref error_code, ref error_data } => {
846-
assert_eq!(*payment_hash, $expected_payment_hash);
847-
assert_eq!(rejected_by_dest, $rejected_by_dest);
848-
assert!(error_code.is_some());
849-
assert!(error_data.is_some());
886+
assert_eq!(*payment_hash, $expected_payment_hash, "unexpected payment_hash");
887+
assert_eq!(rejected_by_dest, $rejected_by_dest, "unexpected rejected_by_dest value");
888+
assert!(error_code.is_some(), "expected error_code.is_some() = true");
889+
assert!(error_data.is_some(), "expected error_data.is_some() = true");
850890
$(
851-
assert_eq!(error_code.unwrap(), $expected_error_code);
852-
assert_eq!(&error_data.as_ref().unwrap()[..], $expected_error_data);
891+
assert_eq!(error_code.unwrap(), $expected_error_code, "unexpected error code");
892+
assert_eq!(&error_data.as_ref().unwrap()[..], $expected_error_data, "unexpected error data");
853893
)*
854894
},
855895
_ => panic!("Unexpected event"),
@@ -1020,7 +1060,7 @@ pub fn claim_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route:
10201060
claim_payment_along_route(origin_node, expected_route, false, our_payment_preimage, expected_amount);
10211061
}
10221062

1023-
pub const TEST_FINAL_CLTV: u32 = 32;
1063+
pub const TEST_FINAL_CLTV: u32 = 50;
10241064

10251065
pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) -> (PaymentPreimage, PaymentHash) {
10261066
let net_graph_msg_handler = &origin_node.net_graph_msg_handler;
@@ -1161,6 +1201,9 @@ pub fn create_node_chanmgrs<'a, 'b>(node_count: usize, cfgs: &'a Vec<NodeCfg<'b>
11611201
let mut chanmgrs = Vec::new();
11621202
for i in 0..node_count {
11631203
let mut default_config = UserConfig::default();
1204+
// Set cltv_expiry_delta slightly lower to keep the final CLTV values inside one byte in our
1205+
// tests so that our script-length checks don't fail (see ACCEPTED_HTLC_SCRIPT_WEIGHT).
1206+
default_config.channel_options.cltv_expiry_delta = 6*6;
11641207
default_config.channel_options.announced_channel = true;
11651208
default_config.peer_channel_config_limits.force_announced_channel_preference = false;
11661209
default_config.own_channel_config.our_htlc_minimum_msat = 1000; // sanitization being done by the sender, to exerce receiver logic we need to lift of limit
@@ -1189,13 +1232,15 @@ pub fn create_network<'a, 'b: 'a, 'c: 'b>(node_count: usize, cfgs: &'b Vec<NodeC
11891232
keys_manager: &cfgs[i].keys_manager, node: &chan_mgrs[i], net_graph_msg_handler,
11901233
node_seed: cfgs[i].node_seed, network_chan_count: chan_count.clone(),
11911234
network_payment_count: payment_count.clone(), logger: cfgs[i].logger,
1235+
blocks: RefCell::new(vec![(genesis_block(Network::Testnet).header, 0)])
11921236
})
11931237
}
11941238

11951239
nodes
11961240
}
11971241

1198-
pub const ACCEPTED_HTLC_SCRIPT_WEIGHT: usize = 138; //Here we have a diff due to HTLC CLTV expiry being < 2^15 in test
1242+
// Note that the following only works for CLTV values up to 128
1243+
pub const ACCEPTED_HTLC_SCRIPT_WEIGHT: usize = 137; //Here we have a diff due to HTLC CLTV expiry being < 2^15 in test
11991244
pub const OFFERED_HTLC_SCRIPT_WEIGHT: usize = 133;
12001245

12011246
#[derive(PartialEq)]

0 commit comments

Comments
 (0)