Merge pull request #2337 from alecchendev/2023-06-watchtower-support

Support third-party watchtowers in persistence pipeline
This commit is contained in:
Matt Corallo 2023-08-23 20:05:40 +00:00 committed by GitHub
commit 32d6e91fd6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 632 additions and 93 deletions

View file

@ -94,6 +94,17 @@ impl MonitorUpdateId {
/// [`ChannelMonitorUpdateStatus::PermanentFailure`], in which case the channel will likely be
/// closed without broadcasting the latest state. See
/// [`ChannelMonitorUpdateStatus::PermanentFailure`] for more details.
///
/// Third-party watchtowers may be built as a part of an implementation of this trait, with the
/// advantage that you can control whether to resume channel operation depending on if an update
/// has been persisted to a watchtower. For this, you may find the following methods useful:
/// [`ChannelMonitor::initial_counterparty_commitment_tx`],
/// [`ChannelMonitor::counterparty_commitment_txs_from_update`],
/// [`ChannelMonitor::sign_to_local_justice_tx`], [`TrustedCommitmentTransaction::revokeable_output_index`],
/// [`TrustedCommitmentTransaction::build_to_local_justice_tx`].
///
/// [`TrustedCommitmentTransaction::revokeable_output_index`]: crate::ln::chan_utils::TrustedCommitmentTransaction::revokeable_output_index
/// [`TrustedCommitmentTransaction::build_to_local_justice_tx`]: crate::ln::chan_utils::TrustedCommitmentTransaction::build_to_local_justice_tx
pub trait Persist<ChannelSigner: WriteableEcdsaChannelSigner> {
/// Persist a new channel's data in response to a [`chain::Watch::watch_channel`] call. This is
/// called by [`ChannelManager`] for new channels, or may be called directly, e.g. on startup.

View file

@ -31,12 +31,13 @@ use bitcoin::hash_types::{Txid, BlockHash, WPubkeyHash};
use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature};
use bitcoin::secp256k1::{SecretKey, PublicKey};
use bitcoin::secp256k1;
use bitcoin::{secp256k1, EcdsaSighashType};
use crate::ln::channel::INITIAL_COMMITMENT_NUMBER;
use crate::ln::{PaymentHash, PaymentPreimage};
use crate::ln::msgs::DecodeError;
use crate::ln::chan_utils;
use crate::ln::chan_utils::{CounterpartyCommitmentSecrets, HTLCOutputInCommitment, HTLCClaim, ChannelTransactionParameters, HolderCommitmentTransaction};
use crate::ln::chan_utils::{CommitmentTransaction, CounterpartyCommitmentSecrets, HTLCOutputInCommitment, HTLCClaim, ChannelTransactionParameters, HolderCommitmentTransaction, TxCreationKeys};
use crate::ln::channelmanager::{HTLCSource, SentHTLCId};
use crate::chain;
use crate::chain::{BestBlock, WatchedOutput};
@ -502,6 +503,9 @@ pub(crate) enum ChannelMonitorUpdateStep {
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>,
commitment_number: u64,
their_per_commitment_point: PublicKey,
feerate_per_kw: Option<u32>,
to_broadcaster_value_sat: Option<u64>,
to_countersignatory_value_sat: Option<u64>,
},
PaymentPreimage {
payment_preimage: PaymentPreimage,
@ -544,8 +548,11 @@ impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep,
},
(1, LatestCounterpartyCommitmentTXInfo) => {
(0, commitment_txid, required),
(1, feerate_per_kw, option),
(2, commitment_number, required),
(3, to_broadcaster_value_sat, option),
(4, their_per_commitment_point, required),
(5, to_countersignatory_value_sat, option),
(6, htlc_outputs, required_vec),
},
(2, PaymentPreimage) => {
@ -882,6 +889,14 @@ pub(crate) struct ChannelMonitorImpl<Signer: WriteableEcdsaChannelSigner> {
/// The node_id of our counterparty
counterparty_node_id: Option<PublicKey>,
/// Initial counterparty commmitment data needed to recreate the commitment tx
/// in the persistence pipeline for third-party watchtowers. This will only be present on
/// monitors created after 0.0.117.
///
/// 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)>,
}
/// Transaction outputs to watch for on-chain spends.
@ -1072,6 +1087,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signe
(11, self.confirmed_commitment_tx_counterparty_output, option),
(13, self.spendable_txids_confirmed, required_vec),
(15, self.counterparty_fulfilled_htlcs, required),
(17, self.initial_counterparty_commitment_info, option),
});
Ok(())
@ -1222,6 +1238,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
best_block,
counterparty_node_id: Some(counterparty_node_id),
initial_counterparty_commitment_info: None,
})
}
@ -1230,11 +1247,31 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
self.inner.lock().unwrap().provide_secret(idx, secret)
}
/// A variant of `Self::provide_latest_counterparty_commitment_tx` used to provide
/// additional information to the monitor to store in order to recreate the initial
/// counterparty commitment transaction during persistence (mainly for use in third-party
/// watchtowers).
///
/// This is used to provide the counterparty commitment information directly to the monitor
/// before the initial persistence of a new channel.
pub(crate) fn provide_initial_counterparty_commitment_tx<L: Deref>(
&self, txid: Txid, htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>,
commitment_number: u64, their_cur_per_commitment_point: PublicKey, feerate_per_kw: u32,
to_broadcaster_value_sat: u64, to_countersignatory_value_sat: u64, logger: &L,
)
where L::Target: Logger
{
self.inner.lock().unwrap().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);
}
/// Informs this monitor of the latest counterparty (ie non-broadcastable) commitment transaction.
/// The monitor watches for it to be broadcasted and then uses the HTLC information (and
/// possibly future revocation/preimage information) to claim outputs where possible.
/// We cache also the mapping hash:commitment number to lighten pruning of old preimages by watchtowers.
pub(crate) fn provide_latest_counterparty_commitment_tx<L: Deref>(
#[cfg(test)]
fn provide_latest_counterparty_commitment_tx<L: Deref>(
&self,
txid: Txid,
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>,
@ -1370,6 +1407,67 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
ret
}
/// Gets the counterparty's initial commitment transaction. The returned commitment
/// transaction is unsigned. This is intended to be called during the initial persistence of
/// the monitor (inside an implementation of [`Persist::persist_new_channel`]), to allow for
/// watchtowers in the persistence pipeline to have enough data to form justice transactions.
///
/// This is similar to [`Self::counterparty_commitment_txs_from_update`], except
/// that for the initial commitment transaction, we don't have a corresponding update.
///
/// This will only return `Some` for channel monitors that have been created after upgrading
/// to LDK 0.0.117+.
///
/// [`Persist::persist_new_channel`]: crate::chain::chainmonitor::Persist::persist_new_channel
pub fn initial_counterparty_commitment_tx(&self) -> Option<CommitmentTransaction> {
self.inner.lock().unwrap().initial_counterparty_commitment_tx()
}
/// Gets all of the counterparty commitment transactions provided by the given update. This
/// may be empty if the update doesn't include any new counterparty commitments. Returned
/// commitment transactions are unsigned.
///
/// This is provided so that watchtower clients in the persistence pipeline are able to build
/// justice transactions for each counterparty commitment upon each update. It's intended to be
/// used within an implementation of [`Persist::update_persisted_channel`], which is provided
/// with a monitor and an update. Once revoked, signing a justice transaction can be done using
/// [`Self::sign_to_local_justice_tx`].
///
/// It is expected that a watchtower client may use this method to retrieve the latest counterparty
/// commitment transaction(s), and then hold the necessary data until a later update in which
/// the monitor has been updated with the corresponding revocation data, at which point the
/// monitor can sign the justice transaction.
///
/// This will only return a non-empty list for monitor updates that have been created after
/// upgrading to LDK 0.0.117+. Note that no restriction lies on the monitors themselves, which
/// may have been created prior to upgrading.
///
/// [`Persist::update_persisted_channel`]: crate::chain::chainmonitor::Persist::update_persisted_channel
pub fn counterparty_commitment_txs_from_update(&self, update: &ChannelMonitorUpdate) -> Vec<CommitmentTransaction> {
self.inner.lock().unwrap().counterparty_commitment_txs_from_update(update)
}
/// Wrapper around [`EcdsaChannelSigner::sign_justice_revoked_output`] to make
/// signing the justice transaction easier for implementors of
/// [`chain::chainmonitor::Persist`]. On success this method returns the provided transaction
/// signing the input at `input_idx`. This method will only produce a valid signature for
/// a transaction spending the `to_local` output of a commitment transaction, i.e. this cannot
/// be used for revoked HTLC outputs.
///
/// `Value` is the value of the output being spent by the input at `input_idx`, committed
/// in the BIP 143 signature.
///
/// This method will only succeed if this monitor has received the revocation secret for the
/// provided `commitment_number`. If a commitment number is provided that does not correspond
/// to the commitment transaction being revoked, this will return a signed transaction, but
/// the signature will not be valid.
///
/// [`EcdsaChannelSigner::sign_justice_revoked_output`]: crate::sign::EcdsaChannelSigner::sign_justice_revoked_output
/// [`Persist`]: crate::chain::chainmonitor::Persist
pub fn sign_to_local_justice_tx(&self, justice_tx: Transaction, input_idx: usize, value: u64, commitment_number: u64) -> Result<Transaction, ()> {
self.inner.lock().unwrap().sign_to_local_justice_tx(justice_tx, input_idx, value, commitment_number)
}
pub(crate) fn get_min_seen_secret(&self) -> u64 {
self.inner.lock().unwrap().get_min_seen_secret()
}
@ -2226,6 +2324,25 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
Ok(())
}
pub(crate) fn provide_initial_counterparty_commitment_tx<L: Deref>(
&mut self, txid: Txid, htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>,
commitment_number: u64, their_per_commitment_point: PublicKey, feerate_per_kw: u32,
to_broadcaster_value: u64, to_countersignatory_value: u64, logger: &L
)
where L::Target: Logger
{
self.initial_counterparty_commitment_info = Some((their_per_commitment_point.clone(),
feerate_per_kw, to_broadcaster_value, to_countersignatory_value));
#[cfg(debug_assertions)] {
let rebuilt_commitment_tx = self.initial_counterparty_commitment_tx().unwrap();
debug_assert_eq!(rebuilt_commitment_tx.trust().txid(), txid);
}
self.provide_latest_counterparty_commitment_tx(txid, htlc_outputs, commitment_number,
their_per_commitment_point, logger);
}
pub(crate) fn provide_latest_counterparty_commitment_tx<L: Deref>(&mut self, txid: Txid, htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>, commitment_number: u64, their_per_commitment_point: PublicKey, logger: &L) where L::Target: Logger {
// TODO: Encrypt the htlc_outputs data with the single-hash of the commitment transaction
// so that a remote monitor doesn't learn anything unless there is a malicious close.
@ -2471,7 +2588,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
ret = Err(());
}
}
ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { commitment_txid, htlc_outputs, commitment_number, their_per_commitment_point } => {
ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { commitment_txid, htlc_outputs, commitment_number, their_per_commitment_point, .. } => {
log_trace!(logger, "Updating ChannelMonitor with latest counterparty commitment transaction info");
self.provide_latest_counterparty_commitment_tx(*commitment_txid, htlc_outputs.clone(), *commitment_number, *their_per_commitment_point, logger)
},
@ -2543,6 +2660,10 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
}
#[cfg(debug_assertions)] {
self.counterparty_commitment_txs_from_update(updates);
}
// If the updates succeeded and we were in an already closed channel state, then there's no
// need to refuse any updates we expect to receive afer seeing a confirmed commitment.
if ret.is_ok() && updates.update_id == CLOSED_CHANNEL_UPDATE_ID && self.latest_update_id == updates.update_id {
@ -2651,6 +2772,91 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
ret
}
pub(crate) fn initial_counterparty_commitment_tx(&mut self) -> Option<CommitmentTransaction> {
let (their_per_commitment_point, feerate_per_kw, to_broadcaster_value,
to_countersignatory_value) = self.initial_counterparty_commitment_info?;
let htlc_outputs = vec![];
let commitment_tx = self.build_counterparty_commitment_tx(INITIAL_COMMITMENT_NUMBER,
&their_per_commitment_point, to_broadcaster_value, to_countersignatory_value,
feerate_per_kw, htlc_outputs);
Some(commitment_tx)
}
fn build_counterparty_commitment_tx(
&self, commitment_number: u64, their_per_commitment_point: &PublicKey,
to_broadcaster_value: u64, to_countersignatory_value: u64, feerate_per_kw: u32,
mut nondust_htlcs: Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>
) -> CommitmentTransaction {
let broadcaster_keys = &self.onchain_tx_handler.channel_transaction_parameters
.counterparty_parameters.as_ref().unwrap().pubkeys;
let countersignatory_keys =
&self.onchain_tx_handler.channel_transaction_parameters.holder_pubkeys;
let broadcaster_funding_key = broadcaster_keys.funding_pubkey;
let countersignatory_funding_key = countersignatory_keys.funding_pubkey;
let keys = TxCreationKeys::from_channel_static_keys(&their_per_commitment_point,
&broadcaster_keys, &countersignatory_keys, &self.onchain_tx_handler.secp_ctx);
let channel_parameters =
&self.onchain_tx_handler.channel_transaction_parameters.as_counterparty_broadcastable();
CommitmentTransaction::new_with_auxiliary_htlc_data(commitment_number,
to_broadcaster_value, to_countersignatory_value, broadcaster_funding_key,
countersignatory_funding_key, keys, feerate_per_kw, &mut nondust_htlcs,
channel_parameters)
}
pub(crate) fn counterparty_commitment_txs_from_update(&self, update: &ChannelMonitorUpdate) -> Vec<CommitmentTransaction> {
update.updates.iter().filter_map(|update| {
match update {
&ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { commitment_txid,
ref htlc_outputs, commitment_number, their_per_commitment_point,
feerate_per_kw: Some(feerate_per_kw),
to_broadcaster_value_sat: Some(to_broadcaster_value),
to_countersignatory_value_sat: Some(to_countersignatory_value) } => {
let nondust_htlcs = htlc_outputs.iter().filter_map(|(htlc, _)| {
htlc.transaction_output_index.map(|_| (htlc.clone(), None))
}).collect::<Vec<_>>();
let commitment_tx = self.build_counterparty_commitment_tx(commitment_number,
&their_per_commitment_point, to_broadcaster_value,
to_countersignatory_value, feerate_per_kw, nondust_htlcs);
debug_assert_eq!(commitment_tx.trust().txid(), commitment_txid);
Some(commitment_tx)
},
_ => None,
}
}).collect()
}
pub(crate) fn sign_to_local_justice_tx(
&self, mut justice_tx: Transaction, input_idx: usize, value: u64, commitment_number: u64
) -> Result<Transaction, ()> {
let secret = self.get_secret(commitment_number).ok_or(())?;
let per_commitment_key = SecretKey::from_slice(&secret).map_err(|_| ())?;
let their_per_commitment_point = PublicKey::from_secret_key(
&self.onchain_tx_handler.secp_ctx, &per_commitment_key);
let revocation_pubkey = chan_utils::derive_public_revocation_key(
&self.onchain_tx_handler.secp_ctx, &their_per_commitment_point,
&self.holder_revocation_basepoint);
let delayed_key = chan_utils::derive_public_key(&self.onchain_tx_handler.secp_ctx,
&their_per_commitment_point,
&self.counterparty_commitment_params.counterparty_delayed_payment_base_key);
let revokeable_redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey,
self.counterparty_commitment_params.on_counterparty_tx_csv, &delayed_key);
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(&[1u8]);
justice_tx.input[input_idx].witness.push(revokeable_redeemscript.as_bytes());
Ok(justice_tx)
}
/// Can only fail if idx is < get_min_seen_secret
fn get_secret(&self, idx: u64) -> Option<[u8; 32]> {
self.commitment_secrets.get_secret(idx)
@ -4113,6 +4319,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
let mut confirmed_commitment_tx_counterparty_output = None;
let mut spendable_txids_confirmed = Some(Vec::new());
let mut counterparty_fulfilled_htlcs = Some(HashMap::new());
let mut initial_counterparty_commitment_info = None;
read_tlv_fields!(reader, {
(1, funding_spend_confirmed, option),
(3, htlcs_resolved_on_chain, optional_vec),
@ -4122,6 +4329,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
(11, confirmed_commitment_tx_counterparty_output, option),
(13, spendable_txids_confirmed, optional_vec),
(15, counterparty_fulfilled_htlcs, option),
(17, initial_counterparty_commitment_info, option),
});
Ok((best_block.block_hash(), ChannelMonitor::from_impl(ChannelMonitorImpl {
@ -4177,6 +4385,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
best_block,
counterparty_node_id,
initial_counterparty_commitment_info,
})))
}
}

