Skip to content

Commit e2e0d8a

Browse files
committed
Add a method to fetch all possible remote-closure script_pubkeys
In the previous commit we (finally) allowed users to opt into a static `remote_key` derivation scheme, enabling them to scan the chain for funds on counterparty commitment transactions without any state at all. This is only possible, however, of course, if they have the full list of scripts to scan the chain for, which we expose here.
1 parent 189b8ac commit e2e0d8a

File tree

1 file changed

+43
-0
lines changed

1 file changed

+43
-0
lines changed

lightning/src/sign/mod.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2074,6 +2074,40 @@ impl KeysManager {
20742074
self.node_secret
20752075
}
20762076

2077+
/// Gets the set of possible `script_pubkey`s which can appear on chain for our
2078+
/// non-HTLC-encumbered balance if our counterparty force-closes a channel.
2079+
///
2080+
/// If you've lost all data except your seed, asking your peers nicely to force-close the
2081+
/// chanels they had with you (and hoping they don't broadcast a stale state and that there are
2082+
/// no pending HTLCs in the latest state) and scanning the chain for these `script_pubkey`s can
2083+
/// allow you to recover (some of) your funds.
2084+
///
2085+
/// Only channels opened when using a [`KeysManager`] with the `v2_remote_key_derivation`
2086+
/// argument to [`KeysManager::new`] set, or any spliced channels will close to such scripts,
2087+
/// other channels will close to a randomly-generated `script_pubkey`.
2088+
pub fn possible_v2_counterparty_closed_balance_spks<C: Signing>(
2089+
&self, secp_ctx: &Secp256k1<C>,
2090+
) -> Vec<ScriptBuf> {
2091+
let mut res = Vec::with_capacity(usize::from(STATIC_PAYMENT_KEY_COUNT) * 2);
2092+
let static_remote_key_features = ChannelTypeFeatures::only_static_remote_key();
2093+
let mut zero_fee_htlc_features = ChannelTypeFeatures::only_static_remote_key();
2094+
zero_fee_htlc_features.set_anchors_zero_fee_htlc_tx_required();
2095+
for idx in 0..STATIC_PAYMENT_KEY_COUNT {
2096+
let key = self
2097+
.static_payment_key
2098+
.derive_priv(
2099+
&self.secp_ctx,
2100+
&ChildNumber::from_hardened_idx(u32::from(idx)).expect("key space exhausted"),
2101+
)
2102+
.expect("Your RNG is busted")
2103+
.private_key;
2104+
let pubkey = PublicKey::from_secret_key(secp_ctx, &key);
2105+
res.push(get_counterparty_payment_script(&static_remote_key_features, &pubkey));
2106+
res.push(get_counterparty_payment_script(&zero_fee_htlc_features, &pubkey));
2107+
}
2108+
res
2109+
}
2110+
20772111
fn derive_payment_key_v2(&self, key_idx: u64) -> SecretKey {
20782112
let idx = key_idx % u64::from(STATIC_PAYMENT_KEY_COUNT);
20792113
self.static_payment_key
@@ -2176,6 +2210,15 @@ impl KeysManager {
21762210
let signer = self.derive_channel_keys(&descriptor.channel_keys_id);
21772211
keys_cache = Some((signer, descriptor.channel_keys_id));
21782212
}
2213+
#[cfg(test)]
2214+
if self.v2_remote_key_derivation {
2215+
// In tests, we don't have to deal with upgrades from V1 signers with
2216+
// `v2_remote_key_derivation` set, so use this opportunity to test
2217+
// `possible_v2_counterparty_closed_balance_spks`.
2218+
let possible_spks =
2219+
self.possible_v2_counterparty_closed_balance_spks(secp_ctx);
2220+
assert!(possible_spks.contains(&descriptor.output.script_pubkey));
2221+
}
21792222
let witness = keys_cache.as_ref().unwrap().0.sign_counterparty_payment_input(
21802223
&psbt.unsigned_tx,
21812224
input_idx,

0 commit comments

Comments
 (0)