+
+ /// Creates a Transaction which spends the given descriptors to the given outputs, plus an
+ /// output to the given change destination (if sufficient change value remains). The
+ /// transaction will have a feerate, at least, of the given value.
+ ///
+ /// Returns `Err(())` if the output value is greater than the input value minus required fee or
+ /// if a descriptor was duplicated.
+ ///
+ /// We do not enforce that outputs meet the dust limit or that any output scripts are standard.
+ ///
+ /// May panic if the `SpendableOutputDescriptor`s were not generated by Channels which used
+ /// this KeysManager or one of the `InMemorySigner` created by this KeysManager.
+ 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, ()> {
+ let mut input = Vec::new();
+ let mut input_value = 0;
+ let mut witness_weight = 0;
+ let mut output_set = HashSet::with_capacity(descriptors.len());
+ for outp in descriptors {
+ match outp {
+ SpendableOutputDescriptor::StaticPaymentOutput(descriptor) => {
+ input.push(TxIn {
+ previous_output: descriptor.outpoint.into_bitcoin_outpoint(),
+ script_sig: Script::new(),
+ sequence: 0,
+ witness: Vec::new(),
+ });
+ witness_weight += StaticPaymentOutputDescriptor::MAX_WITNESS_LENGTH;
+ input_value += descriptor.output.value;
+ if !output_set.insert(descriptor.outpoint) { return Err(()); }
+ },
+ SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) => {
+ input.push(TxIn {
+ previous_output: descriptor.outpoint.into_bitcoin_outpoint(),
+ script_sig: Script::new(),
+ sequence: descriptor.to_self_delay as u32,
+ witness: Vec::new(),
+ });
+ witness_weight += DelayedPaymentOutputDescriptor::MAX_WITNESS_LENGTH;
+ input_value += descriptor.output.value;
+ if !output_set.insert(descriptor.outpoint) { return Err(()); }
+ },
+ SpendableOutputDescriptor::StaticOutput { ref outpoint, ref output } => {
+ input.push(TxIn {
+ previous_output: outpoint.into_bitcoin_outpoint(),
+ script_sig: Script::new(),
+ sequence: 0,
+ witness: Vec::new(),
+ });
+ witness_weight += 1 + 73 + 34;
+ input_value += output.value;
+ if !output_set.insert(*outpoint) { return Err(()); }
+ }
+ }
+ if input_value > MAX_VALUE_MSAT / 1000 { return Err(()); }
+ }
+ let mut spend_tx = Transaction {
+ version: 2,
+ lock_time: 0,
+ input,
+ output: outputs,
+ };
+ transaction_utils::maybe_add_change_output(&mut spend_tx, input_value, witness_weight, feerate_sat_per_1000_weight, change_destination_script)?;
+
+ let mut keys_cache: Option<(InMemorySigner, [u8; 32])> = None;
+ let mut input_idx = 0;
+ for outp in descriptors {
+ match outp {
+ SpendableOutputDescriptor::StaticPaymentOutput(descriptor) => {
+ if keys_cache.is_none() || keys_cache.as_ref().unwrap().1 != descriptor.channel_keys_id {
+ keys_cache = Some((
+ self.derive_channel_keys(descriptor.channel_value_satoshis, &descriptor.channel_keys_id),
+ descriptor.channel_keys_id));
+ }
+ spend_tx.input[input_idx].witness = keys_cache.as_ref().unwrap().0.sign_counterparty_payment_input(&spend_tx, input_idx, &descriptor, &secp_ctx).unwrap();
+ },
+ SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) => {
+ if keys_cache.is_none() || keys_cache.as_ref().unwrap().1 != descriptor.channel_keys_id {
+ keys_cache = Some((
+ self.derive_channel_keys(descriptor.channel_value_satoshis, &descriptor.channel_keys_id),
+ descriptor.channel_keys_id));
+ }
+ spend_tx.input[input_idx].witness = keys_cache.as_ref().unwrap().0.sign_dynamic_p2wsh_input(&spend_tx, input_idx, &descriptor, &secp_ctx).unwrap();
+ },
+ SpendableOutputDescriptor::StaticOutput { ref output, .. } => {
+ let derivation_idx = if output.script_pubkey == self.destination_script {
+ 1
+ } else {
+ 2
+ };
+ let secret = {
+ // Note that when we aren't serializing the key, network doesn't matter
+ match ExtendedPrivKey::new_master(Network::Testnet, &self.seed) {
+ Ok(master_key) => {
+ match master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(derivation_idx).expect("key space exhausted")) {
+ Ok(key) => key,
+ Err(_) => panic!("Your RNG is busted"),
+ }
+ }
+ Err(_) => panic!("Your rng is busted"),
+ }
+ };
+ let pubkey = ExtendedPubKey::from_private(&secp_ctx, &secret).public_key;
+ if derivation_idx == 2 {
+ assert_eq!(pubkey.key, self.shutdown_pubkey);
+ }
+ let witness_script = bitcoin::Address::p2pkh(&pubkey, Network::Testnet).script_pubkey();
+ let sighash = hash_to_message!(&bip143::SigHashCache::new(&spend_tx).signature_hash(input_idx, &witness_script, output.value, SigHashType::All)[..]);
+ let sig = secp_ctx.sign(&sighash, &secret.private_key.key);
+ spend_tx.input[input_idx].witness.push(sig.serialize_der().to_vec());
+ spend_tx.input[input_idx].witness[0].push(SigHashType::All as u8);
+ spend_tx.input[input_idx].witness.push(pubkey.key.serialize().to_vec());
+ },
+ }
+ input_idx += 1;
+ }
+ Ok(spend_tx)
+ }