@@ -19,6 +19,7 @@ use stratum_apps::{
1919 stratum_core:: {
2020 bitcoin:: { Amount , TxOut } ,
2121 channels_sv2:: {
22+ extranonce_manager:: ExtranonceAllocator ,
2223 server:: {
2324 extended:: ExtendedChannel ,
2425 group:: GroupChannel ,
@@ -30,7 +31,7 @@ use stratum_apps::{
3031 handlers_sv2:: {
3132 HandleMiningMessagesFromClientAsync , HandleTemplateDistributionMessagesFromServerAsync ,
3233 } ,
33- mining_sv2:: { ExtendedExtranonce , SetTarget } ,
34+ mining_sv2:: SetTarget ,
3435 parsers_sv2:: { Mining , TemplateDistribution , Tlv } ,
3536 template_distribution_sv2:: { NewTemplate , SetNewPrevHash } ,
3637 } ,
@@ -60,12 +61,11 @@ pub struct ChannelManagerData {
6061 // Mapping of `downstream_id` → `Downstream` object,
6162 // used by the channel manager to locate and interact with downstream clients.
6263 pub ( crate ) downstream : HashMap < DownstreamId , Downstream > ,
63- // Extranonce prefix factory for **extended downstream channels**.
64- // Each new extended downstream receives a unique extranonce prefix.
65- extranonce_prefix_factory_extended : ExtendedExtranonce ,
66- // Extranonce prefix factory for **standard downstream channels**.
67- // Each new standard downstream receives a unique extranonce prefix.
68- extranonce_prefix_factory_standard : ExtendedExtranonce ,
64+ // Extranonce allocator for both standard and extended downstream channels.
65+ // Each new downstream channel receives a unique extranonce prefix.
66+ pub ( crate ) extranonce_allocator : ExtranonceAllocator ,
67+ // Mapping of `(downstream_id, channel_id)` → `local_index` for freeing extranonces.
68+ pub ( crate ) channel_to_local_index : HashMap < ( DownstreamId , ChannelId ) , usize > ,
6969 // Factory that assigns a unique ID to each new **downstream connection**.
7070 downstream_id_factory : AtomicUsize ,
7171 // Mapping of `(downstream_id, channel_id)` → vardiff controller.
@@ -118,32 +118,21 @@ impl ChannelManager {
118118 coinbase_outputs : Vec < u8 > ,
119119 job_declarator : Option < JobDeclarator > ,
120120 ) -> PoolResult < Self , error:: ChannelManager > {
121- let range_0 = 0 ..0 ;
122- let range_1 = 0 ..POOL_ALLOCATION_BYTES ;
123- let range_2 = POOL_ALLOCATION_BYTES ..POOL_ALLOCATION_BYTES + CLIENT_SEARCH_SPACE_BYTES ;
124-
125- let make_extranonce_factory = || {
126- // simulating a scenario where there are multiple mining servers
127- // this static prefix allows unique extranonce_prefix allocation
128- // for this mining server
129- let static_prefix = config. server_id ( ) . to_be_bytes ( ) . to_vec ( ) ;
130-
131- ExtendedExtranonce :: new (
132- range_0. clone ( ) ,
133- range_1. clone ( ) ,
134- range_2. clone ( ) ,
135- Some ( static_prefix) ,
136- )
137- . expect ( "Failed to create ExtendedExtranonce with valid ranges" )
138- } ;
121+ // Server ID is used to distinguish multiple pool server instances.
122+ // It takes 2 bytes of the local_prefix, leaving 2 bytes for channel IDs (65,536 channels).
123+ let server_id = config. server_id ( ) . to_be_bytes ( ) . to_vec ( ) ;
139124
140- let extranonce_prefix_factory_extended = make_extranonce_factory ( ) ;
141- let extranonce_prefix_factory_standard = make_extranonce_factory ( ) ;
125+ let extranonce_allocator = ExtranonceAllocator :: new (
126+ FULL_EXTRANONCE_SIZE , // 20 bytes total extranonce
127+ Some ( server_id) , // 2-byte server identifier
128+ 65_536 , // max concurrent channels (2^16, fits in 2 bytes)
129+ )
130+ . expect ( "Failed to create ExtranonceAllocator with valid configuration" ) ;
142131
143132 let channel_manager_data = Arc :: new ( Mutex :: new ( ChannelManagerData {
144133 downstream : HashMap :: new ( ) ,
145- extranonce_prefix_factory_extended ,
146- extranonce_prefix_factory_standard ,
134+ extranonce_allocator ,
135+ channel_to_local_index : HashMap :: new ( ) ,
147136 downstream_id_factory : AtomicUsize :: new ( 1 ) ,
148137 vardiff : HashMap :: new ( ) ,
149138 coinbase_outputs,
@@ -442,6 +431,19 @@ impl ChannelManager {
442431 cm_data
443432 . vardiff
444433 . retain ( |key, _| key. downstream_id != downstream_id) ;
434+ // Free extranonce prefixes for all channels belonging to this downstream
435+ let prefix_ids_to_free: Vec < usize > = cm_data
436+ . channel_to_local_index
437+ . iter ( )
438+ . filter ( |( ( ds_id, _) , _) | * ds_id == downstream_id)
439+ . map ( |( _, prefix_id) | * prefix_id)
440+ . collect ( ) ;
441+ for prefix_id in prefix_ids_to_free {
442+ cm_data. extranonce_allocator . free ( prefix_id) ;
443+ }
444+ cm_data
445+ . channel_to_local_index
446+ . retain ( |( ds_id, _) , _| * ds_id != downstream_id) ;
445447 } ) ;
446448 self . channel_manager_channel
447449 . downstream_sender
0 commit comments