use crate::{
access_path::AccessPath,
account_address::AccountAddress,
account_config::CORE_CODE_ADDRESS,
event::{EventHandle, EventKey},
};
use anyhow::{format_err, Result};
use move_core_types::{
identifier::Identifier,
language_storage::{StructTag, TypeTag},
move_resource::MoveResource,
};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::{collections::HashMap, fmt, sync::Arc};
mod diem_version;
mod registered_currencies;
mod validator_set;
mod vm_config;
mod vm_publishing_option;
pub use self::{
diem_version::{DiemVersion, DIEM_MAX_KNOWN_VERSION, DIEM_VERSION_2},
registered_currencies::RegisteredCurrencies,
validator_set::{NextValidatorSetProposal, ValidatorSet},
vm_config::VMConfig,
vm_publishing_option::VMPublishingOption,
};
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub struct ConfigID(&'static str, &'static str);
const CONFIG_ADDRESS_STR: &str = "0xA550C18";
pub fn config_address() -> AccountAddress {
AccountAddress::from_hex_literal(CONFIG_ADDRESS_STR)
.expect("failed to get address")
}
impl ConfigID {
pub fn access_path(self) -> AccessPath {
access_path_for_config(
AccountAddress::from_hex_literal(self.0)
.expect("failed to get address"),
Identifier::new(self.1).expect("failed to get Identifier"),
)
}
}
impl fmt::Display for ConfigID {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"OnChain config ID [address: {}, identifier: {}]",
self.0, self.1
)
}
}
pub const ON_CHAIN_CONFIG_REGISTRY: &[ConfigID] = &[
ValidatorSet::CONFIG_ID,
];
#[derive(Clone, Debug, PartialEq)]
pub struct OnChainConfigPayload {
epoch: u64,
configs: Arc<HashMap<ConfigID, Vec<u8>>>,
}
impl OnChainConfigPayload {
pub fn new(epoch: u64, configs: Arc<HashMap<ConfigID, Vec<u8>>>) -> Self {
Self { epoch, configs }
}
pub fn epoch(&self) -> u64 { self.epoch }
pub fn get<T: OnChainConfig>(&self) -> Result<T> {
let bytes = self.configs.get(&T::CONFIG_ID).ok_or_else(|| {
format_err!("[on-chain cfg] config not in payload")
})?;
T::deserialize_into_config(bytes)
}
pub fn configs(&self) -> &HashMap<ConfigID, Vec<u8>> { &self.configs }
}
impl fmt::Display for OnChainConfigPayload {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut config_ids = "".to_string();
for id in self.configs.keys() {
config_ids += &id.to_string();
}
write!(
f,
"OnChainConfigPayload [epoch: {}, configs: {}]",
self.epoch, config_ids
)
}
}
pub trait ConfigStorage {
fn fetch_config(&self, access_path: AccessPath) -> Option<Vec<u8>>;
}
pub trait OnChainConfig: Send + Sync + DeserializeOwned {
const ADDRESS: &'static str = CONFIG_ADDRESS_STR;
const IDENTIFIER: &'static str;
const CONFIG_ID: ConfigID = ConfigID(Self::ADDRESS, Self::IDENTIFIER);
fn deserialize_default_impl(bytes: &[u8]) -> Result<Self> {
bcs::from_bytes::<Self>(&bytes).map_err(|e| {
format_err!(
"[on-chain config] Failed to deserialize into config: {}",
e
)
})
}
fn deserialize_into_config(bytes: &[u8]) -> Result<Self> {
Self::deserialize_default_impl(bytes)
}
fn fetch_config<T>(storage: &T) -> Option<Self>
where T: ConfigStorage {
storage
.fetch_config(Self::CONFIG_ID.access_path())
.and_then(|bytes| Self::deserialize_into_config(&bytes).ok())
}
}
pub fn new_epoch_event_key() -> EventKey {
EventKey::new_from_address(&config_address(), 4)
}
pub fn access_path_for_config(
address: AccountAddress, config_name: Identifier,
) -> AccessPath {
AccessPath::new(
address,
AccessPath::resource_access_vec(StructTag {
address: CORE_CODE_ADDRESS,
module: Identifier::new("DiemConfig").unwrap(),
name: Identifier::new("DiemConfig").unwrap(),
type_params: vec![TypeTag::Struct(StructTag {
address: CORE_CODE_ADDRESS,
module: config_name.clone(),
name: config_name,
type_params: vec![],
})],
}),
)
}
#[derive(Debug, Deserialize, Serialize)]
pub struct ConfigurationResource {
epoch: u64,
last_reconfiguration_time: u64,
events: EventHandle,
}
impl ConfigurationResource {
pub fn epoch(&self) -> u64 { self.epoch }
pub fn last_reconfiguration_time(&self) -> u64 {
self.last_reconfiguration_time
}
pub fn events(&self) -> &EventHandle { &self.events }
#[cfg(feature = "fuzzing")]
pub fn bump_epoch_for_test(&self) -> Self {
let epoch = self.epoch + 1;
let last_reconfiguration_time = self.last_reconfiguration_time + 1;
let mut events = self.events.clone();
*events.count_mut() += 1;
Self {
epoch,
last_reconfiguration_time,
events,
}
}
}
#[cfg(feature = "fuzzing")]
impl Default for ConfigurationResource {
fn default() -> Self {
Self {
epoch: 0,
last_reconfiguration_time: 0,
events: EventHandle::new_from_address(
&crate::account_config::diem_root_address(),
16,
),
}
}
}
impl MoveResource for ConfigurationResource {
const MODULE_NAME: &'static str = "DiemConfig";
const STRUCT_NAME: &'static str = "Configuration";
}