Skip to content

Commit 04ceec8

Browse files
committed
Create and Sign PSBTs for spendable outputs
1 parent f569e9f commit 04ceec8

File tree

1 file changed

+153
-74
lines changed

1 file changed

+153
-74
lines changed

lightning/src/sign/mod.rs

Lines changed: 153 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ use crate::prelude::*;
4747
use core::convert::TryInto;
4848
use core::ops::Deref;
4949
use core::sync::atomic::{AtomicUsize, Ordering};
50+
use bitcoin::psbt::PartiallySignedTransaction;
5051
use crate::io::{self, Error};
5152
use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
5253
use crate::util::atomic_counter::AtomicCounter;
@@ -218,6 +219,121 @@ impl_writeable_tlv_based_enum!(SpendableOutputDescriptor,
218219
(2, StaticPaymentOutput),
219220
);
220221

222+
impl SpendableOutputDescriptor {
223+
/// Turns this into a [`bitcoin::psbt::Input`] which can be used to create a
224+
/// [`PartiallySignedTransaction`] which spends the given descriptor.
225+
///
226+
/// Note that this does not include any signatures, just the information required to
227+
/// construct the transaction and sign it.
228+
pub fn to_psbt_input(&self) -> bitcoin::psbt::Input {
229+
match self {
230+
SpendableOutputDescriptor::StaticOutput { output, .. } => {
231+
// Is a standard P2WPKH, no need for witness script
232+
bitcoin::psbt::Input {
233+
witness_utxo: Some(output.clone()),
234+
..Default::default()
235+
}
236+
},
237+
SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) => {
238+
// Todo we could add the witness script as well
239+
bitcoin::psbt::Input {
240+
witness_utxo: Some(descriptor.output.clone()),
241+
..Default::default()
242+
}
243+
},
244+
SpendableOutputDescriptor::StaticPaymentOutput(descriptor) => {
245+
// Todo we could add the witness script as well
246+
bitcoin::psbt::Input {
247+
witness_utxo: Some(descriptor.output.clone()),
248+
..Default::default()
249+
}
250+
},
251+
}
252+
}
253+
254+
/// Creates an unsigned [`PartiallySignedTransaction`] which spends the given descriptors to
255+
/// the given outputs, plus an output to the given change destination (if sufficient
256+
/// change value remains). The PSBT will have a feerate, at least, of the given value.
257+
///
258+
/// Returns the PSBT and expected max transaction weight.
259+
///
260+
/// Returns `Err(())` if the output value is greater than the input value minus required fee,
261+
/// if a descriptor was duplicated, or if an output descriptor `script_pubkey`
262+
/// does not match the one we can spend.
263+
///
264+
/// We do not enforce that outputs meet the dust limit or that any output scripts are standard.
265+
pub fn create_spendable_outputs_psbt(descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>, change_destination_script: Script, feerate_sat_per_1000_weight: u32, locktime: Option<PackedLockTime>) -> Result<(PartiallySignedTransaction, usize), ()> {
266+
let mut input = Vec::with_capacity(descriptors.len());
267+
let mut input_value = 0;
268+
let mut witness_weight = 0;
269+
let mut output_set = HashSet::with_capacity(descriptors.len());
270+
for outp in descriptors {
271+
match outp {
272+
SpendableOutputDescriptor::StaticPaymentOutput(descriptor) => {
273+
if !output_set.insert(descriptor.outpoint) { return Err(()); }
274+
input.push(TxIn {
275+
previous_output: descriptor.outpoint.into_bitcoin_outpoint(),
276+
script_sig: Script::new(),
277+
sequence: Sequence::ZERO,
278+
witness: Witness::new(),
279+
});
280+
witness_weight += StaticPaymentOutputDescriptor::MAX_WITNESS_LENGTH;
281+
#[cfg(feature = "grind_signatures")]
282+
{ witness_weight -= 1; } // Guarantees a low R signature
283+
input_value += descriptor.output.value;
284+
},
285+
SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) => {
286+
if !output_set.insert(descriptor.outpoint) { return Err(()); }
287+
input.push(TxIn {
288+
previous_output: descriptor.outpoint.into_bitcoin_outpoint(),
289+
script_sig: Script::new(),
290+
sequence: Sequence(descriptor.to_self_delay as u32),
291+
witness: Witness::new(),
292+
});
293+
witness_weight += DelayedPaymentOutputDescriptor::MAX_WITNESS_LENGTH;
294+
#[cfg(feature = "grind_signatures")]
295+
{ witness_weight -= 1; } // Guarantees a low R signature
296+
input_value += descriptor.output.value;
297+
},
298+
SpendableOutputDescriptor::StaticOutput { ref outpoint, ref output } => {
299+
if !output_set.insert(*outpoint) { return Err(()); }
300+
input.push(TxIn {
301+
previous_output: outpoint.into_bitcoin_outpoint(),
302+
script_sig: Script::new(),
303+
sequence: Sequence::ZERO,
304+
witness: Witness::new(),
305+
});
306+
witness_weight += 1 + 73 + 34;
307+
#[cfg(feature = "grind_signatures")]
308+
{ witness_weight -= 1; } // Guarantees a low R signature
309+
input_value += output.value;
310+
}
311+
}
312+
if input_value > MAX_VALUE_MSAT / 1000 { return Err(()); }
313+
}
314+
let mut tx = Transaction {
315+
version: 2,
316+
lock_time: locktime.unwrap_or(PackedLockTime::ZERO),
317+
input,
318+
output: outputs,
319+
};
320+
let expected_max_weight =
321+
transaction_utils::maybe_add_change_output(&mut tx, input_value, witness_weight, feerate_sat_per_1000_weight, change_destination_script)?;
322+
323+
let psbt_inputs = descriptors.iter().map(|d| d.to_psbt_input()).collect::<Vec<_>>();
324+
let psbt = PartiallySignedTransaction {
325+
inputs: psbt_inputs,
326+
outputs: vec![Default::default(); tx.output.len()],
327+
unsigned_tx: tx,
328+
xpub: Default::default(),
329+
version: 0,
330+
proprietary: Default::default(),
331+
unknown: Default::default(),
332+
};
333+
Ok((psbt, expected_max_weight))
334+
}
335+
}
336+
221337
/// A trait to handle Lightning channel key material without concretizing the channel type or
222338
/// the signature mechanism.
223339
pub trait ChannelSigner {
@@ -1171,97 +1287,40 @@ impl KeysManager {
11711287
)
11721288
}
11731289

