Skip to content

Commit 42b75ca

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 fa6b5f9 commit 42b75ca

File tree

1 file changed

+200
-75
lines changed

1 file changed

+200
-75
lines changed

lightning/src/ln/chan_utils.rs

Lines changed: 200 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_self_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: to_self_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(),
@@ -1607,6 +1614,68 @@ impl CommitmentTransaction {
16071614
&self.htlcs
16081615
}
16091616

1617+
/// Returns the index of the revokeable output, i.e. the `to_local` output sending funds to
1618+
/// the broadcaster, in the built transaction, if any exists.
1619+
///
1620+
/// There are two cases where this may return `None`:
1621+
/// - The balance of the revokeable output is below the dust limit (only found on commitments
1622+
/// early in the channel's lifetime, i.e. before the channel reserve is met).
1623+
/// - This commitment was created before LDK 0.0.117. In this case, the
1624+
/// commitment transaction previously didn't contain enough information to locate the
1625+
/// revokeable output.
1626+
pub fn revokeable_output_index(&self) -> Option<usize> {
1627+
let revokeable_redeemscript = get_revokeable_redeemscript(
1628+
&self.keys.revocation_key,
1629+
self.to_broadcaster_delay?,
1630+
&self.keys.broadcaster_delayed_payment_key,
1631+
);
1632+
let revokeable_p2wsh = revokeable_redeemscript.to_v0_p2wsh();
1633+
let outputs = &self.built.transaction.output;
1634+
outputs.iter().enumerate()
1635+
.find(|(_, out)| out.script_pubkey == revokeable_p2wsh)
1636+
.map(|(idx, _)| idx)
1637+
}
1638+
1639+
/// Helper method to build an unsigned justice transaction spending the revokeable
1640+
/// `to_local` output to a destination script. Fee estimation accounts for the expected
1641+
/// revocation witness data that will be added when signed.
1642+
///
1643+
/// This method will error if the given fee rate results in a fee greater than the value
1644+
/// of the output being spent, or if there exists no revokeable `to_local` output on this
1645+
/// commitment transaction. See [`Self::revokeable_output_index`] for more details.
1646+
///
1647+
/// The built transaction will allow fee bumping with RBF, and this method takes
1648+
/// `feerate_per_kw` as an input such that multiple copies of a justice transaction at different
1649+
/// fee rates may be built.
1650+
pub fn build_to_local_justice_tx(&self, feerate_per_kw: u64, destination_script: Script)
1651+
-> Result<Transaction, ()> {
1652+
let output_idx = self.revokeable_output_index().ok_or(())?;
1653+
let input = vec![TxIn {
1654+
previous_output: OutPoint {
1655+
txid: self.trust().txid(),
1656+
vout: output_idx as u32,
1657+
},
1658+
script_sig: Script::new(),
1659+
sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
1660+
witness: Witness::new(),
1661+
}];
1662+
let value = self.built.transaction.output[output_idx].value;
1663+
let output = vec![TxOut {
1664+
script_pubkey: destination_script,
1665+
value,
1666+
}];
1667+
let mut justice_tx = Transaction {
1668+
version: 2,
1669+
lock_time: PackedLockTime::ZERO,
1670+
input,
1671+
output,
1672+
};
1673+
let weight = justice_tx.weight() as u64 + WEIGHT_REVOKED_OUTPUT;
1674+
let fee = fee_for_weight(feerate_per_kw as u32, weight);
1675+
justice_tx.output[0].value = value.checked_sub(fee).ok_or(())?;
1676+
Ok(justice_tx)
1677+
}
1678+
16101679
/// Trust our pre-built transaction and derived transaction creation public keys.
16111680
///
16121681
/// Applies a wrapper which allows access to these fields.
@@ -1758,89 +1827,101 @@ pub fn get_commitment_transaction_number_obscure_factor(
17581827

17591828
#[cfg(test)]
17601829
mod tests {
1761-
use super::CounterpartyCommitmentSecrets;
1830+
use super::{CounterpartyCommitmentSecrets, ChannelPublicKeys};
17621831
use crate::{hex, chain};
17631832
use crate::prelude::*;
17641833
use crate::ln::chan_utils::{get_htlc_redeemscript, get_to_countersignatory_with_anchors_redeemscript, CommitmentTransaction, TxCreationKeys, ChannelTransactionParameters, CounterpartyChannelTransactionParameters, HTLCOutputInCommitment};
17651834
use bitcoin::secp256k1::{PublicKey, SecretKey, Secp256k1};
17661835
use crate::util::test_utils;
17671836
use crate::sign::{ChannelSigner, SignerProvider};
1768-
use bitcoin::{Network, Txid};
1837+
use bitcoin::{Network, Txid, Script};
17691838
use bitcoin::hashes::Hash;
17701839
use crate::ln::PaymentHash;
17711840
use bitcoin::hashes::hex::ToHex;
17721841
use bitcoin::util::address::Payload;
17731842
use bitcoin::PublicKey as BitcoinPublicKey;
17741843
use crate::ln::features::ChannelTypeFeatures;
17751844

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

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-
};
1893+
fn build(&mut self, to_broadcaster_sats: u64, to_countersignatory_sats: u64) -> CommitmentTransaction {
1894+
CommitmentTransaction::new_with_auxiliary_htlc_data(
1895+
self.commitment_number, to_broadcaster_sats, to_countersignatory_sats,
1896+
self.holder_funding_pubkey.clone(),
1897+
self.counterparty_funding_pubkey.clone(),
1898+
self.keys.clone(), self.feerate_per_kw,
1899+
&mut self.htlcs_with_aux, &self.channel_parameters.as_holder_broadcastable()
1900+
)
1901+
}
1902+
}
18001903

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

