Merge pull request #789 from TheBlueMatt/2021-02-chansigner-util-fns
authorMatt Corallo <649246+TheBlueMatt@users.noreply.github.com>
Thu, 18 Feb 2021 17:37:00 +0000 (09:37 -0800)
committerGitHub <noreply@github.com>
Thu, 18 Feb 2021 17:37:00 +0000 (09:37 -0800)
Add util fn for creating a Transaction from spendable outputs

lightning/src/chain/channelmonitor.rs
lightning/src/chain/keysinterface.rs
lightning/src/ln/chan_utils.rs
lightning/src/ln/functional_tests.rs
lightning/src/util/macro_logger.rs
lightning/src/util/test_utils.rs
lightning/src/util/transaction_utils.rs

index 90f03520135053223ce996c076796f35370b470a..83b57c8e566fa859ba0967df3ed3d644d8b0fbbc 100644 (file)
@@ -43,7 +43,7 @@ use ln::channelmanager::{HTLCSource, PaymentPreimage, PaymentHash};
 use ln::onchaintx::{OnchainTxHandler, InputDescriptors};
 use chain::chaininterface::{BroadcasterInterface, FeeEstimator};
 use chain::transaction::{OutPoint, TransactionData};
-use chain::keysinterface::{SpendableOutputDescriptor, ChannelKeys, KeysInterface};
+use chain::keysinterface::{SpendableOutputDescriptor, StaticPaymentOutputDescriptor, DelayedPaymentOutputDescriptor, ChannelKeys, KeysInterface};
 use util::logger::Logger;
 use util::ser::{Readable, ReadableArgs, MaybeReadable, Writer, Writeable, U48};
 use util::byte_utils;
