Skip to content

Commit 4ea07cb

Browse files
Add fake scid namespace for intercepted HTLCs
This is useful for LSPs who wish to create a just-in-time channel for end users receiving a lightning payment. These fake scids will be encoded into route hints in end user invoices, and signal to LDK to create an event triggering the JIT channel, after which the payment will be received. Co-authored-by: John Cantrell <johncantrell97@gmail.com> Co-authored-by: Valentine Wallace <vwallace@protonmail.com>
1 parent f414918 commit 4ea07cb

File tree

1 file changed

+31
-2
lines changed

1 file changed

+31
-2
lines changed

lightning/src/util/scid_utils.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ pub fn scid_from_parts(block: u64, tx_index: u64, vout_index: u64) -> Result<u64
6363
/// LDK has multiple reasons to generate fake short channel ids:
6464
/// 1) outbound SCID aliases we use for private channels
6565
/// 2) phantom node payments, to get an scid for the phantom node's phantom channel
66+
/// 3) payments intended to be intercepted will route using a fake scid (this is typically used so
67+
/// the forwarding node can open a JIT channel to the next hop)
6668
pub(crate) mod fake_scid {
6769
use bitcoin::hash_types::BlockHash;
6870
use bitcoin::hashes::hex::FromHex;
@@ -91,6 +93,7 @@ pub(crate) mod fake_scid {
9193
pub(crate) enum Namespace {
9294
Phantom,
9395
OutboundAlias,
96+
Intercept
9497
}
9598

9699
impl Namespace {
@@ -150,7 +153,7 @@ pub(crate) mod fake_scid {
150153
}
151154
}
152155

153-
/// Returns whether the given fake scid falls into the given namespace.
156+
/// Returns whether the given fake scid falls into the phantom namespace.
154157
pub fn is_valid_phantom(fake_scid_rand_bytes: &[u8; 32], scid: u64, genesis_hash: &BlockHash) -> bool {
155158
let block_height = scid_utils::block_from_scid(&scid);
156159
let tx_index = scid_utils::tx_index_from_scid(&scid);
@@ -160,11 +163,21 @@ pub(crate) mod fake_scid {
160163
&& valid_vout == scid_utils::vout_from_scid(&scid) as u8
161164
}
162165

166+
/// Returns whether the given fake scid falls into the intercept namespace.
167+
pub fn is_valid_intercept(fake_scid_rand_bytes: &[u8; 32], scid: u64, genesis_hash: &BlockHash) -> bool {
168+
let block_height = scid_utils::block_from_scid(&scid);
169+
let tx_index = scid_utils::tx_index_from_scid(&scid);
170+
let namespace = Namespace::Intercept;
171+
let valid_vout = namespace.get_encrypted_vout(block_height, tx_index, fake_scid_rand_bytes);
172+
block_height >= segwit_activation_height(genesis_hash)
173+
&& valid_vout == scid_utils::vout_from_scid(&scid) as u8
174+
}
175+
163176
#[cfg(test)]
164177
mod tests {
165178
use bitcoin::blockdata::constants::genesis_block;
166179
use bitcoin::network::constants::Network;
167-
use crate::util::scid_utils::fake_scid::{is_valid_phantom, MAINNET_SEGWIT_ACTIVATION_HEIGHT, MAX_TX_INDEX, MAX_NAMESPACES, Namespace, NAMESPACE_ID_BITMASK, segwit_activation_height, TEST_SEGWIT_ACTIVATION_HEIGHT};
180+
use crate::util::scid_utils::fake_scid::{is_valid_intercept, is_valid_phantom, MAINNET_SEGWIT_ACTIVATION_HEIGHT, MAX_TX_INDEX, MAX_NAMESPACES, Namespace, NAMESPACE_ID_BITMASK, segwit_activation_height, TEST_SEGWIT_ACTIVATION_HEIGHT};
168181
use crate::util::scid_utils;
169182
use crate::util::test_utils;
170183
use crate::sync::Arc;
@@ -174,6 +187,10 @@ pub(crate) mod fake_scid {
174187
let phantom_namespace = Namespace::Phantom;
175188
assert!((phantom_namespace as u8) < MAX_NAMESPACES);
176189
assert!((phantom_namespace as u8) <= NAMESPACE_ID_BITMASK);
190+
191+
let intercept_namespace = Namespace::Intercept;
192+
assert!((intercept_namespace as u8) < MAX_NAMESPACES);
193+
assert!((intercept_namespace as u8) <= NAMESPACE_ID_BITMASK);
177194
}
178195

179196
#[test]
@@ -203,6 +220,18 @@ pub(crate) mod fake_scid {
203220
assert!(!is_valid_phantom(&fake_scid_rand_bytes, invalid_fake_scid, &testnet_genesis));
204221
}
205222

223+
#[test]
224+
fn test_is_valid_intercept() {
225+
let namespace = Namespace::Intercept;
226+
let fake_scid_rand_bytes = [0; 32];
227+
let testnet_genesis = genesis_block(Network::Testnet).header.block_hash();
228+
let valid_encrypted_vout = namespace.get_encrypted_vout(0, 0, &fake_scid_rand_bytes);
229+
let valid_fake_scid = scid_utils::scid_from_parts(1, 0, valid_encrypted_vout as u64).unwrap();
230+
assert!(is_valid_intercept(&fake_scid_rand_bytes, valid_fake_scid, &testnet_genesis));
231+
let invalid_fake_scid = scid_utils::scid_from_parts(1, 0, 12).unwrap();
232+
assert!(!is_valid_intercept(&fake_scid_rand_bytes, invalid_fake_scid, &testnet_genesis));
233+
}
234+
206235
#[test]
207236
fn test_get_fake_scid() {
208237
let mainnet_genesis = genesis_block(Network::Bitcoin).header.block_hash();

0 commit comments

Comments
 (0)