use bitcoin::hashes::ripemd160::Hash as Ripemd160;
use bitcoin::hash_types::{Txid, PubkeyHash};
-use ln::{PaymentHash, PaymentPreimage};
-use ln::msgs::DecodeError;
-use util::ser::{Readable, Writeable, Writer};
-use util::{byte_utils, transaction_utils};
+use crate::ln::{PaymentHash, PaymentPreimage};
+use crate::ln::msgs::DecodeError;
+use crate::util::ser::{Readable, Writeable, Writer};
+use crate::util::{byte_utils, transaction_utils};
use bitcoin::hash_types::WPubkeyHash;
-use bitcoin::secp256k1::{SecretKey, PublicKey};
+use bitcoin::secp256k1::{SecretKey, PublicKey, Scalar};
use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature, Message};
use bitcoin::secp256k1::Error as SecpError;
-use bitcoin::{secp256k1, Witness};
+use bitcoin::{PackedLockTime, secp256k1, Sequence, Witness};
-use io;
-use prelude::*;
+use crate::io;
+use crate::prelude::*;
use core::cmp;
-use ln::chan_utils;
-use util::transaction_utils::sort_outputs;
-use ln::channel::{INITIAL_COMMITMENT_NUMBER, ANCHOR_OUTPUT_VALUE_SATOSHI};
+use crate::ln::chan_utils;
+use crate::util::transaction_utils::sort_outputs;
+use crate::ln::channel::{INITIAL_COMMITMENT_NUMBER, ANCHOR_OUTPUT_VALUE_SATOSHI};
use core::ops::Deref;
-use chain;
-use util::crypto::sign;
+use crate::chain;
+use crate::util::crypto::sign;
pub(crate) const MAX_HTLCS: u16 = 483;
+pub(crate) const OFFERED_HTLC_SCRIPT_WEIGHT: usize = 133;
+pub(crate) const OFFERED_HTLC_SCRIPT_WEIGHT_ANCHORS: usize = 136;
+// The weight of `accepted_htlc_script` can vary in function of its CLTV argument value. We define a
+// range that encompasses both its non-anchors and anchors variants.
+pub(crate) const MIN_ACCEPTED_HTLC_SCRIPT_WEIGHT: usize = 136;
+pub(crate) const MAX_ACCEPTED_HTLC_SCRIPT_WEIGHT: usize = 143;
/// Gets the weight for an HTLC-Success transaction.
#[inline]
if opt_anchors { HTLC_TIMEOUT_ANCHOR_TX_WEIGHT } else { HTLC_TIMEOUT_TX_WEIGHT }
}
-#[derive(PartialEq)]
-pub(crate) enum HTLCType {
- AcceptedHTLC,
- OfferedHTLC
+#[derive(PartialEq, Eq)]
+pub(crate) enum HTLCClaim {
+ OfferedTimeout,
+ OfferedPreimage,
+ AcceptedTimeout,
+ AcceptedPreimage,
+ Revocation,
}
-impl HTLCType {
- /// Check if a given tx witnessScript len matchs one of a pre-signed HTLC
- pub(crate) fn scriptlen_to_htlctype(witness_script_len: usize) -> Option<HTLCType> {
- if witness_script_len == 133 {
- Some(HTLCType::OfferedHTLC)
- } else if witness_script_len >= 136 && witness_script_len <= 139 {
- Some(HTLCType::AcceptedHTLC)
+impl HTLCClaim {
+ /// Check if a given input witness attempts to claim a HTLC.
+ pub(crate) fn from_witness(witness: &Witness) -> Option<Self> {
+ debug_assert_eq!(OFFERED_HTLC_SCRIPT_WEIGHT_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 {
+ // <revocation sig> <revocationpubkey> <witness_script>
+ Some(Self::Revocation)
+ } else if witness.len() == 3 && second_to_last.len() == 32 {
+ // <remotehtlcsig> <payment_preimage> <witness_script>
+ Some(Self::OfferedPreimage)
+ } else if witness.len() == 5 && second_to_last.len() == 0 {
+ // 0 <remotehtlcsig> <localhtlcsig> <> <witness_script>
+ Some(Self::OfferedTimeout)
+ } else {
+ None
+ }
+ } else if witness_script.len() == OFFERED_HTLC_SCRIPT_WEIGHT_ANCHORS {
+ // It's possible for the weight of `offered_htlc_script` and `accepted_htlc_script` to
+ // match so we check for both here.
+ if witness.len() == 3 && second_to_last.len() == 33 {
+ // <revocation sig> <revocationpubkey> <witness_script>
+ Some(Self::Revocation)
+ } else if witness.len() == 3 && second_to_last.len() == 32 {
+ // <remotehtlcsig> <payment_preimage> <witness_script>
+ Some(Self::OfferedPreimage)
+ } else if witness.len() == 5 && second_to_last.len() == 0 {
+ // 0 <remotehtlcsig> <localhtlcsig> <> <witness_script>
+ Some(Self::OfferedTimeout)
+ } else if witness.len() == 3 && second_to_last.len() == 0 {
+ // <remotehtlcsig> <> <witness_script>
+ Some(Self::AcceptedTimeout)
+ } else if witness.len() == 5 && second_to_last.len() == 32 {
+ // 0 <remotehtlcsig> <localhtlcsig> <payment_preimage> <witness_script>
+ Some(Self::AcceptedPreimage)
+ } else {
+ None
+ }
+ } else if witness_script.len() > MIN_ACCEPTED_HTLC_SCRIPT_WEIGHT &&
+ witness_script.len() <= MAX_ACCEPTED_HTLC_SCRIPT_WEIGHT {
+ // Handle remaining range of ACCEPTED_HTLC_SCRIPT_WEIGHT.
+ if witness.len() == 3 && second_to_last.len() == 33 {
+ // <revocation sig> <revocationpubkey> <witness_script>
+ Some(Self::Revocation)
+ } else if witness.len() == 3 && second_to_last.len() == 0 {
+ // <remotehtlcsig> <> <witness_script>
+ Some(Self::AcceptedTimeout)
+ } else if witness.len() == 5 && second_to_last.len() == 32 {
+ // 0 <remotehtlcsig> <localhtlcsig> <payment_preimage> <witness_script>
+ Some(Self::AcceptedPreimage)
+ } else {
+ None
+ }
} else {
None
}
ins.push(TxIn {
previous_output: funding_outpoint,
script_sig: Script::new(),
- sequence: 0xffffffff,
+ sequence: Sequence::MAX,
witness: Witness::new(),
});
ins
Transaction {
version: 2,
- lock_time: 0,
+ lock_time: PackedLockTime::ZERO,
input: txins,
output: outputs,
}
}
/// Implements the per-commitment secret storage scheme from
-/// [BOLT 3](https://github.com/lightningnetwork/lightning-rfc/blob/dcbf8583976df087c79c3ce0b535311212e6812d/03-transactions.md#efficient-per-commitment-secret-storage).
+/// [BOLT 3](https://github.com/lightning/bolts/blob/dcbf8583976df087c79c3ce0b535311212e6812d/03-transactions.md#efficient-per-commitment-secret-storage).
///
/// Allows us to keep track of all of the revocation secrets of our counterparty in just 50*32 bytes
/// or so.
old_secrets: [([u8; 32], u64); 49],
}
+impl Eq for CounterpartyCommitmentSecrets {}
impl PartialEq for CounterpartyCommitmentSecrets {
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()) {
sha.input(&PublicKey::from_secret_key(&secp_ctx, &base_secret).serialize());
let res = Sha256::from_engine(sha).into_inner();
- let mut key = base_secret.clone();
- key.add_assign(&res)?;
- Ok(key)
+ base_secret.clone().add_tweak(&Scalar::from_be_bytes(res).unwrap())
}
/// Derives a per-commitment-transaction public key (eg an htlc key or a delayed_payment key)
/// Derives a per-commitment-transaction revocation key from its constituent parts.
///
-/// Only the cheating participant owns a valid witness to propagate a revoked
+/// Only the cheating participant owns a valid witness to propagate a revoked
/// commitment transaction, thus per_commitment_secret always come from cheater
/// and revocation_base_secret always come from punisher, which is the broadcaster
/// of the transaction spending with this key knowledge.
Sha256::from_engine(sha).into_inner()
};
- let mut countersignatory_contrib = countersignatory_revocation_base_secret.clone();
- countersignatory_contrib.mul_assign(&rev_append_commit_hash_key)?;
- let mut broadcaster_contrib = per_commitment_secret.clone();
- broadcaster_contrib.mul_assign(&commit_append_rev_hash_key)?;
- countersignatory_contrib.add_assign(&broadcaster_contrib[..])?;
- Ok(countersignatory_contrib)
+ let countersignatory_contrib = countersignatory_revocation_base_secret.clone().mul_tweak(&Scalar::from_be_bytes(rev_append_commit_hash_key).unwrap())?;
+ let broadcaster_contrib = per_commitment_secret.clone().mul_tweak(&Scalar::from_be_bytes(commit_append_rev_hash_key).unwrap())?;
+ countersignatory_contrib.add_tweak(&Scalar::from_be_bytes(broadcaster_contrib.secret_bytes()).unwrap())
}
/// Derives a per-commitment-transaction revocation public key from its constituent parts. This is
/// the public equivalend of derive_private_revocation_key - using only public keys to derive a
/// public key instead of private keys.
///
-/// Only the cheating participant owns a valid witness to propagate a revoked
+/// Only the cheating participant owns a valid witness to propagate a revoked
/// commitment transaction, thus per_commitment_point always come from cheater
/// and revocation_base_point always come from punisher, which is the broadcaster
/// of the transaction spending with this key knowledge.
Sha256::from_engine(sha).into_inner()
};
- let mut countersignatory_contrib = countersignatory_revocation_base_point.clone();
- countersignatory_contrib.mul_assign(&secp_ctx, &rev_append_commit_hash_key)?;
- let mut broadcaster_contrib = per_commitment_point.clone();
- broadcaster_contrib.mul_assign(&secp_ctx, &commit_append_rev_hash_key)?;
+ let countersignatory_contrib = countersignatory_revocation_base_point.clone().mul_tweak(&secp_ctx, &Scalar::from_be_bytes(rev_append_commit_hash_key).unwrap())?;
+ let broadcaster_contrib = per_commitment_point.clone().mul_tweak(&secp_ctx, &Scalar::from_be_bytes(commit_append_rev_hash_key).unwrap())?;
countersignatory_contrib.combine(&broadcaster_contrib)
}
/// channel basepoints via the new function, or they were obtained via
/// CommitmentTransaction.trust().keys() because we trusted the source of the
/// pre-calculated keys.
-#[derive(PartialEq, Clone)]
+#[derive(PartialEq, Eq, Clone)]
pub struct TxCreationKeys {
/// The broadcaster's per-commitment public key which was used to derive the other keys.
pub per_commitment_point: PublicKey,
});
/// One counterparty's public keys which do not change over the life of a channel.
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Eq)]
pub struct ChannelPublicKeys {
/// The public key which is used to sign all commitment transactions, as it appears in the
/// on-chain channel lock-in 2-of-2 multisig output.
res
}
-#[derive(Clone, PartialEq)]
/// Information about an HTLC as it appears in a commitment transaction
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HTLCOutputInCommitment {
/// Whether the HTLC was "offered" (ie outbound in relation to this commitment transaction).
/// Note that this is not the same as whether it is ountbound *from us*. To determine that you
vout: htlc.transaction_output_index.expect("Can't build an HTLC transaction for a dust output"),
},
script_sig: Script::new(),
- sequence: if opt_anchors { 1 } else { 0 },
+ sequence: Sequence(if opt_anchors { 1 } else { 0 }),
witness: Witness::new(),
});
} else {
htlc_success_tx_weight(opt_anchors)
};
- let total_fee = feerate_per_kw as u64 * weight / 1000;
+ let output_value = if opt_anchors {
+ htlc.amount_msat / 1000
+ } else {
+ let total_fee = feerate_per_kw as u64 * weight / 1000;
+ htlc.amount_msat / 1000 - total_fee
+ };
let mut txouts: Vec<TxOut> = Vec::new();
txouts.push(TxOut {
script_pubkey: get_revokeable_redeemscript(revocation_key, contest_delay, broadcaster_delayed_payment_key).to_v0_p2wsh(),
- value: htlc.amount_msat / 1000 - total_fee //TODO: BOLT 3 does not specify if we should add amount_msat before dividing or if we should divide by 1000 before subtracting (as we do here)
+ value: output_value,
});
Transaction {
version: 2,
- lock_time: if htlc.offered { htlc.cltv_expiry } else { 0 },
+ lock_time: PackedLockTime(if htlc.offered { htlc.cltv_expiry } else { 0 }),
input: txins,
output: txouts,
}
.into_script()
}
+#[cfg(anchors)]
+/// Locates the output with an anchor script paying to `funding_pubkey` within `commitment_tx`.
+pub(crate) fn get_anchor_output<'a>(commitment_tx: &'a Transaction, funding_pubkey: &PublicKey) -> Option<(u32, &'a TxOut)> {
+ let anchor_script = chan_utils::get_anchor_redeemscript(funding_pubkey).to_v0_p2wsh();
+ commitment_tx.output.iter().enumerate()
+ .find(|(_, txout)| txout.script_pubkey == anchor_script)
+ .map(|(idx, txout)| (idx as u32, txout))
+}
+
+/// Returns the witness required to satisfy and spend an anchor input.
+pub fn build_anchor_input_witness(funding_key: &PublicKey, funding_sig: &Signature) -> Witness {
+ let anchor_redeem_script = chan_utils::get_anchor_redeemscript(funding_key);
+ let mut funding_sig = funding_sig.serialize_der().to_vec();
+ funding_sig.push(EcdsaSighashType::All as u8);
+ Witness::from_vec(vec![funding_sig, anchor_redeem_script.to_bytes()])
+}
+
/// Per-channel data used to build transactions in conjunction with the per-commitment data (CommitmentTransaction).
/// The fields are organized by holder/counterparty.
///
pub counterparty_parameters: Option<CounterpartyChannelTransactionParameters>,
/// The late-bound funding outpoint
pub funding_outpoint: Option<chain::transaction::OutPoint>,
- /// Are anchors used for this channel. Boolean is serialization backwards-compatible
+ /// Are anchors (zero fee HTLC transaction variant) used for this channel. Boolean is
+ /// serialization backwards-compatible.
pub opt_anchors: Option<()>
}
fn deref(&self) -> &Self::Target { &self.inner }
}
+impl Eq for HolderCommitmentTransaction {}
impl PartialEq for HolderCommitmentTransaction {
// We dont care whether we are signed in equality comparison
fn eq(&self, o: &Self) -> bool {
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(chain::transaction::OutPoint { txid: Default::default(), index: 0 }),
+ funding_outpoint: Some(chain::transaction::OutPoint { txid: Txid::all_zeros(), index: 0 }),
opt_anchors: None
};
let mut htlcs_with_aux: Vec<(_, ())> = Vec::new();
///
/// This class can be used inside a signer implementation to generate a signature given the relevant
/// secret key.
-#[derive(Clone, Hash, PartialEq)]
+#[derive(Clone, Hash, PartialEq, Eq)]
pub struct ClosingTransaction {
to_holder_value_sat: u64,
to_counterparty_value_sat: u64,
built: BuiltCommitmentTransaction,
}
+impl Eq for CommitmentTransaction {}
impl PartialEq for CommitmentTransaction {
fn eq(&self, o: &Self) -> bool {
let eq = self.commitment_number == o.commitment_number &&
fn make_transaction(obscured_commitment_transaction_number: u64, txins: Vec<TxIn>, outputs: Vec<TxOut>) -> Transaction {
Transaction {
version: 2,
- lock_time: ((0x20 as u32) << 8 * 3) | ((obscured_commitment_transaction_number & 0xffffffu64) as u32),
+ lock_time: PackedLockTime(((0x20 as u32) << 8 * 3) | ((obscured_commitment_transaction_number & 0xffffffu64) as u32)),
input: txins,
output: outputs,
}
ins.push(TxIn {
previous_output: channel_parameters.funding_outpoint(),
script_sig: Script::new(),
- sequence: ((0x80 as u32) << 8 * 3)
- | ((obscured_commitment_transaction_number >> 3 * 8) as u32),
+ sequence: Sequence(((0x80 as u32) << 8 * 3)
+ | ((obscured_commitment_transaction_number >> 3 * 8) as u32)),
witness: Witness::new(),
});
ins
#[cfg(test)]
mod tests {
use super::CounterpartyCommitmentSecrets;
- use ::{hex, chain};
- use prelude::*;
- use ln::chan_utils::{get_htlc_redeemscript, get_to_countersignatory_with_anchors_redeemscript, get_p2wpkh_redeemscript, CommitmentTransaction, TxCreationKeys, ChannelTransactionParameters, CounterpartyChannelTransactionParameters, HTLCOutputInCommitment};
+ use crate::{hex, chain};
+ use crate::prelude::*;
+ use crate::ln::chan_utils::{get_htlc_redeemscript, get_to_countersignatory_with_anchors_redeemscript, get_p2wpkh_redeemscript, CommitmentTransaction, TxCreationKeys, ChannelTransactionParameters, CounterpartyChannelTransactionParameters, HTLCOutputInCommitment};
use bitcoin::secp256k1::{PublicKey, SecretKey, Secp256k1};
- use util::test_utils;
- use chain::keysinterface::{KeysInterface, BaseSign};
- use bitcoin::Network;
- use ln::PaymentHash;
+ use crate::util::test_utils;
+ use crate::chain::keysinterface::{KeysInterface, BaseSign};
+ use bitcoin::{Network, Txid};
+ use bitcoin::hashes::Hash;
+ use crate::ln::PaymentHash;
use bitcoin::hashes::hex::ToHex;
#[test]
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: Default::default(), index: 0 }),
+ funding_outpoint: Some(chain::transaction::OutPoint { txid: Txid::all_zeros(), index: 0 }),
opt_anchors: None
};