darkfid: programmatic control daemon

This commit is contained in:
skoupidi
2024-09-30 19:35:22 +03:00
parent 850341ae0d
commit a2d68cc914
25 changed files with 565 additions and 453 deletions

View File

@@ -29,9 +29,6 @@ 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
# Participate in block production
miner = true
# Wallet address to receive mining rewards. # Wallet address to receive mining rewards.
# This is a dummy one so the miner can start, # This is a dummy one so the miner can start,
# replace with your own one. # replace with your own one.
@@ -130,14 +127,11 @@ database = "~/.local/darkfi/darkfid/testnet"
threshold = 6 threshold = 6
# minerd JSON-RPC endpoint # minerd JSON-RPC endpoint
minerd_endpoint = "tcp://127.0.0.1:28467" #minerd_endpoint = "tcp://127.0.0.1:28467"
# PoW block production target, in seconds # PoW block production target, in seconds
pow_target = 90 pow_target = 90
# Participate in block production
miner = false
# Wallet address to receive mining rewards. # Wallet address to receive mining rewards.
#recipient = "YOUR_WALLET_ADDRESS_HERE" #recipient = "YOUR_WALLET_ADDRESS_HERE"
@@ -238,14 +232,11 @@ database = "~/.local/darkfi/darkfid/mainnet"
threshold = 11 threshold = 11
# minerd JSON-RPC endpoint # minerd JSON-RPC endpoint
minerd_endpoint = "tcp://127.0.0.1:28467" #minerd_endpoint = "tcp://127.0.0.1:28467"
# PoW block production target, in seconds # PoW block production target, in seconds
pow_target = 90 pow_target = 90
# Participate in block production
miner = false
# Wallet address to receive mining rewards. # Wallet address to receive mining rewards.
#recipient = "YOUR_WALLET_ADDRESS_HERE" #recipient = "YOUR_WALLET_ADDRESS_HERE"

312
bin/darkfid/src/lib.rs Normal file
View File

@@ -0,0 +1,312 @@
/* This file is part of DarkFi (https://dark.fi)
*
* Copyright (C) 2020-2024 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, HashSet},
sync::Arc,
};
use log::{debug, error, info};
use smol::lock::Mutex;
use url::Url;
use darkfi::{
net::settings::Settings,
rpc::{
client::RpcChadClient,
jsonrpc::JsonSubscriber,
server::{listen_and_serve, RequestHandler},
},
system::{ExecutorPtr, StoppableTask, StoppableTaskPtr},
validator::{Validator, ValidatorConfig, ValidatorPtr},
Error, Result,
};
#[cfg(test)]
mod tests;
mod error;
use error::{server_error, RpcError};
/// JSON-RPC requests handler and methods
mod rpc;
mod rpc_blockchain;
mod rpc_tx;
/// Validator async tasks
pub mod task;
use task::{consensus::ConsensusInitTaskConfig, consensus_init_task};
/// P2P net protocols
mod proto;
use proto::{DarkfidP2pHandler, DarkfidP2pHandlerPtr};
/// 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: RpcChadClient,
}
impl MinerRpcClient {
pub async fn new(endpoint: Url, ex: ExecutorPtr) -> Result<Self> {
let client = RpcChadClient::new(endpoint.clone(), ex.clone()).await?;
Ok(Self { endpoint, ex, client })
}
}
/// Atomic pointer to the DarkFi node
pub type DarkfiNodePtr = Arc<DarkfiNode>;
/// Structure representing a DarkFi node
pub struct DarkfiNode {
/// P2P network protocols handler.
p2p_handler: DarkfidP2pHandlerPtr,
/// Validator(node) pointer
validator: ValidatorPtr,
/// Garbage collection task transactions batch size
txs_batch_size: usize,
/// A map of various subscribers exporting live info from the blockchain
subscribers: HashMap<&'static str, JsonSubscriber>,
/// JSON-RPC connection tracker
rpc_connections: Mutex<HashSet<StoppableTaskPtr>>,
/// JSON-RPC client to execute requests to the miner daemon
rpc_client: Option<Mutex<MinerRpcClient>>,
}
impl DarkfiNode {
pub async fn new(
p2p_handler: DarkfidP2pHandlerPtr,
validator: ValidatorPtr,
txs_batch_size: usize,
subscribers: HashMap<&'static str, JsonSubscriber>,
rpc_client: Option<Mutex<MinerRpcClient>>,
) -> DarkfiNodePtr {
Arc::new(Self {
p2p_handler,
validator,
txs_batch_size,
subscribers,
rpc_connections: Mutex::new(HashSet::new()),
rpc_client,
})
}
}
/// Atomic pointer to the DarkFi daemon
pub type DarkfidPtr = Arc<Darkfid>;
/// Structure representing a DarkFi daemon
pub struct Darkfid {
/// Darkfi node instance
node: DarkfiNodePtr,
/// `dnet` background task
dnet_task: StoppableTaskPtr,
/// JSON-RPC background task
rpc_task: StoppableTaskPtr,
/// Consensus protocol background task
consensus_task: StoppableTaskPtr,
}
impl Darkfid {
/// Initialize a DarkFi daemon.
///
/// Generates a new `DarkfiNode` for provided configuration,
/// along with all the corresponding background tasks.
pub async fn init(
sled_db: &sled_overlay::sled::Db,
config: &ValidatorConfig,
net_settings: &Settings,
minerd_endpoint: &Option<Url>,
txs_batch_size: &Option<usize>,
ex: &ExecutorPtr,
) -> Result<DarkfidPtr> {
info!(target: "darkfid::Darkfid::init", "Initializing a Darkfi daemon...");
// Initialize validator
let validator = Validator::new(&sled_db, config).await?;
// Initialize P2P network
let p2p_handler = DarkfidP2pHandler::init(net_settings, ex).await?;
// Grab blockchain network configured transactions batch size for garbage collection
let txs_batch_size = match txs_batch_size {
Some(b) => {
if *b > 0 {
*b
} else {
50
}
}
None => 50,
};
// Here we initialize various subscribers that can export live blockchain/consensus data.
let mut subscribers = HashMap::new();
subscribers.insert("blocks", JsonSubscriber::new("blockchain.subscribe_blocks"));
subscribers.insert("txs", JsonSubscriber::new("blockchain.subscribe_txs"));
subscribers.insert("proposals", JsonSubscriber::new("blockchain.subscribe_proposals"));
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) => {
let Ok(rpc_client) = MinerRpcClient::new(endpoint.clone(), ex.clone()).await else {
error!(target: "darkfid::Darkfid::init", "Failed to initialize miner daemon rpc client, check if minerd is running");
return Err(Error::RpcClientStopped)
};
Some(Mutex::new(rpc_client))
}
None => None,
};
// Initialize node
let node =
DarkfiNode::new(p2p_handler, validator, txs_batch_size, subscribers, rpc_client).await;
// Generate the background tasks
let dnet_task = StoppableTask::new();
let rpc_task = StoppableTask::new();
let consensus_task = StoppableTask::new();
info!(target: "darkfid::Darkfid::init", "Darkfi daemon initialized successfully!");
Ok(Arc::new(Self { node, dnet_task, rpc_task, consensus_task }))
}
/// Start the DarkFi daemon in the given executor, using the provided JSON-RPC listen url
/// and consensus initialization configuration.
pub async fn start(
&self,
executor: &ExecutorPtr,
rpc_listen: &Url,
config: &ConsensusInitTaskConfig,
) -> Result<()> {
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 {
error!(target: "darkfid::Darkfid::start", "Failed to ping miner daemon: {}", e);
return Err(Error::RpcClientStopped)
}
}
// Start the `dnet` task
info!(target: "darkfid::Darkfid::start", "Starting dnet subs task");
let dnet_sub_ = self.node.subscribers.get("dnet").unwrap().clone();
let p2p_ = self.node.p2p_handler.p2p.clone();
self.dnet_task.clone().start(
async move {
let dnet_sub = p2p_.dnet_subscribe().await;
loop {
let event = dnet_sub.receive().await;
debug!(target: "darkfid::Darkfid::dnet_task", "Got dnet event: {:?}", event);
dnet_sub_.notify(vec![event.into()].into()).await;
}
},
|res| async {
match res {
Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ }
Err(e) => error!(target: "darkfid::Darkfid::start", "Failed starting dnet subs task: {}", e),
}
},
Error::DetachedTaskStopped,
executor.clone(),
);
// Start the JSON-RPC task
info!(target: "darkfid::Darkfid::start", "Starting JSON-RPC server");
let node_ = self.node.clone();
self.rpc_task.clone().start(
listen_and_serve(rpc_listen.clone(), self.node.clone(), None, executor.clone()),
|res| async move {
match res {
Ok(()) | Err(Error::RpcServerStopped) => node_.stop_connections().await,
Err(e) => error!(target: "darkfid::Darkfid::start", "Failed starting JSON-RPC server: {}", e),
}
},
Error::RpcServerStopped,
executor.clone(),
);
// Start the P2P network
info!(target: "darkfid::Darkfid::start", "Starting P2P network");
self.node
.p2p_handler
.clone()
.start(executor, &self.node.validator, &self.node.subscribers)
.await?;
// Start the consensus protocol
info!(target: "darkfid::Darkfid::start", "Starting consensus protocol task");
self.consensus_task.clone().start(
consensus_init_task(
self.node.clone(),
config.clone(),
executor.clone(),
),
|res| async move {
match res {
Ok(()) | Err(Error::ConsensusTaskStopped) | Err(Error::MinerTaskStopped) => { /* Do nothing */ }
Err(e) => error!(target: "darkfid::Darkfid::start", "Failed starting consensus initialization task: {}", e),
}
},
Error::ConsensusTaskStopped,
executor.clone(),
);
info!(target: "darkfid::Darkfid::start", "Darkfi daemon started successfully!");
Ok(())
}
/// Stop the DarkFi daemon.
pub async fn stop(&self) -> Result<()> {
info!(target: "darkfid::Darkfid::stop", "Terminating Darkfi daemon...");
// Stop the `dnet` node
info!(target: "darkfid::Darkfid::stop", "Stopping dnet subs task...");
self.dnet_task.stop().await;
// Stop the JSON-RPC task
info!(target: "darkfid::Darkfid::stop", "Stopping JSON-RPC server...");
self.rpc_task.stop().await;
// Stop the P2P network
info!(target: "darkfid::Darkfid::stop", "Stopping P2P network protocols handler...");
self.node.p2p_handler.stop().await;
// Stop the consensus task
info!(target: "darkfid::Darkfid::stop", "Stopping consensus task...");
self.consensus_task.stop().await;
// Flush sled database data
info!(target: "darkfid::Darkfid::stop", "Flushing sled database...");
let flushed_bytes = self.node.validator.blockchain.sled_db.flush_async().await?;
info!(target: "darkfid::Darkfid::stop", "Flushed {} bytes", flushed_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.client.stop().await;
};
info!(target: "darkfid::Darkfid::stop", "Darkfi daemon terminated successfully!");
Ok(())
}
}

