+ pub(crate) fn add_holder_sig(&self, funding_redeemscript: &Script, holder_sig: Signature) -> Transaction {
+ // First push the multisig dummy, note that due to BIP147 (NULLDUMMY) it must be a zero-length element.
+ let mut tx = self.inner.built.transaction.clone();
+ tx.input[0].witness.push(Vec::new());
+
+ if self.holder_sig_first {
+ tx.input[0].witness.push_bitcoin_signature(&holder_sig.serialize_der(), EcdsaSighashType::All);
+ tx.input[0].witness.push_bitcoin_signature(&self.counterparty_sig.serialize_der(), EcdsaSighashType::All);
+ } else {
+ tx.input[0].witness.push_bitcoin_signature(&self.counterparty_sig.serialize_der(), EcdsaSighashType::All);
+ tx.input[0].witness.push_bitcoin_signature(&holder_sig.serialize_der(), EcdsaSighashType::All);
+ }
+
+ tx.input[0].witness.push(funding_redeemscript.as_bytes().to_vec());
+ tx
+ }
+}
+
+/// A pre-built Bitcoin commitment transaction and its txid.
+#[derive(Clone, Debug)]
+pub struct BuiltCommitmentTransaction {
+ /// The commitment transaction
+ pub transaction: Transaction,
+ /// The txid for the commitment transaction.
+ ///
+ /// This is provided as a performance optimization, instead of calling transaction.txid()
+ /// multiple times.
+ pub txid: Txid,
+}
+
+impl_writeable_tlv_based!(BuiltCommitmentTransaction, {
+ (0, transaction, required),
+ (2, txid, required),
+});
+
+impl BuiltCommitmentTransaction {
+ /// Get the SIGHASH_ALL sighash value of the transaction.
+ ///
+ /// This can be used to verify a signature.
+ pub fn get_sighash_all(&self, funding_redeemscript: &Script, channel_value_satoshis: u64) -> Message {
+ let sighash = &sighash::SighashCache::new(&self.transaction).segwit_signature_hash(0, funding_redeemscript, channel_value_satoshis, EcdsaSighashType::All).unwrap()[..];
+ hash_to_message!(sighash)
+ }
+
+ /// Signs the counterparty's commitment transaction.
+ pub fn sign_counterparty_commitment<T: secp256k1::Signing>(&self, funding_key: &SecretKey, funding_redeemscript: &Script, channel_value_satoshis: u64, secp_ctx: &Secp256k1<T>) -> Signature {
+ let sighash = self.get_sighash_all(funding_redeemscript, channel_value_satoshis);
+ sign(secp_ctx, &sighash, funding_key)
+ }
+
+ /// Signs the holder commitment transaction because we are about to broadcast it.
+ pub fn sign_holder_commitment<T: secp256k1::Signing, ES: Deref>(
+ &self, funding_key: &SecretKey, funding_redeemscript: &Script, channel_value_satoshis: u64,
+ entropy_source: &ES, secp_ctx: &Secp256k1<T>
+ ) -> Signature where ES::Target: EntropySource {
+ let sighash = self.get_sighash_all(funding_redeemscript, channel_value_satoshis);
+ sign_with_aux_rand(secp_ctx, &sighash, funding_key, entropy_source)
+ }
+}
+
+/// This class tracks the per-transaction information needed to build a closing transaction and will
+/// actually build it and sign.
+///
+/// This class can be used inside a signer implementation to generate a signature given the relevant
+/// secret key.
+#[derive(Clone, Hash, PartialEq, Eq)]
+pub struct ClosingTransaction {
+ to_holder_value_sat: u64,
+ to_counterparty_value_sat: u64,
+ to_holder_script: Script,
+ to_counterparty_script: Script,
+ built: Transaction,
+}
+
+impl ClosingTransaction {
+ /// Construct an object of the class
+ pub fn new(
+ to_holder_value_sat: u64,
+ to_counterparty_value_sat: u64,
+ to_holder_script: Script,
+ to_counterparty_script: Script,
+ funding_outpoint: OutPoint,
+ ) -> Self {
+ let built = build_closing_transaction(
+ to_holder_value_sat, to_counterparty_value_sat,
+ to_holder_script.clone(), to_counterparty_script.clone(),
+ funding_outpoint
+ );
+ ClosingTransaction {
+ to_holder_value_sat,
+ to_counterparty_value_sat,
+ to_holder_script,
+ to_counterparty_script,
+ built
+ }
+ }
+
+ /// Trust our pre-built transaction.
+ ///
+ /// Applies a wrapper which allows access to the transaction.
+ ///
+ /// This should only be used if you fully trust the builder of this object. It should not
+ /// be used by an external signer - instead use the verify function.
+ pub fn trust(&self) -> TrustedClosingTransaction {
+ TrustedClosingTransaction { inner: self }
+ }
+
+ /// Verify our pre-built transaction.
+ ///
+ /// Applies a wrapper which allows access to the transaction.
+ ///
+ /// An external validating signer must call this method before signing
+ /// or using the built transaction.
+ pub fn verify(&self, funding_outpoint: OutPoint) -> Result<TrustedClosingTransaction, ()> {
+ let built = build_closing_transaction(
+ self.to_holder_value_sat, self.to_counterparty_value_sat,
+ self.to_holder_script.clone(), self.to_counterparty_script.clone(),
+ funding_outpoint
+ );
+ if self.built != built {
+ return Err(())
+ }
+ Ok(TrustedClosingTransaction { inner: self })
+ }
+
+ /// The value to be sent to the holder, or zero if the output will be omitted
+ pub fn to_holder_value_sat(&self) -> u64 {
+ self.to_holder_value_sat
+ }
+
+ /// The value to be sent to the counterparty, or zero if the output will be omitted
+ pub fn to_counterparty_value_sat(&self) -> u64 {
+ self.to_counterparty_value_sat
+ }
+
+ /// The destination of the holder's output
+ pub fn to_holder_script(&self) -> &Script {
+ &self.to_holder_script
+ }
+
+ /// The destination of the counterparty's output
+ pub fn to_counterparty_script(&self) -> &Script {
+ &self.to_counterparty_script
+ }
+}
+
+/// A wrapper on ClosingTransaction indicating that the built bitcoin
+/// transaction is trusted.
+///
+/// See trust() and verify() functions on CommitmentTransaction.
+///
+/// This structure implements Deref.
+pub struct TrustedClosingTransaction<'a> {
+ inner: &'a ClosingTransaction,
+}
+
+impl<'a> Deref for TrustedClosingTransaction<'a> {
+ type Target = ClosingTransaction;
+
+ fn deref(&self) -> &Self::Target { self.inner }
+}
+
+impl<'a> TrustedClosingTransaction<'a> {
+ /// The pre-built Bitcoin commitment transaction
+ pub fn built_transaction(&self) -> &'a Transaction {
+ &self.inner.built
+ }
+
+ /// Get the SIGHASH_ALL sighash value of the transaction.
+ ///
+ /// This can be used to verify a signature.
+ pub fn get_sighash_all(&self, funding_redeemscript: &Script, channel_value_satoshis: u64) -> Message {
+ let sighash = &sighash::SighashCache::new(&self.inner.built).segwit_signature_hash(0, funding_redeemscript, channel_value_satoshis, EcdsaSighashType::All).unwrap()[..];
+ hash_to_message!(sighash)
+ }
+
+ /// Sign a transaction, either because we are counter-signing the counterparty's transaction or
+ /// because we are about to broadcast a holder transaction.
+ pub fn sign<T: secp256k1::Signing>(&self, funding_key: &SecretKey, funding_redeemscript: &Script, channel_value_satoshis: u64, secp_ctx: &Secp256k1<T>) -> Signature {
+ let sighash = self.get_sighash_all(funding_redeemscript, channel_value_satoshis);
+ sign(secp_ctx, &sighash, funding_key)
+ }
+}
+
+/// This class tracks the per-transaction information needed to build a commitment transaction and will
+/// actually build it and sign. It is used for holder transactions that we sign only when needed
+/// and for transactions we sign for the counterparty.
+///
+/// This class can be used inside a signer implementation to generate a signature given the relevant
+/// secret key.
+#[derive(Clone, Debug)]
+pub struct CommitmentTransaction {
+ commitment_number: u64,
+ to_broadcaster_value_sat: u64,
+ to_countersignatory_value_sat: u64,
+ to_broadcaster_delay: Option<u16>, // Added in 0.0.117
+ feerate_per_kw: u32,
+ htlcs: Vec<HTLCOutputInCommitment>,
+ // Note that on upgrades, some features of existing outputs may be missed.
+ channel_type_features: ChannelTypeFeatures,
+ // A cache of the parties' pubkeys required to construct the transaction, see doc for trust()
+ keys: TxCreationKeys,
+ // For access to the pre-built transaction, see doc for trust()
+ built: BuiltCommitmentTransaction,
+}
+
+impl Eq for CommitmentTransaction {}
+impl PartialEq for CommitmentTransaction {
+ fn eq(&self, o: &Self) -> bool {
+ let eq = self.commitment_number == o.commitment_number &&
+ self.to_broadcaster_value_sat == o.to_broadcaster_value_sat &&
+ self.to_countersignatory_value_sat == o.to_countersignatory_value_sat &&
+ self.feerate_per_kw == o.feerate_per_kw &&
+ self.htlcs == o.htlcs &&
+ self.channel_type_features == o.channel_type_features &&
+ self.keys == o.keys;
+ if eq {
+ debug_assert_eq!(self.built.transaction, o.built.transaction);
+ debug_assert_eq!(self.built.txid, o.built.txid);
+ }
+ eq
+ }
+}
+
+impl Writeable for CommitmentTransaction {
+ fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+ let legacy_deserialization_prevention_marker = legacy_deserialization_prevention_marker_for_channel_type_features(&self.channel_type_features);
+ write_tlv_fields!(writer, {
+ (0, self.commitment_number, required),
+ (1, self.to_broadcaster_delay, option),
+ (2, self.to_broadcaster_value_sat, required),
+ (4, self.to_countersignatory_value_sat, required),
+ (6, self.feerate_per_kw, required),
+ (8, self.keys, required),
+ (10, self.built, required),
+ (12, self.htlcs, required_vec),
+ (14, legacy_deserialization_prevention_marker, option),
+ (15, self.channel_type_features, required),
+ });
+ Ok(())
+ }
+}
+
+impl Readable for CommitmentTransaction {
+ fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
+ _init_and_read_len_prefixed_tlv_fields!(reader, {
+ (0, commitment_number, required),
+ (1, to_broadcaster_delay, option),
+ (2, to_broadcaster_value_sat, required),
+ (4, to_countersignatory_value_sat, required),
+ (6, feerate_per_kw, required),
+ (8, keys, required),
+ (10, built, required),
+ (12, htlcs, required_vec),
+ (14, _legacy_deserialization_prevention_marker, option),
+ (15, channel_type_features, option),
+ });
+
+ let mut additional_features = ChannelTypeFeatures::empty();
+ additional_features.set_anchors_nonzero_fee_htlc_tx_required();
+ chain::package::verify_channel_type_features(&channel_type_features, Some(&additional_features))?;
+
+ Ok(Self {
+ commitment_number: commitment_number.0.unwrap(),
+ to_broadcaster_value_sat: to_broadcaster_value_sat.0.unwrap(),
+ to_countersignatory_value_sat: to_countersignatory_value_sat.0.unwrap(),
+ to_broadcaster_delay,
+ feerate_per_kw: feerate_per_kw.0.unwrap(),
+ keys: keys.0.unwrap(),
+ built: built.0.unwrap(),
+ htlcs,
+ channel_type_features: channel_type_features.unwrap_or(ChannelTypeFeatures::only_static_remote_key())
+ })
+ }
+}
+
+impl CommitmentTransaction {
+ /// Construct an object of the class while assigning transaction output indices to HTLCs.
+ ///
+ /// Populates HTLCOutputInCommitment.transaction_output_index in htlcs_with_aux.
+ ///
+ /// The generic T allows the caller to match the HTLC output index with auxiliary data.
+ /// This auxiliary data is not stored in this object.
+ ///
+ /// Only include HTLCs that are above the dust limit for the channel.
+ ///
+ /// This is not exported to bindings users due to the generic though we likely should expose a version without
+ pub fn new_with_auxiliary_htlc_data<T>(commitment_number: u64, to_broadcaster_value_sat: u64, to_countersignatory_value_sat: u64, broadcaster_funding_key: PublicKey, countersignatory_funding_key: PublicKey, keys: TxCreationKeys, feerate_per_kw: u32, htlcs_with_aux: &mut Vec<(HTLCOutputInCommitment, T)>, channel_parameters: &DirectedChannelTransactionParameters) -> CommitmentTransaction {
+ // Sort outputs and populate output indices while keeping track of the auxiliary data
+ let (outputs, htlcs) = Self::internal_build_outputs(&keys, to_broadcaster_value_sat, to_countersignatory_value_sat, htlcs_with_aux, channel_parameters, &broadcaster_funding_key, &countersignatory_funding_key).unwrap();
+
+ let (obscured_commitment_transaction_number, txins) = Self::internal_build_inputs(commitment_number, channel_parameters);
+ let transaction = Self::make_transaction(obscured_commitment_transaction_number, txins, outputs);
+ let txid = transaction.txid();
+ CommitmentTransaction {
+ commitment_number,
+ to_broadcaster_value_sat,
+ to_countersignatory_value_sat,
+ to_broadcaster_delay: Some(channel_parameters.contest_delay()),
+ feerate_per_kw,
+ htlcs,
+ channel_type_features: channel_parameters.channel_type_features().clone(),
+ keys,
+ built: BuiltCommitmentTransaction {
+ transaction,
+ txid
+ },
+ }
+ }
+
+ /// Use non-zero fee anchors
+ ///
+ /// This is not exported to bindings users due to move, and also not likely to be useful for binding users
+ pub fn with_non_zero_fee_anchors(mut self) -> Self {
+ self.channel_type_features.set_anchors_nonzero_fee_htlc_tx_required();
+ self
+ }
+
+ fn internal_rebuild_transaction(&self, keys: &TxCreationKeys, channel_parameters: &DirectedChannelTransactionParameters, broadcaster_funding_key: &PublicKey, countersignatory_funding_key: &PublicKey) -> Result<BuiltCommitmentTransaction, ()> {
+ let (obscured_commitment_transaction_number, txins) = Self::internal_build_inputs(self.commitment_number, channel_parameters);
+
+ let mut htlcs_with_aux = self.htlcs.iter().map(|h| (h.clone(), ())).collect();
+ let (outputs, _) = Self::internal_build_outputs(keys, self.to_broadcaster_value_sat, self.to_countersignatory_value_sat, &mut htlcs_with_aux, channel_parameters, broadcaster_funding_key, countersignatory_funding_key)?;
+
+ let transaction = Self::make_transaction(obscured_commitment_transaction_number, txins, outputs);
+ let txid = transaction.txid();
+ let built_transaction = BuiltCommitmentTransaction {
+ transaction,
+ txid
+ };
+ Ok(built_transaction)
+ }
+
+ fn make_transaction(obscured_commitment_transaction_number: u64, txins: Vec<TxIn>, outputs: Vec<TxOut>) -> Transaction {
+ Transaction {
+ version: 2,
+ lock_time: PackedLockTime(((0x20 as u32) << 8 * 3) | ((obscured_commitment_transaction_number & 0xffffffu64) as u32)),
+ input: txins,
+ output: outputs,
+ }
+ }
+
+ // This is used in two cases:
+ // - initial sorting of outputs / HTLCs in the constructor, in which case T is auxiliary data the
+ // caller needs to have sorted together with the HTLCs so it can keep track of the output index
+ // - building of a bitcoin transaction during a verify() call, in which case T is just ()
+ fn internal_build_outputs<T>(keys: &TxCreationKeys, to_broadcaster_value_sat: u64, to_countersignatory_value_sat: u64, htlcs_with_aux: &mut Vec<(HTLCOutputInCommitment, T)>, channel_parameters: &DirectedChannelTransactionParameters, broadcaster_funding_key: &PublicKey, countersignatory_funding_key: &PublicKey) -> Result<(Vec<TxOut>, Vec<HTLCOutputInCommitment>), ()> {
+ let countersignatory_pubkeys = channel_parameters.countersignatory_pubkeys();
+ let contest_delay = channel_parameters.contest_delay();
+
+ let mut txouts: Vec<(TxOut, Option<&mut HTLCOutputInCommitment>)> = Vec::new();
+
+ if to_countersignatory_value_sat > 0 {
+ let script = if channel_parameters.channel_type_features().supports_anchors_zero_fee_htlc_tx() {
+ get_to_countersignatory_with_anchors_redeemscript(&countersignatory_pubkeys.payment_point).to_v0_p2wsh()
+ } else {
+ Payload::p2wpkh(&BitcoinPublicKey::new(countersignatory_pubkeys.payment_point)).unwrap().script_pubkey()
+ };
+ txouts.push((
+ TxOut {
+ script_pubkey: script.clone(),
+ value: to_countersignatory_value_sat,
+ },
+ None,
+ ))
+ }
+
+ if to_broadcaster_value_sat > 0 {
+ let redeem_script = get_revokeable_redeemscript(
+ &keys.revocation_key,
+ contest_delay,
+ &keys.broadcaster_delayed_payment_key,
+ );
+ txouts.push((
+ TxOut {
+ script_pubkey: redeem_script.to_v0_p2wsh(),
+ value: to_broadcaster_value_sat,
+ },
+ None,
+ ));
+ }
+
+ if channel_parameters.channel_type_features().supports_anchors_zero_fee_htlc_tx() {
+ if to_broadcaster_value_sat > 0 || !htlcs_with_aux.is_empty() {
+ let anchor_script = get_anchor_redeemscript(broadcaster_funding_key);
+ txouts.push((
+ TxOut {
+ script_pubkey: anchor_script.to_v0_p2wsh(),
+ value: ANCHOR_OUTPUT_VALUE_SATOSHI,
+ },
+ None,
+ ));
+ }
+
+ if to_countersignatory_value_sat > 0 || !htlcs_with_aux.is_empty() {
+ let anchor_script = get_anchor_redeemscript(countersignatory_funding_key);
+ txouts.push((
+ TxOut {
+ script_pubkey: anchor_script.to_v0_p2wsh(),
+ value: ANCHOR_OUTPUT_VALUE_SATOSHI,
+ },
+ None,
+ ));
+ }
+ }
+
+ let mut htlcs = Vec::with_capacity(htlcs_with_aux.len());
+ for (htlc, _) in htlcs_with_aux {
+ let script = chan_utils::get_htlc_redeemscript(&htlc, &channel_parameters.channel_type_features(), &keys);
+ let txout = TxOut {
+ script_pubkey: script.to_v0_p2wsh(),
+ value: htlc.amount_msat / 1000,
+ };
+ txouts.push((txout, Some(htlc)));
+ }
+
+ // Sort output in BIP-69 order (amount, scriptPubkey). Tie-breaks based on HTLC
+ // CLTV expiration height.
+ sort_outputs(&mut txouts, |a, b| {
+ if let &Some(ref a_htlcout) = a {
+ if let &Some(ref b_htlcout) = b {
+ a_htlcout.cltv_expiry.cmp(&b_htlcout.cltv_expiry)
+ // Note that due to hash collisions, we have to have a fallback comparison
+ // here for fuzzing mode (otherwise at least chanmon_fail_consistency
+ // may fail)!
+ .then(a_htlcout.payment_hash.0.cmp(&b_htlcout.payment_hash.0))
+ // For non-HTLC outputs, if they're copying our SPK we don't really care if we
+ // close the channel due to mismatches - they're doing something dumb:
+ } else { cmp::Ordering::Equal }
+ } else { cmp::Ordering::Equal }
+ });
+
+ let mut outputs = Vec::with_capacity(txouts.len());
+ for (idx, out) in txouts.drain(..).enumerate() {
+ if let Some(htlc) = out.1 {
+ htlc.transaction_output_index = Some(idx as u32);
+ htlcs.push(htlc.clone());
+ }
+ outputs.push(out.0);
+ }
+ Ok((outputs, htlcs))
+ }
+
+ fn internal_build_inputs(commitment_number: u64, channel_parameters: &DirectedChannelTransactionParameters) -> (u64, Vec<TxIn>) {
+ let broadcaster_pubkeys = channel_parameters.broadcaster_pubkeys();
+ let countersignatory_pubkeys = channel_parameters.countersignatory_pubkeys();
+ let commitment_transaction_number_obscure_factor = get_commitment_transaction_number_obscure_factor(
+ &broadcaster_pubkeys.payment_point,
+ &countersignatory_pubkeys.payment_point,
+ channel_parameters.is_outbound(),
+ );
+
+ let obscured_commitment_transaction_number =
+ commitment_transaction_number_obscure_factor ^ (INITIAL_COMMITMENT_NUMBER - commitment_number);
+
+ let txins = {
+ let mut ins: Vec<TxIn> = Vec::new();
+ ins.push(TxIn {
+ previous_output: channel_parameters.funding_outpoint(),
+ script_sig: Script::new(),
+ sequence: Sequence(((0x80 as u32) << 8 * 3)
+ | ((obscured_commitment_transaction_number >> 3 * 8) as u32)),
+ witness: Witness::new(),
+ });
+ ins
+ };
+ (obscured_commitment_transaction_number, txins)
+ }
+
+ /// The backwards-counting commitment number
+ pub fn commitment_number(&self) -> u64 {
+ self.commitment_number
+ }
+
+ /// The value to be sent to the broadcaster
+ pub fn to_broadcaster_value_sat(&self) -> u64 {
+ self.to_broadcaster_value_sat
+ }
+
+ /// The value to be sent to the counterparty
+ pub fn to_countersignatory_value_sat(&self) -> u64 {
+ self.to_countersignatory_value_sat
+ }
+
+ /// The feerate paid per 1000-weight-unit in this commitment transaction.
+ pub fn feerate_per_kw(&self) -> u32 {
+ self.feerate_per_kw
+ }
+
+ /// The non-dust HTLCs (direction, amt, height expiration, hash, transaction output index)
+ /// which were included in this commitment transaction in output order.
+ /// The transaction index is always populated.
+ ///
+ /// This is not exported to bindings users as we cannot currently convert Vec references to/from C, though we should
+ /// expose a less effecient version which creates a Vec of references in the future.
+ pub fn htlcs(&self) -> &Vec<HTLCOutputInCommitment> {
+ &self.htlcs
+ }
+
+ /// Trust our pre-built transaction and derived transaction creation public keys.
+ ///
+ /// Applies a wrapper which allows access to these fields.
+ ///
+ /// This should only be used if you fully trust the builder of this object. It should not
+ /// be used by an external signer - instead use the verify function.
+ pub fn trust(&self) -> TrustedCommitmentTransaction {
+ TrustedCommitmentTransaction { inner: self }
+ }
+
+ /// Verify our pre-built transaction and derived transaction creation public keys.
+ ///
+ /// Applies a wrapper which allows access to these fields.
+ ///
+ /// An external validating signer must call this method before signing
+ /// or using the built transaction.
+ pub fn verify<T: secp256k1::Signing + secp256k1::Verification>(&self, channel_parameters: &DirectedChannelTransactionParameters, broadcaster_keys: &ChannelPublicKeys, countersignatory_keys: &ChannelPublicKeys, secp_ctx: &Secp256k1<T>) -> Result<TrustedCommitmentTransaction, ()> {
+ // This is the only field of the key cache that we trust
+ let per_commitment_point = self.keys.per_commitment_point;
+ let keys = TxCreationKeys::from_channel_static_keys(&per_commitment_point, broadcaster_keys, countersignatory_keys, secp_ctx);
+ if keys != self.keys {
+ return Err(());
+ }
+ let tx = self.internal_rebuild_transaction(&keys, channel_parameters, &broadcaster_keys.funding_pubkey, &countersignatory_keys.funding_pubkey)?;
+ if self.built.transaction != tx.transaction || self.built.txid != tx.txid {
+ return Err(());
+ }
+ Ok(TrustedCommitmentTransaction { inner: self })
+ }
+}
+
+/// A wrapper on CommitmentTransaction indicating that the derived fields (the built bitcoin
+/// transaction and the transaction creation keys) are trusted.
+///
+/// See trust() and verify() functions on CommitmentTransaction.
+///
+/// This structure implements Deref.
+pub struct TrustedCommitmentTransaction<'a> {
+ inner: &'a CommitmentTransaction,
+}
+
+impl<'a> Deref for TrustedCommitmentTransaction<'a> {
+ type Target = CommitmentTransaction;
+
+ fn deref(&self) -> &Self::Target { self.inner }
+}
+
+impl<'a> TrustedCommitmentTransaction<'a> {
+ /// The transaction ID of the built Bitcoin transaction
+ pub fn txid(&self) -> Txid {
+ self.inner.built.txid
+ }
+
+ /// The pre-built Bitcoin commitment transaction
+ pub fn built_transaction(&self) -> &'a BuiltCommitmentTransaction {
+ &self.inner.built
+ }
+
+ /// The pre-calculated transaction creation public keys.
+ pub fn keys(&self) -> &'a TxCreationKeys {
+ &self.inner.keys
+ }
+
+ /// Should anchors be used.
+ pub fn channel_type_features(&self) -> &'a ChannelTypeFeatures {
+ &self.inner.channel_type_features
+ }
+
+ /// Get a signature for each HTLC which was included in the commitment transaction (ie for
+ /// which HTLCOutputInCommitment::transaction_output_index.is_some()).
+ ///
+ /// The returned Vec has one entry for each HTLC, and in the same order.
+ ///
+ /// This function is only valid in the holder commitment context, it always uses EcdsaSighashType::All.
+ pub fn get_htlc_sigs<T: secp256k1::Signing, ES: Deref>(
+ &self, htlc_base_key: &SecretKey, channel_parameters: &DirectedChannelTransactionParameters,
+ entropy_source: &ES, secp_ctx: &Secp256k1<T>,
+ ) -> Result<Vec<Signature>, ()> where ES::Target: EntropySource {
+ let inner = self.inner;
+ let keys = &inner.keys;
+ let txid = inner.built.txid;
+ let mut ret = Vec::with_capacity(inner.htlcs.len());
+ let holder_htlc_key = derive_private_key(secp_ctx, &inner.keys.per_commitment_point, htlc_base_key);
+
+ for this_htlc in inner.htlcs.iter() {
+ assert!(this_htlc.transaction_output_index.is_some());
+ let htlc_tx = build_htlc_transaction(&txid, inner.feerate_per_kw, channel_parameters.contest_delay(), &this_htlc, &self.channel_type_features, &keys.broadcaster_delayed_payment_key, &keys.revocation_key);
+
+ let htlc_redeemscript = get_htlc_redeemscript_with_explicit_keys(&this_htlc, &self.channel_type_features, &keys.broadcaster_htlc_key, &keys.countersignatory_htlc_key, &keys.revocation_key);
+
+ let sighash = hash_to_message!(&sighash::SighashCache::new(&htlc_tx).segwit_signature_hash(0, &htlc_redeemscript, this_htlc.amount_msat / 1000, EcdsaSighashType::All).unwrap()[..]);
+ ret.push(sign_with_aux_rand(secp_ctx, &sighash, &holder_htlc_key, entropy_source));
+ }
+ Ok(ret)
+ }
+
+ /// Gets a signed HTLC transaction given a preimage (for !htlc.offered) and the holder HTLC transaction signature.
+ pub(crate) fn get_signed_htlc_tx(&self, channel_parameters: &DirectedChannelTransactionParameters, htlc_index: usize, counterparty_signature: &Signature, signature: &Signature, preimage: &Option<PaymentPreimage>) -> Transaction {
+ let inner = self.inner;
+ let keys = &inner.keys;
+ let txid = inner.built.txid;
+ let this_htlc = &inner.htlcs[htlc_index];
+ assert!(this_htlc.transaction_output_index.is_some());
+ // if we don't have preimage for an HTLC-Success, we can't generate an HTLC transaction.
+ if !this_htlc.offered && preimage.is_none() { unreachable!(); }
+ // Further, we should never be provided the preimage for an HTLC-Timeout transaction.
+ if this_htlc.offered && preimage.is_some() { unreachable!(); }
+
+ let mut htlc_tx = build_htlc_transaction(&txid, inner.feerate_per_kw, channel_parameters.contest_delay(), &this_htlc, &self.channel_type_features, &keys.broadcaster_delayed_payment_key, &keys.revocation_key);
+
+ let htlc_redeemscript = get_htlc_redeemscript_with_explicit_keys(&this_htlc, &self.channel_type_features, &keys.broadcaster_htlc_key, &keys.countersignatory_htlc_key, &keys.revocation_key);
+
+ htlc_tx.input[0].witness = chan_utils::build_htlc_input_witness(
+ signature, counterparty_signature, preimage, &htlc_redeemscript, &self.channel_type_features,
+ );
+ htlc_tx
+ }
+
+ /// Returns the index of the revokeable output, i.e. the `to_local` output sending funds to
+ /// the broadcaster, in the built transaction, if any exists.
+ ///
+ /// There are two cases where this may return `None`:
+ /// - The balance of the revokeable output is below the dust limit (only found on commitments
+ /// early in the channel's lifetime, i.e. before the channel reserve is met).
+ /// - This commitment was created before LDK 0.0.117. In this case, the
+ /// commitment transaction previously didn't contain enough information to locate the
+ /// revokeable output.
+ pub fn revokeable_output_index(&self) -> Option<usize> {
+ let revokeable_redeemscript = get_revokeable_redeemscript(
+ &self.keys.revocation_key,
+ self.to_broadcaster_delay?,
+ &self.keys.broadcaster_delayed_payment_key,
+ );
+ let revokeable_p2wsh = revokeable_redeemscript.to_v0_p2wsh();
+ let outputs = &self.inner.built.transaction.output;
+ outputs.iter().enumerate()
+ .find(|(_, out)| out.script_pubkey == revokeable_p2wsh)
+ .map(|(idx, _)| idx)
+ }
+
+ /// Helper method to build an unsigned justice transaction spending the revokeable
+ /// `to_local` output to a destination script. Fee estimation accounts for the expected
+ /// revocation witness data that will be added when signed.
+ ///
+ /// This method will error if the given fee rate results in a fee greater than the value
+ /// of the output being spent, or if there exists no revokeable `to_local` output on this
+ /// commitment transaction. See [`Self::revokeable_output_index`] for more details.
+ ///
+ /// The built transaction will allow fee bumping with RBF, and this method takes
+ /// `feerate_per_kw` as an input such that multiple copies of a justice transaction at different
+ /// fee rates may be built.
+ pub fn build_to_local_justice_tx(&self, feerate_per_kw: u64, destination_script: Script)
+ -> Result<Transaction, ()> {
+ let output_idx = self.revokeable_output_index().ok_or(())?;
+ let input = vec![TxIn {
+ previous_output: OutPoint {
+ txid: self.trust().txid(),
+ vout: output_idx as u32,
+ },
+ script_sig: Script::new(),
+ sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
+ witness: Witness::new(),
+ }];
+ let value = self.inner.built.transaction.output[output_idx].value;
+ let output = vec![TxOut {
+ script_pubkey: destination_script,
+ value,
+ }];
+ let mut justice_tx = Transaction {
+ version: 2,
+ lock_time: PackedLockTime::ZERO,
+ input,
+ output,
+ };
+ let weight = justice_tx.weight() as u64 + WEIGHT_REVOKED_OUTPUT;
+ let fee = fee_for_weight(feerate_per_kw as u32, weight);
+ justice_tx.output[0].value = value.checked_sub(fee).ok_or(())?;
+ Ok(justice_tx)
+ }
+
+}
+
+/// Commitment transaction numbers which appear in the transactions themselves are XOR'd with a
+/// shared secret first. This prevents on-chain observers from discovering how many commitment
+/// transactions occurred in a channel before it was closed.
+///
+/// This function gets the shared secret from relevant channel public keys and can be used to
+/// "decrypt" the commitment transaction number given a commitment transaction on-chain.
+pub fn get_commitment_transaction_number_obscure_factor(
+ broadcaster_payment_basepoint: &PublicKey,
+ countersignatory_payment_basepoint: &PublicKey,
+ outbound_from_broadcaster: bool,
+) -> u64 {
+ let mut sha = Sha256::engine();
+
+ if outbound_from_broadcaster {
+ sha.input(&broadcaster_payment_basepoint.serialize());
+ sha.input(&countersignatory_payment_basepoint.serialize());
+ } else {
+ sha.input(&countersignatory_payment_basepoint.serialize());
+ sha.input(&broadcaster_payment_basepoint.serialize());
+ }
+ let res = Sha256::from_engine(sha).into_inner();
+
+ ((res[26] as u64) << 5 * 8)
+ | ((res[27] as u64) << 4 * 8)
+ | ((res[28] as u64) << 3 * 8)
+ | ((res[29] as u64) << 2 * 8)
+ | ((res[30] as u64) << 1 * 8)
+ | ((res[31] as u64) << 0 * 8)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::{CounterpartyCommitmentSecrets, ChannelPublicKeys};
+ use crate::{hex, chain};
+ use crate::prelude::*;
+ use crate::ln::chan_utils::{get_htlc_redeemscript, get_to_countersignatory_with_anchors_redeemscript, CommitmentTransaction, TxCreationKeys, ChannelTransactionParameters, CounterpartyChannelTransactionParameters, HTLCOutputInCommitment};
+ use bitcoin::secp256k1::{PublicKey, SecretKey, Secp256k1};
+ use crate::util::test_utils;
+ use crate::sign::{ChannelSigner, SignerProvider};
+ use bitcoin::{Network, Txid, Script};
+ use bitcoin::hashes::Hash;
+ use crate::ln::PaymentHash;
+ use bitcoin::hashes::hex::ToHex;
+ use bitcoin::util::address::Payload;
+ use bitcoin::PublicKey as BitcoinPublicKey;
+ use crate::ln::features::ChannelTypeFeatures;
+
+ struct TestCommitmentTxBuilder {
+ commitment_number: u64,
+ holder_funding_pubkey: PublicKey,
+ counterparty_funding_pubkey: PublicKey,
+ keys: TxCreationKeys,
+ feerate_per_kw: u32,
+ htlcs_with_aux: Vec<(HTLCOutputInCommitment, ())>,
+ channel_parameters: ChannelTransactionParameters,
+ counterparty_pubkeys: ChannelPublicKeys,
+ }
+
+ impl TestCommitmentTxBuilder {
+ fn new() -> Self {
+ let secp_ctx = Secp256k1::new();
+ let seed = [42; 32];
+ let network = Network::Testnet;
+ let keys_provider = test_utils::TestKeysInterface::new(&seed, network);
+ let signer = keys_provider.derive_channel_signer(3000, keys_provider.generate_channel_keys_id(false, 1_000_000, 0));
+ let counterparty_signer = keys_provider.derive_channel_signer(3000, keys_provider.generate_channel_keys_id(true, 1_000_000, 1));
+ let delayed_payment_base = &signer.pubkeys().delayed_payment_basepoint;
+ let per_commitment_secret = SecretKey::from_slice(&hex::decode("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100").unwrap()[..]).unwrap();
+ let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret);
+ let htlc_basepoint = &signer.pubkeys().htlc_basepoint;
+ let holder_pubkeys = signer.pubkeys();
+ let counterparty_pubkeys = counterparty_signer.pubkeys().clone();
+ let keys = TxCreationKeys::derive_new(&secp_ctx, &per_commitment_point, delayed_payment_base, htlc_basepoint, &counterparty_pubkeys.revocation_basepoint, &counterparty_pubkeys.htlc_basepoint);
+ let channel_parameters = ChannelTransactionParameters {
+ holder_pubkeys: holder_pubkeys.clone(),
+ holder_selected_contest_delay: 0,
+ is_outbound_from_holder: false,
+ counterparty_parameters: Some(CounterpartyChannelTransactionParameters { pubkeys: counterparty_pubkeys.clone(), selected_contest_delay: 0 }),
+ funding_outpoint: Some(chain::transaction::OutPoint { txid: Txid::all_zeros(), index: 0 }),
+ channel_type_features: ChannelTypeFeatures::only_static_remote_key(),
+ };
+ let htlcs_with_aux = Vec::new();
+
+ Self {
+ commitment_number: 0,
+ holder_funding_pubkey: holder_pubkeys.funding_pubkey,
+ counterparty_funding_pubkey: counterparty_pubkeys.funding_pubkey,
+ keys,
+ feerate_per_kw: 1,
+ htlcs_with_aux,
+ channel_parameters,
+ counterparty_pubkeys,
+ }
+ }
+
+ fn build(&mut self, to_broadcaster_sats: u64, to_countersignatory_sats: u64) -> CommitmentTransaction {
+ CommitmentTransaction::new_with_auxiliary_htlc_data(
+ self.commitment_number, to_broadcaster_sats, to_countersignatory_sats,
+ self.holder_funding_pubkey.clone(),
+ self.counterparty_funding_pubkey.clone(),
+ self.keys.clone(), self.feerate_per_kw,
+ &mut self.htlcs_with_aux, &self.channel_parameters.as_holder_broadcastable()
+ )
+ }
+ }
+
+ #[test]
+ fn test_anchors() {
+ let mut builder = TestCommitmentTxBuilder::new();
+
+ // Generate broadcaster and counterparty outputs
+ let tx = builder.build(1000, 2000);
+ assert_eq!(tx.built.transaction.output.len(), 2);
+ assert_eq!(tx.built.transaction.output[1].script_pubkey, Payload::p2wpkh(&BitcoinPublicKey::new(builder.counterparty_pubkeys.payment_point)).unwrap().script_pubkey());
+
+ // Generate broadcaster and counterparty outputs as well as two anchors
+ builder.channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies();
+ let tx = builder.build(1000, 2000);
+ assert_eq!(tx.built.transaction.output.len(), 4);
+ assert_eq!(tx.built.transaction.output[3].script_pubkey, get_to_countersignatory_with_anchors_redeemscript(&builder.counterparty_pubkeys.payment_point).to_v0_p2wsh());
+
+ // Generate broadcaster output and anchor
+ let tx = builder.build(3000, 0);
+ assert_eq!(tx.built.transaction.output.len(), 2);
+
+ // Generate counterparty output and anchor
+ let tx = builder.build(0, 3000);
+ assert_eq!(tx.built.transaction.output.len(), 2);
+
+ let received_htlc = HTLCOutputInCommitment {
+ offered: false,
+ amount_msat: 400000,
+ cltv_expiry: 100,
+ payment_hash: PaymentHash([42; 32]),
+ transaction_output_index: None,
+ };
+
+ let offered_htlc = HTLCOutputInCommitment {
+ offered: true,
+ amount_msat: 600000,
+ cltv_expiry: 100,
+ payment_hash: PaymentHash([43; 32]),
+ transaction_output_index: None,
+ };
+
+ // Generate broadcaster output and received and offered HTLC outputs, w/o anchors
+ builder.channel_parameters.channel_type_features = ChannelTypeFeatures::only_static_remote_key();
+ builder.htlcs_with_aux = vec![(received_htlc.clone(), ()), (offered_htlc.clone(), ())];
+ let tx = builder.build(3000, 0);
+ let keys = &builder.keys.clone();
+ assert_eq!(tx.built.transaction.output.len(), 3);
+ assert_eq!(tx.built.transaction.output[0].script_pubkey, get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_v0_p2wsh());
+ assert_eq!(tx.built.transaction.output[1].script_pubkey, get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_v0_p2wsh());
+ assert_eq!(get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_v0_p2wsh().to_hex(),
+ "0020e43a7c068553003fe68fcae424fb7b28ec5ce48cd8b6744b3945631389bad2fb");
+ assert_eq!(get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_v0_p2wsh().to_hex(),
+ "0020215d61bba56b19e9eadb6107f5a85d7f99c40f65992443f69229c290165bc00d");
+
+ // Generate broadcaster output and received and offered HTLC outputs, with anchors
+ builder.channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies();
+ builder.htlcs_with_aux = vec![(received_htlc.clone(), ()), (offered_htlc.clone(), ())];
+ let tx = builder.build(3000, 0);
+ assert_eq!(tx.built.transaction.output.len(), 5);
+ assert_eq!(tx.built.transaction.output[2].script_pubkey, get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(), &keys).to_v0_p2wsh());
+ assert_eq!(tx.built.transaction.output[3].script_pubkey, get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(), &keys).to_v0_p2wsh());
+ assert_eq!(get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(), &keys).to_v0_p2wsh().to_hex(),
+ "0020b70d0649c72b38756885c7a30908d912a7898dd5d79457a7280b8e9a20f3f2bc");
+ assert_eq!(get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(), &keys).to_v0_p2wsh().to_hex(),
+ "002087a3faeb1950a469c0e2db4a79b093a41b9526e5a6fc6ef5cb949bde3be379c7");
+ }
+
+ #[test]
+ fn test_finding_revokeable_output_index() {
+ let mut builder = TestCommitmentTxBuilder::new();
+
+ // Revokeable output present
+ let tx = builder.build(1000, 2000);
+ assert_eq!(tx.built.transaction.output.len(), 2);
+ assert_eq!(tx.trust().revokeable_output_index(), Some(0));
+
+ // Revokeable output present (but to_broadcaster_delay missing)
+ let tx = CommitmentTransaction { to_broadcaster_delay: None, ..tx };
+ assert_eq!(tx.built.transaction.output.len(), 2);
+ assert_eq!(tx.trust().revokeable_output_index(), None);
+
+ // Revokeable output not present (our balance is dust)
+ let tx = builder.build(0, 2000);
+ assert_eq!(tx.built.transaction.output.len(), 1);
+ assert_eq!(tx.trust().revokeable_output_index(), None);
+ }
+
+ #[test]
+ fn test_building_to_local_justice_tx() {
+ let mut builder = TestCommitmentTxBuilder::new();
+
+ // Revokeable output not present (our balance is dust)
+ let tx = builder.build(0, 2000);
+ assert_eq!(tx.built.transaction.output.len(), 1);
+ assert!(tx.trust().build_to_local_justice_tx(253, Script::new()).is_err());
+
+ // Revokeable output present
+ let tx = builder.build(1000, 2000);
+ assert_eq!(tx.built.transaction.output.len(), 2);
+
+ // Too high feerate
+ assert!(tx.trust().build_to_local_justice_tx(100_000, Script::new()).is_err());
+
+ // Generate a random public key for destination script
+ let secret_key = SecretKey::from_slice(
+ &hex::decode("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100")
+ .unwrap()[..]).unwrap();
+ let pubkey_hash = BitcoinPublicKey::new(
+ PublicKey::from_secret_key(&Secp256k1::new(), &secret_key)).wpubkey_hash().unwrap();
+ let destination_script = Script::new_v0_p2wpkh(&pubkey_hash);
+
+ let justice_tx = tx.trust().build_to_local_justice_tx(253, destination_script.clone()).unwrap();
+ assert_eq!(justice_tx.input.len(), 1);
+ assert_eq!(justice_tx.input[0].previous_output.txid, tx.built.transaction.txid());
+ assert_eq!(justice_tx.input[0].previous_output.vout, tx.trust().revokeable_output_index().unwrap() as u32);
+ assert!(justice_tx.input[0].sequence.is_rbf());
+
+ assert_eq!(justice_tx.output.len(), 1);
+ assert!(justice_tx.output[0].value < 1000);
+ assert_eq!(justice_tx.output[0].script_pubkey, destination_script);
+ }
+
+ #[test]
+ fn test_per_commitment_storage() {
+ // Test vectors from BOLT 3:
+ let mut secrets: Vec<[u8; 32]> = Vec::new();
+ let mut monitor;
+
+ macro_rules! test_secrets {
+ () => {
+ let mut idx = 281474976710655;
+ for secret in secrets.iter() {
+ assert_eq!(monitor.get_secret(idx).unwrap(), *secret);
+ idx -= 1;
+ }
+ assert_eq!(monitor.get_min_seen_secret(), idx + 1);
+ assert!(monitor.get_secret(idx).is_none());
+ };
+ }
+
+ {
+ // insert_secret correct sequence
+ monitor = CounterpartyCommitmentSecrets::new();
+ secrets.clear();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc").unwrap());
+ monitor.provide_secret(281474976710655, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("c7518c8ae4660ed02894df8976fa1a3659c1a8b4b5bec0c4b872abeba4cb8964").unwrap());
+ monitor.provide_secret(281474976710654, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8").unwrap());
+ monitor.provide_secret(281474976710653, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116").unwrap());
+ monitor.provide_secret(281474976710652, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("c65716add7aa98ba7acb236352d665cab17345fe45b55fb879ff80e6bd0c41dd").unwrap());
+ monitor.provide_secret(281474976710651, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("969660042a28f32d9be17344e09374b379962d03db1574df5a8a5a47e19ce3f2").unwrap());
+ monitor.provide_secret(281474976710650, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("a5a64476122ca0925fb344bdc1854c1c0a59fc614298e50a33e331980a220f32").unwrap());
+ monitor.provide_secret(281474976710649, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("05cde6323d949933f7f7b78776bcc1ea6d9b31447732e3802e1f7ac44b650e17").unwrap());
+ monitor.provide_secret(281474976710648, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+ }
+
+ {
+ // insert_secret #1 incorrect
+ monitor = CounterpartyCommitmentSecrets::new();
+ secrets.clear();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("02a40c85b6f28da08dfdbe0926c53fab2de6d28c10301f8f7c4073d5e42e3148").unwrap());
+ monitor.provide_secret(281474976710655, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("c7518c8ae4660ed02894df8976fa1a3659c1a8b4b5bec0c4b872abeba4cb8964").unwrap());
+ assert!(monitor.provide_secret(281474976710654, secrets.last().unwrap().clone()).is_err());
+ }
+
+ {
+ // insert_secret #2 incorrect (#1 derived from incorrect)
+ monitor = CounterpartyCommitmentSecrets::new();
+ secrets.clear();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("02a40c85b6f28da08dfdbe0926c53fab2de6d28c10301f8f7c4073d5e42e3148").unwrap());
+ monitor.provide_secret(281474976710655, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("dddc3a8d14fddf2b68fa8c7fbad2748274937479dd0f8930d5ebb4ab6bd866a3").unwrap());
+ monitor.provide_secret(281474976710654, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8").unwrap());
+ monitor.provide_secret(281474976710653, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116").unwrap());
+ assert!(monitor.provide_secret(281474976710652, secrets.last().unwrap().clone()).is_err());
+ }
+
+ {
+ // insert_secret #3 incorrect
+ monitor = CounterpartyCommitmentSecrets::new();
+ secrets.clear();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc").unwrap());
+ monitor.provide_secret(281474976710655, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("c7518c8ae4660ed02894df8976fa1a3659c1a8b4b5bec0c4b872abeba4cb8964").unwrap());
+ monitor.provide_secret(281474976710654, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("c51a18b13e8527e579ec56365482c62f180b7d5760b46e9477dae59e87ed423a").unwrap());
+ monitor.provide_secret(281474976710653, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116").unwrap());
+ assert!(monitor.provide_secret(281474976710652, secrets.last().unwrap().clone()).is_err());
+ }
+
+ {
+ // insert_secret #4 incorrect (1,2,3 derived from incorrect)
+ monitor = CounterpartyCommitmentSecrets::new();
+ secrets.clear();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("02a40c85b6f28da08dfdbe0926c53fab2de6d28c10301f8f7c4073d5e42e3148").unwrap());
+ monitor.provide_secret(281474976710655, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("dddc3a8d14fddf2b68fa8c7fbad2748274937479dd0f8930d5ebb4ab6bd866a3").unwrap());
+ monitor.provide_secret(281474976710654, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("c51a18b13e8527e579ec56365482c62f180b7d5760b46e9477dae59e87ed423a").unwrap());
+ monitor.provide_secret(281474976710653, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("ba65d7b0ef55a3ba300d4e87af29868f394f8f138d78a7011669c79b37b936f4").unwrap());
+ monitor.provide_secret(281474976710652, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("c65716add7aa98ba7acb236352d665cab17345fe45b55fb879ff80e6bd0c41dd").unwrap());
+ monitor.provide_secret(281474976710651, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("969660042a28f32d9be17344e09374b379962d03db1574df5a8a5a47e19ce3f2").unwrap());
+ monitor.provide_secret(281474976710650, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("a5a64476122ca0925fb344bdc1854c1c0a59fc614298e50a33e331980a220f32").unwrap());
+ monitor.provide_secret(281474976710649, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("05cde6323d949933f7f7b78776bcc1ea6d9b31447732e3802e1f7ac44b650e17").unwrap());
+ assert!(monitor.provide_secret(281474976710648, secrets.last().unwrap().clone()).is_err());
+ }
+
+ {
+ // insert_secret #5 incorrect
+ monitor = CounterpartyCommitmentSecrets::new();
+ secrets.clear();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc").unwrap());
+ monitor.provide_secret(281474976710655, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("c7518c8ae4660ed02894df8976fa1a3659c1a8b4b5bec0c4b872abeba4cb8964").unwrap());
+ monitor.provide_secret(281474976710654, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8").unwrap());
+ monitor.provide_secret(281474976710653, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116").unwrap());
+ monitor.provide_secret(281474976710652, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("631373ad5f9ef654bb3dade742d09504c567edd24320d2fcd68e3cc47e2ff6a6").unwrap());
+ monitor.provide_secret(281474976710651, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("969660042a28f32d9be17344e09374b379962d03db1574df5a8a5a47e19ce3f2").unwrap());
+ assert!(monitor.provide_secret(281474976710650, secrets.last().unwrap().clone()).is_err());
+ }
+
+ {
+ // insert_secret #6 incorrect (5 derived from incorrect)
+ monitor = CounterpartyCommitmentSecrets::new();
+ secrets.clear();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc").unwrap());
+ monitor.provide_secret(281474976710655, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("c7518c8ae4660ed02894df8976fa1a3659c1a8b4b5bec0c4b872abeba4cb8964").unwrap());
+ monitor.provide_secret(281474976710654, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8").unwrap());
+ monitor.provide_secret(281474976710653, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116").unwrap());
+ monitor.provide_secret(281474976710652, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("631373ad5f9ef654bb3dade742d09504c567edd24320d2fcd68e3cc47e2ff6a6").unwrap());
+ monitor.provide_secret(281474976710651, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("b7e76a83668bde38b373970155c868a653304308f9896692f904a23731224bb1").unwrap());
+ monitor.provide_secret(281474976710650, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("a5a64476122ca0925fb344bdc1854c1c0a59fc614298e50a33e331980a220f32").unwrap());
+ monitor.provide_secret(281474976710649, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("05cde6323d949933f7f7b78776bcc1ea6d9b31447732e3802e1f7ac44b650e17").unwrap());
+ assert!(monitor.provide_secret(281474976710648, secrets.last().unwrap().clone()).is_err());
+ }
+
+ {
+ // insert_secret #7 incorrect
+ monitor = CounterpartyCommitmentSecrets::new();
+ secrets.clear();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc").unwrap());
+ monitor.provide_secret(281474976710655, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("c7518c8ae4660ed02894df8976fa1a3659c1a8b4b5bec0c4b872abeba4cb8964").unwrap());
+ monitor.provide_secret(281474976710654, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8").unwrap());
+ monitor.provide_secret(281474976710653, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116").unwrap());
+ monitor.provide_secret(281474976710652, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("c65716add7aa98ba7acb236352d665cab17345fe45b55fb879ff80e6bd0c41dd").unwrap());
+ monitor.provide_secret(281474976710651, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("969660042a28f32d9be17344e09374b379962d03db1574df5a8a5a47e19ce3f2").unwrap());
+ monitor.provide_secret(281474976710650, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("e7971de736e01da8ed58b94c2fc216cb1dca9e326f3a96e7194fe8ea8af6c0a3").unwrap());
+ monitor.provide_secret(281474976710649, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("05cde6323d949933f7f7b78776bcc1ea6d9b31447732e3802e1f7ac44b650e17").unwrap());
+ assert!(monitor.provide_secret(281474976710648, secrets.last().unwrap().clone()).is_err());
+ }
+
+ {
+ // insert_secret #8 incorrect
+ monitor = CounterpartyCommitmentSecrets::new();
+ secrets.clear();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc").unwrap());
+ monitor.provide_secret(281474976710655, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("c7518c8ae4660ed02894df8976fa1a3659c1a8b4b5bec0c4b872abeba4cb8964").unwrap());
+ monitor.provide_secret(281474976710654, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8").unwrap());
+ monitor.provide_secret(281474976710653, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116").unwrap());
+ monitor.provide_secret(281474976710652, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("c65716add7aa98ba7acb236352d665cab17345fe45b55fb879ff80e6bd0c41dd").unwrap());
+ monitor.provide_secret(281474976710651, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("969660042a28f32d9be17344e09374b379962d03db1574df5a8a5a47e19ce3f2").unwrap());
+ monitor.provide_secret(281474976710650, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("a5a64476122ca0925fb344bdc1854c1c0a59fc614298e50a33e331980a220f32").unwrap());
+ monitor.provide_secret(281474976710649, secrets.last().unwrap().clone()).unwrap();
+ test_secrets!();
+
+ secrets.push([0; 32]);
+ secrets.last_mut().unwrap()[0..32].clone_from_slice(&hex::decode("a7efbc61aac46d34f77778bac22c8a20c6a46ca460addc49009bda875ec88fa4").unwrap());
+ assert!(monitor.provide_secret(281474976710648, secrets.last().unwrap().clone()).is_err());
+ }