18031908
// 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-
);
1909+
let tx = builder.build(1000, 2000);
18111910
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());
1911+
assert_eq!(tx.built.transaction.output[1].script_pubkey, Payload::p2wpkh(&BitcoinPublicKey::new(builder.counterparty_pubkeys.payment_point)).unwrap().script_pubkey());
18131912

18141913
// 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-
);
1914+
builder.channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies();
1915+
let tx = builder.build(1000, 2000);
18231916
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());
1917+
assert_eq!(tx.built.transaction.output[3].script_pubkey, get_to_countersignatory_with_anchors_redeemscript(&builder.counterparty_pubkeys.payment_point).to_v0_p2wsh());
18251918

18261919
// 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-
);
1920+
let tx = builder.build(3000, 0);
18341921
assert_eq!(tx.built.transaction.output.len(), 2);
18351922

18361923
// 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-
);
1924+
let tx = builder.build(0, 3000);
18441925
assert_eq!(tx.built.transaction.output.len(), 2);
18451926

18461927
let received_htlc = HTLCOutputInCommitment {
@@ -1860,15 +1941,10 @@ mod tests {
18601941
};
18611942

18621943
// 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-
);
1944+
builder.channel_parameters.channel_type_features = ChannelTypeFeatures::only_static_remote_key();
1945+
builder.htlcs_with_aux = vec![(received_htlc.clone(), ()), (offered_htlc.clone(), ())];
1946+
let tx = builder.build(3000, 0);
1947+
let keys = &builder.keys.clone();
18721948
assert_eq!(tx.built.transaction.output.len(), 3);
18731949
assert_eq!(tx.built.transaction.output[0].script_pubkey, get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_v0_p2wsh());
18741950
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 +1954,9 @@ mod tests {
18781954
"0020215d61bba56b19e9eadb6107f5a85d7f99c40f65992443f69229c290165bc00d");
18791955

18801956
// 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-
);
1957+
builder.channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies();
1958+
builder.htlcs_with_aux = vec![(received_htlc.clone(), ()), (offered_htlc.clone(), ())];
1959+
let tx = builder.build(3000, 0);
18901960
assert_eq!(tx.built.transaction.output.len(), 5);
18911961
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());
18921962
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 +1966,61 @@ mod tests {
18961966
"002087a3faeb1950a469c0e2db4a79b093a41b9526e5a6fc6ef5cb949bde3be379c7");
18971967
}
18981968

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

0 commit comments

Comments
 (0)