Skip to content

Commit 325a021

Browse files
committed
Add util fn for creating a Transaction from spendable outputs
This adds a utility method, `KeysManager::spend_spendable_outputs`, which constructs a Transaction from a given set of `SpendableOutputDescriptor`s, deriving relevant keys as needed. It also adds methods which can sign individual inputs where channel-specific key derivation is required to `InMemoryChannelKeys`, making it easy to sign transaction inputs when a custom `KeysInterface` is used with `InMemoryChannelKeys`.
1 parent 294cba4 commit 325a021

File tree

1 file changed

+178
-3
lines changed

1 file changed

+178
-3
lines changed

lightning/src/chain/keysinterface.rs

Lines changed: 178 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//! spendable on-chain outputs which the user owns and is responsible for using just as any other
1212
//! on-chain output which is theirs.
1313
14-
use bitcoin::blockdata::transaction::{Transaction, TxOut, SigHashType};
14+
use bitcoin::blockdata::transaction::{Transaction, TxOut, TxIn, SigHashType};
1515
use bitcoin::blockdata::script::{Script, Builder};
1616
use bitcoin::blockdata::opcodes;
1717
use bitcoin::network::constants::Network;
@@ -28,17 +28,18 @@ use bitcoin::secp256k1::key::{SecretKey, PublicKey};
2828
use bitcoin::secp256k1::{Secp256k1, Signature, Signing};
2929
use bitcoin::secp256k1;
3030

31-
use util::byte_utils;
31+
use util::{byte_utils, transaction_utils};
3232
use util::ser::{Writeable, Writer, Readable};
3333

3434
use chain::transaction::OutPoint;
3535
use ln::chan_utils;
3636
use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction};
3737
use ln::msgs::UnsignedChannelAnnouncement;
3838

39+
use std::collections::HashSet;
3940
use std::sync::atomic::{AtomicUsize, Ordering};
4041
use std::io::Error;
41-
use ln::msgs::DecodeError;
42+
use ln::msgs::{DecodeError, MAX_VALUE_MSAT};
4243

