mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-09 22:57:59 -05:00
darkfid: programmatic control daemon
This commit is contained in:
@@ -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
312
bin/darkfid/src/lib.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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...");
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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?;
|
||||||
|
|
||||||
|
|||||||
@@ -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(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
@@ -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(
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user