@@ -338,6 +338,157 @@ 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 ln:: channelmanager:: DecodeError ;
350
+ use util:: scid_utils;
351
+ use util:: ser:: Readable ;
352
+
353
+ use core:: convert:: TryInto ;
354
+ use core:: ops:: Deref ;
355
+
356
+ const TEST_SEGWIT_ACTIVATION_HEIGHT : u32 = 0 ;
357
+ const MAINNET_SEGWIT_ACTIVATION_HEIGHT : u32 = 481_824 ;
358
+ const MAX_TX_INDEX : u32 = 2_500 ;
359
+ const MAX_VOUT : u16 = 10_000 ;
360
+ const MAX_NAMESPACES : u8 = 8 ;
361
+ const NAMESPACE_ID_BITMASK : u8 = 0b111 ;
362
+
363
+ /// Fake scids are divided into namespaces, with each namespace having its own randomly-selected
364
+ /// identifier between [0..7], such that `scid_tx_index % MAX_NAMESPACES == namespace_identifier`.
365
+ /// This allows us to identify what namespace a fake scid corresponds to upon HTLC receipt, and
366
+ /// handle the HTLC accordingly.
367
+ pub ( super ) enum Namespace {
368
+ Phantom ( u8 ) ,
369
+ // Coming soon: a variant for the zero-conf scid namespace
370
+ }
371
+
372
+ impl Namespace {
373
+ /// We generate "realistic-looking" random scids here, meaning the scid's block height is
374
+ /// between segwit activation and the current best known height, and the tx index and output
375
+ /// index are also selected from a "reasonable" range. We add this logic because it makes it
376
+ /// non-obvious at a glance that the scid is fake, e.g. if it appears in invoice route hints.
377
+ pub ( super ) fn get_fake_scid < Signer : Sign , K : Deref > ( & self , highest_seen_blockheight : u32 , genesis_hash : & BlockHash , keys_manager : & K ) -> u64
378
+ where K :: Target : KeysInterface < Signer = Signer > ,
379
+ {
380
+ let scid_namespace = self . as_u8 ( ) ;
381
+ let rand_bytes = keys_manager. get_secure_random_bytes ( ) ;
382
+
383
+ let valid_block_range = highest_seen_blockheight - segwit_activation_height ( genesis_hash) ;
384
+ let rand_value_for_height = u32:: from_be_bytes ( rand_bytes[ ..4 ] . try_into ( ) . unwrap ( ) ) ;
385
+ let fake_scid_height = segwit_activation_height ( genesis_hash) + rand_value_for_height % valid_block_range;
386
+
387
+ let rand_i32 = i32:: from_be_bytes ( rand_bytes[ 4 ..8 ] . try_into ( ) . unwrap ( ) ) . abs ( ) ;
388
+ let rand_tx_index = rand_i32 % ( MAX_TX_INDEX as i32 ) ;
389
+ // Adding this offset to `rand_tx_index` will put it into the given `scid_namespace`.
390
+ let offset = ( scid_namespace as i32 - rand_tx_index) % ( MAX_NAMESPACES as i32 ) ;
391
+ let fake_scid_tx_index: u64 = ( rand_tx_index + offset) as u64 ;
392
+
393
+ let rand_value_for_vout = u16:: from_be_bytes ( rand_bytes[ 8 ..10 ] . try_into ( ) . unwrap ( ) ) ;
394
+ let fake_scid_vout_index = rand_value_for_vout % MAX_VOUT ;
395
+ scid_utils:: scid_from_parts ( fake_scid_height as u64 , fake_scid_tx_index, fake_scid_vout_index as u64 ) . unwrap ( )
396
+ }
397
+
398
+ fn as_u8 ( & self ) -> u8 {
399
+ match self {
400
+ Namespace :: Phantom ( namespace) => * namespace,
401
+ }
402
+ }
403
+
404
+ pub ( super ) fn phantom < Signer : Sign , K : Deref > ( keys_manager : & K ) -> Namespace
405
+ where K :: Target : KeysInterface < Signer = Signer > ,
406
+ {
407
+ let phantom_scid_namespace_rand_bytes = keys_manager. get_secure_random_bytes ( ) ;
408
+ let phantom_scid_namespace = phantom_scid_namespace_rand_bytes[ 0 ] & NAMESPACE_ID_BITMASK ;
409
+ Namespace :: Phantom ( phantom_scid_namespace)
410
+ }
411
+ }
412
+
413
+ fn segwit_activation_height ( genesis : & BlockHash ) -> u32 {
414
+ if genesis_block ( Network :: Bitcoin ) . header . block_hash ( ) == * genesis {
415
+ MAINNET_SEGWIT_ACTIVATION_HEIGHT
416
+ } else {
417
+ TEST_SEGWIT_ACTIVATION_HEIGHT
418
+ }
419
+ }
420
+
421
+ /// Returns whether the given fake scid falls into the given namespace.
422
+ pub ( super ) fn is_valid ( namespace : & Namespace , scid : u64 ) -> bool {
423
+ scid_utils:: tx_index_from_scid ( & scid) % MAX_NAMESPACES as u32 == namespace. as_u8 ( ) as u32
424
+ }
425
+
426
+ impl_writeable_tlv_based_enum ! ( Namespace , ;
427
+ ( 0 , Phantom ) ,
428
+ ) ;
429
+
430
+ #[ cfg( test) ]
431
+ mod tests {
432
+ use bitcoin:: blockdata:: constants:: genesis_block;
433
+ use bitcoin:: network:: constants:: Network ;
434
+ use ln:: channelmanager:: fake_scid:: { is_valid, MAINNET_SEGWIT_ACTIVATION_HEIGHT , MAX_TX_INDEX , MAX_VOUT , Namespace , NAMESPACE_ID_BITMASK , segwit_activation_height, TEST_SEGWIT_ACTIVATION_HEIGHT } ;
435
+ use util:: scid_utils;
436
+ use util:: test_utils;
437
+ use sync:: Arc ;
438
+
439
+ #[ test]
440
+ fn namespace_identifier_is_within_range ( ) {
441
+ let seed = [ 0 as u8 ; 32 ] ;
442
+ let keys_manager = Arc :: new ( test_utils:: TestKeysInterface :: new ( & seed, Network :: Testnet ) ) ;
443
+ let Namespace :: Phantom ( ns) = Namespace :: phantom ( & keys_manager) ;
444
+ assert ! ( ns <= NAMESPACE_ID_BITMASK ) ;
445
+ }
446
+
447
+ #[ test]
448
+ fn test_segwit_activation_height ( ) {
449
+ let mainnet_genesis = genesis_block ( Network :: Bitcoin ) . header . block_hash ( ) ;
450
+ assert_eq ! ( segwit_activation_height( & mainnet_genesis) , MAINNET_SEGWIT_ACTIVATION_HEIGHT ) ;
451
+
452
+ let testnet_genesis = genesis_block ( Network :: Testnet ) . header . block_hash ( ) ;
453
+ assert_eq ! ( segwit_activation_height( & testnet_genesis) , TEST_SEGWIT_ACTIVATION_HEIGHT ) ;
454
+
455
+ let signet_genesis = genesis_block ( Network :: Signet ) . header . block_hash ( ) ;
456
+ assert_eq ! ( segwit_activation_height( & signet_genesis) , TEST_SEGWIT_ACTIVATION_HEIGHT ) ;
457
+
458
+ let regtest_genesis = genesis_block ( Network :: Regtest ) . header . block_hash ( ) ;
459
+ assert_eq ! ( segwit_activation_height( & regtest_genesis) , TEST_SEGWIT_ACTIVATION_HEIGHT ) ;
460
+ }
461
+
462
+ #[ test]
463
+ fn test_is_valid ( ) {
464
+ let namespace = Namespace :: Phantom ( 3 ) ;
465
+ let valid_fake_scid = scid_utils:: scid_from_parts ( 0 , 11 , 0 ) . unwrap ( ) ;
466
+ assert ! ( is_valid( & namespace, valid_fake_scid) ) ;
467
+ let invalid_fake_scid = scid_utils:: scid_from_parts ( 0 , 12 , 0 ) . unwrap ( ) ;
468
+ assert ! ( !is_valid( & namespace, invalid_fake_scid) ) ;
469
+ }
470
+
471
+ #[ test]
472
+ fn test_get_fake_scid ( ) {
473
+ let mainnet_genesis = genesis_block ( Network :: Bitcoin ) . header . block_hash ( ) ;
474
+ let seed = [ 0 as u8 ; 32 ] ;
475
+ let keys_manager = Arc :: new ( test_utils:: TestKeysInterface :: new ( & seed, Network :: Testnet ) ) ;
476
+ let namespace = Namespace :: phantom ( & keys_manager) ;
477
+ let fake_scid = namespace. get_fake_scid ( 500_000 , & mainnet_genesis, & keys_manager) ;
478
+
479
+ let fake_height = scid_utils:: block_from_scid ( & fake_scid) ;
480
+ assert ! ( fake_height >= MAINNET_SEGWIT_ACTIVATION_HEIGHT ) ;
481
+ assert ! ( fake_height <= 500_000 ) ;
482
+
483
+ let fake_tx_index = scid_utils:: tx_index_from_scid ( & fake_scid) ;
484
+ assert ! ( fake_tx_index <= MAX_TX_INDEX ) ;
485
+
486
+ let fake_vout = fake_scid & scid_utils:: MAX_SCID_VOUT_INDEX ;
487
+ assert ! ( fake_vout <= MAX_VOUT . into( ) ) ;
488
+ }
489
+ }
490
+ }
491
+
341
492
// We hold various information about HTLC relay in the HTLC objects in Channel itself:
342
493
//
343
494
// Upon receipt of an HTLC from a peer, we'll give it a PendingHTLCStatus indicating if it should
@@ -966,6 +1117,8 @@ pub struct ChannelManager<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref,
966
1117
967
1118
inbound_payment_key : inbound_payment:: ExpandedKey ,
968
1119
1120
+ phantom_scid_namespace : fake_scid:: Namespace ,
1121
+
969
1122
/// Used to track the last value sent in a node_announcement "timestamp" field. We ensure this
970
1123
/// value increases strictly since we don't assume access to a time source.
971
1124
last_node_announcement_serial : AtomicUsize ,
@@ -1661,6 +1814,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
1661
1814
secp_ctx. seeded_randomize ( & keys_manager. get_secure_random_bytes ( ) ) ;
1662
1815
let inbound_pmt_key_material = keys_manager. get_inbound_payment_key_material ( ) ;
1663
1816
let expanded_inbound_key = inbound_payment:: ExpandedKey :: new ( & inbound_pmt_key_material) ;
1817
+ let phantom_scid_namespace = fake_scid:: Namespace :: phantom ( & keys_manager) ;
1664
1818
ChannelManager {
1665
1819
default_configuration : config. clone ( ) ,
1666
1820
genesis_hash : genesis_block ( params. network ) . header . block_hash ( ) ,
@@ -1686,6 +1840,8 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
1686
1840
1687
1841
inbound_payment_key : expanded_inbound_key,
1688
1842
1843
+ phantom_scid_namespace,
1844
+
1689
1845
last_node_announcement_serial : AtomicUsize :: new ( 0 ) ,
1690
1846
highest_seen_timestamp : AtomicUsize :: new ( 0 ) ,
1691
1847
@@ -6189,6 +6345,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable f
6189
6345
write_tlv_fields ! ( writer, {
6190
6346
( 1 , pending_outbound_payments_no_retry, required) ,
6191
6347
( 3 , pending_outbound_payments, required) ,
6348
+ ( 5 , self . phantom_scid_namespace, required) ,
6192
6349
} ) ;
6193
6350
6194
6351
Ok ( ( ) )
@@ -6483,10 +6640,16 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
6483
6640
// pending_outbound_payments_no_retry is for compatibility with 0.0.101 clients.
6484
6641
let mut pending_outbound_payments_no_retry: Option < HashMap < PaymentId , HashSet < [ u8 ; 32 ] > > > = None ;
6485
6642
let mut pending_outbound_payments = None ;
6643
+ let mut phantom_scid_namespace: Option < fake_scid:: Namespace > = None ;
6486
6644
read_tlv_fields ! ( reader, {
6487
6645
( 1 , pending_outbound_payments_no_retry, option) ,
6488
6646
( 3 , pending_outbound_payments, option) ,
6647
+ ( 5 , phantom_scid_namespace, option) ,
6489
6648
} ) ;
6649
+ if phantom_scid_namespace. is_none ( ) {
6650
+ phantom_scid_namespace = Some ( fake_scid:: Namespace :: phantom ( & args. keys_manager ) ) ;
6651
+ }
6652
+
6490
6653
if pending_outbound_payments. is_none ( ) && pending_outbound_payments_no_retry. is_none ( ) {
6491
6654
pending_outbound_payments = Some ( pending_outbound_payments_compat) ;
6492
6655
} else if pending_outbound_payments. is_none ( ) {
@@ -6569,6 +6732,7 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
6569
6732
inbound_payment_key : expanded_inbound_key,
6570
6733
pending_inbound_payments : Mutex :: new ( pending_inbound_payments) ,
6571
6734
pending_outbound_payments : Mutex :: new ( pending_outbound_payments. unwrap ( ) ) ,
6735
+ phantom_scid_namespace : phantom_scid_namespace. unwrap ( ) ,
6572
6736
6573
6737
our_network_key : args. keys_manager . get_node_secret ( ) ,
6574
6738
our_network_pubkey : PublicKey :: from_secret_key ( & secp_ctx, & args. keys_manager . get_node_secret ( ) ) ,
0 commit comments