use bitcoin::amount::Amount;
use bitcoin::constants::WITNESS_SCALE_FACTOR;
use bitcoin::opcodes;
use bitcoin::script::{Builder, Script, ScriptBuf};
use bitcoin::sighash;
use bitcoin::sighash::EcdsaSighashType;
use bitcoin::transaction::Version;
use bitcoin::transaction::{OutPoint, Transaction, TxIn, TxOut};
use bitcoin::{PubkeyHash, WPubkeyHash};
use bitcoin::hash_types::Txid;
use bitcoin::hashes::hash160::Hash as Hash160;
use bitcoin::hashes::ripemd160::Hash as Ripemd160;
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::{Hash, HashEngine};
use crate::chain::chaininterface::{
fee_for_weight, ConfirmationTarget, FeeEstimator, LowerBoundedFeeEstimator,
};
use crate::chain::package::WEIGHT_REVOKED_OUTPUT;
use crate::ln::msgs::DecodeError;
use crate::sign::EntropySource;
use crate::types::payment::{PaymentHash, PaymentPreimage};
use crate::util::ser::{Readable, ReadableArgs, RequiredWrapper, Writeable, Writer};
use crate::util::transaction_utils;
use bitcoin::ecdsa::Signature as BitcoinSignature;
use bitcoin::locktime::absolute::LockTime;
use bitcoin::secp256k1::{ecdsa::Signature, Message, Secp256k1};
use bitcoin::secp256k1::{PublicKey, Scalar, SecretKey};
use bitcoin::{secp256k1, Sequence, Witness};
use super::channel_keys::{
DelayedPaymentBasepoint, DelayedPaymentKey, HtlcBasepoint, HtlcKey, RevocationBasepoint,
RevocationKey,
};
use crate::chain;
use crate::crypto::utils::{sign, sign_with_aux_rand};
use crate::io;
use crate::ln::channel::{ANCHOR_OUTPUT_VALUE_SATOSHI, INITIAL_COMMITMENT_NUMBER};
use crate::types::features::ChannelTypeFeatures;
use core::cmp;
use core::ops::Deref;
#[allow(unused_imports)]
use crate::prelude::*;
pub fn max_htlcs(channel_type: &ChannelTypeFeatures) -> u16 {
if channel_type.supports_anchor_zero_fee_commitments() {
114
} else {
483
}
}
pub const OFFERED_HTLC_SCRIPT_WEIGHT: usize = 133;
pub const OFFERED_HTLC_SCRIPT_WEIGHT_KEYED_ANCHORS: usize = 136;
pub(crate) const MIN_ACCEPTED_HTLC_SCRIPT_WEIGHT: usize = 136;
pub const MAX_ACCEPTED_HTLC_SCRIPT_WEIGHT: usize = 143;
#[cfg(feature = "grind_signatures")]
pub const ANCHOR_INPUT_WITNESS_WEIGHT: u64 = 114;
#[cfg(not(feature = "grind_signatures"))]
pub const ANCHOR_INPUT_WITNESS_WEIGHT: u64 = 115;
pub const EMPTY_WITNESS_WEIGHT: u64 = 1;
pub const P2A_MAX_VALUE: u64 = 240;
pub const TRUC_MAX_WEIGHT: u64 = 10_000 * WITNESS_SCALE_FACTOR as u64;
pub const TRUC_CHILD_MAX_WEIGHT: u64 = 1000 * WITNESS_SCALE_FACTOR as u64;
pub const HTLC_TIMEOUT_INPUT_KEYED_ANCHOR_WITNESS_WEIGHT: u64 = 288;
pub const HTLC_TIMEOUT_INPUT_P2A_ANCHOR_WITNESS_WEIGHT: u64 = 285;
pub const HTLC_SUCCESS_INPUT_KEYED_ANCHOR_WITNESS_WEIGHT: u64 = 327;
pub const HTLC_SUCCESS_INPUT_P2A_ANCHOR_WITNESS_WEIGHT: u64 = 324;
const MULTISIG_SCRIPT_SIZE: u64 = 1 + 1 + crate::sign::COMPRESSED_PUBLIC_KEY_SIZE as u64 + 1 + crate::sign::COMPRESSED_PUBLIC_KEY_SIZE as u64 + 1 + 1;
pub const FUNDING_TRANSACTION_WITNESS_WEIGHT: u64 = 1 + 1 + 1 + crate::sign::MAX_STANDARD_SIGNATURE_SIZE as u64 + 1 + crate::sign::MAX_STANDARD_SIGNATURE_SIZE as u64 + 1 + MULTISIG_SCRIPT_SIZE;
pub(crate) const BASE_TX_SIZE: u64 = 4 + 1 + 1 + 4 ;
pub(crate) const SEGWIT_MARKER_FLAG_WEIGHT: u64 = 2;
pub(crate) const EMPTY_SCRIPT_SIG_WEIGHT: u64 =
1 * WITNESS_SCALE_FACTOR as u64;
pub(crate) const BASE_INPUT_SIZE: u64 = 32 + 4 + 4 ;
pub(crate) const BASE_INPUT_WEIGHT: u64 = BASE_INPUT_SIZE * WITNESS_SCALE_FACTOR as u64;
pub(crate) const P2WSH_TXOUT_WEIGHT: u64 =
(8 + 1 + 34) * WITNESS_SCALE_FACTOR as u64;
#[inline]
#[rustfmt::skip]
pub fn htlc_success_tx_weight(channel_type_features: &ChannelTypeFeatures) -> u64 {
const HTLC_SUCCESS_TX_WEIGHT: u64 = 703;
const HTLC_SUCCESS_ANCHOR_TX_WEIGHT: u64 = 706;
if channel_type_features.supports_anchors_zero_fee_htlc_tx() { HTLC_SUCCESS_ANCHOR_TX_WEIGHT } else { HTLC_SUCCESS_TX_WEIGHT }
}
pub fn aggregated_htlc_success_input_output_pair_weight(
channel_type_features: &ChannelTypeFeatures,
) -> u64 {
let satisfaction_weight = if channel_type_features.supports_anchors_zero_fee_htlc_tx() {
EMPTY_SCRIPT_SIG_WEIGHT + HTLC_SUCCESS_INPUT_KEYED_ANCHOR_WITNESS_WEIGHT
} else {
EMPTY_SCRIPT_SIG_WEIGHT + HTLC_SUCCESS_INPUT_P2A_ANCHOR_WITNESS_WEIGHT
};
BASE_INPUT_WEIGHT + P2WSH_TXOUT_WEIGHT + satisfaction_weight
}
#[inline]
#[rustfmt::skip]
pub fn htlc_timeout_tx_weight(channel_type_features: &ChannelTypeFeatures) -> u64 {
const HTLC_TIMEOUT_TX_WEIGHT: u64 = 663;
const HTLC_TIMEOUT_ANCHOR_TX_WEIGHT: u64 = 666;
if channel_type_features.supports_anchors_zero_fee_htlc_tx() { HTLC_TIMEOUT_ANCHOR_TX_WEIGHT } else { HTLC_TIMEOUT_TX_WEIGHT }
}
pub fn aggregated_htlc_timeout_input_output_pair_weight(
channel_type_features: &ChannelTypeFeatures,
) -> u64 {
let satisfaction_weight = if channel_type_features.supports_anchors_zero_fee_htlc_tx() {
EMPTY_SCRIPT_SIG_WEIGHT + HTLC_TIMEOUT_INPUT_KEYED_ANCHOR_WITNESS_WEIGHT
} else {
EMPTY_SCRIPT_SIG_WEIGHT + HTLC_TIMEOUT_INPUT_P2A_ANCHOR_WITNESS_WEIGHT
};
BASE_INPUT_WEIGHT + P2WSH_TXOUT_WEIGHT + satisfaction_weight
}
#[derive(PartialEq, Eq)]
pub enum HTLCClaim {
OfferedTimeout,
OfferedPreimage,
AcceptedTimeout,
AcceptedPreimage,
Revocation,
}
impl HTLCClaim {
#[rustfmt::skip]
pub fn from_witness(witness: &Witness) -> Option<Self> {
debug_assert_eq!(OFFERED_HTLC_SCRIPT_WEIGHT_KEYED_ANCHORS, MIN_ACCEPTED_HTLC_SCRIPT_WEIGHT);
if witness.len() < 2 {
return None;
}
let witness_script = witness.last().unwrap();
let second_to_last = witness.second_to_last().unwrap();
if witness_script.len() == OFFERED_HTLC_SCRIPT_WEIGHT {
if witness.len() == 3 && second_to_last.len() == 33 {
Some(Self::Revocation)
} else if witness.len() == 3 && second_to_last.len() == 32 {
Some(Self::OfferedPreimage)
} else if witness.len() == 5 && second_to_last.len() == 0 {
Some(Self::OfferedTimeout)
} else {
None
}
} else if witness_script.len() == OFFERED_HTLC_SCRIPT_WEIGHT_KEYED_ANCHORS {
if witness.len() == 3 && second_to_last.len() == 33 {
Some(Self::Revocation)
} else if witness.len() == 3 && second_to_last.len() == 32 {
Some(Self::OfferedPreimage)
} else if witness.len() == 5 && second_to_last.len() == 0 {
Some(Self::OfferedTimeout)
} else if witness.len() == 3 && second_to_last.len() == 0 {
Some(Self::AcceptedTimeout)
} else if witness.len() == 5 && second_to_last.len() == 32 {
Some(Self::AcceptedPreimage)
} else {
None
}
} else if witness_script.len() > MIN_ACCEPTED_HTLC_SCRIPT_WEIGHT &&
witness_script.len() <= MAX_ACCEPTED_HTLC_SCRIPT_WEIGHT {
if witness.len() == 3 && second_to_last.len() == 33 {
Some(Self::Revocation)
} else if witness.len() == 3 && second_to_last.len() == 0 {
Some(Self::AcceptedTimeout)
} else if witness.len() == 5 && second_to_last.len() == 32 {
Some(Self::AcceptedPreimage)
} else {
None
}
} else {
None
}
}
}
#[cfg(not(any(test, feature = "_test_utils")))]
const COMMITMENT_TX_WEIGHT_PER_HTLC: u64 = 172;
#[cfg(any(test, feature = "_test_utils"))]
pub const COMMITMENT_TX_WEIGHT_PER_HTLC: u64 = 172;
#[rustfmt::skip]
pub(crate) fn commitment_tx_base_weight(channel_type_features: &ChannelTypeFeatures) -> u64 {
const COMMITMENT_TX_BASE_WEIGHT: u64 = 724;
const COMMITMENT_TX_BASE_ANCHOR_WEIGHT: u64 = 1124;
if channel_type_features.supports_anchors_zero_fee_htlc_tx() { COMMITMENT_TX_BASE_ANCHOR_WEIGHT } else { COMMITMENT_TX_BASE_WEIGHT }
}
#[rustfmt::skip]
pub(crate) fn commit_tx_fee_sat(feerate_per_kw: u32, num_htlcs: usize, channel_type_features: &ChannelTypeFeatures) -> u64 {
feerate_per_kw as u64 *
(commitment_tx_base_weight(channel_type_features) +
num_htlcs as u64 * COMMITMENT_TX_WEIGHT_PER_HTLC)
/ 1000
}
pub(crate) fn second_stage_tx_fees_sat(
channel_type: &ChannelTypeFeatures, feerate_sat_per_1000_weight: u32,
) -> (u64, u64) {
if channel_type.supports_anchors_zero_fee_htlc_tx()
|| channel_type.supports_anchor_zero_fee_commitments()
{
(0, 0)
} else {
(
feerate_sat_per_1000_weight as u64 * htlc_success_tx_weight(channel_type) / 1000,
feerate_sat_per_1000_weight as u64 * htlc_timeout_tx_weight(channel_type) / 1000,
)
}
}
#[rustfmt::skip]
pub(crate) fn htlc_tx_fees_sat(feerate_per_kw: u32, num_accepted_htlcs: usize, num_offered_htlcs: usize, channel_type_features: &ChannelTypeFeatures) -> u64 {
let (htlc_success_tx_fee_sat, htlc_timeout_tx_fee_sat) = second_stage_tx_fees_sat(
channel_type_features, feerate_per_kw,
);
num_accepted_htlcs as u64 * htlc_success_tx_fee_sat + num_offered_htlcs as u64 * htlc_timeout_tx_fee_sat
}
pub(super) fn selected_commitment_sat_per_1000_weight<F: Deref>(
fee_estimator: &LowerBoundedFeeEstimator<F>, channel_type: &ChannelTypeFeatures,
) -> u32
where
F::Target: FeeEstimator,
{
if channel_type.supports_anchor_zero_fee_commitments() {
0
} else if channel_type.supports_anchors_zero_fee_htlc_tx() {
fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::AnchorChannelFee)
} else {
fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::NonAnchorChannelFee)
}
}
pub fn build_commitment_secret(commitment_seed: &[u8; 32], idx: u64) -> [u8; 32] {
let mut res: [u8; 32] = commitment_seed.clone();
for i in 0..48 {
let bitpos = 47 - i;
if idx & (1 << bitpos) == (1 << bitpos) {
res[bitpos / 8] ^= 1 << (bitpos & 7);
res = Sha256::hash(&res).to_byte_array();
}
}
res
}
#[rustfmt::skip]
pub fn build_closing_transaction(to_holder_value_sat: Amount, to_counterparty_value_sat: Amount, to_holder_script: ScriptBuf, to_counterparty_script: ScriptBuf, funding_outpoint: OutPoint) -> Transaction {
let txins = {
let ins: Vec<TxIn> = vec![TxIn {
previous_output: funding_outpoint,
script_sig: ScriptBuf::new(),
sequence: Sequence::MAX,
witness: Witness::new(),
}];
ins
};
let mut txouts: Vec<(TxOut, ())> = Vec::new();
if to_counterparty_value_sat > Amount::ZERO {
txouts.push((TxOut {
script_pubkey: to_counterparty_script,
value: to_counterparty_value_sat
}, ()));
}
if to_holder_value_sat > Amount::ZERO {
txouts.push((TxOut {
script_pubkey: to_holder_script,
value: to_holder_value_sat
}, ()));
}
transaction_utils::sort_outputs(&mut txouts, |_, _| { cmp::Ordering::Equal });
let mut outputs: Vec<TxOut> = Vec::new();
for out in txouts.drain(..) {
outputs.push(out.0);
}
Transaction {
version: Version::TWO,
lock_time: LockTime::ZERO,
input: txins,
output: outputs,
}
}
#[derive(Clone)]
pub struct CounterpartyCommitmentSecrets {
old_secrets: [([u8; 32], u64); 49],
}
impl Eq for CounterpartyCommitmentSecrets {}
impl PartialEq for CounterpartyCommitmentSecrets {
#[rustfmt::skip]
fn eq(&self, other: &Self) -> bool {
for (&(ref secret, ref idx), &(ref o_secret, ref o_idx)) in self.old_secrets.iter().zip(other.old_secrets.iter()) {
if secret != o_secret || idx != o_idx {
return false
}
}
true
}
}
impl CounterpartyCommitmentSecrets {
#[rustfmt::skip]
pub fn new() -> Self {
Self { old_secrets: [([0; 32], 1 << 48); 49], }
}
#[inline]
#[rustfmt::skip]
fn place_secret(idx: u64) -> u8 {
for i in 0..48 {
if idx & (1 << i) == (1 << i) {
return i
}
}
48
}
pub fn get_min_seen_secret(&self) -> u64 {
let mut min = 1 << 48;
for &(_, idx) in self.old_secrets.iter() {
if idx < min {
min = idx;
}
}
min
}
#[inline]
fn derive_secret(secret: [u8; 32], bits: u8, idx: u64) -> [u8; 32] {
let mut res: [u8; 32] = secret;
for i in 0..bits {
let bitpos = bits - 1 - i;
if idx & (1 << bitpos) == (1 << bitpos) {
res[(bitpos / 8) as usize] ^= 1 << (bitpos & 7);
res = Sha256::hash(&res).to_byte_array();
}
}
res
}
pub fn provide_secret(&mut self, idx: u64, secret: [u8; 32]) -> Result<(), ()> {
let pos = Self::place_secret(idx);
for i in 0..pos {
let (old_secret, old_idx) = self.old_secrets[i as usize];
if Self::derive_secret(secret, pos, old_idx) != old_secret {
return Err(());
}
}
if self.get_min_seen_secret() <= idx {
return Ok(());
}
self.old_secrets[pos as usize] = (secret, idx);
Ok(())
}
#[rustfmt::skip]
pub fn get_secret(&self, idx: u64) -> Option<[u8; 32]> {
for i in 0..self.old_secrets.len() {
if (idx & (!((1 << i) - 1))) == self.old_secrets[i].1 {
return Some(Self::derive_secret(self.old_secrets[i].0, i as u8, idx))
}
}
assert!(idx < self.get_min_seen_secret());
None
}
}
impl Writeable for CounterpartyCommitmentSecrets {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
for &(ref secret, ref idx) in self.old_secrets.iter() {
writer.write_all(secret)?;
writer.write_all(&idx.to_be_bytes())?;
}
write_tlv_fields!(writer, {});
Ok(())
}
}
impl Readable for CounterpartyCommitmentSecrets {
fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
let mut old_secrets = [([0; 32], 1 << 48); 49];
for &mut (ref mut secret, ref mut idx) in old_secrets.iter_mut() {
*secret = Readable::read(reader)?;
*idx = Readable::read(reader)?;
}
read_tlv_fields!(reader, {});
Ok(Self { old_secrets })
}
}
pub fn derive_private_key<T: secp256k1::Signing>(
secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, base_secret: &SecretKey,
) -> SecretKey {
let mut sha = Sha256::engine();
sha.input(&per_commitment_point.serialize());
sha.input(&PublicKey::from_secret_key(&secp_ctx, &base_secret).serialize());
let res = Sha256::from_engine(sha).to_byte_array();
base_secret.clone().add_tweak(&Scalar::from_be_bytes(res).unwrap())
.expect("Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak contains the hash of the key.")
}
#[rustfmt::skip]
pub fn derive_private_revocation_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>,
per_commitment_secret: &SecretKey, countersignatory_revocation_base_secret: &SecretKey)
-> SecretKey {
let countersignatory_revocation_base_point = PublicKey::from_secret_key(&secp_ctx, &countersignatory_revocation_base_secret);
let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret);
let rev_append_commit_hash_key = {
let mut sha = Sha256::engine();
sha.input(&countersignatory_revocation_base_point.serialize());
sha.input(&per_commitment_point.serialize());
Sha256::from_engine(sha).to_byte_array()
};
let commit_append_rev_hash_key = {
let mut sha = Sha256::engine();
sha.input(&per_commitment_point.serialize());
sha.input(&countersignatory_revocation_base_point.serialize());
Sha256::from_engine(sha).to_byte_array()
};
let countersignatory_contrib = countersignatory_revocation_base_secret.clone().mul_tweak(&Scalar::from_be_bytes(rev_append_commit_hash_key).unwrap())
.expect("Multiplying a secret key by a hash is expected to never fail per secp256k1 docs");
let broadcaster_contrib = per_commitment_secret.clone().mul_tweak(&Scalar::from_be_bytes(commit_append_rev_hash_key).unwrap())
.expect("Multiplying a secret key by a hash is expected to never fail per secp256k1 docs");
countersignatory_contrib.add_tweak(&Scalar::from_be_bytes(broadcaster_contrib.secret_bytes()).unwrap())
.expect("Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak commits to the key.")
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct TxCreationKeys {
pub per_commitment_point: PublicKey,
pub revocation_key: RevocationKey,
pub broadcaster_htlc_key: HtlcKey,
pub countersignatory_htlc_key: HtlcKey,
pub broadcaster_delayed_payment_key: DelayedPaymentKey,
}
impl_writeable_tlv_based!(TxCreationKeys, {
(0, per_commitment_point, required),
(2, revocation_key, required),
(4, broadcaster_htlc_key, required),
(6, countersignatory_htlc_key, required),
(8, broadcaster_delayed_payment_key, required),
});
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct ChannelPublicKeys {
pub funding_pubkey: PublicKey,
pub revocation_basepoint: RevocationBasepoint,
pub payment_point: PublicKey,
pub delayed_payment_basepoint: DelayedPaymentBasepoint,
pub htlc_basepoint: HtlcBasepoint,
}
impl_writeable_tlv_based!(ChannelPublicKeys, {
(0, funding_pubkey, required),
(2, revocation_basepoint, required),
(4, payment_point, required),
(6, delayed_payment_basepoint, required),
(8, htlc_basepoint, required),
});
impl TxCreationKeys {
#[rustfmt::skip]
pub fn derive_new<T: secp256k1::Signing + secp256k1::Verification>(secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, broadcaster_delayed_payment_base: &DelayedPaymentBasepoint, broadcaster_htlc_base: &HtlcBasepoint, countersignatory_revocation_base: &RevocationBasepoint, countersignatory_htlc_base: &HtlcBasepoint) -> TxCreationKeys {
TxCreationKeys {
per_commitment_point: per_commitment_point.clone(),
revocation_key: RevocationKey::from_basepoint(&secp_ctx, &countersignatory_revocation_base, &per_commitment_point),
broadcaster_htlc_key: HtlcKey::from_basepoint(&secp_ctx, &broadcaster_htlc_base, &per_commitment_point),
countersignatory_htlc_key: HtlcKey::from_basepoint(&secp_ctx, &countersignatory_htlc_base, &per_commitment_point),
broadcaster_delayed_payment_key: DelayedPaymentKey::from_basepoint(&secp_ctx, &broadcaster_delayed_payment_base, &per_commitment_point),
}
}
pub fn from_channel_static_keys<T: secp256k1::Signing + secp256k1::Verification>(
per_commitment_point: &PublicKey, broadcaster_keys: &ChannelPublicKeys,
countersignatory_keys: &ChannelPublicKeys, secp_ctx: &Secp256k1<T>,
) -> TxCreationKeys {
TxCreationKeys::derive_new(
&secp_ctx,
&per_commitment_point,
&broadcaster_keys.delayed_payment_basepoint,
&broadcaster_keys.htlc_basepoint,
&countersignatory_keys.revocation_basepoint,
&countersignatory_keys.htlc_basepoint,
)
}
}
pub const REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH: usize = 6 + 4 + 34 * 2;
#[rustfmt::skip]
pub fn get_revokeable_redeemscript(revocation_key: &RevocationKey, contest_delay: u16, broadcaster_delayed_payment_key: &DelayedPaymentKey) -> ScriptBuf {
let res = Builder::new().push_opcode(opcodes::all::OP_IF)
.push_slice(&revocation_key.to_public_key().serialize())
.push_opcode(opcodes::all::OP_ELSE)
.push_int(contest_delay as i64)
.push_opcode(opcodes::all::OP_CSV)
.push_opcode(opcodes::all::OP_DROP)
.push_slice(&broadcaster_delayed_payment_key.to_public_key().serialize())
.push_opcode(opcodes::all::OP_ENDIF)
.push_opcode(opcodes::all::OP_CHECKSIG)
.into_script();
debug_assert!(res.len() <= REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH);
res
}
pub fn get_countersigner_payment_script(
channel_type_features: &ChannelTypeFeatures, payment_key: &PublicKey,
) -> ScriptBuf {
if channel_type_features.supports_anchors_zero_fee_htlc_tx() {
get_to_countersigner_keyed_anchor_redeemscript(payment_key).to_p2wsh()
} else {
ScriptBuf::new_p2wpkh(&WPubkeyHash::hash(&payment_key.serialize()))
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HTLCOutputInCommitment {
pub offered: bool,
pub amount_msat: u64,
pub cltv_expiry: u32,
pub payment_hash: PaymentHash,
pub transaction_output_index: Option<u32>,
}
impl HTLCOutputInCommitment {
pub const fn to_bitcoin_amount(&self) -> Amount {
Amount::from_sat(self.amount_msat / 1000)
}
pub(crate) fn is_data_equal(&self, other: &HTLCOutputInCommitment) -> bool {
self.offered == other.offered
&& self.amount_msat == other.amount_msat
&& self.cltv_expiry == other.cltv_expiry
&& self.payment_hash == other.payment_hash
}
}
impl_writeable_tlv_based!(HTLCOutputInCommitment, {
(0, offered, required),
(2, amount_msat, required),
(4, cltv_expiry, required),
(6, payment_hash, required),
(8, transaction_output_index, option),
});
#[inline]
#[rustfmt::skip]
pub(crate) fn get_htlc_redeemscript_with_explicit_keys(htlc: &HTLCOutputInCommitment, channel_type_features: &ChannelTypeFeatures, broadcaster_htlc_key: &HtlcKey, countersignatory_htlc_key: &HtlcKey, revocation_key: &RevocationKey) -> ScriptBuf {
let payment_hash160 = Ripemd160::hash(&htlc.payment_hash.0[..]).to_byte_array();
if htlc.offered {
let mut bldr = Builder::new().push_opcode(opcodes::all::OP_DUP)
.push_opcode(opcodes::all::OP_HASH160)
.push_slice(PubkeyHash::hash(&revocation_key.to_public_key().serialize()))
.push_opcode(opcodes::all::OP_EQUAL)
.push_opcode(opcodes::all::OP_IF)
.push_opcode(opcodes::all::OP_CHECKSIG)
.push_opcode(opcodes::all::OP_ELSE)
.push_slice(&countersignatory_htlc_key.to_public_key().serialize())
.push_opcode(opcodes::all::OP_SWAP)
.push_opcode(opcodes::all::OP_SIZE)
.push_int(32)
.push_opcode(opcodes::all::OP_EQUAL)
.push_opcode(opcodes::all::OP_NOTIF)
.push_opcode(opcodes::all::OP_DROP)
.push_int(2)
.push_opcode(opcodes::all::OP_SWAP)
.push_slice(&broadcaster_htlc_key.to_public_key().serialize())
.push_int(2)
.push_opcode(opcodes::all::OP_CHECKMULTISIG)
.push_opcode(opcodes::all::OP_ELSE)
.push_opcode(opcodes::all::OP_HASH160)
.push_slice(&payment_hash160)
.push_opcode(opcodes::all::OP_EQUALVERIFY)
.push_opcode(opcodes::all::OP_CHECKSIG)
.push_opcode(opcodes::all::OP_ENDIF);
if channel_type_features.supports_anchors_zero_fee_htlc_tx() {
bldr = bldr.push_opcode(opcodes::all::OP_PUSHNUM_1)
.push_opcode(opcodes::all::OP_CSV)
.push_opcode(opcodes::all::OP_DROP);
}
bldr.push_opcode(opcodes::all::OP_ENDIF)
.into_script()
} else {
let mut bldr = Builder::new().push_opcode(opcodes::all::OP_DUP)
.push_opcode(opcodes::all::OP_HASH160)
.push_slice(&PubkeyHash::hash(&revocation_key.to_public_key().serialize()))
.push_opcode(opcodes::all::OP_EQUAL)
.push_opcode(opcodes::all::OP_IF)
.push_opcode(opcodes::all::OP_CHECKSIG)
.push_opcode(opcodes::all::OP_ELSE)
.push_slice(&countersignatory_htlc_key.to_public_key().serialize())
.push_opcode(opcodes::all::OP_SWAP)
.push_opcode(opcodes::all::OP_SIZE)
.push_int(32)
.push_opcode(opcodes::all::OP_EQUAL)
.push_opcode(opcodes::all::OP_IF)
.push_opcode(opcodes::all::OP_HASH160)
.push_slice(&payment_hash160)
.push_opcode(opcodes::all::OP_EQUALVERIFY)
.push_int(2)
.push_opcode(opcodes::all::OP_SWAP)
.push_slice(&broadcaster_htlc_key.to_public_key().serialize())
.push_int(2)
.push_opcode(opcodes::all::OP_CHECKMULTISIG)
.push_opcode(opcodes::all::OP_ELSE)
.push_opcode(opcodes::all::OP_DROP)
.push_int(htlc.cltv_expiry as i64)
.push_opcode(opcodes::all::OP_CLTV)
.push_opcode(opcodes::all::OP_DROP)
.push_opcode(opcodes::all::OP_CHECKSIG)
.push_opcode(opcodes::all::OP_ENDIF);
if channel_type_features.supports_anchors_zero_fee_htlc_tx() {
bldr = bldr.push_opcode(opcodes::all::OP_PUSHNUM_1)
.push_opcode(opcodes::all::OP_CSV)
.push_opcode(opcodes::all::OP_DROP);
}
bldr.push_opcode(opcodes::all::OP_ENDIF)
.into_script()
}
}
#[inline]
#[rustfmt::skip]
pub fn get_htlc_redeemscript(htlc: &HTLCOutputInCommitment, channel_type_features: &ChannelTypeFeatures, keys: &TxCreationKeys) -> ScriptBuf {
get_htlc_redeemscript_with_explicit_keys(htlc, channel_type_features, &keys.broadcaster_htlc_key, &keys.countersignatory_htlc_key, &keys.revocation_key)
}
pub fn make_funding_redeemscript(
broadcaster: &PublicKey, countersignatory: &PublicKey,
) -> ScriptBuf {
let broadcaster_funding_key = broadcaster.serialize();
let countersignatory_funding_key = countersignatory.serialize();
make_funding_redeemscript_from_slices(&broadcaster_funding_key, &countersignatory_funding_key)
}
#[rustfmt::skip]
pub(crate) fn make_funding_redeemscript_from_slices(broadcaster_funding_key: &[u8; 33], countersignatory_funding_key: &[u8; 33]) -> ScriptBuf {
let builder = Builder::new().push_opcode(opcodes::all::OP_PUSHNUM_2);
if broadcaster_funding_key[..] < countersignatory_funding_key[..] {
builder.push_slice(broadcaster_funding_key)
.push_slice(countersignatory_funding_key)
} else {
builder.push_slice(countersignatory_funding_key)
.push_slice(broadcaster_funding_key)
}.push_opcode(opcodes::all::OP_PUSHNUM_2).push_opcode(opcodes::all::OP_CHECKMULTISIG).into_script()
}
pub fn build_htlc_transaction(
commitment_txid: &Txid, feerate_per_kw: u32, contest_delay: u16, htlc: &HTLCOutputInCommitment,
channel_type_features: &ChannelTypeFeatures,
broadcaster_delayed_payment_key: &DelayedPaymentKey, revocation_key: &RevocationKey,
) -> Transaction {
let txins = vec![build_htlc_input(commitment_txid, htlc, channel_type_features)];
let txouts: Vec<TxOut> = vec![build_htlc_output(
feerate_per_kw,
contest_delay,
htlc,
channel_type_features,
broadcaster_delayed_payment_key,
revocation_key,
)];
let version = if channel_type_features.supports_anchor_zero_fee_commitments() {
Version::non_standard(3)
} else {
Version::TWO
};
Transaction {
version,
lock_time: LockTime::from_consensus(if htlc.offered { htlc.cltv_expiry } else { 0 }),
input: txins,
output: txouts,
}
}
#[rustfmt::skip]
pub(crate) fn build_htlc_input(commitment_txid: &Txid, htlc: &HTLCOutputInCommitment, channel_type_features: &ChannelTypeFeatures) -> TxIn {
TxIn {
previous_output: OutPoint {
txid: commitment_txid.clone(),
vout: htlc.transaction_output_index.expect("Can't build an HTLC transaction for a dust output"),
},
script_sig: ScriptBuf::new(),
sequence: Sequence(if channel_type_features.supports_anchors_zero_fee_htlc_tx() { 1 } else { 0 }),
witness: Witness::new(),
}
}
#[rustfmt::skip]
pub(crate) fn build_htlc_output(
feerate_per_kw: u32, contest_delay: u16, htlc: &HTLCOutputInCommitment, channel_type_features: &ChannelTypeFeatures, broadcaster_delayed_payment_key: &DelayedPaymentKey, revocation_key: &RevocationKey
) -> TxOut {
let (htlc_success_tx_fee_sat, htlc_timeout_tx_fee_sat) = second_stage_tx_fees_sat(
channel_type_features, feerate_per_kw,
);
let output_value = {
let total_fee = if htlc.offered {
htlc_timeout_tx_fee_sat
} else {
htlc_success_tx_fee_sat
};
htlc.to_bitcoin_amount() - Amount::from_sat(total_fee)
};
TxOut {
script_pubkey: get_revokeable_redeemscript(revocation_key, contest_delay, broadcaster_delayed_payment_key).to_p2wsh(),
value: output_value,
}
}
pub fn build_htlc_input_witness(
local_sig: &Signature, remote_sig: &Signature, preimage: &Option<PaymentPreimage>,
redeem_script: &Script, channel_type_features: &ChannelTypeFeatures,
) -> Witness {
let remote_sighash_type = if channel_type_features.supports_anchors_zero_fee_htlc_tx()
|| channel_type_features.supports_anchor_zero_fee_commitments()
{
EcdsaSighashType::SinglePlusAnyoneCanPay
} else {
EcdsaSighashType::All
};
let mut witness = Witness::new();
witness.push(vec![]);
witness.push_ecdsa_signature(&BitcoinSignature {
signature: *remote_sig,
sighash_type: remote_sighash_type,
});
witness.push_ecdsa_signature(&BitcoinSignature::sighash_all(*local_sig));
if let Some(preimage) = preimage {
witness.push(preimage.0.to_vec());
} else {
witness.push(vec![]);
}
witness.push(redeem_script.to_bytes());
witness
}
pub(crate) fn legacy_deserialization_prevention_marker_for_channel_type_features(
features: &ChannelTypeFeatures,
) -> Option<()> {
let mut legacy_version_bit_set = ChannelTypeFeatures::only_static_remote_key();
legacy_version_bit_set.set_scid_privacy_required();
legacy_version_bit_set.set_zero_conf_required();
debug_assert!(!legacy_version_bit_set.supports_any_optional_bits());
debug_assert!(!features.supports_any_optional_bits());
if features.requires_unknown_bits_from(&legacy_version_bit_set) {
Some(())
} else {
None
}
}
#[inline]
pub fn get_to_countersigner_keyed_anchor_redeemscript(payment_point: &PublicKey) -> ScriptBuf {
Builder::new()
.push_slice(payment_point.serialize())
.push_opcode(opcodes::all::OP_CHECKSIGVERIFY)
.push_int(1)
.push_opcode(opcodes::all::OP_CSV)
.into_script()
}
pub fn shared_anchor_script_pubkey() -> ScriptBuf {
Builder::new().push_int(1).push_slice(&[0x4e, 0x73]).into_script()
}
#[rustfmt::skip]
pub fn get_keyed_anchor_redeemscript(funding_pubkey: &PublicKey) -> ScriptBuf {
Builder::new().push_slice(funding_pubkey.serialize())
.push_opcode(opcodes::all::OP_CHECKSIG)
.push_opcode(opcodes::all::OP_IFDUP)
.push_opcode(opcodes::all::OP_NOTIF)
.push_int(16)
.push_opcode(opcodes::all::OP_CSV)
.push_opcode(opcodes::all::OP_ENDIF)
.into_script()
}
pub fn build_keyed_anchor_input_witness(
funding_key: &PublicKey, funding_sig: &Signature,
) -> Witness {
let anchor_redeem_script = get_keyed_anchor_redeemscript(funding_key);
let mut ret = Witness::new();
ret.push_ecdsa_signature(&BitcoinSignature::sighash_all(*funding_sig));
ret.push(anchor_redeem_script.as_bytes());
ret
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct ChannelTransactionParameters {
pub holder_pubkeys: ChannelPublicKeys,
pub holder_selected_contest_delay: u16,
pub is_outbound_from_holder: bool,
pub counterparty_parameters: Option<CounterpartyChannelTransactionParameters>,
pub funding_outpoint: Option<chain::transaction::OutPoint>,
pub splice_parent_funding_txid: Option<Txid>,
pub channel_type_features: ChannelTypeFeatures,
pub channel_value_satoshis: u64,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct CounterpartyChannelTransactionParameters {
pub pubkeys: ChannelPublicKeys,
pub selected_contest_delay: u16,
}
impl ChannelTransactionParameters {
pub fn is_populated(&self) -> bool {
self.counterparty_parameters.is_some() && self.funding_outpoint.is_some()
}
#[rustfmt::skip]
pub fn as_holder_broadcastable(&self) -> DirectedChannelTransactionParameters<'_> {
assert!(self.is_populated(), "self.late_parameters must be set before using as_holder_broadcastable");
DirectedChannelTransactionParameters {
inner: self,
holder_is_broadcaster: true
}
}
#[rustfmt::skip]
pub fn as_counterparty_broadcastable(&self) -> DirectedChannelTransactionParameters<'_> {
assert!(self.is_populated(), "self.late_parameters must be set before using as_counterparty_broadcastable");
DirectedChannelTransactionParameters {
inner: self,
holder_is_broadcaster: false
}
}
pub(crate) fn make_funding_redeemscript(&self) -> ScriptBuf {
self.make_funding_redeemscript_opt().unwrap()
}
pub(crate) fn make_funding_redeemscript_opt(&self) -> Option<ScriptBuf> {
self.counterparty_parameters.as_ref().map(|p| {
make_funding_redeemscript(
&self.holder_pubkeys.funding_pubkey,
&p.pubkeys.funding_pubkey,
)
})
}
pub fn counterparty_pubkeys(&self) -> Option<&ChannelPublicKeys> {
self.counterparty_parameters.as_ref().map(|params| ¶ms.pubkeys)
}
#[cfg(test)]
#[rustfmt::skip]
pub fn test_dummy(channel_value_satoshis: u64) -> Self {
let dummy_keys = ChannelPublicKeys {
funding_pubkey: PublicKey::from_slice(&[2; 33]).unwrap(),
revocation_basepoint: PublicKey::from_slice(&[2; 33]).unwrap().into(),
payment_point: PublicKey::from_slice(&[2; 33]).unwrap(),
delayed_payment_basepoint: PublicKey::from_slice(&[2; 33]).unwrap().into(),
htlc_basepoint: PublicKey::from_slice(&[2; 33]).unwrap().into(),
};
Self {
holder_pubkeys: dummy_keys.clone(),
holder_selected_contest_delay: 42,
is_outbound_from_holder: true,
counterparty_parameters: Some(CounterpartyChannelTransactionParameters {
pubkeys: dummy_keys,
selected_contest_delay: 42,
}),
funding_outpoint: Some(chain::transaction::OutPoint {
txid: Txid::from_byte_array([42; 32]), index: 0
}),
splice_parent_funding_txid: None,
channel_type_features: ChannelTypeFeatures::empty(),
channel_value_satoshis,
}
}
}
impl_writeable_tlv_based!(CounterpartyChannelTransactionParameters, {
(0, pubkeys, required),
(2, selected_contest_delay, required),
});
impl Writeable for ChannelTransactionParameters {
#[rustfmt::skip]
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.holder_pubkeys, required),
(2, self.holder_selected_contest_delay, required),
(4, self.is_outbound_from_holder, required),
(6, self.counterparty_parameters, option),
(8, self.funding_outpoint, option),
(10, legacy_deserialization_prevention_marker, option),
(11, self.channel_type_features, required),
(12, self.splice_parent_funding_txid, option),
(13, self.channel_value_satoshis, required),
});
Ok(())
}
}
impl ReadableArgs<Option<u64>> for ChannelTransactionParameters {
#[rustfmt::skip]
fn read<R: io::Read>(reader: &mut R, read_args: Option<u64>) -> Result<Self, DecodeError> {
let mut holder_pubkeys = RequiredWrapper(None);
let mut holder_selected_contest_delay = RequiredWrapper(None);
let mut is_outbound_from_holder = RequiredWrapper(None);
let mut counterparty_parameters = None;
let mut funding_outpoint = None;
let mut splice_parent_funding_txid = None;
let mut _legacy_deserialization_prevention_marker: Option<()> = None;
let mut channel_type_features = None;
let mut channel_value_satoshis = None;
read_tlv_fields!(reader, {
(0, holder_pubkeys, required),
(2, holder_selected_contest_delay, required),
(4, is_outbound_from_holder, required),
(6, counterparty_parameters, option),
(8, funding_outpoint, option),
(10, _legacy_deserialization_prevention_marker, option),
(11, channel_type_features, option),
(12, splice_parent_funding_txid, option),
(13, channel_value_satoshis, option),
});
let channel_value_satoshis = match read_args {
None => channel_value_satoshis.ok_or(DecodeError::InvalidValue)?,
Some(expected_value) => {
let channel_value_satoshis = channel_value_satoshis.unwrap_or(expected_value);
if channel_value_satoshis == expected_value {
channel_value_satoshis
} else {
return Err(DecodeError::InvalidValue);
}
},
};
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 {
holder_pubkeys: holder_pubkeys.0.unwrap(),
holder_selected_contest_delay: holder_selected_contest_delay.0.unwrap(),
is_outbound_from_holder: is_outbound_from_holder.0.unwrap(),
counterparty_parameters,
funding_outpoint,
splice_parent_funding_txid,
channel_type_features: channel_type_features.unwrap_or(ChannelTypeFeatures::only_static_remote_key()),
channel_value_satoshis,
})
}
}
pub struct DirectedChannelTransactionParameters<'a> {
inner: &'a ChannelTransactionParameters,
holder_is_broadcaster: bool,
}
impl<'a> DirectedChannelTransactionParameters<'a> {
pub fn broadcaster_pubkeys(&self) -> &'a ChannelPublicKeys {
if self.holder_is_broadcaster {
&self.inner.holder_pubkeys
} else {
&self.inner.counterparty_parameters.as_ref().unwrap().pubkeys
}
}
pub fn countersignatory_pubkeys(&self) -> &'a ChannelPublicKeys {
if self.holder_is_broadcaster {
&self.inner.counterparty_parameters.as_ref().unwrap().pubkeys
} else {
&self.inner.holder_pubkeys
}
}
#[rustfmt::skip]
pub fn contest_delay(&self) -> u16 {
let counterparty_parameters = self.inner.counterparty_parameters.as_ref().unwrap();
if self.holder_is_broadcaster { counterparty_parameters.selected_contest_delay } else { self.inner.holder_selected_contest_delay }
}
#[rustfmt::skip]
pub fn is_outbound(&self) -> bool {
if self.holder_is_broadcaster { self.inner.is_outbound_from_holder } else { !self.inner.is_outbound_from_holder }
}
pub fn funding_outpoint(&self) -> OutPoint {
self.inner.funding_outpoint.unwrap().into_bitcoin_outpoint()
}
pub fn channel_type_features(&self) -> &'a ChannelTypeFeatures {
&self.inner.channel_type_features
}
pub fn channel_value_satoshis(&self) -> u64 {
self.inner.channel_value_satoshis
}
}
#[derive(Clone, Debug)]
pub struct HolderCommitmentTransaction {
inner: CommitmentTransaction,
pub counterparty_sig: Signature,
pub counterparty_htlc_sigs: Vec<Signature>,
holder_sig_first: bool,
}
impl Deref for HolderCommitmentTransaction {
type Target = CommitmentTransaction;
#[rustfmt::skip]
fn deref(&self) -> &Self::Target { &self.inner }
}
impl Eq for HolderCommitmentTransaction {}
impl PartialEq for HolderCommitmentTransaction {
fn eq(&self, o: &Self) -> bool {
self.inner == o.inner
}
}
impl_writeable_tlv_based!(HolderCommitmentTransaction, {
(0, inner, required),
(2, counterparty_sig, required),
(4, holder_sig_first, required),
(6, counterparty_htlc_sigs, required_vec),
});
impl HolderCommitmentTransaction {
#[cfg(test)]
#[rustfmt::skip]
pub fn dummy(channel_value_satoshis: u64, funding_outpoint: chain::transaction::OutPoint, nondust_htlcs: Vec<HTLCOutputInCommitment>) -> Self {
let secp_ctx = Secp256k1::new();
let dummy_key = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let dummy_sig = sign(&secp_ctx, &secp256k1::Message::from_digest([42; 32]), &SecretKey::from_slice(&[42; 32]).unwrap());
let channel_pubkeys = ChannelPublicKeys {
funding_pubkey: dummy_key.clone(),
revocation_basepoint: RevocationBasepoint::from(dummy_key),
payment_point: dummy_key.clone(),
delayed_payment_basepoint: DelayedPaymentBasepoint::from(dummy_key.clone()),
htlc_basepoint: HtlcBasepoint::from(dummy_key.clone())
};
let channel_parameters = ChannelTransactionParameters {
holder_pubkeys: channel_pubkeys.clone(),
holder_selected_contest_delay: 0,
is_outbound_from_holder: false,
counterparty_parameters: Some(CounterpartyChannelTransactionParameters { pubkeys: channel_pubkeys.clone(), selected_contest_delay: 0 }),
funding_outpoint: Some(funding_outpoint),
splice_parent_funding_txid: None,
channel_type_features: ChannelTypeFeatures::only_static_remote_key(),
channel_value_satoshis,
};
let mut counterparty_htlc_sigs = Vec::new();
for _ in 0..nondust_htlcs.len() {
counterparty_htlc_sigs.push(dummy_sig);
}
let inner = CommitmentTransaction::new(0, &dummy_key, 0, 0, 0, nondust_htlcs, &channel_parameters.as_counterparty_broadcastable(), &secp_ctx);
HolderCommitmentTransaction {
inner,
counterparty_sig: dummy_sig,
counterparty_htlc_sigs,
holder_sig_first: false
}
}
#[rustfmt::skip]
pub fn new(commitment_tx: CommitmentTransaction, counterparty_sig: Signature, counterparty_htlc_sigs: Vec<Signature>, holder_funding_key: &PublicKey, counterparty_funding_key: &PublicKey) -> Self {
Self {
inner: commitment_tx,
counterparty_sig,
counterparty_htlc_sigs,
holder_sig_first: holder_funding_key.serialize()[..] < counterparty_funding_key.serialize()[..],
}
}
#[rustfmt::skip]
pub(crate) fn add_holder_sig(&self, funding_redeemscript: &Script, holder_sig: Signature) -> Transaction {
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_ecdsa_signature(&BitcoinSignature::sighash_all(holder_sig));
tx.input[0].witness.push_ecdsa_signature(&BitcoinSignature::sighash_all(self.counterparty_sig));
} else {
tx.input[0].witness.push_ecdsa_signature(&BitcoinSignature::sighash_all(self.counterparty_sig));
tx.input[0].witness.push_ecdsa_signature(&BitcoinSignature::sighash_all(holder_sig));
}
tx.input[0].witness.push(funding_redeemscript.as_bytes().to_vec());
tx
}
}
#[derive(Clone, Debug)]
pub struct BuiltCommitmentTransaction {
pub transaction: Transaction,
pub txid: Txid,
}
impl_writeable_tlv_based!(BuiltCommitmentTransaction, {
(0, transaction, required),
(2, txid, required),
});
impl BuiltCommitmentTransaction {
#[rustfmt::skip]
pub fn get_sighash_all(&self, funding_redeemscript: &Script, channel_value_satoshis: u64) -> Message {
let sighash = &sighash::SighashCache::new(&self.transaction).p2wsh_signature_hash(0, funding_redeemscript, Amount::from_sat(channel_value_satoshis), EcdsaSighashType::All).unwrap()[..];
hash_to_message!(sighash)
}
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)
}
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)
}
}
#[derive(Clone, Hash, PartialEq, Eq)]
pub struct ClosingTransaction {
to_holder_value_sat: Amount,
to_counterparty_value_sat: Amount,
to_holder_script: ScriptBuf,
to_counterparty_script: ScriptBuf,
built: Transaction,
}
impl ClosingTransaction {
#[rustfmt::skip]
pub fn new(
to_holder_value_sat: u64,
to_counterparty_value_sat: u64,
to_holder_script: ScriptBuf,
to_counterparty_script: ScriptBuf,
funding_outpoint: OutPoint,
) -> Self {
let to_holder_value_sat = Amount::from_sat(to_holder_value_sat);
let to_counterparty_value_sat = Amount::from_sat(to_counterparty_value_sat);
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
}
}
pub fn trust(&self) -> TrustedClosingTransaction<'_> {
TrustedClosingTransaction { inner: self }
}
#[rustfmt::skip]
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 })
}
pub fn to_holder_value_sat(&self) -> u64 {
self.to_holder_value_sat.to_sat()
}
pub fn to_counterparty_value_sat(&self) -> u64 {
self.to_counterparty_value_sat.to_sat()
}
pub fn to_holder_script(&self) -> &Script {
&self.to_holder_script
}
pub fn to_counterparty_script(&self) -> &Script {
&self.to_counterparty_script
}
}
pub struct TrustedClosingTransaction<'a> {
inner: &'a ClosingTransaction,
}
impl<'a> Deref for TrustedClosingTransaction<'a> {
type Target = ClosingTransaction;
#[rustfmt::skip]
fn deref(&self) -> &Self::Target { self.inner }
}
impl<'a> TrustedClosingTransaction<'a> {
pub fn built_transaction(&self) -> &'a Transaction {
&self.inner.built
}
#[rustfmt::skip]
pub fn get_sighash_all(&self, funding_redeemscript: &Script, channel_value_satoshis: u64) -> Message {
let sighash = &sighash::SighashCache::new(&self.inner.built).p2wsh_signature_hash(0, funding_redeemscript, Amount::from_sat(channel_value_satoshis), EcdsaSighashType::All).unwrap()[..];
hash_to_message!(sighash)
}
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)
}
}
#[derive(Clone, Debug)]
pub struct CommitmentTransaction {
commitment_number: u64,
to_broadcaster_value_sat: Amount,
to_countersignatory_value_sat: Amount,
to_broadcaster_delay: Option<u16>, feerate_per_kw: u32,
nondust_htlcs: Vec<HTLCOutputInCommitment>,
channel_type_features: ChannelTypeFeatures,
keys: TxCreationKeys,
built: BuiltCommitmentTransaction,
}
impl Eq for CommitmentTransaction {}
impl PartialEq for CommitmentTransaction {
#[rustfmt::skip]
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.nondust_htlcs == o.nondust_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 {
#[rustfmt::skip]
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.nondust_htlcs, required_vec),
(14, legacy_deserialization_prevention_marker, option),
(15, self.channel_type_features, required),
});
Ok(())
}
}
impl Readable for CommitmentTransaction {
#[rustfmt::skip]
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, nondust_htlcs, required_vec),
(14, _legacy_deserialization_prevention_marker, (option, explicit_type: ())),
(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(),
nondust_htlcs,
channel_type_features: channel_type_features.unwrap_or(ChannelTypeFeatures::only_static_remote_key())
})
}
}
impl CommitmentTransaction {
#[rustfmt::skip]
pub fn new(commitment_number: u64, per_commitment_point: &PublicKey, to_broadcaster_value_sat: u64, to_countersignatory_value_sat: u64, feerate_per_kw: u32, mut nondust_htlcs: Vec<HTLCOutputInCommitment>, channel_parameters: &DirectedChannelTransactionParameters, secp_ctx: &Secp256k1<secp256k1::All>) -> CommitmentTransaction {
let to_broadcaster_value_sat = Amount::from_sat(to_broadcaster_value_sat);
let to_countersignatory_value_sat = Amount::from_sat(to_countersignatory_value_sat);
let keys = TxCreationKeys::from_channel_static_keys(per_commitment_point, channel_parameters.broadcaster_pubkeys(), channel_parameters.countersignatory_pubkeys(), secp_ctx);
let outputs = Self::build_outputs_and_htlcs(&keys, to_broadcaster_value_sat, to_countersignatory_value_sat, &mut nondust_htlcs, channel_parameters);
let (obscured_commitment_transaction_number, txins) = Self::build_inputs(commitment_number, channel_parameters);
let transaction = Self::make_transaction(obscured_commitment_transaction_number, txins, outputs, channel_parameters);
let txid = transaction.compute_txid();
CommitmentTransaction {
commitment_number,
to_broadcaster_value_sat,
to_countersignatory_value_sat,
to_broadcaster_delay: Some(channel_parameters.contest_delay()),
feerate_per_kw,
nondust_htlcs,
channel_type_features: channel_parameters.channel_type_features().clone(),
keys,
built: BuiltCommitmentTransaction {
transaction,
txid
},
}
}
pub fn with_non_zero_fee_anchors(mut self) -> Self {
self.channel_type_features.set_anchors_nonzero_fee_htlc_tx_required();
self
}
#[rustfmt::skip]
fn is_left_greater(i: usize, txouts: &Vec<TxOut>, nondust_htlcs: &Vec<HTLCOutputInCommitment>) -> bool {
txouts[i - 1].value.cmp(&txouts[i].value)
.then(txouts[i - 1].script_pubkey.cmp(&txouts[i].script_pubkey))
.then(nondust_htlcs[i - 1].cltv_expiry.cmp(&nondust_htlcs[i].cltv_expiry))
.then(nondust_htlcs[i - 1].payment_hash.cmp(&nondust_htlcs[i].payment_hash))
.is_gt()
}
#[rustfmt::skip]
fn rebuild_transaction(&self, keys: &TxCreationKeys, channel_parameters: &DirectedChannelTransactionParameters) -> Result<BuiltCommitmentTransaction, ()> {
let (obscured_commitment_transaction_number, txins) = Self::build_inputs(self.commitment_number, channel_parameters);
let mut outputs = Self::build_htlc_outputs(keys, &self.nondust_htlcs, channel_parameters.channel_type_features());
let nondust_htlcs_value_sum_sat = self.nondust_htlcs.iter().map(|htlc| htlc.to_bitcoin_amount()).sum();
if (1..outputs.len()).into_iter().any(|i| Self::is_left_greater(i, &outputs, &self.nondust_htlcs)) {
return Err(())
}
let insert_non_htlc_output = |non_htlc_output: TxOut| {
let idx = match outputs.binary_search_by(|output| output.value.cmp(&non_htlc_output.value).then(output.script_pubkey.cmp(&non_htlc_output.script_pubkey))) {
Ok(i) => i,
Err(i) => i,
};
outputs.insert(idx, non_htlc_output);
};
Self::insert_non_htlc_outputs(
keys,
self.to_broadcaster_value_sat,
self.to_countersignatory_value_sat,
channel_parameters,
nondust_htlcs_value_sum_sat,
insert_non_htlc_output
);
let transaction = Self::make_transaction(obscured_commitment_transaction_number, txins, outputs, channel_parameters);
let txid = transaction.compute_txid();
let built_transaction = BuiltCommitmentTransaction {
transaction,
txid
};
Ok(built_transaction)
}
#[rustfmt::skip]
fn make_transaction(obscured_commitment_transaction_number: u64, txins: Vec<TxIn>, outputs: Vec<TxOut>, channel_parameters: &DirectedChannelTransactionParameters) -> Transaction {
let version = if channel_parameters.channel_type_features().supports_anchor_zero_fee_commitments() {
Version::non_standard(3)
} else {
Version::TWO
};
Transaction {
version,
lock_time: LockTime::from_consensus(((0x20 as u32) << 8 * 3) | ((obscured_commitment_transaction_number & 0xffffffu64) as u32)),
input: txins,
output: outputs,
}
}
#[rustfmt::skip]
fn build_outputs_and_htlcs(
keys: &TxCreationKeys,
to_broadcaster_value_sat: Amount,
to_countersignatory_value_sat: Amount,
nondust_htlcs: &mut Vec<HTLCOutputInCommitment>,
channel_parameters: &DirectedChannelTransactionParameters
) -> Vec<TxOut> {
let mut outputs = Self::build_sorted_htlc_outputs(keys, nondust_htlcs, channel_parameters.channel_type_features());
let nondust_htlcs_value_sum_sat = nondust_htlcs.iter().map(|htlc| htlc.to_bitcoin_amount()).sum();
nondust_htlcs
.iter_mut()
.enumerate()
.for_each(|(i, htlc)| htlc.transaction_output_index = Some(i as u32));
let insert_non_htlc_output = |non_htlc_output: TxOut| {
let idx = match outputs.binary_search_by(|output| output.value.cmp(&non_htlc_output.value).then(output.script_pubkey.cmp(&non_htlc_output.script_pubkey))) {
Ok(i) => i,
Err(i) => i,
};
outputs.insert(idx, non_htlc_output);
nondust_htlcs
.iter_mut()
.rev()
.map_while(|htlc| {
let i = htlc.transaction_output_index.as_mut().unwrap();
(*i >= idx as u32).then(|| i)
})
.for_each(|i| *i += 1);
};
Self::insert_non_htlc_outputs(
keys,
to_broadcaster_value_sat,
to_countersignatory_value_sat,
channel_parameters,
nondust_htlcs_value_sum_sat,
insert_non_htlc_output
);
outputs
}
#[rustfmt::skip]
fn insert_non_htlc_outputs<F>(
keys: &TxCreationKeys,
to_broadcaster_value_sat: Amount,
to_countersignatory_value_sat: Amount,
channel_parameters: &DirectedChannelTransactionParameters,
nondust_htlcs_value_sum_sat: Amount,
mut insert_non_htlc_output: F,
) where
F: FnMut(TxOut),
{
let countersignatory_payment_point = &channel_parameters.countersignatory_pubkeys().payment_point;
let countersignatory_funding_key = &channel_parameters.countersignatory_pubkeys().funding_pubkey;
let broadcaster_funding_key = &channel_parameters.broadcaster_pubkeys().funding_pubkey;
let channel_type = channel_parameters.channel_type_features();
let contest_delay = channel_parameters.contest_delay();
let tx_has_htlc_outputs = nondust_htlcs_value_sum_sat != Amount::ZERO;
if to_countersignatory_value_sat > Amount::ZERO {
let script = if channel_type.supports_anchors_zero_fee_htlc_tx() {
get_to_countersigner_keyed_anchor_redeemscript(countersignatory_payment_point).to_p2wsh()
} else {
ScriptBuf::new_p2wpkh(&Hash160::hash(&countersignatory_payment_point.serialize()).into())
};
insert_non_htlc_output(TxOut {
script_pubkey: script,
value: to_countersignatory_value_sat,
});
}
if to_broadcaster_value_sat > Amount::ZERO {
let redeem_script = get_revokeable_redeemscript(
&keys.revocation_key,
contest_delay,
&keys.broadcaster_delayed_payment_key,
);
insert_non_htlc_output(TxOut {
script_pubkey: redeem_script.to_p2wsh(),
value: to_broadcaster_value_sat,
});
}
if channel_type.supports_anchors_zero_fee_htlc_tx() {
if to_broadcaster_value_sat > Amount::ZERO || tx_has_htlc_outputs {
let anchor_script = get_keyed_anchor_redeemscript(broadcaster_funding_key);
insert_non_htlc_output(TxOut {
script_pubkey: anchor_script.to_p2wsh(),
value: Amount::from_sat(ANCHOR_OUTPUT_VALUE_SATOSHI),
});
}
if to_countersignatory_value_sat > Amount::ZERO || tx_has_htlc_outputs {
let anchor_script = get_keyed_anchor_redeemscript(countersignatory_funding_key);
insert_non_htlc_output(TxOut {
script_pubkey: anchor_script.to_p2wsh(),
value: Amount::from_sat(ANCHOR_OUTPUT_VALUE_SATOSHI),
});
}
}
if channel_type.supports_anchor_zero_fee_commitments() {
let channel_value_satoshis = Amount::from_sat(channel_parameters.channel_value_satoshis());
let trimmed_sum_sat = channel_value_satoshis - nondust_htlcs_value_sum_sat - to_broadcaster_value_sat - to_countersignatory_value_sat;
insert_non_htlc_output(TxOut {
script_pubkey: shared_anchor_script_pubkey(),
value: cmp::min(Amount::from_sat(P2A_MAX_VALUE), trimmed_sum_sat),
});
}
}
#[rustfmt::skip]
fn build_htlc_outputs(keys: &TxCreationKeys, nondust_htlcs: &Vec<HTLCOutputInCommitment>, channel_type: &ChannelTypeFeatures) -> Vec<TxOut> {
let mut txouts = Vec::with_capacity(nondust_htlcs.len() + 4);
for htlc in nondust_htlcs {
let script = get_htlc_redeemscript(htlc, channel_type, keys);
let txout = TxOut {
script_pubkey: script.to_p2wsh(),
value: htlc.to_bitcoin_amount(),
};
txouts.push(txout);
}
txouts
}
#[rustfmt::skip]
fn build_sorted_htlc_outputs(
keys: &TxCreationKeys,
nondust_htlcs: &mut Vec<HTLCOutputInCommitment>,
channel_type: &ChannelTypeFeatures
) -> Vec<TxOut> {
let mut txouts = Self::build_htlc_outputs(keys, nondust_htlcs, channel_type);
for i in 1..txouts.len() {
let mut j = i;
while j > 0 && Self::is_left_greater(j, &txouts, &nondust_htlcs) {
txouts.swap(j - 1, j);
nondust_htlcs.swap(j - 1, j);
j -= 1;
}
}
txouts
}
#[rustfmt::skip]
fn 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 ins: Vec<TxIn> = vec![TxIn {
previous_output: channel_parameters.funding_outpoint(),
script_sig: ScriptBuf::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)
}
pub fn commitment_number(&self) -> u64 {
self.commitment_number
}
pub fn per_commitment_point(&self) -> PublicKey {
self.keys.per_commitment_point
}
pub fn to_broadcaster_value_sat(&self) -> u64 {
self.to_broadcaster_value_sat.to_sat()
}
pub fn to_countersignatory_value_sat(&self) -> u64 {
self.to_countersignatory_value_sat.to_sat()
}
pub fn negotiated_feerate_per_kw(&self) -> u32 {
self.feerate_per_kw
}
pub fn nondust_htlcs(&self) -> &Vec<HTLCOutputInCommitment> {
&self.nondust_htlcs
}
pub fn trust(&self) -> TrustedCommitmentTransaction<'_> {
TrustedCommitmentTransaction { inner: self }
}
#[rustfmt::skip]
pub fn verify<T: secp256k1::Signing + secp256k1::Verification>(&self, channel_parameters: &DirectedChannelTransactionParameters, secp_ctx: &Secp256k1<T>) -> Result<TrustedCommitmentTransaction<'_>, ()> {
let per_commitment_point = &self.keys.per_commitment_point;
let keys = TxCreationKeys::from_channel_static_keys(per_commitment_point, channel_parameters.broadcaster_pubkeys(), channel_parameters.countersignatory_pubkeys(), secp_ctx);
if keys != self.keys {
return Err(());
}
let tx = self.rebuild_transaction(&keys, channel_parameters)?;
if self.built.transaction != tx.transaction || self.built.txid != tx.txid {
return Err(());
}
Ok(TrustedCommitmentTransaction { inner: self })
}
}
pub struct TrustedCommitmentTransaction<'a> {
inner: &'a CommitmentTransaction,
}
impl<'a> Deref for TrustedCommitmentTransaction<'a> {
type Target = CommitmentTransaction;
#[rustfmt::skip]
fn deref(&self) -> &Self::Target { self.inner }
}
impl<'a> TrustedCommitmentTransaction<'a> {
pub fn txid(&self) -> Txid {
self.inner.built.txid
}
pub fn built_transaction(&self) -> &'a BuiltCommitmentTransaction {
&self.inner.built
}
pub fn keys(&self) -> &'a TxCreationKeys {
&self.inner.keys
}
pub fn channel_type_features(&self) -> &'a ChannelTypeFeatures {
&self.inner.channel_type_features
}
#[rustfmt::skip]
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.nondust_htlcs.len());
let holder_htlc_key = derive_private_key(secp_ctx, &inner.keys.per_commitment_point, htlc_base_key);
for this_htlc in inner.nondust_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).p2wsh_signature_hash(0, &htlc_redeemscript, this_htlc.to_bitcoin_amount(), EcdsaSighashType::All).unwrap()[..]);
ret.push(sign_with_aux_rand(secp_ctx, &sighash, &holder_htlc_key, entropy_source));
}
Ok(ret)
}
#[rustfmt::skip]
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_p2wsh();
let outputs = &self.inner.built.transaction.output;
outputs.iter().enumerate()
.find(|(_, out)| out.script_pubkey == revokeable_p2wsh)
.map(|(idx, _)| idx)
}
#[rustfmt::skip]
pub fn build_to_local_justice_tx(&self, feerate_per_kw: u64, destination_script: ScriptBuf)
-> 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: ScriptBuf::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: Version::TWO,
lock_time: LockTime::ZERO,
input,
output,
};
let weight = justice_tx.weight().to_wu() + WEIGHT_REVOKED_OUTPUT;
let fee = Amount::from_sat(fee_for_weight(feerate_per_kw as u32, weight));
justice_tx.output[0].value = value.checked_sub(fee).ok_or(())?;
Ok(justice_tx)
}
}
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).to_byte_array();
((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::{ChannelPublicKeys, CounterpartyCommitmentSecrets};
use crate::chain;
use crate::ln::chan_utils::{
get_htlc_redeemscript, get_keyed_anchor_redeemscript,
get_to_countersigner_keyed_anchor_redeemscript, shared_anchor_script_pubkey,
BuiltCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction,
CounterpartyChannelTransactionParameters, HTLCOutputInCommitment,
TrustedCommitmentTransaction,
};
use crate::sign::{ChannelSigner, SignerProvider};
use crate::types::features::ChannelTypeFeatures;
use crate::types::payment::PaymentHash;
use crate::util::test_utils;
use bitcoin::hashes::Hash;
use bitcoin::hex::FromHex;
use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey};
use bitcoin::PublicKey as BitcoinPublicKey;
use bitcoin::{CompressedPublicKey, Network, ScriptBuf, Txid};
#[allow(unused_imports)]
use crate::prelude::*;
struct TestCommitmentTxBuilder {
commitment_number: u64,
per_commitment_point: PublicKey,
feerate_per_kw: u32,
channel_parameters: ChannelTransactionParameters,
counterparty_pubkeys: ChannelPublicKeys,
secp_ctx: Secp256k1<secp256k1::All>,
}
impl TestCommitmentTxBuilder {
#[rustfmt::skip]
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(keys_provider.generate_channel_keys_id(false, 0));
let counterparty_signer = keys_provider.derive_channel_signer(keys_provider.generate_channel_keys_id(true, 1));
let per_commitment_secret = SecretKey::from_slice(&<Vec<u8>>::from_hex("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100").unwrap()[..]).unwrap();
let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret);
let holder_pubkeys = signer.pubkeys(&secp_ctx);
let counterparty_pubkeys = counterparty_signer.pubkeys(&secp_ctx).clone();
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 }),
splice_parent_funding_txid: None,
channel_type_features: ChannelTypeFeatures::only_static_remote_key(),
channel_value_satoshis: 4000,
};
Self {
commitment_number: 0,
per_commitment_point,
feerate_per_kw: 1,
channel_parameters,
counterparty_pubkeys,
secp_ctx,
}
}
#[rustfmt::skip]
fn build(&self, to_broadcaster_sats: u64, to_countersignatory_sats: u64, nondust_htlcs: Vec<HTLCOutputInCommitment>) -> CommitmentTransaction {
CommitmentTransaction::new(
self.commitment_number, &self.per_commitment_point, to_broadcaster_sats, to_countersignatory_sats, self.feerate_per_kw,
nondust_htlcs, &self.channel_parameters.as_holder_broadcastable(), &self.secp_ctx
)
}
fn verify<'a>(
&self, tx: &'a CommitmentTransaction,
) -> Result<TrustedCommitmentTransaction<'a>, ()> {
tx.verify(&self.channel_parameters.as_holder_broadcastable(), &self.secp_ctx)
}
}
#[test]
#[rustfmt::skip]
fn test_anchors() {
let mut builder = TestCommitmentTxBuilder::new();
let tx = builder.build(1000, 2000, Vec::new());
assert_eq!(tx.built.transaction.output.len(), 2);
assert_eq!(tx.built.transaction.output[1].script_pubkey, bitcoin::address::Address::p2wpkh(&CompressedPublicKey(builder.counterparty_pubkeys.payment_point), Network::Testnet).script_pubkey());
builder.channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies();
let tx = builder.build(1000, 2000, Vec::new());
assert_eq!(tx.built.transaction.output.len(), 4);
assert_eq!(tx.built.transaction.output[3].script_pubkey, get_to_countersigner_keyed_anchor_redeemscript(&builder.counterparty_pubkeys.payment_point).to_p2wsh());
assert_eq!(tx.built.transaction.output[0].script_pubkey, get_keyed_anchor_redeemscript(&builder.channel_parameters.holder_pubkeys.funding_pubkey).to_p2wsh());
assert_eq!(tx.built.transaction.output[0].value.to_sat(), 330);
assert_eq!(tx.built.transaction.output[1].script_pubkey, get_keyed_anchor_redeemscript(&builder.counterparty_pubkeys.funding_pubkey).to_p2wsh());
assert_eq!(tx.built.transaction.output[1].value.to_sat(), 330);
let tx = builder.build(3000, 0, Vec::new());
assert_eq!(tx.built.transaction.output.len(), 2);
assert_eq!(tx.built.transaction.output[0].script_pubkey, get_keyed_anchor_redeemscript(&builder.channel_parameters.holder_pubkeys.funding_pubkey).to_p2wsh());
assert_eq!(tx.built.transaction.output[0].value.to_sat(), 330);
let tx = builder.build(0, 3000, Vec::new());
assert_eq!(tx.built.transaction.output.len(), 2);
assert_eq!(tx.built.transaction.output[0].script_pubkey, get_keyed_anchor_redeemscript(&builder.counterparty_pubkeys.funding_pubkey).to_p2wsh());
assert_eq!(tx.built.transaction.output[0].value.to_sat(), 330);
builder.channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_fee_commitments();
let tx = builder.build(1000, 2000, Vec::new());
assert_eq!(tx.built.transaction.output.len(), 3);
assert_eq!(tx.built.transaction.output[2].script_pubkey, bitcoin::address::Address::p2wpkh(&CompressedPublicKey(builder.counterparty_pubkeys.payment_point), Network::Testnet).script_pubkey());
assert_eq!(tx.built.transaction.output[0].script_pubkey, shared_anchor_script_pubkey());
assert_eq!(tx.built.transaction.output[0].value.to_sat(), 240);
let tx = builder.build(3000, 0, Vec::new());
assert_eq!(tx.built.transaction.output.len(), 2);
assert_eq!(tx.built.transaction.output[0].script_pubkey, shared_anchor_script_pubkey());
assert_eq!(tx.built.transaction.output[0].value.to_sat(), 240);
let tx = builder.build(0, 3000, Vec::new());
assert_eq!(tx.built.transaction.output.len(), 2);
assert_eq!(tx.built.transaction.output[0].script_pubkey, shared_anchor_script_pubkey());
assert_eq!(tx.built.transaction.output[0].value.to_sat(), 240);
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,
};
builder.channel_parameters.channel_type_features = ChannelTypeFeatures::only_static_remote_key();
let tx = builder.build(3000, 0, vec![received_htlc.clone(), offered_htlc.clone()]);
let keys = tx.trust().keys();
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_p2wsh());
assert_eq!(tx.built.transaction.output[1].script_pubkey, get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_p2wsh());
assert_eq!(get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_p2wsh().to_hex_string(),
"0020e43a7c068553003fe68fcae424fb7b28ec5ce48cd8b6744b3945631389bad2fb");
assert_eq!(get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_p2wsh().to_hex_string(),
"0020215d61bba56b19e9eadb6107f5a85d7f99c40f65992443f69229c290165bc00d");
builder.channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies();
let tx = builder.build(3000, 0, vec![received_htlc.clone(), offered_htlc.clone()]);
assert_eq!(tx.built.transaction.output.len(), 5);
assert_eq!(tx.built.transaction.output[0].script_pubkey, get_keyed_anchor_redeemscript(&builder.channel_parameters.holder_pubkeys.funding_pubkey).to_p2wsh());
assert_eq!(tx.built.transaction.output[0].value.to_sat(), 330);
assert_eq!(tx.built.transaction.output[1].script_pubkey, get_keyed_anchor_redeemscript(&builder.counterparty_pubkeys.funding_pubkey).to_p2wsh());
assert_eq!(tx.built.transaction.output[1].value.to_sat(), 330);
assert_eq!(tx.built.transaction.output[2].script_pubkey, get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(), &keys).to_p2wsh());
assert_eq!(tx.built.transaction.output[3].script_pubkey, get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(), &keys).to_p2wsh());
assert_eq!(get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(), &keys).to_p2wsh().to_hex_string(),
"0020b70d0649c72b38756885c7a30908d912a7898dd5d79457a7280b8e9a20f3f2bc");
assert_eq!(get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(), &keys).to_p2wsh().to_hex_string(),
"002087a3faeb1950a469c0e2db4a79b093a41b9526e5a6fc6ef5cb949bde3be379c7");
builder.channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_fee_commitments();
let tx = builder.build(3000, 0, vec![received_htlc.clone(), offered_htlc.clone()]);
assert_eq!(tx.built.transaction.output.len(), 4);
assert_eq!(tx.built.transaction.output[0].script_pubkey, shared_anchor_script_pubkey());
assert_eq!(tx.built.transaction.output[0].value.to_sat(), 0);
assert_eq!(tx.built.transaction.output[1].script_pubkey, get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::anchors_zero_fee_commitments(), &keys).to_p2wsh());
assert_eq!(tx.built.transaction.output[2].script_pubkey, get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::anchors_zero_fee_commitments(), &keys).to_p2wsh());
assert_eq!(get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::anchors_zero_fee_commitments(), &keys).to_p2wsh().to_hex_string(),
"0020e43a7c068553003fe68fcae424fb7b28ec5ce48cd8b6744b3945631389bad2fb");
assert_eq!(get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::anchors_zero_fee_commitments(), &keys).to_p2wsh().to_hex_string(),
"0020215d61bba56b19e9eadb6107f5a85d7f99c40f65992443f69229c290165bc00d");
}
#[test]
fn test_finding_revokeable_output_index() {
let builder = TestCommitmentTxBuilder::new();
let tx = builder.build(1000, 2000, Vec::new());
assert_eq!(tx.built.transaction.output.len(), 2);
assert_eq!(tx.trust().revokeable_output_index(), Some(0));
let tx = CommitmentTransaction { to_broadcaster_delay: None, ..tx };
assert_eq!(tx.built.transaction.output.len(), 2);
assert_eq!(tx.trust().revokeable_output_index(), None);
let tx = builder.build(0, 2000, Vec::new());
assert_eq!(tx.built.transaction.output.len(), 1);
assert_eq!(tx.trust().revokeable_output_index(), None);
}
#[test]
#[rustfmt::skip]
fn test_building_to_local_justice_tx() {
let builder = TestCommitmentTxBuilder::new();
let tx = builder.build(0, 2000, Vec::new());
assert_eq!(tx.built.transaction.output.len(), 1);
assert!(tx.trust().build_to_local_justice_tx(253, ScriptBuf::new()).is_err());
let tx = builder.build(1000, 2000, Vec::new());
assert_eq!(tx.built.transaction.output.len(), 2);
assert!(tx.trust().build_to_local_justice_tx(100_000, ScriptBuf::new()).is_err());
let secret_key = SecretKey::from_slice(
&<Vec<u8>>::from_hex("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100")
.unwrap()[..]).unwrap();
let pubkey_hash = BitcoinPublicKey::new(
PublicKey::from_secret_key(&Secp256k1::new(), &secret_key)).wpubkey_hash().unwrap();
let destination_script = ScriptBuf::new_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.compute_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.to_sat() < 1000);
assert_eq!(justice_tx.output[0].script_pubkey, destination_script);
}
#[test]
fn test_per_commitment_storage() {
let mut secrets: Vec<[u8; 32]> = Vec::new();
let mut monitor;
#[rustfmt::skip]
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());
};
}
{
monitor = CounterpartyCommitmentSecrets::new();
secrets.clear();
let hex = "7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710655, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "c7518c8ae4660ed02894df8976fa1a3659c1a8b4b5bec0c4b872abeba4cb8964";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710654, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710653, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710652, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "c65716add7aa98ba7acb236352d665cab17345fe45b55fb879ff80e6bd0c41dd";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710651, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "969660042a28f32d9be17344e09374b379962d03db1574df5a8a5a47e19ce3f2";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710650, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "a5a64476122ca0925fb344bdc1854c1c0a59fc614298e50a33e331980a220f32";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710649, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "05cde6323d949933f7f7b78776bcc1ea6d9b31447732e3802e1f7ac44b650e17";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710648, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
}
{
monitor = CounterpartyCommitmentSecrets::new();
secrets.clear();
let hex = "02a40c85b6f28da08dfdbe0926c53fab2de6d28c10301f8f7c4073d5e42e3148";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710655, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "c7518c8ae4660ed02894df8976fa1a3659c1a8b4b5bec0c4b872abeba4cb8964";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
assert!(monitor
.provide_secret(281474976710654, secrets.last().unwrap().clone())
.is_err());
}
{
monitor = CounterpartyCommitmentSecrets::new();
secrets.clear();
let hex = "02a40c85b6f28da08dfdbe0926c53fab2de6d28c10301f8f7c4073d5e42e3148";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710655, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "dddc3a8d14fddf2b68fa8c7fbad2748274937479dd0f8930d5ebb4ab6bd866a3";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710654, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710653, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
assert!(monitor
.provide_secret(281474976710652, secrets.last().unwrap().clone())
.is_err());
}
{
monitor = CounterpartyCommitmentSecrets::new();
secrets.clear();
let hex = "7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710655, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "c7518c8ae4660ed02894df8976fa1a3659c1a8b4b5bec0c4b872abeba4cb8964";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710654, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "c51a18b13e8527e579ec56365482c62f180b7d5760b46e9477dae59e87ed423a";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710653, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
assert!(monitor
.provide_secret(281474976710652, secrets.last().unwrap().clone())
.is_err());
}
{
monitor = CounterpartyCommitmentSecrets::new();
secrets.clear();
let hex = "02a40c85b6f28da08dfdbe0926c53fab2de6d28c10301f8f7c4073d5e42e3148";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710655, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "dddc3a8d14fddf2b68fa8c7fbad2748274937479dd0f8930d5ebb4ab6bd866a3";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710654, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "c51a18b13e8527e579ec56365482c62f180b7d5760b46e9477dae59e87ed423a";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710653, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "ba65d7b0ef55a3ba300d4e87af29868f394f8f138d78a7011669c79b37b936f4";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710652, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "c65716add7aa98ba7acb236352d665cab17345fe45b55fb879ff80e6bd0c41dd";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710651, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "969660042a28f32d9be17344e09374b379962d03db1574df5a8a5a47e19ce3f2";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710650, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "a5a64476122ca0925fb344bdc1854c1c0a59fc614298e50a33e331980a220f32";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710649, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "05cde6323d949933f7f7b78776bcc1ea6d9b31447732e3802e1f7ac44b650e17";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
assert!(monitor
.provide_secret(281474976710648, secrets.last().unwrap().clone())
.is_err());
}
{
monitor = CounterpartyCommitmentSecrets::new();
secrets.clear();
let hex = "7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710655, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "c7518c8ae4660ed02894df8976fa1a3659c1a8b4b5bec0c4b872abeba4cb8964";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710654, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710653, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710652, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "631373ad5f9ef654bb3dade742d09504c567edd24320d2fcd68e3cc47e2ff6a6";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710651, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "969660042a28f32d9be17344e09374b379962d03db1574df5a8a5a47e19ce3f2";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
assert!(monitor
.provide_secret(281474976710650, secrets.last().unwrap().clone())
.is_err());
}
{
monitor = CounterpartyCommitmentSecrets::new();
secrets.clear();
let hex = "7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710655, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "c7518c8ae4660ed02894df8976fa1a3659c1a8b4b5bec0c4b872abeba4cb8964";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710654, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710653, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710652, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "631373ad5f9ef654bb3dade742d09504c567edd24320d2fcd68e3cc47e2ff6a6";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710651, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "b7e76a83668bde38b373970155c868a653304308f9896692f904a23731224bb1";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710650, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "a5a64476122ca0925fb344bdc1854c1c0a59fc614298e50a33e331980a220f32";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710649, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "05cde6323d949933f7f7b78776bcc1ea6d9b31447732e3802e1f7ac44b650e17";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
assert!(monitor
.provide_secret(281474976710648, secrets.last().unwrap().clone())
.is_err());
}
{
monitor = CounterpartyCommitmentSecrets::new();
secrets.clear();
let hex = "7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710655, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "c7518c8ae4660ed02894df8976fa1a3659c1a8b4b5bec0c4b872abeba4cb8964";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710654, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710653, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710652, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "c65716add7aa98ba7acb236352d665cab17345fe45b55fb879ff80e6bd0c41dd";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710651, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "969660042a28f32d9be17344e09374b379962d03db1574df5a8a5a47e19ce3f2";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710650, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "e7971de736e01da8ed58b94c2fc216cb1dca9e326f3a96e7194fe8ea8af6c0a3";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710649, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "05cde6323d949933f7f7b78776bcc1ea6d9b31447732e3802e1f7ac44b650e17";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
assert!(monitor
.provide_secret(281474976710648, secrets.last().unwrap().clone())
.is_err());
}
{
monitor = CounterpartyCommitmentSecrets::new();
secrets.clear();
let hex = "7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710655, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "c7518c8ae4660ed02894df8976fa1a3659c1a8b4b5bec0c4b872abeba4cb8964";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710654, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710653, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710652, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "c65716add7aa98ba7acb236352d665cab17345fe45b55fb879ff80e6bd0c41dd";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710651, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "969660042a28f32d9be17344e09374b379962d03db1574df5a8a5a47e19ce3f2";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710650, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "a5a64476122ca0925fb344bdc1854c1c0a59fc614298e50a33e331980a220f32";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
monitor.provide_secret(281474976710649, secrets.last().unwrap().clone()).unwrap();
test_secrets!();
let hex = "a7efbc61aac46d34f77778bac22c8a20c6a46ca460addc49009bda875ec88fa4";
secrets.push([0; 32]);
secrets.last_mut().unwrap()[0..32].clone_from_slice(&<Vec<u8>>::from_hex(hex).unwrap());
assert!(monitor
.provide_secret(281474976710648, secrets.last().unwrap().clone())
.is_err());
}
}
#[test]
fn test_verify_sorted_htlcs() {
#[rustfmt::skip]
macro_rules! swap_htlcs {
($small_htlc: expr, $big_htlc: expr) => {
let builder = TestCommitmentTxBuilder::new();
let nondust_htlcs = vec![$small_htlc.clone(), $big_htlc.clone()];
let mut commit_tx = builder.build(0, 0, nondust_htlcs.clone());
builder.verify(&commit_tx).unwrap();
assert_eq!(commit_tx.nondust_htlcs, nondust_htlcs);
commit_tx.nondust_htlcs.swap(0, 1);
let mut transaction = commit_tx.built.transaction.clone();
assert_eq!(transaction.output.len(), 2);
transaction.output.swap(0, 1);
let txid = transaction.compute_txid();
let built = BuiltCommitmentTransaction {
transaction,
txid,
};
commit_tx.built = built;
assert!(builder.verify(&commit_tx).is_err());
}
}
let small_htlc = HTLCOutputInCommitment {
offered: true,
amount_msat: 10_000,
cltv_expiry: 123,
payment_hash: PaymentHash([0xbb; 32]),
transaction_output_index: Some(0),
};
let mut big_htlc = small_htlc.clone();
big_htlc.amount_msat = 20_000;
big_htlc.transaction_output_index = Some(1);
swap_htlcs!(small_htlc.clone(), big_htlc);
let mut big_htlc = small_htlc.clone();
big_htlc.payment_hash = PaymentHash([0xaa; 32]);
big_htlc.transaction_output_index = Some(1);
swap_htlcs!(small_htlc.clone(), big_htlc);
let mut big_htlc = small_htlc.clone();
big_htlc.cltv_expiry = 124;
big_htlc.transaction_output_index = Some(1);
swap_htlcs!(small_htlc, big_htlc);
}
}