@@ -338,6 +338,170 @@ mod inbound_payment {
338
338
}
339
339
}
340
340
341
+ /// LDK has multiple reasons to generate fake short channel ids:
342
+ /// 1) zero-conf channels that don't have a confirmed channel id yet
343
+ /// 2) phantom node payments, to get an scid for the phantom node's phantom channel
344
+ mod fake_scid {
345
+ use bitcoin:: hash_types:: BlockHash ;
346
+ use bitcoin:: hashes:: hex:: FromHex ;
347
+ use chain:: keysinterface:: { Sign , KeysInterface } ;
348
+ use util:: scid_utils;
349
+
350
+ use core:: convert:: TryInto ;
351
+ use core:: ops:: Deref ;
352
+
353
+ const TEST_SEGWIT_ACTIVATION_HEIGHT : u32 = 0 ;
354
+ const MAINNET_SEGWIT_ACTIVATION_HEIGHT : u32 = 481_824 ;
355
+ const MAX_TX_INDEX : u32 = 2_500 ;
356
+ const MAX_VOUT : u16 = 3 ;
357
+ const MAX_NAMESPACES : u8 = 8 ;
358
+ const NAMESPACE_ID_BITMASK : u8 = 0b111 ;
359
+
360
+ /// Fake scids are divided into namespaces, with each namespace having its own randomly-selected
361
+ /// identifier between [0..7], such that `scid_tx_index % MAX_NAMESPACES == namespace_identifier`.
362
+ /// This allows us to identify what namespace a fake scid corresponds to upon HTLC receipt, and
363
+ /// handle the HTLC accordingly.
364
+ pub ( super ) enum Namespace {
365
+ Phantom ( u8 ) ,
366
+ // Coming soon: a variant for the zero-conf scid namespace
367
+ }
368
+
369
+ impl Namespace {
370
+ /// We generate "realistic-looking" random scids here, meaning the scid's block height is
371
+ /// between segwit activation and the current best known height, and the tx index and output
372
+ /// index are also selected from a "reasonable" range. We add this logic because it makes it
373
+ /// non-obvious at a glance that the scid is fake, e.g. if it appears in invoice route hints.
374
+ pub ( super ) fn get_fake_scid < Signer : Sign , K : Deref > ( & self , highest_seen_blockheight : u32 , genesis_hash : & BlockHash , keys_manager : & K ) -> u64
375
+ where K :: Target : KeysInterface < Signer = Signer > ,
376
+ {
377
+ const NUM_BLOCKS_24HRS : u32 = 144 ;
378
+ let scid_namespace = self . as_u8 ( ) ;
379
+ let rand_bytes = keys_manager. get_secure_random_bytes ( ) ;
380
+
381
+ let mut valid_block_range = highest_seen_blockheight - segwit_activation_height ( genesis_hash) ;
382
+ // We want to ensure that this fake channel won't conflict with any transactions we haven't
383
+ // seen yet, in case `highest_seen_blockheight` is updated before we get full information
384
+ // about transactions confirmed in the given block.
385
+ if valid_block_range > NUM_BLOCKS_24HRS { valid_block_range -= NUM_BLOCKS_24HRS ; }
386
+
387
+ let rand_value_for_height = u32:: from_be_bytes ( rand_bytes[ ..4 ] . try_into ( ) . unwrap ( ) ) ;
388
+ let fake_scid_height = segwit_activation_height ( genesis_hash) + rand_value_for_height % valid_block_range;
389
+
390
+ let rand_u32 = u32:: from_be_bytes ( rand_bytes[ 4 ..8 ] . try_into ( ) . unwrap ( ) ) ;
391
+ let rand_tx_index = rand_u32 % MAX_TX_INDEX ;
392
+ // Put the tx index into the given `scid_namespace`.
393
+ let fake_scid_tx_index = ( ( rand_tx_index & !( NAMESPACE_ID_BITMASK as u32 ) ) ) + scid_namespace as u32 ;
394
+
395
+ let rand_value_for_vout = u16:: from_be_bytes ( rand_bytes[ 8 ..10 ] . try_into ( ) . unwrap ( ) ) ;
396
+ let fake_scid_vout_index = rand_value_for_vout % MAX_VOUT ;
397
+ scid_utils:: scid_from_parts ( fake_scid_height as u64 , fake_scid_tx_index as u64 , fake_scid_vout_index as u64 ) . unwrap ( )
398
+ }
399
+
400
+ fn as_u8 ( & self ) -> u8 {
401
+ match self {
402
+ Namespace :: Phantom ( namespace) => * namespace,
403
+ }
404
+ }
405
+
406
+ fn phantom ( fake_scid_offset : u8 ) -> Namespace {
407
+ const PHANTOM_OFFSET : u8 = 0 ;
408
+ Namespace :: Phantom ( ( fake_scid_offset + PHANTOM_OFFSET ) % MAX_NAMESPACES )
409
+ }
410
+ }
411
+
412
+ pub ( super ) fn get_phantom_scid < Signer : Sign , K : Deref > ( fake_scid_offset : u8 , highest_seen_blockheight : u32 , genesis_hash : & BlockHash , keys_manager : & K ) -> u64
413
+ where K :: Target : KeysInterface < Signer = Signer > ,
414
+ {
415
+ let namespace = Namespace :: phantom ( fake_scid_offset) ;
416
+ namespace. get_fake_scid ( highest_seen_blockheight, genesis_hash, keys_manager)
417
+ }
418
+
419
+ /// Each LDK node uses a random offset for its fake scid namespaces, to make it harder for a third
420
+ /// party to identify phantom node payments.
421
+ pub ( super ) fn get_new_offset < Signer : Sign , K : Deref > ( keys_manager : & K ) -> u8
422
+ where K :: Target : KeysInterface < Signer = Signer > ,
423
+ {
424
+ let offset_rand_bytes = keys_manager. get_secure_random_bytes ( ) ;
425
+ offset_rand_bytes [ 0 ] & NAMESPACE_ID_BITMASK
426
+ }
427
+
428
+ fn segwit_activation_height ( genesis : & BlockHash ) -> u32 {
429
+ const MAINNET_GENESIS_STR : & ' static str = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" ;
430
+ if BlockHash :: from_hex ( MAINNET_GENESIS_STR ) . unwrap ( ) == * genesis {
431
+ MAINNET_SEGWIT_ACTIVATION_HEIGHT
432
+ } else {
433
+ TEST_SEGWIT_ACTIVATION_HEIGHT
434
+ }
435
+ }
436
+
437
+ /// Returns whether the given fake scid falls into the given namespace.
438
+ pub ( super ) fn is_valid_phantom ( fake_scid_offset : u8 , scid : u64 ) -> bool {
439
+ let namespace = Namespace :: phantom ( fake_scid_offset) ;
440
+ scid_utils:: tx_index_from_scid ( & scid) % MAX_NAMESPACES as u32 == namespace. as_u8 ( ) as u32
441
+ }
442
+
443
+ #[ cfg( test) ]
444
+ mod tests {
445
+ use bitcoin:: blockdata:: constants:: genesis_block;
446
+ use bitcoin:: network:: constants:: Network ;
447
+ use ln:: channelmanager:: fake_scid:: { is_valid_phantom, MAINNET_SEGWIT_ACTIVATION_HEIGHT , MAX_TX_INDEX , MAX_VOUT , Namespace , NAMESPACE_ID_BITMASK , segwit_activation_height, TEST_SEGWIT_ACTIVATION_HEIGHT } ;
448
+ use util:: scid_utils;
449
+ use util:: test_utils;
450
+ use sync:: Arc ;
451
+
452
+ const TEST_FAKE_SCID_OFFSET : u8 = 0xAB ;
453
+
454
+ #[ test]
455
+ fn namespace_identifier_is_within_range ( ) {
456
+ let Namespace :: Phantom ( ns) = Namespace :: phantom ( TEST_FAKE_SCID_OFFSET ) ;
457
+ assert ! ( ns <= NAMESPACE_ID_BITMASK ) ;
458
+ }
459
+
460
+ #[ test]
461
+ fn test_segwit_activation_height ( ) {
462
+ let mainnet_genesis = genesis_block ( Network :: Bitcoin ) . header . block_hash ( ) ;
463
+ assert_eq ! ( segwit_activation_height( & mainnet_genesis) , MAINNET_SEGWIT_ACTIVATION_HEIGHT ) ;
464
+
465
+ let testnet_genesis = genesis_block ( Network :: Testnet ) . header . block_hash ( ) ;
466
+ assert_eq ! ( segwit_activation_height( & testnet_genesis) , TEST_SEGWIT_ACTIVATION_HEIGHT ) ;
467
+
468
+ let signet_genesis = genesis_block ( Network :: Signet ) . header . block_hash ( ) ;
469
+ assert_eq ! ( segwit_activation_height( & signet_genesis) , TEST_SEGWIT_ACTIVATION_HEIGHT ) ;
470
+
471
+ let regtest_genesis = genesis_block ( Network :: Regtest ) . header . block_hash ( ) ;
472
+ assert_eq ! ( segwit_activation_height( & regtest_genesis) , TEST_SEGWIT_ACTIVATION_HEIGHT ) ;
473
+ }
474
+
475
+ #[ test]
476
+ fn test_is_valid_phantom ( ) {
477
+ let namespace = Namespace :: Phantom ( 3 ) ;
478
+ let valid_fake_scid = scid_utils:: scid_from_parts ( 0 , 11 , 0 ) . unwrap ( ) ;
479
+ assert ! ( is_valid_phantom( namespace. as_u8( ) , valid_fake_scid) ) ;
480
+ let invalid_fake_scid = scid_utils:: scid_from_parts ( 0 , 12 , 0 ) . unwrap ( ) ;
481
+ assert ! ( !is_valid_phantom( namespace. as_u8( ) , invalid_fake_scid) ) ;
482
+ }
483
+
484
+ #[ test]
485
+ fn test_get_fake_scid ( ) {
486
+ let mainnet_genesis = genesis_block ( Network :: Bitcoin ) . header . block_hash ( ) ;
487
+ let seed = [ 0 as u8 ; 32 ] ;
488
+ let keys_manager = Arc :: new ( test_utils:: TestKeysInterface :: new ( & seed, Network :: Testnet ) ) ;
489
+ let namespace = Namespace :: phantom ( TEST_FAKE_SCID_OFFSET ) ;
490
+ let fake_scid = namespace. get_fake_scid ( 500_000 , & mainnet_genesis, & keys_manager) ;
491
+
492
+ let fake_height = scid_utils:: block_from_scid ( & fake_scid) ;
493
+ assert ! ( fake_height >= MAINNET_SEGWIT_ACTIVATION_HEIGHT ) ;
494
+ assert ! ( fake_height <= 500_000 ) ;
495
+
496
+ let fake_tx_index = scid_utils:: tx_index_from_scid ( & fake_scid) ;
497
+ assert ! ( fake_tx_index <= MAX_TX_INDEX ) ;
498
+
499
+ let fake_vout = fake_scid & scid_utils:: MAX_SCID_VOUT_INDEX ;
500
+ assert ! ( fake_vout <= MAX_VOUT . into( ) ) ;
501
+ }
502
+ }
503
+ }
504
+
341
505
// We hold various information about HTLC relay in the HTLC objects in Channel itself:
342
506
//
343
507
// Upon receipt of an HTLC from a peer, we'll give it a PendingHTLCStatus indicating if it should
@@ -972,6 +1136,8 @@ pub struct ChannelManager<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref,
972
1136
973
1137
inbound_payment_key : inbound_payment:: ExpandedKey ,
974
1138
1139
+ fake_scid_offset : u8 ,
1140
+
975
1141
/// Used to track the last value sent in a node_announcement "timestamp" field. We ensure this
976
1142
/// value increases strictly since we don't assume access to a time source.
977
1143
last_node_announcement_serial : AtomicUsize ,
@@ -1689,6 +1855,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
1689
1855
secp_ctx,
1690
1856
1691
1857
inbound_payment_key : expanded_inbound_key,
1858
+ fake_scid_offset : fake_scid:: get_new_offset ( & keys_manager) ,
1692
1859
1693
1860
last_node_announcement_serial : AtomicUsize :: new ( 0 ) ,
1694
1861
highest_seen_timestamp : AtomicUsize :: new ( 0 ) ,
@@ -5106,6 +5273,26 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
5106
5273
inbound_payment:: get_payment_preimage ( payment_hash, payment_secret, & self . inbound_payment_key )
5107
5274
}
5108
5275
5276
+ /// Gets a fake short channel id for use in receiving phantom node payments.
5277
+ ///
5278
+ /// A phantom node payment is a payment made to a phantom invoice, which is an invoice that can be
5279
+ /// paid to one of multiple nodes. This works because the invoice officially pays to a fake node
5280
+ /// (the "phantom"), with route hints containing phantom channels. This method is used to retrieve
5281
+ /// the short channel ids for these phantom route hints.
5282
+ ///
5283
+ /// While these scids can be reused across invoices, it's best for privacy to use new ones when
5284
+ /// possible.
5285
+ pub fn get_phantom_scid ( & self ) -> u64 {
5286
+ let mut channel_state = self . channel_state . lock ( ) . unwrap ( ) ;
5287
+ let best_block = self . best_block . read ( ) . unwrap ( ) ;
5288
+ let scid_candidate = fake_scid:: get_phantom_scid ( self . fake_scid_offset , best_block. height ( ) , & self . genesis_hash , & self . keys_manager ) ;
5289
+ // Ensure the generated scid doesn't conflict with a real channel.
5290
+ match channel_state. short_to_id . entry ( scid_candidate) {
5291
+ hash_map:: Entry :: Occupied ( _) => scid_candidate + 1 ,
5292
+ hash_map:: Entry :: Vacant ( _) => scid_candidate
5293
+ }
5294
+ }
5295
+
5109
5296
#[ cfg( any( test, feature = "fuzztarget" , feature = "_test_utils" ) ) ]
5110
5297
pub fn get_and_clear_pending_events ( & self ) -> Vec < events:: Event > {
5111
5298
let events = core:: cell:: RefCell :: new ( Vec :: new ( ) ) ;
@@ -6193,7 +6380,8 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable f
6193
6380
write_tlv_fields ! ( writer, {
6194
6381
( 1 , pending_outbound_payments_no_retry, required) ,
6195
6382
( 3 , pending_outbound_payments, required) ,
6196
- ( 5 , self . our_network_pubkey, required)
6383
+ ( 5 , self . our_network_pubkey, required) ,
6384
+ ( 7 , self . fake_scid_offset, required) ,
6197
6385
} ) ;
6198
6386
6199
6387
Ok ( ( ) )
@@ -6489,11 +6677,16 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
6489
6677
let mut pending_outbound_payments_no_retry: Option < HashMap < PaymentId , HashSet < [ u8 ; 32 ] > > > = None ;
6490
6678
let mut pending_outbound_payments = None ;
6491
6679
let mut received_network_pubkey: Option < PublicKey > = None ;
6680
+ let mut fake_scid_offset: Option < u8 > = None ;
6492
6681
read_tlv_fields ! ( reader, {
6493
6682
( 1 , pending_outbound_payments_no_retry, option) ,
6494
6683
( 3 , pending_outbound_payments, option) ,
6495
- ( 5 , received_network_pubkey, option)
6684
+ ( 5 , received_network_pubkey, option) ,
6685
+ ( 7 , fake_scid_offset, option) ,
6496
6686
} ) ;
6687
+ if fake_scid_offset. is_none ( ) {
6688
+ fake_scid_offset = Some ( fake_scid:: get_new_offset ( & args. keys_manager ) ) ;
6689
+ }
6497
6690
6498
6691
if pending_outbound_payments. is_none ( ) && pending_outbound_payments_no_retry. is_none ( ) {
6499
6692
pending_outbound_payments = Some ( pending_outbound_payments_compat) ;
@@ -6585,6 +6778,7 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
6585
6778
inbound_payment_key : expanded_inbound_key,
6586
6779
pending_inbound_payments : Mutex :: new ( pending_inbound_payments) ,
6587
6780
pending_outbound_payments : Mutex :: new ( pending_outbound_payments. unwrap ( ) ) ,
6781
+ fake_scid_offset : fake_scid_offset. unwrap ( ) ,
6588
6782
6589
6783
our_network_key : args. keys_manager . get_node_secret ( ) ,
6590
6784
our_network_pubkey,
0 commit comments