Skip to content

Commit 3ec662b

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 bd112c2 commit 3ec662b

File tree

6 files changed

+33
-14
lines changed

6 files changed

+33
-14
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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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()],
183183
height: 0,
184184
max_height: 0,
185185
blocks_connected: 0,

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
@@ -3259,6 +3259,16 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
32593259

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

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

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

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

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)