use ckb_types::{H256, U256};
use multiaddr::Multiaddr;
use rand::Rng;
use serde::{Deserialize, Serialize};
use std::fs;
use std::io::{Error, ErrorKind, Read, Write};
use std::path::PathBuf;
use ubyte::ByteUnit;
const DEFAULT_SEND_BUFFER: usize = 24 * 1024 * 1024;
const DEFAULT_CHANNEL_SIZE: usize = 128;
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
#[serde(deny_unknown_fields)]
pub struct Config {
#[serde(default)]
pub whitelist_only: bool,
pub max_peers: u32,
pub max_outbound_peers: u32,
#[serde(default)]
pub path: PathBuf,
#[serde(default)]
pub dns_seeds: Vec<String>,
#[serde(default)]
pub discovery_local_address: bool,
#[serde(default)]
pub discovery_announce_check_interval_secs: Option<u64>,
pub ping_interval_secs: u64,
pub ping_timeout_secs: u64,
pub connect_outbound_interval_secs: u64,
pub listen_addresses: Vec<Multiaddr>,
#[serde(default)]
pub public_addresses: Vec<Multiaddr>,
pub bootnodes: Vec<Multiaddr>,
#[serde(default)]
pub whitelist_peers: Vec<Multiaddr>,
#[serde(default)]
pub upnp: bool,
#[serde(default)]
pub bootnode_mode: bool,
#[serde(default = "default_support_all_protocols")]
pub support_protocols: Vec<SupportProtocol>,
pub max_send_buffer: Option<usize>,
#[serde(default = "default_reuse")]
pub reuse_port_on_linux: bool,
#[serde(default = "default_reuse_tcp_with_ws")]
pub reuse_tcp_with_ws: bool,
#[serde(default)]
pub sync: SyncConfig,
pub channel_size: Option<usize>,
#[cfg(target_family = "wasm")]
#[serde(skip)]
pub secret_key: [u8; 32],
}
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
#[serde(deny_unknown_fields)]
pub struct SyncConfig {
#[serde(default)]
pub header_map: HeaderMapConfig,
#[serde(skip, default)]
pub assume_valid_target: Option<H256>,
#[serde(skip, default)]
pub min_chain_work: U256,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct HeaderMapConfig {
pub primary_limit: Option<usize>,
pub backend_close_threshold: Option<usize>,
#[serde(default = "default_memory_limit")]
pub memory_limit: ByteUnit,
}
impl Default for HeaderMapConfig {
fn default() -> Self {
Self {
primary_limit: None,
backend_close_threshold: None,
memory_limit: default_memory_limit(),
}
}
}
const fn default_memory_limit() -> ByteUnit {
ByteUnit::Megabyte(256)
}
#[derive(Clone, Debug, Copy, Eq, PartialEq, Serialize, Deserialize, Hash)]
#[allow(missing_docs)]
pub enum SupportProtocol {
Ping,
Discovery,
Identify,
Feeler,
DisconnectMessage,
Sync,
Relay,
Time,
Alert,
LightClient,
Filter,
}
#[allow(missing_docs)]
pub fn default_support_all_protocols() -> Vec<SupportProtocol> {
vec![
SupportProtocol::Ping,
SupportProtocol::Discovery,
SupportProtocol::Identify,
SupportProtocol::Feeler,
SupportProtocol::DisconnectMessage,
SupportProtocol::Sync,
SupportProtocol::Relay,
SupportProtocol::Time,
SupportProtocol::Alert,
SupportProtocol::LightClient,
SupportProtocol::Filter,
]
}
pub fn generate_random_key() -> [u8; 32] {
loop {
let mut key: [u8; 32] = [0; 32];
rand::thread_rng().fill(&mut key);
if secio::SecioKeyPair::secp256k1_raw_key(key).is_ok() {
return key;
}
}
}
pub fn write_secret_to_file(secret: &[u8], path: PathBuf) -> Result<(), Error> {
fs::OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(path)
.and_then(|mut file| {
file.write_all(secret)?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
file.set_permissions(fs::Permissions::from_mode(0o400))
}
#[cfg(not(unix))]
{
let mut permissions = file.metadata()?.permissions();
permissions.set_readonly(true);
file.set_permissions(permissions)
}
})
}
pub fn read_secret_key(path: PathBuf) -> Result<Option<secio::SecioKeyPair>, Error> {
let mut file = match fs::File::open(path.clone()) {
Ok(file) => file,
Err(_) => return Ok(None),
};
let warn = |m: bool, d: &str| {
if m {
ckb_logger::warn!(
"Your network secret file's permission is not {}, path: {:?}. \
Please fix it as soon as possible",
d,
path
)
}
};
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
warn(
file.metadata()?.permissions().mode() & 0o177 != 0,
"less than 0o600",
);
}
#[cfg(not(unix))]
{
warn(!file.metadata()?.permissions().readonly(), "readonly");
}
let mut buf = Vec::new();
file.read_to_end(&mut buf).and_then(|_read_size| {
secio::SecioKeyPair::secp256k1_raw_key(&buf)
.map(Some)
.map_err(|_| Error::new(ErrorKind::InvalidData, "invalid secret key data"))
})
}
impl Config {
pub fn secret_key_path(&self) -> PathBuf {
let mut path = self.path.clone();
path.push("secret_key");
path
}
pub fn peer_store_path(&self) -> PathBuf {
let mut path = self.path.clone();
path.push("peer_store");
path
}
pub fn create_dir_if_not_exists(&self) -> Result<(), Error> {
if !self.path.exists() {
fs::create_dir(&self.path)
} else {
Ok(())
}
}
pub fn max_inbound_peers(&self) -> u32 {
self.max_peers.saturating_sub(self.max_outbound_peers)
}
pub fn max_outbound_peers(&self) -> u32 {
self.max_outbound_peers
}
pub fn max_send_buffer(&self) -> usize {
self.max_send_buffer.unwrap_or(DEFAULT_SEND_BUFFER)
}
pub fn channel_size(&self) -> usize {
self.channel_size.unwrap_or(DEFAULT_CHANNEL_SIZE)
}
#[cfg(not(target_family = "wasm"))]
fn read_secret_key(&self) -> Result<Option<secio::SecioKeyPair>, Error> {
let path = self.secret_key_path();
read_secret_key(path)
}
#[cfg(not(target_family = "wasm"))]
fn write_secret_key_to_file(&self) -> Result<(), Error> {
let path = self.secret_key_path();
let random_key_pair = generate_random_key();
write_secret_to_file(&random_key_pair, path)
}
#[cfg(not(target_family = "wasm"))]
pub fn fetch_private_key(&self) -> Result<secio::SecioKeyPair, Error> {
match self.read_secret_key()? {
Some(key) => Ok(key),
None => {
self.write_secret_key_to_file()?;
Ok(self.read_secret_key()?.expect("key must exists"))
}
}
}
#[cfg(target_family = "wasm")]
pub fn fetch_private_key(&self) -> Result<secio::SecioKeyPair, Error> {
if self.secret_key == [0; 32] {
return Err(Error::new(
ErrorKind::InvalidData,
"invalid secret key data",
));
} else {
secio::SecioKeyPair::secp256k1_raw_key(&self.secret_key)
.map_err(|_| Error::new(ErrorKind::InvalidData, "invalid secret key data"))
}
}
pub fn whitelist_peers(&self) -> Vec<Multiaddr> {
self.whitelist_peers.clone()
}
pub fn bootnodes(&self) -> Vec<Multiaddr> {
self.bootnodes.clone()
}
pub fn outbound_peer_service_enabled(&self) -> bool {
self.connect_outbound_interval_secs > 0
}
pub fn dns_seeding_service_enabled(&self) -> bool {
!self.dns_seeds.is_empty()
}
}
const fn default_reuse() -> bool {
true
}
const fn default_reuse_tcp_with_ws() -> bool {
true
}