use std::{
collections::{BTreeMap, HashSet},
fmt,
};
use linera_base::{
data_types::{ApplicationPermissions, BlanketMessagePolicy, MessagePolicy, TimeDelta},
identifiers::{AccountOwner, ApplicationId, ChainId, GenericApplicationId},
ownership::ChainOwnership,
time::Duration,
};
use linera_core::{
client::{
chain_client, DEFAULT_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
DEFAULT_CERTIFICATE_UPLOAD_BATCH_SIZE, DEFAULT_MAX_CONCURRENT_BATCH_DOWNLOADS,
DEFAULT_MAX_EVENT_STREAM_QUERIES, DEFAULT_SENDER_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
},
node::CrossChainMessageDelivery,
DEFAULT_QUORUM_GRACE_PERIOD,
};
use linera_execution::ResourceControlPolicy;
#[cfg(not(web))]
use crate::client_metrics::TimingConfig;
use crate::util;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("I/O error: {0}")]
IoError(#[from] std::io::Error),
#[error("there are {public_keys} public keys but {weights} weights")]
MisalignedWeights { public_keys: usize, weights: usize },
#[error("config error: {0}")]
Config(#[from] crate::config::GenesisConfigError),
}
util::impl_from_infallible!(Error);
#[derive(Clone, clap::Parser, serde::Deserialize, tsify::Tsify)]
#[tsify(from_wasm_abi)]
#[group(skip)]
#[serde(default, rename_all = "camelCase")]
pub struct Options {
#[arg(long = "send-timeout-ms", default_value = "4000", value_parser = util::parse_millis)]
pub send_timeout: Duration,
#[arg(long = "recv-timeout-ms", default_value = "4000", value_parser = util::parse_millis)]
pub recv_timeout: Duration,
#[arg(long, default_value = "300")]
pub max_pending_message_bundles: usize,
#[arg(long, default_value = "3")]
pub max_block_limit_errors: u32,
#[arg(long, default_value = "10")]
pub max_new_events_per_block: usize,
#[arg(long = "staging-bundles-time-budget-ms", value_parser = util::parse_millis)]
pub staging_bundles_time_budget: Option<Duration>,
#[arg(long, value_parser = util::parse_chain_set)]
pub prioritize_bundles_from: Option<HashSet<ChainId>>,
#[arg(long, value_parser = util::parse_chain_set)]
pub ignore_bundles_from: Option<HashSet<ChainId>>,
#[arg(
long = "chain-worker-ttl-ms",
default_value = "30000",
env = "LINERA_CHAIN_WORKER_TTL_MS",
value_parser = util::parse_millis,
)]
pub chain_worker_ttl: Duration,
#[arg(
long = "sender-chain-worker-ttl-ms",
default_value = "1000",
env = "LINERA_SENDER_CHAIN_WORKER_TTL_MS",
value_parser = util::parse_millis
)]
pub sender_chain_worker_ttl: Duration,
#[arg(long, default_value_t = 1000)]
pub cross_chain_batch_size_limit: usize,
#[arg(
long = "retry-delay-ms",
default_value = "1000",
value_parser = util::parse_millis
)]
pub retry_delay: Duration,
#[arg(long, default_value = "10")]
pub max_retries: u32,
#[arg(
long = "max-backoff-ms",
default_value = "30000",
value_parser = util::parse_millis
)]
pub max_backoff: Duration,
#[arg(
long = "notification-circuit-breaker-initial-probe-interval-ms",
default_value = "300000",
value_parser = util::parse_millis
)]
pub notification_circuit_breaker_initial_probe_interval: Duration,
#[arg(
long = "notification-circuit-breaker-max-probe-interval-ms",
default_value = "3600000",
value_parser = util::parse_millis
)]
pub notification_circuit_breaker_max_probe_interval: Duration,
#[arg(long)]
pub wait_for_outgoing_messages: bool,
#[arg(long)]
pub allow_fast_blocks: bool,
#[arg(long)]
pub long_lived_services: bool,
#[arg(long, default_value_t, value_enum)]
pub blanket_message_policy: BlanketMessagePolicy,
#[arg(long, value_parser = util::parse_chain_set)]
pub restrict_chain_ids_to: Option<HashSet<ChainId>>,
#[arg(long, value_parser = util::parse_app_set)]
pub reject_message_bundles_without_application_ids: Option<HashSet<GenericApplicationId>>,
#[arg(long, value_parser = util::parse_app_set)]
pub reject_message_bundles_with_other_application_ids: Option<HashSet<GenericApplicationId>>,
#[arg(long, value_parser = util::parse_app_set)]
pub process_events_from_application_ids: Option<HashSet<GenericApplicationId>>,
#[arg(long, value_parser = util::parse_app_set)]
pub never_reject_application_ids: Option<HashSet<GenericApplicationId>>,
#[cfg(not(web))]
#[arg(long)]
pub timings: bool,
#[cfg(not(web))]
#[arg(long, default_value = "5")]
pub timing_interval: u64,
#[arg(long, default_value_t = DEFAULT_QUORUM_GRACE_PERIOD)]
pub quorum_grace_period: f64,
#[arg(
long = "blob-download-timeout-ms",
default_value = "1000",
value_parser = util::parse_millis,
)]
pub blob_download_timeout: Duration,
#[arg(
long = "cert-batch-download-timeout-ms",
default_value = "1000",
value_parser = util::parse_millis
)]
pub certificate_batch_download_timeout: Duration,
#[arg(
long,
default_value_t = DEFAULT_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
)]
pub certificate_download_batch_size: u64,
#[arg(
long,
default_value_t = DEFAULT_CERTIFICATE_UPLOAD_BATCH_SIZE,
)]
pub certificate_upload_batch_size: u64,
#[arg(
long,
default_value_t = DEFAULT_SENDER_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
)]
pub sender_certificate_download_batch_size: usize,
#[arg(long, default_value_t = DEFAULT_MAX_CONCURRENT_BATCH_DOWNLOADS)]
pub max_concurrent_batch_downloads: usize,
#[arg(long, default_value = "100")]
pub max_joined_tasks: usize,
#[arg(long, default_value_t = DEFAULT_MAX_EVENT_STREAM_QUERIES)]
pub max_event_stream_queries: usize,
#[arg(
long,
default_value_t = linera_core::client::requests_scheduler::MAX_ACCEPTED_LATENCY_MS,
env = "LINERA_REQUESTS_SCHEDULER_MAX_ACCEPTED_LATENCY_MS"
)]
pub max_accepted_latency_ms: f64,
#[arg(
long,
default_value_t = linera_core::client::requests_scheduler::CACHE_TTL_MS,
env = "LINERA_REQUESTS_SCHEDULER_CACHE_TTL_MS"
)]
pub cache_ttl_ms: u64,
#[arg(
long,
default_value_t = linera_core::client::requests_scheduler::CACHE_MAX_SIZE,
env = "LINERA_REQUESTS_SCHEDULER_CACHE_MAX_SIZE"
)]
pub cache_max_size: usize,
#[arg(
long,
default_value_t = linera_core::client::requests_scheduler::MAX_REQUEST_TTL_MS,
env = "LINERA_REQUESTS_SCHEDULER_MAX_REQUEST_TTL_MS"
)]
pub max_request_ttl_ms: u64,
#[arg(
long,
default_value_t = linera_core::client::requests_scheduler::ALPHA_SMOOTHING_FACTOR,
env = "LINERA_REQUESTS_SCHEDULER_ALPHA"
)]
pub alpha: f64,
#[arg(
long,
default_value_t = linera_core::client::requests_scheduler::STAGGERED_DELAY_MS,
env = "LINERA_REQUESTS_SCHEDULER_ALTERNATIVE_PEERS_RETRY_DELAY_MS"
)]
pub alternative_peers_retry_delay_ms: u64,
#[serde(flatten)]
#[clap(flatten)]
pub chain_listener_config: crate::chain_listener::ChainListenerConfig,
}
impl Default for Options {
fn default() -> Self {
use clap::Parser;
#[derive(Parser)]
struct OptionsParser {
#[clap(flatten)]
options: Options,
}
OptionsParser::try_parse_from(std::iter::empty::<std::ffi::OsString>())
.expect("Options has no required arguments")
.options
}
}
impl Options {
pub(crate) fn to_chain_client_options(&self) -> chain_client::Options {
let message_policy = MessagePolicy {
blanket: self.blanket_message_policy,
restrict_chain_ids_to: self.restrict_chain_ids_to.clone(),
ignore_chain_ids: self.ignore_bundles_from.clone().unwrap_or_default(),
reject_message_bundles_without_application_ids: self
.reject_message_bundles_without_application_ids
.clone(),
reject_message_bundles_with_other_application_ids: self
.reject_message_bundles_with_other_application_ids
.clone(),
process_events_from_application_ids: self.process_events_from_application_ids.clone(),
never_reject_application_ids: self
.never_reject_application_ids
.clone()
.unwrap_or_default(),
};
let cross_chain_message_delivery =
CrossChainMessageDelivery::new(self.wait_for_outgoing_messages);
chain_client::Options {
max_pending_message_bundles: self.max_pending_message_bundles,
max_block_limit_errors: self.max_block_limit_errors,
max_new_events_per_block: self.max_new_events_per_block,
staging_bundles_time_budget: self.staging_bundles_time_budget,
priority_bundle_origins: self.prioritize_bundles_from.clone().unwrap_or_default(),
message_policy,
cross_chain_message_delivery,
quorum_grace_period: self.quorum_grace_period,
blob_download_timeout: self.blob_download_timeout,
certificate_batch_download_timeout: self.certificate_batch_download_timeout,
certificate_download_batch_size: self.certificate_download_batch_size,
certificate_upload_batch_size: self.certificate_upload_batch_size,
sender_certificate_download_batch_size: self.sender_certificate_download_batch_size,
max_concurrent_batch_downloads: self.max_concurrent_batch_downloads,
max_joined_tasks: self.max_joined_tasks,
allow_fast_blocks: self.allow_fast_blocks,
notification_circuit_breaker_initial_probe_interval: self
.notification_circuit_breaker_initial_probe_interval,
notification_circuit_breaker_max_probe_interval: self
.notification_circuit_breaker_max_probe_interval,
max_event_stream_queries: self.max_event_stream_queries,
}
}
#[cfg(not(web))]
pub(crate) fn to_timing_config(&self) -> TimingConfig {
TimingConfig {
enabled: self.timings,
report_interval_secs: self.timing_interval,
}
}
pub(crate) fn to_requests_scheduler_config(
&self,
) -> linera_core::client::RequestsSchedulerConfig {
linera_core::client::RequestsSchedulerConfig {
max_accepted_latency_ms: self.max_accepted_latency_ms,
cache_ttl_ms: self.cache_ttl_ms,
cache_max_size: self.cache_max_size,
max_request_ttl_ms: self.max_request_ttl_ms,
alpha: self.alpha,
retry_delay_ms: self.alternative_peers_retry_delay_ms,
}
}
}
#[derive(Debug, Clone, clap::Args)]
pub struct ChainOwnershipConfig {
#[arg(long, value_parser = util::parse_json::<Vec<AccountOwner>>)]
pub super_owners: Option<std::vec::Vec<AccountOwner>>,
#[arg(long, value_parser = util::parse_json::<BTreeMap<AccountOwner, u64>>)]
pub owners: Option<BTreeMap<AccountOwner, u64>>,
#[arg(long, value_parser = util::parse_json::<Option<u32>>)]
pub multi_leader_rounds: Option<std::option::Option<u32>>,
#[arg(long)]
pub open_multi_leader_rounds: bool,
#[arg(long = "fast-round-ms", value_parser = util::parse_json_optional_millis_delta)]
pub fast_round_duration: Option<std::option::Option<TimeDelta>>,
#[arg(
long = "base-timeout-ms",
value_parser = util::parse_millis_delta
)]
pub base_timeout: Option<TimeDelta>,
#[arg(
long = "timeout-increment-ms",
value_parser = util::parse_millis_delta
)]
pub timeout_increment: Option<TimeDelta>,
#[arg(
long = "fallback-duration-ms",
value_parser = util::parse_millis_delta
)]
pub fallback_duration: Option<TimeDelta>,
}
impl ChainOwnershipConfig {
pub fn update(self, chain_ownership: &mut ChainOwnership) -> Result<(), Error> {
let ChainOwnershipConfig {
super_owners,
owners,
multi_leader_rounds,
fast_round_duration,
open_multi_leader_rounds,
base_timeout,
timeout_increment,
fallback_duration,
} = self;
if let Some(owners) = owners {
chain_ownership.owners = owners;
}
if let Some(super_owners) = super_owners {
chain_ownership.super_owners = super_owners.into_iter().collect();
}
if let Some(multi_leader_rounds) = multi_leader_rounds {
chain_ownership.multi_leader_rounds = multi_leader_rounds.unwrap_or(u32::MAX);
}
chain_ownership.open_multi_leader_rounds = open_multi_leader_rounds;
if let Some(fast_round_duration) = fast_round_duration {
chain_ownership.timeout_config.fast_round_duration = fast_round_duration;
}
if let Some(base_timeout) = base_timeout {
chain_ownership.timeout_config.base_timeout = base_timeout;
}
if let Some(timeout_increment) = timeout_increment {
chain_ownership.timeout_config.timeout_increment = timeout_increment;
}
if let Some(fallback_duration) = fallback_duration {
chain_ownership.timeout_config.fallback_duration = fallback_duration;
}
Ok(())
}
}
impl TryFrom<ChainOwnershipConfig> for ChainOwnership {
type Error = Error;
fn try_from(config: ChainOwnershipConfig) -> Result<ChainOwnership, Error> {
let mut chain_ownership = ChainOwnership::default();
config.update(&mut chain_ownership)?;
Ok(chain_ownership)
}
}
#[derive(Debug, Clone, clap::Args)]
pub struct ApplicationPermissionsConfig {
#[arg(long, value_parser = util::parse_json::<Option<Vec<ApplicationId>>>)]
pub execute_operations: Option<std::option::Option<Vec<ApplicationId>>>,
#[arg(long, value_parser = util::parse_json::<Vec<ApplicationId>>)]
pub mandatory_applications: Option<std::vec::Vec<ApplicationId>>,
#[arg(long, value_parser = util::parse_json::<Vec<ApplicationId>>)]
pub close_chain: Option<std::vec::Vec<ApplicationId>>,
#[arg(long, value_parser = util::parse_json::<Vec<ApplicationId>>)]
pub change_application_permissions: Option<std::vec::Vec<ApplicationId>>,
#[arg(long, value_parser = util::parse_json::<Option<Vec<ApplicationId>>>)]
pub call_service_as_oracle: Option<std::option::Option<Vec<ApplicationId>>>,
#[arg(long, value_parser = util::parse_json::<Option<Vec<ApplicationId>>>)]
pub make_http_requests: Option<std::option::Option<Vec<ApplicationId>>>,
}
impl ApplicationPermissionsConfig {
pub fn update(self, application_permissions: &mut ApplicationPermissions) {
if let Some(execute_operations) = self.execute_operations {
application_permissions.execute_operations = execute_operations;
}
if let Some(mandatory_applications) = self.mandatory_applications {
application_permissions.mandatory_applications = mandatory_applications;
}
if let Some(close_chain) = self.close_chain {
application_permissions.close_chain = close_chain;
}
if let Some(change_application_permissions) = self.change_application_permissions {
application_permissions.change_application_permissions = change_application_permissions;
}
if let Some(call_service_as_oracle) = self.call_service_as_oracle {
application_permissions.call_service_as_oracle = call_service_as_oracle;
}
if let Some(make_http_requests) = self.make_http_requests {
application_permissions.make_http_requests = make_http_requests;
}
}
}
#[derive(clap::ValueEnum, Clone, Copy, Debug, PartialEq, Eq)]
pub enum ResourceControlPolicyConfig {
NoFees,
Testnet,
#[cfg(with_testing)]
OnlyFuel,
#[cfg(with_testing)]
AllCategories,
}
impl ResourceControlPolicyConfig {
pub fn into_policy(self) -> ResourceControlPolicy {
match self {
ResourceControlPolicyConfig::NoFees => ResourceControlPolicy::no_fees(),
ResourceControlPolicyConfig::Testnet => ResourceControlPolicy::testnet(),
#[cfg(with_testing)]
ResourceControlPolicyConfig::OnlyFuel => ResourceControlPolicy::only_fuel(),
#[cfg(with_testing)]
ResourceControlPolicyConfig::AllCategories => ResourceControlPolicy::all_categories(),
}
}
}
impl std::str::FromStr for ResourceControlPolicyConfig {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
clap::ValueEnum::from_str(s, true)
}
}
impl fmt::Display for ResourceControlPolicyConfig {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{self:?}")
}
}