4344
/// Information about a spendable output to a P2WSH script. See
4445
/// SpendableOutputDescriptor::DynamicOutputP2WSH for more details on how to spend this.
@@ -498,6 +499,63 @@ impl InMemoryChannelKeys {
498499
pub fn get_channel_parameters(&self) -> &ChannelTransactionParameters {
499500
self.channel_parameters.as_ref().unwrap()
500501
}
502+
503+
/// Sign the single input of spend_tx at index `input_idx` which spends the output
504+
/// described by descriptor, returning the witness stack for the input.
505+
///
506+
/// Returns an Err if the input at input_idx does not exist, has a non-empty script_sig,
507+
/// or is not spending the outpoint described by `descriptor.outpoint`.
508+
pub fn sign_counterparty_payment_input<C: Signing>(&self, spend_tx: &Transaction, input_idx: usize, descriptor: &StaticCounterpartyPaymentOutputDescriptor, secp_ctx: &Secp256k1<C>) -> Result<Vec<Vec<u8>>, ()> {
509+
// TODO: We really should be taking the SigHashCache as a parameter here instead of
510+
// spend_tx, but ideally the SigHashCache would expose the transaction's inputs read-only
511+
// so that we can check them. This requires upstream rust-bitcoin changes (as well as
512+
// bindings updates to support SigHashCache objects).
513+
if spend_tx.input.len() <= input_idx { return Err(()); }
514+
if !spend_tx.input[input_idx].script_sig.is_empty() { return Err(()); }
515+
if spend_tx.input[input_idx].previous_output != descriptor.outpoint.into_bitcoin_outpoint() { return Err(()); }
516+
517+
let remotepubkey = self.pubkeys().payment_point;
518+
let witness_script = bitcoin::Address::p2pkh(&::bitcoin::PublicKey{compressed: true, key: remotepubkey}, Network::Testnet).script_pubkey();
519+
let sighash = hash_to_message!(&bip143::SigHashCache::new(spend_tx).signature_hash(input_idx, &witness_script, descriptor.output.value, SigHashType::All)[..]);
520+
let remotesig = secp_ctx.sign(&sighash, &self.payment_key);
521+
522+
let mut witness = Vec::with_capacity(2);
523+
witness.push(remotesig.serialize_der().to_vec());
524+
witness[0].push(SigHashType::All as u8);
525+
witness.push(remotepubkey.serialize().to_vec());
526+
Ok(witness)
527+
}
528+
529+
/// Sign the single input of spend_tx at index `input_idx` which spends the output
530+
/// described by descriptor, returning the witness stack for the input.
531+
///
532+
/// Returns an Err if the input at input_idx does not exist, has a non-empty script_sig,
533+
/// is not spending the outpoint described by `descriptor.outpoint`, or does not have a
534+
/// sequence set to `descriptor.to_self_delay`.
535+
pub fn sign_dynamic_p2wsh_input<C: Signing>(&self, spend_tx: &Transaction, input_idx: usize, descriptor: &DynamicP2WSHOutputDescriptor, secp_ctx: &Secp256k1<C>) -> Result<Vec<Vec<u8>>, ()> {
536+
// TODO: We really should be taking the SigHashCache as a parameter here instead of
537+
// spend_tx, but ideally the SigHashCache would expose the transaction's inputs read-only
538+
// so that we can check them. This requires upstream rust-bitcoin changes (as well as
539+
// bindings updates to support SigHashCache objects).
540+
if spend_tx.input.len() <= input_idx { return Err(()); }
541+
if !spend_tx.input[input_idx].script_sig.is_empty() { return Err(()); }
542+
if spend_tx.input[input_idx].previous_output != descriptor.outpoint.into_bitcoin_outpoint() { return Err(()); }
543+
if spend_tx.input[input_idx].sequence != descriptor.to_self_delay as u32 { return Err(()); }
544+
545+
let delayed_payment_key = chan_utils::derive_private_key(&secp_ctx, &descriptor.per_commitment_point, &self.delayed_payment_base_key)
546+
.expect("We constructed the payment_base_key, so we can only fail here if the RNG is busted.");
547+
let delayed_payment_pubkey = PublicKey::from_secret_key(&secp_ctx, &delayed_payment_key);
548+
let witness_script = chan_utils::get_revokeable_redeemscript(&descriptor.revocation_pubkey, descriptor.to_self_delay, &delayed_payment_pubkey);
549+
let sighash = hash_to_message!(&bip143::SigHashCache::new(spend_tx).signature_hash(input_idx, &witness_script, descriptor.output.value, SigHashType::All)[..]);
550+
let local_delayedsig = secp_ctx.sign(&sighash, &delayed_payment_key);
551+
552+
let mut witness = Vec::with_capacity(3);
553+
witness.push(local_delayedsig.serialize_der().to_vec());
554+
witness[0].push(SigHashType::All as u8);
555+
witness.push(vec!()); //MINIMALIF
556+
witness.push(witness_script.clone().into_bytes());
557+
Ok(witness)
558+
}
501559
}
502560