View file

@ -21,6 +21,8 @@ use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::ripemd160::Hash as Ripemd160;
use bitcoin::hash_types::{Txid, PubkeyHash};
use crate::chain::chaininterface::fee_for_weight;
use crate::chain::package::WEIGHT_REVOKED_OUTPUT;
use crate::sign::EntropySource;
use crate::ln::{PaymentHash, PaymentPreimage};
use crate::ln::msgs::DecodeError;
@ -1308,6 +1310,7 @@ pub struct CommitmentTransaction {
commitment_number: u64,
to_broadcaster_value_sat: u64,
to_countersignatory_value_sat: u64,
to_broadcaster_delay: Option<u16>, // Added in 0.0.117
feerate_per_kw: u32,
htlcs: Vec<HTLCOutputInCommitment>,
// Note that on upgrades, some features of existing outputs may be missed.
@ -1341,6 +1344,7 @@ impl Writeable for CommitmentTransaction {
let legacy_deserialization_prevention_marker = legacy_deserialization_prevention_marker_for_channel_type_features(&self.channel_type_features);
write_tlv_fields!(writer, {
(0, self.commitment_number, required),
(1, self.to_broadcaster_delay, option),
(2, self.to_broadcaster_value_sat, required),
(4, self.to_countersignatory_value_sat, required),
(6, self.feerate_per_kw, required),
@ -1358,6 +1362,7 @@ impl Readable for CommitmentTransaction {
fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
_init_and_read_len_prefixed_tlv_fields!(reader, {
(0, commitment_number, required),
(1, to_broadcaster_delay, option),
(2, to_broadcaster_value_sat, required),
(4, to_countersignatory_value_sat, required),
(6, feerate_per_kw, required),
@ -1376,6 +1381,7 @@ impl Readable for CommitmentTransaction {
commitment_number: commitment_number.0.unwrap(),
to_broadcaster_value_sat: to_broadcaster_value_sat.0.unwrap(),
to_countersignatory_value_sat: to_countersignatory_value_sat.0.unwrap(),
to_broadcaster_delay,
feerate_per_kw: feerate_per_kw.0.unwrap(),
keys: keys.0.unwrap(),
built: built.0.unwrap(),
@ -1407,6 +1413,7 @@ impl CommitmentTransaction {
commitment_number,
to_broadcaster_value_sat,
to_countersignatory_value_sat,
to_broadcaster_delay: Some(channel_parameters.contest_delay()),
feerate_per_kw,
htlcs,
channel_type_features: channel_parameters.channel_type_features().clone(),
@ -1724,6 +1731,69 @@ impl<'a> TrustedCommitmentTransaction<'a> {
);
htlc_tx
}
/// Returns the index of the revokeable output, i.e. the `to_local` output sending funds to
/// the broadcaster, in the built transaction, if any exists.
///
/// There are two cases where this may return `None`:
/// - The balance of the revokeable output is below the dust limit (only found on commitments
/// early in the channel's lifetime, i.e. before the channel reserve is met).
/// - This commitment was created before LDK 0.0.117. In this case, the
/// commitment transaction previously didn't contain enough information to locate the
/// revokeable output.
pub fn revokeable_output_index(&self) -> Option<usize> {
let revokeable_redeemscript = get_revokeable_redeemscript(
&self.keys.revocation_key,
self.to_broadcaster_delay?,
&self.keys.broadcaster_delayed_payment_key,
);
let revokeable_p2wsh = revokeable_redeemscript.to_v0_p2wsh();
let outputs = &self.inner.built.transaction.output;
outputs.iter().enumerate()
.find(|(_, out)| out.script_pubkey == revokeable_p2wsh)
.map(|(idx, _)| idx)
}
/// Helper method to build an unsigned justice transaction spending the revokeable
/// `to_local` output to a destination script. Fee estimation accounts for the expected
/// revocation witness data that will be added when signed.
///
/// This method will error if the given fee rate results in a fee greater than the value
/// of the output being spent, or if there exists no revokeable `to_local` output on this
/// commitment transaction. See [`Self::revokeable_output_index`] for more details.
///
/// The built transaction will allow fee bumping with RBF, and this method takes
/// `feerate_per_kw` as an input such that multiple copies of a justice transaction at different
/// fee rates may be built.
pub fn build_to_local_justice_tx(&self, feerate_per_kw: u64, destination_script: Script)
-> Result<Transaction, ()> {
let output_idx = self.revokeable_output_index().ok_or(())?;
let input = vec![TxIn {
previous_output: OutPoint {
txid: self.trust().txid(),
vout: output_idx as u32,
},
script_sig: Script::new(),
sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
witness: Witness::new(),
}];
let value = self.inner.built.transaction.output[output_idx].value;
let output = vec![TxOut {
script_pubkey: destination_script,
value,
}];
let mut justice_tx = Transaction {
version: 2,
lock_time: PackedLockTime::ZERO,
input,
output,
};
let weight = justice_tx.weight() as u64 + WEIGHT_REVOKED_OUTPUT;
let fee = fee_for_weight(feerate_per_kw as u32, weight);
justice_tx.output[0].value = value.checked_sub(fee).ok_or(())?;
Ok(justice_tx)
}
}
/// Commitment transaction numbers which appear in the transactions themselves are XOR'd with a
@ -1758,14 +1828,14 @@ pub fn get_commitment_transaction_number_obscure_factor(
#[cfg(test)]
mod tests {
use super::CounterpartyCommitmentSecrets;
use super::{CounterpartyCommitmentSecrets, ChannelPublicKeys};
use crate::{hex, chain};
use crate::prelude::*;
use crate::ln::chan_utils::{get_htlc_redeemscript, get_to_countersignatory_with_anchors_redeemscript, CommitmentTransaction, TxCreationKeys, ChannelTransactionParameters, CounterpartyChannelTransactionParameters, HTLCOutputInCommitment};
use bitcoin::secp256k1::{PublicKey, SecretKey, Secp256k1};
use crate::util::test_utils;
use crate::sign::{ChannelSigner, SignerProvider};
use bitcoin::{Network, Txid};
use bitcoin::{Network, Txid, Script};
use bitcoin::hashes::Hash;
use crate::ln::PaymentHash;
use bitcoin::hashes::hex::ToHex;
@ -1773,74 +1843,86 @@ mod tests {
use bitcoin::PublicKey as BitcoinPublicKey;
use crate::ln::features::ChannelTypeFeatures;
struct TestCommitmentTxBuilder {
commitment_number: u64,
holder_funding_pubkey: PublicKey,
counterparty_funding_pubkey: PublicKey,
keys: TxCreationKeys,
feerate_per_kw: u32,
htlcs_with_aux: Vec<(HTLCOutputInCommitment, ())>,
channel_parameters: ChannelTransactionParameters,
counterparty_pubkeys: ChannelPublicKeys,
}
impl TestCommitmentTxBuilder {
fn new() -> Self {
let secp_ctx = Secp256k1::new();
let seed = [42; 32];
let network = Network::Testnet;
let keys_provider = test_utils::TestKeysInterface::new(&seed, network);
let signer = keys_provider.derive_channel_signer(3000, keys_provider.generate_channel_keys_id(false, 1_000_000, 0));
let counterparty_signer = keys_provider.derive_channel_signer(3000, keys_provider.generate_channel_keys_id(true, 1_000_000, 1));
let delayed_payment_base = &signer.pubkeys().delayed_payment_basepoint;
let per_commitment_secret = SecretKey::from_slice(&hex::decode("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100").unwrap()[..]).unwrap();
let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret);
let htlc_basepoint = &signer.pubkeys().htlc_basepoint;
let holder_pubkeys = signer.pubkeys();
let counterparty_pubkeys = counterparty_signer.pubkeys().clone();
let keys = TxCreationKeys::derive_new(&secp_ctx, &per_commitment_point, delayed_payment_base, htlc_basepoint, &counterparty_pubkeys.revocation_basepoint, &counterparty_pubkeys.htlc_basepoint);
let channel_parameters = ChannelTransactionParameters {
holder_pubkeys: holder_pubkeys.clone(),
holder_selected_contest_delay: 0,
is_outbound_from_holder: false,
counterparty_parameters: Some(CounterpartyChannelTransactionParameters { pubkeys: counterparty_pubkeys.clone(), selected_contest_delay: 0 }),
funding_outpoint: Some(chain::transaction::OutPoint { txid: Txid::all_zeros(), index: 0 }),
channel_type_features: ChannelTypeFeatures::only_static_remote_key(),
};
let htlcs_with_aux = Vec::new();
Self {
commitment_number: 0,
holder_funding_pubkey: holder_pubkeys.funding_pubkey,
counterparty_funding_pubkey: counterparty_pubkeys.funding_pubkey,
keys,
feerate_per_kw: 1,
htlcs_with_aux,
channel_parameters,
counterparty_pubkeys,
}
}
fn build(&mut self, to_broadcaster_sats: u64, to_countersignatory_sats: u64) -> CommitmentTransaction {
CommitmentTransaction::new_with_auxiliary_htlc_data(
self.commitment_number, to_broadcaster_sats, to_countersignatory_sats,
self.holder_funding_pubkey.clone(),
self.counterparty_funding_pubkey.clone(),
self.keys.clone(), self.feerate_per_kw,
&mut self.htlcs_with_aux, &self.channel_parameters.as_holder_broadcastable()
)
}
}
#[test]
fn test_anchors() {
let secp_ctx = Secp256k1::new();
let seed = [42; 32];
let network = Network::Testnet;
let keys_provider = test_utils::TestKeysInterface::new(&seed, network);
let signer = keys_provider.derive_channel_signer(3000, keys_provider.generate_channel_keys_id(false, 1_000_000, 0));
let counterparty_signer = keys_provider.derive_channel_signer(3000, keys_provider.generate_channel_keys_id(true, 1_000_000, 1));
let delayed_payment_base = &signer.pubkeys().delayed_payment_basepoint;
let per_commitment_secret = SecretKey::from_slice(&hex::decode("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100").unwrap()[..]).unwrap();
let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret);
let htlc_basepoint = &signer.pubkeys().htlc_basepoint;
let holder_pubkeys = signer.pubkeys();
let counterparty_pubkeys = counterparty_signer.pubkeys();
let keys = TxCreationKeys::derive_new(&secp_ctx, &per_commitment_point, delayed_payment_base, htlc_basepoint, &counterparty_pubkeys.revocation_basepoint, &counterparty_pubkeys.htlc_basepoint);
let mut channel_parameters = ChannelTransactionParameters {
holder_pubkeys: holder_pubkeys.clone(),
holder_selected_contest_delay: 0,
is_outbound_from_holder: false,
counterparty_parameters: Some(CounterpartyChannelTransactionParameters { pubkeys: counterparty_pubkeys.clone(), selected_contest_delay: 0 }),
funding_outpoint: Some(chain::transaction::OutPoint { txid: Txid::all_zeros(), index: 0 }),
channel_type_features: ChannelTypeFeatures::only_static_remote_key(),
};
let mut htlcs_with_aux: Vec<(_, ())> = Vec::new();
let mut builder = TestCommitmentTxBuilder::new();
// Generate broadcaster and counterparty outputs
let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(
0, 1000, 2000,
holder_pubkeys.funding_pubkey,
counterparty_pubkeys.funding_pubkey,
keys.clone(), 1,
&mut htlcs_with_aux, &channel_parameters.as_holder_broadcastable()
);
let tx = builder.build(1000, 2000);
assert_eq!(tx.built.transaction.output.len(), 2);
assert_eq!(tx.built.transaction.output[1].script_pubkey, Payload::p2wpkh(&BitcoinPublicKey::new(counterparty_pubkeys.payment_point)).unwrap().script_pubkey());
assert_eq!(tx.built.transaction.output[1].script_pubkey, Payload::p2wpkh(&BitcoinPublicKey::new(builder.counterparty_pubkeys.payment_point)).unwrap().script_pubkey());
// Generate broadcaster and counterparty outputs as well as two anchors
channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies();
let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(
0, 1000, 2000,
holder_pubkeys.funding_pubkey,
counterparty_pubkeys.funding_pubkey,
keys.clone(), 1,
&mut htlcs_with_aux, &channel_parameters.as_holder_broadcastable()
);
builder.channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies();
let tx = builder.build(1000, 2000);
assert_eq!(tx.built.transaction.output.len(), 4);
assert_eq!(tx.built.transaction.output[3].script_pubkey, get_to_countersignatory_with_anchors_redeemscript(&counterparty_pubkeys.payment_point).to_v0_p2wsh());
assert_eq!(tx.built.transaction.output[3].script_pubkey, get_to_countersignatory_with_anchors_redeemscript(&builder.counterparty_pubkeys.payment_point).to_v0_p2wsh());
// Generate broadcaster output and anchor
let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(
0, 3000, 0,
holder_pubkeys.funding_pubkey,
counterparty_pubkeys.funding_pubkey,
keys.clone(), 1,
&mut htlcs_with_aux, &channel_parameters.as_holder_broadcastable()
);
let tx = builder.build(3000, 0);
assert_eq!(tx.built.transaction.output.len(), 2);
// Generate counterparty output and anchor
let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(
0, 0, 3000,
holder_pubkeys.funding_pubkey,
counterparty_pubkeys.funding_pubkey,
keys.clone(), 1,
&mut htlcs_with_aux, &channel_parameters.as_holder_broadcastable()
);
let tx = builder.build(0, 3000);
assert_eq!(tx.built.transaction.output.len(), 2);
let received_htlc = HTLCOutputInCommitment {
@ -1860,15 +1942,10 @@ mod tests {
};
// Generate broadcaster output and received and offered HTLC outputs, w/o anchors
channel_parameters.channel_type_features = ChannelTypeFeatures::only_static_remote_key();
let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(
0, 3000, 0,
holder_pubkeys.funding_pubkey,
counterparty_pubkeys.funding_pubkey,
keys.clone(), 1,
&mut vec![(received_htlc.clone(), ()), (offered_htlc.clone(), ())],
&channel_parameters.as_holder_broadcastable()
);
builder.channel_parameters.channel_type_features = ChannelTypeFeatures::only_static_remote_key();
builder.htlcs_with_aux = vec![(received_htlc.clone(), ()), (offered_htlc.clone(), ())];
let tx = builder.build(3000, 0);
let keys = &builder.keys.clone();
assert_eq!(tx.built.transaction.output.len(), 3);
assert_eq!(tx.built.transaction.output[0].script_pubkey, get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_v0_p2wsh());
assert_eq!(tx.built.transaction.output[1].script_pubkey, get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_v0_p2wsh());
@ -1878,15 +1955,9 @@ mod tests {
"0020215d61bba56b19e9eadb6107f5a85d7f99c40f65992443f69229c290165bc00d");
// Generate broadcaster output and received and offered HTLC outputs, with anchors
channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies();
let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(
0, 3000, 0,
holder_pubkeys.funding_pubkey,
counterparty_pubkeys.funding_pubkey,
keys.clone(), 1,
&mut vec![(received_htlc.clone(), ()), (offered_htlc.clone(), ())],
&channel_parameters.as_holder_broadcastable()
);
builder.channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies();
builder.htlcs_with_aux = vec![(received_htlc.clone(), ()), (offered_htlc.clone(), ())];
let tx = builder.build(3000, 0);
assert_eq!(tx.built.transaction.output.len(), 5);
assert_eq!(tx.built.transaction.output[2].script_pubkey, get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(), &keys).to_v0_p2wsh());
assert_eq!(tx.built.transaction.output[3].script_pubkey, get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(), &keys).to_v0_p2wsh());
@ -1896,6 +1967,61 @@ mod tests {
"002087a3faeb1950a469c0e2db4a79b093a41b9526e5a6fc6ef5cb949bde3be379c7");
}
#[test]
fn test_finding_revokeable_output_index() {
let mut builder = TestCommitmentTxBuilder::new();
// Revokeable output present
let tx = builder.build(1000, 2000);
assert_eq!(tx.built.transaction.output.len(), 2);
assert_eq!(tx.trust().revokeable_output_index(), Some(0));
// Revokeable output present (but to_broadcaster_delay missing)
let tx = CommitmentTransaction { to_broadcaster_delay: None, ..tx };
assert_eq!(tx.built.transaction.output.len(), 2);
assert_eq!(tx.trust().revokeable_output_index(), None);
// Revokeable output not present (our balance is dust)
let tx = builder.build(0, 2000);
assert_eq!(tx.built.transaction.output.len(), 1);
assert_eq!(tx.trust().revokeable_output_index(), None);
}
#[test]
fn test_building_to_local_justice_tx() {
let mut builder = TestCommitmentTxBuilder::new();
// Revokeable output not present (our balance is dust)
let tx = builder.build(0, 2000);
assert_eq!(tx.built.transaction.output.len(), 1);
assert!(tx.trust().build_to_local_justice_tx(253, Script::new()).is_err());
// Revokeable output present
let tx = builder.build(1000, 2000);
assert_eq!(tx.built.transaction.output.len(), 2);
// Too high feerate
assert!(tx.trust().build_to_local_justice_tx(100_000, Script::new()).is_err());
// Generate a random public key for destination script
let secret_key = SecretKey::from_slice(
&hex::decode("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100")
.unwrap()[..]).unwrap();
let pubkey_hash = BitcoinPublicKey::new(
PublicKey::from_secret_key(&Secp256k1::new(), &secret_key)).wpubkey_hash().unwrap();
let destination_script = Script::new_v0_p2wpkh(&pubkey_hash);
let justice_tx = tx.trust().build_to_local_justice_tx(253, destination_script.clone()).unwrap();
assert_eq!(justice_tx.input.len(), 1);
assert_eq!(justice_tx.input[0].previous_output.txid, tx.built.transaction.txid());
assert_eq!(justice_tx.input[0].previous_output.vout, tx.trust().revokeable_output_index().unwrap() as u32);
assert!(justice_tx.input[0].sequence.is_rbf());
assert_eq!(justice_tx.output.len(), 1);
assert!(justice_tx.output[0].value < 1000);
assert_eq!(justice_tx.output[0].script_pubkey, destination_script);
}
#[test]
fn test_per_commitment_storage() {
// Test vectors from BOLT 3:

View file

@ -2534,7 +2534,13 @@ impl<SP: Deref> Channel<SP> where
obscure_factor,
holder_commitment_tx, best_block, self.context.counterparty_node_id);
channel_monitor.provide_latest_counterparty_commitment_tx(counterparty_initial_bitcoin_tx.txid, Vec::new(), self.context.cur_counterparty_commitment_transaction_number, self.context.counterparty_cur_commitment_point.unwrap(), logger);
channel_monitor.provide_initial_counterparty_commitment_tx(
counterparty_initial_bitcoin_tx.txid, Vec::new(),
self.context.cur_counterparty_commitment_transaction_number,
self.context.counterparty_cur_commitment_point.unwrap(),
counterparty_initial_commitment_tx.feerate_per_kw(),
counterparty_initial_commitment_tx.to_broadcaster_value_sat(),
counterparty_initial_commitment_tx.to_countersignatory_value_sat(), logger);
assert_eq!(self.context.channel_state & (ChannelState::MonitorUpdateInProgress as u32), 0); // We have no had any monitor(s) yet to fail update!
self.context.channel_state = ChannelState::FundingSent as u32;
@ -5289,7 +5295,9 @@ impl<SP: Deref> Channel<SP> where
}
self.context.resend_order = RAACommitmentOrder::RevokeAndACKFirst;
let (counterparty_commitment_txid, mut htlcs_ref) = self.build_commitment_no_state_update(logger);
let (mut htlcs_ref, counterparty_commitment_tx) =
self.build_commitment_no_state_update(logger);
let counterparty_commitment_txid = counterparty_commitment_tx.trust().txid();
let htlcs: Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)> =
htlcs_ref.drain(..).map(|(htlc, htlc_source)| (htlc, htlc_source.map(|source_ref| Box::new(source_ref.clone())))).collect();
@ -5304,17 +5312,23 @@ impl<SP: Deref> Channel<SP> where
commitment_txid: counterparty_commitment_txid,
htlc_outputs: htlcs.clone(),
commitment_number: self.context.cur_counterparty_commitment_transaction_number,
their_per_commitment_point: self.context.counterparty_cur_commitment_point.unwrap()
their_per_commitment_point: self.context.counterparty_cur_commitment_point.unwrap(),
feerate_per_kw: Some(counterparty_commitment_tx.feerate_per_kw()),
to_broadcaster_value_sat: Some(counterparty_commitment_tx.to_broadcaster_value_sat()),
to_countersignatory_value_sat: Some(counterparty_commitment_tx.to_countersignatory_value_sat()),
}]
};
self.context.channel_state |= ChannelState::AwaitingRemoteRevoke as u32;
monitor_update
}
fn build_commitment_no_state_update<L: Deref>(&self, logger: &L) -> (Txid, Vec<(HTLCOutputInCommitment, Option<&HTLCSource>)>) where L::Target: Logger {
fn build_commitment_no_state_update<L: Deref>(&self, logger: &L)
-> (Vec<(HTLCOutputInCommitment, Option<&HTLCSource>)>, CommitmentTransaction)
where L::Target: Logger
{
let counterparty_keys = self.context.build_remote_transaction_keys();
let commitment_stats = self.context.build_commitment_transaction(self.context.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, true, logger);
let counterparty_commitment_txid = commitment_stats.tx.trust().txid();
let counterparty_commitment_tx = commitment_stats.tx;
#[cfg(any(test, fuzzing))]
{
@ -5334,7 +5348,7 @@ impl<SP: Deref> Channel<SP> where
}
}
(counterparty_commitment_txid, commitment_stats.htlcs_included)
(commitment_stats.htlcs_included, counterparty_commitment_tx)
}
/// Only fails in case of signer rejection. Used for channel_reestablish commitment_signed
@ -6456,7 +6470,7 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
self.generate_accept_channel_message()
}
fn funding_created_signature<L: Deref>(&mut self, sig: &Signature, logger: &L) -> Result<(Txid, CommitmentTransaction, Signature), ChannelError> where L::Target: Logger {
fn funding_created_signature<L: Deref>(&mut self, sig: &Signature, logger: &L) -> Result<(CommitmentTransaction, CommitmentTransaction, Signature), ChannelError> where L::Target: Logger {
let funding_script = self.context.get_funding_redeemscript();
let keys = self.context.build_holder_transaction_keys(self.context.cur_holder_commitment_transaction_number);
@ -6488,7 +6502,7 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
.map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?.0;
// We sign "counterparty" commitment transaction, allowing them to broadcast the tx if they wish.
Ok((counterparty_initial_bitcoin_tx.txid, initial_commitment_tx, counterparty_signature))
Ok((counterparty_initial_commitment_tx, initial_commitment_tx, counterparty_signature))
}
}
}
@ -6520,7 +6534,7 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
// funding_created_signature may fail.
self.context.holder_signer.as_mut().provide_channel_parameters(&self.context.channel_transaction_parameters);
let (counterparty_initial_commitment_txid, initial_commitment_tx, signature) = match self.funding_created_signature(&msg.signature, logger) {
let (counterparty_initial_commitment_tx, initial_commitment_tx, signature) = match self.funding_created_signature(&msg.signature, logger) {
Ok(res) => res,
Err(ChannelError::Close(e)) => {
self.context.channel_transaction_parameters.funding_outpoint = None;
@ -6561,7 +6575,12 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
obscure_factor,
holder_commitment_tx, best_block, self.context.counterparty_node_id);
channel_monitor.provide_latest_counterparty_commitment_tx(counterparty_initial_commitment_txid, Vec::new(), self.context.cur_counterparty_commitment_transaction_number, self.context.counterparty_cur_commitment_point.unwrap(), logger);
channel_monitor.provide_initial_counterparty_commitment_tx(
counterparty_initial_commitment_tx.trust().txid(), Vec::new(),
self.context.cur_counterparty_commitment_transaction_number,
self.context.counterparty_cur_commitment_point.unwrap(), self.context.feerate_per_kw,
counterparty_initial_commitment_tx.to_broadcaster_value_sat(),
counterparty_initial_commitment_tx.to_countersignatory_value_sat(), logger);
self.context.channel_state = ChannelState::FundingSent as u32;
self.context.channel_id = funding_txo.to_channel_id();

View file

@ -10,7 +10,7 @@
//! A bunch of useful utilities for building networks of nodes and exchanging messages between
//! nodes for functional tests.
use crate::chain::{BestBlock, ChannelMonitorUpdateStatus, Confirm, Listen, Watch};
use crate::chain::{BestBlock, ChannelMonitorUpdateStatus, Confirm, Listen, Watch, chainmonitor::Persist};
use crate::sign::EntropySource;
use crate::chain::channelmonitor::ChannelMonitor;
use crate::chain::transaction::OutPoint;
@ -2643,10 +2643,14 @@ pub fn create_chanmon_cfgs(node_count: usize) -> Vec<TestChanMonCfg> {
}
pub fn create_node_cfgs<'a>(node_count: usize, chanmon_cfgs: &'a Vec<TestChanMonCfg>) -> Vec<NodeCfg<'a>> {
create_node_cfgs_with_persisters(node_count, chanmon_cfgs, chanmon_cfgs.iter().map(|c| &c.persister).collect())
}
pub fn create_node_cfgs_with_persisters<'a>(node_count: usize, chanmon_cfgs: &'a Vec<TestChanMonCfg>, persisters: Vec<&'a impl Persist<EnforcingSigner>>) -> Vec<NodeCfg<'a>> {
let mut nodes = Vec::new();
for i in 0..node_count {
let chain_monitor = test_utils::TestChainMonitor::new(Some(&chanmon_cfgs[i].chain_source), &chanmon_cfgs[i].tx_broadcaster, &chanmon_cfgs[i].logger, &chanmon_cfgs[i].fee_estimator, &chanmon_cfgs[i].persister, &chanmon_cfgs[i].keys_manager);
let chain_monitor = test_utils::TestChainMonitor::new(Some(&chanmon_cfgs[i].chain_source), &chanmon_cfgs[i].tx_broadcaster, &chanmon_cfgs[i].logger, &chanmon_cfgs[i].fee_estimator, persisters[i], &chanmon_cfgs[i].keys_manager);
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &chanmon_cfgs[i].logger));
let seed = [i as u8; 32];
nodes.push(NodeCfg {

View file

@ -17,7 +17,7 @@ use crate::chain::chaininterface::LowerBoundedFeeEstimator;
use crate::chain::channelmonitor;
use crate::chain::channelmonitor::{CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ANTI_REORG_DELAY};
use crate::chain::transaction::OutPoint;
use crate::sign::{EcdsaChannelSigner, EntropySource};
use crate::sign::{ChannelSigner, EcdsaChannelSigner, EntropySource, SignerProvider};
use crate::events::{Event, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentPurpose, ClosureReason, HTLCDestination, PaymentFailureReason};
use crate::ln::{PaymentPreimage, PaymentSecret, PaymentHash};
use crate::ln::channel::{commitment_tx_base_weight, COMMITMENT_TX_WEIGHT_PER_HTLC, CONCURRENT_INBOUND_HTLC_FEE_BUFFER, FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE, MIN_AFFORDABLE_HTLC_COUNT, get_holder_selected_channel_reserve_satoshis, OutboundV1Channel, InboundV1Channel};
@ -31,7 +31,7 @@ use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, NodeFeatures};
use crate::ln::msgs;
use crate::ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, ErrorAction};
use crate::util::enforcing_trait_impls::EnforcingSigner;
use crate::util::test_utils;
use crate::util::test_utils::{self, WatchtowerPersister};
use crate::util::errors::APIError;
use crate::util::ser::{Writeable, ReadableArgs};
use crate::util::string::UntrustedString;
@ -2554,6 +2554,72 @@ fn revoked_output_claim() {
check_closed_event!(nodes[0], 1, ClosureReason::CommitmentTxConfirmed, [nodes[1].node.get_our_node_id()], 100000);
}
#[test]
fn test_forming_justice_tx_from_monitor_updates() {
do_test_forming_justice_tx_from_monitor_updates(true);
do_test_forming_justice_tx_from_monitor_updates(false);
}
fn do_test_forming_justice_tx_from_monitor_updates(broadcast_initial_commitment: bool) {
// Simple test to make sure that the justice tx formed in WatchtowerPersister
// is properly formed and can be broadcasted/confirmed successfully in the event
// that a revoked commitment transaction is broadcasted
// (Similar to `revoked_output_claim` test but we get the justice tx + broadcast manually)
let chanmon_cfgs = create_chanmon_cfgs(2);
let destination_script0 = chanmon_cfgs[0].keys_manager.get_destination_script().unwrap();
let destination_script1 = chanmon_cfgs[1].keys_manager.get_destination_script().unwrap();
let persisters = vec![WatchtowerPersister::new(destination_script0),
WatchtowerPersister::new(destination_script1)];
let node_cfgs = create_node_cfgs_with_persisters(2, &chanmon_cfgs, persisters.iter().collect());
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
let (_, _, channel_id, funding_tx) = create_announced_chan_between_nodes(&nodes, 0, 1);
let funding_txo = OutPoint { txid: funding_tx.txid(), index: 0 };
if !broadcast_initial_commitment {
// Send a payment to move the channel forward
send_payment(&nodes[0], &vec!(&nodes[1])[..], 5_000_000);
}
// node[0] is gonna to revoke an old state thus node[1] should be able to claim the revoked output.
// We'll keep this commitment transaction to broadcast once it's revoked.
let revoked_local_txn = get_local_commitment_txn!(nodes[0], channel_id);
assert_eq!(revoked_local_txn.len(), 1);
let revoked_commitment_tx = &revoked_local_txn[0];
// Send another payment, now revoking the previous commitment tx
send_payment(&nodes[0], &vec!(&nodes[1])[..], 5_000_000);
let justice_tx = persisters[1].justice_tx(funding_txo, &revoked_commitment_tx.txid()).unwrap();
check_spends!(justice_tx, revoked_commitment_tx);
mine_transactions(&nodes[1], &[revoked_commitment_tx, &justice_tx]);
mine_transactions(&nodes[0], &[revoked_commitment_tx, &justice_tx]);
check_added_monitors!(nodes[1], 1);
check_closed_event(&nodes[1], 1, ClosureReason::CommitmentTxConfirmed, false,
&[nodes[0].node.get_our_node_id()], 100_000);
get_announce_close_broadcast_events(&nodes, 1, 0);
check_added_monitors!(nodes[0], 1);
check_closed_event(&nodes[0], 1, ClosureReason::CommitmentTxConfirmed, false,
&[nodes[1].node.get_our_node_id()], 100_000);
// Check that the justice tx has sent the revoked output value to nodes[1]
let monitor = get_monitor!(nodes[1], channel_id);
let total_claimable_balance = monitor.get_claimable_balances().iter().fold(0, |sum, balance| {
match balance {
channelmonitor::Balance::ClaimableAwaitingConfirmations { amount_satoshis, .. } => sum + amount_satoshis,
_ => panic!("Unexpected balance type"),
}
});
// On the first commitment, node[1]'s balance was below dust so it didn't have an output
let node1_channel_balance = if broadcast_initial_commitment { 0 } else { revoked_commitment_tx.output[0].value };
let expected_claimable_balance = node1_channel_balance + justice_tx.output[0].value;
assert_eq!(total_claimable_balance, expected_claimable_balance);
}
#[test]
fn claim_htlc_outputs_shared_tx() {
// Node revoked old state, htlcs haven't time out yet, claim them in shared justice tx

View file

@ -11,6 +11,7 @@ use crate::chain;
use crate::chain::WatchedOutput;
use crate::chain::chaininterface;
use crate::chain::chaininterface::ConfirmationTarget;
use crate::chain::chaininterface::FEERATE_FLOOR_SATS_PER_KW;
use crate::chain::chainmonitor;
use crate::chain::chainmonitor::MonitorUpdateId;
use crate::chain::channelmonitor;
@ -20,6 +21,7 @@ use crate::sign;
use crate::events;
use crate::events::bump_transaction::{WalletSource, Utxo};
use crate::ln::channelmanager;
use crate::ln::chan_utils::CommitmentTransaction;
use crate::ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
use crate::ln::{msgs, wire};
use crate::ln::msgs::LightningError;
@ -272,6 +274,108 @@ impl<'a> chain::Watch<EnforcingSigner> for TestChainMonitor<'a> {
}
}
struct JusticeTxData {
justice_tx: Transaction,
value: u64,
commitment_number: u64,
}
pub(crate) struct WatchtowerPersister {
persister: TestPersister,
/// Upon a new commitment_signed, we'll get a
/// ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTxInfo. We'll store the justice tx
/// amount, and commitment number so we can build the justice tx after our counterparty
/// revokes it.
unsigned_justice_tx_data: Mutex<HashMap<OutPoint, VecDeque<JusticeTxData>>>,
/// After receiving a revoke_and_ack for a commitment number, we'll form and store the justice
/// tx which would be used to provide a watchtower with the data it needs.
watchtower_state: Mutex<HashMap<OutPoint, HashMap<Txid, Transaction>>>,
destination_script: Script,
}
impl WatchtowerPersister {
pub(crate) fn new(destination_script: Script) -> Self {
WatchtowerPersister {
persister: TestPersister::new(),
unsigned_justice_tx_data: Mutex::new(HashMap::new()),
watchtower_state: Mutex::new(HashMap::new()),
destination_script,
}
}
pub(crate) fn justice_tx(&self, funding_txo: OutPoint, commitment_txid: &Txid)
-> Option<Transaction> {
self.watchtower_state.lock().unwrap().get(&funding_txo).unwrap().get(commitment_txid).cloned()
}
fn form_justice_data_from_commitment(&self, counterparty_commitment_tx: &CommitmentTransaction)
-> Option<JusticeTxData> {
let trusted_tx = counterparty_commitment_tx.trust();
let output_idx = trusted_tx.revokeable_output_index()?;
let built_tx = trusted_tx.built_transaction();
let value = built_tx.transaction.output[output_idx as usize].value;
let justice_tx = trusted_tx.build_to_local_justice_tx(
FEERATE_FLOOR_SATS_PER_KW as u64, self.destination_script.clone()).ok()?;
let commitment_number = counterparty_commitment_tx.commitment_number();
Some(JusticeTxData { justice_tx, value, commitment_number })
}
}
impl<Signer: sign::WriteableEcdsaChannelSigner> chainmonitor::Persist<Signer> for WatchtowerPersister {
fn persist_new_channel(&self, funding_txo: OutPoint,
data: &channelmonitor::ChannelMonitor<Signer>, id: MonitorUpdateId
) -> chain::ChannelMonitorUpdateStatus {
let res = self.persister.persist_new_channel(funding_txo, data, id);
assert!(self.unsigned_justice_tx_data.lock().unwrap()
.insert(funding_txo, VecDeque::new()).is_none());
assert!(self.watchtower_state.lock().unwrap()
.insert(funding_txo, HashMap::new()).is_none());
let initial_counterparty_commitment_tx = data.initial_counterparty_commitment_tx()
.expect("First and only call expects Some");
if let Some(justice_data)
= self.form_justice_data_from_commitment(&initial_counterparty_commitment_tx) {
self.unsigned_justice_tx_data.lock().unwrap()
.get_mut(&funding_txo).unwrap()
.push_back(justice_data);
}
res
}
fn update_persisted_channel(
&self, funding_txo: OutPoint, update: Option<&channelmonitor::ChannelMonitorUpdate>,
data: &channelmonitor::ChannelMonitor<Signer>, update_id: MonitorUpdateId
) -> chain::ChannelMonitorUpdateStatus {
let res = self.persister.update_persisted_channel(funding_txo, update, data, update_id);
if let Some(update) = update {
let commitment_txs = data.counterparty_commitment_txs_from_update(update);
let justice_datas = commitment_txs.into_iter()
.filter_map(|commitment_tx| self.form_justice_data_from_commitment(&commitment_tx));
let mut channels_justice_txs = self.unsigned_justice_tx_data.lock().unwrap();
let channel_state = channels_justice_txs.get_mut(&funding_txo).unwrap();
channel_state.extend(justice_datas);
while let Some(JusticeTxData { justice_tx, value, commitment_number }) = channel_state.front() {
let input_idx = 0;
let commitment_txid = justice_tx.input[input_idx].previous_output.txid;
match data.sign_to_local_justice_tx(justice_tx.clone(), input_idx, *value, *commitment_number) {
Ok(signed_justice_tx) => {
let dup = self.watchtower_state.lock().unwrap()
.get_mut(&funding_txo).unwrap()
.insert(commitment_txid, signed_justice_tx);
assert!(dup.is_none());
channel_state.pop_front();
},
Err(_) => break,
}
}
}
res
}
}
pub struct TestPersister {
/// The queue of update statuses we'll return. If none are queued, ::Completed will always be
/// returned.