Skip to content

Commit f804d60

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

File tree

1 file changed

+140
-74
lines changed

1 file changed

+140
-74
lines changed

lightning/src/sign/mod.rs

Lines changed: 140 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,108 @@ impl_writeable_tlv_based_enum!(SpendableOutputDescriptor,
218219
(2, StaticPaymentOutput),
219220
);
220221

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

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.
1277+
/// Signs the given [`PartiallySignedTransaction`] which spends the given [`SpendableOutputDescriptor`]s.
1278+
/// The resulting inputs will be finalized and the PSBT will be ready for broadcast if there
1279+
/// are no other inputs that need signing.
11771280
///
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.
1281+
/// Returns `Err(())` if the PSBT is missing a descriptor or if we fail to sign.
11831282
///
11841283
/// May panic if the [`SpendableOutputDescriptor`]s were not generated by channels which used
11851284
/// 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-
1285+
pub fn sign_spendable_outputs_psbt<C: Signing>(&self, descriptors: &[&SpendableOutputDescriptor], psbt: &mut PartiallySignedTransaction, secp_ctx: &Secp256k1<C>) -> Result<(), ()> {
12441286
let mut keys_cache: Option<(InMemorySigner, [u8; 32])> = None;
1245-
let mut input_idx = 0;
12461287
for outp in descriptors {
12471288
match outp {
12481289
SpendableOutputDescriptor::StaticPaymentOutput(descriptor) => {
1290+
let input_idx = psbt.unsigned_tx.input.iter().position(|i| i.previous_output == descriptor.outpoint.into_bitcoin_outpoint()).ok_or(())?;
12491291
if keys_cache.is_none() || keys_cache.as_ref().unwrap().1 != descriptor.channel_keys_id {
12501292
keys_cache = Some((
12511293
self.derive_channel_keys(descriptor.channel_value_satoshis, &descriptor.channel_keys_id),
12521294
descriptor.channel_keys_id));
12531295
}
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)?);
1296+
let witness = Witness::from_vec(keys_cache.as_ref().unwrap().0.sign_counterparty_payment_input(&psbt.unsigned_tx, input_idx, &descriptor, &secp_ctx)?);
1297+
psbt.inputs[input_idx].final_script_witness = Some(witness);
12551298
},
12561299
SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) => {
1300+
let input_idx = psbt.unsigned_tx.input.iter().position(|i| i.previous_output == descriptor.outpoint.into_bitcoin_outpoint()).ok_or(())?;
12571301
if keys_cache.is_none() || keys_cache.as_ref().unwrap().1 != descriptor.channel_keys_id {
12581302
keys_cache = Some((
12591303
self.derive_channel_keys(descriptor.channel_value_satoshis, &descriptor.channel_keys_id),
12601304
descriptor.channel_keys_id));
12611305
}
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)?);
1306+
let witness = Witness::from_vec(keys_cache.as_ref().unwrap().0.sign_dynamic_p2wsh_input(&psbt.unsigned_tx, input_idx, &descriptor, &secp_ctx)?);
1307+
psbt.inputs[input_idx].final_script_witness = Some(witness);
12631308
},
1264-
SpendableOutputDescriptor::StaticOutput { ref output, .. } => {
1309+
SpendableOutputDescriptor::StaticOutput { ref outpoint, ref output } => {
1310+
let input_idx = psbt.unsigned_tx.input.iter().position(|i| i.previous_output == outpoint.into_bitcoin_outpoint()).ok_or(())?;
12651311
let derivation_idx = if output.script_pubkey == self.destination_script {
12661312
1
12671313
} else {
@@ -1288,17 +1334,37 @@ impl KeysManager {
12881334

12891335
if payment_script != output.script_pubkey { return Err(()); };
12901336

1291-
let sighash = hash_to_message!(&sighash::SighashCache::new(&spend_tx).segwit_signature_hash(input_idx, &witness_script, output.value, EcdsaSighashType::All).unwrap()[..]);
1337+
let sighash = hash_to_message!(&sighash::SighashCache::new(&psbt.unsigned_tx).segwit_signature_hash(input_idx, &witness_script, output.value, EcdsaSighashType::All).unwrap()[..]);
12921338
let sig = sign_with_aux_rand(secp_ctx, &sighash, &secret.private_key, &self);
12931339
let mut sig_ser = sig.serialize_der().to_vec();
12941340
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());
1341+
let witness = Witness::from_vec(vec![sig_ser, pubkey.inner.serialize().to_vec()]);
1342+
psbt.inputs[input_idx].final_script_witness = Some(witness);
12971343
},
12981344
}
1299-
input_idx += 1;
13001345
}
13011346

1347+
Ok(())
1348+
}
1349+
1350+
/// Creates a [`Transaction`] which spends the given descriptors to the given outputs, plus an
1351+
/// output to the given change destination (if sufficient change value remains). The
1352+
/// transaction will have a feerate, at least, of the given value.
1353+
///
1354+
/// Returns `Err(())` if the output value is greater than the input value minus required fee,
1355+
/// if a descriptor was duplicated, or if an output descriptor `script_pubkey`
1356+
/// does not match the one we can spend.
1357+
///
1358+
/// We do not enforce that outputs meet the dust limit or that any output scripts are standard.
1359+
///
1360+
/// May panic if the [`SpendableOutputDescriptor`]s were not generated by channels which used
1361+
/// this [`KeysManager`] or one of the [`InMemorySigner`] created by this [`KeysManager`].
1362+
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, ()> {
1363+
let (mut psbt, expected_max_weight) = SpendableOutputDescriptor::create_spendable_outputs_psbt(descriptors, outputs, change_destination_script, feerate_sat_per_1000_weight)?;
1364+
self.sign_spendable_outputs_psbt(descriptors, &mut psbt, secp_ctx)?;
1365+
1366+
let spend_tx = psbt.extract_tx();
1367+
13021368
debug_assert!(expected_max_weight >= spend_tx.weight());
13031369
// Note that witnesses with a signature vary somewhat in size, so allow
13041370
// `expected_max_weight` to overshoot by up to 3 bytes per input.

0 commit comments

Comments
 (0)