Prechádzať zdrojové kódy

minerd: don't cache next mining key VMs to preserve memory as init is fast enough now

skoupidi 7 mesiacov pred
rodič
commit
179fa08d87

+ 1 - 1
bin/darkfid/src/rpc.rs

@@ -84,7 +84,7 @@ impl RequestHandler<DefaultRpcHandler> for DarkfiNode {
             // =============
             // Miner methods
             // =============
-            "miner.get_current_randomx_keys" => self.miner_get_current_randomx_keys(req.id, req.params).await,
+            "miner.get_current_mining_randomx_key" => self.miner_get_current_mining_randomx_key(req.id, req.params).await,
             "miner.get_header" => self.miner_get_header(req.id, req.params).await,
             "miner.submit_solution" => self.miner_submit_solution(req.id, req.params).await,
 

+ 21 - 30
bin/darkfid/src/rpc_miner.rs

@@ -66,8 +66,6 @@ pub struct BlockTemplate {
     pub block: BlockInfo,
     /// The base64 encoded RandomX key used
     randomx_key: String,
-    /// The base64 encoded next RandomX key used
-    next_randomx_key: String,
     /// The base64 encoded mining target used
     target: String,
     /// The signing secret for this block
@@ -76,21 +74,24 @@ pub struct BlockTemplate {
 
 impl DarkfiNode {
     // RPCAPI:
-    // Queries the validator for the current and next RandomX keys.
-    // If no forks exist, retrieves the canonical ones.
-    // Returns the current and next RandomX keys, both encoded as
-    // base64 strings.
+    // Queries the validator for the current mining RandomX key,
+    // based on next block height.
+    // If no forks exist, returns the canonical key.
+    // Returns the current mining RandomX key encoded as base64 string.
     //
     // **Params:**
     // * `None`
     //
     // **Returns:**
-    // * `String`: Current RandomX key (base64 encoded)
-    // * `String`: Current next RandomX key (base64 encoded)
+    // * `String`: Current mining RandomX key (base64 encoded)
     //
-    // --> {"jsonrpc": "2.0", "method": "miner.get_current_randomx_keys", "params": [], "id": 1}
-    // <-- {"jsonrpc": "2.0", "result": ["randomx_key", "next_randomx_key"], "id": 1}
-    pub async fn miner_get_current_randomx_keys(&self, id: u16, params: JsonValue) -> JsonResult {
+    // --> {"jsonrpc": "2.0", "method": "miner.get_current_mining_randomx_key", "params": [], "id": 1}
+    // <-- {"jsonrpc": "2.0", "result": ["randomx_key"], "id": 1}
+    pub async fn miner_get_current_mining_randomx_key(
+        &self,
+        id: u16,
+        params: JsonValue,
+    ) -> JsonResult {
         // Verify request params
         let Some(params) = params.get::<Vec<JsonValue>>() else {
             return JsonError::new(InvalidParams, None, id).into()
@@ -99,23 +100,22 @@ impl DarkfiNode {
             return JsonError::new(InvalidParams, None, id).into()
         }
 
-        // Grab current RandomX keys
-        let (randomx_key, next_randomx_key) = match self.validator.current_randomx_keys().await {
-            Ok(keys) => keys,
+        // Grab current mining RandomX key
+        let randomx_key = match self.validator.current_mining_randomx_key().await {
+            Ok(key) => key,
             Err(e) => {
                 error!(
-                    target: "darkfid::rpc::miner_get_current_randomx_keys",
-                    "[RPC] Retrieving current RandomX keys failed: {e}",
+                    target: "darkfid::rpc::current_mining_randomx_key",
+                    "[RPC] Retrieving current mining RandomX key failed: {e}",
                 );
                 return JsonError::new(ErrorCode::InternalError, None, id).into()
             }
         };
 
         // Encode them and build response
-        let response = JsonValue::Array(vec![
-            JsonValue::String(base64::encode(&serialize_async(&randomx_key).await)),
-            JsonValue::String(base64::encode(&serialize_async(&next_randomx_key).await)),
-        ]);
+        let response = JsonValue::Array(vec![JsonValue::String(base64::encode(
+            &serialize_async(&randomx_key).await,
+        ))]);
 
         JsonResponse::new(response, id).into()
     }
@@ -134,12 +134,11 @@ impl DarkfiNode {
     //
     // **Returns:**
     // * `String`: Current best fork RandomX key (base64 encoded)
-    // * `String`: Current best fork next RandomX key (base64 encoded)
     // * `String`: Current best fork mining target (base64 encoded)
     // * `String`: Current best fork next block header (base64 encoded)
     //
     // --> {"jsonrpc": "2.0", "method": "miner.get_header", "params": {"header": "hash", "recipient": "address"}, "id": 1}
-    // <-- {"jsonrpc": "2.0", "result": ["randomx_key", "next_randomx_key", "target", "header"], "id": 1}
+    // <-- {"jsonrpc": "2.0", "result": ["randomx_key", "target", "header"], "id": 1}
     pub async fn miner_get_header(&self, id: u16, params: JsonValue) -> JsonResult {
         // Check if node is synced before responding to miner
         if !*self.validator.synced.read().await {
@@ -250,7 +249,6 @@ impl DarkfiNode {
                     JsonResponse::new(
                         JsonValue::Array(vec![
                             JsonValue::String(blocktemplate.randomx_key.clone()),
-                            JsonValue::String(blocktemplate.next_randomx_key.clone()),
                             JsonValue::String(blocktemplate.target.clone()),
                             JsonValue::String(base64::encode(
                                 &serialize_async(&blocktemplate.block.header).await,
@@ -312,11 +310,6 @@ impl DarkfiNode {
             base64::encode(&serialize_async(&extended_fork.module.darkfi_rx_keys.0).await)
         };
 
-        // Grab the next RandomX key to use so miner can pregenerate
-        // mining VMs.
-        let next_randomx_key =
-            base64::encode(&serialize_async(&extended_fork.module.darkfi_rx_keys.1).await);
-
         // Convert the target
         let target = base64::encode(&target.to_bytes_le());
 
@@ -324,7 +317,6 @@ impl DarkfiNode {
         let blocktemplate = BlockTemplate {
             block,
             randomx_key: randomx_key.clone(),
-            next_randomx_key: next_randomx_key.clone(),
             target: target.clone(),
             secret,
         };
@@ -341,7 +333,6 @@ impl DarkfiNode {
 
         let response = JsonValue::Array(vec![
             JsonValue::String(randomx_key),
-            JsonValue::String(next_randomx_key),
             JsonValue::String(target),
             JsonValue::String(header),
         ]);

+ 40 - 126
bin/minerd/src/rpc.rs

@@ -16,11 +16,10 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use std::{sync::Arc, thread};
+use std::sync::Arc;
 
 use num_bigint::BigUint;
-use randomx::{RandomXFlags, RandomXVM};
-use smol::channel::{Receiver, Sender};
+use randomx::RandomXVM;
 use tracing::{debug, error, info};
 use url::Url;
 
@@ -60,70 +59,63 @@ impl DarkfidRpcClient {
 
 impl MinerNode {
     /// Auxiliary function to request configured darkfid daemon for
-    /// its current and next RandomX keys.
-    async fn randomx_keys(&self) -> Result<(HeaderHash, HeaderHash)> {
+    /// its current mining RandomX key.
+    async fn randomx_key(&self) -> Result<HeaderHash> {
         loop {
-            debug!(target: "minerd::rpc::randomx_keys", "Executing RandomX keys request to darkfid...");
+            debug!(target: "minerd::rpc::randomx_key", "Executing mining RandomX key request to darkfid...");
             let params = match self
-                .darkfid_daemon_request("miner.get_current_randomx_keys", &JsonValue::Array(vec![]))
+                .darkfid_daemon_request(
+                    "miner.get_current_mining_randomx_key",
+                    &JsonValue::Array(vec![]),
+                )
                 .await
             {
                 Ok(params) => params,
                 Err(e) => {
-                    error!(target: "minerd::rpc::randomx_keys", "darkfid request failed: {e}");
+                    error!(target: "minerd::rpc::randomx_key", "darkfid request failed: {e}");
                     self.sleep().await?;
                     continue
                 }
             };
-            debug!(target: "minerd::rpc::randomx_keys", "Got reply: {params:?}");
+            debug!(target: "minerd::rpc::randomx_key", "Got reply: {params:?}");
 
             // Verify response parameters
             if !params.is_array() {
-                error!(target: "minerd::rpc::randomx_keys", "darkfid responded with invalid params: {params:?}");
+                error!(target: "minerd::rpc::randomx_key", "darkfid responded with invalid params: {params:?}");
                 self.sleep().await?;
                 continue
             }
             let params = params.get::<Vec<JsonValue>>().unwrap();
             if params.is_empty() {
-                debug!(target: "minerd::rpc::randomx_keys", "darkfid response is empty");
+                debug!(target: "minerd::rpc::randomx_key", "darkfid response is empty");
                 self.sleep().await?;
                 continue
             }
-            if params.len() != 2 || !params[0].is_string() || !params[1].is_string() {
-                error!(target: "minerd::rpc::randomx_keys", "darkfid responded with invalid params: {params:?}");
+            if params.len() != 1 || !params[0].is_string() {
+                error!(target: "minerd::rpc::randomx_key", "darkfid responded with invalid params: {params:?}");
                 self.sleep().await?;
                 continue
             }
 
             // Parse parameters
             let Some(randomx_key_bytes) = base64::decode(params[0].get::<String>().unwrap()) else {
-                error!(target: "minerd::rpc::randomx_keys", "Failed to parse RandomX key bytes");
+                error!(target: "minerd::rpc::randomx_key", "Failed to parse RandomX key bytes");
                 self.sleep().await?;
                 continue
             };
             let Ok(randomx_key) = deserialize_async::<HeaderHash>(&randomx_key_bytes).await else {
-                error!(target: "minerd::rpc::randomx_keys", "Failed to parse RandomX key");
-                self.sleep().await?;
-                continue
-            };
-            let Some(next_key_bytes) = base64::decode(params[1].get::<String>().unwrap()) else {
-                error!(target: "minerd::rpc::randomx_keys", "Failed to parse next RandomX key bytes");
-                self.sleep().await?;
-                continue
-            };
-            let Ok(next_key) = deserialize_async::<HeaderHash>(&next_key_bytes).await else {
-                error!(target: "minerd::rpc::randomx_keys", "Failed to parse next RandomX key");
+                error!(target: "minerd::rpc::randomx_key", "Failed to parse RandomX key");
                 self.sleep().await?;
                 continue
             };
 
-            return Ok((randomx_key, next_key))
+            return Ok(randomx_key)
         }
     }
 
     /// Auxiliary function to poll configured darkfid daemon for a new
     /// mining job.
-    async fn poll(&self, header: &str) -> Result<(HeaderHash, HeaderHash, BigUint, Header)> {
+    async fn poll(&self, header: &str) -> Result<(HeaderHash, BigUint, Header)> {
         loop {
             debug!(target: "minerd::rpc::poll", "Executing poll request to darkfid...");
             let mut request_params = self.config.wallet_config.clone();
@@ -153,11 +145,10 @@ impl MinerNode {
                 self.sleep().await?;
                 continue
             }
-            if params.len() != 4 ||
+            if params.len() != 3 ||
                 !params[0].is_string() ||
                 !params[1].is_string() ||
-                !params[2].is_string() ||
-                !params[3].is_string()
+                !params[2].is_string()
             {
                 error!(target: "minerd::rpc::poll", "darkfid responded with invalid params: {params:?}");
                 self.sleep().await?;
@@ -175,23 +166,13 @@ impl MinerNode {
                 self.sleep().await?;
                 continue
             };
-            let Some(next_key_bytes) = base64::decode(params[1].get::<String>().unwrap()) else {
-                error!(target: "minerd::rpc::poll", "Failed to parse next RandomX key bytes");
-                self.sleep().await?;
-                continue
-            };
-            let Ok(next_key) = deserialize_async::<HeaderHash>(&next_key_bytes).await else {
-                error!(target: "minerd::rpc::poll", "Failed to parse next RandomX key");
-                self.sleep().await?;
-                continue
-            };
-            let Some(target_bytes) = base64::decode(params[2].get::<String>().unwrap()) else {
+            let Some(target_bytes) = base64::decode(params[1].get::<String>().unwrap()) else {
                 error!(target: "minerd::rpc::poll", "Failed to parse target bytes");
                 self.sleep().await?;
                 continue
             };
             let target = BigUint::from_bytes_le(&target_bytes);
-            let Some(header_bytes) = base64::decode(params[3].get::<String>().unwrap()) else {
+            let Some(header_bytes) = base64::decode(params[2].get::<String>().unwrap()) else {
                 error!(target: "minerd::rpc::poll", "Failed to parse header bytes");
                 self.sleep().await?;
                 continue
@@ -202,7 +183,7 @@ impl MinerNode {
                 continue
             };
 
-            return Ok((randomx_key, next_key, target, header))
+            return Ok((randomx_key, target, header))
         }
     }
 
@@ -273,46 +254,26 @@ impl MinerNode {
 /// Async task to poll darkfid for new mining jobs. Once a new job is
 /// received, spawns a mining task in the background.
 pub async fn polling_task(miner: MinerNodePtr, ex: ExecutorPtr) -> Result<()> {
-    // Cache current and next RandomX keys and current VMs
-    let (mut current_randomx_key, mut next_randomx_key) = miner.randomx_keys().await?;
+    // Cache current RandomX key and its VMs
+    let mut current_randomx_key = miner.randomx_key().await?;
     info!(target: "minerd::rpc::mining_task", "Initializing {} mining VMs for key: {current_randomx_key}", miner.config.threads);
     let mining_flags =
         get_mining_flags(miner.config.fast_mode, miner.config.large_pages, miner.config.secure);
-    let mut current_vms = Arc::new(generate_mining_vms(
+    let mut _current_vms = Arc::new(generate_mining_vms(
         mining_flags,
         &current_randomx_key,
         miner.config.threads,
         &miner.mining_channel.1.clone(),
     )?);
 
-    // Initialize the smol channel to send signal between the threads
-    let (vms_sender, vms_receiver) = smol::channel::bounded(1);
-
-    // Detach next RandomX VMs generation in the background if needed
-    if current_randomx_key != next_randomx_key {
-        let threads = miner.config.threads;
-        let sender = vms_sender.clone();
-        let stop_singal = miner.background_channel.1.clone();
-        thread::spawn(move || {
-            match vms_generation_task(mining_flags, next_randomx_key, threads, sender, stop_singal)
-            {
-                Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ }
-                Err(e) => {
-                    error!(target: "minerd::rpc::polling_task", "RandomX VMs generation task failed: {e}")
-                }
-            }
-        });
-    }
-
     // Use the dummy Header on first poll
     let mut current_job = current_randomx_key.to_string();
     loop {
         // Poll darkfid for a mining job
-        let (randomx_key, next_key, target, header) = miner.poll(&current_job).await?;
+        let (randomx_key, target, header) = miner.poll(&current_job).await?;
         let header_hash = header.hash().to_string();
         debug!(target: "minerd::rpc::polling_task", "Received job:");
         debug!(target: "minerd::rpc::polling_task", "\tRandomX key - {randomx_key}");
-        debug!(target: "minerd::rpc::polling_task", "\tNext RandomX key - {next_key}");
         debug!(target: "minerd::rpc::polling_task", "\tTarget - {target}");
         debug!(target: "minerd::rpc::polling_task", "\tHeader - {header_hash}");
 
@@ -337,53 +298,23 @@ pub async fn polling_task(miner: MinerNodePtr, ex: ExecutorPtr) -> Result<()> {
 
         // Check if the current RandomX key has changed
         if randomx_key != current_randomx_key {
+            // Drop previous VMs
+            _current_vms = Arc::new(vec![]);
+
+            // Generate the RandomX VMs for the key
+            info!(target: "minerd::rpc::mining_task", "Initializing {} mining VMs for key: {randomx_key}", miner.config.threads);
+            _current_vms = Arc::new(generate_mining_vms(
+                mining_flags,
+                &randomx_key,
+                miner.config.threads,
+                &miner.mining_channel.1.clone(),
+            )?);
             current_randomx_key = randomx_key;
-
-            // Check if we should shift to next VMs
-            if current_randomx_key == next_randomx_key {
-                // Shift next generated VMs into current ones
-                info!(target: "minerd::rpc::mining_task", "Grabing next mining VMs from channel for key: {randomx_key}");
-                current_vms = Arc::new(vms_receiver.recv().await?);
-            } else {
-                // Generate the RandomX VMs for the key
-                info!(target: "minerd::rpc::mining_task", "Initializing {} mining VMs for key: {randomx_key}", miner.config.threads);
-                current_vms = Arc::new(generate_mining_vms(
-                    mining_flags,
-                    &randomx_key,
-                    miner.config.threads,
-                    &miner.mining_channel.1.clone(),
-                )?);
-            }
-        }
-
-        // Check if the next RandomX key has changed
-        if next_key != next_randomx_key && next_key != randomx_key {
-            // Abord pending VMs generation task
-            miner.abort_background().await;
-
-            // Consume VMs channel item so its empty
-            if let Err(e) = vms_receiver.try_recv() {
-                debug!(target: "minerd::rpc::mining_task", "Failed to cleanup VMs receiver: {e}");
-            }
-
-            // Detach next RandomX VMs generation in the background
-            let threads = miner.config.threads;
-            let sender = vms_sender.clone();
-            let stop_singal = miner.background_channel.1.clone();
-            thread::spawn(move || {
-                match vms_generation_task(mining_flags, next_key, threads, sender, stop_singal) {
-                    Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ }
-                    Err(e) => {
-                        error!(target: "minerd::rpc::polling_task", "RandomX VMs generation task failed: {e}")
-                    }
-                }
-            });
-            next_randomx_key = next_key;
         }
 
         // Detach mining task
         StoppableTask::new().start(
-            mining_task(miner.clone(), current_vms.clone(), target, header),
+            mining_task(miner.clone(), _current_vms.clone(), target, header),
             |res| async {
                 match res {
                     Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ }
@@ -428,20 +359,3 @@ async fn mining_task(
 
     Ok(())
 }
-
-/// Async task to generate RandomX VMs in the background and push them
-/// in provided channel.
-fn vms_generation_task(
-    mining_flags: RandomXFlags,
-    randomx_key: HeaderHash,
-    threads: usize,
-    sender: Sender<Vec<Arc<RandomXVM>>>,
-    stop_signal: Receiver<()>,
-) -> Result<()> {
-    // Generate the RandomX VMs for the key
-    info!(target: "minerd::rpc::vms_generation_task", "Initializing {threads} mining VMs for key: {randomx_key}");
-    let vms = generate_mining_vms(mining_flags, &randomx_key, threads, &stop_signal)?;
-    // Push them into the channel
-    sender.send_blocking(vms)?;
-    Ok(())
-}

+ 25 - 10
src/validator/consensus.rs

@@ -37,7 +37,7 @@ use crate::{
     runtime::vm_runtime::GAS_LIMIT,
     tx::{Transaction, MAX_TX_CALLS},
     validator::{
-        pow::PoWModule,
+        pow::{PoWModule, RANDOMX_KEY_CHANGE_DELAY, RANDOMX_KEY_CHANGING_HEIGHT},
         utils::{best_fork_index, block_rank, find_extended_fork_index},
         verification::{verify_proposal, verify_transaction},
     },
@@ -448,19 +448,34 @@ impl Consensus {
         Ok(proposals)
     }
 
-    /// Auxiliary function to grab current RandomX keys.
-    /// If no forks exist, returns canonical keys.
-    pub async fn current_randomx_keys(&self) -> Result<(HeaderHash, HeaderHash)> {
+    /// Auxiliary function to grab current mining RandomX key,
+    /// based on next block height.
+    /// If no forks exist, returns the canonical key.
+    pub async fn current_mining_randomx_key(&self) -> Result<HeaderHash> {
         // Grab a lock over current forks
         let forks = self.forks.read().await;
 
-        // If no forks exist, return canonical keys
-        if forks.is_empty() {
-            return Ok(self.module.read().await.darkfi_rx_keys)
-        }
+        // Grab next block height and current keys.
+        // If no forks exist, use canonical keys
+        let (next_block_height, rx_keys) = if forks.is_empty() {
+            let (next_block_height, _) = self.blockchain.last()?;
+            (next_block_height + 1, self.module.read().await.darkfi_rx_keys)
+        } else {
+            // Grab best fork and its last proposal
+            let fork = &forks[best_fork_index(&forks)?];
+            let last = fork.last_proposal()?;
+            (last.block.header.height + 1, fork.module.darkfi_rx_keys)
+        };
 
-        // Grab best fork keys
-        Ok(forks[best_fork_index(&forks)?].module.darkfi_rx_keys)
+        // We only use the next key when the next block is the
+        // height changing one.
+        if next_block_height > RANDOMX_KEY_CHANGING_HEIGHT &&
+            next_block_height % RANDOMX_KEY_CHANGING_HEIGHT == RANDOMX_KEY_CHANGE_DELAY
+        {
+            Ok(rx_keys.1)
+        } else {
+            Ok(rx_keys.0)
+        }
     }
 
     /// Auxiliary function to grab best current fork full clone.

+ 5 - 4
src/validator/mod.rs

@@ -825,10 +825,11 @@ impl Validator {
         Ok(())
     }
 
-    /// Auxiliary function to grab current RandomX keys.
-    /// If no forks exist, returns canonical keys.
-    pub async fn current_randomx_keys(&self) -> Result<(HeaderHash, HeaderHash)> {
-        self.consensus.current_randomx_keys().await
+    /// Auxiliary function to grab current mining RandomX key,
+    /// based on next block height.
+    /// If no forks exist, returns the canonical key.
+    pub async fn current_mining_randomx_key(&self) -> Result<HeaderHash> {
+        self.consensus.current_mining_randomx_key().await
     }
 
     /// Auxiliary function to grab best current fork full clone.