1use 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
31pub 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 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 fs::rename(&new_path, disk_storage.file_path())
99 .map_err(|e| Error::InternalError(e.to_string()))?;
100 *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 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}