Skip to content

Commit 9477d43

Browse files
committed
f - cache compute_txid
1 parent b2dc948 commit 9477d43

File tree

1 file changed

+126
-98
lines changed

1 file changed

+126
-98
lines changed

lightning/src/ln/channel.rs

Lines changed: 126 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1861,6 +1861,30 @@ struct PendingSplice {
18611861
received_funding_txid: Option<Txid>,
18621862
}
18631863

1864+
/// Wrapper around a [`Transaction`] useful for caching the result of [`Transaction::compute_txid`].
1865+
struct ConfirmedTransaction<'a> {
1866+
tx: &'a Transaction,
1867+
txid: Option<Txid>,
1868+
}
1869+
1870+
impl<'a> ConfirmedTransaction<'a> {
1871+
/// Returns the underlying [`Transaction`].
1872+
pub fn tx(&self) -> &'a Transaction {
1873+
self.tx
1874+
}
1875+
1876+
/// Returns the [`Txid`], computing and caching it if necessary.
1877+
pub fn txid(&mut self) -> Txid {
1878+
*self.txid.get_or_insert_with(|| self.tx.compute_txid())
1879+
}
1880+
}
1881+
1882+
impl<'a> From<&'a Transaction> for ConfirmedTransaction<'a> {
1883+
fn from(tx: &'a Transaction) -> Self {
1884+
ConfirmedTransaction { tx, txid: None }
1885+
}
1886+
}
1887+
18641888
/// Contains everything about the channel including state, and various flags.
18651889
pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
18661890
config: LegacyChannelConfig,
@@ -4929,83 +4953,83 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
49294953
return true;
49304954
}
49314955

