use bitcoin::blockdata::block::{Block, BlockHeader};
use bitcoin::blockdata::transaction::{TxOut,Transaction};
-use bitcoin::blockdata::transaction::OutPoint as BitcoinOutPoint;
use bitcoin::blockdata::script::{Script, Builder};
use bitcoin::blockdata::opcodes;
use ln::msgs::DecodeError;
use ln::chan_utils;
use ln::chan_utils::{CounterpartyCommitmentSecrets, HTLCOutputInCommitment, HTLCType, ChannelTransactionParameters, HolderCommitmentTransaction};
-use ln::channelmanager::{BestBlock, HTLCSource};
-use ln::onchaintx::{OnchainTxHandler, InputDescriptors};
+use ln::channelmanager::HTLCSource;
use chain;
-use chain::WatchedOutput;
+use chain::{BestBlock, WatchedOutput};
use chain::chaininterface::{BroadcasterInterface, FeeEstimator};
use chain::transaction::{OutPoint, TransactionData};
use chain::keysinterface::{SpendableOutputDescriptor, StaticPaymentOutputDescriptor, DelayedPaymentOutputDescriptor, Sign, KeysInterface};
+use chain::onchaintx::OnchainTxHandler;
+use chain::package::{CounterpartyOfferedHTLCOutput, CounterpartyReceivedHTLCOutput, HolderFundingOutput, HolderHTLCOutput, PackageSolvingData, PackageTemplate, RevokedOutput, RevokedHTLCOutput};
use chain::Filter;
use util::logger::Logger;
-use util::ser::{Readable, ReadableArgs, MaybeReadable, Writer, Writeable, U48};
+use util::ser::{Readable, ReadableArgs, MaybeReadable, Writer, Writeable, U48, OptionDeserWrapper};
use util::byte_utils;
use util::events::Event;
-use std::collections::{HashMap, HashSet};
-use std::{cmp, mem};
+use prelude::*;
+use core::{cmp, mem};
use std::io::Error;
-use std::ops::Deref;
-use std::sync::Mutex;
+use core::ops::Deref;
+use sync::Mutex;
/// An update generated by the underlying Channel itself which contains some new information the
/// ChannelMonitor should be made aware of.
/// then we allow the `ChannelManager` to send a `ChannelMonitorUpdate` with this update ID,
/// with the update providing said payment preimage. No other update types are allowed after
/// force-close.
-pub const CLOSED_CHANNEL_UPDATE_ID: u64 = std::u64::MAX;
+pub const CLOSED_CHANNEL_UPDATE_ID: u64 = core::u64::MAX;
impl Writeable for ChannelMonitorUpdate {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), ::std::io::Error> {
+ write_ver_prefix!(w, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION);
self.update_id.write(w)?;
(self.updates.len() as u64).write(w)?;
for update_step in self.updates.iter() {
update_step.write(w)?;
}
+ write_tlv_fields!(w, {});
Ok(())
}
}
impl Readable for ChannelMonitorUpdate {
fn read<R: ::std::io::Read>(r: &mut R) -> Result<Self, DecodeError> {
+ let _ver = read_ver_prefix!(r, SERIALIZATION_VERSION);
let update_id: u64 = Readable::read(r)?;
let len: u64 = Readable::read(r)?;
- let mut updates = Vec::with_capacity(cmp::min(len as usize, MAX_ALLOC_SIZE / ::std::mem::size_of::<ChannelMonitorUpdateStep>()));
+ let mut updates = Vec::with_capacity(cmp::min(len as usize, MAX_ALLOC_SIZE / ::core::mem::size_of::<ChannelMonitorUpdateStep>()));
for _ in 0..len {
updates.push(Readable::read(r)?);
}
+ read_tlv_fields!(r, {});
Ok(Self { update_id, updates })
}
}
/// An error enum representing a failure to persist a channel monitor update.
-#[derive(Clone, Debug)]
+#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ChannelMonitorUpdateErr {
/// Used to indicate a temporary failure (eg connection to a watchtower or remote backup of
/// our state failed, but is expected to succeed at some point in the future).
pub struct HTLCUpdate {
pub(crate) payment_hash: PaymentHash,
pub(crate) payment_preimage: Option<PaymentPreimage>,
- pub(crate) source: HTLCSource
+ pub(crate) source: HTLCSource,
+ pub(crate) onchain_value_satoshis: Option<u64>,
}
-impl_writeable!(HTLCUpdate, 0, { payment_hash, payment_preimage, source });
+impl_writeable_tlv_based!(HTLCUpdate, {
+ (0, payment_hash, required),
+ (1, onchain_value_satoshis, option),
+ (2, source, required),
+ (4, payment_preimage, option),
+});
/// If an HTLC expires within this many blocks, don't try to claim it in a shared transaction,
/// instead claiming it in its own individual transaction.
/// HTLC-Success transaction.
/// In other words, this is an upper bound on how many blocks we think it can take us to get a
/// transaction confirmed (and we use it in a few more, equivalent, places).
-pub(crate) const CLTV_CLAIM_BUFFER: u32 = 6;
+pub(crate) const CLTV_CLAIM_BUFFER: u32 = 18;
/// Number of blocks by which point we expect our counterparty to have seen new blocks on the
/// network and done a full update_fail_htlc/commitment_signed dance (+ we've updated all our
/// copies of ChannelMonitors, including watchtowers). We could enforce the contract by failing
pub(crate) const LATENCY_GRACE_PERIOD_BLOCKS: u32 = 3;
/// Number of blocks we wait on seeing a HTLC output being solved before we fail corresponding inbound
/// HTLCs. This prevents us from failing backwards and then getting a reorg resulting in us losing money.
-/// We use also this delay to be sure we can remove our in-flight claim txn from bump candidates buffer.
-/// It may cause spurrious generation of bumped claim txn but that's allright given the outpoint is already
-/// solved by a previous claim tx. What we want to avoid is reorg evicting our claim tx and us not
-/// keeping bumping another claim tx to solve the outpoint.
-pub(crate) const ANTI_REORG_DELAY: u32 = 6;
+// We also use this delay to be sure we can remove our in-flight claim txn from bump candidates buffer.
+// It may cause spurious generation of bumped claim txn but that's alright given the outpoint is already
+// solved by a previous claim tx. What we want to avoid is reorg evicting our claim tx and us not
+// keep bumping another claim tx to solve the outpoint.
+pub const ANTI_REORG_DELAY: u32 = 6;
/// Number of blocks before confirmation at which we fail back an un-relayed HTLC or at which we
/// refuse to accept a new HTLC.
///
feerate_per_kw: u32,
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,
}
+impl_writeable_tlv_based!(HolderSignedTx, {
+ (0, txid, required),
+ (2, revocation_key, required),
+ (4, a_htlc_key, required),
+ (6, b_htlc_key, required),
+ (8, delayed_payment_key, required),
+ (10, per_commitment_point, required),
+ (12, feerate_per_kw, required),
+ (14, htlc_outputs, vec_type)
+});
/// We use this to track counterparty commitment transactions and htlcs outputs and
/// use it to generate any justice or 2nd-stage preimage/timeout transactions.
impl Writeable for CounterpartyCommitmentTransaction {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), ::std::io::Error> {
- self.counterparty_delayed_payment_base_key.write(w)?;
- self.counterparty_htlc_base_key.write(w)?;
- w.write_all(&byte_utils::be16_to_array(self.on_counterparty_tx_csv))?;
w.write_all(&byte_utils::be64_to_array(self.per_htlc.len() as u64))?;
for (ref txid, ref htlcs) in self.per_htlc.iter() {
w.write_all(&txid[..])?;
htlc.write(w)?;
}
}
+ write_tlv_fields!(w, {
+ (0, self.counterparty_delayed_payment_base_key, required),
+ (2, self.counterparty_htlc_base_key, required),
+ (4, self.on_counterparty_tx_csv, required),
+ });
Ok(())
}
}
impl Readable for CounterpartyCommitmentTransaction {
fn read<R: ::std::io::Read>(r: &mut R) -> Result<Self, DecodeError> {
let counterparty_commitment_transaction = {
- let counterparty_delayed_payment_base_key = Readable::read(r)?;
- let counterparty_htlc_base_key = Readable::read(r)?;
- let on_counterparty_tx_csv: u16 = Readable::read(r)?;
let per_htlc_len: u64 = Readable::read(r)?;
let mut per_htlc = HashMap::with_capacity(cmp::min(per_htlc_len as usize, MAX_ALLOC_SIZE / 64));
for _ in 0..per_htlc_len {
return Err(DecodeError::InvalidValue);
}
}
+ let mut counterparty_delayed_payment_base_key = OptionDeserWrapper(None);
+ let mut counterparty_htlc_base_key = OptionDeserWrapper(None);
+ let mut on_counterparty_tx_csv: u16 = 0;
+ read_tlv_fields!(r, {
+ (0, counterparty_delayed_payment_base_key, required),
+ (2, counterparty_htlc_base_key, required),
+ (4, on_counterparty_tx_csv, required),
+ });
CounterpartyCommitmentTransaction {
- counterparty_delayed_payment_base_key,
- counterparty_htlc_base_key,
+ counterparty_delayed_payment_base_key: counterparty_delayed_payment_base_key.0.unwrap(),
+ counterparty_htlc_base_key: counterparty_htlc_base_key.0.unwrap(),
on_counterparty_tx_csv,
per_htlc,
}
}
}
-/// When ChannelMonitor discovers an onchain outpoint being a step of a channel and that it needs
-/// to generate a tx to push channel state forward, we cache outpoint-solving tx material to build
-/// a new bumped one in case of lenghty confirmation delay
-#[derive(Clone, PartialEq)]
-pub(crate) enum InputMaterial {
- Revoked {
- per_commitment_point: PublicKey,
- counterparty_delayed_payment_base_key: PublicKey,
- counterparty_htlc_base_key: PublicKey,
- per_commitment_key: SecretKey,
- input_descriptor: InputDescriptors,
- amount: u64,
- htlc: Option<HTLCOutputInCommitment>,
- on_counterparty_tx_csv: u16,
- },
- CounterpartyHTLC {
- per_commitment_point: PublicKey,
- counterparty_delayed_payment_base_key: PublicKey,
- counterparty_htlc_base_key: PublicKey,
- preimage: Option<PaymentPreimage>,
- htlc: HTLCOutputInCommitment
- },
- HolderHTLC {
- preimage: Option<PaymentPreimage>,
- amount: u64,
- },
- Funding {
- funding_redeemscript: Script,
- }
-}
-
-impl Writeable for InputMaterial {
- fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
- match self {
- &InputMaterial::Revoked { ref per_commitment_point, ref counterparty_delayed_payment_base_key, ref counterparty_htlc_base_key, ref per_commitment_key, ref input_descriptor, ref amount, ref htlc, ref on_counterparty_tx_csv} => {
- writer.write_all(&[0; 1])?;
- per_commitment_point.write(writer)?;
- counterparty_delayed_payment_base_key.write(writer)?;
- counterparty_htlc_base_key.write(writer)?;
- writer.write_all(&per_commitment_key[..])?;
- input_descriptor.write(writer)?;
- writer.write_all(&byte_utils::be64_to_array(*amount))?;
- htlc.write(writer)?;
- on_counterparty_tx_csv.write(writer)?;
- },
- &InputMaterial::CounterpartyHTLC { ref per_commitment_point, ref counterparty_delayed_payment_base_key, ref counterparty_htlc_base_key, ref preimage, ref htlc} => {
- writer.write_all(&[1; 1])?;
- per_commitment_point.write(writer)?;
- counterparty_delayed_payment_base_key.write(writer)?;
- counterparty_htlc_base_key.write(writer)?;
- preimage.write(writer)?;
- htlc.write(writer)?;
- },
- &InputMaterial::HolderHTLC { ref preimage, ref amount } => {
- writer.write_all(&[2; 1])?;
- preimage.write(writer)?;
- writer.write_all(&byte_utils::be64_to_array(*amount))?;
- },
- &InputMaterial::Funding { ref funding_redeemscript } => {
- writer.write_all(&[3; 1])?;
- funding_redeemscript.write(writer)?;
- }
- }
- Ok(())
- }
-}
-
-impl Readable for InputMaterial {
- fn read<R: ::std::io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
- let input_material = match <u8 as Readable>::read(reader)? {
- 0 => {
- let per_commitment_point = Readable::read(reader)?;
- let counterparty_delayed_payment_base_key = Readable::read(reader)?;
- let counterparty_htlc_base_key = Readable::read(reader)?;
- let per_commitment_key = Readable::read(reader)?;
- let input_descriptor = Readable::read(reader)?;
- let amount = Readable::read(reader)?;
- let htlc = Readable::read(reader)?;
- let on_counterparty_tx_csv = Readable::read(reader)?;
- InputMaterial::Revoked {
- per_commitment_point,
- counterparty_delayed_payment_base_key,
- counterparty_htlc_base_key,
- per_commitment_key,
- input_descriptor,
- amount,
- htlc,
- on_counterparty_tx_csv
- }
- },
- 1 => {
- let per_commitment_point = Readable::read(reader)?;
- let counterparty_delayed_payment_base_key = Readable::read(reader)?;
- let counterparty_htlc_base_key = Readable::read(reader)?;
- let preimage = Readable::read(reader)?;
- let htlc = Readable::read(reader)?;
- InputMaterial::CounterpartyHTLC {
- per_commitment_point,
- counterparty_delayed_payment_base_key,
- counterparty_htlc_base_key,
- preimage,
- htlc
- }
- },
- 2 => {
- let preimage = Readable::read(reader)?;
- let amount = Readable::read(reader)?;
- InputMaterial::HolderHTLC {
- preimage,
- amount,
- }
- },
- 3 => {
- InputMaterial::Funding {
- funding_redeemscript: Readable::read(reader)?,
- }
- }
- _ => return Err(DecodeError::InvalidValue),
- };
- Ok(input_material)
- }
-}
-
-/// ClaimRequest is a descriptor structure to communicate between detection
-/// and reaction module. They are generated by ChannelMonitor while parsing
-/// onchain txn leaked from a channel and handed over to OnchainTxHandler which
-/// is responsible for opportunistic aggregation, selecting and enforcing
-/// bumping logic, building and signing transactions.
-pub(crate) struct ClaimRequest {
- // Block height before which claiming is exclusive to one party,
- // after reaching it, claiming may be contentious.
- pub(crate) absolute_timelock: u32,
- // Timeout tx must have nLocktime set which means aggregating multiple
- // ones must take the higher nLocktime among them to satisfy all of them.
- // Sadly it has few pitfalls, a) it takes longuer to get fund back b) CLTV_DELTA
- // of a sooner-HTLC could be swallowed by the highest nLocktime of the HTLC set.
- // Do simplify we mark them as non-aggregable.
- pub(crate) aggregable: bool,
- // Basic bitcoin outpoint (txid, vout)
- pub(crate) outpoint: BitcoinOutPoint,
- // Following outpoint type, set of data needed to generate transaction digest
- // and satisfy witness program.
- pub(crate) witness_data: InputMaterial
-}
-
/// An entry for an [`OnchainEvent`], stating the block height when the event was observed and the
/// transaction causing it.
///
impl OnchainEventEntry {
fn confirmation_threshold(&self) -> u32 {
- self.height + ANTI_REORG_DELAY - 1
+ let mut conf_threshold = self.height + ANTI_REORG_DELAY - 1;
+ if let OnchainEvent::MaturingOutput {
+ descriptor: SpendableOutputDescriptor::DelayedPaymentOutput(ref descriptor)
+ } = self.event {
+ // A CSV'd transaction is confirmable in block (input height) + CSV delay, which means
+ // it's broadcastable when we see the previous block.
+ conf_threshold = cmp::max(conf_threshold, self.height + descriptor.to_self_delay as u32 - 1);
+ }
+ conf_threshold
}
- fn has_reached_confirmation_threshold(&self, height: u32) -> bool {
- height >= self.confirmation_threshold()
+ fn has_reached_confirmation_threshold(&self, best_block: &BestBlock) -> bool {
+ best_block.height() >= self.confirmation_threshold()
}
}
/// inbound HTLC in backward channel. Note, in case of preimage, we pass info to upstream without delay as we can
/// only win from it, so it's never an OnchainEvent
HTLCUpdate {
- htlc_update: (HTLCSource, PaymentHash),
+ source: HTLCSource,
+ payment_hash: PaymentHash,
+ onchain_value_satoshis: Option<u64>,
},
MaturingOutput {
descriptor: SpendableOutputDescriptor,
},
}
-const SERIALIZATION_VERSION: u8 = 1;
-const MIN_SERIALIZATION_VERSION: u8 = 1;
+impl_writeable_tlv_based!(OnchainEventEntry, {
+ (0, txid, required),
+ (2, height, required),
+ (4, event, required),
+});
+
+impl_writeable_tlv_based_enum!(OnchainEvent,
+ (0, HTLCUpdate) => {
+ (0, source, required),
+ (1, onchain_value_satoshis, option),
+ (2, payment_hash, required),
+ },
+ (1, MaturingOutput) => {
+ (0, descriptor, required),
+ },
+;);
#[cfg_attr(any(test, feature = "fuzztarget", feature = "_test_utils"), derive(PartialEq))]
#[derive(Clone)]
},
}
-impl Writeable for ChannelMonitorUpdateStep {
- fn write<W: Writer>(&self, w: &mut W) -> Result<(), ::std::io::Error> {
- match self {
- &ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { ref commitment_tx, ref htlc_outputs } => {
- 0u8.write(w)?;
- commitment_tx.write(w)?;
- (htlc_outputs.len() as u64).write(w)?;
- for &(ref output, ref signature, ref source) in htlc_outputs.iter() {
- output.write(w)?;
- signature.write(w)?;
- source.write(w)?;
- }
- }
- &ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { commitment_txid, ref htlc_outputs, ref commitment_number, ref their_revocation_point } => {
- 1u8.write(w)?;
- commitment_txid.write(w)?;
- commitment_number.write(w)?;
- their_revocation_point.write(w)?;
- (htlc_outputs.len() as u64).write(w)?;
- for &(ref output, ref source) in htlc_outputs.iter() {
- output.write(w)?;
- source.as_ref().map(|b| b.as_ref()).write(w)?;
- }
- },
- &ChannelMonitorUpdateStep::PaymentPreimage { ref payment_preimage } => {
- 2u8.write(w)?;
- payment_preimage.write(w)?;
- },
- &ChannelMonitorUpdateStep::CommitmentSecret { ref idx, ref secret } => {
- 3u8.write(w)?;
- idx.write(w)?;
- secret.write(w)?;
- },
- &ChannelMonitorUpdateStep::ChannelForceClosed { ref should_broadcast } => {
- 4u8.write(w)?;
- should_broadcast.write(w)?;
- },
- }
- Ok(())
- }
-}
-impl Readable for ChannelMonitorUpdateStep {
- fn read<R: ::std::io::Read>(r: &mut R) -> Result<Self, DecodeError> {
- match Readable::read(r)? {
- 0u8 => {
- Ok(ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo {
- commitment_tx: Readable::read(r)?,
- htlc_outputs: {
- let len: u64 = Readable::read(r)?;
- let mut res = Vec::new();
- for _ in 0..len {
- res.push((Readable::read(r)?, Readable::read(r)?, Readable::read(r)?));
- }
- res
- },
- })
- },
- 1u8 => {
- Ok(ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo {
- commitment_txid: Readable::read(r)?,
- commitment_number: Readable::read(r)?,
- their_revocation_point: Readable::read(r)?,
- htlc_outputs: {
- let len: u64 = Readable::read(r)?;
- let mut res = Vec::new();
- for _ in 0..len {
- res.push((Readable::read(r)?, <Option<HTLCSource> as Readable>::read(r)?.map(|o| Box::new(o))));
- }
- res
- },
- })
- },
- 2u8 => {
- Ok(ChannelMonitorUpdateStep::PaymentPreimage {
- payment_preimage: Readable::read(r)?,
- })
- },
- 3u8 => {
- Ok(ChannelMonitorUpdateStep::CommitmentSecret {
- idx: Readable::read(r)?,
- secret: Readable::read(r)?,
- })
- },
- 4u8 => {
- Ok(ChannelMonitorUpdateStep::ChannelForceClosed {
- should_broadcast: Readable::read(r)?
- })
- },
- _ => Err(DecodeError::InvalidValue),
- }
- }
-}
+impl_writeable_tlv_based_enum!(ChannelMonitorUpdateStep,
+ (0, LatestHolderCommitmentTXInfo) => {
+ (0, commitment_tx, required),
+ (2, htlc_outputs, vec_type),
+ },
+ (1, LatestCounterpartyCommitmentTXInfo) => {
+ (0, commitment_txid, required),
+ (2, commitment_number, required),
+ (4, their_revocation_point, required),
+ (6, htlc_outputs, vec_type),
+ },
+ (2, PaymentPreimage) => {
+ (0, payment_preimage, required),
+ },
+ (3, CommitmentSecret) => {
+ (0, idx, required),
+ (2, secret, required),
+ },
+ (4, ChannelForceClosed) => {
+ (0, should_broadcast, required),
+ },
+;);
/// A ChannelMonitor handles chain events (blocks connected and disconnected) and generates
/// on-chain transactions to ensure no loss of funds occurs.
impl<Signer: Sign> Writeable for ChannelMonitor<Signer> {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
- //TODO: We still write out all the serialization here manually instead of using the fancy
- //serialization framework we have, we should migrate things over to it.
- writer.write_all(&[SERIALIZATION_VERSION; 1])?;
- writer.write_all(&[MIN_SERIALIZATION_VERSION; 1])?;
-
self.inner.lock().unwrap().write(writer)
}
}
+// These are also used for ChannelMonitorUpdate, above.
+const SERIALIZATION_VERSION: u8 = 1;
+const MIN_SERIALIZATION_VERSION: u8 = 1;
+
impl<Signer: Sign> Writeable for ChannelMonitorImpl<Signer> {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
+ write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION);
+
self.latest_update_id.write(writer)?;
// Set in initial Channel-object creation, so should always be set by now:
writer.write_all(&byte_utils::be48_to_array(*commitment_number))?;
}
- macro_rules! serialize_holder_tx {
- ($holder_tx: expr) => {
- $holder_tx.txid.write(writer)?;
- writer.write_all(&$holder_tx.revocation_key.serialize())?;
- writer.write_all(&$holder_tx.a_htlc_key.serialize())?;
- writer.write_all(&$holder_tx.b_htlc_key.serialize())?;
- writer.write_all(&$holder_tx.delayed_payment_key.serialize())?;
- writer.write_all(&$holder_tx.per_commitment_point.serialize())?;
-
- writer.write_all(&byte_utils::be32_to_array($holder_tx.feerate_per_kw))?;
- writer.write_all(&byte_utils::be64_to_array($holder_tx.htlc_outputs.len() as u64))?;
- for &(ref htlc_output, ref sig, ref htlc_source) in $holder_tx.htlc_outputs.iter() {
- serialize_htlc_in_commitment!(htlc_output);
- if let &Some(ref their_sig) = sig {
- 1u8.write(writer)?;
- writer.write_all(&their_sig.serialize_compact())?;
- } else {
- 0u8.write(writer)?;
- }
- htlc_source.write(writer)?;
- }
- }
- }
-
if let Some(ref prev_holder_tx) = self.prev_holder_signed_commitment_tx {
writer.write_all(&[1; 1])?;
- serialize_holder_tx!(prev_holder_tx);
+ prev_holder_tx.write(writer)?;
} else {
writer.write_all(&[0; 1])?;
}
- serialize_holder_tx!(self.current_holder_commitment_tx);
+ self.current_holder_commitment_tx.write(writer)?;
writer.write_all(&byte_utils::be48_to_array(self.current_counterparty_commitment_number))?;
writer.write_all(&byte_utils::be48_to_array(self.current_holder_commitment_number))?;
writer.write_all(&byte_utils::be64_to_array(self.onchain_events_awaiting_threshold_conf.len() as u64))?;
for ref entry in self.onchain_events_awaiting_threshold_conf.iter() {
- entry.txid.write(writer)?;
- writer.write_all(&byte_utils::be32_to_array(entry.height))?;
- match entry.event {
- OnchainEvent::HTLCUpdate { ref htlc_update } => {
- 0u8.write(writer)?;
- htlc_update.0.write(writer)?;
- htlc_update.1.write(writer)?;
- },
- OnchainEvent::MaturingOutput { ref descriptor } => {
- 1u8.write(writer)?;
- descriptor.write(writer)?;
- },
- }
+ entry.write(writer)?;
}
(self.outputs_to_watch.len() as u64).write(writer)?;
self.lockdown_from_offchain.write(writer)?;
self.holder_tx_signed.write(writer)?;
+ write_tlv_fields!(writer, {});
+
Ok(())
}
}
txids.dedup();
txids
}
+
+ /// Gets the latest best block which was connected either via the [`chain::Listen`] or
+ /// [`chain::Confirm`] interfaces.
+ pub fn current_best_block(&self) -> BestBlock {
+ self.inner.lock().unwrap().best_block.clone()
+ }
}
impl<Signer: Sign> ChannelMonitorImpl<Signer> {
macro_rules! claim_htlcs {
($commitment_number: expr, $txid: expr) => {
let htlc_claim_reqs = self.get_counterparty_htlc_output_claim_reqs($commitment_number, $txid, None);
- self.onchain_tx_handler.update_claims_view(&Vec::new(), htlc_claim_reqs, None, broadcaster, fee_estimator, logger);
+ self.onchain_tx_handler.update_claims_view(&Vec::new(), htlc_claim_reqs, self.best_block.height(), self.best_block.height(), broadcaster, fee_estimator, logger);
}
}
if let Some(txid) = self.current_counterparty_commitment_txid {
// *we* sign a holder commitment transaction, not when e.g. a watchtower broadcasts one of our
// holder commitment transactions.
if self.broadcasted_holder_revokable_script.is_some() {
- let (claim_reqs, _) = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx);
- self.onchain_tx_handler.update_claims_view(&Vec::new(), claim_reqs, None, broadcaster, fee_estimator, logger);
+ // Assume that the broadcasted commitment transaction confirmed in the current best
+ // block. Even if not, its a reasonable metric for the bump criteria on the HTLC
+ // transactions.
+ let (claim_reqs, _) = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx, self.best_block.height());
+ self.onchain_tx_handler.update_claims_view(&Vec::new(), claim_reqs, self.best_block.height(), self.best_block.height(), broadcaster, fee_estimator, logger);
if let Some(ref tx) = self.prev_holder_signed_commitment_tx {
- let (claim_reqs, _) = self.get_broadcasted_holder_claims(&tx);
- self.onchain_tx_handler.update_claims_view(&Vec::new(), claim_reqs, None, broadcaster, fee_estimator, logger);
+ let (claim_reqs, _) = self.get_broadcasted_holder_claims(&tx, self.best_block.height());
+ self.onchain_tx_handler.update_claims_view(&Vec::new(), claim_reqs, self.best_block.height(), self.best_block.height(), broadcaster, fee_estimator, logger);
}
}
}
L::Target: Logger,
{
for tx in self.get_latest_holder_commitment_txn(logger).iter() {
+ log_info!(logger, "Broadcasting local {}", log_tx!(tx));
broadcaster.broadcast_transaction(tx);
}
self.pending_monitor_events.push(MonitorEvent::CommitmentTxBroadcasted(self.funding_info.0));
/// HTLC-Success/HTLC-Timeout transactions.
/// Return updates for HTLC pending in the channel and failed automatically by the broadcast of
/// revoked counterparty commitment tx
- fn check_spend_counterparty_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L) -> (Vec<ClaimRequest>, TransactionOutputs) where L::Target: Logger {
+ fn check_spend_counterparty_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L) -> (Vec<PackageTemplate>, TransactionOutputs) where L::Target: Logger {
// Most secp and related errors trying to create keys means we have no hope of constructing
// a spend transaction...so we return no transactions to broadcast
let mut claimable_outpoints = Vec::new();
// First, process non-htlc outputs (to_holder & to_counterparty)
for (idx, outp) in tx.output.iter().enumerate() {
if outp.script_pubkey == revokeable_p2wsh {
- let witness_data = InputMaterial::Revoked { per_commitment_point, counterparty_delayed_payment_base_key: self.counterparty_tx_cache.counterparty_delayed_payment_base_key, counterparty_htlc_base_key: self.counterparty_tx_cache.counterparty_htlc_base_key, per_commitment_key, input_descriptor: InputDescriptors::RevokedOutput, amount: outp.value, htlc: None, on_counterparty_tx_csv: self.counterparty_tx_cache.on_counterparty_tx_csv};
- claimable_outpoints.push(ClaimRequest { absolute_timelock: height + self.counterparty_tx_cache.on_counterparty_tx_csv as u32, aggregable: true, outpoint: BitcoinOutPoint { txid: commitment_txid, vout: idx as u32 }, witness_data});
+ let revk_outp = RevokedOutput::build(per_commitment_point, self.counterparty_tx_cache.counterparty_delayed_payment_base_key, self.counterparty_tx_cache.counterparty_htlc_base_key, per_commitment_key, outp.value, self.counterparty_tx_cache.on_counterparty_tx_csv);
+ let justice_package = PackageTemplate::build_package(commitment_txid, idx as u32, PackageSolvingData::RevokedOutput(revk_outp), height + self.counterparty_tx_cache.on_counterparty_tx_csv as u32, true, height);
+ claimable_outpoints.push(justice_package);
}
}
tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 {
return (claimable_outpoints, (commitment_txid, watch_outputs)); // Corrupted per_commitment_data, fuck this user
}
- let witness_data = InputMaterial::Revoked { per_commitment_point, counterparty_delayed_payment_base_key: self.counterparty_tx_cache.counterparty_delayed_payment_base_key, counterparty_htlc_base_key: self.counterparty_tx_cache.counterparty_htlc_base_key, per_commitment_key, input_descriptor: if htlc.offered { InputDescriptors::RevokedOfferedHTLC } else { InputDescriptors::RevokedReceivedHTLC }, amount: tx.output[transaction_output_index as usize].value, htlc: Some(htlc.clone()), on_counterparty_tx_csv: self.counterparty_tx_cache.on_counterparty_tx_csv};
- claimable_outpoints.push(ClaimRequest { absolute_timelock: htlc.cltv_expiry, aggregable: true, outpoint: BitcoinOutPoint { txid: commitment_txid, vout: transaction_output_index }, witness_data });
+ let revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, self.counterparty_tx_cache.counterparty_delayed_payment_base_key, self.counterparty_tx_cache.counterparty_htlc_base_key, per_commitment_key, htlc.amount_msat / 1000, htlc.clone());
+ let justice_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, PackageSolvingData::RevokedHTLCOutput(revk_htlc_outp), htlc.cltv_expiry, true, height);
+ claimable_outpoints.push(justice_package);
}
}
}
// Last, track onchain revoked commitment transaction and fail backward outgoing HTLCs as payment path is broken
if !claimable_outpoints.is_empty() || per_commitment_option.is_some() { // ie we're confident this is actually ours
// We're definitely a counterparty commitment transaction!
- log_trace!(logger, "Got broadcast of revoked counterparty commitment transaction, going to generate general spend tx with {} inputs", claimable_outpoints.len());
+ log_error!(logger, "Got broadcast of revoked counterparty commitment transaction, going to generate general spend tx with {} inputs", claimable_outpoints.len());
for (idx, outp) in tx.output.iter().enumerate() {
watch_outputs.push((idx as u32, outp.clone()));
}
self.onchain_events_awaiting_threshold_conf.retain(|ref entry| {
if entry.height != height { return true; }
match entry.event {
- OnchainEvent::HTLCUpdate { ref htlc_update } => {
- htlc_update.0 != **source
- },
- _ => true,
+ OnchainEvent::HTLCUpdate { source: ref update_source, .. } => {
+ *update_source != **source
+ },
+ _ => true,
}
});
let entry = OnchainEventEntry {
txid: *$txid,
height,
event: OnchainEvent::HTLCUpdate {
- htlc_update: ((**source).clone(), htlc.payment_hash.clone())
+ source: (**source).clone(),
+ payment_hash: htlc.payment_hash.clone(),
+ onchain_value_satoshis: Some(htlc.amount_msat / 1000),
},
};
log_info!(logger, "Failing HTLC with payment_hash {} from {} counterparty commitment tx due to broadcast of revoked counterparty commitment transaction, waiting for confirmation (at height {})", log_bytes!(htlc.payment_hash.0), $commitment_tx, entry.confirmation_threshold());
}
self.counterparty_commitment_txn_on_chain.insert(commitment_txid, commitment_number);
- log_trace!(logger, "Got broadcast of non-revoked counterparty commitment transaction {}", commitment_txid);
+ log_info!(logger, "Got broadcast of non-revoked counterparty commitment transaction {}", commitment_txid);
macro_rules! check_htlc_fails {
($txid: expr, $commitment_tx: expr, $id: tt) => {
self.onchain_events_awaiting_threshold_conf.retain(|ref entry| {
if entry.height != height { return true; }
match entry.event {
- OnchainEvent::HTLCUpdate { ref htlc_update } => {
- htlc_update.0 != **source
- },
- _ => true,
+ OnchainEvent::HTLCUpdate { source: ref update_source, .. } => {
+ *update_source != **source
+ },
+ _ => true,
}
});
self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry {
txid: *$txid,
height,
event: OnchainEvent::HTLCUpdate {
- htlc_update: ((**source).clone(), htlc.payment_hash.clone())
+ source: (**source).clone(),
+ payment_hash: htlc.payment_hash.clone(),
+ onchain_value_satoshis: Some(htlc.amount_msat / 1000),
},
});
}
(claimable_outpoints, (commitment_txid, watch_outputs))
}
- fn get_counterparty_htlc_output_claim_reqs(&self, commitment_number: u64, commitment_txid: Txid, tx: Option<&Transaction>) -> Vec<ClaimRequest> {
- let mut claims = Vec::new();
+ fn get_counterparty_htlc_output_claim_reqs(&self, commitment_number: u64, commitment_txid: Txid, tx: Option<&Transaction>) -> Vec<PackageTemplate> {
+ let mut claimable_outpoints = Vec::new();
if let Some(htlc_outputs) = self.counterparty_claimable_outpoints.get(&commitment_txid) {
if let Some(revocation_points) = self.their_cur_revocation_points {
let revocation_point_option =
if let Some(transaction) = tx {
if transaction_output_index as usize >= transaction.output.len() ||
transaction.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 {
- return claims; // Corrupted per_commitment_data, fuck this user
+ return claimable_outpoints; // Corrupted per_commitment_data, fuck this user
}
}
- let preimage =
- if htlc.offered {
- if let Some(p) = self.payment_preimages.get(&htlc.payment_hash) {
- Some(*p)
- } else { None }
- } else { None };
- let aggregable = if !htlc.offered { false } else { true };
+ let preimage = if htlc.offered { if let Some(p) = self.payment_preimages.get(&htlc.payment_hash) { Some(*p) } else { None } } else { None };
if preimage.is_some() || !htlc.offered {
- let witness_data = InputMaterial::CounterpartyHTLC { per_commitment_point: *revocation_point, counterparty_delayed_payment_base_key: self.counterparty_tx_cache.counterparty_delayed_payment_base_key, counterparty_htlc_base_key: self.counterparty_tx_cache.counterparty_htlc_base_key, preimage, htlc: htlc.clone() };
- claims.push(ClaimRequest { absolute_timelock: htlc.cltv_expiry, aggregable, outpoint: BitcoinOutPoint { txid: commitment_txid, vout: transaction_output_index }, witness_data });
+ let counterparty_htlc_outp = if htlc.offered { PackageSolvingData::CounterpartyOfferedHTLCOutput(CounterpartyOfferedHTLCOutput::build(*revocation_point, self.counterparty_tx_cache.counterparty_delayed_payment_base_key, self.counterparty_tx_cache.counterparty_htlc_base_key, preimage.unwrap(), htlc.clone())) } else { PackageSolvingData::CounterpartyReceivedHTLCOutput(CounterpartyReceivedHTLCOutput::build(*revocation_point, self.counterparty_tx_cache.counterparty_delayed_payment_base_key, self.counterparty_tx_cache.counterparty_htlc_base_key, htlc.clone())) };
+ let aggregation = if !htlc.offered { false } else { true };
+ let counterparty_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, counterparty_htlc_outp, htlc.cltv_expiry,aggregation, 0);
+ claimable_outpoints.push(counterparty_package);
}
}
}
}
}
}
- claims
+ claimable_outpoints
}
/// Attempts to claim a counterparty HTLC-Success/HTLC-Timeout's outputs using the revocation key
- fn check_spend_counterparty_htlc<L: Deref>(&mut self, tx: &Transaction, commitment_number: u64, height: u32, logger: &L) -> (Vec<ClaimRequest>, Option<TransactionOutputs>) where L::Target: Logger {
+ fn check_spend_counterparty_htlc<L: Deref>(&mut self, tx: &Transaction, commitment_number: u64, height: u32, logger: &L) -> (Vec<PackageTemplate>, Option<TransactionOutputs>) where L::Target: Logger {
let htlc_txid = tx.txid();
if tx.input.len() != 1 || tx.output.len() != 1 || tx.input[0].witness.len() != 5 {
return (Vec::new(), None)
let per_commitment_key = ignore_error!(SecretKey::from_slice(&secret));
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
- log_trace!(logger, "Counterparty HTLC broadcast {}:{}", htlc_txid, 0);
- let witness_data = InputMaterial::Revoked { per_commitment_point, counterparty_delayed_payment_base_key: self.counterparty_tx_cache.counterparty_delayed_payment_base_key, counterparty_htlc_base_key: self.counterparty_tx_cache.counterparty_htlc_base_key, per_commitment_key, input_descriptor: InputDescriptors::RevokedOutput, amount: tx.output[0].value, htlc: None, on_counterparty_tx_csv: self.counterparty_tx_cache.on_counterparty_tx_csv };
- let claimable_outpoints = vec!(ClaimRequest { absolute_timelock: height + self.counterparty_tx_cache.on_counterparty_tx_csv as u32, aggregable: true, outpoint: BitcoinOutPoint { txid: htlc_txid, vout: 0}, witness_data });
+ log_error!(logger, "Got broadcast of revoked counterparty HTLC transaction, spending {}:{}", htlc_txid, 0);
+ let revk_outp = RevokedOutput::build(per_commitment_point, self.counterparty_tx_cache.counterparty_delayed_payment_base_key, self.counterparty_tx_cache.counterparty_htlc_base_key, per_commitment_key, tx.output[0].value, self.counterparty_tx_cache.on_counterparty_tx_csv);
+ let justice_package = PackageTemplate::build_package(htlc_txid, 0, PackageSolvingData::RevokedOutput(revk_outp), height + self.counterparty_tx_cache.on_counterparty_tx_csv as u32, true, height);
+ let claimable_outpoints = vec!(justice_package);
let outputs = vec![(0, tx.output[0].clone())];
(claimable_outpoints, Some((htlc_txid, outputs)))
}
- // Returns (1) `ClaimRequest`s that can be given to the OnChainTxHandler, so that the handler can
+ // Returns (1) `PackageTemplate`s that can be given to the OnChainTxHandler, so that the handler can
// broadcast transactions claiming holder HTLC commitment outputs and (2) a holder revokable
// script so we can detect whether a holder transaction has been seen on-chain.
- fn get_broadcasted_holder_claims(&self, holder_tx: &HolderSignedTx) -> (Vec<ClaimRequest>, Option<(Script, PublicKey, PublicKey)>) {
+ fn get_broadcasted_holder_claims(&self, holder_tx: &HolderSignedTx, conf_height: u32) -> (Vec<PackageTemplate>, Option<(Script, PublicKey, PublicKey)>) {
let mut claim_requests = Vec::with_capacity(holder_tx.htlc_outputs.len());
let redeemscript = chan_utils::get_revokeable_redeemscript(&holder_tx.revocation_key, self.on_holder_tx_csv, &holder_tx.delayed_payment_key);
for &(ref htlc, _, _) in holder_tx.htlc_outputs.iter() {
if let Some(transaction_output_index) = htlc.transaction_output_index {
- claim_requests.push(ClaimRequest { absolute_timelock: ::std::u32::MAX, aggregable: false, outpoint: BitcoinOutPoint { txid: holder_tx.txid, vout: transaction_output_index as u32 },
- witness_data: InputMaterial::HolderHTLC {
- preimage: if !htlc.offered {
- if let Some(preimage) = self.payment_preimages.get(&htlc.payment_hash) {
- Some(preimage.clone())
- } else {
- // We can't build an HTLC-Success transaction without the preimage
- continue;
- }
- } else { None },
- amount: htlc.amount_msat,
- }});
+ let htlc_output = if htlc.offered {
+ HolderHTLCOutput::build_offered(htlc.amount_msat, htlc.cltv_expiry)
+ } else {
+ let payment_preimage = if let Some(preimage) = self.payment_preimages.get(&htlc.payment_hash) {
+ preimage.clone()
+ } else {
+ // We can't build an HTLC-Success transaction without the preimage
+ continue;
+ };
+ HolderHTLCOutput::build_accepted(payment_preimage, htlc.amount_msat)
+ };
+ let htlc_package = PackageTemplate::build_package(holder_tx.txid, transaction_output_index, PackageSolvingData::HolderHTLCOutput(htlc_output), htlc.cltv_expiry, false, conf_height);
+ claim_requests.push(htlc_package);
}
}
/// Attempts to claim any claimable HTLCs in a commitment transaction which was not (yet)
/// revoked using data in holder_claimable_outpoints.
/// Should not be used if check_spend_revoked_transaction succeeds.
- fn check_spend_holder_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L) -> (Vec<ClaimRequest>, TransactionOutputs) where L::Target: Logger {
+ fn check_spend_holder_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L) -> (Vec<PackageTemplate>, TransactionOutputs) where L::Target: Logger {
let commitment_txid = tx.txid();
let mut claim_requests = Vec::new();
let mut watch_outputs = Vec::new();
- macro_rules! wait_threshold_conf {
- ($source: expr, $commitment_tx: expr, $payment_hash: expr) => {
- self.onchain_events_awaiting_threshold_conf.retain(|ref entry| {
- if entry.height != height { return true; }
- match entry.event {
- OnchainEvent::HTLCUpdate { ref htlc_update } => {
- htlc_update.0 != $source
- },
- _ => true,
- }
- });
- let entry = OnchainEventEntry {
- txid: commitment_txid,
- height,
- event: OnchainEvent::HTLCUpdate { htlc_update: ($source, $payment_hash) },
- };
- log_trace!(logger, "Failing HTLC with payment_hash {} from {} holder commitment tx due to broadcast of transaction, waiting confirmation (at height{})", log_bytes!($payment_hash.0), $commitment_tx, entry.confirmation_threshold());
- self.onchain_events_awaiting_threshold_conf.push(entry);
- }
- }
-
macro_rules! append_onchain_update {
($updates: expr, $to_watch: expr) => {
claim_requests = $updates.0;
if self.current_holder_commitment_tx.txid == commitment_txid {
is_holder_tx = true;
- log_trace!(logger, "Got latest holder commitment tx broadcast, searching for available HTLCs to claim");
- let res = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx);
+ log_info!(logger, "Got broadcast of latest holder commitment tx {}, searching for available HTLCs to claim", commitment_txid);
+ let res = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx, height);
let mut to_watch = self.get_broadcasted_holder_watch_outputs(&self.current_holder_commitment_tx, tx);
append_onchain_update!(res, to_watch);
} else if let &Some(ref holder_tx) = &self.prev_holder_signed_commitment_tx {
if holder_tx.txid == commitment_txid {
is_holder_tx = true;
- log_trace!(logger, "Got previous holder commitment tx broadcast, searching for available HTLCs to claim");
- let res = self.get_broadcasted_holder_claims(holder_tx);
+ log_info!(logger, "Got broadcast of previous holder commitment tx {}, searching for available HTLCs to claim", commitment_txid);
+ let res = self.get_broadcasted_holder_claims(holder_tx, height);
let mut to_watch = self.get_broadcasted_holder_watch_outputs(holder_tx, tx);
append_onchain_update!(res, to_watch);
}
}
macro_rules! fail_dust_htlcs_after_threshold_conf {
- ($holder_tx: expr) => {
+ ($holder_tx: expr, $commitment_tx: expr) => {
for &(ref htlc, _, ref source) in &$holder_tx.htlc_outputs {
if htlc.transaction_output_index.is_none() {
if let &Some(ref source) = source {
- wait_threshold_conf!(source.clone(), "lastest", htlc.payment_hash.clone());
+ self.onchain_events_awaiting_threshold_conf.retain(|ref entry| {
+ if entry.height != height { return true; }
+ match entry.event {
+ OnchainEvent::HTLCUpdate { source: ref update_source, .. } => {
+ update_source != source
+ },
+ _ => true,
+ }
+ });
+ let entry = OnchainEventEntry {
+ txid: commitment_txid,
+ height,
+ event: OnchainEvent::HTLCUpdate {
+ source: source.clone(), payment_hash: htlc.payment_hash,
+ onchain_value_satoshis: Some(htlc.amount_msat / 1000)
+ },
+ };
+ log_trace!(logger, "Failing HTLC with payment_hash {} from {} holder commitment tx due to broadcast of transaction, waiting confirmation (at height{})",
+ log_bytes!(htlc.payment_hash.0), $commitment_tx, entry.confirmation_threshold());
+ self.onchain_events_awaiting_threshold_conf.push(entry);
}
}
}
}
if is_holder_tx {
- fail_dust_htlcs_after_threshold_conf!(self.current_holder_commitment_tx);
+ fail_dust_htlcs_after_threshold_conf!(self.current_holder_commitment_tx, "latest");
if let &Some(ref holder_tx) = &self.prev_holder_signed_commitment_tx {
- fail_dust_htlcs_after_threshold_conf!(holder_tx);
+ fail_dust_htlcs_after_threshold_conf!(holder_tx, "previous");
}
}
}
pub fn get_latest_holder_commitment_txn<L: Deref>(&mut self, logger: &L) -> Vec<Transaction> where L::Target: Logger {
- log_trace!(logger, "Getting signed latest holder commitment transaction!");
+ log_debug!(logger, "Getting signed latest holder commitment transaction!");
self.holder_tx_signed = true;
let commitment_tx = self.onchain_tx_handler.get_fully_signed_holder_tx(&self.funding_redeemscript);
let txid = commitment_tx.txid();
- let mut res = vec![commitment_tx];
+ let mut holder_transactions = vec![commitment_tx];
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 {
// We can't build an HTLC-Success transaction without the preimage
continue;
}
+ } else if htlc.0.cltv_expiry > self.best_block.height() + 1 {
+ // Don't broadcast HTLC-Timeout transactions immediately as they don't meet the
+ // current locktime requirements on-chain. We will broadcast them in
+ // `block_confirmed` when `should_broadcast_holder_commitment_txn` returns true.
+ // Note that we add + 1 as transactions are broadcastable when they can be
+ // confirmed in the next block.
+ continue;
} else { None };
if let Some(htlc_tx) = self.onchain_tx_handler.get_fully_signed_htlc_tx(
&::bitcoin::OutPoint { txid, vout }, &preimage) {
- res.push(htlc_tx);
+ holder_transactions.push(htlc_tx);
}
}
}
// We throw away the generated waiting_first_conf data as we aren't (yet) confirmed and we don't actually know what the caller wants to do.
// The data will be re-generated and tracked in check_spend_holder_transaction if we get a confirmation.
- return res;
+ holder_transactions
}
#[cfg(any(test,feature = "unsafe_revoked_tx_signing"))]
+ /// Note that this includes possibly-locktimed-in-the-future transactions!
fn unsafe_get_latest_holder_commitment_txn<L: Deref>(&mut self, logger: &L) -> Vec<Transaction> where L::Target: Logger {
- log_trace!(logger, "Getting signed copy of latest holder commitment transaction!");
+ log_debug!(logger, "Getting signed copy of latest holder commitment transaction!");
let commitment_tx = self.onchain_tx_handler.get_fully_signed_copy_holder_tx(&self.funding_redeemscript);
let txid = commitment_tx.txid();
- let mut res = vec![commitment_tx];
+ let mut holder_transactions = vec![commitment_tx];
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 {
} else { None };
if let Some(htlc_tx) = self.onchain_tx_handler.unsafe_get_fully_signed_htlc_tx(
&::bitcoin::OutPoint { txid, vout }, &preimage) {
- res.push(htlc_tx);
+ holder_transactions.push(htlc_tx);
}
}
}
- return res
+ holder_transactions
}
pub fn block_connected<B: Deref, F: Deref, L: Deref>(&mut self, header: &BlockHeader, txdata: &TransactionData, height: u32, broadcaster: B, fee_estimator: F, logger: L) -> Vec<TransactionOutputs>
if height > self.best_block.height() {
self.best_block = BestBlock::new(block_hash, height);
- self.block_confirmed(height, vec![], vec![], vec![], broadcaster, fee_estimator, logger)
- } else {
+ self.block_confirmed(height, vec![], vec![], vec![], &broadcaster, &fee_estimator, &logger)
+ } else if block_hash != self.best_block.block_hash() {
self.best_block = BestBlock::new(block_hash, height);
self.onchain_events_awaiting_threshold_conf.retain(|ref entry| entry.height <= height);
self.onchain_tx_handler.block_disconnected(height + 1, broadcaster, fee_estimator, logger);
Vec::new()
- }
+ } else { Vec::new() }
}
fn transactions_confirmed<B: Deref, F: Deref, L: Deref>(
self.is_paying_spendable_output(&tx, height, &logger);
}
- self.block_confirmed(height, txn_matched, watch_outputs, claimable_outpoints, broadcaster, fee_estimator, logger)
+ if height > self.best_block.height() {
+ self.best_block = BestBlock::new(block_hash, height);
+ }
+
+ self.block_confirmed(height, txn_matched, watch_outputs, claimable_outpoints, &broadcaster, &fee_estimator, &logger)
}
+ /// Update state for new block(s)/transaction(s) confirmed. Note that the caller must update
+ /// `self.best_block` before calling if a new best blockchain tip is available. More
+ /// concretely, `self.best_block` must never be at a lower height than `conf_height`, avoiding
+ /// complexity especially in `OnchainTx::update_claims_view`.
+ ///
+ /// `conf_height` should be set to the height at which any new transaction(s)/block(s) were
+ /// confirmed at, even if it is not the current best height.
fn block_confirmed<B: Deref, F: Deref, L: Deref>(
&mut self,
- height: u32,
+ conf_height: u32,
txn_matched: Vec<&Transaction>,
mut watch_outputs: Vec<TransactionOutputs>,
- mut claimable_outpoints: Vec<ClaimRequest>,
- broadcaster: B,
- fee_estimator: F,
- logger: L,
+ mut claimable_outpoints: Vec<PackageTemplate>,
+ broadcaster: &B,
+ fee_estimator: &F,
+ logger: &L,
) -> Vec<TransactionOutputs>
where
B::Target: BroadcasterInterface,
F::Target: FeeEstimator,
L::Target: Logger,
{
- let should_broadcast = self.would_broadcast_at_height(height, &logger);
+ debug_assert!(self.best_block.height() >= conf_height);
+
+ let should_broadcast = self.should_broadcast_holder_commitment_txn(logger);
if should_broadcast {
- claimable_outpoints.push(ClaimRequest { absolute_timelock: height, aggregable: false, outpoint: BitcoinOutPoint { txid: self.funding_info.0.txid.clone(), vout: self.funding_info.0.index as u32 }, witness_data: InputMaterial::Funding { funding_redeemscript: self.funding_redeemscript.clone() }});
+ let funding_outp = HolderFundingOutput::build(self.funding_redeemscript.clone());
+ let commitment_package = PackageTemplate::build_package(self.funding_info.0.txid.clone(), self.funding_info.0.index as u32, PackageSolvingData::HolderFundingOutput(funding_outp), self.best_block.height(), false, self.best_block.height());
+ claimable_outpoints.push(commitment_package);
self.pending_monitor_events.push(MonitorEvent::CommitmentTxBroadcasted(self.funding_info.0));
let commitment_tx = self.onchain_tx_handler.get_fully_signed_holder_tx(&self.funding_redeemscript);
self.holder_tx_signed = true;
- let (mut new_outpoints, _) = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx);
+ // Because we're broadcasting a commitment transaction, we should construct the package
+ // assuming it gets confirmed in the next block. Sadly, we have code which considers
+ // "not yet confirmed" things as discardable, so we cannot do that here.
+ let (mut new_outpoints, _) = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx, self.best_block.height());
let new_outputs = self.get_broadcasted_holder_watch_outputs(&self.current_holder_commitment_tx, &commitment_tx);
if !new_outputs.is_empty() {
watch_outputs.push((self.current_holder_commitment_tx.txid.clone(), new_outputs));
self.onchain_events_awaiting_threshold_conf.drain(..).collect::<Vec<_>>();
let mut onchain_events_reaching_threshold_conf = Vec::new();
for entry in onchain_events_awaiting_threshold_conf {
- if entry.has_reached_confirmation_threshold(height) {
+ if entry.has_reached_confirmation_threshold(&self.best_block) {
onchain_events_reaching_threshold_conf.push(entry);
} else {
self.onchain_events_awaiting_threshold_conf.push(entry);
let unmatured_htlcs: Vec<_> = self.onchain_events_awaiting_threshold_conf
.iter()
.filter_map(|entry| match &entry.event {
- OnchainEvent::HTLCUpdate { htlc_update } => Some(htlc_update.0.clone()),
+ OnchainEvent::HTLCUpdate { source, .. } => Some(source),
OnchainEvent::MaturingOutput { .. } => None,
})
.collect();
// Produce actionable events from on-chain events having reached their threshold.
for entry in onchain_events_reaching_threshold_conf.drain(..) {
match entry.event {
- OnchainEvent::HTLCUpdate { htlc_update } => {
+ OnchainEvent::HTLCUpdate { ref source, payment_hash, onchain_value_satoshis } => {
// Check for duplicate HTLC resolutions.
#[cfg(debug_assertions)]
{
debug_assert!(
- unmatured_htlcs.iter().find(|&htlc| htlc == &htlc_update.0).is_none(),
+ unmatured_htlcs.iter().find(|&htlc| htlc == &source).is_none(),
"An unmature HTLC transaction conflicts with a maturing one; failed to \
call either transaction_unconfirmed for the conflicting transaction \
or block_disconnected for a block containing it.");
debug_assert!(
- matured_htlcs.iter().find(|&htlc| htlc == &htlc_update.0).is_none(),
+ matured_htlcs.iter().find(|&htlc| htlc == source).is_none(),
"A matured HTLC transaction conflicts with a maturing one; failed to \
call either transaction_unconfirmed for the conflicting transaction \
or block_disconnected for a block containing it.");
- matured_htlcs.push(htlc_update.0.clone());
+ matured_htlcs.push(source.clone());
}
- log_trace!(logger, "HTLC {} failure update has got enough confirmations to be passed upstream", log_bytes!((htlc_update.1).0));
+ log_debug!(logger, "HTLC {} failure update has got enough confirmations to be passed upstream", log_bytes!(payment_hash.0));
self.pending_monitor_events.push(MonitorEvent::HTLCEvent(HTLCUpdate {
- payment_hash: htlc_update.1,
+ payment_hash,
payment_preimage: None,
- source: htlc_update.0,
+ source: source.clone(),
+ onchain_value_satoshis,
}));
},
OnchainEvent::MaturingOutput { descriptor } => {
- log_trace!(logger, "Descriptor {} has got enough confirmations to be passed upstream", log_spendable!(descriptor));
+ log_debug!(logger, "Descriptor {} has got enough confirmations to be passed upstream", log_spendable!(descriptor));
self.pending_events.push(Event::SpendableOutputs {
outputs: vec![descriptor]
});
}
}
- self.onchain_tx_handler.update_claims_view(&txn_matched, claimable_outpoints, Some(height), &&*broadcaster, &&*fee_estimator, &&*logger);
+ self.onchain_tx_handler.update_claims_view(&txn_matched, claimable_outpoints, conf_height, self.best_block.height(), broadcaster, fee_estimator, logger);
// Determine new outputs to watch by comparing against previously known outputs to watch,
// updating the latter in the process.
false
}
- fn would_broadcast_at_height<L: Deref>(&self, height: u32, logger: &L) -> bool where L::Target: Logger {
+ fn should_broadcast_holder_commitment_txn<L: Deref>(&self, logger: &L) -> bool where L::Target: Logger {
// 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
// to the source, and if we don't fail the channel we will have to ensure that the next
// updates that peer sends us are update_fails, failing the channel if not. It's probably
// easier to just fail the channel as this case should be rare enough anyway.
+ let height = self.best_block.height();
macro_rules! scan_commitment {
($htlcs: expr, $holder_tx: expr) => {
for ref htlc in $htlcs {
if pending_htlc.payment_hash == $htlc_output.payment_hash && pending_htlc.amount_msat == $htlc_output.amount_msat {
if let &Some(ref source) = pending_source {
log_claim!("revoked counterparty commitment tx", false, pending_htlc, true);
- payment_data = Some(((**source).clone(), $htlc_output.payment_hash));
+ payment_data = Some(((**source).clone(), $htlc_output.payment_hash, $htlc_output.amount_msat));
break;
}
}
// transaction. This implies we either learned a preimage, the HTLC
// has timed out, or we screwed up. In any case, we should now
// resolve the source HTLC with the original sender.
- payment_data = Some(((*source).clone(), htlc_output.payment_hash));
+ payment_data = Some(((*source).clone(), htlc_output.payment_hash, htlc_output.amount_msat));
} else if !$holder_tx {
check_htlc_valid_counterparty!(self.current_counterparty_commitment_txid, htlc_output);
if payment_data.is_none() {
// Check that scan_commitment, above, decided there is some source worth relaying an
// HTLC resolution backwards to and figure out whether we learned a preimage from it.
- if let Some((source, payment_hash)) = payment_data {
+ if let Some((source, payment_hash, amount_msat)) = payment_data {
let mut payment_preimage = PaymentPreimage([0; 32]);
if accepted_preimage_claim {
if !self.pending_monitor_events.iter().any(
self.pending_monitor_events.push(MonitorEvent::HTLCEvent(HTLCUpdate {
source,
payment_preimage: Some(payment_preimage),
- payment_hash
+ payment_hash,
+ onchain_value_satoshis: Some(amount_msat / 1000),
}));
}
} else if offered_preimage_claim {
self.pending_monitor_events.push(MonitorEvent::HTLCEvent(HTLCUpdate {
source,
payment_preimage: Some(payment_preimage),
- payment_hash
+ payment_hash,
+ onchain_value_satoshis: Some(amount_msat / 1000),
}));
}
} else {
self.onchain_events_awaiting_threshold_conf.retain(|ref entry| {
if entry.height != height { return true; }
match entry.event {
- OnchainEvent::HTLCUpdate { ref htlc_update } => {
- htlc_update.0 != source
- },
- _ => true,
+ OnchainEvent::HTLCUpdate { source: ref htlc_source, .. } => {
+ *htlc_source != source
+ },
+ _ => true,
}
});
let entry = OnchainEventEntry {
txid: tx.txid(),
height,
- event: OnchainEvent::HTLCUpdate { htlc_update: (source, payment_hash) },
+ event: OnchainEvent::HTLCUpdate {
+ source, payment_hash,
+ onchain_value_satoshis: Some(amount_msat / 1000),
+ },
};
- log_info!(logger, "Failing HTLC with payment_hash {} timeout by a spend tx, waiting for confirmation (at height{})", log_bytes!(payment_hash.0), entry.confirmation_threshold());
+ log_info!(logger, "Failing HTLC with payment_hash {} timeout by a spend tx, waiting for confirmation (at height {})", log_bytes!(payment_hash.0), entry.confirmation_threshold());
self.onchain_events_awaiting_threshold_conf.push(entry);
}
}
fn is_paying_spendable_output<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L) where L::Target: Logger {
let mut spendable_output = None;
for (i, outp) in tx.output.iter().enumerate() { // There is max one spendable output for any channel tx, including ones generated by us
- if i > ::std::u16::MAX as usize {
+ if i > ::core::u16::MAX as usize {
// While it is possible that an output exists on chain which is greater than the
// 2^16th output in a given transaction, this is only possible if the output is not
// in a lightning transaction and was instead placed there by some third party who
height: height,
event: OnchainEvent::MaturingOutput { descriptor: spendable_output.clone() },
};
- log_trace!(logger, "Maturing {} until {}", log_spendable!(spendable_output), entry.confirmation_threshold());
+ log_info!(logger, "Received spendable output {}, spendable at height {}", log_spendable!(spendable_output), entry.confirmation_threshold());
self.onchain_events_awaiting_threshold_conf.push(entry);
}
}
}
}
- let _ver: u8 = Readable::read(reader)?;
- let min_ver: u8 = Readable::read(reader)?;
- if min_ver > SERIALIZATION_VERSION {
- return Err(DecodeError::UnknownVersion);
- }
+ let _ver = read_ver_prefix!(reader, SERIALIZATION_VERSION);
let latest_update_id: u64 = Readable::read(reader)?;
let commitment_transaction_number_obscure_factor = <U48 as Readable>::read(reader)?.0;
}
}
- macro_rules! read_holder_tx {
- () => {
- {
- let txid = Readable::read(reader)?;
- let revocation_key = Readable::read(reader)?;
- let a_htlc_key = Readable::read(reader)?;
- let b_htlc_key = Readable::read(reader)?;
- let delayed_payment_key = Readable::read(reader)?;
- let per_commitment_point = Readable::read(reader)?;
- let feerate_per_kw: u32 = Readable::read(reader)?;
-
- let htlcs_len: u64 = Readable::read(reader)?;
- let mut htlcs = Vec::with_capacity(cmp::min(htlcs_len as usize, MAX_ALLOC_SIZE / 128));
- for _ in 0..htlcs_len {
- let htlc = read_htlc_in_commitment!();
- let sigs = match <u8 as Readable>::read(reader)? {
- 0 => None,
- 1 => Some(Readable::read(reader)?),
- _ => return Err(DecodeError::InvalidValue),
- };
- htlcs.push((htlc, sigs, Readable::read(reader)?));
- }
-
- HolderSignedTx {
- txid,
- revocation_key, a_htlc_key, b_htlc_key, delayed_payment_key, per_commitment_point, feerate_per_kw,
- htlc_outputs: htlcs
- }
- }
- }
- }
-
let prev_holder_signed_commitment_tx = match <u8 as Readable>::read(reader)? {
0 => None,
1 => {
- Some(read_holder_tx!())
+ Some(Readable::read(reader)?)
},
_ => return Err(DecodeError::InvalidValue),
};
- let current_holder_commitment_tx = read_holder_tx!();
+ let current_holder_commitment_tx = Readable::read(reader)?;
let current_counterparty_commitment_number = <U48 as Readable>::read(reader)?.0;
let current_holder_commitment_number = <U48 as Readable>::read(reader)?.0;
let waiting_threshold_conf_len: u64 = Readable::read(reader)?;
let mut onchain_events_awaiting_threshold_conf = Vec::with_capacity(cmp::min(waiting_threshold_conf_len as usize, MAX_ALLOC_SIZE / 128));
for _ in 0..waiting_threshold_conf_len {
- let txid = Readable::read(reader)?;
- let height = Readable::read(reader)?;
- let event = match <u8 as Readable>::read(reader)? {
- 0 => {
- let htlc_source = Readable::read(reader)?;
- let hash = Readable::read(reader)?;
- OnchainEvent::HTLCUpdate {
- htlc_update: (htlc_source, hash)
- }
- },
- 1 => {
- let descriptor = Readable::read(reader)?;
- OnchainEvent::MaturingOutput {
- descriptor
- }
- },
- _ => return Err(DecodeError::InvalidValue),
- };
- onchain_events_awaiting_threshold_conf.push(OnchainEventEntry { txid, height, event });
+ onchain_events_awaiting_threshold_conf.push(Readable::read(reader)?);
}
let outputs_to_watch_len: u64 = Readable::read(reader)?;
let lockdown_from_offchain = Readable::read(reader)?;
let holder_tx_signed = Readable::read(reader)?;
+ read_tlv_fields!(reader, {});
+
let mut secp_ctx = Secp256k1::new();
secp_ctx.seeded_randomize(&keys_manager.get_secure_random_bytes());
use bitcoin::hash_types::Txid;
use bitcoin::network::constants::Network;
use hex;
+ use chain::BestBlock;
use chain::channelmonitor::ChannelMonitor;
+ use chain::package::{WEIGHT_OFFERED_HTLC, WEIGHT_RECEIVED_HTLC, WEIGHT_REVOKED_OFFERED_HTLC, WEIGHT_REVOKED_RECEIVED_HTLC, WEIGHT_REVOKED_OUTPUT};
use chain::transaction::OutPoint;
use ln::{PaymentPreimage, PaymentHash};
- use ln::channelmanager::BestBlock;
- use ln::onchaintx::{OnchainTxHandler, InputDescriptors};
use ln::chan_utils;
use ln::chan_utils::{HTLCOutputInCommitment, ChannelPublicKeys, ChannelTransactionParameters, HolderCommitmentTransaction, CounterpartyChannelTransactionParameters};
use util::test_utils::{TestLogger, TestBroadcaster, TestFeeEstimator};
use bitcoin::secp256k1::key::{SecretKey,PublicKey};
use bitcoin::secp256k1::Secp256k1;
- use std::sync::{Arc, Mutex};
+ use sync::{Arc, Mutex};
use chain::keysinterface::InMemorySigner;
+ use prelude::*;
#[test]
fn test_prune_preimages() {
let secp_ctx = Secp256k1::new();
let logger = Arc::new(TestLogger::new());
- let broadcaster = Arc::new(TestBroadcaster{txn_broadcasted: Mutex::new(Vec::new())});
- let fee_estimator = Arc::new(TestFeeEstimator { sat_per_kw: 253 });
+ let broadcaster = Arc::new(TestBroadcaster{txn_broadcasted: Mutex::new(Vec::new()), blocks: Arc::new(Mutex::new(Vec::new()))});
+ let fee_estimator = Arc::new(TestFeeEstimator { sat_per_kw: Mutex::new(253) });
let dummy_key = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let dummy_tx = Transaction { version: 0, lock_time: 0, input: Vec::new(), output: Vec::new() };
let mut sum_actual_sigs = 0;
macro_rules! sign_input {
- ($sighash_parts: expr, $idx: expr, $amount: expr, $input_type: expr, $sum_actual_sigs: expr) => {
+ ($sighash_parts: expr, $idx: expr, $amount: expr, $weight: expr, $sum_actual_sigs: expr) => {
let htlc = HTLCOutputInCommitment {
- offered: if *$input_type == InputDescriptors::RevokedOfferedHTLC || *$input_type == InputDescriptors::OfferedHTLC { true } else { false },
+ offered: if *$weight == WEIGHT_REVOKED_OFFERED_HTLC || *$weight == WEIGHT_OFFERED_HTLC { true } else { false },
amount_msat: 0,
cltv_expiry: 2 << 16,
payment_hash: PaymentHash([1; 32]),
transaction_output_index: Some($idx as u32),
};
- let redeem_script = if *$input_type == InputDescriptors::RevokedOutput { chan_utils::get_revokeable_redeemscript(&pubkey, 256, &pubkey) } else { chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, &pubkey, &pubkey, &pubkey) };
+ let redeem_script = if *$weight == WEIGHT_REVOKED_OUTPUT { chan_utils::get_revokeable_redeemscript(&pubkey, 256, &pubkey) } else { chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, &pubkey, &pubkey, &pubkey) };
let sighash = hash_to_message!(&$sighash_parts.signature_hash($idx, &redeem_script, $amount, SigHashType::All)[..]);
let sig = secp_ctx.sign(&sighash, &privkey);
$sighash_parts.access_witness($idx).push(sig.serialize_der().to_vec());
$sighash_parts.access_witness($idx)[0].push(SigHashType::All as u8);
sum_actual_sigs += $sighash_parts.access_witness($idx)[0].len();
- if *$input_type == InputDescriptors::RevokedOutput {
+ if *$weight == WEIGHT_REVOKED_OUTPUT {
$sighash_parts.access_witness($idx).push(vec!(1));
- } else if *$input_type == InputDescriptors::RevokedOfferedHTLC || *$input_type == InputDescriptors::RevokedReceivedHTLC {
+ } else if *$weight == WEIGHT_REVOKED_OFFERED_HTLC || *$weight == WEIGHT_REVOKED_RECEIVED_HTLC {
$sighash_parts.access_witness($idx).push(pubkey.clone().serialize().to_vec());
- } else if *$input_type == InputDescriptors::ReceivedHTLC {
+ } else if *$weight == WEIGHT_RECEIVED_HTLC {
$sighash_parts.access_witness($idx).push(vec![0]);
} else {
$sighash_parts.access_witness($idx).push(PaymentPreimage([1; 32]).0.to_vec());
value: 0,
});
let base_weight = claim_tx.get_weight();
- let inputs_des = vec![InputDescriptors::RevokedOutput, InputDescriptors::RevokedOfferedHTLC, InputDescriptors::RevokedOfferedHTLC, InputDescriptors::RevokedReceivedHTLC];
+ let inputs_weight = vec![WEIGHT_REVOKED_OUTPUT, WEIGHT_REVOKED_OFFERED_HTLC, WEIGHT_REVOKED_OFFERED_HTLC, WEIGHT_REVOKED_RECEIVED_HTLC];
+ let mut inputs_total_weight = 2; // count segwit flags
{
let mut sighash_parts = bip143::SigHashCache::new(&mut claim_tx);
- for (idx, inp) in inputs_des.iter().enumerate() {
+ for (idx, inp) in inputs_weight.iter().enumerate() {
sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs);
+ inputs_total_weight += inp;
}
}
- assert_eq!(base_weight + OnchainTxHandler::<InMemorySigner>::get_witnesses_weight(&inputs_des[..]), claim_tx.get_weight() + /* max_length_sig */ (73 * inputs_des.len() - sum_actual_sigs));
+ assert_eq!(base_weight + inputs_total_weight as usize, claim_tx.get_weight() + /* max_length_sig */ (73 * inputs_weight.len() - sum_actual_sigs));
// Claim tx with 1 offered HTLCs, 3 received HTLCs
claim_tx.input.clear();
});
}
let base_weight = claim_tx.get_weight();
- let inputs_des = vec![InputDescriptors::OfferedHTLC, InputDescriptors::ReceivedHTLC, InputDescriptors::ReceivedHTLC, InputDescriptors::ReceivedHTLC];
+ let inputs_weight = vec![WEIGHT_OFFERED_HTLC, WEIGHT_RECEIVED_HTLC, WEIGHT_RECEIVED_HTLC, WEIGHT_RECEIVED_HTLC];
+ let mut inputs_total_weight = 2; // count segwit flags
{
let mut sighash_parts = bip143::SigHashCache::new(&mut claim_tx);
- for (idx, inp) in inputs_des.iter().enumerate() {
+ for (idx, inp) in inputs_weight.iter().enumerate() {
sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs);
+ inputs_total_weight += inp;
}
}
- assert_eq!(base_weight + OnchainTxHandler::<InMemorySigner>::get_witnesses_weight(&inputs_des[..]), claim_tx.get_weight() + /* max_length_sig */ (73 * inputs_des.len() - sum_actual_sigs));
+ assert_eq!(base_weight + inputs_total_weight as usize, claim_tx.get_weight() + /* max_length_sig */ (73 * inputs_weight.len() - sum_actual_sigs));
// Justice tx with 1 revoked HTLC-Success tx output
claim_tx.input.clear();
witness: Vec::new(),
});
let base_weight = claim_tx.get_weight();
- let inputs_des = vec![InputDescriptors::RevokedOutput];
+ let inputs_weight = vec![WEIGHT_REVOKED_OUTPUT];
+ let mut inputs_total_weight = 2; // count segwit flags
{
let mut sighash_parts = bip143::SigHashCache::new(&mut claim_tx);
- for (idx, inp) in inputs_des.iter().enumerate() {
+ for (idx, inp) in inputs_weight.iter().enumerate() {
sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs);
+ inputs_total_weight += inp;
}
}
- assert_eq!(base_weight + OnchainTxHandler::<InMemorySigner>::get_witnesses_weight(&inputs_des[..]), claim_tx.get_weight() + /* max_length_isg */ (73 * inputs_des.len() - sum_actual_sigs));
+ assert_eq!(base_weight + inputs_total_weight as usize, claim_tx.get_weight() + /* max_length_isg */ (73 * inputs_weight.len() - sum_actual_sigs));
}
// Further testing is done in the ChannelManager integration tests.