Răsfoiți Sursa

minerd: changed comms logic so it polls darkfid for new mining jobs

skoupidi 8 luni în urmă
părinte
comite
9b141f3fcb
46 a modificat fișierele cu 1503 adăugiri și 1329 ștergeri
  1. 2 0
      Cargo.lock
  2. 0 41
      bin/darkfid/darkfid_config.toml
  3. 20 4
      bin/darkfid/src/error.rs
  4. 12 35
      bin/darkfid/src/lib.rs
  5. 4 27
      bin/darkfid/src/main.rs
  6. 10 116
      bin/darkfid/src/rpc.rs
  7. 540 0
      bin/darkfid/src/rpc_miner.rs
  8. 2 3
      bin/darkfid/src/rpc_xmr.rs
  9. 45 65
      bin/darkfid/src/task/consensus.rs
  10. 0 428
      bin/darkfid/src/task/miner.rs
  11. 0 3
      bin/darkfid/src/task/mod.rs
  12. 1 2
      bin/darkfid/src/tests/harness.rs
  13. 0 5
      bin/darkfid/src/tests/mod.rs
  14. 3 1
      bin/minerd/Cargo.toml
  15. 52 6
      bin/minerd/minerd.toml
  16. 0 54
      bin/minerd/src/error.rs
  17. 134 126
      bin/minerd/src/lib.rs
  18. 139 14
      bin/minerd/src/main.rs
  19. 226 122
      bin/minerd/src/rpc.rs
  20. 2 17
      contrib/localnet/darkfid-five-nodes/darkfid0.toml
  21. 2 14
      contrib/localnet/darkfid-five-nodes/darkfid1.toml
  22. 2 14
      contrib/localnet/darkfid-five-nodes/darkfid2.toml
  23. 2 14
      contrib/localnet/darkfid-five-nodes/darkfid3.toml
  24. 2 14
      contrib/localnet/darkfid-five-nodes/darkfid4.toml
  25. 24 6
      contrib/localnet/darkfid-five-nodes/minerd0.toml
  26. 24 6
      contrib/localnet/darkfid-five-nodes/minerd1.toml
  27. 24 6
      contrib/localnet/darkfid-five-nodes/minerd2.toml
  28. 24 6
      contrib/localnet/darkfid-five-nodes/minerd3.toml
  29. 24 6
      contrib/localnet/darkfid-five-nodes/minerd4.toml
  30. 10 10
      contrib/localnet/darkfid-five-nodes/tmux_sessions.sh
  31. 2 2
      contrib/localnet/darkfid-single-node/README.md
  32. 1 1
      contrib/localnet/darkfid-single-node/clean.sh
  33. 0 15
      contrib/localnet/darkfid-single-node/darkfid.toml
  34. 1 1
      contrib/localnet/darkfid-single-node/init-wallet.sh
  35. 26 6
      contrib/localnet/darkfid-single-node/minerd.toml
  36. 20 20
      contrib/localnet/darkfid-single-node/run-dao-test.sh
  37. 2 2
      contrib/localnet/darkfid-small/README.md
  38. 2 14
      contrib/localnet/darkfid-small/darkfid0.toml
  39. 2 14
      contrib/localnet/darkfid-small/darkfid1.toml
  40. 3 3
      contrib/localnet/darkfid-small/darkfid2.toml
  41. 24 6
      contrib/localnet/darkfid-small/minerd0.toml
  42. 24 6
      contrib/localnet/darkfid-small/minerd1.toml
  43. 5 5
      contrib/localnet/darkfid-small/tmux_sessions.sh
  44. 1 1
      doc/src/testnet/dao.md
  45. 51 59
      doc/src/testnet/node.md
  46. 9 9
      src/validator/pow.rs

+ 2 - 0
Cargo.lock

@@ -4312,6 +4312,7 @@ dependencies = [
 name = "minerd"
 name = "minerd"
 version = "0.5.0"
 version = "0.5.0"
 dependencies = [
 dependencies = [
+ "bs58",
  "darkfi",
  "darkfi",
  "darkfi-sdk",
  "darkfi-sdk",
  "darkfi-serial",
  "darkfi-serial",
@@ -4324,6 +4325,7 @@ dependencies = [
  "structopt",
  "structopt",
  "structopt-toml",
  "structopt-toml",
  "tinyjson",
  "tinyjson",
+ "toml 0.9.8",
  "tracing",
  "tracing",
  "tracing-appender",
  "tracing-appender",
  "tracing-subscriber",
  "tracing-subscriber",

+ 0 - 41
bin/darkfid/darkfid_config.toml

@@ -17,22 +17,9 @@ database = "~/.local/share/darkfi/darkfid/testnet"
 # Confirmation threshold, denominated by number of blocks
 # Confirmation threshold, denominated by number of blocks
 threshold = 6
 threshold = 6
 
 
-# minerd JSON-RPC endpoint
-#minerd_endpoint = "tcp://127.0.0.1:28467"
-
 # PoW block production target, in seconds
 # PoW block production target, in seconds
 pow_target = 120
 pow_target = 120
 
 
-# Wallet address to receive mining rewards.
-#recipient = "YOUR_WALLET_ADDRESS_HERE"
-
-# Optional contract spend hook to use in the mining reward
-#spend_hook = "YOUR_SPEND_HOOK_HERE"
-
-# Optional contract user data to use in the mining reward.
-# This is not arbitrary data.
-#user_data = "YOUR_USER_DATA_HERE"
-
 # Skip syncing process and start node right away
 # Skip syncing process and start node right away
 skip_sync = false
 skip_sync = false
 
 
@@ -156,22 +143,9 @@ database = "~/.local/share/darkfi/darkfid/mainnet"
 # Confirmation threshold, denominated by number of blocks
 # Confirmation threshold, denominated by number of blocks
 threshold = 11
 threshold = 11
 
 
-# minerd JSON-RPC endpoint
-#minerd_endpoint = "tcp://127.0.0.1:28467"
-
 # PoW block production target, in seconds
 # PoW block production target, in seconds
 pow_target = 120
 pow_target = 120
 
 
-# Wallet address to receive mining rewards.
-#recipient = "YOUR_WALLET_ADDRESS_HERE"
-
-# Optional contract spend hook to use in the mining reward
-#spend_hook = "YOUR_SPEND_HOOK_HERE"
-
-# Optional contract user data to use in the mining reward.
-# This is not arbitrary data.
-#user_data = "YOUR_USER_DATA_HERE"
-
 # Skip syncing process and start node right away
 # Skip syncing process and start node right away
 skip_sync = false
 skip_sync = false
 
 
@@ -297,27 +271,12 @@ database = "~/.local/share/darkfi/darkfid/localnet"
 # Confirmation threshold, denominated by number of blocks
 # Confirmation threshold, denominated by number of blocks
 threshold = 3
 threshold = 3
 
 
-# minerd JSON-RPC endpoint
-minerd_endpoint = "tcp://127.0.0.1:28467"
-
 # PoW block production target, in seconds
 # PoW block production target, in seconds
 pow_target = 10
 pow_target = 10
 
 
 # Optional fixed PoW difficulty, used for testing
 # Optional fixed PoW difficulty, used for testing
 pow_fixed_difficulty = 1
 pow_fixed_difficulty = 1
 
 
-# Wallet address to receive mining rewards.
-# This is a dummy one so the miner can start,
-# replace with your own one.
-recipient = "5ZHfYpt4mpJcwBNxfEyxLzeFJUEeoePs5NQ5jVEgHrMf"
-
-# Optional contract spend hook to use in the mining reward
-#spend_hook = "YOUR_SPEND_HOOK_HERE"
-
-# Optional contract user data to use in the mining reward.
-# This is not arbitrary data.
-#user_data = "YOUR_USER_DATA_HERE"
-
 # Skip syncing process and start node right away
 # Skip syncing process and start node right away
 skip_sync = true
 skip_sync = true
 
 

+ 20 - 4
bin/darkfid/src/error.rs

@@ -37,8 +37,16 @@ pub enum RpcError {
     ContractStateNotFound = -32201,
     ContractStateNotFound = -32201,
     ContractStateKeyNotFound = -32202,
     ContractStateKeyNotFound = -32202,
 
 
-    // Misc errors
-    PingFailed = -32300,
+    // Miner errors
+    MinerMissingHeader = -32300,
+    MinerInvalidHeader = -32301,
+    MinerMissingRecipient = -32302,
+    MinerInvalidRecipient = -32303,
+    MinerInvalidSpendHook = -32304,
+    MinerInvalidUserData = -32305,
+    MinerMissingNonce = -32306,
+    MinerInvalidNonce = -32307,
+    MinerUnknownJob = -32308,
 }
 }
 
 
 fn to_tuple(e: RpcError) -> (i32, String) {
 fn to_tuple(e: RpcError) -> (i32, String) {
@@ -55,8 +63,16 @@ fn to_tuple(e: RpcError) -> (i32, String) {
         RpcError::ContractZkasDbNotFound => "zkas database not found for given contract",
         RpcError::ContractZkasDbNotFound => "zkas database not found for given contract",
         RpcError::ContractStateNotFound => "Records not found for given contract state",
         RpcError::ContractStateNotFound => "Records not found for given contract state",
         RpcError::ContractStateKeyNotFound => "Value not found for given contract state key",
         RpcError::ContractStateKeyNotFound => "Value not found for given contract state key",
-        // Misc errors
-        RpcError::PingFailed => "Miner daemon ping error",
+        // Miner errors
+        RpcError::MinerMissingHeader => "Request is missing the Header hash",
+        RpcError::MinerInvalidHeader => "Request Header hash is invalid",
+        RpcError::MinerMissingRecipient => "Request is missing the recipient wallet address",
+        RpcError::MinerInvalidRecipient => "Request recipient wallet address is invalid",
+        RpcError::MinerInvalidSpendHook => "Request spend hook is invalid",
+        RpcError::MinerInvalidUserData => "Request user data is invalid",
+        RpcError::MinerMissingNonce => "Request is missing the Header nonce",
+        RpcError::MinerInvalidNonce => "Request Header nonce is invalid",
+        RpcError::MinerUnknownJob => "Request job is unknown",
     };
     };
 
 
     (e as i32, msg.to_string())
     (e as i32, msg.to_string())

+ 12 - 35
bin/darkfid/src/lib.rs

@@ -22,8 +22,7 @@ use std::{
 };
 };
 
 
 use smol::lock::Mutex;
 use smol::lock::Mutex;
-use tracing::{debug, error, info, warn};
-use url::Url;
+use tracing::{debug, error, info};
 
 
 use darkfi::{
 use darkfi::{
     blockchain::BlockInfo,
     blockchain::BlockInfo,
@@ -52,9 +51,11 @@ use error::{server_error, RpcError};
 
 
 /// JSON-RPC requests handler and methods
 /// JSON-RPC requests handler and methods
 mod rpc;
 mod rpc;
-use rpc::{DefaultRpcHandler, MinerRpcClient, MmRpcHandler};
+use rpc::{DefaultRpcHandler, MmRpcHandler};
 mod rpc_blockchain;
 mod rpc_blockchain;
+mod rpc_miner;
 mod rpc_tx;
 mod rpc_tx;
+use rpc_miner::BlockTemplate;
 mod rpc_xmr;
 mod rpc_xmr;
 
 
 /// Validator async tasks
 /// Validator async tasks
@@ -78,14 +79,14 @@ pub struct DarkfiNode {
     txs_batch_size: usize,
     txs_batch_size: usize,
     /// A map of various subscribers exporting live info from the blockchain
     /// A map of various subscribers exporting live info from the blockchain
     subscribers: HashMap<&'static str, JsonSubscriber>,
     subscribers: HashMap<&'static str, JsonSubscriber>,
+    /// Native mining block templates
+    blocktemplates: Mutex<HashMap<Vec<u8>, BlockTemplate>>,
+    /// Merge mining block templates
+    mm_blocktemplates: Mutex<HashMap<Vec<u8>, (BlockInfo, f64, SecretKey)>>,
     /// JSON-RPC connection tracker
     /// JSON-RPC connection tracker
     rpc_connections: Mutex<HashSet<StoppableTaskPtr>>,
     rpc_connections: Mutex<HashSet<StoppableTaskPtr>>,
-    /// JSON-RPC client to execute requests to the miner daemon
-    rpc_client: Option<Mutex<MinerRpcClient>>,
     /// HTTP JSON-RPC connection tracker
     /// HTTP JSON-RPC connection tracker
     mm_rpc_connections: Mutex<HashSet<StoppableTaskPtr>>,
     mm_rpc_connections: Mutex<HashSet<StoppableTaskPtr>>,
-    /// Merge mining block templates
-    mm_blocktemplates: Mutex<HashMap<Vec<u8>, (BlockInfo, f64, SecretKey)>>,
     /// PowRewardV1 ZK data
     /// PowRewardV1 ZK data
     powrewardv1_zk: PowRewardV1Zk,
     powrewardv1_zk: PowRewardV1Zk,
 }
 }
@@ -96,7 +97,6 @@ impl DarkfiNode {
         validator: ValidatorPtr,
         validator: ValidatorPtr,
         txs_batch_size: usize,
         txs_batch_size: usize,
         subscribers: HashMap<&'static str, JsonSubscriber>,
         subscribers: HashMap<&'static str, JsonSubscriber>,
-        rpc_client: Option<Mutex<MinerRpcClient>>,
     ) -> Result<DarkfiNodePtr> {
     ) -> Result<DarkfiNodePtr> {
         let powrewardv1_zk = PowRewardV1Zk::new(validator.clone())?;
         let powrewardv1_zk = PowRewardV1Zk::new(validator.clone())?;
 
 
@@ -105,15 +105,15 @@ impl DarkfiNode {
             validator,
             validator,
             txs_batch_size,
             txs_batch_size,
             subscribers,
             subscribers,
+            blocktemplates: Mutex::new(HashMap::new()),
+            mm_blocktemplates: Mutex::new(HashMap::new()),
             rpc_connections: Mutex::new(HashSet::new()),
             rpc_connections: Mutex::new(HashSet::new()),
-            rpc_client,
             mm_rpc_connections: Mutex::new(HashSet::new()),
             mm_rpc_connections: Mutex::new(HashSet::new()),
-            mm_blocktemplates: Mutex::new(HashMap::new()),
             powrewardv1_zk,
             powrewardv1_zk,
         }))
         }))
     }
     }
 
 
