use std::{
convert::TryFrom,
fs, io,
path::{Path, PathBuf},
};
use log::error;
use num_rational::Ratio;
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use casper_execution_engine::{
core::engine_state::{
engine_config::{
EngineConfig, EngineConfigBuilder, FeeHandling, RefundHandling, DEFAULT_MAX_QUERY_DEPTH,
},
genesis::ExecConfigBuilder,
run_genesis_request::RunGenesisRequest,
ExecConfig, GenesisAccount,
},
shared::{system_config::SystemConfig, wasm_config::WasmConfig},
};
use casper_types::{system::auction::VESTING_SCHEDULE_LENGTH_MILLIS, ProtocolVersion, TimeDiff};
use crate::{
DEFAULT_ACCOUNTS, DEFAULT_CHAINSPEC_REGISTRY, DEFAULT_GENESIS_CONFIG_HASH,
DEFAULT_GENESIS_TIMESTAMP_MILLIS,
};
pub const CHAINSPEC_NAME: &str = "chainspec.toml";
pub static PRODUCTION_CHAINSPEC_PATH: Lazy<PathBuf> = Lazy::new(|| {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("resources/")
.join(CHAINSPEC_NAME)
});
#[derive(Debug)]
#[allow(clippy::enum_variant_names)]
pub enum Error {
FailedToLoadChainspec {
path: PathBuf,
error: io::Error,
},
FailedToParseChainspec(toml::de::Error),
Validation,
}
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct CoreConfig {
pub(crate) validator_slots: u32,
pub(crate) auction_delay: u64,
pub(crate) locked_funds_period: TimeDiff,
pub(crate) vesting_schedule_period: TimeDiff,
pub(crate) unbonding_delay: u64,
pub(crate) round_seigniorage_rate: Ratio<u64>,
pub(crate) max_associated_keys: u32,
pub(crate) max_runtime_call_stack_height: u32,
pub(crate) minimum_delegation_amount: u64,
pub(crate) strict_argument_checking: bool,
pub(crate) max_delegators_per_validator: Option<u32>,
pub(crate) refund_handling: RefundHandling,
pub(crate) fee_handling: FeeHandling,
}
#[derive(Deserialize, Clone)]
pub struct ChainspecConfig {
#[serde(rename = "core")]
pub(crate) core_config: CoreConfig,
#[serde(rename = "wasm")]
pub(crate) wasm_config: WasmConfig,
#[serde(rename = "system_costs")]
pub(crate) system_costs_config: SystemConfig,
}
impl ChainspecConfig {
fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
let chainspec_config: ChainspecConfig =
toml::from_slice(bytes).map_err(Error::FailedToParseChainspec)?;
if !chainspec_config.is_valid() {
return Err(Error::Validation);
}
Ok(chainspec_config)
}
fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
let path = path.as_ref();
let bytes = fs::read(path).map_err(|error| Error::FailedToLoadChainspec {
path: path.to_path_buf(),
error,
})?;
ChainspecConfig::from_bytes(&bytes)
}
pub fn from_chainspec_path<P: AsRef<Path>>(filename: P) -> Result<Self, Error> {
Self::from_path(filename)
}
fn is_valid(&self) -> bool {
if self.core_config.vesting_schedule_period
> TimeDiff::from_millis(VESTING_SCHEDULE_LENGTH_MILLIS)
{
error!(
"vesting schedule period too long (actual {}; maximum {})",
self.core_config.vesting_schedule_period.millis(),
VESTING_SCHEDULE_LENGTH_MILLIS,
);
return false;
}
true
}
pub(crate) fn create_genesis_request_from_chainspec<P: AsRef<Path>>(
filename: P,
genesis_accounts: Vec<GenesisAccount>,
protocol_version: ProtocolVersion,
) -> Result<RunGenesisRequest, Error> {
let chainspec_config = ChainspecConfig::from_path(filename)?;
let ChainspecConfig {
core_config,
wasm_config,
system_costs_config,
} = chainspec_config;
let CoreConfig {
validator_slots,
auction_delay,
locked_funds_period,
vesting_schedule_period: _,
unbonding_delay,
round_seigniorage_rate,
max_associated_keys: _,
max_runtime_call_stack_height: _,
minimum_delegation_amount: _,
strict_argument_checking: _,
max_delegators_per_validator: _,
refund_handling: _,
fee_handling: _,
} = core_config;
let exec_config = ExecConfigBuilder::new()
.with_accounts(genesis_accounts)
.with_wasm_config(wasm_config)
.with_system_config(system_costs_config)
.with_validator_slots(validator_slots)
.with_auction_delay(auction_delay)
.with_locked_funds_period_millis(locked_funds_period.millis())
.with_round_seigniorage_rate(round_seigniorage_rate)
.with_unbonding_delay(unbonding_delay)
.with_genesis_timestamp_millis(DEFAULT_GENESIS_TIMESTAMP_MILLIS)
.build();
Ok(RunGenesisRequest::new(
*DEFAULT_GENESIS_CONFIG_HASH,
protocol_version,
exec_config,
DEFAULT_CHAINSPEC_REGISTRY.clone(),
))
}
pub fn create_genesis_request_from_production_chainspec(
genesis_accounts: Vec<GenesisAccount>,
protocol_version: ProtocolVersion,
) -> Result<RunGenesisRequest, Error> {
Self::create_genesis_request_from_chainspec(
&*PRODUCTION_CHAINSPEC_PATH,
genesis_accounts,
protocol_version,
)
}
pub fn max_associated_keys(&self) -> u32 {
self.core_config.max_associated_keys
}
}
impl From<ChainspecConfig> for EngineConfig {
fn from(chainspec_config: ChainspecConfig) -> Self {
EngineConfigBuilder::new()
.with_max_query_depth(DEFAULT_MAX_QUERY_DEPTH)
.with_max_associated_keys(chainspec_config.core_config.max_associated_keys)
.with_max_runtime_call_stack_height(
chainspec_config.core_config.max_runtime_call_stack_height,
)
.with_minimum_delegation_amount(chainspec_config.core_config.minimum_delegation_amount)
.with_strict_argument_checking(chainspec_config.core_config.strict_argument_checking)
.with_vesting_schedule_period_millis(
chainspec_config
.core_config
.vesting_schedule_period
.millis(),
)
.with_max_delegators_per_validator(
chainspec_config.core_config.max_delegators_per_validator,
)
.with_wasm_config(chainspec_config.wasm_config)
.with_system_config(chainspec_config.system_costs_config)
.build()
}
}
impl TryFrom<ChainspecConfig> for ExecConfig {
type Error = Error;
fn try_from(chainspec_config: ChainspecConfig) -> Result<Self, Self::Error> {
Ok(ExecConfigBuilder::new()
.with_accounts(DEFAULT_ACCOUNTS.clone())
.with_wasm_config(chainspec_config.wasm_config)
.with_system_config(chainspec_config.system_costs_config)
.with_validator_slots(chainspec_config.core_config.validator_slots)
.with_auction_delay(chainspec_config.core_config.auction_delay)
.with_locked_funds_period_millis(
chainspec_config.core_config.locked_funds_period.millis(),
)
.with_round_seigniorage_rate(chainspec_config.core_config.round_seigniorage_rate)
.with_unbonding_delay(chainspec_config.core_config.unbonding_delay)
.with_genesis_timestamp_millis(DEFAULT_GENESIS_TIMESTAMP_MILLIS)
.build())
}
}
#[cfg(test)]
mod tests {
use std::{convert::TryFrom, path::PathBuf};
use once_cell::sync::Lazy;
use super::{ChainspecConfig, ExecConfig, CHAINSPEC_NAME};
pub static LOCAL_PATH: Lazy<PathBuf> =
Lazy::new(|| PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../resources/local/"));
#[test]
fn should_load_chainspec_config_from_chainspec() {
let path = &LOCAL_PATH.join(CHAINSPEC_NAME);
let chainspec_config = ChainspecConfig::from_chainspec_path(path).unwrap();
assert_eq!(chainspec_config.core_config.auction_delay, 1);
}
#[test]
fn should_get_exec_config_from_chainspec_values() {
let path = &LOCAL_PATH.join(CHAINSPEC_NAME);
let chainspec_config = ChainspecConfig::from_chainspec_path(path).unwrap();
let exec_config = ExecConfig::try_from(chainspec_config).unwrap();
assert_eq!(exec_config.auction_delay(), 1)
}
}