use prelude::*;
use core::{cmp, mem};
-use std::io::Error;
+use io::{self, Error};
use core::ops::Deref;
use sync::Mutex;
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> {
+ fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
write_ver_prefix!(w, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION);
self.update_id.write(w)?;
(self.updates.len() as u64).write(w)?;
}
}
impl Readable for ChannelMonitorUpdate {
- fn read<R: ::std::io::Read>(r: &mut R) -> Result<Self, DecodeError> {
+ fn read<R: 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)?;
}
/// 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_tlv_based!(HTLCUpdate, {
(0, payment_hash, required),
+ (1, onchain_value_satoshis, option),
(2, source, required),
(4, payment_preimage, option),
});
}
impl Writeable for CounterpartyCommitmentTransaction {
- fn write<W: Writer>(&self, w: &mut W) -> Result<(), ::std::io::Error> {
+ fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
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[..])?;
}
}
impl Readable for CounterpartyCommitmentTransaction {
- fn read<R: ::std::io::Read>(r: &mut R) -> Result<Self, DecodeError> {
+ fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
let counterparty_commitment_transaction = {
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));
HTLCUpdate {
source: HTLCSource,
payment_hash: PaymentHash,
+ onchain_value_satoshis: Option<u64>,
},
MaturingOutput {
descriptor: SpendableOutputDescriptor,
impl_writeable_tlv_based_enum!(OnchainEvent,
(0, HTLCUpdate) => {
(0, source, required),
+ (1, onchain_value_satoshis, option),
(2, payment_hash, required),
},
(1, MaturingOutput) => {
/// think we've fallen behind!
should_broadcast: bool,
},
+ ShutdownScript {
+ scriptpubkey: Script,
+ },
}
impl_writeable_tlv_based_enum!(ChannelMonitorUpdateStep,
(4, ChannelForceClosed) => {
(0, should_broadcast, required),
},
+ (5, ShutdownScript) => {
+ (0, scriptpubkey, required),
+ },
;);
/// A ChannelMonitor handles chain events (blocks connected and disconnected) and generates
destination_script: Script,
broadcasted_holder_revokable_script: Option<(Script, PublicKey, PublicKey)>,
counterparty_payment_script: Script,
- shutdown_script: Script,
+ shutdown_script: Option<Script>,
channel_keys_id: [u8; 32],
holder_revocation_basepoint: PublicKey,
}
self.counterparty_payment_script.write(writer)?;
- self.shutdown_script.write(writer)?;
+ match &self.shutdown_script {
+ Some(script) => script.write(writer)?,
+ None => Script::new().write(writer)?,
+ }
self.channel_keys_id.write(writer)?;
self.holder_revocation_basepoint.write(writer)?;
}
impl<Signer: Sign> ChannelMonitor<Signer> {
- pub(crate) fn new(secp_ctx: Secp256k1<secp256k1::All>, keys: Signer, shutdown_pubkey: &PublicKey,
+ pub(crate) fn new(secp_ctx: Secp256k1<secp256k1::All>, keys: Signer, shutdown_script: Option<Script>,
on_counterparty_tx_csv: u16, destination_script: &Script, funding_info: (OutPoint, Script),
channel_parameters: &ChannelTransactionParameters,
funding_redeemscript: Script, channel_value_satoshis: u64,
best_block: BestBlock) -> ChannelMonitor<Signer> {
assert!(commitment_transaction_number_obscure_factor <= (1 << 48));
- let our_channel_close_key_hash = WPubkeyHash::hash(&shutdown_pubkey.serialize());
- let shutdown_script = Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&our_channel_close_key_hash[..]).into_script();
let payment_key_hash = WPubkeyHash::hash(&keys.pubkeys().payment_point.serialize());
let counterparty_payment_script = Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&payment_key_hash[..]).into_script();
// shouldn't print the scary warning above.
log_info!(logger, "Channel off-chain state closed after we broadcasted our latest commitment transaction.");
}
- }
+ },
+ ChannelMonitorUpdateStep::ShutdownScript { scriptpubkey } => {
+ log_trace!(logger, "Updating ChannelMonitor with shutdown script");
+ if let Some(shutdown_script) = self.shutdown_script.replace(scriptpubkey.clone()) {
+ panic!("Attempted to replace shutdown script {} with {}", shutdown_script, scriptpubkey);
+ }
+ },
}
}
self.latest_update_id = updates.update_id;
event: OnchainEvent::HTLCUpdate {
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());
event: OnchainEvent::HTLCUpdate {
source: (**source).clone(),
payment_hash: htlc.payment_hash.clone(),
+ onchain_value_satoshis: Some(htlc.amount_msat / 1000),
},
});
}
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 { source: ref update_source, .. } => {
- *update_source != $source
- },
- _ => true,
- }
- });
- let entry = OnchainEventEntry {
- txid: commitment_txid,
- height,
- event: OnchainEvent::HTLCUpdate { source: $source, payment_hash: $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;
}
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");
}
}
// 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 { ref source, payment_hash } => {
+ OnchainEvent::HTLCUpdate { ref source, payment_hash, onchain_value_satoshis } => {
// Check for duplicate HTLC resolutions.
#[cfg(debug_assertions)]
{
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: payment_hash,
+ payment_hash,
payment_preimage: None,
source: source.clone(),
+ onchain_value_satoshis,
}));
},
OnchainEvent::MaturingOutput { descriptor } => {
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 {
let entry = OnchainEventEntry {
txid: tx.txid(),
height,
- event: OnchainEvent::HTLCUpdate { source: source, payment_hash: 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());
self.onchain_events_awaiting_threshold_conf.push(entry);
output: outp.clone(),
});
break;
- } else if let Some(ref broadcasted_holder_revokable_script) = self.broadcasted_holder_revokable_script {
+ }
+ if let Some(ref broadcasted_holder_revokable_script) = self.broadcasted_holder_revokable_script {
if broadcasted_holder_revokable_script.0 == outp.script_pubkey {
spendable_output = Some(SpendableOutputDescriptor::DelayedPaymentOutput(DelayedPaymentOutputDescriptor {
outpoint: OutPoint { txid: tx.txid(), index: i as u16 },
}));
break;
}
- } else if self.counterparty_payment_script == outp.script_pubkey {
+ }
+ if self.counterparty_payment_script == outp.script_pubkey {
spendable_output = Some(SpendableOutputDescriptor::StaticPaymentOutput(StaticPaymentOutputDescriptor {
outpoint: OutPoint { txid: tx.txid(), index: i as u16 },
output: outp.clone(),
channel_value_satoshis: self.channel_value_satoshis,
}));
break;
- } else if outp.script_pubkey == self.shutdown_script {
+ }
+ if self.shutdown_script.as_ref() == Some(&outp.script_pubkey) {
spendable_output = Some(SpendableOutputDescriptor::StaticOutput {
outpoint: OutPoint { txid: tx.txid(), index: i as u16 },
output: outp.clone(),
});
+ break;
}
}
if let Some(spendable_output) = spendable_output {
impl<'a, Signer: Sign, K: KeysInterface<Signer = Signer>> ReadableArgs<&'a K>
for (BlockHash, ChannelMonitor<Signer>) {
- fn read<R: ::std::io::Read>(reader: &mut R, keys_manager: &'a K) -> Result<Self, DecodeError> {
+ fn read<R: io::Read>(reader: &mut R, keys_manager: &'a K) -> Result<Self, DecodeError> {
macro_rules! unwrap_obj {
($key: expr) => {
match $key {
_ => return Err(DecodeError::InvalidValue),
};
let counterparty_payment_script = Readable::read(reader)?;
- let shutdown_script = Readable::read(reader)?;
+ let shutdown_script = {
+ let script = <Script as Readable>::read(reader)?;
+ if script.is_empty() { None } else { Some(script) }
+ };
let channel_keys_id = Readable::read(reader)?;
let holder_revocation_basepoint = Readable::read(reader)?;
use ln::{PaymentPreimage, PaymentHash};
use ln::chan_utils;
use ln::chan_utils::{HTLCOutputInCommitment, ChannelPublicKeys, ChannelTransactionParameters, HolderCommitmentTransaction, CounterpartyChannelTransactionParameters};
+ use ln::script::ShutdownScript;
use util::test_utils::{TestLogger, TestBroadcaster, TestFeeEstimator};
use bitcoin::secp256k1::key::{SecretKey,PublicKey};
use bitcoin::secp256k1::Secp256k1;
};
// Prune with one old state and a holder commitment tx holding a few overlaps with the
// old state.
+ let shutdown_pubkey = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let best_block = BestBlock::from_genesis(Network::Testnet);
let monitor = ChannelMonitor::new(Secp256k1::new(), keys,
- &PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()), 0, &Script::new(),
+ Some(ShutdownScript::new_p2wpkh_from_pubkey(shutdown_pubkey).into_inner()), 0, &Script::new(),
(OutPoint { txid: Txid::from_slice(&[43; 32]).unwrap(), index: 0 }, Script::new()),
&channel_parameters,
Script::new(), 46, 0,