-    /// Grab best current fork
+    /// Auxiliary function to grab best current fork.
     pub async fn best_current_fork(&self) -> Result<Fork> {
     pub async fn best_current_fork(&self) -> Result<Fork> {
         let forks = self.validator.consensus.forks.read().await;
         let forks = self.validator.consensus.forks.read().await;
         let index = best_fork_index(&forks)?;
         let index = best_fork_index(&forks)?;
@@ -173,7 +173,6 @@ impl Darkfid {
         sled_db: &sled_overlay::sled::Db,
         sled_db: &sled_overlay::sled::Db,
         config: &ValidatorConfig,
         config: &ValidatorConfig,
         net_settings: &Settings,
         net_settings: &Settings,
-        minerd_endpoint: &Option<Url>,
         txs_batch_size: &Option<usize>,
         txs_batch_size: &Option<usize>,
         ex: &ExecutorPtr,
         ex: &ExecutorPtr,
     ) -> Result<DarkfidPtr> {
     ) -> Result<DarkfidPtr> {
@@ -203,17 +202,8 @@ impl Darkfid {
         subscribers.insert("proposals", JsonSubscriber::new("blockchain.subscribe_proposals"));
         subscribers.insert("proposals", JsonSubscriber::new("blockchain.subscribe_proposals"));
         subscribers.insert("dnet", JsonSubscriber::new("dnet.subscribe_events"));
         subscribers.insert("dnet", JsonSubscriber::new("dnet.subscribe_events"));
 
 
-        // Initialize JSON-RPC client to perform requests to minerd
-        let rpc_client = match minerd_endpoint {
-            Some(endpoint) => {
-                Some(Mutex::new(MinerRpcClient::new(endpoint.clone(), ex.clone()).await))
-            }
-            None => None,
-        };
-
         // Initialize node
         // Initialize node
-        let node = DarkfiNode::new(p2p_handler, validator, txs_batch_size, subscribers, rpc_client)
-            .await?;
+        let node = DarkfiNode::new(p2p_handler, validator, txs_batch_size, subscribers).await?;
 
 
         // Generate the background tasks
         // Generate the background tasks
         let dnet_task = StoppableTask::new();
         let dnet_task = StoppableTask::new();
@@ -237,13 +227,6 @@ impl Darkfid {
     ) -> Result<()> {
     ) -> Result<()> {
         info!(target: "darkfid::Darkfid::start", "Starting Darkfi daemon...");
         info!(target: "darkfid::Darkfid::start", "Starting Darkfi daemon...");
 
 
-        // Pinging minerd daemon to verify it listens
-        if self.node.rpc_client.is_some() {
-            if let Err(e) = self.node.ping_miner_daemon().await {
-                warn!(target: "darkfid::Darkfid::start", "Failed to ping miner daemon: {e}");
-            }
-        }
-
         // Start the `dnet` task
         // Start the `dnet` task
         info!(target: "darkfid::Darkfid::start", "Starting dnet subs task");
         info!(target: "darkfid::Darkfid::start", "Starting dnet subs task");
         let dnet_sub_ = self.node.subscribers.get("dnet").unwrap().clone();
         let dnet_sub_ = self.node.subscribers.get("dnet").unwrap().clone();
@@ -366,12 +349,6 @@ impl Darkfid {
         let flushed_bytes = self.node.validator.blockchain.sled_db.flush_async().await?;
         let flushed_bytes = self.node.validator.blockchain.sled_db.flush_async().await?;
         info!(target: "darkfid::Darkfid::stop", "Flushed {flushed_bytes} bytes");
         info!(target: "darkfid::Darkfid::stop", "Flushed {flushed_bytes} bytes");
 
 
-        // Close the JSON-RPC client, if it was initialized
-        if let Some(ref rpc_client) = self.node.rpc_client {
-            info!(target: "darkfid::Darkfid::stop", "Stopping JSON-RPC client...");
-            rpc_client.lock().await.stop().await;
-        };
-
         info!(target: "darkfid::Darkfid::stop", "Darkfi daemon terminated successfully!");
         info!(target: "darkfid::Darkfid::stop", "Darkfi daemon terminated successfully!");
         Ok(())
         Ok(())
     }
     }

+ 4 - 27
bin/darkfid/src/main.rs

@@ -21,7 +21,6 @@ use std::sync::Arc;
 use smol::{fs::read_to_string, stream::StreamExt};
 use smol::{fs::read_to_string, stream::StreamExt};
 use structopt_toml::{serde::Deserialize, structopt::StructOpt, StructOptToml};
 use structopt_toml::{serde::Deserialize, structopt::StructOpt, StructOptToml};
 use tracing::{debug, error, info};
 use tracing::{debug, error, info};
-use url::Url;
 
 
 use darkfi::{
 use darkfi::{
     async_daemonize,
     async_daemonize,
@@ -99,14 +98,6 @@ pub struct BlockchainNetwork {
     /// Confirmation threshold, denominated by number of blocks
     /// Confirmation threshold, denominated by number of blocks
     threshold: usize,
     threshold: usize,
 
 
-    #[structopt(long)]
-    /// minerd JSON-RPC endpoint
-    minerd_endpoint: Option<Url>,
-
-    #[structopt(skip)]
-    /// Optional JSON-RPC settings for p2pool merge mining requests
-    mm_rpc: Option<RpcSettingsOpt>,
-
     #[structopt(long, default_value = "120")]
     #[structopt(long, default_value = "120")]
     /// PoW block production target, in seconds
     /// PoW block production target, in seconds
     pow_target: u32,
     pow_target: u32,
@@ -115,19 +106,6 @@ pub struct BlockchainNetwork {
     /// Optional fixed PoW difficulty, used for testing
     /// Optional fixed PoW difficulty, used for testing
     pow_fixed_difficulty: Option<usize>,
     pow_fixed_difficulty: Option<usize>,
 
 
-    #[structopt(long)]
-    /// Wallet address to receive mining rewards
-    recipient: Option<String>,
-
-    #[structopt(long)]
-    /// Optional contract spend hook to use in the mining reward
-    spend_hook: Option<String>,
-
-    #[structopt(long)]
-    /// Optional contract user data to use in the mining reward.
-    /// This is not arbitrary data.
-    user_data: Option<String>,
-
     #[structopt(long)]
     #[structopt(long)]
     /// Skip syncing process and start node right away
     /// Skip syncing process and start node right away
     skip_sync: bool,
     skip_sync: bool,
@@ -159,6 +137,10 @@ pub struct BlockchainNetwork {
     #[structopt(flatten)]
     #[structopt(flatten)]
     /// JSON-RPC settings
     /// JSON-RPC settings
     rpc: RpcSettingsOpt,
     rpc: RpcSettingsOpt,
+
+    #[structopt(skip)]
+    /// Optional JSON-RPC settings for p2pool merge mining requests
+    mm_rpc: Option<RpcSettingsOpt>,
 }
 }
 
 
 async_daemonize!(realmain);
 async_daemonize!(realmain);
@@ -255,7 +237,6 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
         &sled_db,
         &sled_db,
         &config,
         &config,
         &blockchain_config.net.into(),
         &blockchain_config.net.into(),
-        &blockchain_config.minerd_endpoint,
         &blockchain_config.txs_batch_size,
         &blockchain_config.txs_batch_size,
         &ex,
         &ex,
     )
     )
@@ -266,10 +247,6 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
         skip_sync: blockchain_config.skip_sync,
         skip_sync: blockchain_config.skip_sync,
         checkpoint_height: blockchain_config.checkpoint_height,
         checkpoint_height: blockchain_config.checkpoint_height,
         checkpoint: blockchain_config.checkpoint,
         checkpoint: blockchain_config.checkpoint,
-        miner: blockchain_config.minerd_endpoint.is_some(),
-        recipient: blockchain_config.recipient,
-        spend_hook: blockchain_config.spend_hook,
-        user_data: blockchain_config.user_data,
         bootstrap,
         bootstrap,
     };
     };
     daemon
     daemon

+ 10 - 116
bin/darkfid/src/rpc.rs

@@ -16,65 +16,31 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
  */
 
 
-use std::{collections::HashSet, time::Instant};
+use std::collections::HashSet;
 
 
 use async_trait::async_trait;
 use async_trait::async_trait;
 use smol::lock::MutexGuard;
 use smol::lock::MutexGuard;
 use tinyjson::JsonValue;
 use tinyjson::JsonValue;
-use tracing::{debug, error, info, warn};
-use url::Url;
+use tracing::debug;
 
 
 use darkfi::{
 use darkfi::{
     net::P2pPtr,
     net::P2pPtr,
     rpc::{
     rpc::{
-        client::RpcChadClient,
         jsonrpc::{ErrorCode, JsonError, JsonRequest, JsonResponse, JsonResult},
         jsonrpc::{ErrorCode, JsonError, JsonRequest, JsonResponse, JsonResult},
         p2p_method::HandlerP2p,
         p2p_method::HandlerP2p,
         server::RequestHandler,
         server::RequestHandler,
     },
     },
-    system::{sleep, ExecutorPtr, StoppableTaskPtr},
+    system::StoppableTaskPtr,
     util::time::Timestamp,
     util::time::Timestamp,
-    Error, Result,
 };
 };
 
 
-use crate::{
-    error::{server_error, RpcError},
-    DarkfiNode,
-};
+use crate::DarkfiNode;
 
 
 /// Default JSON-RPC `RequestHandler` type
 /// Default JSON-RPC `RequestHandler` type
 pub struct DefaultRpcHandler;
 pub struct DefaultRpcHandler;
 /// HTTP JSON-RPC `RequestHandler` type for p2pool
 /// HTTP JSON-RPC `RequestHandler` type for p2pool
 pub struct MmRpcHandler;
 pub struct MmRpcHandler;
 
 
-/// Structure to hold a JSON-RPC client and its config,
-/// so we can recreate it in case of an error.
-pub struct MinerRpcClient {
-    endpoint: Url,
-    ex: ExecutorPtr,
-    client: Option<RpcChadClient>,
-}
-
-impl MinerRpcClient {
-    pub async fn new(endpoint: Url, ex: ExecutorPtr) -> Self {
-        let client = match RpcChadClient::new(endpoint.clone(), ex.clone()).await {
-            Ok(c) => Some(c),
-            Err(_) => {
-                warn!(target: "darkfid::Darkfid::init", "Failed to initialize miner daemon rpc client, will try later");
-                None
-            }
-        };
-        Self { endpoint, ex, client }
-    }
-
-    /// Stop the client.
-    pub async fn stop(&self) {
-        if let Some(ref client) = self.client {
-            client.stop().await
-        }
-    }
-}
-
 #[async_trait]
 #[async_trait]
 #[rustfmt::skip]
 #[rustfmt::skip]
 impl RequestHandler<DefaultRpcHandler> for DarkfiNode {
 impl RequestHandler<DefaultRpcHandler> for DarkfiNode {
@@ -87,7 +53,6 @@ impl RequestHandler<DefaultRpcHandler> for DarkfiNode {
             // =====================
             // =====================
             "ping" => <DarkfiNode as RequestHandler<DefaultRpcHandler>>::pong(self, req.id, req.params).await,
             "ping" => <DarkfiNode as RequestHandler<DefaultRpcHandler>>::pong(self, req.id, req.params).await,
             "clock" => self.clock(req.id, req.params).await,
             "clock" => self.clock(req.id, req.params).await,
-            "ping_miner" => self.ping_miner(req.id, req.params).await,
             "dnet.switch" => self.dnet_switch(req.id, req.params).await,
             "dnet.switch" => self.dnet_switch(req.id, req.params).await,
             "dnet.subscribe_events" => self.dnet_subscribe_events(req.id, req.params).await,
             "dnet.subscribe_events" => self.dnet_subscribe_events(req.id, req.params).await,
             "p2p.get_info" => self.p2p_get_info(req.id, req.params).await,
             "p2p.get_info" => self.p2p_get_info(req.id, req.params).await,
@@ -116,6 +81,12 @@ impl RequestHandler<DefaultRpcHandler> for DarkfiNode {
             "tx.clean_pending" => self.tx_clean_pending(req.id, req.params).await,
             "tx.clean_pending" => self.tx_clean_pending(req.id, req.params).await,
             "tx.calculate_fee" => self.tx_calculate_fee(req.id, req.params).await,
             "tx.calculate_fee" => self.tx_calculate_fee(req.id, req.params).await,
 
 
+            // =============
+            // Miner methods
+            // =============
+            "miner.get_header" => self.miner_get_header(req.id, req.params).await,
+            "miner.submit_solution" => self.miner_submit_solution(req.id, req.params).await,
+
             // ==============
             // ==============
             // Invalid method
             // Invalid method
             // ==============
             // ==============
@@ -204,83 +175,6 @@ impl DarkfiNode {
 
 
         self.subscribers.get("dnet").unwrap().clone().into()
         self.subscribers.get("dnet").unwrap().clone().into()
     }
     }
-
-    // RPCAPI:
-    // Pings configured miner daemon for liveness.
-    // Returns `true` on success.
-    //
-    // --> {"jsonrpc": "2.0", "method": "ping_miner", "params": [], "id": 1}
-    // <-- {"jsonrpc": "2.0", "result": "true", "id": 1}
-    async fn ping_miner(&self, id: u16, _params: JsonValue) -> JsonResult {
-        if let Err(e) = self.ping_miner_daemon().await {
-            error!(target: "darkfid::rpc::ping_miner", "Failed to ping miner daemon: {e}");
-            return server_error(RpcError::PingFailed, id, None)
-        }
-        JsonResponse::new(JsonValue::Boolean(true), id).into()
-    }
-
-    /// Ping configured miner daemon JSON-RPC endpoint.
-    pub async fn ping_miner_daemon(&self) -> Result<()> {
-        debug!(target: "darkfid::ping_miner_daemon", "Pinging miner daemon...");
-        self.miner_daemon_request("ping", &JsonValue::Array(vec![])).await?;
-        Ok(())
-    }
-
-    /// Auxiliary function to execute a request towards the configured miner daemon JSON-RPC endpoint.
-    pub async fn miner_daemon_request(
-        &self,
-        method: &str,
-        params: &JsonValue,
-    ) -> Result<JsonValue> {
-        let Some(ref rpc_client) = self.rpc_client else { return Err(Error::RpcClientStopped) };
-        debug!(target: "darkfid::rpc::miner_daemon_request", "Executing request {method} with params: {params:?}");
-        let latency = Instant::now();
-        let req = JsonRequest::new(method, params.clone());
-        let lock = rpc_client.lock().await;
-        let Some(ref client) = lock.client else { return Err(Error::RpcClientStopped) };
-        let rep = client.request(req).await?;
-        drop(lock);
-        let latency = latency.elapsed();
-        debug!(target: "darkfid::rpc::miner_daemon_request", "Got reply: {rep:?}");
-        debug!(target: "darkfid::rpc::miner_daemon_request", "Latency: {latency:?}");
-        Ok(rep)
-    }
-
-    /// Auxiliary function to execute a request towards the configured miner daemon JSON-RPC endpoint,
-    /// but in case of failure, sleep and retry until connection is re-established.
-    pub async fn miner_daemon_request_with_retry(
-        &self,
-        method: &str,
-        params: &JsonValue,
-    ) -> JsonValue {
-        loop {
-            // Try to execute the request using current client
-            match self.miner_daemon_request(method, params).await {
-                Ok(v) => return v,
-                Err(e) => {
-                    error!(target: "darkfid::rpc::miner_daemon_request_with_retry", "Failed to execute miner daemon request: {e}");
-                }
-            }
-            loop {
-                // Sleep a bit before retrying
-                info!(target: "darkfid::rpc::miner_daemon_request_with_retry", "Sleeping so we can retry later");
-                sleep(10).await;
-                // Create a new client
-                let mut rpc_client = self.rpc_client.as_ref().unwrap().lock().await;
-                let Ok(client) =
-                    RpcChadClient::new(rpc_client.endpoint.clone(), rpc_client.ex.clone()).await
-                else {
-                    error!(target: "darkfid::rpc::miner_daemon_request_with_retry", "Failed to initialize miner daemon rpc client, check if minerd is running");
-                    drop(rpc_client);
-                    continue
-                };
-                info!(target: "darkfid::rpc::miner_daemon_request_with_retry", "Connection re-established!");
-                // Set the new client as the daemon one
-                rpc_client.client = Some(client);
-                break;
-            }
-        }
-    }
 }
 }
 
 
 impl HandlerP2p for DarkfiNode {
 impl HandlerP2p for DarkfiNode {

+ 540 - 0
bin/darkfid/src/rpc_miner.rs

@@ -0,0 +1,540 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2025 Dyne.org foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+use std::{collections::HashMap, str::FromStr};
+
+use darkfi::{
+    blockchain::{BlockInfo, Header, HeaderHash},
+    rpc::jsonrpc::{ErrorCode, ErrorCode::InvalidParams, JsonError, JsonResponse, JsonResult},
+    tx::{ContractCallLeaf, Transaction, TransactionBuilder},
+    util::{encoding::base64, time::Timestamp},
+    validator::{
+        consensus::{Fork, Proposal},
+        pow::{RANDOMX_KEY_CHANGE_DELAY, RANDOMX_KEY_CHANGING_HEIGHT},
+        verification::apply_producer_transaction,
+    },
+    zk::ProvingKey,
+    zkas::ZkBinary,
+    Error, Result,
+};
+use darkfi_money_contract::{client::pow_reward_v1::PoWRewardCallBuilder, MoneyFunction};
+use darkfi_sdk::{
+    crypto::{
+        pasta_prelude::PrimeField, FuncId, Keypair, MerkleTree, PublicKey, SecretKey,
+        MONEY_CONTRACT_ID,
+    },
+    pasta::pallas,
+    ContractCall,
+};
+use darkfi_serial::{serialize_async, Encodable};
+use num_bigint::BigUint;
+use rand::rngs::OsRng;
+use tinyjson::JsonValue;
+use tracing::{error, info};
+
+use crate::{proto::ProposalMessage, server_error, DarkfiNode, RpcError};
+
+/// Auxiliary structure representing node miner rewards recipient configuration.
+pub struct MinerRewardsRecipientConfig {
+    /// Wallet mining address to receive mining rewards
+    pub recipient: PublicKey,
+    /// Optional contract spend hook to use in the mining reward
+    pub spend_hook: Option<FuncId>,
+    /// Optional contract user data to use in the mining reward.
+    /// This is not arbitrary data.
+    pub user_data: Option<pallas::Base>,
+}
+
+/// Auxiliary structure representing a block template for native mining.
+pub struct BlockTemplate {
+    /// The block that is being mined
+    pub block: BlockInfo,
+    /// The base64 encoded RandomX key used
+    randomx_key: String,
+    /// The base64 encoded mining target used
+    target: String,
+    /// The signing secret for this block
+    secret: SecretKey,
+}
+
+impl DarkfiNode {
+    // RPCAPI:
+    // Queries the validator for the current best fork next header to
+    // mine.
+    // Returns the current RandomX key, the mining target and the next
+    // block header, all encoded as base64 strings.
+    //
+    // **Params:**
+    // * `header`    : Mining job Header hash that is currently being polled (as string)
+    // * `recipient` : Wallet mining address to receive mining rewards (as string)
+    // * `spend_hook`: Optional contract spend hook to use in the mining reward (as string)
+    // * `user_data` : Optional contract user data (not arbitrary data) to use in the mining reward (as string)
+    //
+    // **Returns:**
+    // * `String`: Current best fork 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", "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 {
+            return server_error(RpcError::NotSynced, id, None)
+        }
+
+        // Parse request params
+        let Some(params) = params.get::<HashMap<String, JsonValue>>() else {
+            return JsonError::new(InvalidParams, None, id).into()
+        };
+        if params.len() < 2 || params.len() > 4 {
+            return JsonError::new(InvalidParams, None, id).into()
+        }
+
+        // Parse header hash
+        let Some(header_hash) = params.get("header") else {
+            return server_error(RpcError::MinerMissingHeader, id, None)
+        };
+        let Some(header_hash) = header_hash.get::<String>() else {
+            return server_error(RpcError::MinerInvalidHeader, id, None)
+        };
+        let Ok(header_hash) = HeaderHash::from_str(header_hash) else {
+            return server_error(RpcError::MinerInvalidHeader, id, None)
+        };
+
+        // Parse recipient wallet address
+        let Some(recipient) = params.get("recipient") else {
+            return server_error(RpcError::MinerMissingRecipient, id, None)
+        };
+        let Some(recipient) = recipient.get::<String>() else {
+            return server_error(RpcError::MinerInvalidRecipient, id, None)
+        };
+        let Ok(recipient) = PublicKey::from_str(recipient) else {
+            return server_error(RpcError::MinerInvalidRecipient, id, None)
+        };
+
+        // Parse spend hook
+        let spend_hook = match params.get("spend_hook") {
+            Some(spend_hook) => {
+                let Some(spend_hook) = spend_hook.get::<String>() else {
+                    return server_error(RpcError::MinerInvalidSpendHook, id, None)
+                };
+                let Ok(spend_hook) = FuncId::from_str(spend_hook) else {
+                    return server_error(RpcError::MinerInvalidSpendHook, id, None)
+                };
+                Some(spend_hook)
+            }
+            None => None,
+        };
+
+        // Parse user data
+        let user_data: Option<pallas::Base> = match params.get("user_data") {
+            Some(user_data) => {
+                let Some(user_data) = user_data.get::<String>() else {
+                    return server_error(RpcError::MinerInvalidUserData, id, None)
+                };
+                let Ok(bytes) = bs58::decode(&user_data).into_vec() else {
+                    return server_error(RpcError::MinerInvalidUserData, id, None)
+                };
+                let bytes: [u8; 32] = match bytes.try_into() {
+                    Ok(b) => b,
+                    Err(_) => return server_error(RpcError::MinerInvalidUserData, id, None),
+                };
+                let Some(user_data) = pallas::Base::from_repr(bytes).into() else {
+                    return server_error(RpcError::MinerInvalidUserData, id, None)
+                };
+                Some(user_data)
+            }
+            None => None,
+        };
+
+        // Now that method params format is correct, we can check if we
+        // already have a mining job for this wallet. If we already
+        // have it, we check if the fork it extends is still the best
+        // one. If both checks pass, we can just return an empty
+        // response if the request `aux_hash` matches the job one,
+        // otherwise return the job block template hash. In case the
+        // best fork has changed, we drop this job and generate a
+        // new one. If we don't know this wallet, we create a new job.
+        // We'll also obtain a lock here to avoid getting polled
+        // multiple times and potentially missing a job. The lock is
+        // released when this function exits.
+        let address_bytes = serialize_async(&(recipient, spend_hook, user_data)).await;
+        let mut blocktemplates = self.blocktemplates.lock().await;
+        let mut extended_fork = match self.best_current_fork().await {
+            Ok(f) => f,
+            Err(e) => {
+                error!(
+                    target: "darkfid::rpc::miner_get_header",
+                    "[RPC] Finding best fork index failed: {e}",
+                );
+                return JsonError::new(ErrorCode::InternalError, None, id).into()
+            }
+        };
+        if let Some(blocktemplate) = blocktemplates.get(&address_bytes) {
+            let last_proposal = match extended_fork.last_proposal() {
+                Ok(p) => p,
+                Err(e) => {
+                    error!(
+                        target: "darkfid::rpc::miner_get_header",
+                        "[RPC] Retrieving best fork last proposal failed: {e}",
+                    );
+                    return JsonError::new(ErrorCode::InternalError, None, id).into()
+                }
+            };
+            if last_proposal.hash == blocktemplate.block.header.previous {
+                return if blocktemplate.block.header.hash() != header_hash {
+                    JsonResponse::new(
+                        JsonValue::Array(vec![
+                            JsonValue::String(blocktemplate.randomx_key.clone()),
+                            JsonValue::String(blocktemplate.target.clone()),
+                            JsonValue::String(base64::encode(
+                                &serialize_async(&blocktemplate.block.header).await,
+                            )),
+                        ]),
+                        id,
+                    )
+                    .into()
+                } else {
+                    JsonResponse::new(JsonValue::Array(vec![]), id).into()
+                }
+            }
+            blocktemplates.remove(&address_bytes);
+        }
+
+        // At this point, we should query the Validator for a new blocktemplate.
+        // We first need to construct `MinerRewardsRecipientConfig` from the
+        // address configuration provided to us through the RPC.
+        let recipient_str = format!("{recipient}");
+        let spend_hook_str = match spend_hook {
+            Some(spend_hook) => format!("{spend_hook}"),
+            None => String::from("-"),
+        };
+        let user_data_str = match user_data {
+            Some(user_data) => bs58::encode(user_data.to_repr()).into_string(),
+            None => String::from("-"),
+        };
+        let recipient_config = MinerRewardsRecipientConfig { recipient, spend_hook, user_data };
+
+        // Now let's try to construct the blocktemplate.
+        let (target, block, secret) = match generate_next_block(
+            &mut extended_fork,
+            &recipient_config,
+            &self.powrewardv1_zk.zkbin,
+            &self.powrewardv1_zk.provingkey,
+            self.validator.consensus.module.read().await.target,
+            self.validator.verify_fees,
+        )
+        .await
+        {
+            Ok(v) => v,
+            Err(e) => {
+                error!(
+                    target: "darkfid::rpc::miner_get_header",
+                    "[RPC] Failed to generate next blocktemplate: {e}",
+                );
+                return JsonError::new(ErrorCode::InternalError, None, id).into()
+            }
+        };
+
+        // Grab the RandomX key to use.
+        // We only use the next key when the next block is the
+        // height changing one.
+        let randomx_key = if block.header.height > RANDOMX_KEY_CHANGING_HEIGHT &&
+            block.header.height % RANDOMX_KEY_CHANGING_HEIGHT == RANDOMX_KEY_CHANGE_DELAY
+        {
+            base64::encode(&serialize_async(&extended_fork.module.darkfi_rx_keys.1).await)
+        } else {
+            base64::encode(&serialize_async(&extended_fork.module.darkfi_rx_keys.0).await)
+        };
+
+        // Convert the target
+        let target = base64::encode(&target.to_bytes_le());
+
+        // Construct the block template
+        let blocktemplate = BlockTemplate {
+            block,
+            randomx_key: randomx_key.clone(),
+            target: target.clone(),
+            secret,
+        };
+
+        // Now we have the blocktemplate. We'll mark it down in memory,
+        // and then ship it to RPC.
+        let header_hash = blocktemplate.block.header.hash().to_string();
+        let header = base64::encode(&serialize_async(&blocktemplate.block.header).await);
+        blocktemplates.insert(address_bytes, blocktemplate);
+        info!(
+            target: "darkfid::rpc::miner_get_header",
+            "[RPC] Created new blocktemplate: address={recipient_str}, spend_hook={spend_hook_str}, user_data={user_data_str}, hash={header_hash}"
+        );
+
+        let response = JsonValue::Array(vec![
+            JsonValue::String(randomx_key),
+            JsonValue::String(target),
+            JsonValue::String(header),
+        ]);
+
+        JsonResponse::new(response, id).into()
+    }
+
+    // RPCAPI:
+    // Submits a PoW solution header nonce for a block.
+    // Returns the block submittion status.
+    //
+    // **Params:**
+    // * `recipient` : Wallet mining address used (as string)
+    // * `spend_hook`: Optional contract spend hook used (as string)
+    // * `user_data` : Optional contract user data (not arbitrary data) used (as string)
+    // * `nonce`     : The solution header nonce (as f64)
+    //
+    // **Returns:**
+    // * `String`: Block submit status
+    //
+    // --> {"jsonrpc": "2.0", "method": "miner.submit_solution", "params": {"recipient": "address", "nonce": 42}, "id": 1}
+    // <-- {"jsonrpc": "2.0", "result": "accepted", "id": 1}
+    pub async fn miner_submit_solution(&self, id: u16, params: JsonValue) -> JsonResult {
+        // Check if node is synced before responding to p2pool
+        if !*self.validator.synced.read().await {
+            return server_error(RpcError::NotSynced, id, None)
+        }
+
+        // Parse request params
+        let Some(params) = params.get::<HashMap<String, JsonValue>>() else {
+            return JsonError::new(InvalidParams, None, id).into()
+        };
+        if params.len() < 2 || params.len() > 4 {
+            return JsonError::new(InvalidParams, None, id).into()
+        }
+
+        // Parse recipient wallet address
+        let Some(recipient) = params.get("recipient") else {
+            return server_error(RpcError::MinerMissingRecipient, id, None)
+        };
+        let Some(recipient) = recipient.get::<String>() else {
+            return server_error(RpcError::MinerInvalidRecipient, id, None)
+        };
+        let Ok(recipient) = PublicKey::from_str(recipient) else {
+            return server_error(RpcError::MinerInvalidRecipient, id, None)
+        };
+
+        // Parse spend hook
+        let spend_hook = match params.get("spend_hook") {
+            Some(spend_hook) => {
+                let Some(spend_hook) = spend_hook.get::<String>() else {
+                    return server_error(RpcError::MinerInvalidSpendHook, id, None)
+                };
+                let Ok(spend_hook) = FuncId::from_str(spend_hook) else {
+                    return server_error(RpcError::MinerInvalidSpendHook, id, None)
+                };
+                Some(spend_hook)
+            }
+            None => None,
+        };
+
+        // Parse user data
+        let user_data: Option<pallas::Base> = match params.get("user_data") {
+            Some(user_data) => {
+                let Some(user_data) = user_data.get::<String>() else {
+                    return server_error(RpcError::MinerInvalidUserData, id, None)
+                };
+                let Ok(bytes) = bs58::decode(&user_data).into_vec() else {
+                    return server_error(RpcError::MinerInvalidUserData, id, None)
+                };
+                let bytes: [u8; 32] = match bytes.try_into() {
+                    Ok(b) => b,
+                    Err(_) => return server_error(RpcError::MinerInvalidUserData, id, None),
+                };
+                let Some(user_data) = pallas::Base::from_repr(bytes).into() else {
+                    return server_error(RpcError::MinerInvalidUserData, id, None)
+                };
+                Some(user_data)
+            }
+            None => None,
+        };
+
+        // Parse nonce
+        let Some(nonce) = params.get("nonce") else {
+            return server_error(RpcError::MinerMissingNonce, id, None)
+        };
+        let Some(nonce) = nonce.get::<f64>() else {
+            return server_error(RpcError::MinerInvalidNonce, id, None)
+        };
+
+        // If we don't know about this job, we can just abort here.
+        let address_bytes = serialize_async(&(recipient, spend_hook, user_data)).await;
+        let mut blocktemplates = self.blocktemplates.lock().await;
+        let Some(blocktemplate) = blocktemplates.get(&address_bytes) else {
+            return server_error(RpcError::MinerUnknownJob, id, None)
+        };
+
+        info!(
+            target: "darkfid::rpc::miner_submit_solution",
+            "[RPC] Got solution submission for block template: {}", blocktemplate.block.header.hash(),
+        );
+
+        // Sign the DarkFi block
+        let mut block = blocktemplate.block.clone();
+        block.header.nonce = *nonce as u64;
+        block.sign(&blocktemplate.secret);
+        info!(
+            target: "darkfid::rpc::miner_submit_solution",
+            "[RPC] Mined block header hash: {}", blocktemplate.block.header.hash(),
+        );
+
+        // At this point we should be able to remove the submitted job.
+        // We still won't release the lock in hope of proposing the block
+        // first.
+        blocktemplates.remove(&address_bytes);
+
+        // Propose the new block
+        info!(
+            target: "darkfid::rpc::miner_submit_solution",
+            "[RPC] Proposing new block to network",
+        );
+        let proposal = Proposal::new(block);
+        if let Err(e) = self.validator.append_proposal(&proposal).await {
+            error!(
+                target: "darkfid::rpc::miner_submit_solution",
+                "[RPC] Error proposing new block: {e}",
+            );
+            return JsonResponse::new(JsonValue::String(String::from("rejected")), id).into()
+        }
+
+        let proposals_sub = self.subscribers.get("proposals").unwrap();
+        let enc_prop = JsonValue::String(base64::encode(&serialize_async(&proposal).await));
+        proposals_sub.notify(vec![enc_prop].into()).await;
+
+        info!(
+            target: "darkfid::rpc::miner_submit_solution",
+            "[RPC] Broadcasting new block to network",
+        );
+        let message = ProposalMessage(proposal);
+        self.p2p_handler.p2p.broadcast(&message).await;
+
+        JsonResponse::new(JsonValue::String(String::from("accepted")), id).into()
+    }
+}
+
+/// Auxiliary function to generate next block in an atomic manner.
+pub async fn generate_next_block(
+    extended_fork: &mut Fork,
+    recipient_config: &MinerRewardsRecipientConfig,
+    zkbin: &ZkBinary,
+    pk: &ProvingKey,
+    block_target: u32,
+    verify_fees: bool,
+) -> Result<(BigUint, BlockInfo, SecretKey)> {
+    // Grab forks' last block proposal(previous)
+    let last_proposal = extended_fork.last_proposal()?;
+
+    // Grab forks' next block height
+    let next_block_height = last_proposal.block.header.height + 1;
+
+    // Grab forks' unproposed transactions
+    let (mut txs, _, fees, overlay) = extended_fork
+        .unproposed_txs(&extended_fork.blockchain, next_block_height, block_target, verify_fees)
+        .await?;
+
+    // Create an ephemeral block signing keypair. Its secret key will
+    // be stored in the PowReward transaction's encrypted note for
+    // later retrieval. It is encrypted towards the recipient's public
+    // key.
+    let block_signing_keypair = Keypair::random(&mut OsRng);
+
+    // Generate reward transaction
+    let tx = generate_transaction(
+        next_block_height,
+        fees,
+        &block_signing_keypair,
+        recipient_config,
+        zkbin,
+        pk,
+    )?;
+
+    // Apply producer transaction in the overlay
+    let _ = apply_producer_transaction(
+        &overlay,
+        next_block_height,
+        block_target,
+        &tx,
+        &mut MerkleTree::new(1),
+    )
+    .await?;
+    txs.push(tx);
+
+    // Grab the updated contracts states root
+    overlay.lock().unwrap().contracts.update_state_monotree(&mut extended_fork.state_monotree)?;
+    let Some(state_root) = extended_fork.state_monotree.get_headroot()? else {
+        return Err(Error::ContractsStatesRootNotFoundError);
+    };
+
+    // Drop new trees opened by the unproposed transactions overlay
+    overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
+
+    // Generate the new header
+    let mut header =
+        Header::new(last_proposal.hash, next_block_height, Timestamp::current_time(), 0);
+    header.state_root = state_root;
+
+    // Generate the block
+    let mut next_block = BlockInfo::new_empty(header);
+
+    // Add transactions to the block
+    next_block.append_txs(txs);
+
+    // Grab the next mine target
+    let target = extended_fork.module.next_mine_target()?;
+
+    Ok((target, next_block, block_signing_keypair.secret))
+}
+
+/// Auxiliary function to generate a Money::PoWReward transaction.
+fn generate_transaction(
+    block_height: u32,
+    fees: u64,
+    block_signing_keypair: &Keypair,
+    recipient_config: &MinerRewardsRecipientConfig,
+    zkbin: &ZkBinary,
+    pk: &ProvingKey,
+) -> Result<Transaction> {
+    // Build the transaction debris
+    let debris = PoWRewardCallBuilder {
+        signature_keypair: *block_signing_keypair,
+        block_height,
+        fees,
+        recipient: Some(recipient_config.recipient),
+        spend_hook: recipient_config.spend_hook,
+        user_data: recipient_config.user_data,
+        mint_zkbin: zkbin.clone(),
+        mint_pk: pk.clone(),
+    }
+    .build()?;
+
+    // Generate and sign the actual transaction
+    let mut data = vec![MoneyFunction::PoWRewardV1 as u8];
+    debris.params.encode(&mut data)?;
+    let call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
+    let mut tx_builder =
+        TransactionBuilder::new(ContractCallLeaf { call, proofs: debris.proofs }, vec![])?;
+    let mut tx = tx_builder.build()?;
+    let sigs = tx.create_sigs(&[block_signing_keypair.secret])?;
+    tx.signatures = vec![sigs];
+
+    Ok(tx)
+}

+ 2 - 3
bin/darkfid/src/rpc_xmr.rs

@@ -42,9 +42,8 @@ use tracing::{error, info};
 
 
 use crate::{
 use crate::{
     proto::ProposalMessage,
     proto::ProposalMessage,
-    server_error,
-    task::miner::{generate_next_block, MinerRewardsRecipientConfig},
-    DarkfiNode, RpcError,
+    rpc_miner::{generate_next_block, MinerRewardsRecipientConfig},
+    server_error, DarkfiNode, RpcError,
 };
 };
 
 
 // https://github.com/SChernykh/p2pool/blob/master/docs/MERGE_MINING.MD
 // https://github.com/SChernykh/p2pool/blob/master/docs/MERGE_MINING.MD

+ 45 - 65
bin/darkfid/src/task/consensus.rs

@@ -25,28 +25,24 @@ use darkfi::{
     util::{encoding::base64, time::Timestamp},
     util::{encoding::base64, time::Timestamp},
     Error, Result,
     Error, Result,
 };
 };
-use darkfi_sdk::{
-    crypto::{FuncId, PublicKey},
-    pasta::{group::ff::PrimeField, pallas},
-};
 use darkfi_serial::serialize_async;
 use darkfi_serial::serialize_async;
 use tracing::{error, info};
 use tracing::{error, info};
 
 
 use crate::{
 use crate::{
-    task::{garbage_collect_task, miner::MinerRewardsRecipientConfig, miner_task, sync_task},
+    task::{garbage_collect_task, sync_task},
     DarkfiNodePtr,
     DarkfiNodePtr,
 };
 };
 
 
-/// Auxiliary structure representing node consensus init task configuration
+/// Auxiliary structure representing node consensus init task configuration.
 #[derive(Clone)]
 #[derive(Clone)]
 pub struct ConsensusInitTaskConfig {
 pub struct ConsensusInitTaskConfig {
+    /// Skip syncing process and start node right away
     pub skip_sync: bool,
     pub skip_sync: bool,
+    /// Optional sync checkpoint height
     pub checkpoint_height: Option<u32>,
     pub checkpoint_height: Option<u32>,
+    /// Optional sync checkpoint hash
     pub checkpoint: Option<String>,
     pub checkpoint: Option<String>,
-    pub miner: bool,
-    pub recipient: Option<String>,
-    pub spend_hook: Option<String>,
-    pub user_data: Option<String>,
+    /// Optional bootstrap timestamp
     pub bootstrap: u64,
     pub bootstrap: u64,
 }
 }
 
 
@@ -95,54 +91,9 @@ pub async fn consensus_init_task(
         None
         None
     };
     };
 
 
-    // Grab rewards recipient public key(address) if node is a miner,
-    // along with configured spend hook and user data.
-    let recipient_config = if config.miner {
-        if config.recipient.is_none() {
-            return Err(Error::ParseFailed("Recipient address missing"))
-        }
-        let recipient = match PublicKey::from_str(config.recipient.as_ref().unwrap()) {
-            Ok(address) => address,
-            Err(_) => return Err(Error::InvalidAddress),
-        };
-
-        let spend_hook = match &config.spend_hook {
-            Some(s) => match FuncId::from_str(s) {
-                Ok(s) => Some(s),
-                Err(_) => return Err(Error::ParseFailed("Invalid spend hook")),
-            },
-            None => None,
-        };
-
-        let user_data = match &config.user_data {
-            Some(u) => {
-                let bytes: [u8; 32] = match bs58::decode(&u).into_vec()?.try_into() {
-                    Ok(b) => b,
-                    Err(_) => return Err(Error::ParseFailed("Invalid user data")),
-                };
-
-                match pallas::Base::from_repr(bytes).into() {
-                    Some(v) => Some(v),
-                    None => return Err(Error::ParseFailed("Invalid user data")),
-                }
-            }
-            None => None,
-        };
-
-        Some(MinerRewardsRecipientConfig { recipient, spend_hook, user_data })
-    } else {
-        None
-    };
-
     // Gracefully handle network disconnections
     // Gracefully handle network disconnections
     loop {
     loop {
-        let result = if config.miner {
-            miner_task(&node, recipient_config.as_ref().unwrap(), config.skip_sync, &ex).await
-        } else {
-            replicator_task(&node, &ex).await
-        };
-
-        match result {
+        match listen_to_network(&node, &ex).await {
             Ok(_) => return Ok(()),
             Ok(_) => return Ok(()),
             Err(Error::NetworkNotConnected) => {
             Err(Error::NetworkNotConnected) => {
                 // Sync node again
                 // Sync node again
@@ -160,7 +111,7 @@ pub async fn consensus_init_task(
 }
 }
 
 
 /// Async task to start the consensus task, while monitoring for a network disconnections.
 /// Async task to start the consensus task, while monitoring for a network disconnections.
-async fn replicator_task(node: &DarkfiNodePtr, ex: &ExecutorPtr) -> Result<()> {
+async fn listen_to_network(node: &DarkfiNodePtr, ex: &ExecutorPtr) -> Result<()> {
     // Grab proposals subscriber and subscribe to it
     // Grab proposals subscriber and subscribe to it
     let proposals_sub = node.subscribers.get("proposals").unwrap();
     let proposals_sub = node.subscribers.get("proposals").unwrap();
     let prop_subscription = proposals_sub.publisher.clone().subscribe().await;
     let prop_subscription = proposals_sub.publisher.clone().subscribe().await;
@@ -225,8 +176,8 @@ async fn consensus_task(
             continue
             continue
         }
         }
 
 
-        if let Err(e) = clean_mm_blocktemplates(node).await {
-            error!(target: "darkfid", "Failed cleaning merge mining block templates: {e}")
+        if let Err(e) = clean_blocktemplates(node).await {
+            error!(target: "darkfid", "Failed cleaning mining block templates: {e}")
         }
         }
 
 
         let mut notif_blocks = Vec::with_capacity(confirmed.len());
         let mut notif_blocks = Vec::with_capacity(confirmed.len());
@@ -253,14 +204,15 @@ async fn consensus_task(
     }
     }
 }
 }
 
 
-/// Auxiliary function to drop merge mining block templates not
-/// referencing active forks or last confirmed block.
-pub async fn clean_mm_blocktemplates(node: &DarkfiNodePtr) -> Result<()> {
-    // Grab a lock over node merge mining templates
+/// Auxiliary function to drop mining block templates not referencing
+/// active forks or last confirmed block.
+async fn clean_blocktemplates(node: &DarkfiNodePtr) -> Result<()> {
+    // Grab a lock over node mining templates
+    let mut blocktemplates = node.blocktemplates.lock().await;
     let mut mm_blocktemplates = node.mm_blocktemplates.lock().await;
     let mut mm_blocktemplates = node.mm_blocktemplates.lock().await;
 
 
-    // Early return if no merge mining block templates exist
-    if mm_blocktemplates.is_empty() {
+    // Early return if no mining block templates exist
+    if blocktemplates.is_empty() && mm_blocktemplates.is_empty() {
         return Ok(())
         return Ok(())
     }
     }
 
 
@@ -272,6 +224,34 @@ pub async fn clean_mm_blocktemplates(node: &DarkfiNodePtr) -> Result<()> {
 
 
     // Loop through templates to find which can be dropped
     // Loop through templates to find which can be dropped
     let mut dropped_templates = vec![];
     let mut dropped_templates = vec![];
+    'outer: for (key, blocktemplate) in blocktemplates.iter() {
+        // Loop through all the forks
+        for fork in forks.iter() {
+            // Traverse fork proposals sequence in reverse
+            for p_hash in fork.proposals.iter().rev() {
+                // Check if job extends this fork
+                if &blocktemplate.block.header.previous == p_hash {
+                    continue 'outer
+                }
+            }
+        }
+
+        // Check if it extends last confirmed block
+        if blocktemplate.block.header.previous == last_confirmed {
+            continue
+        }
+
+        // This job doesn't reference something so we drop it
+        dropped_templates.push(key.clone());
+    }
+
+    // Drop jobs not referencing active forks or last confirmed block
+    for key in dropped_templates {
+        blocktemplates.remove(&key);
+    }
+
+    // Loop through merge mining templates to find which can be dropped
+    let mut dropped_templates = vec![];
     'outer: for (key, (block, _, _)) in mm_blocktemplates.iter() {
     'outer: for (key, (block, _, _)) in mm_blocktemplates.iter() {
         // Loop through all the forks
         // Loop through all the forks
         for fork in forks.iter() {
         for fork in forks.iter() {

+ 0 - 428
bin/darkfid/src/task/miner.rs

@@ -1,428 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2025 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use darkfi::{
-    blockchain::{BlockInfo, Header, HeaderHash},
-    rpc::{jsonrpc::JsonNotification, util::JsonValue},
-    system::{ExecutorPtr, StoppableTask, Subscription},
-    tx::{ContractCallLeaf, Transaction, TransactionBuilder},
-    util::{encoding::base64, time::Timestamp},
-    validator::{
-        consensus::{Fork, Proposal},
-        pow::{RANDOMX_KEY_CHANGE_DELAY, RANDOMX_KEY_CHANGING_HEIGHT},
-        utils::best_fork_index,
-        verification::apply_producer_transaction,
-    },
-    zk::ProvingKey,
-    zkas::ZkBinary,
-    Error, Result,
-};
-use darkfi_money_contract::{client::pow_reward_v1::PoWRewardCallBuilder, MoneyFunction};
-use darkfi_sdk::{
-    crypto::{FuncId, Keypair, MerkleTree, PublicKey, SecretKey, MONEY_CONTRACT_ID},
-    pasta::pallas,
-    ContractCall,
-};
-use darkfi_serial::{serialize_async, Encodable};
-use num_bigint::BigUint;
-use rand::rngs::OsRng;
-use smol::channel::{Receiver, Sender};
-use tracing::{error, info};
-
-use crate::{
-    proto::ProposalMessage,
-    task::{consensus::clean_mm_blocktemplates, garbage_collect_task},
-    DarkfiNodePtr,
-};
-
-/// Auxiliary structure representing node miner rewards recipient configuration
-pub struct MinerRewardsRecipientConfig {
-    pub recipient: PublicKey,
-    pub spend_hook: Option<FuncId>,
-    pub user_data: Option<pallas::Base>,
-}
-
-/// Async task used for participating in the PoW block production.
-///
-/// Miner initializes their setup and waits for next confirmation,
-/// by listening for new proposals from the network, for optimal
-/// conditions. After confirmation occurs, they start the actual
-/// miner loop, where they first grab the best ranking fork to extend,
-/// and start mining procedure for its next block. Additionally, they
-/// listen to the network for new proposals, and check if these
-/// proposals produce a new best ranking fork. If they do, the stop
-/// mining. These two tasks run in parallel, and after one of them
-/// finishes, node triggers confirmation check.
-pub async fn miner_task(
-    node: &DarkfiNodePtr,
-    recipient_config: &MinerRewardsRecipientConfig,
-    skip_sync: bool,
-    ex: &ExecutorPtr,
-) -> Result<()> {
-    // Initialize miner configuration
-    info!(target: "darkfid::task::miner_task", "Starting miner task...");
-
-    // Grab blocks subscriber
-    let block_sub = node.subscribers.get("blocks").unwrap();
-
-    // Grab proposals subscriber and subscribe to it
-    let proposals_sub = node.subscribers.get("proposals").unwrap();
-    let subscription = proposals_sub.publisher.clone().subscribe().await;
-
-    // Create channels so threads can signal each other
-    let (sender, stop_signal) = smol::channel::bounded(1);
-
-    // Create the garbage collection task using a dummy task
-    let gc_task = StoppableTask::new();
-    gc_task.clone().start(
-        async { Ok(()) },
-        |_| async { /* Do nothing */ },
-        Error::GarbageCollectionTaskStopped,
-        ex.clone(),
-    );
-
-    info!(target: "darkfid::task::miner_task", "Miner initialized successfully!");
-
-    // Start miner loop
-    loop {
-        // Grab best current fork
-        let extended_fork = match node.best_current_fork().await {
-            Ok(f) => f,
-            Err(e) => {
-                error!(
-                    target: "darkfid::task::miner_task",
-                    "Finding best fork index failed: {e}",
-                );
-                continue
-            }
-        };
-
-        // Grab extended fork last proposal hash
-        let last_proposal_hash = extended_fork.last_proposal()?.hash;
-
-        // Grab zkas proving keys and bin for PoWReward transaction
-        let zkbin = &node.powrewardv1_zk.zkbin;
-        let pk = &node.powrewardv1_zk.provingkey;
-
-        // Start listenning for network proposals and mining next block for best fork.
-        match smol::future::or(
-            listen_to_network(node, last_proposal_hash, &subscription, &sender),
-            mine(node, extended_fork, recipient_config, zkbin, pk, &stop_signal, skip_sync),
-        )
-        .await
-        {
-            Ok(_) => { /* Do nothing */ }
-            Err(Error::NetworkNotConnected) => {
-                error!(target: "darkfid::task::miner_task", "Node disconnected from the network");
-                subscription.unsubscribe().await;
-                return Err(Error::NetworkNotConnected)
-            }
-            Err(e) => {
-                error!(
-                    target: "darkfid::task::miner_task",
-                    "Error during listen_to_network() or mine(): {e}"
-                );
-                continue
-            }
-        }
-
-        // Check if we can confirm anything and broadcast them
-        let confirmed = match node.validator.confirmation().await {
-            Ok(f) => f,
-            Err(e) => {
-                error!(
-                    target: "darkfid::task::miner_task",
-                    "Confirmation failed: {e}"
-                );
-                continue
-            }
-        };
-
-        if confirmed.is_empty() {
-            continue
-        }
-
-        if let Err(e) = clean_mm_blocktemplates(node).await {
-            error!(target: "darkfid", "Failed cleaning merge mining block templates: {e}")
-        }
-
-        let mut notif_blocks = Vec::with_capacity(confirmed.len());
-        for block in confirmed {
-            notif_blocks.push(JsonValue::String(base64::encode(&serialize_async(&block).await)));
-        }
-        block_sub.notify(JsonValue::Array(notif_blocks)).await;
-
-        // Invoke the detached garbage collection task
-        gc_task.clone().stop().await;
-        gc_task.clone().start(
-            garbage_collect_task(node.clone()),
-            |res| async {
-                match res {
-                    Ok(()) | Err(Error::GarbageCollectionTaskStopped) => { /* Do nothing */ }
-                    Err(e) => {
-                        error!(target: "darkfid", "Failed starting garbage collection task: {e}")
-                    }
-                }
-            },
-            Error::GarbageCollectionTaskStopped,
-            ex.clone(),
-        );
-    }
-}
-
-/// Async task to listen for incoming proposals and check if the best fork has changed.
-async fn listen_to_network(
-    node: &DarkfiNodePtr,
-    last_proposal_hash: HeaderHash,
-    subscription: &Subscription<JsonNotification>,
-    sender: &Sender<()>,
-) -> Result<()> {
-    loop {
-        // Wait until a new proposal has been received
-        subscription.receive().await;
-
-        // Grab a lock over node forks
-        let forks = node.validator.consensus.forks.read().await;
-
-        // Grab best current fork index
-        let index = best_fork_index(&forks)?;
-
-        // Verify if proposals sequence has changed
-        if forks[index].last_proposal()?.hash != last_proposal_hash {
-            drop(forks);
-            break
-        }
-
-        drop(forks);
-    }
-
-    // Signal miner to abort mining
-    sender.send(()).await?;
-    if let Err(e) = node.miner_daemon_request("abort", &JsonValue::Array(vec![])).await {
-        error!(target: "darkfid::task::miner::listen_to_network", "Failed to execute miner daemon abort request: {e}");
-    }
-
-    Ok(())
-}
-
-/// Async task to generate and mine provided fork index next block,
-/// while listening for a stop signal.
-#[allow(clippy::too_many_arguments)]
-async fn mine(
-    node: &DarkfiNodePtr,
-    extended_fork: Fork,
-    recipient_config: &MinerRewardsRecipientConfig,
-    zkbin: &ZkBinary,
-    pk: &ProvingKey,
-    stop_signal: &Receiver<()>,
-    skip_sync: bool,
-) -> Result<()> {
-    smol::future::or(
-        wait_stop_signal(stop_signal),
-        mine_next_block(node, extended_fork, recipient_config, zkbin, pk, skip_sync),
-    )
-    .await
-}
-
-/// Async task to wait for listener's stop signal.
-pub async fn wait_stop_signal(stop_signal: &Receiver<()>) -> Result<()> {
-    // Clean stop signal channel
-    if stop_signal.is_full() {
-        stop_signal.recv().await?;
-    }
-
-    // Wait for listener signal
-    stop_signal.recv().await?;
-
-    Ok(())
-}
-
-/// Async task to generate and mine provided fork index next block.
-async fn mine_next_block(
-    node: &DarkfiNodePtr,
-    mut extended_fork: Fork,
-    recipient_config: &MinerRewardsRecipientConfig,
-    zkbin: &ZkBinary,
-    pk: &ProvingKey,
-    skip_sync: bool,
-) -> Result<()> {
-    // Grab next target and block
-    let (next_target, mut next_block, block_signing_secret) = generate_next_block(
-        &mut extended_fork,
-        recipient_config,
-        zkbin,
-        pk,
-        node.validator.consensus.module.read().await.target,
-        node.validator.verify_fees,
-    )
-    .await?;
-
-    // Execute request to minerd and parse response
-    let target = JsonValue::String(base64::encode(&next_target.to_bytes_le()));
-    // Grab the RandomX key to use.
-    // We only use the next key when the next block is the
-    // height changing one.
-    let randomx_key = if next_block.header.height > RANDOMX_KEY_CHANGING_HEIGHT &&
-        next_block.header.height % RANDOMX_KEY_CHANGING_HEIGHT == RANDOMX_KEY_CHANGE_DELAY
-    {
-        JsonValue::String(base64::encode(
-            &serialize_async(&extended_fork.module.darkfi_rx_keys.1).await,
-        ))
-    } else {
-        JsonValue::String(base64::encode(
-            &serialize_async(&extended_fork.module.darkfi_rx_keys.0).await,
-        ))
-    };
-    let header = JsonValue::String(base64::encode(&serialize_async(&next_block.header).await));
-    let response = node
-        .miner_daemon_request_with_retry(
-            "mine",
-            &JsonValue::Array(vec![target, randomx_key, header]),
-        )
-        .await;
-    next_block.header.nonce = *response.get::<f64>().unwrap() as u64;
-
-    // Sign the mined block
-    next_block.sign(&block_signing_secret);
-
-    // Verify it
-    extended_fork.module.verify_current_block(&next_block.header)?;
-
-    // Check if we are connected to the network
-    if !skip_sync && !node.p2p_handler.p2p.is_connected() {
-        return Err(Error::NetworkNotConnected)
-    }
-
-    // Append the mined block as a proposal
-    let proposal = Proposal::new(next_block);
-    node.validator.append_proposal(&proposal).await?;
-
-    // Broadcast proposal to the network
-    let message = ProposalMessage(proposal);
-    node.p2p_handler.p2p.broadcast(&message).await;
-
-    Ok(())
-}
-
-/// Auxiliary function to generate next block in an atomic manner.
-pub async fn generate_next_block(
-    extended_fork: &mut Fork,
-    recipient_config: &MinerRewardsRecipientConfig,
-    zkbin: &ZkBinary,
-    pk: &ProvingKey,
-    block_target: u32,
-    verify_fees: bool,
-) -> Result<(BigUint, BlockInfo, SecretKey)> {
-    // Grab forks' last block proposal(previous)
-    let last_proposal = extended_fork.last_proposal()?;
-
-    // Grab forks' next block height
-    let next_block_height = last_proposal.block.header.height + 1;
-
-    // Grab forks' unproposed transactions
-    let (mut txs, _, fees, overlay) = extended_fork
-        .unproposed_txs(&extended_fork.blockchain, next_block_height, block_target, verify_fees)
-        .await?;
-
-    // Create an ephemeral block signing keypair. Its secret key will
-    // be stored in the PowReward transaction's encrypted note for
-    // later retrieval. It is encrypted towards the recipient's public
-    // key.
-    let block_signing_keypair = Keypair::random(&mut OsRng);
-
-    // Generate reward transaction
-    let tx = generate_transaction(
-        next_block_height,
-        fees,
-        &block_signing_keypair,
-        recipient_config,
-        zkbin,
-        pk,
-    )?;
-
-    // Apply producer transaction in the overlay
-    let _ = apply_producer_transaction(
-        &overlay,
-        next_block_height,
-        block_target,
-        &tx,
-        &mut MerkleTree::new(1),
-    )
-    .await?;
-    txs.push(tx);
-
-    // Grab the updated contracts states root
-    overlay.lock().unwrap().contracts.update_state_monotree(&mut extended_fork.state_monotree)?;
-    let Some(state_root) = extended_fork.state_monotree.get_headroot()? else {
-        return Err(Error::ContractsStatesRootNotFoundError);
-    };
-
-    // Drop new trees opened by the unproposed transactions overlay
-    overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
-
-    // Generate the new header
-    let mut header =
-        Header::new(last_proposal.hash, next_block_height, Timestamp::current_time(), 0);
-    header.state_root = state_root;
-
-    // Generate the block
-    let mut next_block = BlockInfo::new_empty(header);
-
-    // Add transactions to the block
-    next_block.append_txs(txs);
-
-    // Grab the next mine target
-    let target = extended_fork.module.next_mine_target()?;
-
-    Ok((target, next_block, block_signing_keypair.secret))
-}
-
-/// Auxiliary function to generate a Money::PoWReward transaction.
-fn generate_transaction(
-    block_height: u32,
-    fees: u64,
-    block_signing_keypair: &Keypair,
-    recipient_config: &MinerRewardsRecipientConfig,
-    zkbin: &ZkBinary,
-    pk: &ProvingKey,
-) -> Result<Transaction> {
-    // Build the transaction debris
-    let debris = PoWRewardCallBuilder {
-        signature_keypair: *block_signing_keypair,
-        block_height,
-        fees,
-        recipient: Some(recipient_config.recipient),
-        spend_hook: recipient_config.spend_hook,
-        user_data: recipient_config.user_data,
-        mint_zkbin: zkbin.clone(),
-        mint_pk: pk.clone(),
-    }
-    .build()?;
-
-    // Generate and sign the actual transaction
-    let mut data = vec![MoneyFunction::PoWRewardV1 as u8];
-    debris.params.encode(&mut data)?;
-    let call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
-    let mut tx_builder =
-        TransactionBuilder::new(ContractCallLeaf { call, proofs: debris.proofs }, vec![])?;
-    let mut tx = tx_builder.build()?;
-    let sigs = tx.create_sigs(&[block_signing_keypair.secret])?;
-    tx.signatures = vec![sigs];
-
-    Ok(tx)
-}

+ 0 - 3
bin/darkfid/src/task/mod.rs

@@ -19,9 +19,6 @@
 pub mod consensus;
 pub mod consensus;
 pub use consensus::consensus_init_task;
 pub use consensus::consensus_init_task;
 
 
-pub mod miner;
-pub use miner::{generate_next_block, miner_task};
-
 pub mod sync;
 pub mod sync;
 pub use sync::sync_task;
 pub use sync::sync_task;
 
 

+ 1 - 2
bin/darkfid/src/tests/harness.rs

@@ -289,8 +289,7 @@ pub async fn generate_node(
 
 
     let p2p_handler = DarkfidP2pHandler::init(settings, ex).await?;
     let p2p_handler = DarkfidP2pHandler::init(settings, ex).await?;
     let node =
     let node =
-        DarkfiNode::new(p2p_handler.clone(), validator.clone(), 50, subscribers.clone(), None)
-            .await?;
+        DarkfiNode::new(p2p_handler.clone(), validator.clone(), 50, subscribers.clone()).await?;
 
 
     p2p_handler.clone().start(ex, &validator, &subscribers).await?;
     p2p_handler.clone().start(ex, &validator, &subscribers).await?;
 
 

+ 0 - 5
bin/darkfid/src/tests/mod.rs

@@ -279,10 +279,6 @@ fn darkfid_programmatic_control() -> Result<()> {
                     skip_sync: true,
                     skip_sync: true,
                     checkpoint_height: None,
                     checkpoint_height: None,
                     checkpoint: None,
                     checkpoint: None,
-                    miner: false,
-                    recipient: None,
-                    spend_hook: None,
-                    user_data: None,
                     bootstrap,
                     bootstrap,
                 };
                 };
                 let rpc_settings = RpcSettings {
                 let rpc_settings = RpcSettings {
@@ -296,7 +292,6 @@ fn darkfid_programmatic_control() -> Result<()> {
                     &config,
                     &config,
                     &darkfi::net::Settings::default(),
                     &darkfi::net::Settings::default(),
                     &None,
                     &None,
-                    &None,
                     &ex,
                     &ex,
                 )
                 )
                 .await
                 .await

+ 3 - 1
bin/minerd/Cargo.toml

@@ -10,11 +10,12 @@ edition = "2021"
 
 
 [dependencies]
 [dependencies]
 # Darkfi
 # Darkfi
-darkfi = {path = "../../", features = ["async-daemonize", "validator", "rpc"]}
+darkfi = {path = "../../", features = ["async-daemonize", "validator", "rpc", "bs58"]}
 darkfi-sdk = {path = "../../src/sdk"}
 darkfi-sdk = {path = "../../src/sdk"}
 darkfi-serial = {version = "0.5.0", features = ["async"]}
 darkfi-serial = {version = "0.5.0", features = ["async"]}
 
 
 # Misc
 # Misc
+bs58 = "0.5.1"
 tracing = "0.1.41"
 tracing = "0.1.41"
 num-bigint = "0.4.6"
 num-bigint = "0.4.6"
 
 
@@ -34,6 +35,7 @@ smol = "2.0.2"
 serde = {version = "1.0.228", features = ["derive"]}
 serde = {version = "1.0.228", features = ["derive"]}
 structopt = "0.3.26"
 structopt = "0.3.26"
 structopt-toml = "0.5.1"
 structopt-toml = "0.5.1"
+toml = "0.9.8"
 
 
 [lints]
 [lints]
 workspace = true
 workspace = true

+ 52 - 6
bin/minerd/minerd.toml

@@ -9,10 +9,56 @@
 # PoW miner number of threads to use
 # PoW miner number of threads to use
 #threads = 4
 #threads = 4
 
 
-# JSON-RPC settings
-[rpc]
-# JSON-RPC listen URL
-rpc_listen = "tcp://127.0.0.1:28467"
+# Polling rate to ask darkfid for mining jobs
+#polling_rate = 2
 
 
-# Disabled RPC methods
-#rpc_disabled_methods = []
+# Stop mining at given height (0 mines forever)
+#stop_at_height = 0
+
+# Blockchain network to use
+network = "testnet"
+
+# Localnet blockchain network configuration
+[network_config."localnet"]
+# Wallet mining address to receive mining rewards
+recipient = "YOUR_WALLET_ADDRESS_HERE"
+
+# Optional contract spend hook to use in the mining reward
+#spend_hook = "YOUR_SPEND_HOOK_HERE"
+
+# Optional contract user data to use in the mining reward.
+# This is not arbitrary data.
+#user_data = "YOUR_USER_DATA_HERE"
+
+# darkfid JSON-RPC endpoint
+endpoint = "tcp://127.0.0.1:8240"
+
+# Testnet blockchain network configuration
+[network_config."testnet"]
+# Wallet mining address to receive mining rewards
+recipient = "YOUR_WALLET_ADDRESS_HERE"
+
+# Optional contract spend hook to use in the mining reward
+#spend_hook = "YOUR_SPEND_HOOK_HERE"
+
+# Optional contract user data to use in the mining reward.
+# This is not arbitrary data.
+#user_data = "YOUR_USER_DATA_HERE"
+
+# darkfid JSON-RPC endpoint
+endpoint = "tcp://127.0.0.1:8340"
+
+# Mainnet blockchain network configuration
+[network_config."mainnet"]
+# Wallet mining address to receive mining rewards
+recipient = "YOUR_WALLET_ADDRESS_HERE"
+
+# Optional contract spend hook to use in the mining reward
+#spend_hook = "YOUR_SPEND_HOOK_HERE"
+
+# Optional contract user data to use in the mining reward.
+# This is not arbitrary data.
+#user_data = "YOUR_USER_DATA_HERE"
+
+# darkfid JSON-RPC endpoint
+endpoint = "tcp://127.0.0.1:8440"

+ 0 - 54
bin/minerd/src/error.rs

@@ -1,54 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2025 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use darkfi::rpc::jsonrpc::{ErrorCode::ServerError, JsonError, JsonResult};
-
-/// Custom RPC errors available for minerd.
-/// Please sort them sensefully.
-pub enum RpcError {
-    // Parsing errors
-    TargetParseError = -32101,
-    BlockParseError = -32102,
-
-    // Miner errors
-    MiningFailed = -32201,
-    StopFailed = -32202,
-}
-
-fn to_tuple(e: RpcError) -> (i32, String) {
-    let msg = match e {
-        // Parsing errors
-        RpcError::TargetParseError => "Target parse error",
-        RpcError::BlockParseError => "Block parse error",
-        // Miner errors
-        RpcError::MiningFailed => "Mining block failed",
-        RpcError::StopFailed => "Failed to stop previous request",
-    };
-
-    (e as i32, msg.to_string())
-}
-
-pub fn server_error(e: RpcError, id: u16, msg: Option<&str>) -> JsonResult {
-    let (code, default_msg) = to_tuple(e);
-
-    if let Some(message) = msg {
-        return JsonError::new(ServerError(code), Some(message.to_string()), id).into()
-    }
-
-    JsonError::new(ServerError(code), Some(default_msg), id).into()
-}

+ 134 - 126
bin/minerd/src/lib.rs

@@ -16,60 +16,116 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
  */
 
 
-use std::{collections::HashSet, sync::Arc};
+use std::{collections::HashMap, sync::Arc};
 
 
 use smol::{
 use smol::{
     channel::{Receiver, Sender},
     channel::{Receiver, Sender},
-    lock::Mutex,
+    lock::RwLock,
 };
 };
-use tracing::{error, info};
+use tracing::{debug, error, info};
+use url::Url;
 
 
 use darkfi::{
 use darkfi::{
-    rpc::{
-        server::{listen_and_serve, RequestHandler},
-        settings::RpcSettings,
-    },
+    rpc::util::JsonValue,
     system::{sleep, ExecutorPtr, StoppableTask, StoppableTaskPtr},
     system::{sleep, ExecutorPtr, StoppableTask, StoppableTaskPtr},
-    Error, Result,
+    Error,
 };
 };
+use darkfi_sdk::crypto::Keypair;
 
 
-/// Daemon error codes
-mod error;
-
-/// JSON-RPC server methods
+/// darkfid JSON-RPC related methods
 mod rpc;
 mod rpc;
+use rpc::{polling_task, DarkfidRpcClient};
+
+/// Auxiliary structure representing miner node configuration.
+pub struct MinerNodeConfig {
+    /// PoW miner number of threads to use
+    threads: usize,
+    /// Polling rate to ask darkfid for mining jobs
+    polling_rate: u64,
+    /// Stop mining at this height (0 mines forever)
+    stop_at_height: u32,
+    /// Wallet mining configuration to receive mining rewards
+    wallet_config: HashMap<String, JsonValue>,
+}
+
+impl Default for MinerNodeConfig {
+    fn default() -> Self {
+        Self::new(
+            1,
+            5,
+            0,
+            HashMap::from([(
+                String::from("recipient"),
+                JsonValue::String(Keypair::default().public.to_string()),
+            )]),
+        )
+    }
+}
+
+impl MinerNodeConfig {
+    pub fn new(
+        threads: usize,
+        polling_rate: u64,
+        stop_at_height: u32,
+        wallet_config: HashMap<String, JsonValue>,
+    ) -> Self {
+        Self { threads, polling_rate, stop_at_height, wallet_config }
+    }
+}
 
 
 /// Atomic pointer to the DarkFi mining node
 /// Atomic pointer to the DarkFi mining node
 pub type MinerNodePtr = Arc<MinerNode>;
 pub type MinerNodePtr = Arc<MinerNode>;
 
 
 /// Structure representing a DarkFi mining node
 /// Structure representing a DarkFi mining node
 pub struct MinerNode {
 pub struct MinerNode {
-    /// PoW miner number of threads to use
-    threads: usize,
-    /// Stop mining at this height
-    stop_at_height: u32,
+    /// Node configuration
+    config: MinerNodeConfig,
     /// Sender to stop miner threads
     /// Sender to stop miner threads
     sender: Sender<()>,
     sender: Sender<()>,
     /// Receiver to stop miner threads
     /// Receiver to stop miner threads
     stop_signal: Receiver<()>,
     stop_signal: Receiver<()>,
-    /// JSON-RPC connection tracker
-    rpc_connections: Mutex<HashSet<StoppableTaskPtr>>,
+    /// JSON-RPC client to execute requests to darkfid daemon
+    rpc_client: RwLock<DarkfidRpcClient>,
 }
 }
 
 
 impl MinerNode {
 impl MinerNode {
-    pub fn new(
-        threads: usize,
-        stop_at_height: u32,
-        sender: Sender<()>,
-        stop_signal: Receiver<()>,
-    ) -> MinerNodePtr {
-        Arc::new(Self {
-            threads,
-            stop_at_height,
-            sender,
-            stop_signal,
-            rpc_connections: Mutex::new(HashSet::new()),
-        })
+    pub async fn new(config: MinerNodeConfig, endpoint: Url, ex: &ExecutorPtr) -> MinerNodePtr {
+        // Initialize the smol channels to send signal between the threads
+        let (sender, stop_signal) = smol::channel::bounded(1);
+
+        // Initialize JSON-RPC client
+        let rpc_client = RwLock::new(DarkfidRpcClient::new(endpoint, ex.clone()).await);
+
+        Arc::new(Self { config, sender, stop_signal, rpc_client })
+    }
+
+    /// Auxiliary function to abort pending job.
+    pub async fn abort_pending(&self) {
+        // Check if a pending request is being processed
+        debug!(target: "minerd::abort_pending", "Checking if a pending job is being processed...");
+        if self.stop_signal.receiver_count() <= 1 {
+            debug!(target: "minerd::rpc", "No pending job!");
+            return
+        }
+
+        info!(target: "minerd::abort_pending", "Pending job is in progress, sending stop signal...");
+        // Send stop signal to worker
+        if let Err(e) = self.sender.try_send(()) {
+            error!(target: "minerd::abort_pending", "Failed to stop pending job: {e}");
+            return
+        }
+
+        // Wait for worker to terminate
+        info!(target: "minerd::abort_pending", "Waiting for job to terminate...");
+        while self.stop_signal.receiver_count() > 1 {
+            sleep(1).await;
+        }
+        info!(target: "minerd::abort_pending", "Pending job terminated!");
+
+        // Consume channel item so its empty again
+        if let Err(e) = self.stop_signal.try_recv() {
+            error!(target: "minerd::abort_pending", "Failed to cleanup stop signal channel: {e}");
+        }
     }
     }
 }
 }
 
 
@@ -80,77 +136,68 @@ pub type MinerdPtr = Arc<Minerd>;
 pub struct Minerd {
 pub struct Minerd {
     /// Miner node instance conducting the mining operations
     /// Miner node instance conducting the mining operations
     node: MinerNodePtr,
     node: MinerNodePtr,
-    /// JSON-RPC background task
-    rpc_task: StoppableTaskPtr,
+    /// Miner darkfid polling background task
+    polling_task: StoppableTaskPtr,
 }
 }
 
 
 impl Minerd {
 impl Minerd {
     /// Initialize a DarkFi mining daemon.
     /// Initialize a DarkFi mining daemon.
     ///
     ///
-    /// Corresponding communication channels are setup to generate a new `MinerNode`,
-    /// and a new task is generated to handle the JSON-RPC API.
-    pub fn init(threads: usize, stop_at_height: u32) -> MinerdPtr {
+    /// Generate a new `MinerNode` and a new task to handle the darkfid
+    /// polling.
+    pub async fn init(config: MinerNodeConfig, endpoint: Url, ex: &ExecutorPtr) -> MinerdPtr {
         info!(target: "minerd::Minerd::init", "Initializing a new mining daemon...");
         info!(target: "minerd::Minerd::init", "Initializing a new mining daemon...");
 
 
-        // Initialize the smol channels to send signal between the threads
-        let (sender, stop_signal) = smol::channel::bounded(1);
-
         // Generate the node
         // Generate the node
-        let node = MinerNode::new(threads, stop_at_height, sender, stop_signal);
+        let node = MinerNode::new(config, endpoint, ex).await;
 
 
-        // Generate the JSON-RPC task
-        let rpc_task = StoppableTask::new();
+        // Generate the polling task
+        let polling_task = StoppableTask::new();
 
 
         info!(target: "minerd::Minerd::init", "Mining daemon initialized successfully!");
         info!(target: "minerd::Minerd::init", "Mining daemon initialized successfully!");
 
 
-        Arc::new(Self { node, rpc_task })
+        Arc::new(Self { node, polling_task })
     }
     }
 
 
-    /// Start the DarkFi mining daemon in the given executor, using the provided JSON-RPC listen url.
-    pub fn start(&self, executor: &ExecutorPtr, rpc_settings: &RpcSettings) {
+    /// Start the DarkFi mining daemon in the given executor.
+    pub fn start(&self, ex: &ExecutorPtr) {
         info!(target: "minerd::Minerd::start", "Starting mining daemon...");
         info!(target: "minerd::Minerd::start", "Starting mining daemon...");
 
 
-        // Start the JSON-RPC task
-        let node_ = self.node.clone();
-        self.rpc_task.clone().start(
-            listen_and_serve(rpc_settings.clone(), self.node.clone(), None, executor.clone()),
-            |res| async move {
+        // Start the polling task
+        self.polling_task.clone().start(
+            polling_task(self.node.clone(), ex.clone()),
+            |res| async {
                 match res {
                 match res {
-                    Ok(()) | Err(Error::RpcServerStopped) => node_.stop_connections().await,
-                    Err(e) => error!(target: "minerd::Minerd::start", "Failed starting JSON-RPC server: {e}"),
+                    Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ }
+                    Err(e) => {
+                        error!(target: "minerd::Minerd::start", "Failed starting polling task: {e}")
+                    }
                 }
                 }
             },
             },
-            Error::RpcServerStopped,
-            executor.clone(),
+            Error::DetachedTaskStopped,
+            ex.clone(),
         );
         );
 
 
         info!(target: "minerd::Minerd::start", "Mining daemon started successfully!");
         info!(target: "minerd::Minerd::start", "Mining daemon started successfully!");
     }
     }
 
 
     /// Stop the DarkFi mining daemon.
     /// Stop the DarkFi mining daemon.
-    pub async fn stop(&self) -> Result<()> {
+    pub async fn stop(&self) {
         info!(target: "minerd::Minerd::stop", "Terminating mining daemon...");
         info!(target: "minerd::Minerd::stop", "Terminating mining daemon...");
 
 
+        // Stop the polling task
+        info!(target: "minerd::Minerd::stop", "Stopping polling task...");
+        self.polling_task.stop().await;
+
         // Stop the mining node
         // Stop the mining node
         info!(target: "minerd::Minerd::stop", "Stopping miner threads...");
         info!(target: "minerd::Minerd::stop", "Stopping miner threads...");
-        if self.node.stop_signal.is_empty() {
-            self.node.sender.send(()).await?;
-        }
-        while self.node.stop_signal.receiver_count() > 1 {
-            sleep(1).await;
-        }
+        self.node.abort_pending().await;
 
 
-        // Stop the JSON-RPC task
-        info!(target: "minerd::Minerd::stop", "Stopping JSON-RPC server...");
-        self.rpc_task.stop().await;
-
-        // Consume channel item so its empty again
-        if self.node.stop_signal.is_full() {
-            self.node.stop_signal.recv().await?;
-        }
+        // Close the JSON-RPC client
+        info!(target: "minerd::Minerd::stop", "Stopping JSON-RPC client...");
+        self.node.stop_rpc_client().await;
 
 
         info!(target: "minerd::Minerd::stop", "Mining daemon terminated successfully!");
         info!(target: "minerd::Minerd::stop", "Mining daemon terminated successfully!");
-        Ok(())
     }
     }
 }
 }
 
 
@@ -158,7 +205,6 @@ impl Minerd {
 use {
 use {
     darkfi::util::logger::{setup_test_logger, Level},
     darkfi::util::logger::{setup_test_logger, Level},
     tracing::warn,
     tracing::warn,
-    url::Url,
 };
 };
 
 
 #[test]
 #[test]
@@ -166,7 +212,7 @@ use {
 ///
 ///
 /// First we initialize a daemon, start it and then perform
 /// First we initialize a daemon, start it and then perform
 /// couple of restarts to verify everything works as expected.
 /// couple of restarts to verify everything works as expected.
-fn minerd_programmatic_control() -> Result<()> {
+fn minerd_programmatic_control() {
     // We check this error so we can execute same file tests in parallel,
     // We check this error so we can execute same file tests in parallel,
     // otherwise second one fails to init logger here.
     // otherwise second one fails to init logger here.
     if setup_test_logger(
     if setup_test_logger(
@@ -182,74 +228,36 @@ fn minerd_programmatic_control() -> Result<()> {
         warn!(target: "minerd_programmatic_control", "Logger already initialized");
         warn!(target: "minerd_programmatic_control", "Logger already initialized");
     }
     }
 
 
-    // Daemon configuration
-    let threads = 4;
-    let rpc_settings =
-        RpcSettings { listen: Url::parse("tcp://127.0.0.1:28467")?, ..RpcSettings::default() };
-
     // Create an executor and communication signals
     // Create an executor and communication signals
     let ex = Arc::new(smol::Executor::new());
     let ex = Arc::new(smol::Executor::new());
     let (signal, shutdown) = smol::channel::unbounded::<()>();
     let (signal, shutdown) = smol::channel::unbounded::<()>();
 
 
-    // Generate a dummy mining job
-    let target = darkfi::rpc::util::JsonValue::String(
-        num_bigint::BigUint::from_bytes_le(&[0xFF; 32]).to_string(),
-    );
-    let block = darkfi::rpc::util::JsonValue::String(darkfi::util::encoding::base64::encode(
-        &darkfi_serial::serialize(&darkfi::blockchain::BlockInfo::default()),
-    ));
-    let mining_job = darkfi::rpc::jsonrpc::JsonRequest::new(
-        "mine",
-        darkfi::rpc::util::JsonValue::Array(vec![target, block]),
-    );
-
-    easy_parallel::Parallel::new()
-        .each(0..threads, |_| smol::block_on(ex.run(shutdown.recv())))
-        .finish(|| {
+    easy_parallel::Parallel::new().each(0..1, |_| smol::block_on(ex.run(shutdown.recv()))).finish(
+        || {
             smol::block_on(async {
             smol::block_on(async {
                 // Initialize a daemon
                 // Initialize a daemon
-                let daemon = Minerd::init(threads, 0);
-
-                // Start it
-                daemon.start(&ex, &rpc_settings);
-
-                // Generate a JSON-RPC client to send mining jobs
-                let mut rpc_client =
-                    darkfi::rpc::client::RpcClient::new(rpc_settings.listen.clone(), ex.clone())
-                        .await;
-                while rpc_client.is_err() {
-                    rpc_client = darkfi::rpc::client::RpcClient::new(
-                        rpc_settings.listen.clone(),
-                        ex.clone(),
-                    )
-                    .await;
-                }
-                let rpc_client = rpc_client.unwrap();
-
-                // Send a mining job but stop the daemon after it starts mining
-                smol::future::or(
-                    async {
-                        let _ = rpc_client.request(mining_job).await;
-                    },
-                    async {
-                        // Wait node to start mining
-                        darkfi::system::sleep(2).await;
-                        daemon.stop().await.unwrap();
-                    },
+                let daemon = Minerd::init(
+                    MinerNodeConfig::default(),
+                    Url::parse("tcp://127.0.0.1:12345").unwrap(),
+                    &ex,
                 )
                 )
                 .await;
                 .await;
-                rpc_client.stop().await;
+
+                // Start it
+                daemon.start(&ex);
+
+                // Stop it
+                daemon.stop().await;
 
 
                 // Start it again
                 // Start it again
-                daemon.start(&ex, &rpc_settings);
+                daemon.start(&ex);
 
 
                 // Stop it
                 // Stop it
-                daemon.stop().await.unwrap();
+                daemon.stop().await;
 
 
                 // Shutdown entirely
                 // Shutdown entirely
                 drop(signal);
                 drop(signal);
             })
             })
-        });
-
-    Ok(())
+        },
+    );
 }
 }

+ 139 - 14
bin/minerd/src/main.rs

@@ -16,15 +16,23 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
  */
 
 
-use std::sync::Arc;
+use std::{collections::HashMap, str::FromStr};
 
 
-use smol::{stream::StreamExt, Executor};
+use smol::{fs::read_to_string, stream::StreamExt};
 use structopt_toml::{serde::Deserialize, structopt::StructOpt, StructOptToml};
 use structopt_toml::{serde::Deserialize, structopt::StructOpt, StructOptToml};
-use tracing::info;
+use tracing::{debug, error, info};
+use url::Url;
 
 
-use darkfi::{async_daemonize, cli_desc, rpc::settings::RpcSettingsOpt, Error, Result};
+use darkfi::{
+    async_daemonize, cli_desc, rpc::util::JsonValue, system::ExecutorPtr,
+    util::path::get_config_path, Error, Result,
+};
+use darkfi_sdk::{
+    crypto::{pasta_prelude::PrimeField, FuncId, PublicKey},
+    pasta::pallas,
+};
 
 
-use minerd::Minerd;
+use minerd::{MinerNodeConfig, Minerd};
 
 
 const CONFIG_FILE: &str = "minerd.toml";
 const CONFIG_FILE: &str = "minerd.toml";
 const CONFIG_FILE_CONTENTS: &str = include_str!("../minerd.toml");
 const CONFIG_FILE_CONTENTS: &str = include_str!("../minerd.toml");
@@ -37,18 +45,22 @@ struct Args {
     /// Configuration file to use
     /// Configuration file to use
     config: Option<String>,
     config: Option<String>,
 
 
-    #[structopt(flatten)]
-    /// JSON-RPC settings
-    rpc: RpcSettingsOpt,
-
     #[structopt(short, long, default_value = "4")]
     #[structopt(short, long, default_value = "4")]
     /// PoW miner number of threads to use
     /// PoW miner number of threads to use
     threads: usize,
     threads: usize,
 
 
+    #[structopt(short, long, default_value = "2")]
+    /// Polling rate to ask darkfid for mining jobs
+    polling_rate: u64,
+
     #[structopt(long, default_value = "0")]
     #[structopt(long, default_value = "0")]
-    /// Refuse mining at given height (0 mines forever)
+    /// Stop mining at given height (0 mines forever)
     stop_at_height: u32,
     stop_at_height: u32,
 
 
+    #[structopt(short, long, default_value = "testnet")]
+    /// Blockchain network to use
+    network: String,
+
     #[structopt(short, long)]
     #[structopt(short, long)]
     /// Set log file to ouput into
     /// Set log file to ouput into
     log: Option<String>,
     log: Option<String>,
@@ -58,18 +70,131 @@ struct Args {
     verbose: u8,
     verbose: u8,
 }
 }
 
 
+#[derive(Clone, Debug, Deserialize, StructOpt, StructOptToml)]
+#[structopt()]
+/// Defines a blockchain network configuration.
+/// Default values correspond to a local network.
+pub struct BlockchainNetwork {
+    #[structopt(short, long, default_value = "tcp://127.0.0.1:8240")]
+    /// darkfid JSON-RPC endpoint
+    endpoint: Url,
+
+    #[structopt(long, default_value = "")]
+    /// Wallet mining address to receive mining rewards
+    recipient: String,
+
+    #[structopt(long)]
+    /// Optional contract spend hook to use in the mining reward
+    spend_hook: Option<String>,
+
+    #[structopt(long)]
+    /// Optional contract user data to use in the mining reward.
+    /// This is not arbitrary data.
+    user_data: Option<String>,
+}
+
+/// Auxiliary function to parse minerd configuration file and extract
+/// requested blockchain network config.
+pub async fn parse_blockchain_config(
+    config: Option<String>,
+    network: &str,
+) -> Result<BlockchainNetwork> {
+    // Grab config path
+    let config_path = get_config_path(config, CONFIG_FILE)?;
+    debug!(target: "minerd", "Parsing configuration file: {config_path:?}");
+
+    // Parse TOML file contents
+    let contents = read_to_string(&config_path).await?;
+    let contents: toml::Value = match toml::from_str(&contents) {
+        Ok(v) => v,
+        Err(e) => {
+            error!(target: "minerd", "Failed parsing TOML config: {e}");
+            return Err(Error::ParseFailed("Failed parsing TOML config"))
+        }
+    };
+
+    // Grab requested network config
+    let Some(table) = contents.as_table() else { return Err(Error::ParseFailed("TOML not a map")) };
+    let Some(network_configs) = table.get("network_config") else {
+        return Err(Error::ParseFailed("TOML does not contain network configurations"))
+    };
+    let Some(network_configs) = network_configs.as_table() else {
+        return Err(Error::ParseFailed("`network_config` not a map"))
+    };
+    let Some(network_config) = network_configs.get(network) else {
+        return Err(Error::ParseFailed("TOML does not contain requested network configuration"))
+    };
+    let network_config = toml::to_string(&network_config).unwrap();
+    let network_config =
+        match BlockchainNetwork::from_iter_with_toml::<Vec<String>>(&network_config, vec![]) {
+            Ok(v) => v,
+            Err(e) => {
+                error!(target: "minerd", "Failed parsing requested network configuration: {e}");
+                return Err(Error::ParseFailed("Failed parsing requested network configuration"))
+            }
+        };
+    debug!(target: "minerd", "Parsed network configuration: {network_config:?}");
+
+    Ok(network_config)
+}
+
 async_daemonize!(realmain);
 async_daemonize!(realmain);
-async fn realmain(args: Args, ex: Arc<Executor<'static>>) -> Result<()> {
+async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
     info!(target: "minerd", "Starting DarkFi Mining Daemon...");
     info!(target: "minerd", "Starting DarkFi Mining Daemon...");
-    let daemon = Minerd::init(args.threads, args.stop_at_height);
-    daemon.start(&ex, &args.rpc.into());
+
+    // Grab blockchain network configuration
+    let blockchain_config = match args.network.as_str() {
+        "localnet" => parse_blockchain_config(args.config, "localnet").await?,
+        "testnet" => parse_blockchain_config(args.config, "testnet").await?,
+        "mainnet" => parse_blockchain_config(args.config, "mainnet").await?,
+        _ => {
+            error!(target: "minerd", "Unsupported chain `{}`", args.network);
+            return Err(Error::UnsupportedChain)
+        }
+    };
+    debug!(target: "minerd", "Blockchain config: {blockchain_config:?}");
+
+    // Parse the network wallet configuration
+    if PublicKey::from_str(&blockchain_config.recipient).is_err() {
+        return Err(Error::InvalidAddress)
+    }
+    let mut wallet_config = HashMap::from([(
+        String::from("recipient"),
+        JsonValue::String(blockchain_config.recipient),
+    )]);
+
+    if let Some(spend_hook) = &blockchain_config.spend_hook {
+        if FuncId::from_str(spend_hook).is_err() {
+            return Err(Error::ParseFailed("Invalid spend hook"))
+        }
+        wallet_config.insert(String::from("spend_hook"), JsonValue::String(spend_hook.to_string()));
+    }
+
+    if let Some(user_data_string) = &blockchain_config.user_data {
+        let bytes: [u8; 32] = match bs58::decode(&user_data_string).into_vec()?.try_into() {
+            Ok(b) => b,
+            Err(_) => return Err(Error::ParseFailed("Invalid user data")),
+        };
+        let user_data: Option<pallas::Base> = pallas::Base::from_repr(bytes).into();
+        if user_data.is_none() {
+            return Err(Error::ParseFailed("Invalid user data"))
+        }
+        wallet_config
+            .insert(String::from("user_data"), JsonValue::String(user_data_string.to_string()));
+    }
+
+    // Generate the daemon
+    let miner_config =
+        MinerNodeConfig::new(args.threads, args.polling_rate, args.stop_at_height, wallet_config);
+    let daemon = Minerd::init(miner_config, blockchain_config.endpoint, &ex).await;
+    daemon.start(&ex);
 
 
     // Signal handling for graceful termination.
     // Signal handling for graceful termination.
     let (signals_handler, signals_task) = SignalHandler::new(ex)?;
     let (signals_handler, signals_task) = SignalHandler::new(ex)?;
     signals_handler.wait_termination(signals_task).await?;
     signals_handler.wait_termination(signals_task).await?;
     info!(target: "minerd", "Caught termination signal, cleaning up and exiting");
     info!(target: "minerd", "Caught termination signal, cleaning up and exiting");
 
 
-    daemon.stop().await?;
+    daemon.stop().await;
 
 
     info!(target: "minerd", "Shut down successfully");
     info!(target: "minerd", "Shut down successfully");
     Ok(())
     Ok(())

+ 226 - 122
bin/minerd/src/rpc.rs

@@ -16,162 +16,266 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
  */
 
 
-use std::collections::HashSet;
-
 use num_bigint::BigUint;
 use num_bigint::BigUint;
-use smol::lock::MutexGuard;
 use tracing::{debug, error, info};
 use tracing::{debug, error, info};
+use url::Url;
 
 
 use darkfi::{
 use darkfi::{
-    blockchain::header_store::{Header, HeaderHash},
-    rpc::{
-        jsonrpc::{ErrorCode, JsonError, JsonRequest, JsonResponse, JsonResult},
-        server::RequestHandler,
-        util::JsonValue,
-    },
-    system::{sleep, StoppableTaskPtr},
+    blockchain::{Header, HeaderHash},
+    rpc::{client::RpcClient, jsonrpc::JsonRequest, util::JsonValue},
+    system::{sleep, ExecutorPtr, StoppableTask},
     util::encoding::base64,
     util::encoding::base64,
     validator::pow::mine_block,
     validator::pow::mine_block,
+    Error, Result,
 };
 };
-use darkfi_serial::{async_trait, deserialize_async};
+use darkfi_serial::deserialize_async;
 
 
-use crate::{
-    error::{server_error, RpcError},
-    MinerNode,
-};
+use crate::{MinerNode, MinerNodePtr};
 
 
-#[async_trait]
-impl RequestHandler<()> for MinerNode {
-    async fn handle_request(&self, req: JsonRequest) -> JsonResult {
-        debug!(target: "minerd::rpc", "--> {}", req.stringify().unwrap());
+/// Structure to hold a JSON-RPC client and its config,
+/// so we can recreate it in case of an error.
+pub struct DarkfidRpcClient {
+    endpoint: Url,
+    ex: ExecutorPtr,
+    client: Option<RpcClient>,
+}
 
 
-        match req.method.as_str() {
-            "ping" => self.pong(req.id, req.params).await,
-            "abort" => self.abort(req.id, req.params).await,
-            "mine" => self.mine(req.id, req.params).await,
-            _ => JsonError::new(ErrorCode::MethodNotFound, None, req.id).into(),
-        }
+impl DarkfidRpcClient {
+    pub async fn new(endpoint: Url, ex: ExecutorPtr) -> Self {
+        let client = RpcClient::new(endpoint.clone(), ex.clone()).await.ok();
+        Self { endpoint, ex, client }
     }
     }
 
 
-    async fn connections_mut(&self) -> MutexGuard<'life0, HashSet<StoppableTaskPtr>> {
-        self.rpc_connections.lock().await
+    /// Stop the client.
+    pub async fn stop(&self) {
+        if let Some(ref client) = self.client {
+            client.stop().await
+        }
     }
     }
 }
 }
 
 
 impl MinerNode {
 impl MinerNode {
-    // RPCAPI:
-    // Signals miner daemon to abort mining pending request.
-    // Returns `true` on success.
-    //
-    // --> {"jsonrpc": "2.0", "method": "abort", "params": [], "id": 42}
-    // <-- {"jsonrpc": "2.0", "result": "true", "id": 42}
-    async fn abort(&self, id: u16, _params: JsonValue) -> JsonResult {
-        if let Some(e) = self.abort_pending(id).await {
-            return e
-        };
-        JsonResponse::new(JsonValue::Boolean(true), id).into()
-    }
+    /// Auxiliary function to poll configured darkfid daemon for a new
+    /// mining job.
+    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();
+            request_params.insert(String::from("header"), JsonValue::String(String::from(header)));
+            let params = match self
+                .darkfid_daemon_request("miner.get_header", &JsonValue::from(request_params))
+                .await
+            {
+                Ok(params) => params,
+                Err(e) => {
+                    error!(target: "minerd::rpc::poll", "darkfid poll failed: {e}");
+                    self.sleep().await?;
+                    continue
+                }
+            };
+            debug!(target: "minerd::rpc::poll", "Got reply: {params:?}");
 
 
-    // RPCAPI:
-    // Mine provided block header for requested mine target, using
-    // provided RandomX VM key, and return the corresponding nonce
-    // value.
-    //
-    // --> {"jsonrpc": "2.0", "method": "mine", "params": ["target", "randomx_key", "header"], "id": 42}
-    // --> {"jsonrpc": "2.0", "result": "nonce", "id": 42}
-    async fn mine(&self, id: u16, params: JsonValue) -> JsonResult {
-        // Verify parameters
-        if !params.is_array() {
-            return JsonError::new(ErrorCode::InvalidParams, None, id).into()
-        }
-        let params = params.get::<Vec<JsonValue>>().unwrap();
-        if params.len() != 3 ||
-            !params[0].is_string() ||
-            !params[1].is_string() ||
-            !params[2].is_string()
-        {
-            return JsonError::new(ErrorCode::InvalidParams, None, id).into()
+            // Verify response parameters
+            if !params.is_array() {
+                error!(target: "minerd::rpc::poll", "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::poll", "darkfid response is empty");
+                self.sleep().await?;
+                continue
+            }
+            if params.len() != 3 ||
+                !params[0].is_string() ||
+                !params[1].is_string() ||
+                !params[2].is_string()
+            {
+                error!(target: "minerd::rpc::poll", "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::poll", "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::poll", "Failed to parse RandomX key");
+                self.sleep().await?;
+                continue
+            };
+            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[2].get::<String>().unwrap()) else {
+                error!(target: "minerd::rpc::poll", "Failed to parse header bytes");
+                self.sleep().await?;
+                continue
+            };
+            let Ok(header) = deserialize_async::<Header>(&header_bytes).await else {
+                error!(target: "minerd::rpc::poll", "Failed to parse header");
+                self.sleep().await?;
+                continue
+            };
+
+            return Ok((randomx_key, target, header))
         }
         }
+    }
 
 
-        // Parse parameters
-        let Some(target_bytes) = base64::decode(params[0].get::<String>().unwrap()) else {
-            error!(target: "minerd::rpc", "Failed to parse target bytes");
-            return server_error(RpcError::TargetParseError, id, None)
-        };
-        let target = BigUint::from_bytes_le(&target_bytes);
-        let Some(randomx_key_bytes) = base64::decode(params[1].get::<String>().unwrap()) else {
-            error!(target: "minerd::rpc", "Failed to parse RandomX key bytes");
-            return server_error(RpcError::BlockParseError, id, None)
-        };
-        let Ok(randomx_key) = deserialize_async::<HeaderHash>(&randomx_key_bytes).await else {
-            error!(target: "minerd::rpc", "Failed to parse RandomX key");
-            return server_error(RpcError::BlockParseError, id, None)
-        };
-        let Some(header_bytes) = base64::decode(params[2].get::<String>().unwrap()) else {
-            error!(target: "minerd::rpc", "Failed to parse header bytes");
-            return server_error(RpcError::BlockParseError, id, None)
-        };
-        let Ok(mut header) = deserialize_async::<Header>(&header_bytes).await else {
-            error!(target: "minerd::rpc", "Failed to parse header");
-            return server_error(RpcError::BlockParseError, id, None)
+    /// Auxiliary function to submit a mining solution to configured
+    /// darkfid daemon.
+    async fn submit(&self, nonce: f64) -> String {
+        debug!(target: "minerd::rpc::submit", "Executing submit request to darkfid...");
+        let mut request_params = self.config.wallet_config.clone();
+        request_params.insert(String::from("nonce"), JsonValue::Number(nonce));
+        let result = match self
+            .darkfid_daemon_request("miner.submit_solution", &JsonValue::from(request_params))
+            .await
+        {
+            Ok(result) => result,
+            Err(e) => return format!("darkfid submit failed: {e}"),
         };
         };
-        let header_hash = header.hash();
-        info!(target: "minerd::rpc", "Received request to mine block header {header_hash} with key {randomx_key} for target: {target}");
+        debug!(target: "minerd::rpc::submit", "Got reply: {result:?}");
 
 
-        // If we have a requested mining height, we'll keep dropping here.
-        if self.stop_at_height > 0 && header.height >= self.stop_at_height {
-            info!(target: "minerd::rpc", "Reached requested mining height {}", self.stop_at_height);
-            return server_error(RpcError::MiningFailed, id, None)
+        // Parse response
+        match result.get::<String>() {
+            Some(result) => result.clone(),
+            None => format!("darkfid responded with invalid params: {result:?}"),
         }
         }
+    }
 
 
-        // Check if another request is being processed
-        if let Some(e) = self.abort_pending(id).await {
-            return e
-        };
+    /// Auxiliary function to execute a request towards the configured
+    /// darkfid daemon JSON-RPC endpoint.
+    async fn darkfid_daemon_request(&self, method: &str, params: &JsonValue) -> Result<JsonValue> {
+        let mut lock = self.rpc_client.write().await;
+        let req = JsonRequest::new(method, params.clone());
 
 
-        // Mine provided block header
-        info!(target: "minerd::rpc", "Mining block header {header_hash} with key {randomx_key} for target: {target}");
-        if let Err(e) =
-            mine_block(&target, &randomx_key, &mut header, self.threads, &self.stop_signal.clone())
-        {
-            error!(target: "minerd::rpc", "Failed mining block header {header_hash} with error: {e}");
-            return server_error(RpcError::MiningFailed, id, None)
+        // Check the client is initialized
+        if let Some(ref client) = lock.client {
+            // Execute request
+            if let Ok(rep) = client.request(req.clone()).await {
+                drop(lock);
+                return Ok(rep);
+            }
         }
         }
-        info!(target: "minerd::rpc", "Mined block header {header_hash} with nonce: {}", header.nonce);
 
 
-        // Return block header nonce
-        JsonResponse::new(JsonValue::Number(header.nonce as f64), id).into()
+        // Reset the rpc client in case of an error and try again
+        let client = RpcClient::new(lock.endpoint.clone(), lock.ex.clone()).await?;
+        let rep = client.request(req).await?;
+        lock.client = Some(client);
+        drop(lock);
+        Ok(rep)
     }
     }
 
 
-    /// Auxiliary function to abort pending request.
-    async fn abort_pending(&self, id: u16) -> Option<JsonResult> {
-        // Check if a pending request is being processed
-        info!(target: "minerd::rpc", "Checking if a pending request is being processed...");
-        if self.stop_signal.receiver_count() <= 1 {
-            info!(target: "minerd::rpc", "No pending requests!");
-            return None
-        }
+    /// Auxiliary function to stop current JSON-RPC client, if its
+    /// initialized.
+    pub async fn stop_rpc_client(&self) {
+        self.rpc_client.read().await.stop().await;
+    }
 
 
-        info!(target: "minerd::rpc", "Pending request is in progress, sending stop signal...");
-        // Send stop signal to worker
-        if self.sender.send(()).await.is_err() {
-            error!(target: "minerd::rpc", "Failed to stop pending request");
-            return Some(server_error(RpcError::StopFailed, id, None))
+    /// Auxiliary function to sleep for configured polling rate time.
+    async fn sleep(&self) -> Result<()> {
+        // Check if stop signal is received
+        if self.stop_signal.is_full() {
+            debug!(target: "minerd::rpc::sleep", "Stop signal received, exiting polling task");
+            return Err(Error::DetachedTaskStopped);
         }
         }
+        debug!(target: "minerd::rpc::sleep", "Sleeping for {} until next poll...", self.config.polling_rate);
+        sleep(self.config.polling_rate).await;
+        Ok(())
+    }
+}
+
+/// 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<()> {
+    // Initialize a dummy Header to use on first poll
+    let mut current_job = Header::default().hash().to_string();
+    loop {
+        // Poll darkfid for a mining job
+        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", "\tTarget - {target}");
+        debug!(target: "minerd::rpc::polling_task", "\tHeader - {header_hash}");
 
 
-        // Wait for worker to terminate
-        info!(target: "minerd::rpc", "Waiting for request to terminate...");
-        while self.stop_signal.receiver_count() > 1 {
-            sleep(1).await;
+        // Check if we are already processing this job
+        if header_hash == current_job {
+            debug!(target: "minerd::rpc::polling_task", "Already received job, skipping...");
+            miner.sleep().await?;
+            continue
         }
         }
-        info!(target: "minerd::rpc", "Pending request terminated!");
 
 
-        // Consume channel item so its empty again
-        if self.stop_signal.recv().await.is_err() {
-            error!(target: "minerd::rpc", "Failed to cleanup stop signal channel");
-            return Some(server_error(RpcError::StopFailed, id, None))
+        // Check if we reached the stop height
+        if miner.config.stop_at_height > 0 && header.height > miner.config.stop_at_height {
+            info!(target: "minerd::rpc::polling_task", "Reached requested mining height: {}", miner.config.stop_at_height);
+            info!(target: "minerd::rpc::polling_task", "Daemon can be safely terminated now!");
+            break
         }
         }
 
 
-        None
+        info!(target: "minerd::rpc::polling_task", "Received new job to mine block header {header_hash} with key {randomx_key} for target: 0x{target:064x}");
+
+        // Abord pending job
+        miner.abort_pending().await;
+
+        // Detach mining task
+        StoppableTask::new().start(
+            mining_task(miner.clone(), randomx_key, target, header),
+            |res| async {
+                match res {
+                    Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ }
+                    Err(e) => error!(target: "minerd::rpc::polling_task", "Failed starting mining task: {e}"),
+                }
+            },
+            Error::DetachedTaskStopped,
+            ex.clone(),
+        );
+
+        // Update current job
+        current_job = header_hash;
+
+        // Sleep until next poll
+        miner.sleep().await?;
+    }
+
+    Ok(())
+}
+
+/// Async task to mine provided header and submit solution to darkfid.
+async fn mining_task(
+    miner: MinerNodePtr,
+    randomx_key: HeaderHash,
+    target: BigUint,
+    mut header: Header,
+) -> Result<()> {
+    // Mine provided block header
+    let header_hash = header.hash().to_string();
+    info!(target: "minerd::rpc::mining_task", "Mining block header {header_hash} with key {randomx_key} for target: 0x{target:064x}");
+    if let Err(e) = mine_block(
+        &randomx_key,
+        &target,
+        &mut header,
+        miner.config.threads,
+        &miner.stop_signal.clone(),
+    ) {
+        error!(target: "minerd::rpc::mining_task", "Failed mining block header {header_hash} with error: {e}");
+        return Err(Error::DetachedTaskStopped)
     }
     }
+    info!(target: "minerd::rpc::mining_task", "Mined block header {header_hash} with nonce: {}", header.nonce);
+    info!(target: "minerd::rpc::mining_task", "Mined block header hash: {}", header.hash());
+
+    // Submit solution to darkfid
+    info!(target: "minerd::rpc::submit", "Submitting solution to darkfid...");
+    let result = miner.submit(header.nonce as f64).await;
+    info!(target: "minerd::rpc::submit", "Submition result: {result}");
+
+    Ok(())
 }
 }

+ 2 - 17
contrib/localnet/darkfid-five-nodes/darkfid0.toml

@@ -17,26 +17,11 @@ database = "darkfid0"
 # Confirmation threshold, denominated by number of blocks
 # Confirmation threshold, denominated by number of blocks
 threshold = 6
 threshold = 6
 
 
-# minerd JSON-RPC endpoint
-minerd_endpoint = "tcp://127.0.0.1:48467"
-
 # PoW block production target, in seconds
 # PoW block production target, in seconds
 pow_target = 60
 pow_target = 60
 
 
-# Participate in block production
-miner = true
-
-# Wallet address to receive mining rewards.
-# This is a dummy one so the miner can start,
-# replace with your own one.
-recipient = "9vw6WznKk7xEFQwwXhJWMMdjUPi3cXL8NrFKQpKifG1U"
-
-# Optional contract spend hook to use in the mining reward
-#spend_hook = "YOUR_SPEND_HOOK_HERE"
-
-# Optional contract user data to use in the mining reward.
-# This is not arbitrary data.
-#user_data = "YOUR_USER_DATA_HERE"
+# Optional fixed PoW difficulty, used for testing
+#pow_fixed_difficulty = 1
 
 
 # Skip syncing process and start node right away
 # Skip syncing process and start node right away
 skip_sync = true
 skip_sync = true

+ 2 - 14
contrib/localnet/darkfid-five-nodes/darkfid1.toml

@@ -17,23 +17,11 @@ database = "darkfid1"
 # Confirmation threshold, denominated by number of blocks
 # Confirmation threshold, denominated by number of blocks
 threshold = 6
 threshold = 6
 
 
-# minerd JSON-RPC endpoint
-minerd_endpoint = "tcp://127.0.0.1:48567"
-
 # PoW block production target, in seconds
 # PoW block production target, in seconds
 pow_target = 60
 pow_target = 60
 
 
-# Wallet address to receive mining rewards.
-# This is a dummy one so the miner can start,
-# replace with your own one.
-recipient = "9vw6WznKk7xEFQwwXhJWMMdjUPi3cXL8NrFKQpKifG1U"
-
-# Optional contract spend hook to use in the mining reward
-#spend_hook = "YOUR_SPEND_HOOK_HERE"
-
-# Optional contract user data to use in the mining reward.
-# This is not arbitrary data.
-#user_data = "YOUR_USER_DATA_HERE"
+# Optional fixed PoW difficulty, used for testing
+#pow_fixed_difficulty = 1
 
 
 # Skip syncing process and start node right away
 # Skip syncing process and start node right away
 skip_sync = false
 skip_sync = false

+ 2 - 14
contrib/localnet/darkfid-five-nodes/darkfid2.toml

@@ -17,23 +17,11 @@ database = "darkfid2"
 # Confirmation threshold, denominated by number of blocks
 # Confirmation threshold, denominated by number of blocks
 threshold = 6
 threshold = 6
 
 
-# minerd JSON-RPC endpoint
-minerd_endpoint = "tcp://127.0.0.1:48667"
-
 # PoW block production target, in seconds
 # PoW block production target, in seconds
 pow_target = 60
 pow_target = 60
 
 
-# Wallet address to receive mining rewards.
-# This is a dummy one so the miner can start,
-# replace with your own one.
-recipient = "9vw6WznKk7xEFQwwXhJWMMdjUPi3cXL8NrFKQpKifG1U"
-
-# Optional contract spend hook to use in the mining reward
-#spend_hook = "YOUR_SPEND_HOOK_HERE"
-
-# Optional contract user data to use in the mining reward.
-# This is not arbitrary data.
-#user_data = "YOUR_USER_DATA_HERE"
+# Optional fixed PoW difficulty, used for testing
+#pow_fixed_difficulty = 1
 
 
 # Skip syncing process and start node right away
 # Skip syncing process and start node right away
 skip_sync = false
 skip_sync = false

+ 2 - 14
contrib/localnet/darkfid-five-nodes/darkfid3.toml

@@ -17,23 +17,11 @@ database = "darkfid3"
 # Confirmation threshold, denominated by number of blocks
 # Confirmation threshold, denominated by number of blocks
 threshold = 6
 threshold = 6
 
 
-# minerd JSON-RPC endpoint
-minerd_endpoint = "tcp://127.0.0.1:48767"
-
 # PoW block production target, in seconds
 # PoW block production target, in seconds
 pow_target = 60
 pow_target = 60
 
 
-# Wallet address to receive mining rewards.
-# This is a dummy one so the miner can start,
-# replace with your own one.
-recipient = "9vw6WznKk7xEFQwwXhJWMMdjUPi3cXL8NrFKQpKifG1U"
-
-# Optional contract spend hook to use in the mining reward
-#spend_hook = "YOUR_SPEND_HOOK_HERE"
-
-# Optional contract user data to use in the mining reward.
-# This is not arbitrary data.
-#user_data = "YOUR_USER_DATA_HERE"
+# Optional fixed PoW difficulty, used for testing
+#pow_fixed_difficulty = 1
 
 
 # Skip syncing process and start node right away
 # Skip syncing process and start node right away
 skip_sync = false
 skip_sync = false

+ 2 - 14
contrib/localnet/darkfid-five-nodes/darkfid4.toml

@@ -17,23 +17,11 @@ database = "darkfid4"
 # Confirmation threshold, denominated by number of blocks
 # Confirmation threshold, denominated by number of blocks
 threshold = 6
 threshold = 6
 
 
-# minerd JSON-RPC endpoint
-minerd_endpoint = "tcp://127.0.0.1:48867"
-
 # PoW block production target, in seconds
 # PoW block production target, in seconds
 pow_target = 60
 pow_target = 60
 
 
-# Wallet address to receive mining rewards.
-# This is a dummy one so the miner can start,
-# replace with your own one.
-recipient = "9vw6WznKk7xEFQwwXhJWMMdjUPi3cXL8NrFKQpKifG1U"
-
-# Optional contract spend hook to use in the mining reward
-#spend_hook = "YOUR_SPEND_HOOK_HERE"
-
-# Optional contract user data to use in the mining reward.
-# This is not arbitrary data.
-#user_data = "YOUR_USER_DATA_HERE"
+# Optional fixed PoW difficulty, used for testing
+#pow_fixed_difficulty = 1
 
 
 # Skip syncing process and start node right away
 # Skip syncing process and start node right away
 skip_sync = false
 skip_sync = false

+ 24 - 6
contrib/localnet/darkfid-five-nodes/minerd0.toml

@@ -9,10 +9,28 @@
 # PoW miner number of threads to use
 # PoW miner number of threads to use
 threads = 1
 threads = 1
 
 
-# JSON-RPC settings
-[rpc]
-# JSON-RPC listen URL
-rpc_listen = "tcp://127.0.0.1:48467"
+# Polling rate to ask darkfid for mining jobs
+#polling_rate = 2
 
 
-# Disabled RPC methods
-#rpc_disabled_methods = []
+# Stop mining at given height (0 mines forever)
+#stop_at_height = 0
+
+# Blockchain network to use
+network = "localnet"
+
+# Localnet blockchain network configuration
+[network_config."localnet"]
+# Wallet mining address to receive mining rewards.
+# This is a dummy one so the miner can start,
+# replace with your own one.
+recipient = "9vw6WznKk7xEFQwwXhJWMMdjUPi3cXL8NrFKQpKifG1U"
+
+# Optional contract spend hook to use in the mining reward
+#spend_hook = "YOUR_SPEND_HOOK_HERE"
+
+# Optional contract user data to use in the mining reward.
+# This is not arbitrary data.
+#user_data = "YOUR_USER_DATA_HERE"
+
+# darkfid JSON-RPC endpoint
+endpoint = "tcp://127.0.0.1:48240"

+ 24 - 6
contrib/localnet/darkfid-five-nodes/minerd1.toml

@@ -9,10 +9,28 @@
 # PoW miner number of threads to use
 # PoW miner number of threads to use
 threads = 1
 threads = 1
 
 
-# JSON-RPC settings
-[rpc]
-# JSON-RPC listen URL
-rpc_listen = "tcp://127.0.0.1:48567"
+# Polling rate to ask darkfid for mining jobs
+#polling_rate = 2
 
 
-# Disabled RPC methods
-#rpc_disabled_methods = []
+# Stop mining at given height (0 mines forever)
+#stop_at_height = 0
+
+# Blockchain network to use
+network = "localnet"
+
+# Localnet blockchain network configuration
+[network_config."localnet"]
+# Wallet mining address to receive mining rewards.
+# This is a dummy one so the miner can start,
+# replace with your own one.
+recipient = "9vw6WznKk7xEFQwwXhJWMMdjUPi3cXL8NrFKQpKifG1U"
+
+# Optional contract spend hook to use in the mining reward
+#spend_hook = "YOUR_SPEND_HOOK_HERE"
+
+# Optional contract user data to use in the mining reward.
+# This is not arbitrary data.
+#user_data = "YOUR_USER_DATA_HERE"
+
+# darkfid JSON-RPC endpoint
+endpoint = "tcp://127.0.0.1:48340"

+ 24 - 6
contrib/localnet/darkfid-five-nodes/minerd2.toml

@@ -9,10 +9,28 @@
 # PoW miner number of threads to use
 # PoW miner number of threads to use
 threads = 1
 threads = 1
 
 
-# JSON-RPC settings
-[rpc]
-# JSON-RPC listen URL
-rpc_listen = "tcp://127.0.0.1:48667"
+# Polling rate to ask darkfid for mining jobs
+#polling_rate = 2
 
 
-# Disabled RPC methods
-#rpc_disabled_methods = []
+# Stop mining at given height (0 mines forever)
+#stop_at_height = 0
+
+# Blockchain network to use
+network = "localnet"
+
+# Localnet blockchain network configuration
+[network_config."localnet"]
+# Wallet mining address to receive mining rewards.
+# This is a dummy one so the miner can start,
+# replace with your own one.
+recipient = "9vw6WznKk7xEFQwwXhJWMMdjUPi3cXL8NrFKQpKifG1U"
+
+# Optional contract spend hook to use in the mining reward
+#spend_hook = "YOUR_SPEND_HOOK_HERE"
+
+# Optional contract user data to use in the mining reward.
+# This is not arbitrary data.
+#user_data = "YOUR_USER_DATA_HERE"
+
+# darkfid JSON-RPC endpoint
+endpoint = "tcp://127.0.0.1:48440"

+ 24 - 6
contrib/localnet/darkfid-five-nodes/minerd3.toml

@@ -9,10 +9,28 @@
 # PoW miner number of threads to use
 # PoW miner number of threads to use
 threads = 1
 threads = 1
 
 
-# JSON-RPC settings
-[rpc]
-# JSON-RPC listen URL
-rpc_listen = "tcp://127.0.0.1:48767"
+# Polling rate to ask darkfid for mining jobs
+#polling_rate = 2
 
 
-# Disabled RPC methods
-#rpc_disabled_methods = []
+# Stop mining at given height (0 mines forever)
+#stop_at_height = 0
+
+# Blockchain network to use
+network = "localnet"
+
+# Localnet blockchain network configuration
+[network_config."localnet"]
+# Wallet mining address to receive mining rewards.
+# This is a dummy one so the miner can start,
+# replace with your own one.
+recipient = "9vw6WznKk7xEFQwwXhJWMMdjUPi3cXL8NrFKQpKifG1U"
+
+# Optional contract spend hook to use in the mining reward
+#spend_hook = "YOUR_SPEND_HOOK_HERE"
+
+# Optional contract user data to use in the mining reward.
+# This is not arbitrary data.
+#user_data = "YOUR_USER_DATA_HERE"
+
+# darkfid JSON-RPC endpoint
+endpoint = "tcp://127.0.0.1:48540"

+ 24 - 6
contrib/localnet/darkfid-five-nodes/minerd4.toml

@@ -9,10 +9,28 @@
 # PoW miner number of threads to use
 # PoW miner number of threads to use
 threads = 1
 threads = 1
 
 
-# JSON-RPC settings
-[rpc]
-# JSON-RPC listen URL
-rpc_listen = "tcp://127.0.0.1:48867"
+# Polling rate to ask darkfid for mining jobs
+#polling_rate = 2
 
 
-# Disabled RPC methods
-#rpc_disabled_methods = []
+# Stop mining at given height (0 mines forever)
+#stop_at_height = 0
+
+# Blockchain network to use
+network = "localnet"
+
+# Localnet blockchain network configuration
+[network_config."localnet"]
+# Wallet mining address to receive mining rewards.
+# This is a dummy one so the miner can start,
+# replace with your own one.
+recipient = "9vw6WznKk7xEFQwwXhJWMMdjUPi3cXL8NrFKQpKifG1U"
+
+# Optional contract spend hook to use in the mining reward
+#spend_hook = "YOUR_SPEND_HOOK_HERE"
+
+# Optional contract user data to use in the mining reward.
+# This is not arbitrary data.
+#user_data = "YOUR_USER_DATA_HERE"
+
+# darkfid JSON-RPC endpoint
+endpoint = "tcp://127.0.0.1:48640"

+ 10 - 10
contrib/localnet/darkfid-five-nodes/tmux_sessions.sh

@@ -17,32 +17,32 @@ else
 fi
 fi
 
 
 tmux new-session -d -s $session -n "node0"
 tmux new-session -d -s $session -n "node0"
-tmux send-keys -t $session "$MINERD $verbose -c minerd0.toml" Enter
+tmux send-keys -t $session "$MINERD -c minerd0.toml $verbose" Enter
 sleep 1
 sleep 1
 tmux split-window -t $session -v -l 90%
 tmux split-window -t $session -v -l 90%
-tmux send-keys -t $session "$DARKFID $verbose -c darkfid0.toml" Enter
+tmux send-keys -t $session "$DARKFID -c darkfid0.toml $verbose" Enter
 sleep 2
 sleep 2
 tmux new-window -t $session -n "node1"
 tmux new-window -t $session -n "node1"
-tmux send-keys -t $session "$MINERD $verbose -c minerd1.toml" Enter
+tmux send-keys -t $session "$MINERD -c minerd1.toml $verbose" Enter
 sleep 1
 sleep 1
 tmux split-window -t $session -v -l 90%
 tmux split-window -t $session -v -l 90%
-tmux send-keys -t $session "$DARKFID $verbose -c darkfid1.toml" Enter
+tmux send-keys -t $session "$DARKFID -c darkfid1.toml $verbose" Enter
 sleep 2
 sleep 2
 tmux new-window -t $session -n "node2"
 tmux new-window -t $session -n "node2"
-tmux send-keys -t $session "$MINERD $verbose -c minerd2.toml" Enter
+tmux send-keys -t $session "$MINERD -c minerd2.toml $verbose" Enter
 sleep 1
 sleep 1
 tmux split-window -t $session -v -l 90%
 tmux split-window -t $session -v -l 90%
-tmux send-keys -t $session "$DARKFID $verbose -c darkfid2.toml" Enter
+tmux send-keys -t $session "$DARKFID -c darkfid2.toml $verbose" Enter
 sleep 2
 sleep 2
 tmux new-window -t $session -n "node3"
 tmux new-window -t $session -n "node3"
-tmux send-keys -t $session "$MINERD $verbose -c minerd3.toml" Enter
+tmux send-keys -t $session "$MINERD -c minerd3.toml $verbose" Enter
 sleep 1
 sleep 1
 tmux split-window -t $session -v -l 90%
 tmux split-window -t $session -v -l 90%
-tmux send-keys -t $session "$DARKFID $verbose -c darkfid3.toml" Enter
+tmux send-keys -t $session "$DARKFID -c darkfid3.toml $verbose" Enter
 sleep 2
 sleep 2
 tmux new-window -t $session -n "node4"
 tmux new-window -t $session -n "node4"
-tmux send-keys -t $session "$MINERD $verbose -c minerd4.toml" Enter
+tmux send-keys -t $session "$MINERD -c minerd4.toml $verbose" Enter
 sleep 1
 sleep 1
 tmux split-window -t $session -v -l 90%
 tmux split-window -t $session -v -l 90%
-tmux send-keys -t $session "$DARKFID $verbose -c darkfid4.toml" Enter
+tmux send-keys -t $session "$DARKFID -c darkfid4.toml $verbose" Enter
 tmux attach -t $session
 tmux attach -t $session

+ 2 - 2
contrib/localnet/darkfid-single-node/README.md

@@ -9,12 +9,12 @@ a testing wallet and pass its address to the `darkfid`
 config, so the wallet gets the block rewards the node
 config, so the wallet gets the block rewards the node
 produces. We generate a wallet, set it as the default
 produces. We generate a wallet, set it as the default
 and set its address as the `recipient` field in
 and set its address as the `recipient` field in
-`darkfid.toml`, using the porvided automated script:
+`minerd.toml`, using the porvided automated script:
 ```
 ```
 % ./init-wallet.sh
 % ./init-wallet.sh
 ```
 ```
 
 
-Then start `darkfid` and wait until its initialized:
+Then start the daemones and wait until `darkfid` is initialized:
 ```
 ```
 % ./tmux_sessions.sh
 % ./tmux_sessions.sh
 ```
 ```

+ 1 - 1
contrib/localnet/darkfid-single-node/clean.sh

@@ -1,3 +1,3 @@
 #!/bin/sh
 #!/bin/sh
 rm -rf darkfid drk
 rm -rf darkfid drk
-sed -i -e "s|recipient =.*|recipient = \"9vw6WznKk7xEFQwwXhJWMMdjUPi3cXL8NrFKQpKifG1U\"|g" darkfid.toml
+sed -i -e "s|recipient =.*|recipient = \"9vw6WznKk7xEFQwwXhJWMMdjUPi3cXL8NrFKQpKifG1U\"|g" minerd.toml

+ 0 - 15
contrib/localnet/darkfid-single-node/darkfid.toml

@@ -17,27 +17,12 @@ database = "darkfid"
 # Confirmation threshold, denominated by number of blocks
 # Confirmation threshold, denominated by number of blocks
 threshold = 1
 threshold = 1
 
 
-# minerd JSON-RPC endpoint
-minerd_endpoint = "tcp://127.0.0.1:48467"
-
 # PoW block production target, in seconds
 # PoW block production target, in seconds
 pow_target = 10
 pow_target = 10
 
 
 # Optional fixed PoW difficulty, used for testing
 # Optional fixed PoW difficulty, used for testing
 pow_fixed_difficulty = 1
 pow_fixed_difficulty = 1
 
 
-# Wallet address to receive mining rewards.
-# This is a dummy one so the miner can start,
-# replace with your own one.
-recipient = "9vw6WznKk7xEFQwwXhJWMMdjUPi3cXL8NrFKQpKifG1U"
-
-# Optional contract spend hook to use in the mining reward
-#spend_hook = "6iW9nywZYvyhcM7P1iLwYkh92rvYtREDsC8hgqf2GLuT"
-
-# Optional contract user data to use in the mining reward.
-# This is not arbitrary data.
-#user_data = "YOUR_USER_DATA_HERE"
-
 # Skip syncing process and start node right away
 # Skip syncing process and start node right away
 skip_sync = true
 skip_sync = true
 
 

+ 1 - 1
contrib/localnet/darkfid-single-node/init-wallet.sh

@@ -7,4 +7,4 @@ $DRK wallet initialize
 $DRK wallet keygen
 $DRK wallet keygen
 $DRK wallet default-address 1
 $DRK wallet default-address 1
 wallet=$($DRK wallet address)
 wallet=$($DRK wallet address)
-sed -i -e "s|9vw6WznKk7xEFQwwXhJWMMdjUPi3cXL8NrFKQpKifG1U|$wallet|g" darkfid.toml
+sed -i -e "s|9vw6WznKk7xEFQwwXhJWMMdjUPi3cXL8NrFKQpKifG1U|$wallet|g" minerd.toml

+ 26 - 6
contrib/localnet/darkfid-single-node/minerd.toml

@@ -9,10 +9,30 @@
 # PoW miner number of threads to use
 # PoW miner number of threads to use
 #threads = 4
 #threads = 4
 
 
-# JSON-RPC settings
-[rpc]
-# JSON-RPC listen URL
-rpc_listen = "tcp://127.0.0.1:48467"
+# Polling rate to ask darkfid for mining jobs
+#polling_rate = 2
 
 
-# Disabled RPC methods
-#rpc_disabled_methods = []
+# Stop mining at given height (0 mines forever)
+#stop_at_height = 0
+
+# Blockchain network to use
+network = "localnet"
+
+# Localnet blockchain network configuration
+[network_config."localnet"]
+# Wallet mining address to receive mining rewards.
+# This is a dummy one so the miner can start,
+# replace with your own one.
+recipient = "9vw6WznKk7xEFQwwXhJWMMdjUPi3cXL8NrFKQpKifG1U"
+
+# Optional contract spend hook to use in the mining reward.
+# This is the DAO spend hook set for convinience,
+# replace with your own one.
+#spend_hook = "6iW9nywZYvyhcM7P1iLwYkh92rvYtREDsC8hgqf2GLuT"
+
+# Optional contract user data to use in the mining reward.
+# This is not arbitrary data.
+#user_data = "YOUR_USER_DATA_HERE"
+
+# darkfid JSON-RPC endpoint
+endpoint = "tcp://127.0.0.1:48240"

+ 20 - 20
contrib/localnet/darkfid-single-node/run-dao-test.sh

@@ -52,30 +52,30 @@ wait_token() {
 }
 }
 
 
 mint_dao() {
 mint_dao() {
-    $DRK dao create 20 10 10 0.67 MLDY > $OUTPUT_FOLDER/dao.toml
-    $DRK dao import MiladyMakerDAO < $OUTPUT_FOLDER/dao.toml
+    $DRK dao create 20 10 10 0.67 ANON > $OUTPUT_FOLDER/dao.toml
+    $DRK dao import AnonDAO < $OUTPUT_FOLDER/dao.toml
     $DRK dao list
     $DRK dao list
-    $DRK dao list MiladyMakerDAO
+    $DRK dao list AnonDAO
 
 
-    $DRK dao mint MiladyMakerDAO | tee $OUTPUT_FOLDER/dao-mint.tx | $DRK broadcast
+    $DRK dao mint AnonDAO | tee $OUTPUT_FOLDER/dao-mint.tx | $DRK broadcast
 }
 }
 
 
 wait_dao_mint() {
 wait_dao_mint() {
-    while [ "$($DRK dao list MiladyMakerDAO | grep '^Transaction hash: ' | awk '{print $3}')" = None ]; do
+    while [ "$($DRK dao list AnonDAO | grep '^Transaction hash: ' | awk '{print $3}')" = None ]; do
         sleep $SLEEP_TIME
         sleep $SLEEP_TIME
         sh ./sync-wallet.sh > /dev/null
         sh ./sync-wallet.sh > /dev/null
     done
     done
 }
 }
 
 
 fill_treasury() {
 fill_treasury() {
-    PUBKEY="$($DRK dao list MiladyMakerDAO | grep '^Notes Public key: ' | cut -d ' ' -f4)"
+    PUBKEY="$($DRK dao list AnonDAO | grep '^Notes Public key: ' | cut -d ' ' -f4)"
     SPEND_HOOK="$($DRK dao spend-hook)"
     SPEND_HOOK="$($DRK dao spend-hook)"
-    BULLA="$($DRK dao list MiladyMakerDAO | grep '^Bulla: ' | cut -d' ' -f2)"
-    $DRK transfer 20 WCKD "$PUBKEY" "$SPEND_HOOK" "$BULLA" | tee $OUTPUT_FOLDER/xfer.tx | $DRK broadcast
+    BULLA="$($DRK dao list AnonDAO | grep '^Bulla: ' | cut -d' ' -f2)"
+    $DRK transfer 20 DAWN "$PUBKEY" "$SPEND_HOOK" "$BULLA" | tee $OUTPUT_FOLDER/xfer.tx | $DRK broadcast
 }
 }
 
 
 dao_balance() {
 dao_balance() {
-    BALANCE=$($DRK dao balance MiladyMakerDAO 2>/dev/null)
+    BALANCE=$($DRK dao balance AnonDAO 2>/dev/null)
     # No tokens received at all yet
     # No tokens received at all yet
     if echo "$BALANCE" | grep -q "No unspent balances found"; then
     if echo "$BALANCE" | grep -q "No unspent balances found"; then
         echo 0
         echo 0
@@ -94,7 +94,7 @@ dao_balance() {
 }
 }
 
 
 wait_dao_treasury() {
 wait_dao_treasury() {
-    while [ "$(dao_balance WCKD)" = 0 ]; do
+    while [ "$(dao_balance DAWN)" = 0 ]; do
         sleep $SLEEP_TIME
         sleep $SLEEP_TIME
         sh ./sync-wallet.sh > /dev/null
         sh ./sync-wallet.sh > /dev/null
     done
     done
@@ -102,12 +102,12 @@ wait_dao_treasury() {
 
 
 propose() {
 propose() {
     MY_ADDR=$($DRK wallet address)
     MY_ADDR=$($DRK wallet address)
-    PROPOSAL="$($DRK dao propose-transfer MiladyMakerDAO 1 5 WCKD "$MY_ADDR" | cut -d' ' -f3)"
+    PROPOSAL="$($DRK dao propose-transfer AnonDAO 1 5 DAWN "$MY_ADDR" | cut -d' ' -f3)"
     $DRK dao proposal "$PROPOSAL" --mint-proposal | tee $OUTPUT_FOLDER/propose.tx | $DRK broadcast
     $DRK dao proposal "$PROPOSAL" --mint-proposal | tee $OUTPUT_FOLDER/propose.tx | $DRK broadcast
 }
 }
 
 
 wait_proposal() {
 wait_proposal() {
-    PROPOSAL="$($DRK dao proposals MiladyMakerDAO | cut -d' ' -f2)"
+    PROPOSAL="$($DRK dao proposals AnonDAO | cut -d' ' -f2)"
     while [ "$($DRK dao proposal $PROPOSAL | grep '^Proposal transaction hash: ' | awk '{print $4}')" = None ]; do
     while [ "$($DRK dao proposal $PROPOSAL | grep '^Proposal transaction hash: ' | awk '{print $4}')" = None ]; do
         sleep $SLEEP_TIME
         sleep $SLEEP_TIME
         sh ./sync-wallet.sh > /dev/null
         sh ./sync-wallet.sh > /dev/null
@@ -115,12 +115,12 @@ wait_proposal() {
 }
 }
 
 
 vote() {
 vote() {
-    PROPOSAL="$($DRK dao proposals MiladyMakerDAO | cut -d' ' -f2)"
+    PROPOSAL="$($DRK dao proposals AnonDAO | cut -d' ' -f2)"
     $DRK dao vote "$PROPOSAL" 1 | tee $OUTPUT_FOLDER/dao-vote.tx | $DRK broadcast
     $DRK dao vote "$PROPOSAL" 1 | tee $OUTPUT_FOLDER/dao-vote.tx | $DRK broadcast
 }
 }
 
 
 wait_vote() {
 wait_vote() {
-    PROPOSAL="$($DRK dao proposals MiladyMakerDAO | cut -d' ' -f2)"
+    PROPOSAL="$($DRK dao proposals AnonDAO | cut -d' ' -f2)"
     while [ "$($DRK dao proposal $PROPOSAL | grep '^Current proposal outcome: ' | awk '{print $4}')" != "Approved" ]; do
     while [ "$($DRK dao proposal $PROPOSAL | grep '^Current proposal outcome: ' | awk '{print $4}')" != "Approved" ]; do
         sleep $SLEEP_TIME
         sleep $SLEEP_TIME
         sh ./sync-wallet.sh > /dev/null
         sh ./sync-wallet.sh > /dev/null
@@ -128,12 +128,12 @@ wait_vote() {
 }
 }
 
 
 do_exec() {
 do_exec() {
-    PROPOSAL="$($DRK dao proposals MiladyMakerDAO | cut -d' ' -f2)"
+    PROPOSAL="$($DRK dao proposals AnonDAO | cut -d' ' -f2)"
     $DRK dao exec --early $PROPOSAL | tee $OUTPUT_FOLDER/dao-exec.tx | $DRK broadcast
     $DRK dao exec --early $PROPOSAL | tee $OUTPUT_FOLDER/dao-exec.tx | $DRK broadcast
 }
 }
 
 
 wait_exec() {
 wait_exec() {
-    PROPOSAL="$($DRK dao proposals MiladyMakerDAO | cut -d' ' -f2)"
+    PROPOSAL="$($DRK dao proposals AnonDAO | cut -d' ' -f2)"
     while [ -z "$($DRK dao proposal $PROPOSAL | grep '^Proposal was executed on transaction: ')" ]; do
     while [ -z "$($DRK dao proposal $PROPOSAL | grep '^Proposal was executed on transaction: ')" ]; do
         sleep $SLEEP_TIME
         sleep $SLEEP_TIME
         sh ./sync-wallet.sh > /dev/null
         sh ./sync-wallet.sh > /dev/null
@@ -141,10 +141,10 @@ wait_exec() {
 }
 }
 
 
 wait_token DRK
 wait_token DRK
-mint_token WCKD 42
-wait_token WCKD
-mint_token MLDY 20
-wait_token MLDY
+mint_token ANON 42
+wait_token ANON
+mint_token DAWN 20
+wait_token DAWN
 mint_dao
 mint_dao
 wait_dao_mint
 wait_dao_mint
 fill_treasury
 fill_treasury

+ 2 - 2
contrib/localnet/darkfid-small/README.md

@@ -3,6 +3,6 @@ darkfid localnet
 
 
 This will start three `darkfid` node instances
 This will start three `darkfid` node instances
 in localnet mode. Two of the nodes are activelly
 in localnet mode. Two of the nodes are activelly
-mining, while the other one is just a sync node.
+producing blocks, while the other one is just a sync node.
 We also start two `minerd` daemons to mine blocks,
 We also start two `minerd` daemons to mine blocks,
-one for each of the `darkfid` mining nodes.
+one for each of the `darkfid` block producing nodes.

+ 2 - 14
contrib/localnet/darkfid-small/darkfid0.toml

@@ -17,23 +17,11 @@ database = "darkfid0"
 # Confirmation threshold, denominated by number of blocks
 # Confirmation threshold, denominated by number of blocks
 threshold = 6
 threshold = 6
 
 
-# minerd JSON-RPC endpoint
-minerd_endpoint = "tcp://127.0.0.1:48467"
-
 # PoW block production target, in seconds
 # PoW block production target, in seconds
 pow_target = 20
 pow_target = 20
 
 
-# Wallet address to receive mining rewards.
-# This is a dummy one so the miner can start,
-# replace with your own one.
-recipient = "9vw6WznKk7xEFQwwXhJWMMdjUPi3cXL8NrFKQpKifG1U"
-
-# Optional contract spend hook to use in the mining reward
-#spend_hook = "YOUR_SPEND_HOOK_HERE"
-
-# Optional contract user data to use in the mining reward.
-# This is not arbitrary data.
-#user_data = "YOUR_USER_DATA_HERE"
+# Optional fixed PoW difficulty, used for testing
+#pow_fixed_difficulty = 1
 
 
 # Skip syncing process and start node right away
 # Skip syncing process and start node right away
 skip_sync = true
 skip_sync = true

+ 2 - 14
contrib/localnet/darkfid-small/darkfid1.toml

@@ -25,23 +25,11 @@ database = "darkfid1"
 # Confirmation threshold, denominated by number of blocks
 # Confirmation threshold, denominated by number of blocks
 threshold = 6
 threshold = 6
 
 
-# minerd JSON-RPC endpoint
-minerd_endpoint = "tcp://127.0.0.1:48567"
-
 # PoW block production target, in seconds
 # PoW block production target, in seconds
 pow_target = 20
 pow_target = 20
 
 
-# Wallet address to receive mining rewards.
-# This is a dummy one so the miner can start,
-# replace with your own one.
-recipient = "9vw6WznKk7xEFQwwXhJWMMdjUPi3cXL8NrFKQpKifG1U"
-
-# Optional contract spend hook to use in the mining reward
-#spend_hook = "YOUR_SPEND_HOOK_HERE"
-
-# Optional contract user data to use in the mining reward.
-# This is not arbitrary data.
-#user_data = "YOUR_USER_DATA_HERE"
+# Optional fixed PoW difficulty, used for testing
+#pow_fixed_difficulty = 1
 
 
 # Skip syncing process and start node right away
 # Skip syncing process and start node right away
 skip_sync = false
 skip_sync = false

+ 3 - 3
contrib/localnet/darkfid-small/darkfid2.toml

@@ -17,12 +17,12 @@ database = "darkfid2"
 # Confirmation threshold, denominated by number of blocks
 # Confirmation threshold, denominated by number of blocks
 threshold = 6
 threshold = 6
 
 
-# minerd JSON-RPC endpoint
-#minerd_endpoint = "tcp://127.0.0.1:28467"
-
 # PoW block production target, in seconds
 # PoW block production target, in seconds
 pow_target = 20
 pow_target = 20
 
 
+# Optional fixed PoW difficulty, used for testing
+#pow_fixed_difficulty = 1
+
 # Skip syncing process and start node right away
 # Skip syncing process and start node right away
 skip_sync = false
 skip_sync = false
 
 

+ 24 - 6
contrib/localnet/darkfid-small/minerd0.toml

@@ -9,10 +9,28 @@
 # PoW miner number of threads to use
 # PoW miner number of threads to use
 threads = 2
 threads = 2
 
 
-# JSON-RPC settings
-[rpc]
-# JSON-RPC listen URL
-rpc_listen = "tcp://127.0.0.1:48467"
+# Polling rate to ask darkfid for mining jobs
+#polling_rate = 2
 
 
-# Disabled RPC methods
-#rpc_disabled_methods = []
+# Stop mining at given height (0 mines forever)
+#stop_at_height = 0
+
+# Blockchain network to use
+network = "localnet"
+
+# Localnet blockchain network configuration
+[network_config."localnet"]
+# Wallet mining address to receive mining rewards.
+# This is a dummy one so the miner can start,
+# replace with your own one.
+recipient = "9vw6WznKk7xEFQwwXhJWMMdjUPi3cXL8NrFKQpKifG1U"
+
+# Optional contract spend hook to use in the mining reward
+#spend_hook = "YOUR_SPEND_HOOK_HERE"
+
+# Optional contract user data to use in the mining reward.
+# This is not arbitrary data.
+#user_data = "YOUR_USER_DATA_HERE"
+
+# darkfid JSON-RPC endpoint
+endpoint = "tcp://127.0.0.1:48240"

+ 24 - 6
contrib/localnet/darkfid-small/minerd1.toml

@@ -9,10 +9,28 @@
 # PoW miner number of threads to use
 # PoW miner number of threads to use
 threads = 2
 threads = 2
 
 
-# JSON-RPC settings
-[rpc]
-# JSON-RPC listen URL
-rpc_listen = "tcp://127.0.0.1:48567"
+# Polling rate to ask darkfid for mining jobs
+#polling_rate = 2
 
 
-# Disabled RPC methods
-#rpc_disabled_methods = []
+# Stop mining at given height (0 mines forever)
+#stop_at_height = 0
+
+# Blockchain network to use
+network = "localnet"
+
+# Localnet blockchain network configuration
+[network_config."localnet"]
+# Wallet mining address to receive mining rewards.
+# This is a dummy one so the miner can start,
+# replace with your own one.
+recipient = "9vw6WznKk7xEFQwwXhJWMMdjUPi3cXL8NrFKQpKifG1U"
+
+# Optional contract spend hook to use in the mining reward
+#spend_hook = "YOUR_SPEND_HOOK_HERE"
+
+# Optional contract user data to use in the mining reward.
+# This is not arbitrary data.
+#user_data = "YOUR_USER_DATA_HERE"
+
+# darkfid JSON-RPC endpoint
+endpoint = "tcp://127.0.0.1:48340"

+ 5 - 5
contrib/localnet/darkfid-small/tmux_sessions.sh

@@ -18,17 +18,17 @@ else
 fi
 fi
 
 
 tmux new-session -d -s $session -n "node0"
 tmux new-session -d -s $session -n "node0"
-tmux send-keys -t $session "$MINERD $verbose -c minerd0.toml" Enter
+tmux send-keys -t $session "$MINERD -c minerd0.toml $verbose" Enter
 sleep 1
 sleep 1
 tmux split-window -t $session -v -l 90%
 tmux split-window -t $session -v -l 90%
-tmux send-keys -t $session "$DARKFID $verbose -c darkfid0.toml" Enter
+tmux send-keys -t $session "$DARKFID -c darkfid0.toml $verbose" Enter
 sleep 2
 sleep 2
 tmux new-window -t $session -n "node1"
 tmux new-window -t $session -n "node1"
-tmux send-keys -t $session "$MINERD $verbose -c minerd1.toml" Enter
+tmux send-keys -t $session "$MINERD -c minerd1.toml $verbose" Enter
 sleep 1
 sleep 1
 tmux split-window -t $session -v -l 90%
 tmux split-window -t $session -v -l 90%
-tmux send-keys -t $session "$DARKFID $verbose -c darkfid1.toml" Enter
+tmux send-keys -t $session "$DARKFID -c darkfid1.toml $verbose" Enter
 sleep 2
 sleep 2
 tmux new-window -t $session -n "node2"
 tmux new-window -t $session -n "node2"
-tmux send-keys -t $session "$DARKFID $verbose -c darkfid2.toml" Enter
+tmux send-keys -t $session "$DARKFID -c darkfid2.toml $verbose" Enter
 tmux attach -t $session
 tmux attach -t $session

+ 1 - 1
doc/src/testnet/dao.md

@@ -729,7 +729,7 @@ P2Pool wallet address to use:
 {YOUR_DAO_P2POOL_WALLET_ADDRESS_CONFIGURATION}
 {YOUR_DAO_P2POOL_WALLET_ADDRESS_CONFIGURATION}
 ```
 ```
 
 
-Then configure a `darkfid` instance to mine for a DAO, by setting the
+Then configure a `minerd` instance to mine for a DAO, by setting the
 corresponding fields(uncomment if needed) as per retrieved
 corresponding fields(uncomment if needed) as per retrieved
 configuration:
 configuration:
 
 

+ 51 - 59
doc/src/testnet/node.md

@@ -28,27 +28,27 @@ and stays connected to the p2p network.
 * `drk` is a CLI wallet. It provides an interface to smart contracts such
 * `drk` is a CLI wallet. It provides an interface to smart contracts such
 as Money and DAO, manages our keys and coins, and scans the blockchain
 as Money and DAO, manages our keys and coins, and scans the blockchain
 to update our balances.
 to update our balances.
-* `minerd` is the DarkFi mining daemon. `darkfid` connects to it over
-RPC, and triggers commands to mine blocks.
+* `minerd` is the DarkFi mining daemon. Connects to `darkfid` over RPC,
+and requests new block headers to mine.
 
 
-The config files for `darkfid` and `drk` are sectioned into three parts,
+The config files for all three daemons are sectioned into three parts,
 each marked `[network_config]`. The sections look like this:
 each marked `[network_config]`. The sections look like this:
 
 
 * `[network_config."testnet"]`
 * `[network_config."testnet"]`
 * `[network_config."mainnet"]`
 * `[network_config."mainnet"]`
 * `[network_config."localnet"]`
 * `[network_config."localnet"]`
 
 
-At the top of the `darkfid` and `drk` config file, we can modify the
-network being used by changing the following line:
+At the top of each daemon config file, we can modify the network being
+used by changing the following line:
 
 
 ```toml
 ```toml
 # Blockchain network to use
 # Blockchain network to use
 network = "testnet"
 network = "testnet"
 ```
 ```
 
 
-This enables us to configure `darkfid` and `drk` for different contexts,
-namely mainnet, testnet and localnet. Mainnet is not active yet. Localnet
-can be setup by following the instructions [here](#local-deployment). The
+This enables us to configure the daemons for different contexts, namely
+mainnet, testnet and localnet. Mainnet is not active yet. Localnet can
+be setup by following the instructions [here](#local-deployment). The
 rest of this tutorial assumes we are setting up a testnet node.
 rest of this tutorial assumes we are setting up a testnet node.
 
 
 ## Compiling
 ## Compiling
@@ -191,10 +191,8 @@ rest of the tutorial (`darkfid` and `drk` handle this), but if you want
 to help secure the network, you can participate in the mining process
 to help secure the network, you can participate in the mining process
 by running the native `minerd` mining daemon.
 by running the native `minerd` mining daemon.
 
 
-To mine on DarkFI we need to expose the `minerd` RPC to the `darkfid`
-full node, which will initiate the mining process. We'll also need to
-add a recipient to `darkfid` that specifies where the mining rewards
-will be minted to. 
+To mine on DarkFI we need to add a recipient to `minerd` that specifies
+where the mining rewards will be minted to.
 
 
 First, compile it:
 First, compile it:
 
 
@@ -229,41 +227,19 @@ $ ./minerd
 Config file created in "~/.config/darkfi/minerd_config.toml". Please review it and try again.
 Config file created in "~/.config/darkfi/minerd_config.toml". Please review it and try again.
 ```
 ```
 
 
-Once that's in place, you can run it again and `minerd` will start,
-waiting for requests to mine blocks.
+You now have to configure  `minerd` to use your wallet address as the
+rewards recipient, when it retrieves blocks from `darkfid` to mine.
 
 
-```shell
-$ ./minerd
-
-14:20:06 [INFO] Starting DarkFi Mining Daemon...
-14:20:06 [INFO] Initializing a new mining daemon...
-14:20:06 [INFO] Mining daemon initialized successfully!
-14:20:06 [INFO] Starting mining daemon...
-14:20:06 [INFO] Mining daemon started successfully!
-```
-
-You now have to expose `minerd` RPC to `darkfid`, and configure it
-to use your wallet address as the rewards recipient, when submitting
-blocks to `minerd` to mine.
-
-Open your `darkfid` config file with a text editor (the default path
-is `~/.config/darkfi/darkfid_config.toml`). Find the `recipient` and
-`minerd_endpoint` options under `[network_config."testnet"]`, and
-uncomment them by removing the `#` character at the start of line,
-like this:
+Open your `minerd` config file with a text editor (the default path
+is `~/.config/darkfi/minerd_config.toml`) and replace the
+`YOUR_WALLET_ADDRESS_HERE` string with your `drk` wallet address:
 
 
 ```toml
 ```toml
-# Put your `minerd` endpoint here (default for testnet is in this example)
-minerd_endpoint = "tcp://127.0.0.1:28467"
-# Put the address from `drk wallet address` here
+# Put the address from `./drk wallet address` here
 recipient = "YOUR_WALLET_ADDRESS_HERE"
 recipient = "YOUR_WALLET_ADDRESS_HERE"
 ```
 ```
 
 
-Now ensure that `minerd_endpoint` is set to the same value as the
-`rpc_listen` address in your `minerd` config (the default path
-is `~/.config/darkfi/minerd_config.toml`). Finally, replace the
-`YOUR_WALLET_ADDRESS_HERE` string with your `drk` wallet address that
-you can retrieve as follows:
+You can retrieve your `drk` wallet address as follows:
 
 
 ```shell
 ```shell
 $ ./drk wallet address
 $ ./drk wallet address
@@ -271,10 +247,30 @@ $ ./drk wallet address
 CbaqFqGTgn86Zh9AjUeMw3DJyVCshaPSPFtmj6Cyd5yU
 CbaqFqGTgn86Zh9AjUeMw3DJyVCshaPSPFtmj6Cyd5yU
 ```
 ```
 
 
-Note: when modifying the `darkfid` config file to use with the
+Note: when modifying the `minerd` config file to use with the
 testnet, be sure to change the values under the section marked
 testnet, be sure to change the values under the section marked
 `[network_config."testnet"]` (not localnet or mainnet!).
 `[network_config."testnet"]` (not localnet or mainnet!).
 
 
+Once that's in place, you can run it again and `minerd` will start,
+polling `darkfid` for new block headers to mine.
+
+```shell
+$ ./minerd
+
+14:20:06 [INFO] Starting DarkFi Mining Daemon...
+14:20:06 [INFO] Initializing a new mining daemon...
+14:20:06 [INFO] Mining daemon initialized successfully!
+14:20:06 [INFO] Starting mining daemon...
+14:20:06 [INFO] Mining daemon started successfully!
+14:20:06 [INFO] Received new job to mine block header beb0...42aa with key 0edc...0679 for target: 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+14:20:06 [INFO] Mining block header beb0...42aa with key 0edc...0679 for target: 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+14:20:06 [INFO] Mined block header beb0...42aa with nonce: 1
+14:20:06 [INFO] Mined block header hash: 36fe...753c
+14:20:06 [INFO] Submitting solution to darkfid...
+14:20:06 [INFO] Submition result: accepted
+...
+```
+
 ### Darkfid
 ### Darkfid
 
 
 Now that `darkfid` configuration is in place, you can run it again and
 Now that `darkfid` configuration is in place, you can run it again and
@@ -307,8 +303,7 @@ As its syncing, you'll see periodic messages like this:
 This will give you an indication of the current progress. Keep it running,
 This will give you an indication of the current progress. Keep it running,
 and you should see a `Blockchain synced!` message after some time.
 and you should see a `Blockchain synced!` message after some time.
 
 
-If you're running `minerd`, you should see a notification from the
-`minerd` terminal like this:
+If you're running `minerd`, you should see a notification like this:
 
 
 ```shell
 ```shell
 ...
 ...
@@ -321,36 +316,33 @@ This means that `darkfid` and `minerd` are connected over RPC and
 
 
 ```shell
 ```shell
 ...
 ...
-14:23:56 [INFO] Mining block 4abc760a1f1c7198837e91c24d8e045e9fc9cb9fdf3a5fd45e184c25b03b0b51 for target:
-115792089237316195423570985008687907853269984665640564039457584007913129639935
-14:24:04 [INFO] Mined block 4abc760a1f1c7198837e91c24d8e045e9fc9cb9fdf3a5fd45e184c25b03b0b51 with nonce: 2
-14:24:06 [INFO] Received request to mine block 17e7428ecb3d911477f8452170d0822c831c6912027abb120e4b4c4cf01d6020 for target:
-115792089237316195423570985008687907853269984665640564039457584007913129639935
-14:24:06 [INFO] Checking if a pending request is being processed...
+14:23:56 [INFO] [RPC] Created new blocktemplate: address=9vw6...fG1U, spend_hook=-, user_data=-, hash=beb0...42aa
+14:24:04 [INFO] [RPC] Got solution submission for block template: beb0...42aa
+14:24:06 [INFO] [RPC] Mined block header hash: 36fe...753c
+14:24:06 [INFO] [RPC] Proposing new block to network
 ...
 ...
 ```
 ```
 
 
 When `darkfid` and `minerd` are correctly connected and you get an
 When `darkfid` and `minerd` are correctly connected and you get an
-error like this:
+error on `minerd` like this:
 
 
 ```shell
 ```shell
 ...
 ...
-[ERROR] minerd::rpc: Failed mining block f6b4a0f0c8f90905da271ec0add2e856939ef3b0d6cd5b28964d9c2b6d0a0fa9 with error:
-Miner task stopped
+[ERROR] Failed mining block header b757...5fb1 with error: Miner task stopped
 ...
 ...
 ```
 ```
 
 
 That's expected behavior. It means your setup is correct and you are
 That's expected behavior. It means your setup is correct and you are
-mining blocks. `Failed mining block` happens when a new block was
-received by `darkfid`, extending the current best fork, so it sends an
-interuption message to `minerd` to stop mining the current block and
-start mining the next height one.
+mining blocks. `Failed mining block header` happens when a new block
+was received by `darkfid`, extending the current best fork, so when
+`minerd` polls it again it retrieves the new block header to mine,
+interupting current mining workers to start mining the new one.
 
 
 Otherwise, you'll see a notification like this:
 Otherwise, you'll see a notification like this:
 
 
 ```shell
 ```shell
 ...
 ...
-[INFO] Mined block b6c7bd3545daa81d0e2e56ee780363beef6eb5b54579f54dca0cdd2a59989b76 with nonce: 266292
+[INFO] Mined block header 36fe...753c with nonce: 266292
 ...
 ...
 ```
 ```
 
 
@@ -430,7 +422,7 @@ $ cd contrib/localnet/darkfid-single-node/
 $ ./init-wallet.sh
 $ ./init-wallet.sh
 ```
 ```
 
 
-Then start `darkfid` and wait until its initialized:
+Then start the daemones and wait until `darkfid` is initialized:
 
 
 ```shell
 ```shell
 $ ./tmux_sessions.sh
 $ ./tmux_sessions.sh

+ 9 - 9
src/validator/pow.rs

@@ -396,9 +396,6 @@ impl PoWModule {
         threads: usize,
         threads: usize,
         stop_signal: &Receiver<()>,
         stop_signal: &Receiver<()>,
     ) -> Result<()> {
     ) -> Result<()> {
-        // Grab the next mine target
-        let target = self.next_mine_target()?;
-
         // Grab the RandomX key to use.
         // Grab the RandomX key to use.
         // We only use the next key when the next block is the
         // We only use the next key when the next block is the
         // height changing one.
         // height changing one.
@@ -410,7 +407,10 @@ impl PoWModule {
             &self.darkfi_rx_keys.0
             &self.darkfi_rx_keys.0
         };
         };
 
 
-        mine_block(&target, randomx_key, header, threads, stop_signal)
+        // Grab the next mine target
+        let target = self.next_mine_target()?;
+
+        mine_block(randomx_key, &target, header, threads, stop_signal)
     }
     }
 }
 }
 
 
@@ -435,8 +435,8 @@ fn get_mining_flags() -> RandomXFlags {
 
 
 /// Auxiliary function to mine provided header using a single thread.
 /// Auxiliary function to mine provided header using a single thread.
 fn single_thread_mine(
 fn single_thread_mine(
-    target: &BigUint,
     input: &HeaderHash,
     input: &HeaderHash,
+    target: &BigUint,
     header: &mut Header,
     header: &mut Header,
     stop_signal: &Receiver<()>,
     stop_signal: &Receiver<()>,
 ) -> Result<()> {
 ) -> Result<()> {
@@ -486,8 +486,8 @@ fn single_thread_mine(
 
 
 /// Auxiliary function to mine provided header using a multiple threads.
 /// Auxiliary function to mine provided header using a multiple threads.
 fn multi_thread_mine(
 fn multi_thread_mine(
-    target: &BigUint,
     input: &HeaderHash,
     input: &HeaderHash,
+    target: &BigUint,
     header: &mut Header,
     header: &mut Header,
     threads: usize,
     threads: usize,
     stop_signal: &Receiver<()>,
     stop_signal: &Receiver<()>,
@@ -614,8 +614,8 @@ fn multi_thread_mine(
 
 
 /// Mine provided header, based on provided PoW module next mine target.
 /// Mine provided header, based on provided PoW module next mine target.
 pub fn mine_block(
 pub fn mine_block(
-    target: &BigUint,
     input: &HeaderHash,
     input: &HeaderHash,
+    target: &BigUint,
     header: &mut Header,
     header: &mut Header,
     threads: usize,
     threads: usize,
     stop_signal: &Receiver<()>,
     stop_signal: &Receiver<()>,
@@ -634,8 +634,8 @@ pub fn mine_block(
             error!(target: "validator::pow::mine_block", "[MINER] Can't use 0 threads!");
             error!(target: "validator::pow::mine_block", "[MINER] Can't use 0 threads!");
             Err(Error::MinerTaskStopped)
             Err(Error::MinerTaskStopped)
         }
         }
-        1 => single_thread_mine(target, input, header, stop_signal),
-        _ => multi_thread_mine(target, input, header, threads, stop_signal),
+        1 => single_thread_mine(input, target, header, stop_signal),
+        _ => multi_thread_mine(input, target, header, threads, stop_signal),
     }
     }
 }
 }