diff --git a/pool-apps/pool/src/lib/channel_manager/mining_message_handler.rs b/pool-apps/pool/src/lib/channel_manager/mining_message_handler.rs index 643716fbc..9b87a6f67 100644 --- a/pool-apps/pool/src/lib/channel_manager/mining_message_handler.rs +++ b/pool-apps/pool/src/lib/channel_manager/mining_message_handler.rs @@ -1,8 +1,9 @@ +use std::str::FromStr; use std::sync::atomic::Ordering; use stratum_apps::stratum_core::{ binary_sv2::Str0255, - bitcoin::{consensus::Decodable, Amount, Target, TxOut}, + bitcoin::{consensus::Decodable, Address, Amount, Target, TxOut}, channels_sv2::{ server::{ error::{ExtendedChannelError, StandardChannelError}, @@ -141,10 +142,23 @@ impl HandleMiningMessagesFromClientAsync for ChannelManager { return Err(PoolError::disconnect(PoolErrorKind::LastNewPrevhashNotFound, downstream_id)); }; + // Determine the script pubkey for the coinbase output + // If SOLO mining is enabled, we try to use the address provided by the miner + let script_pubkey = if self.solo_mining { + match Address::from_str(&user_identity) { + Ok(addr) => addr.assume_checked().script_pubkey(), + Err(_) => { + error!("SOLO Mining Error: Invalid address '{}' provided by user. Fallback to pool reward address.", user_identity); + self.coinbase_reward_script.script_pubkey().clone() + } + } + } else { + self.coinbase_reward_script.script_pubkey().clone() + }; let pool_coinbase_output = TxOut { value: Amount::from_sat(last_future_template.coinbase_tx_value_remaining), - script_pubkey: self.coinbase_reward_script.script_pubkey(), + script_pubkey, }; downstream.downstream_data.super_safe_lock(|downstream_data| { @@ -435,11 +449,25 @@ impl HandleMiningMessagesFromClientAsync for ChannelManager { // future extended job // and the SetNewPrevHash message } else { + // Determine the script pubkey for the coinbase output + // If SOLO mining is enabled, we try to use the address provided by the miner + let script_pubkey = if self.solo_mining { + match Address::from_str(&user_identity) { + Ok(addr) => addr.assume_checked().script_pubkey(), + Err(_) => { + error!("SOLO Mining Error: Invalid address '{}' provided by user. Fallback to pool reward address.", user_identity); + self.coinbase_reward_script.script_pubkey().clone() + } + } + } else { + self.coinbase_reward_script.script_pubkey().clone() + }; + let pool_coinbase_output = TxOut { value: Amount::from_sat( last_future_template.coinbase_tx_value_remaining, ), - script_pubkey: self.coinbase_reward_script.script_pubkey(), + script_pubkey, }; extended_channel.on_new_template( diff --git a/pool-apps/pool/src/lib/channel_manager/mod.rs b/pool-apps/pool/src/lib/channel_manager/mod.rs index 594222519..bde78b4b6 100644 --- a/pool-apps/pool/src/lib/channel_manager/mod.rs +++ b/pool-apps/pool/src/lib/channel_manager/mod.rs @@ -98,6 +98,8 @@ pub struct ChannelManager { share_batch_size: usize, shares_per_minute: SharesPerMinute, coinbase_reward_script: CoinbaseRewardScript, + /// Indicates if SOLO mining mode is enabled. + solo_mining: bool, /// Protocol extensions that the pool supports (will accept if requested by clients). supported_extensions: Vec, /// Protocol extensions that the pool requires (clients must support these). @@ -163,6 +165,7 @@ impl ChannelManager { shares_per_minute: config.shares_per_minute(), pool_tag_string: config.pool_signature().to_string(), coinbase_reward_script: config.coinbase_reward_script().clone(), + solo_mining: config.solo_mining(), supported_extensions: config.supported_extensions().to_vec(), required_extensions: config.required_extensions().to_vec(), }; diff --git a/pool-apps/pool/src/lib/config.rs b/pool-apps/pool/src/lib/config.rs index a013a82af..4e692e6c9 100644 --- a/pool-apps/pool/src/lib/config.rs +++ b/pool-apps/pool/src/lib/config.rs @@ -40,6 +40,12 @@ pub struct PoolConfig { required_extensions: Vec, #[serde(default)] monitoring_address: Option, + + /// Enable SOLO mining mode. + /// In this mode, the block reward is sent to the address provided by the miner + /// in the 'user_identity' field. + #[serde(default)] + solo_mining: bool, } impl PoolConfig { @@ -75,6 +81,7 @@ impl PoolConfig { supported_extensions, required_extensions, monitoring_address: None, + solo_mining: false, } } @@ -165,6 +172,11 @@ impl PoolConfig { pub fn monitoring_address(&self) -> Option { self.monitoring_address } + + /// Returns true if SOLO mining mode is enabled. + pub fn solo_mining(&self) -> bool { + self.solo_mining + } } /// Pool's authority public and secret keys.