safety_rules/
persistent_safety_storage.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    counters,
10    logging::{self, LogEntry, LogEvent},
11    Error,
12};
13use consensus_types::{common::Author, safety_data::SafetyData};
14use diem_crypto::{
15    hash::CryptoHash, PrivateKey, SigningKey, ValidCryptoMaterial,
16};
17use diem_global_constants::{
18    CONSENSUS_KEY, EXECUTION_KEY, OWNER_ACCOUNT, SAFETY_DATA, WAYPOINT,
19};
20use diem_logger::prelude::*;
21use diem_secure_storage::{CryptoStorage, KVStorage, OnDiskStorage, Storage};
22use diem_types::{
23    validator_config::{
24        ConsensusPrivateKey, ConsensusPublicKey, ConsensusSignature,
25    },
26    waypoint::Waypoint,
27};
28use serde::Serialize;
29use std::{convert::TryFrom, fs};
30
31/// SafetyRules needs an abstract storage interface to act as a common utility
32/// for storing persistent data to local disk, cloud, secrets managers, or even
33/// memory (for tests) Any set function is expected to sync to the remote system
34/// before returning.
35///
36/// Note: cached_safety_data is a local in-memory copy of SafetyData. As
37/// SafetyData should only ever be used by safety rules, we maintain an
38/// in-memory copy to avoid issuing reads to the internal storage if the
39/// SafetyData hasn't changed. On writes, we update the cache and internal
40/// storage.
41pub struct PersistentSafetyStorage {
42    enable_cached_safety_data: bool,
43    cached_safety_data: Option<SafetyData>,
44    internal_store: Storage,
45    private_key: ConsensusPrivateKey,
46}
47
48impl PersistentSafetyStorage {
49    /// Use this to instantiate a PersistentStorage for a new data store, one
50    /// that has no SafetyRules values set.
51    pub fn initialize(
52        mut internal_store: Storage, author: Author,
53        private_key: ConsensusPrivateKey, waypoint: Waypoint,
54        enable_cached_safety_data: bool,
55    ) -> Self {
56        let geneisis_safety_data = SafetyData::new(1, 0, 0, None);
57        let safety_data = Self::initialize_(
58            &mut internal_store,
59            geneisis_safety_data,
60            author,
61            waypoint,
62        )
63        .expect("Unable to initialize backend storage");
64
65        Self {
66            enable_cached_safety_data,
67            cached_safety_data: Some(safety_data),
68            internal_store,
69            private_key,
70        }
71    }
72
73    pub fn replace_with_suffix(
74        &mut self, new_storage_suffix: &str,
75    ) -> Result<(), Error> {
76        match &mut self.internal_store {
77            Storage::OnDiskStorage(disk_storage) => {
78                let new_path =
79                    disk_storage.file_path().with_extension(new_storage_suffix);
80                if !new_path.exists() {
81                    return Err(Error::SecureStorageUnexpectedError(format!(
82                        "new secure storage path incorrect: {:?}",
83                        new_path
84                    )));
85                }
86                let new_disk_storage = OnDiskStorage::new(new_path.clone());
87                let old_account: Author =
88                    disk_storage.get(OWNER_ACCOUNT)?.value;
89                let new_account: Author =
90                    new_disk_storage.get(OWNER_ACCOUNT)?.value;
91                if old_account != new_account {
92                    return Err(Error::SecureStorageUnexpectedError(format!(
93                        "current: {}, new: {}",
94                        old_account, new_account
95                    )));
96                }
97                // Replace the old secure storage file with the new one.
98                fs::rename(&new_path, disk_storage.file_path())
99                    .map_err(|e| Error::InternalError(e.to_string()))?;
100                // Just replacing file should be sufficient. We create a new
101                // instance here in case we have any cached data
102                // within `OnDiskStorage` in future.
103                *disk_storage =
104                    OnDiskStorage::new(disk_storage.file_path().clone());
105                self.cached_safety_data = disk_storage.get(SAFETY_DATA)?.value;
106                Ok(())
107            }
108            _ => Err(Error::InternalError(
109                "unsupported secure storage type".to_string(),
110            )),
111        }
112    }
113
114    pub fn save_to_suffix(
115        &mut self, new_storage_suffix: &str,
116    ) -> Result<(), Error> {
117        match &self.internal_store {
118            Storage::OnDiskStorage(disk_storage) => {
119                let new_path =
120                    disk_storage.file_path().with_extension(new_storage_suffix);
121                fs::rename(disk_storage.file_path(), &new_path)
122                    .map_err(|e| Error::InternalError(e.to_string()))?;
123                Ok(())
124            }
125            _ => Err(Error::InternalError(
126                "unsupported secure storage type".to_string(),
127            )),
128        }
129    }
130
131    fn initialize_(
132        internal_store: &mut Storage, safety_data: SafetyData, author: Author,
133        waypoint: Waypoint,
134    ) -> Result<SafetyData, Error> {
135        // Attempting to re-initialize existing storage. This can happen in
136        // environments like cluster test. Rather than be rigid here,
137        // leave it up to the developer to detect inconsistencies or why
138        // they did not reset storage between rounds. Do not repeat the
139        // checks again below, because it is just too strange to have a
140        // partially configured storage.
141        // NOTE: If the key exists, `OnDiskStorage` does not return error
142        // when we `set` the value, so we need to `get` first here.
143        if let Ok(safety_data) = internal_store.get::<SafetyData>(SAFETY_DATA) {
144            diem_warn!("Attempted to re-initialize existing storage");
145            return Ok(safety_data.value);
146        }
147
148        internal_store.set(SAFETY_DATA, safety_data.clone())?;
149        internal_store.set(OWNER_ACCOUNT, author)?;
150        internal_store.set(WAYPOINT, waypoint)?;
151        Ok(safety_data)
152    }
153
154    pub fn author(&self) -> Result<Author, Error> {
155        let _timer = counters::start_timer("get", OWNER_ACCOUNT);
156        Ok(self.internal_store.get(OWNER_ACCOUNT).map(|v| v.value)?)
157    }
158
159    pub fn consensus_key_for_version(
160        &self, version: ConsensusPublicKey,
161    ) -> Result<ConsensusPrivateKey, Error> {
162        let _timer = counters::start_timer("get", CONSENSUS_KEY);
163        if self.private_key.public_key() == version {
164            let serialized: &[u8] = &(self.private_key.to_bytes());
165            let cloned = ConsensusPrivateKey::try_from(serialized).unwrap();
166            Ok(cloned)
167        } else {
168            Ok(self
169                .internal_store
170                .export_private_key_for_version(CONSENSUS_KEY, version)?)
171        }
172    }
173
174    pub fn sign<T: Serialize + CryptoHash>(
175        &self, key_name: String, key_version: ConsensusPublicKey, message: &T,
176    ) -> Result<ConsensusSignature, Error> {
177        if key_name == CONSENSUS_KEY || key_name == EXECUTION_KEY {
178            Ok(self.private_key.sign(message))
179        } else {
180            Ok(self.internal_store.sign_using_version(
181                &key_name,
182                key_version,
183                message,
184            )?)
185        }
186    }
187
188    pub fn safety_data(&mut self) -> Result<SafetyData, Error> {
189        if !self.enable_cached_safety_data {
190            let _timer = counters::start_timer("get", SAFETY_DATA);
191            return self.internal_store.get(SAFETY_DATA).map(|v| v.value)?;
192        }
193
194        if let Some(cached_safety_data) = self.cached_safety_data.clone() {
195            Ok(cached_safety_data)
196        } else {
197            let _timer = counters::start_timer("get", SAFETY_DATA);
198            let safety_data: SafetyData =
199                self.internal_store.get(SAFETY_DATA).map(|v| v.value)?;
200            self.cached_safety_data = Some(safety_data.clone());
201            Ok(safety_data)
202        }
203    }
204
205    pub fn set_safety_data(&mut self, data: SafetyData) -> Result<(), Error> {
206        let _timer = counters::start_timer("set", SAFETY_DATA);
207        counters::set_state("epoch", data.epoch as i64);
208        counters::set_state("last_voted_round", data.last_voted_round as i64);
209        counters::set_state("preferred_round", data.preferred_round as i64);
210
211        match self.internal_store.set(SAFETY_DATA, data.clone()) {
212            Ok(_) => {
213                self.cached_safety_data = Some(data);
214                Ok(())
215            }
216            Err(error) => {
217                self.cached_safety_data = None;
218                Err(Error::SecureStorageUnexpectedError(error.to_string()))
219            }
220        }
221    }
222
223    pub fn waypoint(&self) -> Result<Waypoint, Error> {
224        let _timer = counters::start_timer("get", WAYPOINT);
225        Ok(self.internal_store.get(WAYPOINT).map(|v| v.value)?)
226    }
227
228    pub fn set_waypoint(&mut self, waypoint: &Waypoint) -> Result<(), Error> {
229        let _timer = counters::start_timer("set", WAYPOINT);
230        self.internal_store.set(WAYPOINT, waypoint)?;
231        diem_info!(logging::SafetyLogSchema::new(
232            LogEntry::Waypoint,
233            LogEvent::Update
234        )
235        .waypoint(*waypoint));
236        Ok(())
237    }
238
239    #[cfg(any(test, feature = "testing"))]
240    pub fn internal_store(&mut self) -> &mut Storage {
241        &mut self.internal_store
242    }
243}
244
245#[cfg(test)]
246mod tests {
247    use super::*;
248    use diem_secure_storage::InMemoryStorage;
249    use diem_types::validator_signer::ValidatorSigner;
250
251    #[test]
252    fn test() {
253        let consensus_private_key =
254            ValidatorSigner::from_int(0).private_key().clone();
255        let storage = Storage::from(InMemoryStorage::new());
256        let mut safety_storage = PersistentSafetyStorage::initialize(
257            storage,
258            Author::random(),
259            consensus_private_key,
260            Waypoint::default(),
261            true,
262        );
263
264        let safety_data = safety_storage.safety_data().unwrap();
265        assert_eq!(safety_data.epoch, 1);
266        assert_eq!(safety_data.last_voted_round, 0);
267        assert_eq!(safety_data.preferred_round, 0);
268
269        safety_storage
270            .set_safety_data(SafetyData::new(9, 8, 1, None))
271            .unwrap();
272
273        let safety_data = safety_storage.safety_data().unwrap();
274        assert_eq!(safety_data.epoch, 9);
275        assert_eq!(safety_data.last_voted_round, 8);
276        assert_eq!(safety_data.preferred_round, 1);
277    }
278}