Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Miscellaneous anchor outputs preparatory changes #1685

Merged
merged 7 commits into from
Sep 13, 2022
49 changes: 31 additions & 18 deletions lightning/src/chain/channelmonitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use bitcoin::secp256k1;
use ln::{PaymentHash, PaymentPreimage};
use ln::msgs::DecodeError;
use ln::chan_utils;
use ln::chan_utils::{CounterpartyCommitmentSecrets, HTLCOutputInCommitment, HTLCType, ChannelTransactionParameters, HolderCommitmentTransaction};
use ln::chan_utils::{CounterpartyCommitmentSecrets, HTLCOutputInCommitment, HTLCClaim, ChannelTransactionParameters, HolderCommitmentTransaction};
use ln::channelmanager::HTLCSource;
use chain;
use chain::{BestBlock, WatchedOutput};
Expand Down Expand Up @@ -793,7 +793,10 @@ pub(crate) struct ChannelMonitorImpl<Signer: Sign> {
// of block connection between ChannelMonitors and the ChannelManager.
funding_spend_seen: bool,

/// Set to `Some` of the confirmed transaction spending the funding input of the channel after
/// reaching `ANTI_REORG_DELAY` confirmations.
funding_spend_confirmed: Option<Txid>,

confirmed_commitment_tx_counterparty_output: CommitmentTxCounterpartyOutputInfo,
/// The set of HTLCs which have been either claimed or failed on chain and have reached
/// the requisite confirmations on the claim/fail transaction (either ANTI_REORG_DELAY or the
Expand Down Expand Up @@ -2650,6 +2653,11 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
let commitment_tx = self.onchain_tx_handler.get_fully_signed_holder_tx(&self.funding_redeemscript);
let txid = commitment_tx.txid();
let mut holder_transactions = vec![commitment_tx];
// When anchor outputs are present, the HTLC transactions are only valid once the commitment
// transaction confirms.
if self.onchain_tx_handler.opt_anchors() {
return holder_transactions;
}
for htlc in self.current_holder_commitment_tx.htlc_outputs.iter() {
if let Some(vout) = htlc.0.transaction_output_index {
let preimage = if !htlc.0.offered {
Expand Down Expand Up @@ -2683,6 +2691,11 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
let commitment_tx = self.onchain_tx_handler.get_fully_signed_copy_holder_tx(&self.funding_redeemscript);
let txid = commitment_tx.txid();
let mut holder_transactions = vec![commitment_tx];
// When anchor outputs are present, the HTLC transactions are only final once the commitment
// transaction confirms due to the CSV 1 encumberance.
if self.onchain_tx_handler.opt_anchors() {
return holder_transactions;
}
for htlc in self.current_holder_commitment_tx.htlc_outputs.iter() {
if let Some(vout) = htlc.0.transaction_output_index {
let preimage = if !htlc.0.offered {
Expand Down Expand Up @@ -3068,6 +3081,16 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
}

fn should_broadcast_holder_commitment_txn<L: Deref>(&self, logger: &L) -> bool where L::Target: Logger {
// There's no need to broadcast our commitment transaction if we've seen one confirmed (even
// with 1 confirmation) as it'll be rejected as duplicate/conflicting.
if self.funding_spend_confirmed.is_some() ||
self.onchain_events_awaiting_threshold_conf.iter().find(|event| match event.event {
OnchainEvent::FundingSpendConfirmation { .. } => true,
_ => false,
}).is_some()
{
return false;
}
// We need to consider all HTLCs which are:
// * in any unrevoked counterparty commitment transaction, as they could broadcast said
// transactions and we'd end up in a race, or
Expand Down Expand Up @@ -3136,25 +3159,15 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
fn is_resolving_htlc_output<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L) where L::Target: Logger {
'outer_loop: for input in &tx.input {
let mut payment_data = None;
let witness_items = input.witness.len();
let htlctype = input.witness.last().map(|w| w.len()).and_then(HTLCType::scriptlen_to_htlctype);
let prev_last_witness_len = input.witness.second_to_last().map(|w| w.len()).unwrap_or(0);
let revocation_sig_claim = (witness_items == 3 && htlctype == Some(HTLCType::OfferedHTLC) && prev_last_witness_len == 33)
|| (witness_items == 3 && htlctype == Some(HTLCType::AcceptedHTLC) && prev_last_witness_len == 33);
let accepted_preimage_claim = witness_items == 5 && htlctype == Some(HTLCType::AcceptedHTLC)
&& input.witness.second_to_last().unwrap().len() == 32;
#[cfg(not(fuzzing))]
let accepted_timeout_claim = witness_items == 3 && htlctype == Some(HTLCType::AcceptedHTLC) && !revocation_sig_claim;
let offered_preimage_claim = witness_items == 3 && htlctype == Some(HTLCType::OfferedHTLC) &&
!revocation_sig_claim && input.witness.second_to_last().unwrap().len() == 32;

#[cfg(not(fuzzing))]
let offered_timeout_claim = witness_items == 5 && htlctype == Some(HTLCType::OfferedHTLC);
let htlc_claim = HTLCClaim::from_witness(&input.witness);
let revocation_sig_claim = htlc_claim == Some(HTLCClaim::Revocation);
let accepted_preimage_claim = htlc_claim == Some(HTLCClaim::AcceptedPreimage);
let accepted_timeout_claim = htlc_claim == Some(HTLCClaim::AcceptedTimeout);
let offered_preimage_claim = htlc_claim == Some(HTLCClaim::OfferedPreimage);
let offered_timeout_claim = htlc_claim == Some(HTLCClaim::OfferedTimeout);

let mut payment_preimage = PaymentPreimage([0; 32]);
if accepted_preimage_claim {
payment_preimage.0.copy_from_slice(input.witness.second_to_last().unwrap());
} else if offered_preimage_claim {
if offered_preimage_claim || accepted_preimage_claim {
payment_preimage.0.copy_from_slice(input.witness.second_to_last().unwrap());
}

Expand Down
96 changes: 81 additions & 15 deletions lightning/src/ln/chan_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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<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
}
Expand Down Expand Up @@ -285,7 +345,7 @@ pub fn derive_public_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, 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.
Expand Down Expand Up @@ -320,7 +380,7 @@ pub fn derive_private_revocation_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1
/// 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.
Expand Down Expand Up @@ -616,12 +676,17 @@ 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 {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually we could a) recall opt_anchors to opt_anchors_zero_fee or document the field in ChannelTransactionParameters that we're supporting only option_anchors_zero_fee_htlc_tx variant

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's now documented under ChannelTransactionParameters. I'm not opposed to renaming it but it will be a large diff. Perhaps we can leave it as a follow-up PR?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, can be done in a follow-up PR.

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 {
Expand Down Expand Up @@ -680,7 +745,8 @@ pub struct ChannelTransactionParameters {
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<()>
}

Expand Down
Loading