1174-
/// Creates a [`Transaction`] which spends the given descriptors to the given outputs, plus an
1175-
/// output to the given change destination (if sufficient change value remains). The
1176-
/// transaction will have a feerate, at least, of the given value.
1290+
/// Signs the given [`PartiallySignedTransaction`] which spends the given [`SpendableOutputDescriptor`]s.
1291+
/// The resulting inputs will be finalized and the PSBT will be ready for broadcast if there
1292+
/// are no other inputs that need signing.
11771293
///
1178-
/// Returns `Err(())` if the output value is greater than the input value minus required fee,
1179-
/// if a descriptor was duplicated, or if an output descriptor `script_pubkey`
1180-
/// does not match the one we can spend.
1181-
///
1182-
/// We do not enforce that outputs meet the dust limit or that any output scripts are standard.
1294+
/// Returns `Err(())` if the PSBT is missing a descriptor or if we fail to sign.
11831295
///
11841296
/// May panic if the [`SpendableOutputDescriptor`]s were not generated by channels which used
11851297
/// this [`KeysManager`] or one of the [`InMemorySigner`] created by this [`KeysManager`].
1186-
pub fn spend_spendable_outputs<C: Signing>(&self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>, change_destination_script: Script, feerate_sat_per_1000_weight: u32, secp_ctx: &Secp256k1<C>) -> Result<Transaction, ()> {
1187-
let mut input = Vec::new();
1188-
let mut input_value = 0;
1189-
let mut witness_weight = 0;
1190-
let mut output_set = HashSet::with_capacity(descriptors.len());
1191-
for outp in descriptors {
1192-
match outp {
1193-
SpendableOutputDescriptor::StaticPaymentOutput(descriptor) => {
1194-
input.push(TxIn {
1195-
previous_output: descriptor.outpoint.into_bitcoin_outpoint(),
1196-
script_sig: Script::new(),
1197-
sequence: Sequence::ZERO,
1198-
witness: Witness::new(),
1199-
});
1200-
witness_weight += StaticPaymentOutputDescriptor::MAX_WITNESS_LENGTH;
1201-
#[cfg(feature = "grind_signatures")]
1202-
{ witness_weight -= 1; } // Guarantees a low R signature
1203-
input_value += descriptor.output.value;
1204-
if !output_set.insert(descriptor.outpoint) { return Err(()); }
1205-
},
1206-
SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) => {
1207-
input.push(TxIn {
1208-
previous_output: descriptor.outpoint.into_bitcoin_outpoint(),
1209-
script_sig: Script::new(),
1210-
sequence: Sequence(descriptor.to_self_delay as u32),
1211-
witness: Witness::new(),
1212-
});
1213-
witness_weight += DelayedPaymentOutputDescriptor::MAX_WITNESS_LENGTH;
1214-
#[cfg(feature = "grind_signatures")]
1215-
{ witness_weight -= 1; } // Guarantees a low R signature
1216-
input_value += descriptor.output.value;
1217-
if !output_set.insert(descriptor.outpoint) { return Err(()); }
1218-
},
1219-
SpendableOutputDescriptor::StaticOutput { ref outpoint, ref output } => {
1220-
input.push(TxIn {
1221-
previous_output: outpoint.into_bitcoin_outpoint(),
1222-
script_sig: Script::new(),
1223-
sequence: Sequence::ZERO,
1224-
witness: Witness::new(),
1225-
});
1226-
witness_weight += 1 + 73 + 34;
1227-
#[cfg(feature = "grind_signatures")]
1228-
{ witness_weight -= 1; } // Guarantees a low R signature
1229-
input_value += output.value;
1230-
if !output_set.insert(*outpoint) { return Err(()); }
1231-
}
1232-
}
1233-
if input_value > MAX_VALUE_MSAT / 1000 { return Err(()); }
1234-
}
1235-
let mut spend_tx = Transaction {
1236-
version: 2,
1237-
lock_time: PackedLockTime(0),
1238-
input,
1239-
output: outputs,
1240-
};
1241-
let expected_max_weight =
1242-
transaction_utils::maybe_add_change_output(&mut spend_tx, input_value, witness_weight, feerate_sat_per_1000_weight, change_destination_script)?;
1243-
1298+
pub fn sign_spendable_outputs_psbt<C: Signing>(&self, descriptors: &[&SpendableOutputDescriptor], psbt: &mut PartiallySignedTransaction, secp_ctx: &Secp256k1<C>) -> Result<(), ()> {
12441299
let mut keys_cache: Option<(InMemorySigner, [u8; 32])> = None;
1245-
let mut input_idx = 0;
12461300
for outp in descriptors {
12471301
match outp {
12481302
SpendableOutputDescriptor::StaticPaymentOutput(descriptor) => {
1303+
let input_idx = psbt.unsigned_tx.input.iter().position(|i| i.previous_output == descriptor.outpoint.into_bitcoin_outpoint()).ok_or(())?;
12491304
if keys_cache.is_none() || keys_cache.as_ref().unwrap().1 != descriptor.channel_keys_id {
12501305
keys_cache = Some((
12511306
self.derive_channel_keys(descriptor.channel_value_satoshis, &descriptor.channel_keys_id),
12521307
descriptor.channel_keys_id));
12531308
}
1254-
spend_tx.input[input_idx].witness = Witness::from_vec(keys_cache.as_ref().unwrap().0.sign_counterparty_payment_input(&spend_tx, input_idx, &descriptor, &secp_ctx)?);
1309+
let witness = Witness::from_vec(keys_cache.as_ref().unwrap().0.sign_counterparty_payment_input(&psbt.unsigned_tx, input_idx, &descriptor, &secp_ctx)?);
1310+
psbt.inputs[input_idx].final_script_witness = Some(witness);
12551311
},
12561312
SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) => {
1313+
let input_idx = psbt.unsigned_tx.input.iter().position(|i| i.previous_output == descriptor.outpoint.into_bitcoin_outpoint()).ok_or(())?;
12571314
if keys_cache.is_none() || keys_cache.as_ref().unwrap().1 != descriptor.channel_keys_id {
12581315
keys_cache = Some((
12591316
self.derive_channel_keys(descriptor.channel_value_satoshis, &descriptor.channel_keys_id),
12601317
descriptor.channel_keys_id));
12611318
}
1262-
spend_tx.input[input_idx].witness = Witness::from_vec(keys_cache.as_ref().unwrap().0.sign_dynamic_p2wsh_input(&spend_tx, input_idx, &descriptor, &secp_ctx)?);
1319+
let witness = Witness::from_vec(keys_cache.as_ref().unwrap().0.sign_dynamic_p2wsh_input(&psbt.unsigned_tx, input_idx, &descriptor, &secp_ctx)?);
1320+
psbt.inputs[input_idx].final_script_witness = Some(witness);
12631321
},
1264-
SpendableOutputDescriptor::StaticOutput { ref output, .. } => {
1322+
SpendableOutputDescriptor::StaticOutput { ref outpoint, ref output } => {
1323+
let input_idx = psbt.unsigned_tx.input.iter().position(|i| i.previous_output == outpoint.into_bitcoin_outpoint()).ok_or(())?;
12651324
let derivation_idx = if output.script_pubkey == self.destination_script {
12661325
1
12671326
} else {
@@ -1288,17 +1347,37 @@ impl KeysManager {
12881347

12891348
if payment_script != output.script_pubkey { return Err(()); };
12901349

1291-
let sighash = hash_to_message!(&sighash::SighashCache::new(&spend_tx).segwit_signature_hash(input_idx, &witness_script, output.value, EcdsaSighashType::All).unwrap()[..]);
1350+
let sighash = hash_to_message!(&sighash::SighashCache::new(&psbt.unsigned_tx).segwit_signature_hash(input_idx, &witness_script, output.value, EcdsaSighashType::All).unwrap()[..]);
12921351
let sig = sign_with_aux_rand(secp_ctx, &sighash, &secret.private_key, &self);
12931352
let mut sig_ser = sig.serialize_der().to_vec();
12941353
sig_ser.push(EcdsaSighashType::All as u8);
1295-
spend_tx.input[input_idx].witness.push(sig_ser);
1296-
spend_tx.input[input_idx].witness.push(pubkey.inner.serialize().to_vec());
1354+
let witness = Witness::from_vec(vec![sig_ser, pubkey.inner.serialize().to_vec()]);
1355+
psbt.inputs[input_idx].final_script_witness = Some(witness);
12971356
},
12981357
}
1299-
input_idx += 1;
13001358
}
13011359

1360+
Ok(())
1361+
}
1362+
1363+
/// Creates a [`Transaction`] which spends the given descriptors to the given outputs, plus an
1364+
/// output to the given change destination (if sufficient change value remains). The
1365+
/// transaction will have a feerate, at least, of the given value.
1366+
///
1367+
/// Returns `Err(())` if the output value is greater than the input value minus required fee,
1368+
/// if a descriptor was duplicated, or if an output descriptor `script_pubkey`
1369+
/// does not match the one we can spend.
1370+
///
1371+
/// We do not enforce that outputs meet the dust limit or that any output scripts are standard.
1372+
///
1373+
/// May panic if the [`SpendableOutputDescriptor`]s were not generated by channels which used
1374+
/// this [`KeysManager`] or one of the [`InMemorySigner`] created by this [`KeysManager`].
1375+
pub fn spend_spendable_outputs<C: Signing>(&self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>, change_destination_script: Script, feerate_sat_per_1000_weight: u32, secp_ctx: &Secp256k1<C>) -> Result<Transaction, ()> {
1376+
let (mut psbt, expected_max_weight) = SpendableOutputDescriptor::create_spendable_outputs_psbt(descriptors, outputs, change_destination_script, feerate_sat_per_1000_weight, None)?;
1377+
self.sign_spendable_outputs_psbt(descriptors, &mut psbt, secp_ctx)?;
1378+
1379+
let spend_tx = psbt.extract_tx();
1380+
13021381
debug_assert!(expected_max_weight >= spend_tx.weight());
13031382
// Note that witnesses with a signature vary somewhat in size, so allow
13041383
// `expected_max_weight` to overshoot by up to 3 bytes per input.

0 commit comments

Comments
 (0)