X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fchan_utils.rs;h=df0378938b42eeb0b1ff0fbd1413f6f140617a12;hb=7544030bb63fee6484fc178bb2ac8f382fe3b5b1;hp=9e987c3deec005debbdb81fd7557a6be75e955f1;hpb=28d33ff9e03b7e3a0cd7ba3bc59f1303b3903f88;p=rust-lightning diff --git a/lightning/src/ln/chan_utils.rs b/lightning/src/ln/chan_utils.rs index 9e987c3d..df037893 100644 --- a/lightning/src/ln/chan_utils.rs +++ b/lightning/src/ln/chan_utils.rs @@ -26,10 +26,10 @@ use util::ser::{Readable, Writeable, Writer}; use 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::*; @@ -42,6 +42,12 @@ use chain; use 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] @@ -60,18 +66,72 @@ pub fn htlc_timeout_tx_weight(opt_anchors: bool) -> u64 { } #[derive(PartialEq)] -pub(crate) enum HTLCType { - AcceptedHTLC, - OfferedHTLC +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 { - 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 { + 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 { + // + 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 { + // 0 <> + 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 { + // + 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 { + // 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 { + // 0 + 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 { + // + 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 { + // 0 + Some(Self::AcceptedPreimage) + } else { + None + } } else { None } @@ -101,7 +161,7 @@ pub fn build_closing_transaction(to_holder_value_sat: u64, to_counterparty_value ins.push(TxIn { previous_output: funding_outpoint, script_sig: Script::new(), - sequence: 0xffffffff, + sequence: Sequence::MAX, witness: Witness::new(), }); ins @@ -132,14 +192,14 @@ pub fn build_closing_transaction(to_holder_value_sat: u64, to_counterparty_value 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. @@ -264,9 +324,7 @@ pub fn derive_private_key(secp_ctx: &Secp256k1, per_co 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) @@ -287,7 +345,7 @@ pub fn derive_public_key(secp_ctx: &Secp256k1, per_com /// 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. @@ -313,19 +371,16 @@ pub fn derive_private_revocation_key(secp_ctx: &Secp256k1 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. @@ -348,10 +403,8 @@ pub fn derive_public_revocation_key(secp_ctx: &Secp2 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) } @@ -614,7 +667,7 @@ pub fn build_htlc_transaction(commitment_txid: &Txid, feerate_per_kw: u32, conte 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(), }); @@ -623,17 +676,22 @@ pub fn build_htlc_transaction(commitment_txid: &Txid, feerate_per_kw: u32, conte } 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 = 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, } @@ -687,7 +745,8 @@ pub struct ChannelTransactionParameters { pub counterparty_parameters: Option, /// The late-bound funding outpoint pub funding_outpoint: Option, - /// 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<()> } @@ -863,7 +922,7 @@ impl HolderCommitmentTransaction { 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(); @@ -1167,7 +1226,7 @@ impl CommitmentTransaction { fn make_transaction(obscured_commitment_transaction_number: u64, txins: Vec, outputs: Vec) -> 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, } @@ -1291,8 +1350,8 @@ impl CommitmentTransaction { 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 @@ -1508,7 +1567,8 @@ mod tests { use bitcoin::secp256k1::{PublicKey, SecretKey, Secp256k1}; use util::test_utils; use chain::keysinterface::{KeysInterface, BaseSign}; - use bitcoin::Network; + use bitcoin::{Network, Txid}; + use bitcoin::hashes::Hash; use ln::PaymentHash; use bitcoin::hashes::hex::ToHex; @@ -1533,7 +1593,7 @@ mod tests { 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 };