503561
impl ChannelKeys for InMemoryChannelKeys {
@@ -823,6 +881,123 @@ impl KeysManager {
823881
params.clone()
824882
)
825883
}
884+
885+
/// Creates a Transaction which spends the given descriptors to the given outputs, plus an
886+
/// output to the given change destination (if sufficient change value remains). The
887+
/// transaction will have a feerate, at least, of the given value.
888+
///
889+
/// Returns `Err(())` if the output value is greater than the input value minus required fee or
890+
/// if a descriptor was duplicated.
891+
///
892+
/// We do not enforce that outputs meet the dust limit or that any output scripts are standard.
893+
///
894+
/// May panic if the `SpendableOutputDescriptor`s were not generated by Channels which used
895+
/// this KeysManager or one of the `InMemoryChannelKeys` created by this KeysManager.
896+
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, ()> {
897+
let mut input = Vec::new();
898+
let mut input_value = 0;
899+
let mut witness_weight = 0;
900+
let mut output_set = HashSet::with_capacity(descriptors.len());
901+
for outp in descriptors {
902+
match outp {
903+
SpendableOutputDescriptor::StaticOutputCounterpartyPayment(descriptor) => {
904+
input.push(TxIn {
905+
previous_output: descriptor.outpoint.into_bitcoin_outpoint(),
906+
script_sig: Script::new(),
907+
sequence: 0,
908+
witness: Vec::new(),
909+
});
910+
witness_weight += StaticCounterpartyPaymentOutputDescriptor::MAX_WITNESS_LENGTH;
911+
input_value += descriptor.output.value;
912+
if !output_set.insert(descriptor.outpoint) { return Err(()); }
913+
},
914+
SpendableOutputDescriptor::DynamicOutputP2WSH(descriptor) => {
915+
input.push(TxIn {
916+
previous_output: descriptor.outpoint.into_bitcoin_outpoint(),
917+
script_sig: Script::new(),
918+
sequence: descriptor.to_self_delay as u32,
919+
witness: Vec::new(),
920+
});
921+
witness_weight += DynamicP2WSHOutputDescriptor::MAX_WITNESS_LENGTH;
922+
input_value += descriptor.output.value;
923+
if !output_set.insert(descriptor.outpoint) { return Err(()); }
924+
},
925+
SpendableOutputDescriptor::StaticOutput { ref outpoint, ref output } => {
926+
input.push(TxIn {
927+
previous_output: outpoint.into_bitcoin_outpoint(),
928+
script_sig: Script::new(),
929+
sequence: 0,
930+
witness: Vec::new(),
931+
});
932+
witness_weight += 1 + 73 + 34;
933+
input_value += output.value;
934+
if !output_set.insert(*outpoint) { return Err(()); }
935+
}
936+
}
937+
if input_value > MAX_VALUE_MSAT / 1000 { return Err(()); }
938+
}
939+
let mut spend_tx = Transaction {
940+
version: 2,
941+
lock_time: 0,
942+
input,
943+
output: outputs,
944+
};
945+
transaction_utils::maybe_add_change_output(&mut spend_tx, input_value, witness_weight, feerate_sat_per_1000_weight, change_destination_script)?;
946+
947+
let mut keys_cache: Option<(InMemoryChannelKeys, [u8; 32])> = None;
948+
let mut input_idx = 0;
949+
for outp in descriptors {
950+
match outp {
951+
SpendableOutputDescriptor::StaticOutputCounterpartyPayment(descriptor) => {
952+
if keys_cache.is_none() || keys_cache.as_ref().unwrap().1 != descriptor.channel_keys_id {
953+
keys_cache = Some((
954+
self.derive_channel_keys(descriptor.channel_value_satoshis, &descriptor.channel_keys_id),
955+
descriptor.channel_keys_id));
956+
}
957+
spend_tx.input[input_idx].witness = keys_cache.as_ref().unwrap().0.sign_counterparty_payment_input(&spend_tx, input_idx, &descriptor, &secp_ctx).unwrap();
958+
},
959+
SpendableOutputDescriptor::DynamicOutputP2WSH(descriptor) => {
960+
if keys_cache.is_none() || keys_cache.as_ref().unwrap().1 != descriptor.channel_keys_id {
961+
keys_cache = Some((
962+
self.derive_channel_keys(descriptor.channel_value_satoshis, &descriptor.channel_keys_id),
963+
descriptor.channel_keys_id));
964+
}
965+
spend_tx.input[input_idx].witness = keys_cache.as_ref().unwrap().0.sign_dynamic_p2wsh_input(&spend_tx, input_idx, &descriptor, &secp_ctx).unwrap();
966+
},
967+
SpendableOutputDescriptor::StaticOutput { ref output, .. } => {
968+
let derivation_idx = if output.script_pubkey == self.destination_script {
969+
1
970+
} else {
971+
2
972+
};
973+
let secret = {
974+
// Note that when we aren't serializing the key, network doesn't matter
975+
match ExtendedPrivKey::new_master(Network::Testnet, &self.seed) {
976+
Ok(master_key) => {
977+
match master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(derivation_idx).expect("key space exhausted")) {
978+
Ok(key) => key,
979+
Err(_) => panic!("Your RNG is busted"),
980+
}
981+
}
982+
Err(_) => panic!("Your rng is busted"),
983+
}
984+
};
985+
let pubkey = ExtendedPubKey::from_private(&secp_ctx, &secret).public_key;
986+
if derivation_idx == 2 {
987+
assert_eq!(pubkey.key, self.shutdown_pubkey);
988+
}
989+
let witness_script = bitcoin::Address::p2pkh(&pubkey, Network::Testnet).script_pubkey();
990+
let sighash = hash_to_message!(&bip143::SigHashCache::new(&spend_tx).signature_hash(input_idx, &witness_script, output.value, SigHashType::All)[..]);
991+
let sig = secp_ctx.sign(&sighash, &secret.private_key.key);
992+
spend_tx.input[input_idx].witness.push(sig.serialize_der().to_vec());
993+
spend_tx.input[input_idx].witness[0].push(SigHashType::All as u8);
994+
spend_tx.input[input_idx].witness.push(pubkey.key.serialize().to_vec());
995+
},
996+
}
997+
input_idx += 1;
998+
}
999+
Ok(spend_tx)
1000+
}
8261001
}
8271002

8281003
impl KeysInterface for KeysManager {

0 commit comments

Comments
 (0)