//! security-domain-separated system design, you should consider having multiple paths for
//! ChannelMonitors to get out of the HSM and onto monitoring devices.
+use bitcoin::amount::Amount;
use bitcoin::blockdata::block::Header;
use bitcoin::blockdata::transaction::{OutPoint as BitcoinOutPoint, TxOut, Transaction};
use bitcoin::blockdata::script::{Script, ScriptBuf};
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hash_types::{Txid, BlockHash};
+use bitcoin::ecdsa::Signature as BitcoinSignature;
use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature};
use bitcoin::secp256k1::{SecretKey, PublicKey};
use bitcoin::secp256k1;
-use bitcoin::sighash::EcdsaSighashType;
use crate::ln::channel::INITIAL_COMMITMENT_NUMBER;
-use crate::ln::{PaymentHash, PaymentPreimage, ChannelId};
+use crate::ln::types::{PaymentHash, PaymentPreimage, ChannelId};
use crate::ln::msgs::DecodeError;
use crate::ln::channel_keys::{DelayedPaymentKey, DelayedPaymentBasepoint, HtlcBasepoint, HtlcKey, RevocationKey, RevocationBasepoint};
use crate::ln::chan_utils::{self,CommitmentTransaction, CounterpartyCommitmentSecrets, HTLCOutputInCommitment, HTLCClaim, ChannelTransactionParameters, HolderCommitmentTransaction, TxCreationKeys};
use crate::chain::{BestBlock, WatchedOutput};
use crate::chain::chaininterface::{BroadcasterInterface, FeeEstimator, LowerBoundedFeeEstimator};
use crate::chain::transaction::{OutPoint, TransactionData};
-use crate::sign::{ChannelDerivationParameters, HTLCDescriptor, SpendableOutputDescriptor, StaticPaymentOutputDescriptor, DelayedPaymentOutputDescriptor, ecdsa::WriteableEcdsaChannelSigner, SignerProvider, EntropySource};
+use crate::sign::{ChannelDerivationParameters, HTLCDescriptor, SpendableOutputDescriptor, StaticPaymentOutputDescriptor, DelayedPaymentOutputDescriptor, ecdsa::EcdsaChannelSigner, SignerProvider, EntropySource};
use crate::chain::onchaintx::{ClaimEvent, FeerateStrategy, OnchainTxHandler};
use crate::chain::package::{CounterpartyOfferedHTLCOutput, CounterpartyReceivedHTLCOutput, HolderFundingOutput, HolderHTLCOutput, PackageSolvingData, PackageTemplate, RevokedOutput, RevokedHTLCOutput};
use crate::chain::Filter;
use crate::util::logger::{Logger, Record};
use crate::util::ser::{Readable, ReadableArgs, RequiredWrapper, MaybeReadable, UpgradableRequired, Writer, Writeable, U48};
use crate::util::byte_utils;
-use crate::events::{Event, EventHandler};
+use crate::events::{ClosureReason, Event, EventHandler};
use crate::events::bump_transaction::{AnchorDescriptor, BumpTransactionEvent};
+#[allow(unused_imports)]
use crate::prelude::*;
+
use core::{cmp, mem};
use crate::io::{self, Error};
-use core::convert::TryInto;
use core::ops::Deref;
use crate::sync::{Mutex, LockTestExt};
/// A monitor event containing an HTLCUpdate.
HTLCEvent(HTLCUpdate),
+ /// Indicates we broadcasted the channel's latest commitment transaction and thus closed the
+ /// channel. Holds information about the channel and why it was closed.
+ HolderForceClosedWithInfo {
+ /// The reason the channel was closed.
+ reason: ClosureReason,
+ /// The funding outpoint of the channel.
+ outpoint: OutPoint,
+ /// The channel ID of the channel.
+ channel_id: ChannelId,
+ },
+
/// Indicates we broadcasted the channel's latest commitment transaction and thus closed the
/// channel.
HolderForceClosed(OutPoint),
(2, monitor_update_id, required),
(4, channel_id, required),
},
+ (5, HolderForceClosedWithInfo) => {
+ (0, reason, upgradable_required),
+ (2, outpoint, required),
+ (4, channel_id, required),
+ },
;
(2, HTLCEvent),
(4, HolderForceClosed),
/// The (output index, sats value) for the counterparty's output in a commitment transaction.
///
/// This was added as an `Option` in 0.0.110.
-type CommitmentTxCounterpartyOutputInfo = Option<(u32, u64)>;
+type CommitmentTxCounterpartyOutputInfo = Option<(u32, Amount)>;
/// Upon discovering of some classes of onchain tx by ChannelMonitor, we may have to take actions on it
/// once they mature to enough confirmations (ANTI_REORG_DELAY)
/// the "reorg path" (ie disconnecting blocks until you find a common ancestor from both the
/// returned block hash and the the current chain and then reconnecting blocks to get to the
/// best chain) upon deserializing the object!
-pub struct ChannelMonitor<Signer: WriteableEcdsaChannelSigner> {
+pub struct ChannelMonitor<Signer: EcdsaChannelSigner> {
#[cfg(test)]
pub(crate) inner: Mutex<ChannelMonitorImpl<Signer>>,
#[cfg(not(test))]
pub(super) inner: Mutex<ChannelMonitorImpl<Signer>>,
}
-impl<Signer: WriteableEcdsaChannelSigner> Clone for ChannelMonitor<Signer> where Signer: Clone {
+impl<Signer: EcdsaChannelSigner> Clone for ChannelMonitor<Signer> where Signer: Clone {
fn clone(&self) -> Self {
let inner = self.inner.lock().unwrap().clone();
ChannelMonitor::from_impl(inner)
}
#[derive(Clone, PartialEq)]
-pub(crate) struct ChannelMonitorImpl<Signer: WriteableEcdsaChannelSigner> {
+pub(crate) struct ChannelMonitorImpl<Signer: EcdsaChannelSigner> {
latest_update_id: u64,
commitment_transaction_number_obscure_factor: u64,
/// Ordering of tuple data: (their_per_commitment_point, feerate_per_kw, to_broadcaster_sats,
/// to_countersignatory_sats)
initial_counterparty_commitment_info: Option<(PublicKey, u32, u64, u64)>,
+
+ /// The first block height at which we had no remaining claimable balances.
+ balances_empty_height: Option<u32>,
}
/// Transaction outputs to watch for on-chain spends.
pub type TransactionOutputs = (Txid, Vec<(u32, TxOut)>);
-impl<Signer: WriteableEcdsaChannelSigner> PartialEq for ChannelMonitor<Signer> where Signer: PartialEq {
+impl<Signer: EcdsaChannelSigner> PartialEq for ChannelMonitor<Signer> where Signer: PartialEq {
fn eq(&self, other: &Self) -> bool {
// We need some kind of total lockorder. Absent a better idea, we sort by position in
// memory and take locks in that order (assuming that we can't move within memory while a
}
}
-impl<Signer: WriteableEcdsaChannelSigner> Writeable for ChannelMonitor<Signer> {
+impl<Signer: EcdsaChannelSigner> Writeable for ChannelMonitor<Signer> {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
self.inner.lock().unwrap().write(writer)
}
const SERIALIZATION_VERSION: u8 = 1;
const MIN_SERIALIZATION_VERSION: u8 = 1;
-impl<Signer: WriteableEcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signer> {
+impl<Signer: EcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signer> {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION);
writer.write_all(&(self.pending_monitor_events.iter().filter(|ev| match ev {
MonitorEvent::HTLCEvent(_) => true,
MonitorEvent::HolderForceClosed(_) => true,
+ MonitorEvent::HolderForceClosedWithInfo { .. } => true,
_ => false,
}).count() as u64).to_be_bytes())?;
for event in self.pending_monitor_events.iter() {
upd.write(writer)?;
},
MonitorEvent::HolderForceClosed(_) => 1u8.write(writer)?,
+ // `HolderForceClosedWithInfo` replaced `HolderForceClosed` in v0.0.122. To keep
+ // backwards compatibility, we write a `HolderForceClosed` event along with the
+ // `HolderForceClosedWithInfo` event. This is deduplicated in the reader.
+ MonitorEvent::HolderForceClosedWithInfo { .. } => 1u8.write(writer)?,
_ => {}, // Covered in the TLV writes below
}
}
self.lockdown_from_offchain.write(writer)?;
self.holder_tx_signed.write(writer)?;
+ // If we have a `HolderForceClosedWithInfo` event, we need to write the `HolderForceClosed` for backwards compatibility.
+ let pending_monitor_events = match self.pending_monitor_events.iter().find(|ev| match ev {
+ MonitorEvent::HolderForceClosedWithInfo { .. } => true,
+ _ => false,
+ }) {
+ Some(MonitorEvent::HolderForceClosedWithInfo { outpoint, .. }) => {
+ let mut pending_monitor_events = self.pending_monitor_events.clone();
+ pending_monitor_events.push(MonitorEvent::HolderForceClosed(*outpoint));
+ pending_monitor_events
+ }
+ _ => self.pending_monitor_events.clone(),
+ };
+
write_tlv_fields!(writer, {
(1, self.funding_spend_confirmed, option),
(3, self.htlcs_resolved_on_chain, required_vec),
- (5, self.pending_monitor_events, required_vec),
+ (5, pending_monitor_events, required_vec),
(7, self.funding_spend_seen, required),
(9, self.counterparty_node_id, option),
(11, self.confirmed_commitment_tx_counterparty_output, option),
(15, self.counterparty_fulfilled_htlcs, required),
(17, self.initial_counterparty_commitment_info, option),
(19, self.channel_id, required),
+ (21, self.balances_empty_height, option),
});
Ok(())
logger: &'a L,
peer_id: Option<PublicKey>,
channel_id: Option<ChannelId>,
+ payment_hash: Option<PaymentHash>,
}
impl<'a, L: Deref> Logger for WithChannelMonitor<'a, L> where L::Target: Logger {
fn log(&self, mut record: Record) {
record.peer_id = self.peer_id;
record.channel_id = self.channel_id;
+ record.payment_hash = self.payment_hash;
self.logger.log(record)
}
}
impl<'a, L: Deref> WithChannelMonitor<'a, L> where L::Target: Logger {
- pub(crate) fn from<S: WriteableEcdsaChannelSigner>(logger: &'a L, monitor: &ChannelMonitor<S>) -> Self {
- Self::from_impl(logger, &*monitor.inner.lock().unwrap())
+ pub(crate) fn from<S: EcdsaChannelSigner>(logger: &'a L, monitor: &ChannelMonitor<S>, payment_hash: Option<PaymentHash>) -> Self {
+ Self::from_impl(logger, &*monitor.inner.lock().unwrap(), payment_hash)
}
- pub(crate) fn from_impl<S: WriteableEcdsaChannelSigner>(logger: &'a L, monitor_impl: &ChannelMonitorImpl<S>) -> Self {
+ pub(crate) fn from_impl<S: EcdsaChannelSigner>(logger: &'a L, monitor_impl: &ChannelMonitorImpl<S>, payment_hash: Option<PaymentHash>) -> Self {
let peer_id = monitor_impl.counterparty_node_id;
let channel_id = Some(monitor_impl.channel_id());
WithChannelMonitor {
- logger, peer_id, channel_id,
+ logger, peer_id, channel_id, payment_hash,
}
}
}
-impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
+impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
/// For lockorder enforcement purposes, we need to have a single site which constructs the
/// `inner` mutex, otherwise cases where we lock two monitors at the same time (eg in our
/// PartialEq implementation) we may decide a lockorder violation has occurred.
best_block,
counterparty_node_id: Some(counterparty_node_id),
initial_counterparty_commitment_info: None,
+ balances_empty_height: None,
})
}
where L::Target: Logger
{
let mut inner = self.inner.lock().unwrap();
- let logger = WithChannelMonitor::from_impl(logger, &*inner);
+ let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
inner.provide_initial_counterparty_commitment_tx(txid,
htlc_outputs, commitment_number, their_cur_per_commitment_point, feerate_per_kw,
to_broadcaster_value_sat, to_countersignatory_value_sat, &logger);
logger: &L,
) where L::Target: Logger {
let mut inner = self.inner.lock().unwrap();
- let logger = WithChannelMonitor::from_impl(logger, &*inner);
+ let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
inner.provide_latest_counterparty_commitment_tx(
txid, htlc_outputs, commitment_number, their_per_commitment_point, &logger)
}
L::Target: Logger,
{
let mut inner = self.inner.lock().unwrap();
- let logger = WithChannelMonitor::from_impl(logger, &*inner);
+ let logger = WithChannelMonitor::from_impl(logger, &*inner, Some(*payment_hash));
inner.provide_payment_preimage(
payment_hash, payment_preimage, broadcaster, fee_estimator, &logger)
}
L::Target: Logger,
{
let mut inner = self.inner.lock().unwrap();
- let logger = WithChannelMonitor::from_impl(logger, &*inner);
+ let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
inner.update_monitor(updates, broadcaster, fee_estimator, &logger)
}
F::Target: chain::Filter, L::Target: Logger,
{
let lock = self.inner.lock().unwrap();
- let logger = WithChannelMonitor::from_impl(logger, &*lock);
+ let logger = WithChannelMonitor::from_impl(logger, &*lock, None);
log_trace!(&logger, "Registering funding outpoint {}", &lock.get_funding_txo().0);
filter.register_tx(&lock.get_funding_txo().0.txid, &lock.get_funding_txo().1);
for (txid, outputs) in lock.get_outputs_to_watch().iter() {
{
let mut inner = self.inner.lock().unwrap();
let fee_estimator = LowerBoundedFeeEstimator::new(&**fee_estimator);
- let logger = WithChannelMonitor::from_impl(logger, &*inner);
+ let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
inner.queue_latest_holder_commitment_txn_for_broadcast(broadcaster, &fee_estimator, &logger);
}
pub fn unsafe_get_latest_holder_commitment_txn<L: Deref>(&self, logger: &L) -> Vec<Transaction>
where L::Target: Logger {
let mut inner = self.inner.lock().unwrap();
- let logger = WithChannelMonitor::from_impl(logger, &*inner);
+ let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
inner.unsafe_get_latest_holder_commitment_txn(&logger)
}
L::Target: Logger,
{
let mut inner = self.inner.lock().unwrap();
- let logger = WithChannelMonitor::from_impl(logger, &*inner);
+ let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
inner.block_connected(
header, txdata, height, broadcaster, fee_estimator, &logger)
}
L::Target: Logger,
{
let mut inner = self.inner.lock().unwrap();
- let logger = WithChannelMonitor::from_impl(logger, &*inner);
+ let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
inner.block_disconnected(
header, height, broadcaster, fee_estimator, &logger)
}
{
let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
let mut inner = self.inner.lock().unwrap();
- let logger = WithChannelMonitor::from_impl(logger, &*inner);
+ let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
inner.transactions_confirmed(
header, txdata, height, broadcaster, &bounded_fee_estimator, &logger)
}
{
let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
let mut inner = self.inner.lock().unwrap();
- let logger = WithChannelMonitor::from_impl(logger, &*inner);
+ let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
inner.transaction_unconfirmed(
txid, broadcaster, &bounded_fee_estimator, &logger
);
{
let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
let mut inner = self.inner.lock().unwrap();
- let logger = WithChannelMonitor::from_impl(logger, &*inner);
+ let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
inner.best_block_updated(
header, height, broadcaster, &bounded_fee_estimator, &logger
)
{
let fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
let mut inner = self.inner.lock().unwrap();
- let logger = WithChannelMonitor::from_impl(logger, &*inner);
+ let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
let current_height = inner.best_block.height;
inner.onchain_tx_handler.rebroadcast_pending_claims(
current_height, FeerateStrategy::HighestOfPreviousOrNew, &broadcaster, &fee_estimator, &logger,
{
let fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
let mut inner = self.inner.lock().unwrap();
- let logger = WithChannelMonitor::from_impl(logger, &*inner);
+ let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
let current_height = inner.best_block.height;
inner.onchain_tx_handler.rebroadcast_pending_claims(
current_height, FeerateStrategy::RetryPrevious, &broadcaster, &fee_estimator, &logger,
spendable_outputs
}
+ /// Checks if the monitor is fully resolved. Resolved monitor is one that has claimed all of
+ /// its outputs and balances (i.e. [`Self::get_claimable_balances`] returns an empty set).
+ ///
+ /// This function returns true only if [`Self::get_claimable_balances`] has been empty for at least
+ /// 4032 blocks as an additional protection against any bugs resulting in spuriously empty balance sets.
+ pub fn is_fully_resolved<L: Logger>(&self, logger: &L) -> bool {
+ let mut is_all_funds_claimed = self.get_claimable_balances().is_empty();
+ let current_height = self.current_best_block().height;
+ let mut inner = self.inner.lock().unwrap();
+
+ if is_all_funds_claimed {
+ if !inner.funding_spend_seen {
+ debug_assert!(false, "We should see funding spend by the time a monitor clears out");
+ is_all_funds_claimed = false;
+ }
+ }
+
+ const BLOCKS_THRESHOLD: u32 = 4032; // ~four weeks
+ match (inner.balances_empty_height, is_all_funds_claimed) {
+ (Some(balances_empty_height), true) => {
+ // Claimed all funds, check if reached the blocks threshold.
+ return current_height >= balances_empty_height + BLOCKS_THRESHOLD;
+ },
+ (Some(_), false) => {
+ // previously assumed we claimed all funds, but we have new funds to claim.
+ // Should not happen in practice.
+ debug_assert!(false, "Thought we were done claiming funds, but claimable_balances now has entries");
+ log_error!(logger,
+ "WARNING: LDK thought it was done claiming all the available funds in the ChannelMonitor for channel {}, but later decided it had more to claim. This is potentially an important bug in LDK, please report it at https://github.com/lightningdevkit/rust-lightning/issues/new",
+ inner.get_funding_txo().0);
+ inner.balances_empty_height = None;
+ false
+ },
+ (None, true) => {
+ // Claimed all funds but `balances_empty_height` is None. It is set to the
+ // current block height.
+ log_debug!(logger,
+ "ChannelMonitor funded at {} is now fully resolved. It will become archivable in {} blocks",
+ inner.get_funding_txo().0, BLOCKS_THRESHOLD);
+ inner.balances_empty_height = Some(current_height);
+ false
+ },
+ (None, false) => {
+ // Have funds to claim.
+ false
+ },
+ }
+ }
+
#[cfg(test)]
pub fn get_counterparty_payment_script(&self) -> ScriptBuf {
self.inner.lock().unwrap().counterparty_payment_script.clone()
}
}
-impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
+impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
/// Helper for get_claimable_balances which does the work for an individual HTLC, generating up
/// to one `Balance` for the HTLC.
fn get_htlc_balance(&self, htlc: &HTLCOutputInCommitment, holder_commitment: bool,
}
}
-impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
+impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
/// Gets the balances in this channel which are either claimable by us if we were to
/// force-close the channel now or which are claimable on-chain (possibly awaiting
/// confirmation).
} else { None }
}) {
res.push(Balance::ClaimableAwaitingConfirmations {
- amount_satoshis: value,
+ amount_satoshis: value.to_sat(),
confirmation_height: conf_thresh,
});
} else {
descriptor: SpendableOutputDescriptor::StaticOutput { output, .. }
} = &event.event {
res.push(Balance::ClaimableAwaitingConfirmations {
- amount_satoshis: output.value,
+ amount_satoshis: output.value.to_sat(),
confirmation_height: event.confirmation_threshold(),
});
if let Some(confirmed_to_self_idx) = confirmed_counterparty_output.map(|(idx, _)| idx) {
.is_output_spend_pending(&BitcoinOutPoint::new(txid, confirmed_to_self_idx));
if output_spendable {
res.push(Balance::CounterpartyRevokedOutputClaimable {
- amount_satoshis: amt,
+ amount_satoshis: amt.to_sat(),
});
}
} else {
debug_assert_eq!($commitment_tx_confirmed.txid(), $commitment_txid_confirmed);
macro_rules! check_htlc_fails {
- ($txid: expr, $commitment_tx: expr) => {
- if let Some(ref latest_outpoints) = $self.counterparty_claimable_outpoints.get($txid) {
+ ($txid: expr, $commitment_tx: expr, $per_commitment_outpoints: expr) => {
+ if let Some(ref latest_outpoints) = $per_commitment_outpoints {
for &(ref htlc, ref source_option) in latest_outpoints.iter() {
if let &Some(ref source) = source_option {
// Check if the HTLC is present in the commitment transaction that was
}
}
if let Some(ref txid) = $self.current_counterparty_commitment_txid {
- check_htlc_fails!(txid, "current");
+ check_htlc_fails!(txid, "current", $self.counterparty_claimable_outpoints.get(txid));
}
if let Some(ref txid) = $self.prev_counterparty_commitment_txid {
- check_htlc_fails!(txid, "previous");
+ check_htlc_fails!(txid, "previous", $self.counterparty_claimable_outpoints.get(txid));
}
} }
}
vec![Vec::new(), Vec::new(), Vec::new(), Vec::new(), deliberately_bogus_accepted_htlc_witness_program().into()].into()
}
-impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
+impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
/// Inserts a revocation secret into this channel monitor. Prunes old preimages if neither
/// needed by holder commitment transactions HTCLs nor by counterparty ones. Unless we haven't already seen
/// counterparty commitment transaction's secret, they are de facto pruned (we can use revocation key).
// If the channel is force closed, try to claim the output from this preimage.
// First check if a counterparty commitment transaction has been broadcasted:
macro_rules! claim_htlcs {
- ($commitment_number: expr, $txid: expr) => {
- let (htlc_claim_reqs, _) = self.get_counterparty_output_claim_info($commitment_number, $txid, None);
+ ($commitment_number: expr, $txid: expr, $htlcs: expr) => {
+ let (htlc_claim_reqs, _) = self.get_counterparty_output_claim_info($commitment_number, $txid, None, $htlcs);
self.onchain_tx_handler.update_claims_view_from_requests(htlc_claim_reqs, self.best_block.height, self.best_block.height, broadcaster, fee_estimator, logger);
}
}
if let Some(txid) = self.current_counterparty_commitment_txid {
if txid == confirmed_spend_txid {
if let Some(commitment_number) = self.counterparty_commitment_txn_on_chain.get(&txid) {
- claim_htlcs!(*commitment_number, txid);
+ claim_htlcs!(*commitment_number, txid, self.counterparty_claimable_outpoints.get(&txid));
} else {
debug_assert!(false);
log_error!(logger, "Detected counterparty commitment tx on-chain without tracking commitment number");
if let Some(txid) = self.prev_counterparty_commitment_txid {
if txid == confirmed_spend_txid {
if let Some(commitment_number) = self.counterparty_commitment_txn_on_chain.get(&txid) {
- claim_htlcs!(*commitment_number, txid);
+ claim_htlcs!(*commitment_number, txid, self.counterparty_claimable_outpoints.get(&txid));
} else {
debug_assert!(false);
log_error!(logger, "Detected counterparty commitment tx on-chain without tracking commitment number");
}
}
- fn generate_claimable_outpoints_and_watch_outputs(&mut self) -> (Vec<PackageTemplate>, Vec<TransactionOutputs>) {
+ fn generate_claimable_outpoints_and_watch_outputs(&mut self, reason: ClosureReason) -> (Vec<PackageTemplate>, Vec<TransactionOutputs>) {
let funding_outp = HolderFundingOutput::build(
self.funding_redeemscript.clone(),
self.channel_value_satoshis,
self.best_block.height, self.best_block.height
);
let mut claimable_outpoints = vec![commitment_package];
- self.pending_monitor_events.push(MonitorEvent::HolderForceClosed(self.funding_info.0));
+ let event = MonitorEvent::HolderForceClosedWithInfo {
+ reason,
+ outpoint: self.funding_info.0,
+ channel_id: self.channel_id,
+ };
+ self.pending_monitor_events.push(event);
+
// Although we aren't signing the transaction directly here, the transaction will be signed
// in the claim that is queued to OnchainTxHandler. We set holder_tx_signed here to reject
// new channel updates.
F::Target: FeeEstimator,
L::Target: Logger,
{
- let (claimable_outpoints, _) = self.generate_claimable_outpoints_and_watch_outputs();
+ let (claimable_outpoints, _) = self.generate_claimable_outpoints_and_watch_outputs(ClosureReason::HolderForceClosed);
self.onchain_tx_handler.update_claims_view_from_requests(
claimable_outpoints, self.best_block.height, self.best_block.height, broadcaster,
fee_estimator, logger
debug_assert_eq!(self.current_holder_commitment_tx.txid, commitment_txid);
let pending_htlcs = self.current_holder_commitment_tx.non_dust_htlcs();
let commitment_tx_fee_satoshis = self.channel_value_satoshis -
- commitment_tx.output.iter().fold(0u64, |sum, output| sum + output.value);
+ commitment_tx.output.iter().fold(0u64, |sum, output| sum + output.value.to_sat());
ret.push(Event::BumpTransaction(BumpTransactionEvent::ChannelClose {
channel_id,
counterparty_node_id,
},
commitment_txid: htlc.commitment_txid,
per_commitment_number: htlc.per_commitment_number,
- per_commitment_point: self.onchain_tx_handler.signer.get_per_commitment_point(
- htlc.per_commitment_number, &self.onchain_tx_handler.secp_ctx,
- ),
+ per_commitment_point: htlc.per_commitment_point,
feerate_per_kw: 0,
htlc: htlc.htlc,
preimage: htlc.preimage,
let sig = self.onchain_tx_handler.signer.sign_justice_revoked_output(
&justice_tx, input_idx, value, &per_commitment_key, &self.onchain_tx_handler.secp_ctx)?;
- justice_tx.input[input_idx].witness.push_bitcoin_signature(&sig.serialize_der(), EcdsaSighashType::All);
+ justice_tx.input[input_idx].witness.push_ecdsa_signature(&BitcoinSignature::sighash_all(sig));
justice_tx.input[input_idx].witness.push(&[1u8]);
justice_tx.input[input_idx].witness.push(revokeable_redeemscript.as_bytes());
Ok(justice_tx)
/// height > height + CLTV_SHARED_CLAIM_BUFFER. In any case, will install monitoring for
/// HTLC-Success/HTLC-Timeout transactions.
///
- /// Returns packages to claim the revoked output(s), as well as additional outputs to watch and
- /// general information about the output that is to the counterparty in the commitment
- /// transaction.
+ /// Returns packages to claim the revoked output(s) and general information about the output that
+ /// is to the counterparty in the commitment transaction.
fn check_spend_counterparty_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, block_hash: &BlockHash, logger: &L)
- -> (Vec<PackageTemplate>, TransactionOutputs, CommitmentTxCounterpartyOutputInfo)
+ -> (Vec<PackageTemplate>, CommitmentTxCounterpartyOutputInfo)
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();
- let mut watch_outputs = Vec::new();
let mut to_counterparty_output_info = None;
let commitment_txid = tx.txid(); //TODO: This is gonna be a performance bottleneck for watchtowers!
( $thing : expr ) => {
match $thing {
Ok(a) => a,
- Err(_) => return (claimable_outpoints, (commitment_txid, watch_outputs), to_counterparty_output_info)
+ Err(_) => return (claimable_outpoints, to_counterparty_output_info)
}
};
}
let delayed_key = DelayedPaymentKey::from_basepoint(&self.onchain_tx_handler.secp_ctx, &self.counterparty_commitment_params.counterparty_delayed_payment_base_key, &PublicKey::from_secret_key(&self.onchain_tx_handler.secp_ctx, &per_commitment_key));
let revokeable_redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.counterparty_commitment_params.on_counterparty_tx_csv, &delayed_key);
- let revokeable_p2wsh = revokeable_redeemscript.to_v0_p2wsh();
+ let revokeable_p2wsh = revokeable_redeemscript.to_p2wsh();
// First, process non-htlc outputs (to_holder & to_counterparty)
for (idx, outp) in tx.output.iter().enumerate() {
}
// Then, try to find revoked htlc outputs
- if let Some(ref per_commitment_data) = per_commitment_option {
- for (_, &(ref htlc, _)) in per_commitment_data.iter().enumerate() {
+ if let Some(per_commitment_claimable_data) = per_commitment_option {
+ for (htlc, _) in per_commitment_claimable_data {
if let Some(transaction_output_index) = htlc.transaction_output_index {
if transaction_output_index as usize >= tx.output.len() ||
- tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 {
+ tx.output[transaction_output_index as usize].value != htlc.to_bitcoin_amount() {
// per_commitment_data is corrupt or our commitment signing key leaked!
- return (claimable_outpoints, (commitment_txid, watch_outputs),
- to_counterparty_output_info);
+ return (claimable_outpoints, to_counterparty_output_info);
}
let revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, htlc.amount_msat / 1000, htlc.clone(), &self.onchain_tx_handler.channel_transaction_parameters.channel_type_features);
let justice_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, PackageSolvingData::RevokedHTLCOutput(revk_htlc_outp), htlc.cltv_expiry, height);
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_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.counterparty_commitment_txn_on_chain.insert(commitment_txid, commitment_number);
- if let Some(per_commitment_data) = per_commitment_option {
+ if let Some(per_commitment_claimable_data) = per_commitment_option {
fail_unbroadcast_htlcs!(self, "revoked_counterparty", commitment_txid, tx, height,
- block_hash, per_commitment_data.iter().map(|(htlc, htlc_source)|
+ block_hash, per_commitment_claimable_data.iter().map(|(htlc, htlc_source)|
(htlc, htlc_source.as_ref().map(|htlc_source| htlc_source.as_ref()))
), logger);
} else {
block_hash, [].iter().map(|reference| *reference), logger);
}
}
- } else if let Some(per_commitment_data) = per_commitment_option {
+ } else if let Some(per_commitment_claimable_data) = per_commitment_option {
// While this isn't useful yet, there is a potential race where if a counterparty
// revokes a state at the same time as the commitment transaction for that state is
// confirmed, and the watchtower receives the block before the user, the user could
// already processed the block, resulting in the counterparty_commitment_txn_on_chain entry
// not being generated by the above conditional. Thus, to be safe, we go ahead and
// insert it here.
- for (idx, outp) in tx.output.iter().enumerate() {
- watch_outputs.push((idx as u32, outp.clone()));
- }
self.counterparty_commitment_txn_on_chain.insert(commitment_txid, commitment_number);
log_info!(logger, "Got broadcast of non-revoked counterparty commitment transaction {}", commitment_txid);
fail_unbroadcast_htlcs!(self, "counterparty", commitment_txid, tx, height, block_hash,
- per_commitment_data.iter().map(|(htlc, htlc_source)|
+ per_commitment_claimable_data.iter().map(|(htlc, htlc_source)|
(htlc, htlc_source.as_ref().map(|htlc_source| htlc_source.as_ref()))
), logger);
-
let (htlc_claim_reqs, counterparty_output_info) =
- self.get_counterparty_output_claim_info(commitment_number, commitment_txid, Some(tx));
+ self.get_counterparty_output_claim_info(commitment_number, commitment_txid, Some(tx), per_commitment_option);
to_counterparty_output_info = counterparty_output_info;
for req in htlc_claim_reqs {
claimable_outpoints.push(req);
}
}
- (claimable_outpoints, (commitment_txid, watch_outputs), to_counterparty_output_info)
+ (claimable_outpoints, to_counterparty_output_info)
}
/// Returns the HTLC claim package templates and the counterparty output info
- fn get_counterparty_output_claim_info(&self, commitment_number: u64, commitment_txid: Txid, tx: Option<&Transaction>)
+ fn get_counterparty_output_claim_info(&self, commitment_number: u64, commitment_txid: Txid, tx: Option<&Transaction>, per_commitment_option: Option<&Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>>)
-> (Vec<PackageTemplate>, CommitmentTxCounterpartyOutputInfo) {
let mut claimable_outpoints = Vec::new();
let mut to_counterparty_output_info: CommitmentTxCounterpartyOutputInfo = None;
- let htlc_outputs = match self.counterparty_claimable_outpoints.get(&commitment_txid) {
+ let per_commitment_claimable_data = match per_commitment_option {
Some(outputs) => outputs,
None => return (claimable_outpoints, to_counterparty_output_info),
};
let revokeable_p2wsh = chan_utils::get_revokeable_redeemscript(&revocation_pubkey,
self.counterparty_commitment_params.on_counterparty_tx_csv,
- &delayed_key).to_v0_p2wsh();
+ &delayed_key).to_p2wsh();
for (idx, outp) in transaction.output.iter().enumerate() {
if outp.script_pubkey == revokeable_p2wsh {
to_counterparty_output_info =
}
}
- for (_, &(ref htlc, _)) in htlc_outputs.iter().enumerate() {
+ for &(ref htlc, _) in per_commitment_claimable_data.iter() {
if let Some(transaction_output_index) = htlc.transaction_output_index {
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 {
+ transaction.output[transaction_output_index as usize].value != htlc.to_bitcoin_amount() {
// per_commitment_data is corrupt or our commitment signing key leaked!
return (claimable_outpoints, to_counterparty_output_info);
}
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);
- let broadcasted_holder_revokable_script = Some((redeemscript.to_v0_p2wsh(), holder_tx.per_commitment_point.clone(), holder_tx.revocation_key.clone()));
+ let broadcasted_holder_revokable_script = Some((redeemscript.to_p2wsh(), holder_tx.per_commitment_point.clone(), holder_tx.revocation_key.clone()));
for &(ref htlc, _, _) in holder_tx.htlc_outputs.iter() {
if let Some(transaction_output_index) = htlc.transaction_output_index {
if counterparty_commitment_txid == confirmed_commitment_txid {
continue;
}
+ // If we have generated claims for counterparty_commitment_txid earlier, we can rely on always
+ // having claim related htlcs for counterparty_commitment_txid in counterparty_claimable_outpoints.
for (htlc, _) in self.counterparty_claimable_outpoints.get(counterparty_commitment_txid).unwrap_or(&vec![]) {
log_trace!(logger, "Canceling claims for previously confirmed counterparty commitment {}",
counterparty_commitment_txid);
{
let txn_matched = self.filter_block(txdata);
for tx in &txn_matched {
- let mut output_val = 0;
+ let mut output_val = Amount::ZERO;
for out in tx.output.iter() {
- if out.value > 21_000_000_0000_0000 { panic!("Value-overflowing transaction provided to block connected"); }
+ if out.value > Amount::MAX_MONEY { panic!("Value-overflowing transaction provided to block connected"); }
output_val += out.value;
- if output_val > 21_000_000_0000_0000 { panic!("Value-overflowing transaction provided to block connected"); }
+ if output_val > Amount::MAX_MONEY { panic!("Value-overflowing transaction provided to block connected"); }
}
}
self.funding_spend_seen = true;
let mut commitment_tx_to_counterparty_output = None;
if (tx.input[0].sequence.0 >> 8*3) as u8 == 0x80 && (tx.lock_time.to_consensus_u32() >> 8*3) as u8 == 0x20 {
- let (mut new_outpoints, new_outputs, counterparty_output_idx_sats) =
- self.check_spend_counterparty_transaction(&tx, height, &block_hash, &logger);
- commitment_tx_to_counterparty_output = counterparty_output_idx_sats;
- if !new_outputs.1.is_empty() {
- watch_outputs.push(new_outputs);
- }
- claimable_outpoints.append(&mut new_outpoints);
- if new_outpoints.is_empty() {
- if let Some((mut new_outpoints, new_outputs)) = self.check_spend_holder_transaction(&tx, height, &block_hash, &logger) {
- #[cfg(not(fuzzing))]
- debug_assert!(commitment_tx_to_counterparty_output.is_none(),
- "A commitment transaction matched as both a counterparty and local commitment tx?");
- if !new_outputs.1.is_empty() {
- watch_outputs.push(new_outputs);
- }
- claimable_outpoints.append(&mut new_outpoints);
- balance_spendable_csv = Some(self.on_holder_tx_csv);
+ if let Some((mut new_outpoints, new_outputs)) = self.check_spend_holder_transaction(&tx, height, &block_hash, &logger) {
+ if !new_outputs.1.is_empty() {
+ watch_outputs.push(new_outputs);
+ }
+
+ claimable_outpoints.append(&mut new_outpoints);
+ balance_spendable_csv = Some(self.on_holder_tx_csv);
+ } else {
+ let mut new_watch_outputs = Vec::new();
+ for (idx, outp) in tx.output.iter().enumerate() {
+ new_watch_outputs.push((idx as u32, outp.clone()));
}
+ watch_outputs.push((txid, new_watch_outputs));
+
+ let (mut new_outpoints, counterparty_output_idx_sats) =
+ self.check_spend_counterparty_transaction(&tx, height, &block_hash, &logger);
+ commitment_tx_to_counterparty_output = counterparty_output_idx_sats;
+
+ claimable_outpoints.append(&mut new_outpoints);
}
}
self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry {
let should_broadcast = self.should_broadcast_holder_commitment_txn(logger);
if should_broadcast {
- let (mut new_outpoints, mut new_outputs) = self.generate_claimable_outpoints_and_watch_outputs();
+ let (mut new_outpoints, mut new_outputs) = self.generate_claimable_outpoints_and_watch_outputs(ClosureReason::HTLCsTimedOut);
claimable_outpoints.append(&mut new_outpoints);
watch_outputs.append(&mut new_outputs);
}
// If the expected script is a known type, check that the witness
// appears to be spending the correct type (ie that the match would
// actually succeed in BIP 158/159-style filters).
- if _script_pubkey.is_v0_p2wsh() {
+ if _script_pubkey.is_p2wsh() {
if input.witness.last().unwrap().to_vec() == deliberately_bogus_accepted_htlc_witness_program() {
// In at least one test we use a deliberately bogus witness
// script which hit an old panic. Thus, we check for that here
}
assert_eq!(&bitcoin::Address::p2wsh(&ScriptBuf::from(input.witness.last().unwrap().to_vec()), bitcoin::Network::Bitcoin).script_pubkey(), _script_pubkey);
- } else if _script_pubkey.is_v0_p2wpkh() {
+ } else if _script_pubkey.is_p2wpkh() {
assert_eq!(&bitcoin::Address::p2wpkh(&bitcoin::PublicKey::from_slice(&input.witness.last().unwrap()).unwrap(), bitcoin::Network::Bitcoin).unwrap().script_pubkey(), _script_pubkey);
} else { panic!(); }
}
}
macro_rules! check_htlc_valid_counterparty {
- ($counterparty_txid: expr, $htlc_output: expr) => {
- if let Some(txid) = $counterparty_txid {
- for &(ref pending_htlc, ref pending_source) in self.counterparty_claimable_outpoints.get(&txid).unwrap() {
+ ($htlc_output: expr, $per_commitment_data: expr) => {
+ for &(ref pending_htlc, ref pending_source) in $per_commitment_data {
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);
}
}
}
- }
}
}
// resolve the source HTLC with the original sender.
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 let Some(current_counterparty_commitment_txid) = &self.current_counterparty_commitment_txid {
+ check_htlc_valid_counterparty!(htlc_output, self.counterparty_claimable_outpoints.get(current_counterparty_commitment_txid).unwrap());
+ }
if payment_data.is_none() {
- check_htlc_valid_counterparty!(self.prev_counterparty_commitment_txid, htlc_output);
+ if let Some(prev_counterparty_commitment_txid) = &self.prev_counterparty_commitment_txid {
+ check_htlc_valid_counterparty!(htlc_output, self.counterparty_claimable_outpoints.get(prev_counterparty_commitment_txid).unwrap());
+ }
}
}
if payment_data.is_none() {
}
}
if let Some(ref htlc_outputs) = self.counterparty_claimable_outpoints.get(&input.previous_output.txid) {
- scan_commitment!(htlc_outputs.iter().map(|&(ref a, ref b)| (a, (b.as_ref().clone()).map(|boxed| &**boxed))),
+ scan_commitment!(htlc_outputs.iter().map(|&(ref a, ref b)| (a, b.as_ref().map(|boxed| &**boxed))),
"counterparty commitment tx", false);
}
revocation_pubkey: broadcasted_holder_revokable_script.2,
channel_keys_id: self.channel_keys_id,
channel_value_satoshis: self.channel_value_satoshis,
+ channel_transaction_parameters: Some(self.onchain_tx_handler.channel_transaction_parameters.clone()),
}));
}
}
}
}
-impl<Signer: WriteableEcdsaChannelSigner, T: Deref, F: Deref, L: Deref> chain::Listen for (ChannelMonitor<Signer>, T, F, L)
+impl<Signer: EcdsaChannelSigner, T: Deref, F: Deref, L: Deref> chain::Listen for (ChannelMonitor<Signer>, T, F, L)
where
T::Target: BroadcasterInterface,
F::Target: FeeEstimator,
}
}
-impl<Signer: WriteableEcdsaChannelSigner, M, T: Deref, F: Deref, L: Deref> chain::Confirm for (M, T, F, L)
+impl<Signer: EcdsaChannelSigner, M, T: Deref, F: Deref, L: Deref> chain::Confirm for (M, T, F, L)
where
M: Deref<Target = ChannelMonitor<Signer>>,
T::Target: BroadcasterInterface,
let mut spendable_txids_confirmed = Some(Vec::new());
let mut counterparty_fulfilled_htlcs = Some(new_hash_map());
let mut initial_counterparty_commitment_info = None;
+ let mut balances_empty_height = None;
let mut channel_id = None;
read_tlv_fields!(reader, {
(1, funding_spend_confirmed, option),
(15, counterparty_fulfilled_htlcs, option),
(17, initial_counterparty_commitment_info, option),
(19, channel_id, option),
+ (21, balances_empty_height, option),
});
+ // `HolderForceClosedWithInfo` replaced `HolderForceClosed` in v0.0.122. If we have both
+ // events, we can remove the `HolderForceClosed` event and just keep the `HolderForceClosedWithInfo`.
+ if let Some(ref mut pending_monitor_events) = pending_monitor_events {
+ if pending_monitor_events.iter().any(|e| matches!(e, MonitorEvent::HolderForceClosed(_))) &&
+ pending_monitor_events.iter().any(|e| matches!(e, MonitorEvent::HolderForceClosedWithInfo { .. }))
+ {
+ pending_monitor_events.retain(|e| !matches!(e, MonitorEvent::HolderForceClosed(_)));
+ }
+ }
+
// Monitors for anchor outputs channels opened in v0.0.116 suffered from a bug in which the
// wrong `counterparty_payment_script` was being tracked. Fix it now on deserialization to
// give them a chance to recognize the spendable output.
if onchain_tx_handler.channel_type_features().supports_anchors_zero_fee_htlc_tx() &&
- counterparty_payment_script.is_v0_p2wpkh()
+ counterparty_payment_script.is_p2wpkh()
{
let payment_point = onchain_tx_handler.channel_transaction_parameters.holder_pubkeys.payment_point;
counterparty_payment_script =
- chan_utils::get_to_countersignatory_with_anchors_redeemscript(&payment_point).to_v0_p2wsh();
+ chan_utils::get_to_countersignatory_with_anchors_redeemscript(&payment_point).to_p2wsh();
}
Ok((best_block.block_hash, ChannelMonitor::from_impl(ChannelMonitorImpl {
best_block,
counterparty_node_id,
initial_counterparty_commitment_info,
+ balances_empty_height,
})))
}
}
#[cfg(test)]
mod tests {
+ use bitcoin::amount::Amount;
use bitcoin::blockdata::locktime::absolute::LockTime;
use bitcoin::blockdata::script::{ScriptBuf, Builder};
use bitcoin::blockdata::opcodes;
- use bitcoin::blockdata::transaction::{Transaction, TxIn, TxOut};
+ use bitcoin::blockdata::transaction::{Transaction, TxIn, TxOut, Version};
use bitcoin::blockdata::transaction::OutPoint as BitcoinOutPoint;
use bitcoin::sighash;
use bitcoin::sighash::EcdsaSighashType;
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::hex::FromHex;
use bitcoin::hash_types::{BlockHash, Txid};
- use bitcoin::network::constants::Network;
+ use bitcoin::network::Network;
use bitcoin::secp256k1::{SecretKey,PublicKey};
use bitcoin::secp256k1::Secp256k1;
use bitcoin::{Sequence, Witness};
use crate::chain::package::{weight_offered_htlc, weight_received_htlc, weight_revoked_offered_htlc, weight_revoked_received_htlc, WEIGHT_REVOKED_OUTPUT};
use crate::chain::transaction::OutPoint;
use crate::sign::InMemorySigner;
- use crate::ln::{PaymentPreimage, PaymentHash, ChannelId};
+ use crate::ln::types::{PaymentPreimage, PaymentHash, ChannelId};
use crate::ln::channel_keys::{DelayedPaymentBasepoint, DelayedPaymentKey, HtlcBasepoint, RevocationBasepoint, RevocationKey};
use crate::ln::chan_utils::{self,HTLCOutputInCommitment, ChannelPublicKeys, ChannelTransactionParameters, HolderCommitmentTransaction, CounterpartyChannelTransactionParameters};
use crate::ln::channelmanager::{PaymentSendFailure, PaymentId, RecipientOnionFields};
use crate::sync::{Arc, Mutex};
use crate::io;
use crate::ln::features::ChannelTypeFeatures;
+
+ #[allow(unused_imports)]
use crate::prelude::*;
use std::str::FromStr;
}
}
let dummy_sig = crate::crypto::utils::sign(&secp_ctx,
- &bitcoin::secp256k1::Message::from_slice(&[42; 32]).unwrap(),
+ &bitcoin::secp256k1::Message::from_digest([42; 32]),
&SecretKey::from_slice(&[42; 32]).unwrap());
macro_rules! test_preimages_exist {
transaction_output_index: Some($idx as u32),
};
let redeem_script = if *$weight == WEIGHT_REVOKED_OUTPUT { chan_utils::get_revokeable_redeemscript(&RevocationKey::from_basepoint(&secp_ctx, &RevocationBasepoint::from(pubkey), &pubkey), 256, &DelayedPaymentKey::from_basepoint(&secp_ctx, &DelayedPaymentBasepoint::from(pubkey), &pubkey)) } else { chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, $opt_anchors, &HtlcKey::from_basepoint(&secp_ctx, &HtlcBasepoint::from(pubkey), &pubkey), &HtlcKey::from_basepoint(&secp_ctx, &HtlcBasepoint::from(pubkey), &pubkey), &RevocationKey::from_basepoint(&secp_ctx, &RevocationBasepoint::from(pubkey), &pubkey)) };
- let sighash = hash_to_message!(&$sighash_parts.segwit_signature_hash($idx, &redeem_script, $amount, EcdsaSighashType::All).unwrap()[..]);
+ let sighash = hash_to_message!(&$sighash_parts.p2wsh_signature_hash($idx, &redeem_script, $amount, EcdsaSighashType::All).unwrap()[..]);
let sig = secp_ctx.sign_ecdsa(&sighash, &privkey);
let mut ser_sig = sig.serialize_der().to_vec();
ser_sig.push(EcdsaSighashType::All as u8);
// Justice tx with 1 to_holder, 2 revoked offered HTLCs, 1 revoked received HTLCs
for channel_type_features in [ChannelTypeFeatures::only_static_remote_key(), ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies()].iter() {
- let mut claim_tx = Transaction { version: 0, lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
+ let mut claim_tx = Transaction { version: Version(0), lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
let mut sum_actual_sigs = 0;
for i in 0..4 {
claim_tx.input.push(TxIn {
}
claim_tx.output.push(TxOut {
script_pubkey: script_pubkey.clone(),
- value: 0,
+ value: Amount::ZERO,
});
let base_weight = claim_tx.weight().to_wu();
let inputs_weight = vec![WEIGHT_REVOKED_OUTPUT, weight_revoked_offered_htlc(channel_type_features), weight_revoked_offered_htlc(channel_type_features), weight_revoked_received_htlc(channel_type_features)];
{
let mut sighash_parts = sighash::SighashCache::new(&mut claim_tx);
for (idx, inp) in inputs_weight.iter().enumerate() {
- sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs, channel_type_features);
+ sign_input!(sighash_parts, idx, Amount::ZERO, inp, sum_actual_sigs, channel_type_features);
inputs_total_weight += inp;
}
}
// Claim tx with 1 offered HTLCs, 3 received HTLCs
for channel_type_features in [ChannelTypeFeatures::only_static_remote_key(), ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies()].iter() {
- let mut claim_tx = Transaction { version: 0, lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
+ let mut claim_tx = Transaction { version: Version(0), lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
let mut sum_actual_sigs = 0;
for i in 0..4 {
claim_tx.input.push(TxIn {
}
claim_tx.output.push(TxOut {
script_pubkey: script_pubkey.clone(),
- value: 0,
+ value: Amount::ZERO,
});
let base_weight = claim_tx.weight().to_wu();
let inputs_weight = vec![weight_offered_htlc(channel_type_features), weight_received_htlc(channel_type_features), weight_received_htlc(channel_type_features), weight_received_htlc(channel_type_features)];
{
let mut sighash_parts = sighash::SighashCache::new(&mut claim_tx);
for (idx, inp) in inputs_weight.iter().enumerate() {
- sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs, channel_type_features);
+ sign_input!(sighash_parts, idx, Amount::ZERO, inp, sum_actual_sigs, channel_type_features);
inputs_total_weight += inp;
}
}
// Justice tx with 1 revoked HTLC-Success tx output
for channel_type_features in [ChannelTypeFeatures::only_static_remote_key(), ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies()].iter() {
- let mut claim_tx = Transaction { version: 0, lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
+ let mut claim_tx = Transaction { version: Version(0), lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
let mut sum_actual_sigs = 0;
claim_tx.input.push(TxIn {
previous_output: BitcoinOutPoint {
});
claim_tx.output.push(TxOut {
script_pubkey: script_pubkey.clone(),
- value: 0,
+ value: Amount::ZERO,
});
let base_weight = claim_tx.weight().to_wu();
let inputs_weight = vec![WEIGHT_REVOKED_OUTPUT];
{
let mut sighash_parts = sighash::SighashCache::new(&mut claim_tx);
for (idx, inp) in inputs_weight.iter().enumerate() {
- sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs, channel_type_features);
+ sign_input!(sighash_parts, idx, Amount::ZERO, inp, sum_actual_sigs, channel_type_features);
inputs_total_weight += inp;
}
}
best_block, dummy_key, channel_id);
let chan_id = monitor.inner.lock().unwrap().channel_id();
- let context_logger = WithChannelMonitor::from(&logger, &monitor);
+ let payment_hash = PaymentHash([1; 32]);
+ let context_logger = WithChannelMonitor::from(&logger, &monitor, Some(payment_hash));
log_error!(context_logger, "This is an error");
log_warn!(context_logger, "This is an error");
log_debug!(context_logger, "This is an error");