@@ -338,6 +338,169 @@ mod inbound_payment {
338338 }
339339}
340340
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 unique 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+ println ! ( "VMW: tx index of phantom scid: {}, phantom namespace: {}" , scid_utils:: tx_index_from_scid( & scid) , namespace. as_u8( ) ) ;
439+ scid_utils:: tx_index_from_scid ( & scid) % MAX_NAMESPACES as u32 == namespace. as_u8 ( ) as u32
440+ }
441+
442+ #[ cfg( test) ]
443+ mod tests {
444+ use bitcoin:: blockdata:: constants:: genesis_block;
445+ use bitcoin:: network:: constants:: Network ;
446+ 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 } ;
447+ use util:: scid_utils;
448+ use util:: test_utils;
449+ use sync:: Arc ;
450+
451+ const TEST_FAKE_SCID_OFFSET : u8 = 0xAB ;
452+
453+ #[ test]
454+ fn namespace_identifier_is_within_range ( ) {
455+ let Namespace :: Phantom ( ns) = Namespace :: phantom ( TEST_FAKE_SCID_OFFSET ) ;
456+ assert ! ( ns <= NAMESPACE_ID_BITMASK ) ;
457+ }
458+
459+ #[ test]
460+ fn test_segwit_activation_height ( ) {
461+ let mainnet_genesis = genesis_block ( Network :: Bitcoin ) . header . block_hash ( ) ;
462+ assert_eq ! ( segwit_activation_height( & mainnet_genesis) , MAINNET_SEGWIT_ACTIVATION_HEIGHT ) ;
463+
464+ let testnet_genesis = genesis_block ( Network :: Testnet ) . header . block_hash ( ) ;
465+ assert_eq ! ( segwit_activation_height( & testnet_genesis) , TEST_SEGWIT_ACTIVATION_HEIGHT ) ;
466+
467+ let signet_genesis = genesis_block ( Network :: Signet ) . header . block_hash ( ) ;
468+ assert_eq ! ( segwit_activation_height( & signet_genesis) , TEST_SEGWIT_ACTIVATION_HEIGHT ) ;
469+
470+ let regtest_genesis = genesis_block ( Network :: Regtest ) . header . block_hash ( ) ;
471+ assert_eq ! ( segwit_activation_height( & regtest_genesis) , TEST_SEGWIT_ACTIVATION_HEIGHT ) ;
472+ }
473+
474+ #[ test]
475+ fn test_is_valid_phantom ( ) {
476+ let namespace = Namespace :: Phantom ( 3 ) ;
477+ let valid_fake_scid = scid_utils:: scid_from_parts ( 0 , 11 , 0 ) . unwrap ( ) ;
478+ assert ! ( is_valid_phantom( namespace. as_u8( ) , valid_fake_scid) ) ;
479+ let invalid_fake_scid = scid_utils:: scid_from_parts ( 0 , 12 , 0 ) . unwrap ( ) ;
480+ assert ! ( !is_valid_phantom( namespace. as_u8( ) , invalid_fake_scid) ) ;
481+ }
482+
483+ #[ test]
484+ fn test_get_fake_scid ( ) {
485+ let mainnet_genesis = genesis_block ( Network :: Bitcoin ) . header . block_hash ( ) ;
486+ let seed = [ 0 as u8 ; 32 ] ;
487+ let keys_manager = Arc :: new ( test_utils:: TestKeysInterface :: new ( & seed, Network :: Testnet ) ) ;
488+ let namespace = Namespace :: phantom ( TEST_FAKE_SCID_OFFSET ) ;
489+ let fake_scid = namespace. get_fake_scid ( 500_000 , & mainnet_genesis, & keys_manager) ;
490+
491+ let fake_height = scid_utils:: block_from_scid ( & fake_scid) ;
492+ assert ! ( fake_height >= MAINNET_SEGWIT_ACTIVATION_HEIGHT ) ;
493+ assert ! ( fake_height <= 500_000 ) ;
494+
495+ let fake_tx_index = scid_utils:: tx_index_from_scid ( & fake_scid) ;
496+ assert ! ( fake_tx_index <= MAX_TX_INDEX ) ;
497+
498+ let fake_vout = fake_scid & scid_utils:: MAX_SCID_VOUT_INDEX ;
499+ assert ! ( fake_vout <= MAX_VOUT . into( ) ) ;
500+ }
501+ }
502+ }
503+
341504// We hold various information about HTLC relay in the HTLC objects in Channel itself:
342505//
343506// Upon receipt of an HTLC from a peer, we'll give it a PendingHTLCStatus indicating if it should
@@ -966,6 +1129,8 @@ pub struct ChannelManager<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref,
9661129
9671130 inbound_payment_key : inbound_payment:: ExpandedKey ,
9681131
1132+ fake_scid_offset : u8 ,
1133+
9691134 /// Used to track the last value sent in a node_announcement "timestamp" field. We ensure this
9701135 /// value increases strictly since we don't assume access to a time source.
9711136 last_node_announcement_serial : AtomicUsize ,
@@ -1685,6 +1850,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
16851850 secp_ctx,
16861851
16871852 inbound_payment_key : expanded_inbound_key,
1853+ fake_scid_offset : fake_scid:: get_new_offset ( & keys_manager) ,
16881854
16891855 last_node_announcement_serial : AtomicUsize :: new ( 0 ) ,
16901856 highest_seen_timestamp : AtomicUsize :: new ( 0 ) ,
@@ -5102,6 +5268,26 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
51025268 inbound_payment:: get_payment_preimage ( payment_hash, payment_secret, & self . inbound_payment_key )
51035269 }
51045270
5271+ /// Gets a fake short channel id for use in receiving phantom node payments.
5272+ ///
5273+ /// A phantom node payment is a payment made to a phantom invoice, which is an invoice that can be
5274+ /// paid to one of multiple nodes. This works because the invoice officially pays to a fake node
5275+ /// (the "phantom"), with route hints containing phantom channels. This method is used to retrieve
5276+ /// the short channel ids for these phantom route hints.
5277+ ///
5278+ /// These scids are not meant to be reused across invoices and should be fetched anew for each new
5279+ /// invoice.
5280+ pub fn get_phantom_scid ( & self ) -> u64 {
5281+ let mut channel_state = self . channel_state . lock ( ) . unwrap ( ) ;
5282+ let best_block = self . best_block . read ( ) . unwrap ( ) ;
5283+ let scid_candidate = fake_scid:: get_phantom_scid ( self . fake_scid_offset , best_block. height ( ) , & self . genesis_hash , & self . keys_manager ) ;
5284+ // Ensure the generated scid doesn't conflict with a real channel.
5285+ match channel_state. short_to_id . entry ( scid_candidate) {
5286+ hash_map:: Entry :: Occupied ( _) => scid_candidate + 1 ,
5287+ hash_map:: Entry :: Vacant ( _) => scid_candidate
5288+ }
5289+ }
5290+
51055291 #[ cfg( any( test, feature = "fuzztarget" , feature = "_test_utils" ) ) ]
51065292 pub fn get_and_clear_pending_events ( & self ) -> Vec < events:: Event > {
51075293 let events = core:: cell:: RefCell :: new ( Vec :: new ( ) ) ;
@@ -6189,6 +6375,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable f
61896375 write_tlv_fields ! ( writer, {
61906376 ( 1 , pending_outbound_payments_no_retry, required) ,
61916377 ( 3 , pending_outbound_payments, required) ,
6378+ ( 5 , self . fake_scid_offset, required) ,
61926379 } ) ;
61936380
61946381 Ok ( ( ) )
@@ -6483,10 +6670,16 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
64836670 // pending_outbound_payments_no_retry is for compatibility with 0.0.101 clients.
64846671 let mut pending_outbound_payments_no_retry: Option < HashMap < PaymentId , HashSet < [ u8 ; 32 ] > > > = None ;
64856672 let mut pending_outbound_payments = None ;
6673+ let mut fake_scid_offset: Option < u8 > = None ;
64866674 read_tlv_fields ! ( reader, {
64876675 ( 1 , pending_outbound_payments_no_retry, option) ,
64886676 ( 3 , pending_outbound_payments, option) ,
6677+ ( 5 , fake_scid_offset, option) ,
64896678 } ) ;
6679+ if fake_scid_offset. is_none ( ) {
6680+ fake_scid_offset = Some ( fake_scid:: get_new_offset ( & args. keys_manager ) ) ;
6681+ }
6682+
64906683 if pending_outbound_payments. is_none ( ) && pending_outbound_payments_no_retry. is_none ( ) {
64916684 pending_outbound_payments = Some ( pending_outbound_payments_compat) ;
64926685 } else if pending_outbound_payments. is_none ( ) {
@@ -6569,6 +6762,7 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
65696762 inbound_payment_key : expanded_inbound_key,
65706763 pending_inbound_payments : Mutex :: new ( pending_inbound_payments) ,
65716764 pending_outbound_payments : Mutex :: new ( pending_outbound_payments. unwrap ( ) ) ,
6765+ fake_scid_offset : fake_scid_offset. unwrap ( ) ,
65726766
65736767 our_network_key : args. keys_manager . get_node_secret ( ) ,
65746768 our_network_pubkey : PublicKey :: from_secret_key ( & secp_ctx, & args. keys_manager . get_node_secret ( ) ) ,
0 commit comments