@@ -2201,7 +2201,7 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
                                break;
                        } else if let Some(ref broadcasted_holder_revokable_script) = self.broadcasted_holder_revokable_script {
                                if broadcasted_holder_revokable_script.0 == outp.script_pubkey {
-                                       spendable_output =  Some(SpendableOutputDescriptor::DynamicOutputP2WSH {
+                                       spendable_output =  Some(SpendableOutputDescriptor::DelayedPaymentOutput(DelayedPaymentOutputDescriptor {
                                                outpoint: OutPoint { txid: tx.txid(), index: i as u16 },
                                                per_commitment_point: broadcasted_holder_revokable_script.1,
                                                to_self_delay: self.on_holder_tx_csv,
@@ -2209,16 +2209,16 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
                                                revocation_pubkey: broadcasted_holder_revokable_script.2.clone(),
                                                channel_keys_id: self.channel_keys_id,
                                                channel_value_satoshis: self.channel_value_satoshis,
-                                       });
+                                       }));
                                        break;
                                }
                        } else if self.counterparty_payment_script == outp.script_pubkey {
-                               spendable_output = Some(SpendableOutputDescriptor::StaticOutputCounterpartyPayment {
+                               spendable_output = Some(SpendableOutputDescriptor::StaticPaymentOutput(StaticPaymentOutputDescriptor {
                                        outpoint: OutPoint { txid: tx.txid(), index: i as u16 },
                                        output: outp.clone(),
                                        channel_keys_id: self.channel_keys_id,
                                        channel_value_satoshis: self.channel_value_satoshis,
-                               });
+                               }));
                                break;
                        } else if outp.script_pubkey == self.shutdown_script {
                                spendable_output = Some(SpendableOutputDescriptor::StaticOutput {
index da5a99ddbb76a92bd7aaeef65ab0b574b136bbf1..b3e33b57ca8e0ff3282c85f075f12c6bbc81f080 100644 (file)
@@ -11,7 +11,7 @@
 //! spendable on-chain outputs which the user owns and is responsible for using just as any other
 //! on-chain output which is theirs.
 
-use bitcoin::blockdata::transaction::{Transaction, TxOut, SigHashType};
+use bitcoin::blockdata::transaction::{Transaction, TxOut, TxIn, SigHashType};
 use bitcoin::blockdata::script::{Script, Builder};
 use bitcoin::blockdata::opcodes;
 use bitcoin::network::constants::Network;
@@ -28,7 +28,7 @@ use bitcoin::secp256k1::key::{SecretKey, PublicKey};
 use bitcoin::secp256k1::{Secp256k1, Signature, Signing};
 use bitcoin::secp256k1;
 
-use util::byte_utils;
+use util::{byte_utils, transaction_utils};
 use util::ser::{Writeable, Writer, Readable};
 
 use chain::transaction::OutPoint;
@@ -36,9 +36,62 @@ use ln::chan_utils;
 use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction};
 use ln::msgs::UnsignedChannelAnnouncement;
 
+use std::collections::HashSet;
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::io::Error;
-use ln::msgs::DecodeError;
+use ln::msgs::{DecodeError, MAX_VALUE_MSAT};
+
+/// Information about a spendable output to a P2WSH script. See
+/// SpendableOutputDescriptor::DelayedPaymentOutput for more details on how to spend this.
+#[derive(Clone, Debug, PartialEq)]
+pub struct DelayedPaymentOutputDescriptor {
+       /// The outpoint which is spendable
+       pub outpoint: OutPoint,
+       /// Per commitment point to derive delayed_payment_key by key holder
+       pub per_commitment_point: PublicKey,
+       /// The nSequence value which must be set in the spending input to satisfy the OP_CSV in
+       /// the witness_script.
+       pub to_self_delay: u16,
+       /// The output which is referenced by the given outpoint
+       pub output: TxOut,
+       /// The revocation point specific to the commitment transaction which was broadcast. Used to
+       /// derive the witnessScript for this output.
+       pub revocation_pubkey: PublicKey,
+       /// Arbitrary identification information returned by a call to
+       /// `ChannelKeys::channel_keys_id()`. This may be useful in re-deriving keys used in
+       /// the channel to spend the output.
+       pub channel_keys_id: [u8; 32],
+       /// The value of the channel which this output originated from, possibly indirectly.
+       pub channel_value_satoshis: u64,
+}
+impl DelayedPaymentOutputDescriptor {
+       /// The maximum length a well-formed witness spending one of these should have.
+       // Calculated as 1 byte length + 73 byte signature, 1 byte empty vec push, 1 byte length plus
+       // redeemscript push length.
+       pub const MAX_WITNESS_LENGTH: usize = 1 + 73 + 1 + chan_utils::REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH + 1;
+}
+
+/// Information about a spendable output to our "payment key". See
+/// SpendableOutputDescriptor::StaticPaymentOutput for more details on how to spend this.
+#[derive(Clone, Debug, PartialEq)]
+pub struct StaticPaymentOutputDescriptor {
+       /// The outpoint which is spendable
+       pub outpoint: OutPoint,
+       /// The output which is referenced by the given outpoint
+       pub output: TxOut,
+       /// Arbitrary identification information returned by a call to
+       /// `ChannelKeys::channel_keys_id()`. This may be useful in re-deriving keys used in
+       /// the channel to spend the output.
+       pub channel_keys_id: [u8; 32],
+       /// The value of the channel which this transactions spends.
+       pub channel_value_satoshis: u64,
+}
+impl StaticPaymentOutputDescriptor {
+       /// The maximum length a well-formed witness spending one of these should have.
+       // Calculated as 1 byte legnth + 73 byte signature, 1 byte empty vec push, 1 byte length plus
+       // redeemscript push length.
+       pub const MAX_WITNESS_LENGTH: usize = 1 + 73 + 34;
+}
 
 /// When on-chain outputs are created by rust-lightning (which our counterparty is not able to
 /// claim at any point in the future) an event is generated which you must track and be able to
@@ -86,27 +139,7 @@ pub enum SpendableOutputDescriptor {
        /// regenerated by passing the revocation_pubkey (derived as above), our delayed_payment pubkey
        /// (derived as above), and the to_self_delay contained here to
        /// chan_utils::get_revokeable_redeemscript.
-       //
-       // TODO: we need to expose utility methods in KeyManager to do all the relevant derivation.
-       DynamicOutputP2WSH {
-               /// The outpoint which is spendable
-               outpoint: OutPoint,
-               /// Per commitment point to derive delayed_payment_key by key holder
-               per_commitment_point: PublicKey,
-               /// The nSequence value which must be set in the spending input to satisfy the OP_CSV in
-               /// the witness_script.
-               to_self_delay: u16,
-               /// The output which is referenced by the given outpoint
-               output: TxOut,
-               /// The revocation_pubkey used to derive witnessScript
-               revocation_pubkey: PublicKey,
-               /// Arbitrary identification information returned by a call to
-               /// `ChannelKeys::channel_keys_id()`. This may be useful in re-deriving keys used in
-               /// the channel to spend the output.
-               channel_keys_id: [u8; 32],
-               /// The value of the channel which this output originated from, possibly indirectly.
-               channel_value_satoshis: u64,
-       },
+       DelayedPaymentOutput(DelayedPaymentOutputDescriptor),
        /// An output to a P2WPKH, spendable exclusively by our payment key (ie the private key which
        /// corresponds to the public key in ChannelKeys::pubkeys().payment_point).
        /// The witness in the spending input, is, thus, simply:
@@ -114,18 +147,7 @@ pub enum SpendableOutputDescriptor {
        ///
        /// These are generally the result of our counterparty having broadcast the current state,
        /// allowing us to claim the non-HTLC-encumbered outputs immediately.
-       StaticOutputCounterpartyPayment {
-               /// The outpoint which is spendable
-               outpoint: OutPoint,
-               /// The output which is reference by the given outpoint
-               output: TxOut,
-               /// Arbitrary identification information returned by a call to
-               /// `ChannelKeys::channel_keys_id()`. This may be useful in re-deriving keys used in
-               /// the channel to spend the output.
-               channel_keys_id: [u8; 32],
-               /// The value of the channel which this transactions spends.
-               channel_value_satoshis: u64,
-       }
+       StaticPaymentOutput(StaticPaymentOutputDescriptor),
 }
 
 impl Writeable for SpendableOutputDescriptor {
@@ -136,22 +158,22 @@ impl Writeable for SpendableOutputDescriptor {
                                outpoint.write(writer)?;
                                output.write(writer)?;
                        },
-                       &SpendableOutputDescriptor::DynamicOutputP2WSH { ref outpoint, ref per_commitment_point, ref to_self_delay, ref output, ref revocation_pubkey, ref channel_keys_id, channel_value_satoshis } => {
+                       &SpendableOutputDescriptor::DelayedPaymentOutput(ref descriptor) => {
                                1u8.write(writer)?;
-                               outpoint.write(writer)?;
-                               per_commitment_point.write(writer)?;
-                               to_self_delay.write(writer)?;
-                               output.write(writer)?;
-                               revocation_pubkey.write(writer)?;
-                               channel_keys_id.write(writer)?;
-                               channel_value_satoshis.write(writer)?;
+                               descriptor.outpoint.write(writer)?;
+                               descriptor.per_commitment_point.write(writer)?;
+                               descriptor.to_self_delay.write(writer)?;
+                               descriptor.output.write(writer)?;
+                               descriptor.revocation_pubkey.write(writer)?;
+                               descriptor.channel_keys_id.write(writer)?;
+                               descriptor.channel_value_satoshis.write(writer)?;
                        },
-                       &SpendableOutputDescriptor::StaticOutputCounterpartyPayment { ref outpoint, ref output, ref channel_keys_id, channel_value_satoshis } => {
+                       &SpendableOutputDescriptor::StaticPaymentOutput(ref descriptor) => {
                                2u8.write(writer)?;
-                               outpoint.write(writer)?;
-                               output.write(writer)?;
-                               channel_keys_id.write(writer)?;
-                               channel_value_satoshis.write(writer)?;
+                               descriptor.outpoint.write(writer)?;
+                               descriptor.output.write(writer)?;
+                               descriptor.channel_keys_id.write(writer)?;
+                               descriptor.channel_value_satoshis.write(writer)?;
                        },
                }
                Ok(())
@@ -165,7 +187,7 @@ impl Readable for SpendableOutputDescriptor {
                                outpoint: Readable::read(reader)?,
                                output: Readable::read(reader)?,
                        }),
-                       1u8 => Ok(SpendableOutputDescriptor::DynamicOutputP2WSH {
+                       1u8 => Ok(SpendableOutputDescriptor::DelayedPaymentOutput(DelayedPaymentOutputDescriptor {
                                outpoint: Readable::read(reader)?,
                                per_commitment_point: Readable::read(reader)?,
                                to_self_delay: Readable::read(reader)?,
@@ -173,13 +195,13 @@ impl Readable for SpendableOutputDescriptor {
                                revocation_pubkey: Readable::read(reader)?,
                                channel_keys_id: Readable::read(reader)?,
                                channel_value_satoshis: Readable::read(reader)?,
-                       }),
-                       2u8 => Ok(SpendableOutputDescriptor::StaticOutputCounterpartyPayment {
+                       })),
+                       2u8 => Ok(SpendableOutputDescriptor::StaticPaymentOutput(StaticPaymentOutputDescriptor {
                                outpoint: Readable::read(reader)?,
                                output: Readable::read(reader)?,
                                channel_keys_id: Readable::read(reader)?,
                                channel_value_satoshis: Readable::read(reader)?,
-                       }),
+                       })),
                        _ => Err(DecodeError::InvalidValue),
                }
        }
@@ -202,14 +224,6 @@ impl Readable for SpendableOutputDescriptor {
 /// In any case, ChannelMonitor or fallback watchtowers are always going to be trusted
 /// to act, as liveness and breach reply correctness are always going to be hard requirements
 /// of LN security model, orthogonal of key management issues.
-///
-/// If you're implementing a custom signer, you almost certainly want to implement
-/// Readable/Writable to serialize out a unique reference to this set of keys so
-/// that you can serialize the full ChannelManager object.
-///
-// (TODO: We shouldn't require that, and should have an API to get them at deser time, due mostly
-// to the possibility of reentrancy issues by calling the user's code during our deserialization
-// routine).
 // TODO: We should remove Clone by instead requesting a new ChannelKeys copy when we create
 // ChannelMonitors instead of expecting to clone the one out of the Channel into the monitors.
 pub trait ChannelKeys : Send+Clone + Writeable {
@@ -225,7 +239,7 @@ pub trait ChannelKeys : Send+Clone + Writeable {
        /// May be called more than once for the same index.
        ///
        /// Note that the commitment number starts at (1 << 48) - 1 and counts backwards.
-       /// TODO: return a Result so we can signal a validation error
+       // TODO: return a Result so we can signal a validation error
        fn release_commitment_secret(&self, idx: u64) -> [u8; 32];
        /// Gets the holder's channel public keys and basepoints
        fn pubkeys(&self) -> &ChannelPublicKeys;
@@ -476,6 +490,63 @@ impl InMemoryChannelKeys {
        pub fn get_channel_parameters(&self) -> &ChannelTransactionParameters {
                self.channel_parameters.as_ref().unwrap()
        }
+
+       /// Sign the single input of spend_tx at index `input_idx` which spends the output
+       /// described by descriptor, returning the witness stack for the input.
+       ///
+       /// Returns an Err if the input at input_idx does not exist, has a non-empty script_sig,
+       /// or is not spending the outpoint described by `descriptor.outpoint`.
+       pub fn sign_counterparty_payment_input<C: Signing>(&self, spend_tx: &Transaction, input_idx: usize, descriptor: &StaticPaymentOutputDescriptor, secp_ctx: &Secp256k1<C>) -> Result<Vec<Vec<u8>>, ()> {
+               // TODO: We really should be taking the SigHashCache as a parameter here instead of
+               // spend_tx, but ideally the SigHashCache would expose the transaction's inputs read-only
+               // so that we can check them. This requires upstream rust-bitcoin changes (as well as
+               // bindings updates to support SigHashCache objects).
+               if spend_tx.input.len() <= input_idx { return Err(()); }
+               if !spend_tx.input[input_idx].script_sig.is_empty() { return Err(()); }
+               if spend_tx.input[input_idx].previous_output != descriptor.outpoint.into_bitcoin_outpoint() { return Err(()); }
+
+               let remotepubkey = self.pubkeys().payment_point;
+               let witness_script = bitcoin::Address::p2pkh(&::bitcoin::PublicKey{compressed: true, key: remotepubkey}, Network::Testnet).script_pubkey();
+               let sighash = hash_to_message!(&bip143::SigHashCache::new(spend_tx).signature_hash(input_idx, &witness_script, descriptor.output.value, SigHashType::All)[..]);
+               let remotesig = secp_ctx.sign(&sighash, &self.payment_key);
+
+               let mut witness = Vec::with_capacity(2);
+               witness.push(remotesig.serialize_der().to_vec());
+               witness[0].push(SigHashType::All as u8);
+               witness.push(remotepubkey.serialize().to_vec());
+               Ok(witness)
+       }
+
+       /// Sign the single input of spend_tx at index `input_idx` which spends the output
+       /// described by descriptor, returning the witness stack for the input.
+       ///
+       /// Returns an Err if the input at input_idx does not exist, has a non-empty script_sig,
+       /// is not spending the outpoint described by `descriptor.outpoint`, or does not have a
+       /// sequence set to `descriptor.to_self_delay`.
+       pub fn sign_dynamic_p2wsh_input<C: Signing>(&self, spend_tx: &Transaction, input_idx: usize, descriptor: &DelayedPaymentOutputDescriptor, secp_ctx: &Secp256k1<C>) -> Result<Vec<Vec<u8>>, ()> {
+               // TODO: We really should be taking the SigHashCache as a parameter here instead of
+               // spend_tx, but ideally the SigHashCache would expose the transaction's inputs read-only
+               // so that we can check them. This requires upstream rust-bitcoin changes (as well as
+               // bindings updates to support SigHashCache objects).
+               if spend_tx.input.len() <= input_idx { return Err(()); }
+               if !spend_tx.input[input_idx].script_sig.is_empty() { return Err(()); }
+               if spend_tx.input[input_idx].previous_output != descriptor.outpoint.into_bitcoin_outpoint() { return Err(()); }
+               if spend_tx.input[input_idx].sequence != descriptor.to_self_delay as u32 { return Err(()); }
+
+               let delayed_payment_key = chan_utils::derive_private_key(&secp_ctx, &descriptor.per_commitment_point, &self.delayed_payment_base_key)
+                       .expect("We constructed the payment_base_key, so we can only fail here if the RNG is busted.");
+               let delayed_payment_pubkey = PublicKey::from_secret_key(&secp_ctx, &delayed_payment_key);
+               let witness_script = chan_utils::get_revokeable_redeemscript(&descriptor.revocation_pubkey, descriptor.to_self_delay, &delayed_payment_pubkey);
+               let sighash = hash_to_message!(&bip143::SigHashCache::new(spend_tx).signature_hash(input_idx, &witness_script, descriptor.output.value, SigHashType::All)[..]);
+               let local_delayedsig = secp_ctx.sign(&sighash, &delayed_payment_key);
+
+               let mut witness = Vec::with_capacity(3);
+               witness.push(local_delayedsig.serialize_der().to_vec());
+               witness[0].push(SigHashType::All as u8);
+               witness.push(vec!()); //MINIMALIF
+               witness.push(witness_script.clone().into_bytes());
+               Ok(witness)
+       }
 }
 
 impl ChannelKeys for InMemoryChannelKeys {
@@ -701,9 +772,10 @@ impl KeysManager {
        /// Note that until the 0.1 release there is no guarantee of backward compatibility between
        /// versions. Once the library is more fully supported, the docs will be updated to include a
        /// detailed description of the guarantee.
-       pub fn new(seed: &[u8; 32], network: Network, starting_time_secs: u64, starting_time_nanos: u32) -> Self {
+       pub fn new(seed: &[u8; 32], starting_time_secs: u64, starting_time_nanos: u32) -> Self {
                let secp_ctx = Secp256k1::signing_only();
-               match ExtendedPrivKey::new_master(network.clone(), seed) {
+               // Note that when we aren't serializing the key, network doesn't matter
+               match ExtendedPrivKey::new_master(Network::Testnet, seed) {
                        Ok(master_key) => {
                                let node_secret = master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(0).unwrap()).expect("Your RNG is busted").private_key.key;
                                let destination_script = match master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx(1).unwrap()) {
@@ -800,6 +872,123 @@ impl KeysManager {
                        params.clone()
                )
        }
+
+       /// 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 `InMemoryChannelKeys` 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<(InMemoryChannelKeys, [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)
+       }
 }
 
 impl KeysInterface for KeysManager {
index b1171f543989c8d9deb7c6d68a18fb61e5e27af5..f86c53bc3bc7b01584ba9720014ec23b23330263 100644 (file)
@@ -384,11 +384,16 @@ impl TxCreationKeys {
        }
 }
 
+/// The maximum length of a script returned by get_revokeable_redeemscript.
+// Calculated as 6 bytes of opcodes, 1 byte push plus 2 bytes for contest_delay, and two public
+// keys of 33 bytes (+ 1 push).
+pub const REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH: usize = 6 + 3 + 34*2;
+
 /// A script either spendable by the revocation
 /// key or the broadcaster_delayed_payment_key and satisfying the relative-locktime OP_CSV constrain.
 /// Encumbering a `to_holder` output on a commitment transaction or 2nd-stage HTLC transactions.
 pub fn get_revokeable_redeemscript(revocation_key: &PublicKey, contest_delay: u16, broadcaster_delayed_payment_key: &PublicKey) -> Script {
-       Builder::new().push_opcode(opcodes::all::OP_IF)
+       let res = Builder::new().push_opcode(opcodes::all::OP_IF)
                      .push_slice(&revocation_key.serialize())
                      .push_opcode(opcodes::all::OP_ELSE)
                      .push_int(contest_delay as i64)
@@ -397,7 +402,9 @@ pub fn get_revokeable_redeemscript(revocation_key: &PublicKey, contest_delay: u1
                      .push_slice(&broadcaster_delayed_payment_key.serialize())
                      .push_opcode(opcodes::all::OP_ENDIF)
                      .push_opcode(opcodes::all::OP_CHECKSIG)
-                     .into_script()
+                     .into_script();
+       debug_assert!(res.len() <= REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH);
+       res
 }
 
 #[derive(Clone, PartialEq)]
index 53e7649d19b693effba8790f66da986eb482a35e..16212f9272f98201dd40bf0406dd4af7fc959f30 100644 (file)
@@ -15,7 +15,7 @@ use chain::Watch;
 use chain::channelmonitor;
 use chain::channelmonitor::{ChannelMonitor, CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ANTI_REORG_DELAY};
 use chain::transaction::OutPoint;
-use chain::keysinterface::{ChannelKeys, KeysInterface, SpendableOutputDescriptor};
+use chain::keysinterface::{ChannelKeys, KeysInterface};
 use ln::channel::{COMMITMENT_TX_BASE_WEIGHT, COMMITMENT_TX_WEIGHT_PER_HTLC};
 use ln::channelmanager::{ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentPreimage, PaymentHash, PaymentSecret, PaymentSendFailure, BREAKDOWN_TIMEOUT};
 use ln::channel::{Channel, ChannelError};
@@ -33,12 +33,8 @@ use util::config::UserConfig;
 
 use bitcoin::hashes::sha256d::Hash as Sha256dHash;
 use bitcoin::hash_types::{Txid, BlockHash};
-use bitcoin::util::bip143;
-use bitcoin::util::address::Address;
-use bitcoin::util::bip32::{ChildNumber, ExtendedPubKey, ExtendedPrivKey};
 use bitcoin::blockdata::block::{Block, BlockHeader};
-use bitcoin::blockdata::transaction::{Transaction, TxOut, TxIn, SigHashType};
-use bitcoin::blockdata::script::{Builder, Script};
+use bitcoin::blockdata::script::Builder;
 use bitcoin::blockdata::opcodes;
 use bitcoin::blockdata::constants::genesis_block;
 use bitcoin::network::constants::Network;
@@ -4655,122 +4651,27 @@ fn test_manager_serialize_deserialize_inconsistent_monitor() {
 macro_rules! check_spendable_outputs {
        ($node: expr, $der_idx: expr, $keysinterface: expr, $chan_value: expr) => {
                {
-                       let events = $node.chain_monitor.chain_monitor.get_and_clear_pending_events();
+                       let mut events = $node.chain_monitor.chain_monitor.get_and_clear_pending_events();
                        let mut txn = Vec::new();
-                       for event in events {
+                       let mut all_outputs = Vec::new();
+                       let secp_ctx = Secp256k1::new();
+                       for event in events.drain(..) {
                                match event {
-                                       Event::SpendableOutputs { ref outputs } => {
-                                               for outp in outputs {
-                                                       match *outp {
-                                                               SpendableOutputDescriptor::StaticOutputCounterpartyPayment { ref outpoint, ref output, ref channel_keys_id, channel_value_satoshis } => {
-                                                                       assert_eq!(channel_value_satoshis, $chan_value);
-                                                                       let input = TxIn {
-                                                                               previous_output: outpoint.into_bitcoin_outpoint(),
-                                                                               script_sig: Script::new(),
-                                                                               sequence: 0,
-                                                                               witness: Vec::new(),
-                                                                       };
-                                                                       let outp = TxOut {
-                                                                               script_pubkey: Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(),
-                                                                               value: output.value,
-                                                                       };
-                                                                       let mut spend_tx = Transaction {
-                                                                               version: 2,
-                                                                               lock_time: 0,
-                                                                               input: vec![input],
-                                                                               output: vec![outp],
-                                                                       };
-                                                                       spend_tx.output[0].value -= (spend_tx.get_weight() + 2 + 1 + 73 + 35 + 3) as u64 / 4; // (Max weight + 3 (to round up)) / 4
-                                                                       let secp_ctx = Secp256k1::new();
-                                                                       let keys = $keysinterface.derive_channel_keys($chan_value, channel_keys_id);
-                                                                       let remotepubkey = keys.pubkeys().payment_point;
-                                                                       let witness_script = Address::p2pkh(&::bitcoin::PublicKey{compressed: true, key: remotepubkey}, Network::Testnet).script_pubkey();
-                                                                       let sighash = Message::from_slice(&bip143::SigHashCache::new(&spend_tx).signature_hash(0, &witness_script, output.value, SigHashType::All)[..]).unwrap();
-                                                                       let remotesig = secp_ctx.sign(&sighash, &keys.inner.payment_key);
-                                                                       spend_tx.input[0].witness.push(remotesig.serialize_der().to_vec());
-                                                                       spend_tx.input[0].witness[0].push(SigHashType::All as u8);
-                                                                       spend_tx.input[0].witness.push(remotepubkey.serialize().to_vec());
-                                                                       txn.push(spend_tx);
-                                                               },
-                                                               SpendableOutputDescriptor::DynamicOutputP2WSH { ref outpoint, ref per_commitment_point, ref to_self_delay, ref output, ref revocation_pubkey, ref channel_keys_id, channel_value_satoshis  } => {
-                                                                       assert_eq!(channel_value_satoshis, $chan_value);
-                                                                       let input = TxIn {
-                                                                               previous_output: outpoint.into_bitcoin_outpoint(),
-                                                                               script_sig: Script::new(),
-                                                                               sequence: *to_self_delay as u32,
-                                                                               witness: Vec::new(),
-                                                                       };
-                                                                       let outp = TxOut {
-                                                                               script_pubkey: Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(),
-                                                                               value: output.value,
-                                                                       };
-                                                                       let mut spend_tx = Transaction {
-                                                                               version: 2,
-                                                                               lock_time: 0,
-                                                                               input: vec![input],
-                                                                               output: vec![outp],
-                                                                       };
-                                                                       let secp_ctx = Secp256k1::new();
-                                                                       let keys = $keysinterface.derive_channel_keys($chan_value, channel_keys_id);
-                                                                       if let Ok(delayed_payment_key) = chan_utils::derive_private_key(&secp_ctx, &per_commitment_point, &keys.inner.delayed_payment_base_key) {
-
-                                                                               let delayed_payment_pubkey = PublicKey::from_secret_key(&secp_ctx, &delayed_payment_key);
-                                                                               let witness_script = chan_utils::get_revokeable_redeemscript(revocation_pubkey, *to_self_delay, &delayed_payment_pubkey);
-                                                                               spend_tx.output[0].value -= (spend_tx.get_weight() + 2 + 1 + 73 + 1 + witness_script.len() + 1 + 3) as u64 / 4; // (Max weight + 3 (to round up)) / 4
-                                                                               let sighash = Message::from_slice(&bip143::SigHashCache::new(&spend_tx).signature_hash(0, &witness_script, output.value, SigHashType::All)[..]).unwrap();
-                                                                               let local_delayedsig = secp_ctx.sign(&sighash, &delayed_payment_key);
-                                                                               spend_tx.input[0].witness.push(local_delayedsig.serialize_der().to_vec());
-                                                                               spend_tx.input[0].witness[0].push(SigHashType::All as u8);
-                                                                               spend_tx.input[0].witness.push(vec!()); //MINIMALIF
-                                                                               spend_tx.input[0].witness.push(witness_script.clone().into_bytes());
-                                                                       } else { panic!() }
-                                                                       txn.push(spend_tx);
-                                                               },
-                                                               SpendableOutputDescriptor::StaticOutput { ref outpoint, ref output } => {
-                                                                       let secp_ctx = Secp256k1::new();
-                                                                       let input = TxIn {
-                                                                               previous_output: outpoint.into_bitcoin_outpoint(),
-                                                                               script_sig: Script::new(),
-                                                                               sequence: 0,
-                                                                               witness: Vec::new(),
-                                                                       };
-                                                                       let outp = TxOut {
-                                                                               script_pubkey: Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(),
-                                                                               value: output.value,
-                                                                       };
-                                                                       let mut spend_tx = Transaction {
-                                                                               version: 2,
-                                                                               lock_time: 0,
-                                                                               input: vec![input],
-                                                                               output: vec![outp.clone()],
-                                                                       };
-                                                                       spend_tx.output[0].value -= (spend_tx.get_weight() + 2 + 1 + 73 + 35 + 3) as u64 / 4; // (Max weight + 3 (to round up)) / 4
-                                                                       let secret = {
-                                                                               match ExtendedPrivKey::new_master(Network::Testnet, &$node.node_seed) {
-                                                                                       Ok(master_key) => {
-                                                                                               match master_key.ckd_priv(&secp_ctx, ChildNumber::from_hardened_idx($der_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;
-                                                                       let witness_script = Address::p2pkh(&pubkey, Network::Testnet).script_pubkey();
-                                                                       let sighash = Message::from_slice(&bip143::SigHashCache::new(&spend_tx).signature_hash(0, &witness_script, output.value, SigHashType::All)[..]).unwrap();
-                                                                       let sig = secp_ctx.sign(&sighash, &secret.private_key.key);
-                                                                       spend_tx.input[0].witness.push(sig.serialize_der().to_vec());
-                                                                       spend_tx.input[0].witness[0].push(SigHashType::All as u8);
-                                                                       spend_tx.input[0].witness.push(pubkey.key.serialize().to_vec());
-                                                                       txn.push(spend_tx);
-                                                               },
-                                                       }
+                                       Event::SpendableOutputs { mut outputs } => {
+                                               for outp in outputs.drain(..) {
+                                                       let mut outputs = vec![outp];
+                                                       txn.push($keysinterface.backing.spend_spendable_outputs(&outputs, Vec::new(), Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, &secp_ctx).unwrap());
+                                                       all_outputs.push(outputs.pop().unwrap());
                                                }
                                        },
                                        _ => panic!("Unexpected event"),
                                };
                        }
+                       if all_outputs.len() > 1 {
+                               if let Ok(tx) = $keysinterface.backing.spend_spendable_outputs(&all_outputs, Vec::new(), Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, &secp_ctx) {
+                                       txn.push(tx);
+                               }
+                       }
                        txn
                }
        }
@@ -4860,9 +4761,10 @@ fn test_claim_on_remote_revoked_sizeable_push_msat() {
        connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1, 1, true, header.block_hash());
 
        let spend_txn = check_spendable_outputs!(nodes[1], 1, node_cfgs[1].keys_manager, 100000);
-       assert_eq!(spend_txn.len(), 2);
+       assert_eq!(spend_txn.len(), 3);
        check_spends!(spend_txn[0], revoked_local_txn[0]); // to_remote output on revoked remote commitment_tx
        check_spends!(spend_txn[1], node_txn[0]);
+       check_spends!(spend_txn[2], revoked_local_txn[0], node_txn[0]); // Both outputs
 }
 
 #[test]
@@ -4957,8 +4859,10 @@ fn test_static_spendable_outputs_timeout_tx() {
        expect_payment_failed!(nodes[1], our_payment_hash, true);
 
        let spend_txn = check_spendable_outputs!(nodes[1], 1, node_cfgs[1].keys_manager, 100000);
-       assert_eq!(spend_txn.len(), 2); // SpendableOutput: remote_commitment_tx.to_remote, timeout_tx.output
+       assert_eq!(spend_txn.len(), 3); // SpendableOutput: remote_commitment_tx.to_remote, timeout_tx.output
+       check_spends!(spend_txn[0], commitment_tx[0]);
        check_spends!(spend_txn[1], node_txn[0]);
+       check_spends!(spend_txn[2], node_txn[0], commitment_tx[0]); // All outputs
 }
 
 #[test]
@@ -5135,11 +5039,12 @@ fn test_static_spendable_outputs_justice_tx_revoked_htlc_success_tx() {
 
        // Check A's ChannelMonitor was able to generate the right spendable output descriptor
        let spend_txn = check_spendable_outputs!(nodes[0], 1, node_cfgs[0].keys_manager, 100000);
-       assert_eq!(spend_txn.len(), 2);
+       assert_eq!(spend_txn.len(), 3);
        assert_eq!(spend_txn[0].input.len(), 1);
        check_spends!(spend_txn[0], revoked_local_txn[0]); // spending to_remote output from revoked local tx
        assert_ne!(spend_txn[0].input[0].previous_output, revoked_htlc_txn[0].input[0].previous_output);
        check_spends!(spend_txn[1], node_txn[1]); // spending justice tx output on the htlc success tx
+       check_spends!(spend_txn[2], revoked_local_txn[0], node_txn[1]); // Both outputs
 }
 
 #[test]
@@ -5372,6 +5277,7 @@ fn test_dynamic_spendable_outputs_local_htlc_success_tx() {
 
        let payment_preimage = route_payment(&nodes[0], &vec!(&nodes[1])[..], 9000000).0;
        let local_txn = get_local_commitment_txn!(nodes[1], chan_1.2);
+       assert_eq!(local_txn.len(), 1);
        assert_eq!(local_txn[0].input.len(), 1);
        check_spends!(local_txn[0], chan_1.3);
 
@@ -5392,10 +5298,13 @@ fn test_dynamic_spendable_outputs_local_htlc_success_tx() {
        }
        let node_txn = {
                let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
+               assert_eq!(node_txn.len(), 3);
+               assert_eq!(node_txn[0], node_txn[2]);
+               assert_eq!(node_txn[1], local_txn[0]);
                assert_eq!(node_txn[0].input.len(), 1);
                assert_eq!(node_txn[0].input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
                check_spends!(node_txn[0], local_txn[0]);
-               vec![node_txn[0].clone(), node_txn[2].clone()]
+               vec![node_txn[0].clone()]
        };
 
        let header_201 = BlockHeader { version: 0x20000000, prev_blockhash: header.block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
@@ -5404,9 +5313,8 @@ fn test_dynamic_spendable_outputs_local_htlc_success_tx() {
 
        // Verify that B is able to spend its own HTLC-Success tx thanks to spendable output event given back by its ChannelMonitor
        let spend_txn = check_spendable_outputs!(nodes[1], 1, node_cfgs[1].keys_manager, 100000);
-       assert_eq!(spend_txn.len(), 2);
+       assert_eq!(spend_txn.len(), 1);
        check_spends!(spend_txn[0], node_txn[0]);
-       check_spends!(spend_txn[1], node_txn[1]);
 }
 
 fn do_test_fail_backwards_unrevoked_remote_announce(deliver_last_raa: bool, announce_latest: bool) {
@@ -5701,9 +5609,10 @@ fn test_dynamic_spendable_outputs_local_htlc_timeout_tx() {
 
        // Verify that A is able to spend its own HTLC-Timeout tx thanks to spendable output event given back by its ChannelMonitor
        let spend_txn = check_spendable_outputs!(nodes[0], 1, node_cfgs[0].keys_manager, 100000);
-       assert_eq!(spend_txn.len(), 2);
+       assert_eq!(spend_txn.len(), 3);
        check_spends!(spend_txn[0], local_txn[0]);
        check_spends!(spend_txn[1], htlc_timeout);
+       check_spends!(spend_txn[2], local_txn[0], htlc_timeout);
 }
 
 #[test]
@@ -5771,9 +5680,10 @@ fn test_key_derivation_params() {
        // Verify that A is able to spend its own HTLC-Timeout tx thanks to spendable output event given back by its ChannelMonitor
        let new_keys_manager = test_utils::TestKeysInterface::new(&seed, Network::Testnet);
        let spend_txn = check_spendable_outputs!(nodes[0], 1, new_keys_manager, 100000);
-       assert_eq!(spend_txn.len(), 2);
+       assert_eq!(spend_txn.len(), 3);
        check_spends!(spend_txn[0], local_txn_1[0]);
        check_spends!(spend_txn[1], htlc_timeout);
+       check_spends!(spend_txn[2], local_txn_1[0], htlc_timeout);
 }
 
 #[test]
index e667c4d726c5f2075907d9c9f4c2ea465fb8e137..2065f404509721187f4118ad83dbd6c947a22d68 100644 (file)
@@ -138,11 +138,11 @@ impl<'a> std::fmt::Display for DebugSpendable<'a> {
                        &SpendableOutputDescriptor::StaticOutput { ref outpoint, .. } => {
                                write!(f, "StaticOutput {}:{} marked for spending", outpoint.txid, outpoint.index)?;
                        }
-                       &SpendableOutputDescriptor::DynamicOutputP2WSH { ref outpoint, .. } => {
-                               write!(f, "DynamicOutputP2WSH {}:{} marked for spending", outpoint.txid, outpoint.index)?;
+                       &SpendableOutputDescriptor::DelayedPaymentOutput(ref descriptor) => {
+                               write!(f, "DelayedPaymentOutput {}:{} marked for spending", descriptor.outpoint.txid, descriptor.outpoint.index)?;
                        }
-                       &SpendableOutputDescriptor::StaticOutputCounterpartyPayment { ref outpoint, .. } => {
-                               write!(f, "DynamicOutputP2WPKH {}:{} marked for spending", outpoint.txid, outpoint.index)?;
+                       &SpendableOutputDescriptor::StaticPaymentOutput(ref descriptor) => {
+                               write!(f, "DynamicOutputP2WPKH {}:{} marked for spending", descriptor.outpoint.txid, descriptor.outpoint.index)?;
                        }
                }
                Ok(())
index b758f5c65af5a15e168f27a3eaf0c9cc8196b16e..74cc13cc0b1c1f100f88abcb26554e96d2b13eed 100644 (file)
@@ -419,7 +419,7 @@ impl Logger for TestLogger {
 }
 
 pub struct TestKeysInterface {
-       backing: keysinterface::KeysManager,
+       pub backing: keysinterface::KeysManager,
        pub override_session_priv: Mutex<Option<[u8; 32]>>,
        pub override_channel_id_priv: Mutex<Option<[u8; 32]>>,
        pub disable_revocation_policy_check: bool,
@@ -475,7 +475,7 @@ impl TestKeysInterface {
        pub fn new(seed: &[u8; 32], network: Network) -> Self {
                let now = Duration::from_secs(genesis_block(network).header.time as u64);
                Self {
-                       backing: keysinterface::KeysManager::new(seed, network, now.as_secs(), now.subsec_nanos()),
+                       backing: keysinterface::KeysManager::new(seed, now.as_secs(), now.subsec_nanos()),
                        override_session_priv: Mutex::new(None),
                        override_channel_id_priv: Mutex::new(None),
                        disable_revocation_policy_check: false,
index 64ea801c62527ebac914bc7681dbf6b3ceb5173f..1984b480586a7c1da99254c4cdf9413b9964720f 100644 (file)
@@ -7,7 +7,12 @@
 // You may not use this file except in accordance with one or both of these
 // licenses.
 
-use bitcoin::blockdata::transaction::TxOut;
+use bitcoin::blockdata::transaction::{Transaction, TxOut};
+use bitcoin::blockdata::script::Script;
+use bitcoin::consensus::Encodable;
+use bitcoin::consensus::encode::VarInt;
+
+use ln::msgs::MAX_VALUE_MSAT;
 
 use std::cmp::Ordering;
 
@@ -21,12 +26,60 @@ pub fn sort_outputs<T, C : Fn(&T, &T) -> Ordering>(outputs: &mut Vec<(TxOut, T)>
        });
 }
 
+fn get_dust_value(output_script: &Script) -> u64 {
+       //TODO: This belongs in rust-bitcoin (https://github.com/rust-bitcoin/rust-bitcoin/pull/566)
+       if output_script.is_op_return() {
+               0
+       } else if output_script.is_witness_program() {
+               294
+       } else {
+               546
+       }
+}
+
+/// Possibly adds a change output to the given transaction, always doing so if there are excess
+/// funds available beyond the requested feerate.
+/// Assumes at least one input will have a witness (ie spends a segwit output).
+/// Returns an Err(()) if the requested feerate cannot be met.
+pub(crate) fn maybe_add_change_output(tx: &mut Transaction, input_value: u64, witness_max_weight: usize, feerate_sat_per_1000_weight: u32, change_destination_script: Script) -> Result<(), ()> {
+       if input_value > MAX_VALUE_MSAT / 1000 { return Err(()); }
+
+       let mut output_value = 0;
+       for output in tx.output.iter() {
+               output_value += output.value;
+               if output_value >= input_value { return Err(()); }
+       }
+
+       let dust_value = get_dust_value(&change_destination_script);
+       let mut change_output = TxOut {
+               script_pubkey: change_destination_script,
+               value: 0,
+       };
+       let change_len = change_output.consensus_encode(&mut std::io::sink()).unwrap();
+       let mut weight_with_change: i64 = tx.get_weight() as i64 + 2 + witness_max_weight as i64 + change_len as i64 * 4;
+       // Include any extra bytes required to push an extra output.
+       weight_with_change += (VarInt(tx.output.len() as u64 + 1).len() - VarInt(tx.output.len() as u64).len()) as i64 * 4;
+       // When calculating weight, add two for the flag bytes
+       let change_value: i64 = (input_value - output_value) as i64 - weight_with_change * feerate_sat_per_1000_weight as i64 / 1000;
+       if change_value >= dust_value as i64 {
+               change_output.value = change_value as u64;
+               tx.output.push(change_output);
+       } else if (input_value - output_value) as i64 - (tx.get_weight() as i64 + 2 + witness_max_weight as i64) * feerate_sat_per_1000_weight as i64 / 1000 < 0 {
+               return Err(());
+       }
+
+       Ok(())
+}
+
 #[cfg(test)]
 mod tests {
        use super::*;
 
+       use bitcoin::blockdata::transaction::{Transaction, TxOut, TxIn, OutPoint};
        use bitcoin::blockdata::script::{Script, Builder};
-       use bitcoin::blockdata::transaction::TxOut;
+       use bitcoin::hash_types::Txid;
+
+       use bitcoin::hashes::sha256d::Hash as Sha256dHash;
 
        use hex::decode;
 
@@ -158,4 +211,78 @@ mod tests {
                bip69_txout_test_1: TXOUT1.to_vec(),
                bip69_txout_test_2: TXOUT2.to_vec(),
        }
+
+       #[test]
+       fn test_tx_value_overrun() {
+               // If we have a bogus input amount or outputs valued more than inputs, we should fail
+               let mut tx = Transaction { version: 2, lock_time: 0, input: Vec::new(), output: vec![TxOut {
+                       script_pubkey: Script::new(), value: 1000
+               }] };
+               assert!(maybe_add_change_output(&mut tx, 21_000_000_0000_0001, 0, 253, Script::new()).is_err());
+               assert!(maybe_add_change_output(&mut tx, 400, 0, 253, Script::new()).is_err());
+               assert!(maybe_add_change_output(&mut tx, 4000, 0, 253, Script::new()).is_ok());
+       }
+
+       #[test]
+       fn test_tx_change_edge() {
+               // Check that we never add dust outputs
+               let mut tx = Transaction { version: 2, lock_time: 0, input: Vec::new(), output: Vec::new() };
+               let orig_wtxid = tx.wtxid();
+               // 9 sats isn't enough to pay fee on a dummy transaction...
+               assert_eq!(tx.get_weight() as u64, 40); // ie 10 vbytes
+               assert!(maybe_add_change_output(&mut tx, 9, 0, 253, Script::new()).is_err());
+               assert_eq!(tx.wtxid(), orig_wtxid); // Failure doesn't change the transaction
+               // but 10-564 is, just not enough to add a change output...
+               assert!(maybe_add_change_output(&mut tx, 10, 0, 253, Script::new()).is_ok());
+               assert_eq!(tx.output.len(), 0);
+               assert_eq!(tx.wtxid(), orig_wtxid); // If we don't add an output, we don't change the transaction
+               assert!(maybe_add_change_output(&mut tx, 564, 0, 253, Script::new()).is_ok());
+               assert_eq!(tx.output.len(), 0);
+               assert_eq!(tx.wtxid(), orig_wtxid); // If we don't add an output, we don't change the transaction
+               // 565 is also not enough, if we anticipate 2 more weight units pushing us up to the next vbyte
+               // (considering the two bytes for segwit flags)
+               assert!(maybe_add_change_output(&mut tx, 565, 2, 253, Script::new()).is_ok());
+               assert_eq!(tx.output.len(), 0);
+               assert_eq!(tx.wtxid(), orig_wtxid); // If we don't add an output, we don't change the transaction
+               // at 565 we can afford the change output at the dust limit (546)
+               assert!(maybe_add_change_output(&mut tx, 565, 0, 253, Script::new()).is_ok());
+               assert_eq!(tx.output.len(), 1);
+               assert_eq!(tx.output[0].value, 546);
+               assert_eq!(tx.output[0].script_pubkey, Script::new());
+               assert_eq!(tx.get_weight() / 4, 565-546); // New weight is exactly the fee we wanted.
+
+               tx.output.pop();
+               assert_eq!(tx.wtxid(), orig_wtxid); // The only change is the addition of one output.
+       }
+
+       #[test]
+       fn test_tx_extra_outputs() {
+               // Check that we correctly handle existing outputs
+               let mut tx = Transaction { version: 2, lock_time: 0, input: vec![TxIn {
+                       previous_output: OutPoint::new(Txid::from_hash(Sha256dHash::default()), 0), script_sig: Script::new(), witness: Vec::new(), sequence: 0,
+               }], output: vec![TxOut {
+                       script_pubkey: Builder::new().push_int(1).into_script(), value: 1000
+               }] };
+               let orig_wtxid = tx.wtxid();
+               let orig_weight = tx.get_weight();
+               assert_eq!(orig_weight / 4, 61);
+
+               // Input value of the output value + fee - 1 should fail:
+               assert!(maybe_add_change_output(&mut tx, 1000 + 61 + 100 - 1, 400, 250, Builder::new().push_int(2).into_script()).is_err());
+               assert_eq!(tx.wtxid(), orig_wtxid); // Failure doesn't change the transaction
+               // but one more input sat should succeed, without changing the transaction
+               assert!(maybe_add_change_output(&mut tx, 1000 + 61 + 100, 400, 250, Builder::new().push_int(2).into_script()).is_ok());
+               assert_eq!(tx.wtxid(), orig_wtxid); // If we don't add an output, we don't change the transaction
+               // In order to get a change output, we need to add 546 plus the output's weight / 4 (10)...
+               assert!(maybe_add_change_output(&mut tx, 1000 + 61 + 100 + 546 + 9, 400, 250, Builder::new().push_int(2).into_script()).is_ok());
+               assert_eq!(tx.wtxid(), orig_wtxid); // If we don't add an output, we don't change the transaction
+
+               assert!(maybe_add_change_output(&mut tx, 1000 + 61 + 100 + 546 + 10, 400, 250, Builder::new().push_int(2).into_script()).is_ok());
+               assert_eq!(tx.output.len(), 2);
+               assert_eq!(tx.output[1].value, 546);
+               assert_eq!(tx.output[1].script_pubkey, Builder::new().push_int(2).into_script());
+               assert_eq!(tx.get_weight() - orig_weight, 40); // Weight difference matches what we had to add above
+               tx.output.pop();
+               assert_eq!(tx.wtxid(), orig_wtxid); // The only change is the addition of one output.
+       }
 }