Skip to content

Commit bc54441

Browse files
authored
Merge pull request #2212 from wpaulino/off-by-one-locktime
Fix off-by-one finalized transaction locktime
2 parents 5f96d13 + 97e4344 commit bc54441

12 files changed

+87
-71
lines changed

lightning-background-processor/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1033,12 +1033,12 @@ mod tests {
10331033
}
10341034

10351035
fn create_nodes(num_nodes: usize, persist_dir: String) -> Vec<Node> {
1036+
let network = Network::Testnet;
10361037
let mut nodes = Vec::new();
10371038
for i in 0..num_nodes {
1038-
let tx_broadcaster = Arc::new(test_utils::TestBroadcaster{txn_broadcasted: Mutex::new(Vec::new()), blocks: Arc::new(Mutex::new(Vec::new()))});
1039+
let tx_broadcaster = Arc::new(test_utils::TestBroadcaster::new(network));
10391040
let fee_estimator = Arc::new(test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(253) });
10401041
let logger = Arc::new(test_utils::TestLogger::with_id(format!("node {}", i)));
1041-
let network = Network::Testnet;
10421042
let genesis_block = genesis_block(network);
10431043
let network_graph = Arc::new(NetworkGraph::new(network, logger.clone()));
10441044
let scorer = Arc::new(Mutex::new(TestScorer::new()));

lightning/src/chain/channelmonitor.rs

+2-5
Original file line numberDiff line numberDiff line change
@@ -4169,7 +4169,7 @@ mod tests {
41694169
replay_update.updates.push(ChannelMonitorUpdateStep::PaymentPreimage { payment_preimage: payment_preimage_1 });
41704170
replay_update.updates.push(ChannelMonitorUpdateStep::PaymentPreimage { payment_preimage: payment_preimage_2 });
41714171

4172-
let broadcaster = TestBroadcaster::new(Arc::clone(&nodes[1].blocks));
4172+
let broadcaster = TestBroadcaster::with_blocks(Arc::clone(&nodes[1].blocks));
41734173
assert!(
41744174
pre_update_monitor.update_monitor(&replay_update, &&broadcaster, &chanmon_cfgs[1].fee_estimator, &nodes[1].logger)
41754175
.is_err());
@@ -4195,10 +4195,7 @@ mod tests {
41954195
fn test_prune_preimages() {
41964196
let secp_ctx = Secp256k1::new();
41974197
let logger = Arc::new(TestLogger::new());
4198-
let broadcaster = Arc::new(TestBroadcaster {
4199-
txn_broadcasted: Mutex::new(Vec::new()),
4200-
blocks: Arc::new(Mutex::new(Vec::new()))
4201-
});
4198+
let broadcaster = Arc::new(TestBroadcaster::new(Network::Testnet));
42024199
let fee_estimator = TestFeeEstimator { sat_per_kw: Mutex::new(253) };
42034200

42044201
let dummy_key = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());

lightning/src/chain/onchaintx.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -748,8 +748,8 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
748748
preprocessed_requests.push(req);
749749
}
750750

751-
// Claim everything up to and including cur_height + 1
752-
let remaining_locked_packages = self.locktimed_packages.split_off(&(cur_height + 2));
751+
// Claim everything up to and including `cur_height`
752+
let remaining_locked_packages = self.locktimed_packages.split_off(&(cur_height + 1));
753753
for (pop_height, mut entry) in self.locktimed_packages.iter_mut() {
754754
log_trace!(logger, "Restoring delayed claim of package(s) at their timelock at {}.", pop_height);
755755
preprocessed_requests.append(&mut entry);
@@ -1036,8 +1036,10 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
10361036
}
10371037
}
10381038
for ((_package_id, _), ref mut request) in bump_candidates.iter_mut() {
1039+
// `height` is the height being disconnected, so our `current_height` is 1 lower.
1040+
let current_height = height - 1;
10391041
if let Some((new_timer, new_feerate, bump_claim)) = self.generate_claim(
1040-
height, &request, true /* force_feerate_bump */, fee_estimator, &&*logger
1042+
current_height, &request, true /* force_feerate_bump */, fee_estimator, &&*logger
10411043
) {
10421044
request.set_timer(new_timer);
10431045
request.set_feerate(new_feerate);

lightning/src/chain/package.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -460,13 +460,13 @@ impl PackageSolvingData {
460460
}
461461
}
462462
fn absolute_tx_timelock(&self, current_height: u32) -> u32 {
463-
// We use `current_height + 1` as our default locktime to discourage fee sniping and because
463+
// We use `current_height` as our default locktime to discourage fee sniping and because
464464
// transactions with it always propagate.
465465
let absolute_timelock = match self {
466-
PackageSolvingData::RevokedOutput(_) => current_height + 1,
467-
PackageSolvingData::RevokedHTLCOutput(_) => current_height + 1,
468-
PackageSolvingData::CounterpartyOfferedHTLCOutput(_) => current_height + 1,
469-
PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => cmp::max(outp.htlc.cltv_expiry, current_height + 1),
466+
PackageSolvingData::RevokedOutput(_) => current_height,
467+
PackageSolvingData::RevokedHTLCOutput(_) => current_height,
468+
PackageSolvingData::CounterpartyOfferedHTLCOutput(_) => current_height,
469+
PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => cmp::max(outp.htlc.cltv_expiry, current_height),
470470
// HTLC timeout/success transactions rely on a fixed timelock due to the counterparty's
471471
// signature.
472472
PackageSolvingData::HolderHTLCOutput(ref outp) => {
@@ -475,7 +475,7 @@ impl PackageSolvingData {
475475
}
476476
outp.cltv_expiry
477477
},
478-
PackageSolvingData::HolderFundingOutput(_) => current_height + 1,
478+
PackageSolvingData::HolderFundingOutput(_) => current_height,
479479
};
480480
absolute_timelock
481481
}

lightning/src/ln/channelmanager.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -3026,10 +3026,11 @@ where
30263026
}
30273027
{
30283028
let height = self.best_block.read().unwrap().height();
3029-
// Transactions are evaluated as final by network mempools at the next block. However, the modules
3030-
// constituting our Lightning node might not have perfect sync about their blockchain views. Thus, if
3031-
// the wallet module is in advance on the LDK view, allow one more block of headroom.
3032-
if !funding_transaction.input.iter().all(|input| input.sequence == Sequence::MAX) && LockTime::from(funding_transaction.lock_time).is_block_height() && funding_transaction.lock_time.0 > height + 2 {
3029+
// Transactions are evaluated as final by network mempools if their locktime is strictly
3030+
// lower than the next block height. However, the modules constituting our Lightning
3031+
// node might not have perfect sync about their blockchain views. Thus, if the wallet
3032+
// module is ahead of LDK, only allow one more block of headroom.
3033+
if !funding_transaction.input.iter().all(|input| input.sequence == Sequence::MAX) && LockTime::from(funding_transaction.lock_time).is_block_height() && funding_transaction.lock_time.0 > height + 1 {
30333034
return Err(APIError::APIMisuseError {
30343035
err: "Funding transaction absolute timelock is non-final".to_owned()
30353036
});
@@ -9054,7 +9055,7 @@ pub mod bench {
90549055
// calls per node.
90559056
let network = bitcoin::Network::Testnet;
90569057

9057-
let tx_broadcaster = test_utils::TestBroadcaster{txn_broadcasted: Mutex::new(Vec::new()), blocks: Arc::new(Mutex::new(Vec::new()))};
9058+
let tx_broadcaster = test_utils::TestBroadcaster::new(network);
90589059
let fee_estimator = test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(253) };
90599060
let logger_a = test_utils::TestLogger::with_id("node a".to_owned());
90609061
let scorer = Mutex::new(test_utils::TestScorer::new());

lightning/src/ln/functional_test_utils.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,9 @@ fn do_connect_block<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, block: Block, sk
230230
#[cfg(feature = "std")] {
231231
eprintln!("Connecting block using Block Connection Style: {:?}", *node.connect_style.borrow());
232232
}
233+
// Update the block internally before handing it over to LDK, to ensure our assertions regarding
234+
// transaction broadcast are correct.
235+
node.blocks.lock().unwrap().push((block.clone(), height));
233236
if !skip_intermediaries {
234237
let txdata: Vec<_> = block.txdata.iter().enumerate().collect();
235238
match *node.connect_style.borrow() {
@@ -279,7 +282,6 @@ fn do_connect_block<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, block: Block, sk
279282
}
280283
call_claimable_balances(node);
281284
node.node.test_process_background_events();
282-
node.blocks.lock().unwrap().push((block, height));
283285
}
284286

285287
pub fn disconnect_blocks<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, count: u32) {
@@ -2435,10 +2437,7 @@ pub fn fail_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_path: &
24352437
pub fn create_chanmon_cfgs(node_count: usize) -> Vec<TestChanMonCfg> {
24362438
let mut chan_mon_cfgs = Vec::new();
24372439
for i in 0..node_count {
2438-
let tx_broadcaster = test_utils::TestBroadcaster {
2439-
txn_broadcasted: Mutex::new(Vec::new()),
2440-
blocks: Arc::new(Mutex::new(vec![(genesis_block(Network::Testnet), 0)])),
2441-
};
2440+
let tx_broadcaster = test_utils::TestBroadcaster::new(Network::Testnet);
24422441
let fee_estimator = test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(253) };
24432442
let chain_source = test_utils::TestChainSource::new(Network::Testnet);
24442443
let logger = test_utils::TestLogger::with_id(format!("node {}", i));

0 commit comments

Comments
 (0)