use std::convert::From;
use std::sync::Arc;
use chrono::prelude::Utc;
use rand::prelude::*;
use crate::api;
use crate::chain;
use crate::core::global::ChainTypes;
use crate::core::{core, libtx, pow};
use crate::keychain;
use crate::p2p;
use crate::pool;
use crate::pool::types::DandelionConfig;
use crate::store;
#[derive(Debug)]
pub enum Error {
Core(core::block::Error),
LibTx(libtx::Error),
Store(store::Error),
Chain(chain::Error),
P2P(p2p::Error),
API(api::Error),
Cuckoo(pow::Error),
Pool(pool::PoolError),
Keychain(keychain::Error),
ArgumentError(String),
WalletComm(String),
IOError(std::io::Error),
Configuration(String),
General(String),
}
impl From<core::block::Error> for Error {
fn from(e: core::block::Error) -> Error {
Error::Core(e)
}
}
impl From<chain::Error> for Error {
fn from(e: chain::Error) -> Error {
Error::Chain(e)
}
}
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Error {
Error::IOError(e)
}
}
impl From<p2p::Error> for Error {
fn from(e: p2p::Error) -> Error {
Error::P2P(e)
}
}
impl From<pow::Error> for Error {
fn from(e: pow::Error) -> Error {
Error::Cuckoo(e)
}
}
impl From<store::Error> for Error {
fn from(e: store::Error) -> Error {
Error::Store(e)
}
}
impl From<api::Error> for Error {
fn from(e: api::Error) -> Error {
Error::API(e)
}
}
impl From<pool::PoolError> for Error {
fn from(e: pool::PoolError) -> Error {
Error::Pool(e)
}
}
impl From<keychain::Error> for Error {
fn from(e: keychain::Error) -> Error {
Error::Keychain(e)
}
}
impl From<libtx::Error> for Error {
fn from(e: libtx::Error) -> Error {
Error::LibTx(e)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ChainValidationMode {
EveryBlock,
Disabled,
}
impl Default for ChainValidationMode {
fn default() -> ChainValidationMode {
ChainValidationMode::Disabled
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ServerConfig {
pub db_root: String,
pub api_http_addr: String,
pub api_secret_path: Option<String>,
pub foreign_api_secret_path: Option<String>,
pub tls_certificate_file: Option<String>,
pub tls_certificate_key: Option<String>,
#[serde(default)]
pub chain_type: ChainTypes,
#[serde(default)]
pub chain_validation_mode: ChainValidationMode,
pub archive_mode: Option<bool>,
pub skip_sync_wait: Option<bool>,
pub run_tui: Option<bool>,
pub run_test_miner: Option<bool>,
pub test_miner_wallet_url: Option<String>,
pub p2p_config: p2p::P2PConfig,
#[serde(default)]
pub pool_config: pool::PoolConfig,
#[serde(default)]
pub dandelion_config: pool::DandelionConfig,
#[serde(default)]
pub stratum_mining_config: Option<StratumServerConfig>,
#[serde(default)]
pub webhook_config: WebHooksConfig,
}
impl Default for ServerConfig {
fn default() -> ServerConfig {
ServerConfig {
db_root: "grin_chain".to_string(),
api_http_addr: "127.0.0.1:3413".to_string(),
api_secret_path: Some(".api_secret".to_string()),
foreign_api_secret_path: Some(".foreign_api_secret".to_string()),
tls_certificate_file: None,
tls_certificate_key: None,
p2p_config: p2p::P2PConfig::default(),
dandelion_config: pool::DandelionConfig::default(),
stratum_mining_config: Some(StratumServerConfig::default()),
chain_type: ChainTypes::default(),
archive_mode: Some(false),
chain_validation_mode: ChainValidationMode::default(),
pool_config: pool::PoolConfig::default(),
skip_sync_wait: Some(false),
run_tui: Some(true),
run_test_miner: Some(false),
test_miner_wallet_url: None,
webhook_config: WebHooksConfig::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct StratumServerConfig {
pub enable_stratum_server: Option<bool>,
pub stratum_server_addr: Option<String>,
pub attempt_time_per_block: u32,
pub minimum_share_difficulty: u64,
pub wallet_listener_url: String,
pub burn_reward: bool,
}
impl Default for StratumServerConfig {
fn default() -> StratumServerConfig {
StratumServerConfig {
wallet_listener_url: "http://127.0.0.1:3415".to_string(),
burn_reward: false,
attempt_time_per_block: 15,
minimum_share_difficulty: 1,
enable_stratum_server: Some(false),
stratum_server_addr: Some("127.0.0.1:3416".to_string()),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct WebHooksConfig {
pub tx_received_url: Option<String>,
pub header_received_url: Option<String>,
pub block_received_url: Option<String>,
pub block_accepted_url: Option<String>,
#[serde(default = "default_nthreads")]
pub nthreads: u16,
#[serde(default = "default_timeout")]
pub timeout: u16,
}
fn default_timeout() -> u16 {
10
}
fn default_nthreads() -> u16 {
4
}
impl Default for WebHooksConfig {
fn default() -> WebHooksConfig {
WebHooksConfig {
tx_received_url: None,
header_received_url: None,
block_received_url: None,
block_accepted_url: None,
nthreads: default_nthreads(),
timeout: default_timeout(),
}
}
}
#[derive(Debug)]
pub struct DandelionEpoch {
config: DandelionConfig,
start_time: Option<i64>,
is_stem: bool,
relay_peer: Option<Arc<p2p::Peer>>,
}
impl DandelionEpoch {
pub fn new(config: DandelionConfig) -> DandelionEpoch {
DandelionEpoch {
config,
start_time: None,
is_stem: true,
relay_peer: None,
}
}
pub fn is_expired(&self) -> bool {
match self.start_time {
None => true,
Some(start_time) => {
let epoch_secs = self.config.epoch_secs;
Utc::now().timestamp().saturating_sub(start_time) > epoch_secs as i64
}
}
}
pub fn next_epoch(&mut self, peers: &Arc<p2p::Peers>) {
self.start_time = Some(Utc::now().timestamp());
self.relay_peer = peers.outgoing_connected_peers().first().cloned();
let mut rng = rand::thread_rng();
let stem_probability = self.config.stem_probability;
self.is_stem = rng.gen_range(0, 100) < stem_probability;
let addr = self.relay_peer.clone().map(|p| p.info.addr);
info!(
"DandelionEpoch: next_epoch: is_stem: {} ({}%), relay: {:?}",
self.is_stem, stem_probability, addr
);
}
pub fn is_stem(&self) -> bool {
self.is_stem
}
pub fn always_stem_our_txs(&self) -> bool {
self.config.always_stem_our_txs
}
pub fn relay_peer(&mut self, peers: &Arc<p2p::Peers>) -> Option<Arc<p2p::Peer>> {
let mut update_relay = false;
if let Some(peer) = &self.relay_peer {
if !peer.is_connected() {
info!(
"DandelionEpoch: relay_peer: {:?} not connected, choosing a new one.",
peer.info.addr
);
update_relay = true;
}
} else {
update_relay = true;
}
if update_relay {
self.relay_peer = peers.outgoing_connected_peers().first().cloned();
info!(
"DandelionEpoch: relay_peer: new peer chosen: {:?}",
self.relay_peer.clone().map(|p| p.info.addr)
);
}
self.relay_peer.clone()
}
}