diem_types/on_chain_config/
mod.rs

1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4// Copyright 2021 Conflux Foundation. All rights reserved.
5// Conflux is free software and distributed under GNU General Public License.
6// See http://www.gnu.org/licenses/
7
8use crate::{
9    access_path::AccessPath,
10    account_address::AccountAddress,
11    account_config::CORE_CODE_ADDRESS,
12    event::{EventHandle, EventKey},
13};
14use anyhow::{format_err, Result};
15use move_core_types::{
16    identifier::Identifier,
17    language_storage::{StructTag, TypeTag},
18    move_resource::MoveResource,
19};
20use serde::{de::DeserializeOwned, Deserialize, Serialize};
21use std::{collections::HashMap, fmt, sync::Arc};
22
23mod diem_version;
24mod registered_currencies;
25mod validator_set;
26mod vm_config;
27mod vm_publishing_option;
28
29pub use self::{
30    diem_version::{DiemVersion, DIEM_MAX_KNOWN_VERSION, DIEM_VERSION_2},
31    registered_currencies::RegisteredCurrencies,
32    validator_set::{NextValidatorSetProposal, ValidatorSet},
33    vm_config::VMConfig,
34    vm_publishing_option::VMPublishingOption,
35};
36
37/// To register an on-chain config in Rust:
38/// 1. Implement the `OnChainConfig` trait for the Rust representation of the
39/// config 2. Add the config's `ConfigID` to `ON_CHAIN_CONFIG_REGISTRY`
40
41#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
42pub struct ConfigID(&'static str, &'static str);
43
44const CONFIG_ADDRESS_STR: &str = "0xA550C18";
45
46pub fn config_address() -> AccountAddress {
47    AccountAddress::from_hex_literal(CONFIG_ADDRESS_STR)
48        .expect("failed to get address")
49}
50
51impl ConfigID {
52    pub fn access_path(self) -> AccessPath {
53        access_path_for_config(
54            AccountAddress::from_hex_literal(self.0)
55                .expect("failed to get address"),
56            Identifier::new(self.1).expect("failed to get Identifier"),
57        )
58    }
59}
60
61impl fmt::Display for ConfigID {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        write!(
64            f,
65            "OnChain config ID [address: {}, identifier: {}]",
66            self.0, self.1
67        )
68    }
69}
70
71/// State sync will panic if the value of any config in this registry is
72/// uninitialized
73pub const ON_CHAIN_CONFIG_REGISTRY: &[ConfigID] = &[
74    //VMConfig::CONFIG_ID,
75    //VMPublishingOption::CONFIG_ID,
76    //DiemVersion::CONFIG_ID,
77    ValidatorSet::CONFIG_ID,
78    //RegisteredCurrencies::CONFIG_ID,
79];
80
81#[derive(Clone, Debug, PartialEq)]
82pub struct OnChainConfigPayload {
83    epoch: u64,
84    configs: Arc<HashMap<ConfigID, Vec<u8>>>,
85}
86
87impl OnChainConfigPayload {
88    pub fn new(epoch: u64, configs: Arc<HashMap<ConfigID, Vec<u8>>>) -> Self {
89        Self { epoch, configs }
90    }
91
92    pub fn epoch(&self) -> u64 { self.epoch }
93
94    pub fn get<T: OnChainConfig>(&self) -> Result<T> {
95        let bytes = self.configs.get(&T::CONFIG_ID).ok_or_else(|| {
96            format_err!("[on-chain cfg] config not in payload")
97        })?;
98        T::deserialize_into_config(bytes)
99    }
100
101    pub fn configs(&self) -> &HashMap<ConfigID, Vec<u8>> { &self.configs }
102}
103
104impl fmt::Display for OnChainConfigPayload {
105    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106        let mut config_ids = "".to_string();
107        for id in self.configs.keys() {
108            config_ids += &id.to_string();
109        }
110        write!(
111            f,
112            "OnChainConfigPayload [epoch: {}, configs: {}]",
113            self.epoch, config_ids
114        )
115    }
116}
117
118/// Trait to be implemented by a storage type from which to read on-chain
119/// configs
120pub trait ConfigStorage {
121    fn fetch_config(&self, access_path: AccessPath) -> Option<Vec<u8>>;
122}
123
124/// Trait to be implemented by a Rust struct representation of an on-chain
125/// config that is stored in storage as a serialized byte array
126pub trait OnChainConfig: Send + Sync + DeserializeOwned {
127    // diem_root_address
128    const ADDRESS: &'static str = CONFIG_ADDRESS_STR;
129    const IDENTIFIER: &'static str;
130    const CONFIG_ID: ConfigID = ConfigID(Self::ADDRESS, Self::IDENTIFIER);
131
132    // Single-round BCS deserialization from bytes to `Self`
133    // This is the expected deserialization pattern for most Rust
134    // representations, but sometimes `deserialize_into_config` may need an
135    // extra customized round of deserialization (e.g. enums like
136    // `VMPublishingOption`) In the override, we can reuse this default
137    // logic via this function Note: we cannot directly call the default
138    // `deserialize_into_config` implementation in its override - this will
139    // just refer to the override implementation itself
140    fn deserialize_default_impl(bytes: &[u8]) -> Result<Self> {
141        bcs::from_bytes::<Self>(&bytes).map_err(|e| {
142            format_err!(
143                "[on-chain config] Failed to deserialize into config: {}",
144                e
145            )
146        })
147    }
148
149    // Function for deserializing bytes to `Self`
150    // It will by default try one round of BCS deserialization directly to
151    // `Self` The implementation for the concrete type should override this
152    // function if this logic needs to be customized
153    fn deserialize_into_config(bytes: &[u8]) -> Result<Self> {
154        Self::deserialize_default_impl(bytes)
155    }
156
157    fn fetch_config<T>(storage: &T) -> Option<Self>
158    where T: ConfigStorage {
159        storage
160            .fetch_config(Self::CONFIG_ID.access_path())
161            .and_then(|bytes| Self::deserialize_into_config(&bytes).ok())
162    }
163}
164
165pub fn new_epoch_event_key() -> EventKey {
166    EventKey::new_from_address(&config_address(), 4)
167}
168
169pub fn access_path_for_config(
170    address: AccountAddress, config_name: Identifier,
171) -> AccessPath {
172    AccessPath::new(
173        address,
174        AccessPath::resource_access_vec(StructTag {
175            address: CORE_CODE_ADDRESS,
176            module: Identifier::new("DiemConfig").unwrap(),
177            name: Identifier::new("DiemConfig").unwrap(),
178            type_params: vec![TypeTag::Struct(StructTag {
179                address: CORE_CODE_ADDRESS,
180                module: config_name.clone(),
181                name: config_name,
182                type_params: vec![],
183            })],
184        }),
185    )
186}
187
188#[derive(Debug, Deserialize, Serialize)]
189pub struct ConfigurationResource {
190    epoch: u64,
191    last_reconfiguration_time: u64,
192    events: EventHandle,
193}
194
195impl ConfigurationResource {
196    pub fn epoch(&self) -> u64 { self.epoch }
197
198    pub fn last_reconfiguration_time(&self) -> u64 {
199        self.last_reconfiguration_time
200    }
201
202    pub fn events(&self) -> &EventHandle { &self.events }
203
204    #[cfg(feature = "fuzzing")]
205    pub fn bump_epoch_for_test(&self) -> Self {
206        let epoch = self.epoch + 1;
207        let last_reconfiguration_time = self.last_reconfiguration_time + 1;
208        let mut events = self.events.clone();
209        *events.count_mut() += 1;
210
211        Self {
212            epoch,
213            last_reconfiguration_time,
214            events,
215        }
216    }
217}
218
219#[cfg(feature = "fuzzing")]
220impl Default for ConfigurationResource {
221    fn default() -> Self {
222        Self {
223            epoch: 0,
224            last_reconfiguration_time: 0,
225            events: EventHandle::new_from_address(
226                &crate::account_config::diem_root_address(),
227                16,
228            ),
229        }
230    }
231}
232
233impl MoveResource for ConfigurationResource {
234    const MODULE_NAME: &'static str = "DiemConfig";
235    const STRUCT_NAME: &'static str = "Configuration";
236}