@@ -338,6 +338,168 @@ 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:: blockdata:: constants:: genesis_block;
346
+ use bitcoin:: hash_types:: BlockHash ;
347
+ use bitcoin:: network:: constants:: Network ;
348
+ use chain:: keysinterface:: { Sign , KeysInterface } ;
349
+ use util:: scid_utils;
350
+
351
+ use core:: convert:: TryInto ;
352
+ use core:: ops:: Deref ;
353
+
354
+ const TEST_SEGWIT_ACTIVATION_HEIGHT : u32 = 0 ;
355
+ const MAINNET_SEGWIT_ACTIVATION_HEIGHT : u32 = 481_824 ;
356
+ const MAX_TX_INDEX : u32 = 2_500 ;
357
+ const MAX_VOUT : u16 = 3 ;
358
+ const MAX_NAMESPACES : u8 = 8 ;
359
+ const NAMESPACE_ID_BITMASK : u8 = 0b111 ;
360
+
361
+ /// Fake scids are divided into namespaces, with each namespace having its own randomly-selected
362
+ /// identifier between [0..7], such that `scid_tx_index % MAX_NAMESPACES == namespace_identifier`.
363
+ /// This allows us to identify what namespace a fake scid corresponds to upon HTLC receipt, and
364
+ /// handle the HTLC accordingly.
365
+ pub ( super ) enum Namespace {
366
+ Phantom ( u8 ) ,
367
+ // Coming soon: a variant for the zero-conf scid namespace
368
+ }
369
+
370
+ impl Namespace {
371
+ /// We generate "realistic-looking" random scids here, meaning the scid's block height is
372
+ /// between segwit activation and the current best known height, and the tx index and output
373
+ /// index are also selected from a "reasonable" range. We add this logic because it makes it
374
+ /// non-obvious at a glance that the scid is fake, e.g. if it appears in invoice route hints.
375
+ pub ( super ) fn get_fake_scid < Signer : Sign , K : Deref > ( & self , highest_seen_blockheight : u32 , genesis_hash : & BlockHash , keys_manager : & K ) -> u64
376
+ where K :: Target : KeysInterface < Signer = Signer > ,
377
+ {
378
+ const NUM_BLOCKS_24HRS : u32 = 144 ;
379
+ let scid_namespace = self . as_u8 ( ) ;
380
+ let rand_bytes = keys_manager. get_secure_random_bytes ( ) ;
381
+
382
+ let mut valid_block_range = highest_seen_blockheight - segwit_activation_height ( genesis_hash) ;
383
+ // We want to ensure that this fake channel could've plausibly confirmed on-chain.
384
+ if valid_block_range > NUM_BLOCKS_24HRS { valid_block_range -= NUM_BLOCKS_24HRS ; }
385
+
386
+ let rand_value_for_height = u32:: from_be_bytes ( rand_bytes[ ..4 ] . try_into ( ) . unwrap ( ) ) ;
387
+ let fake_scid_height = segwit_activation_height ( genesis_hash) + rand_value_for_height % valid_block_range;
388
+
389
+ let rand_u32 = u32:: from_be_bytes ( rand_bytes[ 4 ..8 ] . try_into ( ) . unwrap ( ) ) ;
390
+ let rand_tx_index = rand_u32 % MAX_TX_INDEX ;
391
+ // Put the tx index into the given `scid_namespace`.
392
+ let fake_scid_tx_index = ( ( rand_tx_index / 8 ) * 8 ) + scid_namespace as u32 ;
393
+
394
+ let rand_value_for_vout = u16:: from_be_bytes ( rand_bytes[ 8 ..10 ] . try_into ( ) . unwrap ( ) ) ;
395
+ let fake_scid_vout_index = rand_value_for_vout % MAX_VOUT ;
396
+ scid_utils:: scid_from_parts ( fake_scid_height as u64 , fake_scid_tx_index as u64 , fake_scid_vout_index as u64 ) . unwrap ( )
397
+ }
398
+
399
+ fn as_u8 ( & self ) -> u8 {
400
+ match self {
401
+ Namespace :: Phantom ( namespace) => * namespace,
402
+ }
403
+ }
404
+
405
+ fn phantom ( fake_scid_offset : u8 ) -> Namespace {
406
+ const PHANTOM_OFFSET : u8 = 0 ;
407
+ Namespace :: Phantom ( ( fake_scid_offset + PHANTOM_OFFSET ) % MAX_NAMESPACES )
408
+ }
409
+ }
410
+
411
+ 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
412
+ where K :: Target : KeysInterface < Signer = Signer > ,
413
+ {
414
+ let namespace = Namespace :: phantom ( fake_scid_offset) ;
415
+ namespace. get_fake_scid ( highest_seen_blockheight, genesis_hash, keys_manager)
416
+ }
417
+
418
+ /// Each LDK node uses a random offset for its fake scid namespaces, to make it harder for a third
419
+ /// party to identify phantom node payments.
420
+ pub ( super ) fn get_new_offset < Signer : Sign , K : Deref > ( keys_manager : & K ) -> u8
421
+ where K :: Target : KeysInterface < Signer = Signer > ,
422
+ {
423
+ let offset_rand_bytes = keys_manager. get_secure_random_bytes ( ) ;
424
+ offset_rand_bytes [ 0 ] & NAMESPACE_ID_BITMASK
425
+ }
426
+
427
+ fn segwit_activation_height ( genesis : & BlockHash ) -> u32 {
428
+ if genesis_block ( Network :: Bitcoin ) . header . block_hash ( ) == * genesis {
429
+ MAINNET_SEGWIT_ACTIVATION_HEIGHT
430
+ } else {
431
+ TEST_SEGWIT_ACTIVATION_HEIGHT
432
+ }
433
+ }
434
+
435
+ /// Returns whether the given fake scid falls into the given namespace.
436
+ pub ( super ) fn is_valid_phantom ( fake_scid_offset : u8 , scid : u64 ) -> bool {
437
+ let namespace = Namespace :: phantom ( fake_scid_offset) ;
438
+ scid_utils:: tx_index_from_scid ( & scid) % MAX_NAMESPACES as u32 == namespace. as_u8 ( ) as u32
439
+ }
440
+
441
+ #[ cfg( test) ]
442
+ mod tests {
443
+ use bitcoin:: blockdata:: constants:: genesis_block;
444
+ use bitcoin:: network:: constants:: Network ;
445
+ 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 } ;
446
+ use util:: scid_utils;
447
+ use util:: test_utils;
448
+ use sync:: Arc ;
449
+
450
+ const TEST_FAKE_SCID_OFFSET : u8 = 0xAB ;
451
+
452
+ #[ test]
453
+ fn namespace_identifier_is_within_range ( ) {
454
+ let Namespace :: Phantom ( ns) = Namespace :: phantom ( TEST_FAKE_SCID_OFFSET ) ;
455
+ assert ! ( ns <= NAMESPACE_ID_BITMASK ) ;
456
+ }
457
+
458
+ #[ test]
459
+ fn test_segwit_activation_height ( ) {
460
+ let mainnet_genesis = genesis_block ( Network :: Bitcoin ) . header . block_hash ( ) ;
461
+ assert_eq ! ( segwit_activation_height( & mainnet_genesis) , MAINNET_SEGWIT_ACTIVATION_HEIGHT ) ;
462
+
463
+ let testnet_genesis = genesis_block ( Network :: Testnet ) . header . block_hash ( ) ;
464
+ assert_eq ! ( segwit_activation_height( & testnet_genesis) , TEST_SEGWIT_ACTIVATION_HEIGHT ) ;
465
+
466
+ let signet_genesis = genesis_block ( Network :: Signet ) . header . block_hash ( ) ;
467
+ assert_eq ! ( segwit_activation_height( & signet_genesis) , TEST_SEGWIT_ACTIVATION_HEIGHT ) ;
468
+
469
+ let regtest_genesis = genesis_block ( Network :: Regtest ) . header . block_hash ( ) ;
470
+ assert_eq ! ( segwit_activation_height( & regtest_genesis) , TEST_SEGWIT_ACTIVATION_HEIGHT ) ;
471
+ }
472
+
473
+ #[ test]
474
+ fn test_is_valid_phantom ( ) {
475
+ let namespace = Namespace :: Phantom ( 3 ) ;
476
+ let valid_fake_scid = scid_utils:: scid_from_parts ( 0 , 11 , 0 ) . unwrap ( ) ;
477
+ assert ! ( is_valid_phantom( namespace. as_u8( ) , valid_fake_scid) ) ;
478
+ let invalid_fake_scid = scid_utils:: scid_from_parts ( 0 , 12 , 0 ) . unwrap ( ) ;
479
+ assert ! ( !is_valid_phantom( namespace. as_u8( ) , invalid_fake_scid) ) ;
480
+ }
481
+
482
+ #[ test]
483
+ fn test_get_fake_scid ( ) {
484
+ let mainnet_genesis = genesis_block ( Network :: Bitcoin ) . header . block_hash ( ) ;
485
+ let seed = [ 0 as u8 ; 32 ] ;
486
+ let keys_manager = Arc :: new ( test_utils:: TestKeysInterface :: new ( & seed, Network :: Testnet ) ) ;
487
+ let namespace = Namespace :: phantom ( TEST_FAKE_SCID_OFFSET ) ;
488
+ let fake_scid = namespace. get_fake_scid ( 500_000 , & mainnet_genesis, & keys_manager) ;
489
+
490
+ let fake_height = scid_utils:: block_from_scid ( & fake_scid) ;
491
+ assert ! ( fake_height >= MAINNET_SEGWIT_ACTIVATION_HEIGHT ) ;
492
+ assert ! ( fake_height <= 500_000 ) ;
493
+
494
+ let fake_tx_index = scid_utils:: tx_index_from_scid ( & fake_scid) ;
495
+ assert ! ( fake_tx_index <= MAX_TX_INDEX ) ;
496
+
497
+ let fake_vout = fake_scid & scid_utils:: MAX_SCID_VOUT_INDEX ;
498
+ assert ! ( fake_vout <= MAX_VOUT . into( ) ) ;
499
+ }
500
+ }
501
+ }
502
+
341
503
// We hold various information about HTLC relay in the HTLC objects in Channel itself:
342
504
//
343
505
// Upon receipt of an HTLC from a peer, we'll give it a PendingHTLCStatus indicating if it should
@@ -966,6 +1128,8 @@ pub struct ChannelManager<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref,
966
1128
967
1129
inbound_payment_key : inbound_payment:: ExpandedKey ,
968
1130
1131
+ fake_scid_offset : u8 ,
1132
+
969
1133
/// Used to track the last value sent in a node_announcement "timestamp" field. We ensure this
970
1134
/// value increases strictly since we don't assume access to a time source.
971
1135
last_node_announcement_serial : AtomicUsize ,
@@ -1685,6 +1849,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
1685
1849
secp_ctx,
1686
1850
1687
1851
inbound_payment_key : expanded_inbound_key,
1852
+ fake_scid_offset : fake_scid:: get_new_offset ( & keys_manager) ,
1688
1853
1689
1854
last_node_announcement_serial : AtomicUsize :: new ( 0 ) ,
1690
1855
highest_seen_timestamp : AtomicUsize :: new ( 0 ) ,
@@ -5102,6 +5267,26 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
5102
5267
inbound_payment:: get_payment_preimage ( payment_hash, payment_secret, & self . inbound_payment_key )
5103
5268
}
5104
5269
5270
+ /// Gets a fake short channel id for use in receiving phantom node payments.
5271
+ ///
5272
+ /// A phantom node payment is a payment made to a phantom invoice, which is an invoice that can be
5273
+ /// paid to one of multiple nodes. This works because the invoice officially pays to a fake node
5274
+ /// (the "phantom"), with route hints containing phantom channels. This method is used to retrieve
5275
+ /// the short channel ids for these phantom route hints.
5276
+ ///
5277
+ /// While these scids can be reused across invoices, it's best for privacy to use new ones when
5278
+ /// possible.
5279
+ pub fn get_phantom_scid ( & self ) -> u64 {
5280
+ let mut channel_state = self . channel_state . lock ( ) . unwrap ( ) ;
5281
+ let best_block = self . best_block . read ( ) . unwrap ( ) ;
5282
+ let scid_candidate = fake_scid:: get_phantom_scid ( self . fake_scid_offset , best_block. height ( ) , & self . genesis_hash , & self . keys_manager ) ;
5283
+ // Ensure the generated scid doesn't conflict with a real channel.
5284
+ match channel_state. short_to_id . entry ( scid_candidate) {
5285
+ hash_map:: Entry :: Occupied ( _) => scid_candidate + 1 ,
5286
+ hash_map:: Entry :: Vacant ( _) => scid_candidate
5287
+ }
5288
+ }
5289
+
5105
5290
#[ cfg( any( test, feature = "fuzztarget" , feature = "_test_utils" ) ) ]
5106
5291
pub fn get_and_clear_pending_events ( & self ) -> Vec < events:: Event > {
5107
5292
let events = core:: cell:: RefCell :: new ( Vec :: new ( ) ) ;
@@ -6189,6 +6374,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable f
6189
6374
write_tlv_fields ! ( writer, {
6190
6375
( 1 , pending_outbound_payments_no_retry, required) ,
6191
6376
( 3 , pending_outbound_payments, required) ,
6377
+ ( 5 , self . fake_scid_offset, required) ,
6192
6378
} ) ;
6193
6379
6194
6380
Ok ( ( ) )
@@ -6483,10 +6669,16 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
6483
6669
// pending_outbound_payments_no_retry is for compatibility with 0.0.101 clients.
6484
6670
let mut pending_outbound_payments_no_retry: Option < HashMap < PaymentId , HashSet < [ u8 ; 32 ] > > > = None ;
6485
6671
let mut pending_outbound_payments = None ;
6672
+ let mut fake_scid_offset: Option < u8 > = None ;
6486
6673
read_tlv_fields ! ( reader, {
6487
6674
( 1 , pending_outbound_payments_no_retry, option) ,
6488
6675
( 3 , pending_outbound_payments, option) ,
6676
+ ( 5 , fake_scid_offset, option) ,
6489
6677
} ) ;
6678
+ if fake_scid_offset. is_none ( ) {
6679
+ fake_scid_offset = Some ( fake_scid:: get_new_offset ( & args. keys_manager ) ) ;
6680
+ }
6681
+
6490
6682
if pending_outbound_payments. is_none ( ) && pending_outbound_payments_no_retry. is_none ( ) {
6491
6683
pending_outbound_payments = Some ( pending_outbound_payments_compat) ;
6492
6684
} else if pending_outbound_payments. is_none ( ) {
@@ -6569,6 +6761,7 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
6569
6761
inbound_payment_key : expanded_inbound_key,
6570
6762
pending_inbound_payments : Mutex :: new ( pending_inbound_payments) ,
6571
6763
pending_outbound_payments : Mutex :: new ( pending_outbound_payments. unwrap ( ) ) ,
6764
+ fake_scid_offset : fake_scid_offset. unwrap ( ) ,
6572
6765
6573
6766
our_network_key : args. keys_manager . get_node_secret ( ) ,
6574
6767
our_network_pubkey : PublicKey :: from_secret_key ( & secp_ctx, & args. keys_manager . get_node_secret ( ) ) ,
0 commit comments