mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-03-15 15:39:09 +01:00
Merge pull request #2609 from wpaulino/monitor-get-spendable-output
Allow retrieval of SpendableOutputDescriptors from relevant transactions
This commit is contained in:
commit
6016101ac8
2 changed files with 71 additions and 36 deletions
|
@ -1669,6 +1669,33 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
|
|||
current_height, &broadcaster, &fee_estimator, &logger,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns the descriptors for relevant outputs (i.e., those that we can spend) within the
|
||||
/// transaction if they exist and the transaction has at least [`ANTI_REORG_DELAY`]
|
||||
/// confirmations.
|
||||
///
|
||||
/// Descriptors returned by this method are primarily exposed via [`Event::SpendableOutputs`]
|
||||
/// once they are no longer under reorg risk. This method serves as a way to retrieve these
|
||||
/// descriptors at a later time, either for historical purposes, or to replay any
|
||||
/// missed/unhandled descriptors. For the purpose of gathering historical records, if the
|
||||
/// channel close has fully resolved (i.e., [`ChannelMonitor::get_claimable_balances`] returns
|
||||
/// an empty set), you can retrieve all spendable outputs by providing all descendant spending
|
||||
/// transactions starting from the channel's funding or closing transaction that have at least
|
||||
/// [`ANTI_REORG_DELAY`] confirmations.
|
||||
///
|
||||
/// `tx` is a transaction we'll scan the outputs of. Any transaction can be provided. If any
|
||||
/// outputs which can be spent by us are found, at least one descriptor is returned.
|
||||
///
|
||||
/// `confirmation_height` must be the height of the block in which `tx` was included in.
|
||||
pub fn get_spendable_outputs(&self, tx: &Transaction, confirmation_height: u32) -> Vec<SpendableOutputDescriptor> {
|
||||
let inner = self.inner.lock().unwrap();
|
||||
let current_height = inner.best_block.height;
|
||||
if current_height.saturating_sub(ANTI_REORG_DELAY) + 1 >= confirmation_height {
|
||||
inner.get_spendable_outputs(tx)
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
|
||||
|
@ -3441,7 +3468,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
|
|||
}
|
||||
self.is_resolving_htlc_output(&tx, height, &block_hash, &logger);
|
||||
|
||||
self.is_paying_spendable_output(&tx, height, &block_hash, &logger);
|
||||
self.check_tx_and_push_spendable_outputs(&tx, height, &block_hash, &logger);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3987,34 +4014,18 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Check if any transaction broadcasted is paying fund back to some address we can assume to own
|
||||
fn is_paying_spendable_output<L: Deref>(&mut self, tx: &Transaction, height: u32, block_hash: &BlockHash, logger: &L) where L::Target: Logger {
|
||||
let mut spendable_output = None;
|
||||
for (i, outp) in tx.output.iter().enumerate() { // There is max one spendable output for any channel tx, including ones generated by us
|
||||
if i > ::core::u16::MAX as usize {
|
||||
// While it is possible that an output exists on chain which is greater than the
|
||||
// 2^16th output in a given transaction, this is only possible if the output is not
|
||||
// in a lightning transaction and was instead placed there by some third party who
|
||||
// wishes to give us money for no reason.
|
||||
// Namely, any lightning transactions which we pre-sign will never have anywhere
|
||||
// near 2^16 outputs both because such transactions must have ~2^16 outputs who's
|
||||
// scripts are not longer than one byte in length and because they are inherently
|
||||
// non-standard due to their size.
|
||||
// Thus, it is completely safe to ignore such outputs, and while it may result in
|
||||
// us ignoring non-lightning fund to us, that is only possible if someone fills
|
||||
// nearly a full block with garbage just to hit this case.
|
||||
continue;
|
||||
}
|
||||
fn get_spendable_outputs(&self, tx: &Transaction) -> Vec<SpendableOutputDescriptor> {
|
||||
let mut spendable_outputs = Vec::new();
|
||||
for (i, outp) in tx.output.iter().enumerate() {
|
||||
if outp.script_pubkey == self.destination_script {
|
||||
spendable_output = Some(SpendableOutputDescriptor::StaticOutput {
|
||||
spendable_outputs.push(SpendableOutputDescriptor::StaticOutput {
|
||||
outpoint: OutPoint { txid: tx.txid(), index: i as u16 },
|
||||
output: outp.clone(),
|
||||
});
|
||||
break;
|
||||
}
|
||||
if let Some(ref broadcasted_holder_revokable_script) = self.broadcasted_holder_revokable_script {
|
||||
if broadcasted_holder_revokable_script.0 == outp.script_pubkey {
|
||||
spendable_output = Some(SpendableOutputDescriptor::DelayedPaymentOutput(DelayedPaymentOutputDescriptor {
|
||||
spendable_outputs.push(SpendableOutputDescriptor::DelayedPaymentOutput(DelayedPaymentOutputDescriptor {
|
||||
outpoint: OutPoint { txid: tx.txid(), index: i as u16 },
|
||||
per_commitment_point: broadcasted_holder_revokable_script.1,
|
||||
to_self_delay: self.on_holder_tx_csv,
|
||||
|
@ -4023,27 +4034,32 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
|
|||
channel_keys_id: self.channel_keys_id,
|
||||
channel_value_satoshis: self.channel_value_satoshis,
|
||||
}));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if self.counterparty_payment_script == outp.script_pubkey {
|
||||
spendable_output = Some(SpendableOutputDescriptor::StaticPaymentOutput(StaticPaymentOutputDescriptor {
|
||||
spendable_outputs.push(SpendableOutputDescriptor::StaticPaymentOutput(StaticPaymentOutputDescriptor {
|
||||
outpoint: OutPoint { txid: tx.txid(), index: i as u16 },
|
||||
output: outp.clone(),
|
||||
channel_keys_id: self.channel_keys_id,
|
||||
channel_value_satoshis: self.channel_value_satoshis,
|
||||
}));
|
||||
break;
|
||||
}
|
||||
if self.shutdown_script.as_ref() == Some(&outp.script_pubkey) {
|
||||
spendable_output = Some(SpendableOutputDescriptor::StaticOutput {
|
||||
spendable_outputs.push(SpendableOutputDescriptor::StaticOutput {
|
||||
outpoint: OutPoint { txid: tx.txid(), index: i as u16 },
|
||||
output: outp.clone(),
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Some(spendable_output) = spendable_output {
|
||||
spendable_outputs
|
||||
}
|
||||
|
||||
/// Checks if the confirmed transaction is paying funds back to some address we can assume to
|
||||
/// own.
|
||||
fn check_tx_and_push_spendable_outputs<L: Deref>(
|
||||
&mut self, tx: &Transaction, height: u32, block_hash: &BlockHash, logger: &L,
|
||||
) where L::Target: Logger {
|
||||
for spendable_output in self.get_spendable_outputs(tx) {
|
||||
let entry = OnchainEventEntry {
|
||||
txid: tx.txid(),
|
||||
transaction: Some(tx.clone()),
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
//! Further functional tests which test blockchain reorganizations.
|
||||
|
||||
use crate::sign::EcdsaChannelSigner;
|
||||
use crate::sign::{EcdsaChannelSigner, SpendableOutputDescriptor};
|
||||
use crate::chain::channelmonitor::{ANTI_REORG_DELAY, LATENCY_GRACE_PERIOD_BLOCKS, Balance};
|
||||
use crate::chain::transaction::OutPoint;
|
||||
use crate::chain::chaininterface::{LowerBoundedFeeEstimator, compute_feerate_sat_per_1000_weight};
|
||||
|
@ -21,6 +21,7 @@ use crate::ln::msgs::ChannelMessageHandler;
|
|||
use crate::util::config::UserConfig;
|
||||
use crate::util::crypto::sign;
|
||||
use crate::util::ser::Writeable;
|
||||
use crate::util::scid_utils::block_from_scid;
|
||||
use crate::util::test_utils;
|
||||
|
||||
use bitcoin::blockdata::transaction::EcdsaSighashType;
|
||||
|
@ -92,7 +93,7 @@ fn chanmon_fail_from_stale_commitment() {
|
|||
expect_payment_failed_with_update!(nodes[0], payment_hash, false, update_a.contents.short_channel_id, true);
|
||||
}
|
||||
|
||||
fn test_spendable_output<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, spendable_tx: &Transaction) {
|
||||
fn test_spendable_output<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, spendable_tx: &Transaction) -> Vec<SpendableOutputDescriptor> {
|
||||
let mut spendable = node.chain_monitor.chain_monitor.get_and_clear_pending_events();
|
||||
assert_eq!(spendable.len(), 1);
|
||||
if let Event::SpendableOutputs { outputs, .. } = spendable.pop().unwrap() {
|
||||
|
@ -100,6 +101,7 @@ fn test_spendable_output<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, spendable_t
|
|||
let spend_tx = node.keys_manager.backing.spend_spendable_outputs(&[&outputs[0]], Vec::new(),
|
||||
Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, None, &Secp256k1::new()).unwrap();
|
||||
check_spends!(spend_tx, spendable_tx);
|
||||
outputs
|
||||
} else { panic!(); }
|
||||
}
|
||||
|
||||
|
@ -196,8 +198,8 @@ fn chanmon_claim_value_coop_close() {
|
|||
assert_eq!(shutdown_tx, nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0));
|
||||
assert_eq!(shutdown_tx.len(), 1);
|
||||
|
||||
mine_transaction(&nodes[0], &shutdown_tx[0]);
|
||||
mine_transaction(&nodes[1], &shutdown_tx[0]);
|
||||
let shutdown_tx_conf_height_a = block_from_scid(&mine_transaction(&nodes[0], &shutdown_tx[0]));
|
||||
let shutdown_tx_conf_height_b = block_from_scid(&mine_transaction(&nodes[1], &shutdown_tx[0]));
|
||||
|
||||
assert!(nodes[0].node.list_channels().is_empty());
|
||||
assert!(nodes[1].node.list_channels().is_empty());
|
||||
|
@ -216,16 +218,33 @@ fn chanmon_claim_value_coop_close() {
|
|||
}],
|
||||
nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
|
||||
|
||||
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
|
||||
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
|
||||
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 2);
|
||||
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 2);
|
||||
|
||||
assert!(get_monitor!(nodes[0], chan_id)
|
||||
.get_spendable_outputs(&shutdown_tx[0], shutdown_tx_conf_height_a).is_empty());
|
||||
assert!(get_monitor!(nodes[1], chan_id)
|
||||
.get_spendable_outputs(&shutdown_tx[0], shutdown_tx_conf_height_b).is_empty());
|
||||
|
||||
connect_blocks(&nodes[0], 1);
|
||||
connect_blocks(&nodes[1], 1);
|
||||
|
||||
assert_eq!(Vec::<Balance>::new(),
|
||||
nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
|
||||
assert_eq!(Vec::<Balance>::new(),
|
||||
nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
|
||||
|
||||
test_spendable_output(&nodes[0], &shutdown_tx[0]);
|
||||
test_spendable_output(&nodes[1], &shutdown_tx[0]);
|
||||
let spendable_outputs_a = test_spendable_output(&nodes[0], &shutdown_tx[0]);
|
||||
assert_eq!(
|
||||
get_monitor!(nodes[0], chan_id).get_spendable_outputs(&shutdown_tx[0], shutdown_tx_conf_height_a),
|
||||
spendable_outputs_a
|
||||
);
|
||||
|
||||
let spendable_outputs_b = test_spendable_output(&nodes[1], &shutdown_tx[0]);
|
||||
assert_eq!(
|
||||
get_monitor!(nodes[1], chan_id).get_spendable_outputs(&shutdown_tx[0], shutdown_tx_conf_height_b),
|
||||
spendable_outputs_b
|
||||
);
|
||||
|
||||
check_closed_event!(nodes[0], 1, ClosureReason::CooperativeClosure, [nodes[1].node.get_our_node_id()], 1000000);
|
||||
check_closed_event!(nodes[1], 1, ClosureReason::CooperativeClosure, [nodes[0].node.get_our_node_id()], 1000000);
|
||||
|
|
Loading…
Add table
Reference in a new issue