Skip to content

Commit 07d1e83

Browse files
TheBlueMattjkczyz
andcommitted
Add assertions for in-order block [dis]connection in ChannelManager
Sadly the connected-in-order tests have to be skipped in our normal test suite as many tests violate it. Luckily we can still enforce it in the tests which run in other crates. Co-authored-by: Matt Corallo <[email protected]> Co-authored-by: Jeffrey Czyz <[email protected]>
1 parent b5d88a5 commit 07d1e83

File tree

6 files changed

+39
-20
lines changed

6 files changed

+39
-20
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
@@ -241,8 +241,8 @@ mod tests {
241241
let node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
242242
assert_eq!(node_txn.len(), 1);
243243

244-
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
245-
connect_block(&nodes[1], &Block { header, txdata: vec![node_txn[0].clone(), node_txn[0].clone()]}, 1);
244+
let header = BlockHeader { version: 0x20000000, prev_blockhash: *nodes[0].last_block_hash.lock().unwrap(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
245+
connect_block(&nodes[1], &Block { header, txdata: vec![node_txn[0].clone(), node_txn[0].clone()]}, CHAN_CONFIRM_DEPTH);
246246
check_closed_broadcast!(nodes[1], false);
247247
check_added_monitors!(nodes[1], 1);
248248

lightning/src/ln/channelmanager.rs

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

32613261
let _persistence_guard = PersistenceNotifierGuard::new(&self.total_consistency_lock, &self.persistence_notifier);
32623262

3263+
3264+
// This assertion should be enforced in tests, however we have a number of tests that
3265+
// were written before this requirement and do not meet it.
3266+
#[cfg(not(test))]
3267+
{
3268+
assert_eq!(*self.last_block_hash.read().unwrap(), header.prev_blockhash,
3269+
"Blocks must be connected in chain-order - the connected header must build on the last connected header");
3270+
assert_eq!(self.latest_block_height.load(Ordering::Acquire) as u64, height as u64 - 1,
3271+
"Blocks must be connected in chain-order - the connected header must build on the last connected header");
3272+
}
32633273
self.latest_block_height.store(height as usize, Ordering::Release);
32643274
*self.last_block_hash.write().unwrap() = block_hash;
32653275

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

3389+
assert_eq!(*self.last_block_hash.read().unwrap(), header.block_hash(),
3390+
"Blocks must be disconnected in chain-order - the disconnected header must be the last connected header");
33793391
self.latest_block_height.fetch_sub(1, Ordering::AcqRel);
33803392
*self.last_block_hash.write().unwrap() = header.prev_blockhash;
33813393

lightning/src/ln/functional_test_utils.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub fn confirm_transaction<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &Tran
5050
let dummy_tx = Transaction { version: 0, lock_time: 0, input: Vec::new(), output: Vec::new() };
5151
let dummy_tx_count = tx.version as usize;
5252
let mut block = Block {
53-
header: BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 },
53+
header: BlockHeader { version: 0x20000000, prev_blockhash: genesis_block(Network::Testnet).header.block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 },
5454
txdata: vec![dummy_tx; dummy_tx_count],
5555
};
5656
block.txdata.push(tx.clone());
@@ -85,12 +85,14 @@ pub fn connect_block<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, block: &Block,
8585
node.chain_monitor.chain_monitor.block_connected(&block.header, &txdata, height);
8686
node.node.block_connected(&block.header, &txdata, height);
8787
node.node.test_process_background_events();
88+
*node.last_block_hash.lock().unwrap() = block.header.block_hash();
8889
}
8990

9091
pub fn disconnect_block<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, header: &BlockHeader, height: u32) {
9192
node.chain_monitor.chain_monitor.block_disconnected(header, height);
9293
node.node.block_disconnected(header);
9394
node.node.test_process_background_events();
95+
*node.last_block_hash.lock().unwrap() = header.prev_blockhash;
9496
}
9597

9698
pub struct TestChanMonCfg {
@@ -123,6 +125,7 @@ pub struct Node<'a, 'b: 'a, 'c: 'b> {
123125
pub network_payment_count: Rc<RefCell<u8>>,
124126
pub network_chan_count: Rc<RefCell<u32>>,
125127
pub logger: &'c test_utils::TestLogger,
128+
pub last_block_hash: Mutex<BlockHash>,
126129
}
127130

128131
impl<'a, 'b, 'c> Drop for Node<'a, 'b, 'c> {
@@ -1189,6 +1192,7 @@ pub fn create_network<'a, 'b: 'a, 'c: 'b>(node_count: usize, cfgs: &'b Vec<NodeC
11891192
keys_manager: &cfgs[i].keys_manager, node: &chan_mgrs[i], net_graph_msg_handler,
11901193
node_seed: cfgs[i].node_seed, network_chan_count: chan_count.clone(),
11911194
network_payment_count: payment_count.clone(), logger: cfgs[i].logger,
1195+
last_block_hash: Mutex::new(genesis_block(Network::Testnet).header.block_hash()),
11921196
})
11931197
}
11941198

