openmina_bootstrap_sandbox/
record.rs1use std::{
2 collections::{BTreeMap, VecDeque},
3 fs::{self, File},
4 io,
5 path::Path,
6};
7
8use binprot::{BinProtRead, BinProtWrite};
9use libp2p::Swarm;
10use mina_p2p_messages::{
11 hash::MinaHash,
12 list::List,
13 rpc::{
14 GetAncestryV2, GetBestTipV2, GetStagedLedgerAuxAndPendingCoinbasesAtHashV2,
15 GetTransitionChainProofV1ForV2, GetTransitionChainV2, WithHashV1,
16 },
17 v2,
18};
19
20use super::{
21 behaviour::Behaviour, bootstrap::Storage, client::Client, snarked_ledger::SnarkedLedger,
22};
23
24pub async fn run(swarm: Swarm<Behaviour>, path_main: &Path, bootstrap: bool) {
25 let mut client = Client::new(swarm);
26
27 fs::create_dir_all(path_main).unwrap();
28
29 let best_tip = client.rpc::<GetBestTipV2>(()).await.unwrap().unwrap();
30
31 let head_height = best_tip
32 .data
33 .header
34 .protocol_state
35 .body
36 .consensus_state
37 .blockchain_length
38 .as_u32();
39
40 log::info!("will record {head_height}");
41 let path = path_main.join(head_height.to_string());
42 fs::create_dir_all(path.join("ledgers")).unwrap();
43
44 let mut file = File::create(path.join("best_tip")).unwrap();
45 Some(best_tip.clone()).binprot_write(&mut file).unwrap();
46
47 let q = best_tip
52 .data
53 .header
54 .protocol_state
55 .body
56 .consensus_state
57 .clone();
58 let hash = MinaHash::try_hash(&best_tip.data.header.protocol_state)
59 .unwrap()
60 .into();
61 let q = WithHashV1 { data: q, hash };
62 let ancestry = client.rpc::<GetAncestryV2>(q).await.unwrap().unwrap();
63
64 let mut file = File::create(path.join("ancestry")).unwrap();
65 Some(ancestry.clone()).binprot_write(&mut file).unwrap();
66
67 let snarked_protocol_state = best_tip.proof.1.header.protocol_state;
68
69 {
70 let staking_epoch_ledger_hash = snarked_protocol_state
71 .body
72 .consensus_state
73 .staking_epoch_data
74 .ledger
75 .hash
76 .clone();
77 let staking_epoch_ledger_hash_str =
78 match serde_json::to_value(&staking_epoch_ledger_hash).unwrap() {
79 serde_json::Value::String(s) => s,
80 _ => panic!(),
81 };
82 let mut epoch_ledger = match File::open(path.join("staking_epoch_ledger.bin")) {
83 Ok(file) => SnarkedLedger::load_bin(file).unwrap(),
84 Err(_) => {
85 match File::open(path.parent().expect("msg").join("staking_epoch_ledger.bin")) {
86 Ok(file) => SnarkedLedger::load_bin(file).unwrap(),
87 Err(_) => SnarkedLedger::empty(),
88 }
89 }
90 };
91 epoch_ledger
92 .sync_new(&mut client, &staking_epoch_ledger_hash)
93 .await;
94 epoch_ledger
95 .store_bin(
96 File::create(path.join("ledgers").join(staking_epoch_ledger_hash_str)).unwrap(),
97 )
98 .unwrap();
99 epoch_ledger
100 .store_bin(File::create(path.join("staking_epoch_ledger.bin")).unwrap())
101 .unwrap();
102 fs::copy(
103 path.join("staking_epoch_ledger.bin"),
104 path.parent()
105 .expect("must have parent")
106 .join("staking_epoch_ledger.bin"),
107 )
108 .unwrap();
109 }
110 let next_epoch_ledger_hash = snarked_protocol_state
111 .body
112 .consensus_state
113 .next_epoch_data
114 .ledger
115 .hash
116 .clone();
117 let next_epoch_ledger_hash_str = match serde_json::to_value(&next_epoch_ledger_hash).unwrap() {
118 serde_json::Value::String(s) => s,
119 _ => panic!(),
120 };
121 let mut epoch_ledger = match File::open(path.join("epoch_ledger.bin")) {
122 Ok(file) => SnarkedLedger::load_bin(file).unwrap(),
123 Err(_) => match File::open(path.parent().expect("msg").join("epoch_ledger.bin")) {
124 Ok(file) => SnarkedLedger::load_bin(file).unwrap(),
125 Err(_) => SnarkedLedger::empty(),
126 },
127 };
128 epoch_ledger
129 .sync_new(&mut client, &next_epoch_ledger_hash)
130 .await;
131 epoch_ledger
132 .store_bin(File::create(path.join("ledgers").join(next_epoch_ledger_hash_str)).unwrap())
133 .unwrap();
134 epoch_ledger
135 .store_bin(File::create(path.join("epoch_ledger.bin")).unwrap())
136 .unwrap();
137 fs::copy(
138 path.join("epoch_ledger.bin"),
139 path.parent()
140 .expect("must have parent")
141 .join("epoch_ledger.bin"),
142 )
143 .unwrap();
144
145 let snarked_ledger_hash = snarked_protocol_state
146 .body
147 .blockchain_state
148 .ledger_proof_statement
149 .target
150 .first_pass_ledger
151 .clone();
152 let snarked_ledger_hash_str = match serde_json::to_value(&snarked_ledger_hash).unwrap() {
153 serde_json::Value::String(s) => s,
154 _ => panic!(),
155 };
156 log::info!("snarked_ledger_hash: {snarked_ledger_hash_str}");
157 let mut snarked_ledger = match File::open(path.join("current_ledger.bin")) {
158 Ok(file) => SnarkedLedger::load_bin(file).unwrap(),
159 Err(_) => match File::open(path.parent().expect("msg").join("current_ledger.bin")) {
160 Ok(file) => SnarkedLedger::load_bin(file).unwrap(),
161 Err(_) => SnarkedLedger::empty(),
162 },
163 };
164 snarked_ledger
165 .sync_new(&mut client, &snarked_ledger_hash)
166 .await;
167 snarked_ledger
168 .store_bin(File::create(path.join("ledgers").join(snarked_ledger_hash_str)).unwrap())
169 .unwrap();
170 snarked_ledger
171 .store_bin(File::create(path.join("current_ledger.bin")).unwrap())
172 .unwrap();
173 fs::copy(
174 path.join("current_ledger.bin"),
175 path.parent()
176 .expect("must have parent")
177 .join("current_ledger.bin"),
178 )
179 .unwrap();
180
181 let expected_hash = snarked_protocol_state
182 .body
183 .blockchain_state
184 .staged_ledger_hash
185 .clone();
186
187 let snarked_block_hash = MinaHash::try_hash(&snarked_protocol_state).unwrap();
188 let snarked_block_hash =
189 v2::StateHash::from(v2::DataHashLibStateHashStableV1(snarked_block_hash.into()));
190 log::info!("downloading staged_ledger_aux and pending_coinbases at {snarked_block_hash}");
191 let info = client
192 .rpc::<GetStagedLedgerAuxAndPendingCoinbasesAtHashV2>(snarked_block_hash.0.clone())
193 .await
194 .unwrap();
195 let mut file = File::create(path.join("staged_ledger_aux")).unwrap();
196 info.binprot_write(&mut file).unwrap();
197
198 let snarked_height = snarked_protocol_state
199 .body
200 .consensus_state
201 .blockchain_length
202 .as_u32();
203 log::info!("will bootstrap: {}..={head_height}", snarked_height);
204
205 let mut blocks = VecDeque::new();
206 blocks.push_back(best_tip.data);
207 download_blocks(
208 &mut client,
209 &mut blocks,
210 &path_main.join("blocks"),
211 head_height,
212 snarked_height,
213 )
214 .await;
215
216 if bootstrap {
217 let mut storage = Storage::new(snarked_ledger.inner, info, expected_hash);
218
219 let mut prev_protocol_state = snarked_protocol_state;
220 while let Some(block) = blocks.pop_back() {
221 storage.apply_block(&block, &prev_protocol_state);
222 prev_protocol_state = block.header.protocol_state.clone();
223 }
224 }
225}
226
227async fn download_blocks(
228 engine: &mut Client,
229 blocks: &mut VecDeque<v2::MinaBlockBlockStableV2>,
230 dir: &Path,
231 head_height: u32,
232 snarked_height: u32,
233) {
234 let create_dir = |dir: &Path| {
235 fs::create_dir_all(dir)
236 .or_else(|e| {
237 if e.kind() == io::ErrorKind::AlreadyExists {
238 Ok(())
239 } else {
240 Err(e)
241 }
242 })
243 .unwrap()
244 };
245 create_dir(dir);
246
247 let mut table = match File::open(dir.join("table.json")) {
248 Ok(f) => serde_json::from_reader(f).unwrap(),
249 Err(_) => BTreeMap::<String, u32>::new(),
250 };
251
252 log::info!("need blocks {}..{head_height}", snarked_height + 1);
253 for i in ((snarked_height + 1)..head_height).rev() {
254 let last_protocol_state = &blocks.back().unwrap().header.protocol_state;
255 let this_hash = &last_protocol_state.previous_state_hash;
256 let this_height = last_protocol_state
257 .body
258 .consensus_state
259 .blockchain_length
260 .as_u32()
261 - 1;
262 let dir = dir.join(this_height.to_string());
263 create_dir(&dir);
264 table.insert(this_hash.to_string(), this_height);
265 let new = if let Ok(mut file) = File::open(dir.join(this_hash.to_string())) {
266 v2::MinaBlockBlockStableV2::binprot_read(&mut file).unwrap()
267 } else {
268 log::info!("downloading block {i}");
269 let new: Vec<_> = engine
270 .rpc::<GetTransitionChainV2>(List::one(this_hash.0.clone()))
271 .await
272 .unwrap()
273 .unwrap()
274 .into_iter()
275 .collect();
276 let mut file = File::create(dir.join(this_hash.to_string())).unwrap();
277 new[0].binprot_write(&mut file).unwrap();
278 if let Ok(new_proof) = engine
279 .rpc::<GetTransitionChainProofV1ForV2>(this_hash.0.clone())
280 .await
281 {
282 let mut file = File::create(dir.join(format!("proof_{this_hash}"))).unwrap();
283 new_proof.binprot_write(&mut file).unwrap();
284 }
285 new[0].clone()
286 };
287 blocks.push_back(new);
288 }
289 let file = File::create(dir.join("table.json")).unwrap();
290 serde_json::to_writer(file, &table).unwrap();
291 log::info!("have blocks {}..{head_height}", snarked_height + 1);
292}