View File

@@ -16,13 +16,10 @@
* 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::{ use std::sync::Arc;
collections::{HashMap, HashSet},
sync::Arc,
};
use log::{debug, error, info}; use log::{debug, error, info};
use smol::{lock::Mutex, 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 url::Url; use url::Url;
@@ -31,40 +28,16 @@ use darkfi::{
blockchain::BlockInfo, blockchain::BlockInfo,
cli_desc, cli_desc,
net::settings::SettingsOpt, net::settings::SettingsOpt,
rpc::{ util::{
client::RpcChadClient, encoding::base64,
jsonrpc::JsonSubscriber, path::{expand_path, get_config_path},
server::{listen_and_serve, RequestHandler},
}, },
system::{StoppableTask, StoppableTaskPtr}, validator::ValidatorConfig,
util::{encoding::base64, path::expand_path},
validator::{Validator, ValidatorConfig, ValidatorPtr},
Error, Result, Error, Result,
}; };
use darkfi_serial::deserialize_async; use darkfi_serial::deserialize_async;
#[cfg(test)] use darkfid::{task::consensus::ConsensusInitTaskConfig, Darkfid};
mod tests;
mod error;
use error::{server_error, RpcError};
/// JSON-RPC requests handler and methods
mod rpc;
mod rpc_blockchain;
mod rpc_tx;
/// Validator async tasks
mod task;
use task::{consensus::ConsensusInitTaskConfig, consensus_init_task};
/// P2P net protocols
mod proto;
use proto::{DarkfidP2pHandler, DarkfidP2pHandlerPtr};
/// Utility functions
mod utils;
use utils::parse_blockchain_config;
const CONFIG_FILE: &str = "darkfid_config.toml"; const CONFIG_FILE: &str = "darkfid_config.toml";
const CONFIG_FILE_CONTENTS: &str = include_str!("../darkfid_config.toml"); const CONFIG_FILE_CONTENTS: &str = include_str!("../darkfid_config.toml");
@@ -103,129 +76,67 @@ struct Args {
pub struct BlockchainNetwork { pub struct BlockchainNetwork {
#[structopt(short, long, default_value = "tcp://127.0.0.1:8240")] #[structopt(short, long, default_value = "tcp://127.0.0.1:8240")]
/// JSON-RPC listen URL /// JSON-RPC listen URL
pub rpc_listen: Url, rpc_listen: Url,
#[structopt(long, default_value = "~/.local/darkfi/darkfid/localnet")] #[structopt(long, default_value = "~/.local/darkfi/darkfid/localnet")]
/// Path to blockchain database /// Path to blockchain database
pub database: String, database: String,
#[structopt(long, default_value = "3")] #[structopt(long, default_value = "3")]
/// Finalization threshold, denominated by number of blocks /// Finalization threshold, denominated by number of blocks
pub threshold: usize, threshold: usize,
#[structopt(long, default_value = "tcp://127.0.0.1:28467")] #[structopt(long)]
/// minerd JSON-RPC endpoint /// minerd JSON-RPC endpoint
pub minerd_endpoint: Url, minerd_endpoint: Option<Url>,
#[structopt(long, default_value = "10")] #[structopt(long, default_value = "10")]
/// PoW block production target, in seconds /// PoW block production target, in seconds
pub pow_target: u32, pow_target: u32,
#[structopt(long)] #[structopt(long)]
/// Optional fixed PoW difficulty, used for testing /// Optional fixed PoW difficulty, used for testing
pub pow_fixed_difficulty: Option<usize>, pow_fixed_difficulty: Option<usize>,
#[structopt(long)]
/// Participate in block production
pub miner: bool,
#[structopt(long)] #[structopt(long)]
/// Wallet address to receive mining rewards /// Wallet address to receive mining rewards
pub recipient: Option<String>, recipient: Option<String>,
#[structopt(long)] #[structopt(long)]
/// Optional contract spend hook to use in the mining reward /// Optional contract spend hook to use in the mining reward
pub spend_hook: Option<String>, spend_hook: Option<String>,
#[structopt(long)] #[structopt(long)]
/// Optional user data to use in the mining reward /// Optional user data to use in the mining reward
pub user_data: Option<String>, user_data: Option<String>,
#[structopt(long)] #[structopt(long)]
/// Skip syncing process and start node right away /// Skip syncing process and start node right away
pub skip_sync: bool, skip_sync: bool,
#[structopt(long)] #[structopt(long)]
/// Disable transaction's fee verification, used for testing /// Disable transaction's fee verification, used for testing
pub skip_fees: bool, skip_fees: bool,
#[structopt(long)] #[structopt(long)]
/// Optional sync checkpoint height /// Optional sync checkpoint height
pub checkpoint_height: Option<u32>, checkpoint_height: Option<u32>,
#[structopt(long)] #[structopt(long)]
/// Optional sync checkpoint hash /// Optional sync checkpoint hash
pub checkpoint: Option<String>, checkpoint: Option<String>,
#[structopt(long)] #[structopt(long)]
/// Optional bootstrap timestamp /// Optional bootstrap timestamp
pub bootstrap: Option<u64>, bootstrap: Option<u64>,
#[structopt(long)] #[structopt(long)]
/// Garbage collection task transactions batch size /// Garbage collection task transactions batch size
pub txs_batch_size: Option<usize>, txs_batch_size: Option<usize>,
/// P2P network settings /// P2P network settings
#[structopt(flatten)] #[structopt(flatten)]
pub net: SettingsOpt, net: SettingsOpt,
}
/// 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: Arc<smol::Executor<'static>>,
client: RpcChadClient,
}
impl MinerRpcCLient {
pub async fn new(endpoint: Url, ex: Arc<smol::Executor<'static>>) -> Result<Self> {
let client = RpcChadClient::new(endpoint.clone(), ex.clone()).await?;
Ok(Self { endpoint, ex, client })
}
}
/// Daemon structure
pub struct Darkfid {
/// P2P network protocols handler.
p2p_handler: DarkfidP2pHandlerPtr,
/// Validator(node) pointer
validator: ValidatorPtr,
/// Flag to specify node is a miner
miner: bool,
/// Garbage collection task transactions batch size
txs_batch_size: usize,
/// A map of various subscribers exporting live info from the blockchain
subscribers: HashMap<&'static str, JsonSubscriber>,
/// JSON-RPC connection tracker
rpc_connections: Mutex<HashSet<StoppableTaskPtr>>,
/// JSON-RPC client to execute requests to the miner daemon
rpc_client: Option<Mutex<MinerRpcCLient>>,
/// dnet JSON-RPC subscriber
dnet_sub: JsonSubscriber,
}
impl Darkfid {
pub async fn new(
p2p_handler: DarkfidP2pHandlerPtr,
validator: ValidatorPtr,
miner: bool,
txs_batch_size: usize,
subscribers: HashMap<&'static str, JsonSubscriber>,
rpc_client: Option<Mutex<MinerRpcCLient>>,
dnet_sub: JsonSubscriber,
) -> Self {
Self {
p2p_handler,
validator,
miner,
txs_batch_size,
subscribers,
rpc_connections: Mutex::new(HashSet::new()),
rpc_client,
dnet_sub,
}
}
} }
async_daemonize!(realmain); async_daemonize!(realmain);
@@ -279,165 +190,83 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
verify_fees: !blockchain_config.skip_fees, verify_fees: !blockchain_config.skip_fees,
}; };
// Initialize validator // Generate the daemon
let validator = Validator::new(&sled_db, config).await?; let daemon = Darkfid::init(
&sled_db,
// Here we initialize various subscribers that can export live blockchain/consensus data. &config,
let mut subscribers = HashMap::new(); &blockchain_config.net.into(),
subscribers.insert("blocks", JsonSubscriber::new("blockchain.subscribe_blocks")); &blockchain_config.minerd_endpoint,
subscribers.insert("txs", JsonSubscriber::new("blockchain.subscribe_txs")); &blockchain_config.txs_batch_size,
subscribers.insert("proposals", JsonSubscriber::new("blockchain.subscribe_proposals")); &ex,
// Initialize P2P network
let p2p_handler = DarkfidP2pHandler::init(&blockchain_config.net.into(), &ex).await?;
// Initialize JSON-RPC client to perform requests to minerd
let rpc_client = if blockchain_config.miner {
let Ok(rpc_client) =
MinerRpcCLient::new(blockchain_config.minerd_endpoint, ex.clone()).await
else {
error!(target: "darkfid", "Failed to initialize miner daemon rpc client, check if minerd is running");
return Err(Error::RpcClientStopped)
};
Some(Mutex::new(rpc_client))
} else {
None
};
// Grab blockchain network configured transactions batch size for garbage collection
let txs_batch_size = match blockchain_config.txs_batch_size {
Some(b) => {
if b > 0 {
b
} else {
50
}
}
None => 50,
};
info!(target: "darkfid", "Starting dnet subs task");
let dnet_sub = JsonSubscriber::new("dnet.subscribe_events");
let dnet_sub_ = dnet_sub.clone();
let p2p_ = p2p_handler.p2p.clone();
let dnet_task = StoppableTask::new();
dnet_task.clone().start(
async move {
let dnet_sub = p2p_.dnet_subscribe().await;
loop {
let event = dnet_sub.receive().await;
debug!(target: "darkfid", "Got dnet event: {:?}", event);
dnet_sub_.notify(vec![event.into()].into()).await;
}
},
|res| async {
match res {
Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ }
Err(e) => error!(target: "darkfid", "Failed starting dnet subs task: {}", e),
}
},
Error::DetachedTaskStopped,
ex.clone(),
);
// Initialize node
let darkfid = Darkfid::new(
p2p_handler.clone(),
validator.clone(),
blockchain_config.miner,
txs_batch_size,
subscribers.clone(),
rpc_client,
dnet_sub,
) )
.await; .await?;
let darkfid = Arc::new(darkfid);
info!(target: "darkfid", "Node initialized successfully!");
// Pinging minerd daemon to verify it listens // Start the daemon
if blockchain_config.miner { let config = ConsensusInitTaskConfig {
if let Err(e) = darkfid.ping_miner_daemon().await { skip_sync: blockchain_config.skip_sync,
error!(target: "darkfid", "Failed to ping miner daemon: {}", e); checkpoint_height: blockchain_config.checkpoint_height,
return Err(Error::RpcClientStopped) checkpoint: blockchain_config.checkpoint,
} miner: blockchain_config.minerd_endpoint.is_some(),
} recipient: blockchain_config.recipient,
spend_hook: blockchain_config.spend_hook,
// JSON-RPC server user_data: blockchain_config.user_data,
info!(target: "darkfid", "Starting JSON-RPC server"); bootstrap,
// Here we create a task variable so we can manually close the };
// task later. P2P tasks don't need this since it has its own daemon.start(&ex, &blockchain_config.rpc_listen, &config).await?;
// stop() function to shut down, also terminating the task we
// created for it.
let rpc_task = StoppableTask::new();
let darkfid_ = darkfid.clone();
rpc_task.clone().start(
listen_and_serve(blockchain_config.rpc_listen, darkfid.clone(), None, ex.clone()),
|res| async move {
match res {
Ok(()) | Err(Error::RpcServerStopped) => darkfid_.stop_connections().await,
Err(e) => error!(target: "darkfid", "Failed starting sync JSON-RPC server: {}", e),
}
},
Error::RpcServerStopped,
ex.clone(),
);
info!(target: "darkfid", "Starting P2P network");
p2p_handler.clone().start(&ex, &validator, &subscribers).await?;
// Consensus protocol
info!(target: "darkfid", "Starting consensus protocol task");
let consensus_task = StoppableTask::new();
consensus_task.clone().start(
consensus_init_task(
darkfid.clone(),
ConsensusInitTaskConfig {
skip_sync: blockchain_config.skip_sync,
checkpoint_height: blockchain_config.checkpoint_height,
checkpoint: blockchain_config.checkpoint,
miner: blockchain_config.miner,
recipient: blockchain_config.recipient,
spend_hook: blockchain_config.spend_hook,
user_data: blockchain_config.user_data,
bootstrap,
},
ex.clone(),
),
|res| async move {
match res {
Ok(()) | Err(Error::ConsensusTaskStopped) | Err(Error::MinerTaskStopped) => { /* Do nothing */ }
Err(e) => error!(target: "darkfid", "Failed starting consensus initialization task: {}", e),
}
},
Error::ConsensusTaskStopped,
ex.clone(),
);
// 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: "darkfid", "Caught termination signal, cleaning up and exiting..."); info!(target: "darkfid", "Caught termination signal, cleaning up and exiting...");
info!(target: "darkfid", "Stopping dnet subs task..."); daemon.stop().await?;
dnet_task.stop().await;
info!(target: "darkfid", "Stopping JSON-RPC server..."); info!(target: "darkfid", "Shut down successfully");
rpc_task.stop().await;
info!(target: "darkfid", "Stopping P2P network protocols handler...");
p2p_handler.stop().await;
info!(target: "darkfid", "Stopping consensus task...");
consensus_task.stop().await;
info!(target: "darkfid", "Flushing sled database...");
let flushed_bytes = sled_db.flush_async().await?;
info!(target: "darkfid", "Flushed {} bytes", flushed_bytes);
if let Some(ref rpc_client) = darkfid.rpc_client {
info!(target: "darkfid", "Stopping JSON-RPC client...");
rpc_client.lock().await.client.stop().await;
};
Ok(()) Ok(())
} }
/// Auxiliary function to parse darkfid 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: "darkfid", "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: "darkfid", "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: "darkfid", "Failed parsing requested network configuration: {}", e);
return Err(Error::ParseFailed("Failed parsing requested network configuration"))
}
};
debug!(target: "darkfid", "Parsed network configuration: {:?}", network_config);
Ok(network_config)
}