lightning/src/ln/reorg_tests.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ use util::test_utils;
2121
use util::ser::{ReadableArgs, Writeable};
2222

2323
use bitcoin::blockdata::block::{Block, BlockHeader};
24+
use bitcoin::blockdata::constants::genesis_block;
2425
use bitcoin::hash_types::BlockHash;
26+
use bitcoin::network::constants::Network;
2527

2628
use std::collections::HashMap;
2729
use std::mem;
@@ -208,7 +210,7 @@ fn do_test_unconf_chan(reload_node: bool, reorg_after_reload: bool) {
208210
mem::drop(channel_state);
209211

210212
let mut headers = Vec::new();
211-
let mut header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
213+
let mut header = BlockHeader { version: 0x20000000, prev_blockhash: genesis_block(Network::Testnet).header.block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
212214
headers.push(header.clone());
213215
for _i in 2..100 {
214216
header = BlockHeader { version: 0x20000000, prev_blockhash: header.block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
@@ -319,7 +321,7 @@ fn test_set_outpoints_partial_claiming() {
319321
check_spends!(remote_txn[2], remote_txn[0]);
320322

321323
// Connect blocks on node A to advance height towards TEST_FINAL_CLTV
322-
let prev_header_100 = connect_blocks(&nodes[1], 100, 0, false, Default::default());
324+
let block_hash_100 = connect_blocks(&nodes[1], 100, 0, false, genesis_block(Network::Testnet).header.block_hash());
323325
// Provide node A with both preimage
324326
nodes[0].node.claim_funds(payment_preimage_1, &None, 3_000_000);
325327
nodes[0].node.claim_funds(payment_preimage_2, &None, 3_000_000);
@@ -328,8 +330,8 @@ fn test_set_outpoints_partial_claiming() {
328330
nodes[0].node.get_and_clear_pending_msg_events();
329331

330332
// Connect blocks on node A commitment transaction
331-
let header = BlockHeader { version: 0x20000000, prev_blockhash: prev_header_100, merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
332-
connect_block(&nodes[0], &Block { header, txdata: vec![remote_txn[0].clone()] }, 101);
333+
let header_101 = BlockHeader { version: 0x20000000, prev_blockhash: block_hash_100, merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
334+
connect_block(&nodes[0], &Block { header: header_101, txdata: vec![remote_txn[0].clone()] }, 101);
333335
check_closed_broadcast!(nodes[0], false);
334336
check_added_monitors!(nodes[0], 1);
335337
// Verify node A broadcast tx claiming both HTLCs
@@ -361,8 +363,8 @@ fn test_set_outpoints_partial_claiming() {
361363
};
362364

363365
// Broadcast partial claim on node A, should regenerate a claiming tx with HTLC dropped
364-
let header = BlockHeader { version: 0x20000000, prev_blockhash: header.block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
365-
connect_block(&nodes[0], &Block { header, txdata: vec![partial_claim_tx.clone()] }, 102);
366+
let header_102 = BlockHeader { version: 0x20000000, prev_blockhash: header_101.block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
367+
connect_block(&nodes[0], &Block { header: header_102, txdata: vec![partial_claim_tx.clone()] }, 102);
366368
{
367369
let mut node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
368370
assert_eq!(node_txn.len(), 1);
@@ -373,7 +375,7 @@ fn test_set_outpoints_partial_claiming() {
373375
nodes[0].node.get_and_clear_pending_msg_events();
374376

375377
// Disconnect last block on node A, should regenerate a claiming tx with HTLC dropped
376-
disconnect_block(&nodes[0], &header, 102);
378+
disconnect_block(&nodes[0], &header_102, 102);
377379
{
378380
let mut node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
379381
assert_eq!(node_txn.len(), 1);
@@ -383,8 +385,8 @@ fn test_set_outpoints_partial_claiming() {
383385
}
384386

385387
//// Disconnect one more block and then reconnect multiple no transaction should be generated
386-
disconnect_block(&nodes[0], &header, 101);
387-
connect_blocks(&nodes[1], 15, 101, false, prev_header_100);
388+
disconnect_block(&nodes[0], &header_101, 101);
389+
connect_blocks(&nodes[1], 15, 101, false, block_hash_100);
388390
{
389391
let mut node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
390392
assert_eq!(node_txn.len(), 0);

0 commit comments

Comments
 (0)