4932-
fn check_for_funding_tx<'a, L: Deref>(
4956+
fn check_for_funding_tx<L: Deref>(
49334957
&mut self, funding: &mut FundingScope, block_hash: &BlockHash, height: u32,
4934-
txdata: &'a TransactionData, logger: &L,
4935-
) -> Result<Option<&'a Transaction>, ClosureReason>
4958+
index_in_block: usize, tx: &mut ConfirmedTransaction, logger: &L,
4959+
) -> Result<bool, ClosureReason>
49364960
where
49374961
L::Target: Logger
49384962
{
49394963
let funding_txo = match funding.get_funding_txo() {
49404964
Some(funding_txo) => funding_txo,
49414965
None => {
49424966
debug_assert!(false);
4943-
return Ok(None);
4967+
return Ok(false);
49444968
},
49454969
};
49464970

4947-
let mut confirmed_funding_tx = None;
4948-
for &(index_in_block, tx) in txdata.iter() {
4949-
// Check if the transaction is the expected funding transaction, and if it is,
4950-
// check that it pays the right amount to the right script.
4951-
if funding.funding_tx_confirmation_height == 0 {
4952-
if tx.compute_txid() == funding_txo.txid {
4953-
let txo_idx = funding_txo.index as usize;
4954-
if txo_idx >= tx.output.len() || tx.output[txo_idx].script_pubkey != funding.get_funding_redeemscript().to_p2wsh() ||
4955-
tx.output[txo_idx].value.to_sat() != funding.get_value_satoshis() {
4956-
if funding.is_outbound() {
4957-
// If we generated the funding transaction and it doesn't match what it
4958-
// should, the client is really broken and we should just panic and
4959-
// tell them off. That said, because hash collisions happen with high
4960-
// probability in fuzzing mode, if we're fuzzing we just close the
4961-
// channel and move on.
4962-
#[cfg(not(fuzzing))]
4963-
panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!");
4964-
}
4965-
self.update_time_counter += 1;
4966-
let err_reason = "funding tx had wrong script/value or output index";
4967-
return Err(ClosureReason::ProcessingError { err: err_reason.to_owned() });
4968-
} else {
4969-
if funding.is_outbound() {
4970-
if !tx.is_coinbase() {
4971-
for input in tx.input.iter() {
4972-
if input.witness.is_empty() {
4973-
// We generated a malleable funding transaction, implying we've
4974-
// just exposed ourselves to funds loss to our counterparty.
4975-
#[cfg(not(fuzzing))]
4976-
panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!");
4977-
}
4971+
let mut is_confirmed_funding_tx = false;
4972+
4973+
// Check if the transaction is the expected funding transaction, and if it is,
4974+
// check that it pays the right amount to the right script.
4975+
if funding.funding_tx_confirmation_height == 0 {
4976+
if tx.txid() == funding_txo.txid {
4977+
let tx = tx.tx();
4978+
let txo_idx = funding_txo.index as usize;
4979+
if txo_idx >= tx.output.len() || tx.output[txo_idx].script_pubkey != funding.get_funding_redeemscript().to_p2wsh() ||
4980+
tx.output[txo_idx].value.to_sat() != funding.get_value_satoshis() {
4981+
if funding.is_outbound() {
4982+
// If we generated the funding transaction and it doesn't match what it
4983+
// should, the client is really broken and we should just panic and
4984+
// tell them off. That said, because hash collisions happen with high
4985+
// probability in fuzzing mode, if we're fuzzing we just close the
4986+
// channel and move on.
4987+
#[cfg(not(fuzzing))]
4988+
panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!");
4989+
}
4990+
self.update_time_counter += 1;
4991+
let err_reason = "funding tx had wrong script/value or output index";
4992+
return Err(ClosureReason::ProcessingError { err: err_reason.to_owned() });
4993+
} else {
4994+
if funding.is_outbound() {
4995+
if !tx.is_coinbase() {
4996+
for input in tx.input.iter() {
4997+
if input.witness.is_empty() {
4998+
// We generated a malleable funding transaction, implying we've
4999+
// just exposed ourselves to funds loss to our counterparty.
5000+
#[cfg(not(fuzzing))]
5001+
panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!");
49785002
}
49795003
}
49805004
}
5005+
}
49815006

4982-
// The acceptor of v1-established channels doesn't have the funding
4983-
// transaction until it is seen on chain. Set it so that minimum_depth
4984-
// checks can tell if the coinbase transaction was used.
4985-
if funding.funding_transaction.is_none() {
4986-
funding.funding_transaction = Some(tx.clone());
4987-
}
4988-
4989-
funding.funding_tx_confirmation_height = height;
4990-
funding.funding_tx_confirmed_in = Some(*block_hash);
4991-
funding.short_channel_id = match scid_from_parts(height as u64, index_in_block as u64, txo_idx as u64) {
4992-
Ok(scid) => Some(scid),
4993-
Err(_) => panic!("Block was bogus - either height was > 16 million, had > 16 million transactions, or had > 65k outputs"),
4994-
};
5007+
// The acceptor of v1-established channels doesn't have the funding
5008+
// transaction until it is seen on chain. Set it so that minimum_depth
5009+
// checks can tell if the coinbase transaction was used.
5010+
if funding.funding_transaction.is_none() {
5011+
funding.funding_transaction = Some(tx.clone());
49955012
}
49965013

4997-
confirmed_funding_tx = Some(tx);
5014+
funding.funding_tx_confirmation_height = height;
5015+
funding.funding_tx_confirmed_in = Some(*block_hash);
5016+
funding.short_channel_id = match scid_from_parts(height as u64, index_in_block as u64, txo_idx as u64) {
5017+
Ok(scid) => Some(scid),
5018+
Err(_) => panic!("Block was bogus - either height was > 16 million, had > 16 million transactions, or had > 65k outputs"),
5019+
};
49985020
}
5021+
5022+
is_confirmed_funding_tx = true;
49995023
}
5000-
for inp in tx.input.iter() {
5001-
if inp.previous_output == funding_txo.into_bitcoin_outpoint() {
5002-
log_info!(logger, "Detected channel-closing tx {} spending {}:{}, closing channel {}", tx.compute_txid(), inp.previous_output.txid, inp.previous_output.vout, &self.channel_id());
5003-
return Err(ClosureReason::CommitmentTxConfirmed);
5004-
}
5024+
}
5025+
for inp in tx.tx().input.iter() {
5026+
if inp.previous_output == funding_txo.into_bitcoin_outpoint() {
5027+
log_info!(logger, "Detected channel-closing tx {} spending {}:{}, closing channel {}", tx.txid(), inp.previous_output.txid, inp.previous_output.vout, &self.channel_id());
5028+
return Err(ClosureReason::CommitmentTxConfirmed);
50055029
}
50065030
}
50075031

5008-
Ok(confirmed_funding_tx)
5032+
Ok(is_confirmed_funding_tx)
50095033
}
50105034
}
50115035

@@ -8327,59 +8351,63 @@ impl<SP: Deref> FundedChannel<SP> where
83278351
NS::Target: NodeSigner,
83288352
L::Target: Logger
83298353
{
8330-
// If we allow 1-conf funding, we may need to check for channel_ready or splice_locked here
8331-
// and send it immediately instead of waiting for a best_block_updated call (which may have
8332-
// already happened for this block).
8333-
let confirmed_funding_tx = self.context.check_for_funding_tx(
8334-
&mut self.funding, block_hash, height, txdata, logger,
8335-
)?;
8354+
for &(index_in_block, tx) in txdata.iter() {
8355+
let mut confirmed_tx = ConfirmedTransaction::from(tx);
83368356

8337-
if let Some(funding_tx) = confirmed_funding_tx {
8338-
if let Some(channel_ready) = self.check_get_channel_ready(height, logger) {
8339-
log_info!(logger, "Sending a channel_ready to our peer for channel {}", &self.context.channel_id);
8340-
let announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger);
8341-
return Ok((Some(FundingConfirmedMessage::Establishment(channel_ready)), announcement_sigs));
8342-
}
8343-
}
8357+
// If we allow 1-conf funding, we may need to check for channel_ready or splice_locked here
8358+
// and send it immediately instead of waiting for a best_block_updated call (which may have
8359+
// already happened for this block).
8360+
let is_confirmed_funding_tx = self.context.check_for_funding_tx(
8361+
&mut self.funding, block_hash, height, index_in_block, &mut confirmed_tx, logger,
8362+
)?;
83448363

8345-
#[cfg(splicing)]
8346-
let mut confirmed_funding = None;
8347-
#[cfg(splicing)]
8348-
for funding in self.pending_funding.iter_mut() {
8349-
if self.context.check_for_funding_tx(funding, block_hash, height, txdata, logger)?.is_some() {
8350-
if confirmed_funding.is_some() {
8351-
let err_reason = "splice tx of another pending funding already confirmed";
8352-
return Err(ClosureReason::ProcessingError { err: err_reason.to_owned() });
8364+
if is_confirmed_funding_tx {
8365+
if let Some(channel_ready) = self.check_get_channel_ready(height, logger) {
8366+
log_info!(logger, "Sending a channel_ready to our peer for channel {}", &self.context.channel_id);
8367+
let announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger);
8368+
return Ok((Some(FundingConfirmedMessage::Establishment(channel_ready)), announcement_sigs));
83538369
}
8370+
}
8371+
8372+
#[cfg(splicing)]
8373+
let mut confirmed_funding = None;
8374+
#[cfg(splicing)]
8375+
for funding in self.pending_funding.iter_mut() {
8376+
if self.context.check_for_funding_tx(funding, block_hash, height, index_in_block, &mut confirmed_tx, logger)? {
8377+
if confirmed_funding.is_some() {
8378+
let err_reason = "splice tx of another pending funding already confirmed";
8379+
return Err(ClosureReason::ProcessingError { err: err_reason.to_owned() });
8380+
}
83548381

8355-
confirmed_funding = Some(funding);
8382+
confirmed_funding = Some(funding);
8383+
}
83568384
}
8357-
}
83588385

8359-
#[cfg(splicing)]
8360-
if let Some(funding) = confirmed_funding {
8361-
let pending_splice = match self.pending_splice.as_mut() {
8362-
Some(pending_splice) => pending_splice,
8363-
None => {
8364-
// TODO: Move pending_funding into pending_splice?
8365-
debug_assert!(false);
8366-
// TODO: Error instead?
8367-
return Ok((None, None));
8368-
},
8369-
};
8386+
#[cfg(splicing)]
8387+
if let Some(funding) = confirmed_funding {
8388+
let pending_splice = match self.pending_splice.as_mut() {
8389+
Some(pending_splice) => pending_splice,
8390+
None => {
8391+
// TODO: Move pending_funding into pending_splice?
8392+
debug_assert!(false);
8393+
// TODO: Error instead?
8394+
return Ok((None, None));
8395+
},
8396+
};
83708397

8371-
if let Some(splice_locked) = self.context.check_get_splice_locked(pending_splice, funding, height, logger) {
8372-
log_info!(logger, "Sending a splice_locked to our peer for channel {}", &self.context.channel_id);
8398+
if let Some(splice_locked) = self.context.check_get_splice_locked(pending_splice, funding, height, logger) {
8399+
log_info!(logger, "Sending a splice_locked to our peer for channel {}", &self.context.channel_id);
83738400

8374-
pending_splice.sent_funding_txid = Some(splice_locked.splice_txid);
8375-
if pending_splice.sent_funding_txid == pending_splice.received_funding_txid {
8376-
promote_splice_funding!(self, funding);
8401+
pending_splice.sent_funding_txid = Some(splice_locked.splice_txid);
8402+
if pending_splice.sent_funding_txid == pending_splice.received_funding_txid {
8403+
promote_splice_funding!(self, funding);
83778404

8378-
let announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger);
8379-
return Ok((Some(FundingConfirmedMessage::Splice(splice_locked)), announcement_sigs));
8380-
}
8405+
let announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger);
8406+
return Ok((Some(FundingConfirmedMessage::Splice(splice_locked)), announcement_sigs));
8407+
}
83818408

8382-
return Ok((Some(FundingConfirmedMessage::Splice(splice_locked)), None));
8409+
return Ok((Some(FundingConfirmedMessage::Splice(splice_locked)), None));
8410+
}
83838411
}
83848412
}
83858413

0 commit comments

Comments
 (0)