Skip to content

Commit 04475c8

Browse files
committed
Expose revokeable output index and building a justice tx from commitment
For watchtowers to be able to build justice transactions for our counterparty's revoked commitments, they need to be able to find the revokeable output for them to sweep. Here we cache `to_self_delay` in `CommitmentTransaction` to allow for finding this output on the struct directly. We also add a simple helper method to aid in building the initial spending transaction. This also adds a unit test for both of these helpers, and refactors a bit of a previous `CommitmentTransaction` unit test to make adding these easier.
1 parent 75c0586 commit 04475c8

File tree

1 file changed

+201
-75
lines changed

1 file changed

+201
-75
lines changed

lightning/src/ln/chan_utils.rs

Lines changed: 201 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ use bitcoin::hashes::sha256::Hash as Sha256;
2121
use bitcoin::hashes::ripemd160::Hash as Ripemd160;
2222
use bitcoin::hash_types::{Txid, PubkeyHash};
2323

24+
use crate::chain::chaininterface::fee_for_weight;
25+
use crate::chain::package::WEIGHT_REVOKED_OUTPUT;
2426
use crate::sign::EntropySource;
2527
use crate::ln::{PaymentHash, PaymentPreimage};
2628
use crate::ln::msgs::DecodeError;
@@ -1308,6 +1310,7 @@ pub struct CommitmentTransaction {
13081310
commitment_number: u64,
13091311
to_broadcaster_value_sat: u64,
13101312
to_countersignatory_value_sat: u64,
1313+
to_broadcaster_delay: Option<u16>, // Added in 0.0.117
13111314
feerate_per_kw: u32,
13121315
htlcs: Vec<HTLCOutputInCommitment>,
13131316
// Note that on upgrades, some features of existing outputs may be missed.
@@ -1341,6 +1344,7 @@ impl Writeable for CommitmentTransaction {
13411344
let legacy_deserialization_prevention_marker = legacy_deserialization_prevention_marker_for_channel_type_features(&self.channel_type_features);
13421345
write_tlv_fields!(writer, {
13431346
(0, self.commitment_number, required),
1347+
(1, self.to_broadcaster_delay, option),
13441348
(2, self.to_broadcaster_value_sat, required),
13451349
(4, self.to_countersignatory_value_sat, required),
13461350
(6, self.feerate_per_kw, required),
@@ -1358,6 +1362,7 @@ impl Readable for CommitmentTransaction {
13581362
fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
13591363
_init_and_read_tlv_fields!(reader, {
13601364
(0, commitment_number, required),
1365+
(1, to_broadcaster_delay, option),
13611366
(2, to_broadcaster_value_sat, required),
13621367
(4, to_countersignatory_value_sat, required),
13631368
(6, feerate_per_kw, required),
@@ -1376,6 +1381,7 @@ impl Readable for CommitmentTransaction {
13761381
commitment_number: commitment_number.0.unwrap(),
13771382
to_broadcaster_value_sat: to_broadcaster_value_sat.0.unwrap(),
13781383
to_countersignatory_value_sat: to_countersignatory_value_sat.0.unwrap(),
1384+
to_broadcaster_delay,
13791385
feerate_per_kw: feerate_per_kw.0.unwrap(),
13801386
keys: keys.0.unwrap(),
13811387
built: built.0.unwrap(),
@@ -1407,6 +1413,7 @@ impl CommitmentTransaction {
14071413
commitment_number,
14081414
to_broadcaster_value_sat,
14091415
to_countersignatory_value_sat,
1416+
to_broadcaster_delay: Some(channel_parameters.contest_delay()),
14101417
feerate_per_kw,
14111418
htlcs,
14121419
channel_type_features: channel_parameters.channel_type_features().clone(),
@@ -1724,6 +1731,69 @@ impl<'a> TrustedCommitmentTransaction<'a> {
17241731
);
17251732
htlc_tx
17261733
}
1734+
1735+
/// Returns the index of the revokeable output, i.e. the `to_local` output sending funds to
1736+
/// the broadcaster, in the built transaction, if any exists.
1737+
///
1738+
/// There are two cases where this may return `None`:
1739+
/// - The balance of the revokeable output is below the dust limit (only found on commitments
1740+
/// early in the channel's lifetime, i.e. before the channel reserve is met).
1741+
/// - This commitment was created before LDK 0.0.117. In this case, the
1742+
/// commitment transaction previously didn't contain enough information to locate the
1743+
/// revokeable output.
1744+
pub fn revokeable_output_index(&self) -> Option<usize> {
1745+
let revokeable_redeemscript = get_revokeable_redeemscript(
1746+
&self.keys.revocation_key,
1747+
self.to_broadcaster_delay?,
1748+
&self.keys.broadcaster_delayed_payment_key,
1749+
);
1750+
let revokeable_p2wsh = revokeable_redeemscript.to_v0_p2wsh();
1751+
let outputs = &self.inner.built.transaction.output;
1752+
outputs.iter().enumerate()
1753+
.find(|(_, out)| out.script_pubkey == revokeable_p2wsh)
1754+
.map(|(idx, _)| idx)
1755+
}
1756+
1757+
/// Helper method to build an unsigned justice transaction spending the revokeable
1758+
/// `to_local` output to a destination script. Fee estimation accounts for the expected
1759+
/// revocation witness data that will be added when signed.
1760+
///
1761+
/// This method will error if the given fee rate results in a fee greater than the value
1762+
/// of the output being spent, or if there exists no revokeable `to_local` output on this
1763+
/// commitment transaction. See [`Self::revokeable_output_index`] for more details.
1764+
///
1765+
/// The built transaction will allow fee bumping with RBF, and this method takes
1766+
/// `feerate_per_kw` as an input such that multiple copies of a justice transaction at different
1767+
/// fee rates may be built.
1768+
pub fn build_to_local_justice_tx(&self, feerate_per_kw: u64, destination_script: Script)
1769+
-> Result<Transaction, ()> {
1770+
let output_idx = self.revokeable_output_index().ok_or(())?;
1771+
let input = vec![TxIn {
1772+
previous_output: OutPoint {
1773+
txid: self.trust().txid(),
1774+
vout: output_idx as u32,
1775+
},
1776+
script_sig: Script::new(),
1777+
sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
1778+
witness: Witness::new(),
1779+
}];
1780+
let value = self.inner.built.transaction.output[output_idx].value;
1781+
let output = vec![TxOut {
1782+
script_pubkey: destination_script,
1783+
value,
1784+
}];
1785+
let mut justice_tx = Transaction {
1786+
version: 2,
1787+
lock_time: PackedLockTime::ZERO,
1788+
input,
1789+
output,
1790+
};
1791+
let weight = justice_tx.weight() as u64 + WEIGHT_REVOKED_OUTPUT;
1792+
let fee = fee_for_weight(feerate_per_kw as u32, weight);
1793+
justice_tx.output[0].value = value.checked_sub(fee).ok_or(())?;
1794+
Ok(justice_tx)
1795+
}
1796+
17271797
}
17281798

17291799
/// Commitment transaction numbers which appear in the transactions themselves are XOR'd with a
@@ -1758,89 +1828,101 @@ pub fn get_commitment_transaction_number_obscure_factor(
17581828

17591829
#[cfg(test)]
17601830
mod tests {
1761-
use super::CounterpartyCommitmentSecrets;
1831+
use super::{CounterpartyCommitmentSecrets, ChannelPublicKeys};
17621832
use crate::{hex, chain};
17631833
use crate::prelude::*;
17641834
use crate::ln::chan_utils::{get_htlc_redeemscript, get_to_countersignatory_with_anchors_redeemscript, CommitmentTransaction, TxCreationKeys, ChannelTransactionParameters, CounterpartyChannelTransactionParameters, HTLCOutputInCommitment};
17651835
use bitcoin::secp256k1::{PublicKey, SecretKey, Secp256k1};
17661836
use crate::util::test_utils;
17671837
use crate::sign::{ChannelSigner, SignerProvider};
1768-
use bitcoin::{Network, Txid};
1838+
use bitcoin::{Network, Txid, Script};
17691839
use bitcoin::hashes::Hash;
17701840
use crate::ln::PaymentHash;
17711841
use bitcoin::hashes::hex::ToHex;
17721842
use bitcoin::util::address::Payload;
17731843
use bitcoin::PublicKey as BitcoinPublicKey;
17741844
use crate::ln::features::ChannelTypeFeatures;
17751845

1776-
#[test]
1777-
fn test_anchors() {
1778-
let secp_ctx = Secp256k1::new();
1846+
struct TestCommitmentTxBuilder {
1847+
commitment_number: u64,
1848+
holder_funding_pubkey: PublicKey,
1849+
counterparty_funding_pubkey: PublicKey,
1850+
keys: TxCreationKeys,
1851+
feerate_per_kw: u32,
1852+
htlcs_with_aux: Vec<(HTLCOutputInCommitment, ())>,
1853+
channel_parameters: ChannelTransactionParameters,
1854+
counterparty_pubkeys: ChannelPublicKeys,
1855+
}
1856+
1857+
impl TestCommitmentTxBuilder {
1858+
fn new() -> Self {
1859+
let secp_ctx = Secp256k1::new();
1860+
let seed = [42; 32];
1861+
let network = Network::Testnet;
1862+
let keys_provider = test_utils::TestKeysInterface::new(&seed, network);
1863+
let signer = keys_provider.derive_channel_signer(3000, keys_provider.generate_channel_keys_id(false, 1_000_000, 0));
1864+
let counterparty_signer = keys_provider.derive_channel_signer(3000, keys_provider.generate_channel_keys_id(true, 1_000_000, 1));
1865+
let delayed_payment_base = &signer.pubkeys().delayed_payment_basepoint;
1866+
let per_commitment_secret = SecretKey::from_slice(&hex::decode("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100").unwrap()[..]).unwrap();
1867+
let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret);
1868+
let htlc_basepoint = &signer.pubkeys().htlc_basepoint;
1869+
let holder_pubkeys = signer.pubkeys();
1870+
let counterparty_pubkeys = counterparty_signer.pubkeys().clone();
1871+
let keys = TxCreationKeys::derive_new(&secp_ctx, &per_commitment_point, delayed_payment_base, htlc_basepoint, &counterparty_pubkeys.revocation_basepoint, &counterparty_pubkeys.htlc_basepoint);
1872+
let channel_parameters = ChannelTransactionParameters {
1873+
holder_pubkeys: holder_pubkeys.clone(),
1874+
holder_selected_contest_delay: 0,
1875+
is_outbound_from_holder: false,
1876+
counterparty_parameters: Some(CounterpartyChannelTransactionParameters { pubkeys: counterparty_pubkeys.clone(), selected_contest_delay: 0 }),
1877+
funding_outpoint: Some(chain::transaction::OutPoint { txid: Txid::all_zeros(), index: 0 }),
1878+
channel_type_features: ChannelTypeFeatures::only_static_remote_key(),
1879+
};
1880+
let htlcs_with_aux = Vec::new();
1881+
1882+
Self {
1883+
commitment_number: 0,
1884+
holder_funding_pubkey: holder_pubkeys.funding_pubkey,
1885+
counterparty_funding_pubkey: counterparty_pubkeys.funding_pubkey,
1886+
keys,
1887+
feerate_per_kw: 1,
1888+
htlcs_with_aux,
1889+
channel_parameters,
1890+
counterparty_pubkeys,
1891+
}
1892+
}
17791893

1780-
let seed = [42; 32];
1781-
let network = Network::Testnet;
1782-
let keys_provider = test_utils::TestKeysInterface::new(&seed, network);
1783-
let signer = keys_provider.derive_channel_signer(3000, keys_provider.generate_channel_keys_id(false, 1_000_000, 0));
1784-
let counterparty_signer = keys_provider.derive_channel_signer(3000, keys_provider.generate_channel_keys_id(true, 1_000_000, 1));
1785-
let delayed_payment_base = &signer.pubkeys().delayed_payment_basepoint;
1786-
let per_commitment_secret = SecretKey::from_slice(&hex::decode("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100").unwrap()[..]).unwrap();
1787-
let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret);
1788-
let htlc_basepoint = &signer.pubkeys().htlc_basepoint;
1789-
let holder_pubkeys = signer.pubkeys();
1790-
let counterparty_pubkeys = counterparty_signer.pubkeys();
1791-
let keys = TxCreationKeys::derive_new(&secp_ctx, &per_commitment_point, delayed_payment_base, htlc_basepoint, &counterparty_pubkeys.revocation_basepoint, &counterparty_pubkeys.htlc_basepoint);
1792-
let mut channel_parameters = ChannelTransactionParameters {
1793-
holder_pubkeys: holder_pubkeys.clone(),
1794-
holder_selected_contest_delay: 0,
1795-
is_outbound_from_holder: false,
1796-
counterparty_parameters: Some(CounterpartyChannelTransactionParameters { pubkeys: counterparty_pubkeys.clone(), selected_contest_delay: 0 }),
1797-
funding_outpoint: Some(chain::transaction::OutPoint { txid: Txid::all_zeros(), index: 0 }),
1798-
channel_type_features: ChannelTypeFeatures::only_static_remote_key(),
1799-
};
1894+
fn build(&mut self, to_broadcaster_sats: u64, to_countersignatory_sats: u64) -> CommitmentTransaction {
1895+
CommitmentTransaction::new_with_auxiliary_htlc_data(
1896+
self.commitment_number, to_broadcaster_sats, to_countersignatory_sats,
1897+
self.holder_funding_pubkey.clone(),
1898+
self.counterparty_funding_pubkey.clone(),
1899+
self.keys.clone(), self.feerate_per_kw,
1900+
&mut self.htlcs_with_aux, &self.channel_parameters.as_holder_broadcastable()
1901+
)
1902+
}
1903+
}
18001904

1801-
let mut htlcs_with_aux: Vec<(_, ())> = Vec::new();
1905+
#[test]
1906+
fn test_anchors() {
1907+
let mut builder = TestCommitmentTxBuilder::new();
18021908

18031909
// Generate broadcaster and counterparty outputs
1804-
let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(
1805-
0, 1000, 2000,
1806-
holder_pubkeys.funding_pubkey,
1807-
counterparty_pubkeys.funding_pubkey,
1808-
keys.clone(), 1,
1809-
&mut htlcs_with_aux, &channel_parameters.as_holder_broadcastable()
1810-
);
1910+
let tx = builder.build(1000, 2000);
18111911
assert_eq!(tx.built.transaction.output.len(), 2);
1812-
assert_eq!(tx.built.transaction.output[1].script_pubkey, Payload::p2wpkh(&BitcoinPublicKey::new(counterparty_pubkeys.payment_point)).unwrap().script_pubkey());
1912+
assert_eq!(tx.built.transaction.output[1].script_pubkey, Payload::p2wpkh(&BitcoinPublicKey::new(builder.counterparty_pubkeys.payment_point)).unwrap().script_pubkey());
18131913

18141914
// Generate broadcaster and counterparty outputs as well as two anchors
1815-
channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies();
1816-
let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(
1817-
0, 1000, 2000,
1818-
holder_pubkeys.funding_pubkey,
1819-
counterparty_pubkeys.funding_pubkey,
1820-
keys.clone(), 1,
1821-
&mut htlcs_with_aux, &channel_parameters.as_holder_broadcastable()
1822-
);
1915+
builder.channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies();
1916+
let tx = builder.build(1000, 2000);
18231917
assert_eq!(tx.built.transaction.output.len(), 4);
1824-
assert_eq!(tx.built.transaction.output[3].script_pubkey, get_to_countersignatory_with_anchors_redeemscript(&counterparty_pubkeys.payment_point).to_v0_p2wsh());
1918+
assert_eq!(tx.built.transaction.output[3].script_pubkey, get_to_countersignatory_with_anchors_redeemscript(&builder.counterparty_pubkeys.payment_point).to_v0_p2wsh());
18251919

18261920
// Generate broadcaster output and anchor
1827-
let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(
1828-
0, 3000, 0,
1829-
holder_pubkeys.funding_pubkey,
1830-
counterparty_pubkeys.funding_pubkey,
1831-
keys.clone(), 1,
1832-
&mut htlcs_with_aux, &channel_parameters.as_holder_broadcastable()
1833-
);
1921+
let tx = builder.build(3000, 0);
18341922
assert_eq!(tx.built.transaction.output.len(), 2);
18351923

18361924
// Generate counterparty output and anchor
1837-
let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(
1838-
0, 0, 3000,
1839-
holder_pubkeys.funding_pubkey,
1840-
counterparty_pubkeys.funding_pubkey,
1841-
keys.clone(), 1,
1842-
&mut htlcs_with_aux, &channel_parameters.as_holder_broadcastable()
1843-
);
1925+
let tx = builder.build(0, 3000);
18441926
assert_eq!(tx.built.transaction.output.len(), 2);
18451927

18461928
let received_htlc = HTLCOutputInCommitment {
@@ -1860,15 +1942,10 @@ mod tests {
18601942
};
18611943

18621944
// Generate broadcaster output and received and offered HTLC outputs, w/o anchors
1863-
channel_parameters.channel_type_features = ChannelTypeFeatures::only_static_remote_key();
1864-
let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(
1865-
0, 3000, 0,
1866-
holder_pubkeys.funding_pubkey,
1867-
counterparty_pubkeys.funding_pubkey,
1868-
keys.clone(), 1,
1869-
&mut vec![(received_htlc.clone(), ()), (offered_htlc.clone(), ())],
1870-
&channel_parameters.as_holder_broadcastable()
1871-
);
1945+
builder.channel_parameters.channel_type_features = ChannelTypeFeatures::only_static_remote_key();
1946+
builder.htlcs_with_aux = vec![(received_htlc.clone(), ()), (offered_htlc.clone(), ())];
1947+
let tx = builder.build(3000, 0);
1948+
let keys = &builder.keys.clone();
18721949
assert_eq!(tx.built.transaction.output.len(), 3);
18731950
assert_eq!(tx.built.transaction.output[0].script_pubkey, get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_v0_p2wsh());
18741951
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 {
18781955
"0020215d61bba56b19e9eadb6107f5a85d7f99c40f65992443f69229c290165bc00d");
18791956

18801957
// Generate broadcaster output and received and offered HTLC outputs, with anchors
1881-
channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies();
1882-
let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(
1883-
0, 3000, 0,
1884-
holder_pubkeys.funding_pubkey,
1885-
counterparty_pubkeys.funding_pubkey,
1886-
keys.clone(), 1,
1887-
&mut vec![(received_htlc.clone(), ()), (offered_htlc.clone(), ())],
1888-
&channel_parameters.as_holder_broadcastable()
1889-
);
1958+
builder.channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies();
1959+
builder.htlcs_with_aux = vec![(received_htlc.clone(), ()), (offered_htlc.clone(), ())];
1960+
let tx = builder.build(3000, 0);
18901961
assert_eq!(tx.built.transaction.output.len(), 5);
18911962
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());
18921963
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 {
18961967
"002087a3faeb1950a469c0e2db4a79b093a41b9526e5a6fc6ef5cb949bde3be379c7");
18971968
}
18981969

1970+
#[test]
1971+
fn test_finding_revokeable_output_index() {
1972+
let mut builder = TestCommitmentTxBuilder::new();
1973+
1974+
// Revokeable output present
1975+
let tx = builder.build(1000, 2000);
1976+
assert_eq!(tx.built.transaction.output.len(), 2);
1977+
assert_eq!(tx.trust().revokeable_output_index(), Some(0));
1978+
1979+
// Revokeable output present (but to_broadcaster_delay missing)
1980+
let tx = CommitmentTransaction { to_broadcaster_delay: None, ..tx };
1981+
assert_eq!(tx.built.transaction.output.len(), 2);
1982+
assert_eq!(tx.trust().revokeable_output_index(), None);
1983+
1984+
// Revokeable output not present (our balance is dust)
1985+
let tx = builder.build(0, 2000);
1986+
assert_eq!(tx.built.transaction.output.len(), 1);
1987+
assert_eq!(tx.trust().revokeable_output_index(), None);
1988+
}
1989+
1990+
#[test]
1991+
fn test_building_to_local_justice_tx() {
1992+
let mut builder = TestCommitmentTxBuilder::new();
1993+
1994+
// Revokeable output not present (our balance is dust)
1995+
let tx = builder.build(0, 2000);
1996+
assert_eq!(tx.built.transaction.output.len(), 1);
1997+
assert!(tx.trust().build_to_local_justice_tx(253, Script::new()).is_err());
1998+
1999+
// Revokeable output present
2000+
let tx = builder.build(1000, 2000);
2001+
assert_eq!(tx.built.transaction.output.len(), 2);
2002+
2003+
// Too high feerate
2004+
assert!(tx.trust().build_to_local_justice_tx(100_000, Script::new()).is_err());
2005+
2006+
// Generate a random public key for destination script
2007+
let secret_key = SecretKey::from_slice(
2008+
&hex::decode("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100")
2009+
.unwrap()[..]).unwrap();
2010+
let pubkey_hash = BitcoinPublicKey::new(
2011+
PublicKey::from_secret_key(&Secp256k1::new(), &secret_key)).wpubkey_hash().unwrap();
2012+
let destination_script = Script::new_v0_p2wpkh(&pubkey_hash);
2013+
2014+
let justice_tx = tx.trust().build_to_local_justice_tx(253, destination_script.clone()).unwrap();
2015+
assert_eq!(justice_tx.input.len(), 1);
2016+
assert_eq!(justice_tx.input[0].previous_output.txid, tx.built.transaction.txid());
2017+
assert_eq!(justice_tx.input[0].previous_output.vout, tx.trust().revokeable_output_index().unwrap() as u32);
2018+
assert!(justice_tx.input[0].sequence.is_rbf());
2019+
2020+
assert_eq!(justice_tx.output.len(), 1);
2021+
assert!(justice_tx.output[0].value < 1000);
2022+
assert_eq!(justice_tx.output[0].script_pubkey, destination_script);
2023+
}
2024+
18992025
#[test]
19002026
fn test_per_commitment_storage() {
19012027
// Test vectors from BOLT 3:

0 commit comments

Comments
 (0)