1use {
2 crate::consensus::{
3 Result, Tower, TowerError, TowerVersions, tower1_7_14::SavedTower1_7_14,
4 tower1_14_11::Tower1_14_11,
5 },
6 serde::{Deserialize, Serialize},
7 solana_pubkey::Pubkey,
8 solana_signature::Signature,
9 solana_signer::Signer,
10 std::{
11 fs::{self, File},
12 io::{self, BufReader},
13 path::PathBuf,
14 },
15};
16
17#[cfg_attr(feature = "frozen-abi", derive(AbiExample, StableAbi, StableAbiSample))]
18#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
19pub enum SavedTowerVersions {
20 V1_17_14(SavedTower1_7_14),
21 Current(SavedTower),
22}
23
24impl SavedTowerVersions {
25 fn try_into_tower(&self, node_pubkey: &Pubkey) -> Result<Tower> {
26 assert_eq!(self.pubkey(), Pubkey::default());
28
29 let tv = match self {
30 SavedTowerVersions::V1_17_14(t) => {
31 if !t.signature.verify(node_pubkey.as_ref(), &t.data) {
32 return Err(TowerError::InvalidSignature);
33 }
34 bincode::deserialize(&t.data).map(TowerVersions::V1_7_14)
35 }
36 SavedTowerVersions::Current(t) => {
37 if !t.signature.verify(node_pubkey.as_ref(), &t.data) {
38 return Err(TowerError::InvalidSignature);
39 }
40 bincode::deserialize(&t.data).map(TowerVersions::V1_14_11)
41 }
42 };
43 tv.map_err(|e| e.into()).and_then(|tv: TowerVersions| {
44 let tower = tv.convert_to_current();
45 if tower.node_pubkey != *node_pubkey {
46 return Err(TowerError::WrongTower(format!(
47 "node_pubkey is {:?} but found tower for {:?}",
48 node_pubkey, tower.node_pubkey
49 )));
50 }
51 Ok(tower)
52 })
53 }
54
55 fn serialize_into(&self, file: &mut File) -> Result<()> {
56 bincode::serialize_into(file, self).map_err(|e| e.into())
57 }
58
59 fn pubkey(&self) -> Pubkey {
60 match self {
61 SavedTowerVersions::V1_17_14(t) => t.node_pubkey,
62 SavedTowerVersions::Current(t) => t.node_pubkey,
63 }
64 }
65}
66
67impl From<SavedTower> for SavedTowerVersions {
68 fn from(tower: SavedTower) -> SavedTowerVersions {
69 SavedTowerVersions::Current(tower)
70 }
71}
72
73impl From<SavedTower1_7_14> for SavedTowerVersions {
74 fn from(tower: SavedTower1_7_14) -> SavedTowerVersions {
75 SavedTowerVersions::V1_17_14(tower)
76 }
77}
78
79#[cfg_attr(
80 feature = "frozen-abi",
81 derive(AbiExample, StableAbi, StableAbiSample),
82 frozen_abi(
83 api_digest = "2Ne3NmHSeLpPfv38wn7ZRsuq4i56kqYzJeFLYmz6bw3Z",
84 abi_digest = "6U5RkdsuzLHyWpjGWKCHj9AzGFreginb6cXUiXodbgRv"
85 )
86)]
87#[derive(Default, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
88pub struct SavedTower {
89 signature: Signature,
90 #[serde(with = "serde_bytes")]
91 data: Vec<u8>,
92 #[serde(skip)]
93 node_pubkey: Pubkey,
94}
95
96impl SavedTower {
97 pub fn new<T: Signer>(tower: &Tower, keypair: &T) -> Result<Self> {
98 let node_pubkey = keypair.pubkey();
99 if tower.node_pubkey != node_pubkey {
100 return Err(TowerError::WrongTower(format!(
101 "node_pubkey is {:?} but found tower for {:?}",
102 node_pubkey, tower.node_pubkey
103 )));
104 }
105
106 let tower: Tower1_14_11 = tower.clone().into();
108
109 let data = bincode::serialize(&tower)?;
110 let signature = keypair.sign_message(&data);
111 Ok(Self {
112 signature,
113 data,
114 node_pubkey,
115 })
116 }
117}
118
119pub trait TowerStorage: Sync + Send {
120 fn load(&self, node_pubkey: &Pubkey) -> Result<Tower>;
121 fn store(&self, saved_tower: &SavedTowerVersions) -> Result<()>;
122}
123
124#[derive(Debug, Default, Clone, PartialEq, Eq)]
125pub struct NullTowerStorage {}
126
127impl TowerStorage for NullTowerStorage {
128 fn load(&self, _node_pubkey: &Pubkey) -> Result<Tower> {
129 Err(TowerError::IoError(io::Error::other(
130 "NullTowerStorage::load() not available",
131 )))
132 }
133
134 fn store(&self, _saved_tower: &SavedTowerVersions) -> Result<()> {
135 Ok(())
136 }
137}
138
139#[derive(Debug, Default, Clone, PartialEq, Eq)]
140pub struct FileTowerStorage {
141 pub tower_path: PathBuf,
142}
143
144impl FileTowerStorage {
145 pub fn new(tower_path: PathBuf) -> Self {
146 Self { tower_path }
147 }
148
149 pub fn old_filename(&self, node_pubkey: &Pubkey) -> PathBuf {
151 self.tower_path
152 .join(format!("tower-{node_pubkey}"))
153 .with_extension("bin")
154 }
155
156 pub fn filename(&self, node_pubkey: &Pubkey) -> PathBuf {
157 self.tower_path
158 .join(format!("tower-1_9-{node_pubkey}"))
159 .with_extension("bin")
160 }
161
162 #[cfg(test)]
163 fn store_old(&self, saved_tower: &SavedTower1_7_14) -> Result<()> {
164 let pubkey = saved_tower.node_pubkey;
165 let filename = self.old_filename(&pubkey);
166 trace!("store: {}", filename.display());
167 let new_filename = filename.with_extension("bin.new");
168
169 {
170 let file = File::create(&new_filename)?;
172 bincode::serialize_into(file, saved_tower)?;
173 }
175 fs::rename(&new_filename, &filename)?;
176 Ok(())
178 }
179}
180
181impl TowerStorage for FileTowerStorage {
182 fn load(&self, node_pubkey: &Pubkey) -> Result<Tower> {
183 let filename = self.filename(node_pubkey);
184 trace!("load {}", filename.display());
185
186 fs::create_dir_all(filename.parent().unwrap())?;
188
189 if let Ok(file) = File::open(&filename) {
190 let mut stream = BufReader::new(file);
192
193 bincode::deserialize_from(&mut stream)
194 .map_err(|e| e.into())
195 .and_then(|t: SavedTowerVersions| t.try_into_tower(node_pubkey))
196 } else {
197 let file = File::open(self.old_filename(node_pubkey))?;
199 let mut stream = BufReader::new(file);
200 bincode::deserialize_from(&mut stream)
201 .map_err(|e| e.into())
202 .and_then(|t: SavedTower1_7_14| {
203 SavedTowerVersions::from(t).try_into_tower(node_pubkey)
204 })
205 }
206 }
207
208 fn store(&self, saved_tower: &SavedTowerVersions) -> Result<()> {
209 let pubkey = saved_tower.pubkey();
210 let filename = self.filename(&pubkey);
211 trace!("store: {}", filename.display());
212 let new_filename = filename.with_extension("bin.new");
213
214 {
215 let mut file = File::create(&new_filename)?;
217 saved_tower.serialize_into(&mut file)?;
218 }
220 fs::rename(&new_filename, &filename)?;
221 Ok(())
223 }
224}
225
226#[cfg(test)]
227pub mod test {
228 use {
229 super::*,
230 crate::consensus::{
231 BlockhashStatus, Tower,
232 tower1_7_14::{SavedTower1_7_14, Tower1_7_14},
233 },
234 solana_hash::Hash,
235 solana_keypair::Keypair,
236 solana_vote::vote_transaction::VoteTransaction,
237 solana_vote_program::vote_state::{
238 BlockTimestamp, Lockout, MAX_LOCKOUT_HISTORY, Vote, VoteState1_14_11,
239 },
240 tempfile::TempDir,
241 };
242
243 #[test]
244 fn test_tower_migration() {
245 let tower_path = TempDir::new().unwrap();
246 let identity_keypair = Keypair::new();
247 let node_pubkey = identity_keypair.pubkey();
248 let mut vote_state = VoteState1_14_11::default();
249 vote_state
250 .votes
251 .resize(MAX_LOCKOUT_HISTORY, Lockout::default());
252 vote_state.root_slot = Some(1);
253
254 let vote = Vote::new(vec![1, 2, 3, 4], Hash::default());
255 let tower_storage = FileTowerStorage::new(tower_path.path().to_path_buf());
256
257 let old_tower = Tower1_7_14 {
258 node_pubkey,
259 threshold_depth: 10,
260 threshold_size: 0.9,
261 vote_state,
262 last_vote: vote.clone(),
263 last_timestamp: BlockTimestamp::default(),
264 last_vote_tx_blockhash: BlockhashStatus::Uninitialized,
265 stray_restored_slot: Some(2),
266 last_switch_threshold_check: Option::default(),
267 };
268
269 {
270 let saved_tower = SavedTower1_7_14::new(&old_tower, &identity_keypair).unwrap();
271 tower_storage.store_old(&saved_tower).unwrap();
272 }
273
274 let loaded = Tower::restore(&tower_storage, &node_pubkey).unwrap();
275 assert_eq!(loaded.node_pubkey, old_tower.node_pubkey);
276 assert_eq!(loaded.last_vote(), VoteTransaction::from(vote));
277 assert_eq!(loaded.vote_state.root_slot, Some(1));
278 assert_eq!(loaded.stray_restored_slot(), None);
279 }
280}