View File

@@ -38,12 +38,12 @@ use darkfi::{
use crate::{ use crate::{
error::{server_error, RpcError}, error::{server_error, RpcError},
Darkfid, DarkfiNode,
}; };
#[async_trait] #[async_trait]
#[rustfmt::skip] #[rustfmt::skip]
impl RequestHandler for Darkfid { impl RequestHandler for DarkfiNode {
async fn handle_request(&self, req: JsonRequest) -> JsonResult { async fn handle_request(&self, req: JsonRequest) -> JsonResult {
debug!(target: "darkfid::rpc", "--> {}", req.stringify().unwrap()); debug!(target: "darkfid::rpc", "--> {}", req.stringify().unwrap());
@@ -94,7 +94,7 @@ impl RequestHandler for Darkfid {
} }
} }
impl Darkfid { impl DarkfiNode {
// RPCAPI: // RPCAPI:
// Returns current system clock as `u64` (String) timestamp. // Returns current system clock as `u64` (String) timestamp.
// //
@@ -142,7 +142,7 @@ impl Darkfid {
return JsonError::new(ErrorCode::InvalidParams, None, id).into() return JsonError::new(ErrorCode::InvalidParams, None, id).into()
} }
self.dnet_sub.clone().into() self.subscribers.get("dnet").unwrap().clone().into()
} }
// RPCAPI: // RPCAPI:
@@ -222,7 +222,7 @@ impl Darkfid {
} }
} }
impl HandlerP2p for Darkfid { impl HandlerP2p for DarkfiNode {
fn p2p(&self) -> P2pPtr { fn p2p(&self) -> P2pPtr {
self.p2p_handler.p2p.clone() self.p2p_handler.p2p.clone()
} }

View File

@@ -32,9 +32,9 @@ use darkfi::{
util::encoding::base64, util::encoding::base64,
}; };
use crate::{server_error, Darkfid, RpcError}; use crate::{server_error, DarkfiNode, RpcError};
impl Darkfid { impl DarkfiNode {
// RPCAPI: // RPCAPI:
// Queries the blockchain database for a block in the given height. // Queries the blockchain database for a block in the given height.
// Returns a readable block upon success. // Returns a readable block upon success.

View File

@@ -29,10 +29,10 @@ use darkfi::{
util::encoding::base64, util::encoding::base64,
}; };
use super::Darkfid; use super::DarkfiNode;
use crate::{server_error, RpcError}; use crate::{server_error, RpcError};
impl Darkfid { impl DarkfiNode {
// RPCAPI: // RPCAPI:
// Simulate a network state transition with the given transaction. // Simulate a network state transition with the given transaction.
// Returns `true` if the transaction is valid, otherwise, a corresponding // Returns `true` if the transaction is valid, otherwise, a corresponding
@@ -122,13 +122,13 @@ impl Darkfid {
// Block production participants can directly perform // Block production participants can directly perform
// the state transition check and append to their // the state transition check and append to their
// pending transactions store. // pending transactions store.
let error_message = if self.miner { let error_message = if self.rpc_client.is_some() {
"Failed to append transaction to mempool" "Failed to append transaction to mempool"
} else { } else {
"Failed to validate state transition" "Failed to validate state transition"
}; };
// We'll perform the state transition check here. // We'll perform the state transition check here.
if let Err(e) = self.validator.append_tx(&tx, self.miner).await { if let Err(e) = self.validator.append_tx(&tx, self.rpc_client.is_some()).await {
error!(target: "darkfid::rpc::tx_broadcast", "{}: {}", error_message, e); error!(target: "darkfid::rpc::tx_broadcast", "{}: {}", error_message, e);
return server_error(RpcError::TxSimulationFail, id, None) return server_error(RpcError::TxSimulationFail, id, None)
}; };

View File

@@ -16,12 +16,12 @@
* 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::{str::FromStr, sync::Arc}; use std::str::FromStr;
use darkfi::{ use darkfi::{
blockchain::HeaderHash, blockchain::HeaderHash,
rpc::{jsonrpc::JsonNotification, util::JsonValue}, rpc::{jsonrpc::JsonNotification, util::JsonValue},
system::{sleep, StoppableTask, Subscription}, system::{sleep, ExecutorPtr, StoppableTask, Subscription},
util::{encoding::base64, time::Timestamp}, util::{encoding::base64, time::Timestamp},
Error, Result, Error, Result,
}; };
@@ -34,10 +34,11 @@ use log::{error, info};
use crate::{ use crate::{
task::{garbage_collect_task, miner::MinerRewardsRecipientConfig, miner_task, sync_task}, task::{garbage_collect_task, miner::MinerRewardsRecipientConfig, miner_task, sync_task},
Darkfid, DarkfiNodePtr,
}; };
/// Auxiliary structure representing node consensus init task configuration /// Auxiliary structure representing node consensus init task configuration
#[derive(Clone)]
pub struct ConsensusInitTaskConfig { pub struct ConsensusInitTaskConfig {
pub skip_sync: bool, pub skip_sync: bool,
pub checkpoint_height: Option<u32>, pub checkpoint_height: Option<u32>,
@@ -51,9 +52,9 @@ pub struct ConsensusInitTaskConfig {
/// Sync the node consensus state and start the corresponding task, based on node type. /// Sync the node consensus state and start the corresponding task, based on node type.
pub async fn consensus_init_task( pub async fn consensus_init_task(
node: Arc<Darkfid>, node: DarkfiNodePtr,
config: ConsensusInitTaskConfig, config: ConsensusInitTaskConfig,
ex: Arc<smol::Executor<'static>>, ex: ExecutorPtr,
) -> Result<()> { ) -> Result<()> {
// Check if network is configured to start in the future. // Check if network is configured to start in the future.
// NOTE: Always configure the network to start in the future when bootstrapping // NOTE: Always configure the network to start in the future when bootstrapping
@@ -100,15 +101,15 @@ pub async fn consensus_init_task(
Err(_) => return Err(Error::InvalidAddress), Err(_) => return Err(Error::InvalidAddress),
}; };
let spend_hook = match config.spend_hook { let spend_hook = match &config.spend_hook {
Some(s) => match FuncId::from_str(&s) { Some(s) => match FuncId::from_str(s) {
Ok(s) => Some(s), Ok(s) => Some(s),
Err(_) => return Err(Error::ParseFailed("Invalid spend hook")), Err(_) => return Err(Error::ParseFailed("Invalid spend hook")),
}, },
None => None, None => None,
}; };
let user_data = match config.user_data { let user_data = match &config.user_data {
Some(u) => { Some(u) => {
let bytes: [u8; 32] = match bs58::decode(&u).into_vec()?.try_into() { let bytes: [u8; 32] = match bs58::decode(&u).into_vec()?.try_into() {
Ok(b) => b, Ok(b) => b,
@@ -131,15 +132,9 @@ pub async fn consensus_init_task(
// Gracefully handle network disconnections // Gracefully handle network disconnections
loop { loop {
let result = if config.miner { let result = if config.miner {
miner_task( miner_task(&node, recipient_config.as_ref().unwrap(), config.skip_sync, &ex).await
node.clone(),
recipient_config.as_ref().unwrap(),
config.skip_sync,
ex.clone(),
)
.await
} else { } else {
replicator_task(node.clone(), ex.clone()).await replicator_task(&node, &ex).await
}; };
match result { match result {
@@ -160,7 +155,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: Arc<Darkfid>, ex: Arc<smol::Executor<'static>>) -> Result<()> { async fn replicator_task(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;
@@ -188,9 +183,9 @@ async fn monitor_network(subscription: &Subscription<Error>) -> Result<()> {
/// Async task used for listening for new blocks and perform consensus. /// Async task used for listening for new blocks and perform consensus.
async fn consensus_task( async fn consensus_task(
node: Arc<Darkfid>, node: &DarkfiNodePtr,
subscription: &Subscription<JsonNotification>, subscription: &Subscription<JsonNotification>,
ex: Arc<smol::Executor<'static>>, ex: &ExecutorPtr,
) -> Result<()> { ) -> Result<()> {
info!(target: "darkfid::task::consensus_task", "Starting consensus task..."); info!(target: "darkfid::task::consensus_task", "Starting consensus task...");

View File

@@ -16,16 +16,14 @@
* 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 darkfi::{error::TxVerifyFailed, validator::verification::verify_transactions, Error, Result}; use darkfi::{error::TxVerifyFailed, validator::verification::verify_transactions, Error, Result};
use darkfi_sdk::crypto::MerkleTree; use darkfi_sdk::crypto::MerkleTree;
use log::{debug, error, info}; use log::{debug, error, info};
use crate::Darkfid; use crate::DarkfiNodePtr;
/// Async task used for purging erroneous pending transactions from the nodes mempool. /// Async task used for purging erroneous pending transactions from the nodes mempool.
pub async fn garbage_collect_task(node: Arc<Darkfid>) -> Result<()> { pub async fn garbage_collect_task(node: DarkfiNodePtr) -> Result<()> {
info!(target: "darkfid::task::garbage_collect_task", "Starting garbage collection task..."); info!(target: "darkfid::task::garbage_collect_task", "Starting garbage collection task...");
// Grab all current unproposed transactions. We verify them in batches, // Grab all current unproposed transactions. We verify them in batches,

View File

@@ -16,12 +16,10 @@
* 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 darkfi::{ use darkfi::{
blockchain::{BlockInfo, Header}, blockchain::{BlockInfo, Header},
rpc::{jsonrpc::JsonNotification, util::JsonValue}, rpc::{jsonrpc::JsonNotification, util::JsonValue},
system::{StoppableTask, Subscription}, system::{ExecutorPtr, StoppableTask, Subscription},
tx::{ContractCallLeaf, Transaction, TransactionBuilder}, tx::{ContractCallLeaf, Transaction, TransactionBuilder},
util::{encoding::base64, time::Timestamp}, util::{encoding::base64, time::Timestamp},
validator::{ validator::{
@@ -46,7 +44,7 @@ use num_bigint::BigUint;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use smol::channel::{Receiver, Sender}; use smol::channel::{Receiver, Sender};
use crate::{proto::ProposalMessage, task::garbage_collect_task, Darkfid}; use crate::{proto::ProposalMessage, task::garbage_collect_task, DarkfiNodePtr};
/// Auxiliary structure representing node miner rewards recipient configuration /// Auxiliary structure representing node miner rewards recipient configuration
pub struct MinerRewardsRecipientConfig { pub struct MinerRewardsRecipientConfig {
@@ -56,6 +54,7 @@ pub struct MinerRewardsRecipientConfig {
} }
/// Async task used for participating in the PoW block production. /// Async task used for participating in the PoW block production.
///
/// Miner initializes their setup and waits for next finalization, /// Miner initializes their setup and waits for next finalization,
/// by listenning for new proposals from the network, for optimal /// by listenning for new proposals from the network, for optimal
/// conditions. After finalization occurs, they start the actual /// conditions. After finalization occurs, they start the actual
@@ -66,10 +65,10 @@ pub struct MinerRewardsRecipientConfig {
/// mining. These two tasks run in parallel, and after one of them /// mining. These two tasks run in parallel, and after one of them
/// finishes, node triggers finallization check. /// finishes, node triggers finallization check.
pub async fn miner_task( pub async fn miner_task(
node: Arc<Darkfid>, node: &DarkfiNodePtr,
recipient_config: &MinerRewardsRecipientConfig, recipient_config: &MinerRewardsRecipientConfig,
skip_sync: bool, skip_sync: bool,
ex: Arc<smol::Executor<'static>>, ex: &ExecutorPtr,
) -> Result<()> { ) -> Result<()> {
// Initialize miner configuration // Initialize miner configuration
info!(target: "darkfid::task::miner_task", "Starting miner task..."); info!(target: "darkfid::task::miner_task", "Starting miner task...");
@@ -163,9 +162,9 @@ pub async fn miner_task(
// Start listenning for network proposals and mining next block for best fork. // Start listenning for network proposals and mining next block for best fork.
match smol::future::or( match smol::future::or(
listen_to_network(&node, &extended_fork, &subscription, &sender), listen_to_network(node, &extended_fork, &subscription, &sender),
mine( mine(
&node, node,
&extended_fork, &extended_fork,
&mut secret, &mut secret,
recipient_config, recipient_config,
@@ -234,7 +233,7 @@ pub async fn miner_task(
/// Async task to listen for incoming proposals and check if the best fork has changed. /// Async task to listen for incoming proposals and check if the best fork has changed.
async fn listen_to_network( async fn listen_to_network(
node: &Darkfid, node: &DarkfiNodePtr,
extended_fork: &Fork, extended_fork: &Fork,
subscription: &Subscription<JsonNotification>, subscription: &Subscription<JsonNotification>,
sender: &Sender<()>, sender: &Sender<()>,
@@ -273,7 +272,7 @@ async fn listen_to_network(
/// while listening for a stop signal. /// while listening for a stop signal.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
async fn mine( async fn mine(
node: &Darkfid, node: &DarkfiNodePtr,
extended_fork: &Fork, extended_fork: &Fork,
secret: &mut SecretKey, secret: &mut SecretKey,
recipient_config: &MinerRewardsRecipientConfig, recipient_config: &MinerRewardsRecipientConfig,
@@ -304,7 +303,7 @@ pub async fn wait_stop_signal(stop_signal: &Receiver<()>) -> Result<()> {
/// Async task to generate and mine provided fork index next block. /// Async task to generate and mine provided fork index next block.
async fn mine_next_block( async fn mine_next_block(
node: &Darkfid, node: &DarkfiNodePtr,
extended_fork: &Fork, extended_fork: &Fork,
secret: &mut SecretKey, secret: &mut SecretKey,
recipient_config: &MinerRewardsRecipientConfig, recipient_config: &MinerRewardsRecipientConfig,

View File

@@ -32,14 +32,14 @@ use crate::{
ForkSyncRequest, ForkSyncResponse, HeaderSyncRequest, HeaderSyncResponse, SyncRequest, ForkSyncRequest, ForkSyncResponse, HeaderSyncRequest, HeaderSyncResponse, SyncRequest,
SyncResponse, TipRequest, TipResponse, BATCH, SyncResponse, TipRequest, TipResponse, BATCH,
}, },
Darkfid, DarkfiNodePtr,
}; };
// TODO: Parallelize independent requests. // TODO: Parallelize independent requests.
// We can also make them be like torrents, where we retrieve chunks not in order. // We can also make them be like torrents, where we retrieve chunks not in order.
/// async task used for block syncing. /// async task used for block syncing.
/// A checkpoint can be provided to ensure node syncs the correct sequence. /// A checkpoint can be provided to ensure node syncs the correct sequence.
pub async fn sync_task(node: &Darkfid, checkpoint: Option<(u32, HeaderHash)>) -> Result<()> { pub async fn sync_task(node: &DarkfiNodePtr, checkpoint: Option<(u32, HeaderHash)>) -> Result<()> {
info!(target: "darkfid::task::sync_task", "Starting blockchain sync..."); info!(target: "darkfid::task::sync_task", "Starting blockchain sync...");
// Grab blocks subscriber // Grab blocks subscriber
@@ -133,7 +133,7 @@ pub async fn sync_task(node: &Darkfid, checkpoint: Option<(u32, HeaderHash)>) ->
/// Auxiliary function to block until node is connected to at least one synced peer, /// Auxiliary function to block until node is connected to at least one synced peer,
/// and retrieve the synced peers tips. /// and retrieve the synced peers tips.
async fn synced_peers( async fn synced_peers(
node: &Darkfid, node: &DarkfiNodePtr,
last_tip: &HeaderHash, last_tip: &HeaderHash,
checkpoint: Option<(u32, HeaderHash)>, checkpoint: Option<(u32, HeaderHash)>,
) -> HashMap<(u32, [u8; 32]), Vec<ChannelPtr>> { ) -> HashMap<(u32, [u8; 32]), Vec<ChannelPtr>> {
@@ -223,7 +223,7 @@ async fn synced_peers(
/// Auxiliary function to ask all peers for their current tip and find the most common one. /// Auxiliary function to ask all peers for their current tip and find the most common one.
async fn most_common_tip( async fn most_common_tip(
node: &Darkfid, node: &DarkfiNodePtr,
last_tip: &HeaderHash, last_tip: &HeaderHash,
checkpoint: Option<(u32, HeaderHash)>, checkpoint: Option<(u32, HeaderHash)>,
) -> (u32, Vec<ChannelPtr>) { ) -> (u32, Vec<ChannelPtr>) {
@@ -253,7 +253,7 @@ async fn most_common_tip(
/// Auxiliary function to retrieve headers backwards until our last known one and verify them. /// Auxiliary function to retrieve headers backwards until our last known one and verify them.
async fn retrieve_headers( async fn retrieve_headers(
node: &Darkfid, node: &DarkfiNodePtr,
peers: &[ChannelPtr], peers: &[ChannelPtr],
last_known: u32, last_known: u32,
tip_height: u32, tip_height: u32,
@@ -371,7 +371,7 @@ async fn retrieve_headers(
/// Auxiliary function to retrieve blocks of provided headers and apply them to canonical. /// Auxiliary function to retrieve blocks of provided headers and apply them to canonical.
async fn retrieve_blocks( async fn retrieve_blocks(
node: &Darkfid, node: &DarkfiNodePtr,
peers: &[ChannelPtr], peers: &[ChannelPtr],
last_known: (u32, HeaderHash), last_known: (u32, HeaderHash),
block_sub: &JsonSubscriber, block_sub: &JsonSubscriber,
@@ -484,7 +484,7 @@ async fn retrieve_blocks(
} }
/// Auxiliary function to retrieve best fork state from a random peer. /// Auxiliary function to retrieve best fork state from a random peer.
async fn sync_best_fork(node: &Darkfid, peers: &[ChannelPtr], last_tip: &HeaderHash) { async fn sync_best_fork(node: &DarkfiNodePtr, peers: &[ChannelPtr], last_tip: &HeaderHash) {
info!(target: "darkfid::task::sync::sync_best_fork", "Syncing fork states from peers..."); info!(target: "darkfid::task::sync::sync_best_fork", "Syncing fork states from peers...");
// Getting a random peer to ask for blocks // Getting a random peer to ask for blocks
let peer = &peers.choose(&mut OsRng).unwrap(); let peer = &peers.choose(&mut OsRng).unwrap();

View File

@@ -44,7 +44,7 @@ use url::Url;
use crate::{ use crate::{
proto::{DarkfidP2pHandler, ProposalMessage}, proto::{DarkfidP2pHandler, ProposalMessage},
task::sync::sync_task, task::sync::sync_task,
Darkfid, DarkfiNode, DarkfiNodePtr,
}; };
pub struct HarnessConfig { pub struct HarnessConfig {
@@ -59,8 +59,8 @@ pub struct Harness {
pub config: HarnessConfig, pub config: HarnessConfig,
pub vks: Vec<(Vec<u8>, String, Vec<u8>)>, pub vks: Vec<(Vec<u8>, String, Vec<u8>)>,
pub validator_config: ValidatorConfig, pub validator_config: ValidatorConfig,
pub alice: Darkfid, pub alice: DarkfiNodePtr,
pub bob: Darkfid, pub bob: DarkfiNodePtr,
} }
impl Harness { impl Harness {
@@ -97,13 +97,13 @@ impl Harness {
// Alice // Alice
let alice_url = Url::parse(&config.alice_url)?; let alice_url = Url::parse(&config.alice_url)?;
settings.inbound_addrs = vec![alice_url.clone()]; settings.inbound_addrs = vec![alice_url.clone()];
let alice = generate_node(&vks, &validator_config, &settings, ex, true, true, None).await?; let alice = generate_node(&vks, &validator_config, &settings, ex, true, None).await?;
// Bob // Bob
let bob_url = Url::parse(&config.bob_url)?; let bob_url = Url::parse(&config.bob_url)?;
settings.inbound_addrs = vec![bob_url]; settings.inbound_addrs = vec![bob_url];
settings.peers = vec![alice_url]; settings.peers = vec![alice_url];
let bob = generate_node(&vks, &validator_config, &settings, ex, true, false, None).await?; let bob = generate_node(&vks, &validator_config, &settings, ex, false, None).await?;
Ok(Self { config, vks, validator_config, alice, bob }) Ok(Self { config, vks, validator_config, alice, bob })
} }
@@ -223,40 +223,30 @@ impl Harness {
} }
} }
// Note: This function should mirror darkfid::main // Note: This function should mirror `darkfid::Darkfid::init`
pub async fn generate_node( pub async fn generate_node(
vks: &Vec<(Vec<u8>, String, Vec<u8>)>, vks: &Vec<(Vec<u8>, String, Vec<u8>)>,
config: &ValidatorConfig, config: &ValidatorConfig,
settings: &Settings, settings: &Settings,
ex: &Arc<smol::Executor<'static>>, ex: &Arc<smol::Executor<'static>>,
miner: bool,
skip_sync: bool, skip_sync: bool,
checkpoint: Option<(u32, HeaderHash)>, checkpoint: Option<(u32, HeaderHash)>,
) -> Result<Darkfid> { ) -> Result<DarkfiNodePtr> {
let sled_db = sled::Config::new().temporary(true).open()?; let sled_db = sled::Config::new().temporary(true).open()?;
vks::inject(&sled_db, vks)?; vks::inject(&sled_db, vks)?;
let validator = Validator::new(&sled_db, config.clone()).await?; let validator = Validator::new(&sled_db, config).await?;
let mut subscribers = HashMap::new(); let mut subscribers = HashMap::new();
subscribers.insert("blocks", JsonSubscriber::new("blockchain.subscribe_blocks")); subscribers.insert("blocks", JsonSubscriber::new("blockchain.subscribe_blocks"));
subscribers.insert("txs", JsonSubscriber::new("blockchain.subscribe_txs")); subscribers.insert("txs", JsonSubscriber::new("blockchain.subscribe_txs"));
subscribers.insert("proposals", JsonSubscriber::new("blockchain.subscribe_proposals")); subscribers.insert("proposals", JsonSubscriber::new("blockchain.subscribe_proposals"));
subscribers.insert("dnet", JsonSubscriber::new("dnet.subscribe_events"));
// We initialize a dnet subscriber but do not activate it.
let dnet_sub = JsonSubscriber::new("dnet.subscribe_events");
let p2p_handler = DarkfidP2pHandler::init(settings, ex).await?; let p2p_handler = DarkfidP2pHandler::init(settings, ex).await?;
let node = Darkfid::new( let node =
p2p_handler.clone(), DarkfiNode::new(p2p_handler.clone(), validator.clone(), 50, subscribers.clone(), None)
validator.clone(), .await;
miner,
50,
subscribers.clone(),
None,
dnet_sub,
)
.await;
p2p_handler.clone().start(ex, &validator, &subscribers).await?; p2p_handler.clone().start(ex, &validator, &subscribers).await?;

View File

@@ -98,7 +98,6 @@ async fn sync_blocks_real(ex: Arc<Executor<'static>>) -> Result<()> {
&settings, &settings,
&ex, &ex,
false, false,
false,
Some((block2.header.height, block2.hash())), Some((block2.header.height, block2.hash())),
) )
.await?; .await?;
@@ -200,3 +199,94 @@ fn sync_blocks() -> Result<()> {
Ok(()) Ok(())
} }
#[test]
/// Test the programmatic control of `Darkfid`.
///
/// First we initialize a daemon, start it and then perform
/// couple of restarts to verify everything works as expected.
fn darkfid_programmatic_control() -> Result<()> {
// Initialize logger
let mut cfg = simplelog::ConfigBuilder::new();
// We check this error so we can execute same file tests in parallel,
// otherwise second one fails to init logger here.
if simplelog::TermLogger::init(
simplelog::LevelFilter::Info,
//simplelog::LevelFilter::Debug,
//simplelog::LevelFilter::Trace,
cfg.build(),
simplelog::TerminalMode::Mixed,
simplelog::ColorChoice::Auto,
)
.is_err()
{
log::debug!(target: "darkfid_programmatic_control", "Logger initialized");
}
// Daemon configuration
let mut genesis_block = darkfi::blockchain::BlockInfo::default();
let producer_tx = genesis_block.txs.pop().unwrap();
genesis_block.append_txs(vec![producer_tx]);
let bootstrap = genesis_block.header.timestamp.inner();
let config = darkfi::validator::ValidatorConfig {
finalization_threshold: 1,
pow_target: 20,
pow_fixed_difficulty: Some(BigUint::one()),
genesis_block,
verify_fees: false,
};
let consensus_config = crate::ConsensusInitTaskConfig {
skip_sync: true,
checkpoint_height: None,
checkpoint: None,
miner: false,
recipient: None,
spend_hook: None,
user_data: None,
bootstrap,
};
let sled_db = sled_overlay::sled::Config::new().temporary(true).open()?;
let (_, vks) = darkfi_contract_test_harness::vks::get_cached_pks_and_vks()?;
darkfi_contract_test_harness::vks::inject(&sled_db, &vks)?;
let rpc_listen = Url::parse("tcp://127.0.0.1:8240")?;
// Create an executor and communication signals
let ex = Arc::new(smol::Executor::new());
let (signal, shutdown) = smol::channel::unbounded::<()>();
easy_parallel::Parallel::new().each(0..1, |_| smol::block_on(ex.run(shutdown.recv()))).finish(
|| {
smol::block_on(async {
// Initialize a daemon
let daemon = crate::Darkfid::init(
&sled_db,
&config,
&darkfi::net::Settings::default(),
&None,
&None,
&ex,
)
.await
.unwrap();
// Start it
daemon.start(&ex, &rpc_listen, &consensus_config).await.unwrap();
// Stop it
daemon.stop().await.unwrap();
// Start it again
daemon.start(&ex, &rpc_listen, &consensus_config).await.unwrap();
// Stop it
daemon.stop().await.unwrap();
// Shutdown entirely
drop(signal);
})
},
);
Ok(())
}

View File

@@ -69,8 +69,7 @@ async fn sync_forks_real(ex: Arc<Executor<'static>>) -> Result<()> {
settings.inbound_addrs = vec![charlie_url]; settings.inbound_addrs = vec![charlie_url];
let bob_url = th.bob.p2p_handler.p2p.settings().read().await.inbound_addrs[0].clone(); let bob_url = th.bob.p2p_handler.p2p.settings().read().await.inbound_addrs[0].clone();
settings.peers = vec![bob_url]; settings.peers = vec![bob_url];
let charlie = let charlie = generate_node(&th.vks, &th.validator_config, &settings, &ex, false, None).await?;
generate_node(&th.vks, &th.validator_config, &settings, &ex, false, false, None).await?;
// Verify node synced the best fork // Verify node synced the best fork
let forks = th.alice.validator.consensus.forks.read().await; let forks = th.alice.validator.consensus.forks.read().await;

View File

@@ -1,70 +0,0 @@
/* This file is part of DarkFi (https://dark.fi)
*
* Copyright (C) 2020-2024 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 log::{debug, error};
use smol::fs::read_to_string;
use structopt_toml::StructOptToml;
use darkfi::{util::path::get_config_path, Error, Result};
use crate::{BlockchainNetwork, CONFIG_FILE};
/// Auxiliary function to parse darkfid 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: "darkfid", "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: "darkfid", "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: "darkfid", "Failed parsing requested network configuration: {}", e);
return Err(Error::ParseFailed("Failed parsing requested network configuration"))
}
};
debug!(target: "darkfid", "Parsed network configuration: {:?}", network_config);
Ok(network_config)
}

View File

@@ -102,7 +102,7 @@ impl Minerd {
|res| async move { |res| async move {
match res { match res {
Ok(()) | Err(Error::RpcServerStopped) => node_.stop_connections().await, Ok(()) | Err(Error::RpcServerStopped) => node_.stop_connections().await,
Err(e) => error!(target: "minerd::Minerd::start", "Failed stopping JSON-RPC server: {}", e), Err(e) => error!(target: "minerd::Minerd::start", "Failed starting JSON-RPC server: {}", e),
} }
}, },
Error::RpcServerStopped, Error::RpcServerStopped,
@@ -189,10 +189,13 @@ fn minerd_programmatic_control() -> Result<()> {
daemon.start(&ex, &rpc_listen); daemon.start(&ex, &rpc_listen);
// Generate a JSON-RPC client to send mining jobs // Generate a JSON-RPC client to send mining jobs
let rpc_client = let mut rpc_client =
darkfi::rpc::client::RpcClient::new(rpc_listen.clone(), ex.clone()) darkfi::rpc::client::RpcClient::new(rpc_listen.clone(), ex.clone()).await;
.await while rpc_client.is_err() {
.unwrap(); rpc_client =
darkfi::rpc::client::RpcClient::new(rpc_listen.clone(), ex.clone()).await;
}
let rpc_client = rpc_client.unwrap();
// Send a mining job but stop the daemon after it starts mining // Send a mining job but stop the daemon after it starts mining
smol::future::or( smol::future::or(

View File

@@ -26,9 +26,6 @@ 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
# Participate in block production
miner = true
# Wallet address to receive mining rewards. # Wallet address to receive mining rewards.
# This is a dummy one so the miner can start, # This is a dummy one so the miner can start,
# replace with your own one. # replace with your own one.

View File

@@ -26,9 +26,6 @@ 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
# Participate in block production
miner = true
# Wallet address to receive mining rewards. # Wallet address to receive mining rewards.
# This is a dummy one so the miner can start, # This is a dummy one so the miner can start,
# replace with your own one. # replace with your own one.

View File

@@ -26,9 +26,6 @@ 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
# Participate in block production
miner = true
# Wallet address to receive mining rewards. # Wallet address to receive mining rewards.
# This is a dummy one so the miner can start, # This is a dummy one so the miner can start,
# replace with your own one. # replace with your own one.

View File

@@ -26,9 +26,6 @@ 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
# Participate in block production
miner = true
# Wallet address to receive mining rewards. # Wallet address to receive mining rewards.
# This is a dummy one so the miner can start, # This is a dummy one so the miner can start,
# replace with your own one. # replace with your own one.

View File

@@ -29,9 +29,6 @@ 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
# Participate in block production
miner = true
# Wallet address to receive mining rewards. # Wallet address to receive mining rewards.
# This is a dummy one so the miner can start, # This is a dummy one so the miner can start,
# replace with your own one. # replace with your own one.

View File

@@ -26,9 +26,6 @@ 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
# Participate in block production
miner = true
# Wallet address to receive mining rewards. # Wallet address to receive mining rewards.
# This is a dummy one so the miner can start, # This is a dummy one so the miner can start,
# replace with your own one. # replace with your own one.

View File

@@ -34,9 +34,6 @@ 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
# Participate in block production
miner = true
# Wallet address to receive mining rewards. # Wallet address to receive mining rewards.
# This is a dummy one so the miner can start, # This is a dummy one so the miner can start,
# replace with your own one. # replace with your own one.

View File

@@ -26,9 +26,6 @@ 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
# Participate in block production
miner = false
# Skip syncing process and start node right away # Skip syncing process and start node right away
skip_sync = false skip_sync = false

View File

@@ -170,7 +170,7 @@ impl Wallet {
genesis_block, genesis_block,
verify_fees, verify_fees,
}; };
let validator = Validator::new(&sled_db, validator_config).await?; let validator = Validator::new(&sled_db, &validator_config).await?;
// The Merkle tree for the `Money` contract is initialized with a "null" // The Merkle tree for the `Money` contract is initialized with a "null"
// leaf at position 0. // leaf at position 0.

View File

@@ -88,7 +88,7 @@ pub struct Validator {
} }
impl Validator { impl Validator {
pub async fn new(db: &sled::Db, config: ValidatorConfig) -> Result<ValidatorPtr> { pub async fn new(db: &sled::Db, config: &ValidatorConfig) -> Result<ValidatorPtr> {
info!(target: "validator::new", "Initializing Validator"); info!(target: "validator::new", "Initializing Validator");
info!(target: "validator::new", "Initializing Blockchain"); info!(target: "validator::new", "Initializing Blockchain");
@@ -114,7 +114,7 @@ impl Validator {
blockchain.clone(), blockchain.clone(),
config.finalization_threshold, config.finalization_threshold,
config.pow_target, config.pow_target,
config.pow_fixed_difficulty, config.pow_fixed_difficulty.clone(),
)?; )?;
// Create the actual state // Create the actual state