Skip to content

Commit dc49b41

Browse files
committed
Compute ConstructedTransaction weight from Transaction
Instead of defining a custom function for calculating a transaction's weight, have ConstructedTransaction hold a Transaction so that its weight method can be re-used. As a result NegotiatedTxInput no longer needs to store the transaction inputs.
1 parent 227d383 commit dc49b41

File tree

3 files changed

+57
-80
lines changed

3 files changed

+57
-80
lines changed

lightning/src/ln/channel.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8615,7 +8615,7 @@ where
86158615
return Err(APIError::APIMisuseError { err });
86168616
};
86178617

8618-
let tx = signing_session.unsigned_tx().build_unsigned_tx();
8618+
let tx = signing_session.unsigned_tx().tx();
86198619
if funding_txid_signed != tx.compute_txid() {
86208620
return Err(APIError::APIMisuseError {
86218621
err: "Transaction was malleated prior to signing".to_owned(),
@@ -8627,7 +8627,7 @@ where
86278627
let sig = match &self.context.holder_signer {
86288628
ChannelSignerType::Ecdsa(signer) => signer.sign_splice_shared_input(
86298629
&self.funding.channel_transaction_parameters,
8630-
&tx,
8630+
tx,
86318631
splice_input_index as usize,
86328632
&self.context.secp_ctx,
86338633
),

lightning/src/ln/channelmanager.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9137,7 +9137,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
91379137
{
91389138
if signing_session.has_local_contribution() {
91399139
let mut pending_events = self.pending_events.lock().unwrap();
9140-
let unsigned_transaction = signing_session.unsigned_tx().build_unsigned_tx();
9140+
let unsigned_transaction = signing_session.unsigned_tx().tx().clone();
91419141
let event_action = (
91429142
Event::FundingTransactionReadyForSigning {
91439143
unsigned_transaction,

lightning/src/ln/interactivetxs.rs

Lines changed: 54 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -200,21 +200,20 @@ pub(crate) struct ConstructedTransaction {
200200

201201
inputs: Vec<NegotiatedTxInput>,
202202
outputs: Vec<InteractiveTxOutput>,
203+
tx: Transaction,
203204

204205
local_inputs_value_satoshis: u64,
205206
local_outputs_value_satoshis: u64,
206207

207208
remote_inputs_value_satoshis: u64,
208209
remote_outputs_value_satoshis: u64,
209210

210-
lock_time: AbsoluteLockTime,
211211
shared_input_index: Option<u32>,
212212
}
213213

214214
#[derive(Clone, Debug, Eq, PartialEq)]
215215
pub(crate) struct NegotiatedTxInput {
216216
serial_id: SerialId,
217-
txin: TxIn,
218217
prev_output: TxOut,
219218
}
220219

@@ -230,19 +229,18 @@ impl NegotiatedTxInput {
230229

231230
impl_writeable_tlv_based!(NegotiatedTxInput, {
232231
(1, serial_id, required),
233-
(3, txin, required),
234-
(5, prev_output, required),
232+
(3, prev_output, required),
235233
});
236234

237235
impl_writeable_tlv_based!(ConstructedTransaction, {
238236
(1, holder_is_initiator, required),
239237
(3, inputs, required),
240238
(5, outputs, required),
241-
(7, local_inputs_value_satoshis, required),
242-
(9, local_outputs_value_satoshis, required),
243-
(11, remote_inputs_value_satoshis, required),
244-
(13, remote_outputs_value_satoshis, required),
245-
(15, lock_time, required),
239+
(7, tx, required),
240+
(9, local_inputs_value_satoshis, required),
241+
(11, local_outputs_value_satoshis, required),
242+
(13, remote_inputs_value_satoshis, required),
243+
(15, remote_outputs_value_satoshis, required),
246244
(17, shared_input_index, option),
247245
});
248246

@@ -282,23 +280,38 @@ impl ConstructedTransaction {
282280
.fold(0u64, |value, (_, input)| value.saturating_add(input.satisfaction_weight().to_wu()))
283281
);
284282

285-
let mut inputs: Vec<NegotiatedTxInput> =
286-
context.inputs.into_values().map(|tx_input| tx_input.into_negotiated_input()).collect();
283+
let mut inputs: Vec<(TxIn, NegotiatedTxInput)> =
284+
context.inputs.into_values().map(|input| input.into_txin_and_negotiated_input()).collect();
287285
let mut outputs: Vec<InteractiveTxOutput> = context.outputs.into_values().collect();
288-
inputs.sort_unstable_by_key(|input| input.serial_id);
286+
inputs.sort_unstable_by_key(|(_, input)| input.serial_id);
289287
outputs.sort_unstable_by_key(|output| output.serial_id);
290288

291289
let shared_input_index =
292290
context.shared_funding_input.as_ref().and_then(|shared_funding_input| {
293291
inputs
294292
.iter()
295-
.position(|input| {
296-
input.txin.previous_output == shared_funding_input.input.previous_output
293+
.position(|(txin, _)| {
294+
txin.previous_output == shared_funding_input.input.previous_output
297295
})
298296
.map(|position| position as u32)
299297
});
300298

301-
let constructed_tx = Self {
299+
let (input, inputs): (Vec<TxIn>, Vec<NegotiatedTxInput>) = inputs.into_iter().unzip();
300+
let output = outputs.iter().map(|output| output.tx_out().clone()).collect();
301+
302+
let tx = Transaction {
303+
version: Version::TWO,
304+
lock_time: context.tx_locktime,
305+
input,
306+
output,
307+
};
308+
309+
let tx_weight = tx.weight().checked_add(satisfaction_weight).unwrap_or(Weight::MAX);
310+
if tx_weight > Weight::from_wu(MAX_STANDARD_TX_WEIGHT as u64) {
311+
return Err(AbortReason::TransactionTooLarge);
312+
}
313+
314+
Ok(Self {
302315
holder_is_initiator: context.holder_is_initiator,
303316

304317
local_inputs_value_satoshis,
@@ -309,39 +322,14 @@ impl ConstructedTransaction {
309322

310323
inputs,
311324
outputs,
325+
tx,
312326

313-
lock_time: context.tx_locktime,
314327
shared_input_index,
315-
};
316-
317-
let tx_weight = constructed_tx.weight(satisfaction_weight);
318-
if tx_weight > Weight::from_wu(MAX_STANDARD_TX_WEIGHT as u64) {
319-
return Err(AbortReason::TransactionTooLarge);
320-
}
321-
322-
Ok(constructed_tx)
328+
})
323329
}
324330

325-
fn weight(&self, satisfaction_weight: Weight) -> Weight {
326-
let inputs_weight = Weight::from_wu(self.inputs.len() as u64 * BASE_INPUT_WEIGHT)
327-
.checked_add(satisfaction_weight)
328-
.unwrap_or(Weight::MAX);
329-
let outputs_weight = self.outputs.iter().fold(Weight::from_wu(0), |weight, output| {
330-
weight.checked_add(get_output_weight(output.script_pubkey())).unwrap_or(Weight::MAX)
331-
});
332-
Weight::from_wu(TX_COMMON_FIELDS_WEIGHT)
333-
.checked_add(inputs_weight)
334-
.and_then(|weight| weight.checked_add(outputs_weight))
335-
.unwrap_or(Weight::MAX)
336-
}
337-
338-
pub fn build_unsigned_tx(&self) -> Transaction {
339-
let ConstructedTransaction { inputs, outputs, .. } = self;
340-
341-
let input: Vec<TxIn> = inputs.iter().map(|input| input.txin.clone()).collect();
342-
let output: Vec<TxOut> = outputs.iter().map(|output| output.tx_out().clone()).collect();
343-
344-
Transaction { version: Version::TWO, lock_time: self.lock_time, input, output }
331+
pub fn tx(&self) -> &Transaction {
332+
&self.tx
345333
}
346334

347335
pub fn outputs(&self) -> impl Iterator<Item = &InteractiveTxOutput> {
@@ -353,23 +341,25 @@ impl ConstructedTransaction {
353341
}
354342

355343
pub fn compute_txid(&self) -> Txid {
356-
self.build_unsigned_tx().compute_txid()
344+
self.tx().compute_txid()
357345
}
358346

359347
/// Adds provided holder witnesses to holder inputs of unsigned transaction.
360348
///
361349
/// Note that it is assumed that the witness count equals the holder input count.
362350
fn add_local_witnesses(&mut self, witnesses: Vec<Witness>) {
363-
self.inputs
351+
self.tx
352+
.input
364353
.iter_mut()
354+
.zip(self.inputs.iter())
365355
.enumerate()
366-
.filter(|(_, input)| input.is_local(self.holder_is_initiator))
356+
.filter(|(_, (_, input))| input.is_local(self.holder_is_initiator))
367357
.filter(|(index, _)| {
368358
self.shared_input_index
369359
.map(|shared_index| *index != shared_index as usize)
370360
.unwrap_or(true)
371361
})
372-
.map(|(_, input)| &mut input.txin)
362+
.map(|(_, (txin, _))| txin)
373363
.zip(witnesses)
374364
.for_each(|(input, witness)| input.witness = witness);
375365
}
@@ -378,16 +368,18 @@ impl ConstructedTransaction {
378368
///
379369
/// Note that it is assumed that the witness count equals the counterparty input count.
380370
fn add_remote_witnesses(&mut self, witnesses: Vec<Witness>) {
381-
self.inputs
371+
self.tx
372+
.input
382373
.iter_mut()
374+
.zip(self.inputs.iter())
383375
.enumerate()
384-
.filter(|(_, input)| !input.is_local(self.holder_is_initiator))
376+
.filter(|(_, (_, input))| !input.is_local(self.holder_is_initiator))
385377
.filter(|(index, _)| {
386378
self.shared_input_index
387379
.map(|shared_index| *index != shared_index as usize)
388380
.unwrap_or(true)
389381
})
390-
.map(|(_, input)| &mut input.txin)
382+
.map(|(_, (txin, _))| txin)
391383
.zip(witnesses)
392384
.for_each(|(input, witness)| input.witness = witness);
393385
}
@@ -595,18 +587,7 @@ impl InteractiveTxSigningSession {
595587
}
596588

597589
fn finalize_funding_tx(&mut self) -> Transaction {
598-
let lock_time = self.unsigned_tx.lock_time;
599-
let ConstructedTransaction { inputs, outputs, shared_input_index, .. } =
600-
&mut self.unsigned_tx;
601-
602-
let mut tx = Transaction {
603-
version: Version::TWO,
604-
lock_time,
605-
input: inputs.iter().cloned().map(|input| input.txin).collect(),
606-
output: outputs.iter().cloned().map(|output| output.into_tx_out()).collect(),
607-
};
608-
609-
if let Some(shared_input_index) = shared_input_index {
590+
if let Some(shared_input_index) = self.unsigned_tx.shared_input_index {
610591
if let Some(holder_shared_input_sig) = self
611592
.holder_tx_signatures
612593
.as_ref()
@@ -629,7 +610,7 @@ impl InteractiveTxSigningSession {
629610
witness.push_ecdsa_signature(&holder_sig);
630611
}
631612
witness.push(&shared_input_sig.witness_script);
632-
tx.input[*shared_input_index as usize].witness = witness;
613+
self.unsigned_tx.tx.input[shared_input_index as usize].witness = witness;
633614
} else {
634615
debug_assert!(false);
635616
}
@@ -641,19 +622,19 @@ impl InteractiveTxSigningSession {
641622
}
642623
}
643624

644-
tx
625+
self.unsigned_tx.tx.clone()
645626
}
646627

647628
fn verify_interactive_tx_signatures<C: bitcoin::secp256k1::Verification>(
648629
&self, secp_ctx: &Secp256k1<C>, witnesses: &Vec<Witness>,
649630
) -> Result<(), String> {
650631
let unsigned_tx = self.unsigned_tx();
651-
let built_tx = unsigned_tx.build_unsigned_tx();
632+
let built_tx = unsigned_tx.tx();
652633
let prev_outputs: Vec<&TxOut> =
653634
unsigned_tx.inputs().map(|input| input.prev_output()).collect::<Vec<_>>();
654635
let all_prevouts = sighash::Prevouts::All(&prev_outputs[..]);
655636

656-
let mut cache = SighashCache::new(&built_tx);
637+
let mut cache = SighashCache::new(built_tx);
657638

658639
let script_pubkeys = unsigned_tx
659640
.inputs()
@@ -1848,9 +1829,9 @@ impl InteractiveTxInput {
18481829
self.input.satisfaction_weight()
18491830
}
18501831

1851-
fn into_negotiated_input(self) -> NegotiatedTxInput {
1832+
fn into_txin_and_negotiated_input(self) -> (TxIn, NegotiatedTxInput) {
18521833
let (txin, prev_output) = self.input.into_tx_in_with_prev_output();
1853-
NegotiatedTxInput { serial_id: self.serial_id, txin, prev_output }
1834+
(txin, NegotiatedTxInput { serial_id: self.serial_id, prev_output })
18541835
}
18551836
}
18561837

@@ -3310,16 +3291,12 @@ mod tests {
33103291
fn do_verify_tx_signatures(
33113292
transaction: Transaction, prev_outputs: Vec<TxOut>,
33123293
) -> Result<(), String> {
3313-
let inputs: Vec<NegotiatedTxInput> = transaction
3314-
.input
3315-
.iter()
3316-
.cloned()
3317-
.zip(prev_outputs.into_iter())
3294+
let inputs: Vec<NegotiatedTxInput> = prev_outputs
3295+
.into_iter()
33183296
.enumerate()
3319-
.map(|(idx, (txin, prev_output))| {
3297+
.map(|(idx, prev_output)| {
33203298
NegotiatedTxInput {
33213299
serial_id: idx as u64, // even values will be holder (initiator in this test)
3322-
txin,
33233300
prev_output,
33243301
}
33253302
})
@@ -3340,11 +3317,11 @@ mod tests {
33403317
holder_is_initiator: true,
33413318
inputs,
33423319
outputs,
3320+
tx: transaction.clone(),
33433321
local_inputs_value_satoshis: 0, // N/A for test
33443322
local_outputs_value_satoshis: 0, // N/A for test
33453323
remote_inputs_value_satoshis: 0, // N/A for test
33463324
remote_outputs_value_satoshis: 0, // N/A for test
3347-
lock_time: transaction.lock_time,
33483325
shared_input_index: None,
33493326
};
33503327

0 commit comments

Comments
 (0)