Skip to content

Commit 686365b

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 finding the revokeable output, and refactors a bit of a previous `CommitmentTransaction` unit test to make adding this one easier.
1 parent 276bb38 commit 686365b

File tree

1 file changed

+147
-74
lines changed

1 file changed

+147
-74
lines changed

lightning/src/ln/chan_utils.rs

Lines changed: 147 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,6 +1308,7 @@ pub struct CommitmentTransaction {
13081308
commitment_number: u64,
13091309
to_broadcaster_value_sat: u64,
13101310
to_countersignatory_value_sat: u64,
1311+
to_self_delay: Option<u16>, // Added in <TODO: expected version>
13111312
feerate_per_kw: u32,
13121313
htlcs: Vec<HTLCOutputInCommitment>,
13131314
// Note that on upgrades, some features of existing outputs may be missed.
@@ -1341,6 +1342,7 @@ impl Writeable for CommitmentTransaction {
13411342
let legacy_deserialization_prevention_marker = legacy_deserialization_prevention_marker_for_channel_type_features(&self.channel_type_features);
13421343
write_tlv_fields!(writer, {
13431344
(0, self.commitment_number, required),
1345+
(1, self.to_self_delay, option),
13441346
(2, self.to_broadcaster_value_sat, required),
13451347
(4, self.to_countersignatory_value_sat, required),
13461348
(6, self.feerate_per_kw, required),
@@ -1359,6 +1361,7 @@ impl Readable for CommitmentTransaction {
13591361
let mut commitment_number = RequiredWrapper(None);
13601362
let mut to_broadcaster_value_sat = RequiredWrapper(None);
13611363
let mut to_countersignatory_value_sat = RequiredWrapper(None);
1364+
let mut to_self_delay = None;
13621365
let mut feerate_per_kw = RequiredWrapper(None);
13631366
let mut keys = RequiredWrapper(None);
13641367
let mut built = RequiredWrapper(None);
@@ -1368,6 +1371,7 @@ impl Readable for CommitmentTransaction {
13681371

13691372
read_tlv_fields!(reader, {
13701373
(0, commitment_number, required),
1374+
(1, to_self_delay, option),
13711375
(2, to_broadcaster_value_sat, required),
13721376
(4, to_countersignatory_value_sat, required),
13731377
(6, feerate_per_kw, required),
@@ -1386,6 +1390,7 @@ impl Readable for CommitmentTransaction {
13861390
commitment_number: commitment_number.0.unwrap(),
13871391
to_broadcaster_value_sat: to_broadcaster_value_sat.0.unwrap(),
13881392
to_countersignatory_value_sat: to_countersignatory_value_sat.0.unwrap(),
1393+
to_self_delay,
13891394
feerate_per_kw: feerate_per_kw.0.unwrap(),
13901395
keys: keys.0.unwrap(),
13911396
built: built.0.unwrap(),
@@ -1417,6 +1422,7 @@ impl CommitmentTransaction {
14171422
commitment_number,
14181423
to_broadcaster_value_sat,
14191424
to_countersignatory_value_sat,
1425+
to_self_delay: Some(channel_parameters.contest_delay()),
14201426
feerate_per_kw,
14211427
htlcs,
14221428
channel_type_features: channel_parameters.channel_type_features().clone(),
@@ -1617,6 +1623,52 @@ impl CommitmentTransaction {
16171623
&self.htlcs
16181624
}
16191625

1626+
/// Returns the index of the revokeable output in the built transaction, if any exists.
1627+
/// There are two cases where this may return `None`:
1628+
/// - The balance of the revokeable output is below the dust limit (only found on commitments
1629+
/// early in the channel's lifetime, i.e. before the channel reserve is met).
1630+
/// - This commitment was created before <TODO: expected version>. In this case, the
1631+
/// commitment transaction previously didn't contain enough information to locate the
1632+
/// revokeable output.
1633+
pub fn revokeable_output_index(&self) -> Option<usize> {
1634+
let revokeable_redeemscript = get_revokeable_redeemscript(
1635+
&self.keys.revocation_key,
1636+
self.to_self_delay?,
1637+
&self.keys.broadcaster_delayed_payment_key,
1638+
);
1639+
let revokeable_p2wsh = revokeable_redeemscript.to_v0_p2wsh();
1640+
let outputs = &self.built.transaction.output;
1641+
outputs.iter().enumerate()
1642+
.find(|(_, out)| out.script_pubkey == revokeable_p2wsh)
1643+
.map(|(idx, _)| idx)
1644+
}
1645+
1646+
/// Helper method to build a basic justice transaction spending `value` sats from an output
1647+
/// on this transaction to a destination script. Use [`Self::revokeable_output_index`] to get
1648+
/// the index of the output to spend to create a valid justice transaction.
1649+
pub fn build_justice_tx(&self, output_idx: u32, value: u64, destination_script: Script)
1650+
-> Transaction {
1651+
let input = vec![TxIn {
1652+
previous_output: OutPoint {
1653+
txid: self.trust().txid(),
1654+
vout: output_idx,
1655+
},
1656+
script_sig: Script::new(),
1657+
sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
1658+
witness: Witness::new(),
1659+
}];
1660+
let output = vec![TxOut {
1661+
script_pubkey: destination_script,
1662+
value,
1663+
}];
1664+
Transaction {
1665+
version: 2,
1666+
lock_time: bitcoin::PackedLockTime::ZERO,
1667+
input,
1668+
output,
1669+
}
1670+
}
1671+
16201672
/// Trust our pre-built transaction and derived transaction creation public keys.
16211673
///
16221674
/// Applies a wrapper which allows access to these fields.
@@ -1768,7 +1820,7 @@ pub fn get_commitment_transaction_number_obscure_factor(
17681820

17691821
#[cfg(test)]
17701822
mod tests {
1771-
use super::CounterpartyCommitmentSecrets;
1823+
use super::{CounterpartyCommitmentSecrets, ChannelPublicKeys};
17721824
use crate::{hex, chain};
17731825
use crate::prelude::*;
17741826
use crate::ln::chan_utils::{get_htlc_redeemscript, get_to_countersignatory_with_anchors_redeemscript, CommitmentTransaction, TxCreationKeys, ChannelTransactionParameters, CounterpartyChannelTransactionParameters, HTLCOutputInCommitment};
@@ -1783,74 +1835,86 @@ mod tests {
17831835
use bitcoin::PublicKey as BitcoinPublicKey;
17841836
use crate::ln::features::ChannelTypeFeatures;
17851837

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

1790-
let seed = [42; 32];
1791-
let network = Network::Testnet;
1792-
let keys_provider = test_utils::TestKeysInterface::new(&seed, network);
1793-
let signer = keys_provider.derive_channel_signer(3000, keys_provider.generate_channel_keys_id(false, 1_000_000, 0));
1794-
let counterparty_signer = keys_provider.derive_channel_signer(3000, keys_provider.generate_channel_keys_id(true, 1_000_000, 1));
1795-
let delayed_payment_base = &signer.pubkeys().delayed_payment_basepoint;
1796-
let per_commitment_secret = SecretKey::from_slice(&hex::decode("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100").unwrap()[..]).unwrap();
1797-
let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret);
1798-
let htlc_basepoint = &signer.pubkeys().htlc_basepoint;
1799-
let holder_pubkeys = signer.pubkeys();
1800-
let counterparty_pubkeys = counterparty_signer.pubkeys();
1801-
let keys = TxCreationKeys::derive_new(&secp_ctx, &per_commitment_point, delayed_payment_base, htlc_basepoint, &counterparty_pubkeys.revocation_basepoint, &counterparty_pubkeys.htlc_basepoint);
1802-
let mut channel_parameters = ChannelTransactionParameters {
1803-
holder_pubkeys: holder_pubkeys.clone(),
1804-
holder_selected_contest_delay: 0,
1805-
is_outbound_from_holder: false,
1806-
counterparty_parameters: Some(CounterpartyChannelTransactionParameters { pubkeys: counterparty_pubkeys.clone(), selected_contest_delay: 0 }),
1807-
funding_outpoint: Some(chain::transaction::OutPoint { txid: Txid::all_zeros(), index: 0 }),
1808-
channel_type_features: ChannelTypeFeatures::only_static_remote_key(),
1809-
};
1886+
fn build(&mut self, to_broadcaster_sats: u64, to_countersignatory_sats: u64) -> CommitmentTransaction {
1887+
CommitmentTransaction::new_with_auxiliary_htlc_data(
1888+
self.commitment_number, to_broadcaster_sats, to_countersignatory_sats,
1889+
self.holder_funding_pubkey.clone(),
1890+
self.counterparty_funding_pubkey.clone(),
1891+
self.keys.clone(), self.feerate_per_kw,
1892+
&mut self.htlcs_with_aux, &self.channel_parameters.as_holder_broadcastable()
1893+
)
1894+
}
1895+
}
18101896

1811-
let mut htlcs_with_aux: Vec<(_, ())> = Vec::new();
1897+
#[test]
1898+
fn test_anchors() {
1899+
let mut builder = TestCommitmentTxBuilder::new();
18121900

18131901
// Generate broadcaster and counterparty outputs
1814-
let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(
1815-
0, 1000, 2000,
1816-
holder_pubkeys.funding_pubkey,
1817-
counterparty_pubkeys.funding_pubkey,
1818-
keys.clone(), 1,
1819-
&mut htlcs_with_aux, &channel_parameters.as_holder_broadcastable()
1820-
);
1902+
let tx = builder.build(1000, 2000);
18211903
assert_eq!(tx.built.transaction.output.len(), 2);
1822-
assert_eq!(tx.built.transaction.output[1].script_pubkey, Payload::p2wpkh(&BitcoinPublicKey::new(counterparty_pubkeys.payment_point)).unwrap().script_pubkey());
1904+
assert_eq!(tx.built.transaction.output[1].script_pubkey, Payload::p2wpkh(&BitcoinPublicKey::new(builder.counterparty_pubkeys.payment_point)).unwrap().script_pubkey());
18231905

18241906
// Generate broadcaster and counterparty outputs as well as two anchors
1825-
channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies();
1826-
let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(
1827-
0, 1000, 2000,
1828-
holder_pubkeys.funding_pubkey,
1829-
counterparty_pubkeys.funding_pubkey,
1830-
keys.clone(), 1,
1831-
&mut htlcs_with_aux, &channel_parameters.as_holder_broadcastable()
1832-
);
1907+
builder.channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies();
1908+
let tx = builder.build(1000, 2000);
18331909
assert_eq!(tx.built.transaction.output.len(), 4);
1834-
assert_eq!(tx.built.transaction.output[3].script_pubkey, get_to_countersignatory_with_anchors_redeemscript(&counterparty_pubkeys.payment_point).to_v0_p2wsh());
1910+
assert_eq!(tx.built.transaction.output[3].script_pubkey, get_to_countersignatory_with_anchors_redeemscript(&builder.counterparty_pubkeys.payment_point).to_v0_p2wsh());
18351911

18361912
// Generate broadcaster output and anchor
1837-
let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(
1838-
0, 3000, 0,
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-
);
1913+
let tx = builder.build(3000, 0);
18441914
assert_eq!(tx.built.transaction.output.len(), 2);
18451915

18461916
// Generate counterparty output and anchor
1847-
let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(
1848-
0, 0, 3000,
1849-
holder_pubkeys.funding_pubkey,
1850-
counterparty_pubkeys.funding_pubkey,
1851-
keys.clone(), 1,
1852-
&mut htlcs_with_aux, &channel_parameters.as_holder_broadcastable()
1853-
);
1917+
let tx = builder.build(0, 3000);
18541918
assert_eq!(tx.built.transaction.output.len(), 2);
18551919

18561920
let received_htlc = HTLCOutputInCommitment {
@@ -1870,15 +1934,10 @@ mod tests {
18701934
};
18711935

18721936
// Generate broadcaster output and received and offered HTLC outputs, w/o anchors
1873-
channel_parameters.channel_type_features = ChannelTypeFeatures::only_static_remote_key();
1874-
let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(
1875-
0, 3000, 0,
1876-
holder_pubkeys.funding_pubkey,
1877-
counterparty_pubkeys.funding_pubkey,
1878-
keys.clone(), 1,
1879-
&mut vec![(received_htlc.clone(), ()), (offered_htlc.clone(), ())],
1880-
&channel_parameters.as_holder_broadcastable()
1881-
);
1937+
builder.channel_parameters.channel_type_features = ChannelTypeFeatures::only_static_remote_key();
1938+
builder.htlcs_with_aux = vec![(received_htlc.clone(), ()), (offered_htlc.clone(), ())];
1939+
let tx = builder.build(3000, 0);
1940+
let keys = &builder.keys.clone();
18821941
assert_eq!(tx.built.transaction.output.len(), 3);
18831942
assert_eq!(tx.built.transaction.output[0].script_pubkey, get_htlc_redeemscript(&received_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_v0_p2wsh());
18841943
assert_eq!(tx.built.transaction.output[1].script_pubkey, get_htlc_redeemscript(&offered_htlc, &ChannelTypeFeatures::only_static_remote_key(), &keys).to_v0_p2wsh());
@@ -1888,15 +1947,9 @@ mod tests {
18881947
"0020215d61bba56b19e9eadb6107f5a85d7f99c40f65992443f69229c290165bc00d");
18891948

18901949
// Generate broadcaster output and received and offered HTLC outputs, with anchors
1891-
channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies();
1892-
let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(
1893-
0, 3000, 0,
1894-
holder_pubkeys.funding_pubkey,
1895-
counterparty_pubkeys.funding_pubkey,
1896-
keys.clone(), 1,
1897-
&mut vec![(received_htlc.clone(), ()), (offered_htlc.clone(), ())],
1898-
&channel_parameters.as_holder_broadcastable()
1899-
);
1950+
builder.channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies();
1951+
builder.htlcs_with_aux = vec![(received_htlc.clone(), ()), (offered_htlc.clone(), ())];
1952+
let tx = builder.build(3000, 0);
19001953
assert_eq!(tx.built.transaction.output.len(), 5);
19011954
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());
19021955
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());
@@ -1906,6 +1959,26 @@ mod tests {
19061959
"002087a3faeb1950a469c0e2db4a79b093a41b9526e5a6fc6ef5cb949bde3be379c7");
19071960
}
19081961

1962+
#[test]
1963+
fn test_finding_revokeable_output_index() {
1964+
let mut builder = TestCommitmentTxBuilder::new();
1965+
1966+
// Revokeable output present
1967+
let tx = builder.build(1000, 2000);
1968+
assert_eq!(tx.built.transaction.output.len(), 2);
1969+
assert_eq!(tx.revokeable_output_index(), Some(0));
1970+
1971+
// Revokeable output present (but to_self_delay missing)
1972+
let tx = CommitmentTransaction { to_self_delay: None, ..tx };
1973+
assert_eq!(tx.built.transaction.output.len(), 2);
1974+
assert_eq!(tx.revokeable_output_index(), None);
1975+
1976+
// Revokeable output not present (our balance is dust)
1977+
let tx = builder.build(0, 2000);
1978+
assert_eq!(tx.built.transaction.output.len(), 1);
1979+
assert_eq!(tx.revokeable_output_index(), None);
1980+
}
1981+
19091982
#[test]
19101983
fn test_per_commitment_storage() {
19111984
// Test vectors from BOLT 3:

0 commit comments

Comments
 (0)