1use crate::Config;
8use crate::blocks::TipsetKey;
9use crate::db::CAR_DB_DIR_NAME;
10use crate::db::db_engine::Db;
11use crate::db::migration::migration_map::MigrationOperationExt as _;
12use crate::db::migration::v0_26_0::paritydb_0_25_3::{DbColumn, ParityDb};
13use crate::rpc::eth::types::EthHash;
14use crate::utils::multihash::prelude::*;
15use anyhow::Context;
16use cid::Cid;
17use fs_extra::dir::CopyOptions;
18use fvm_ipld_encoding::DAG_CBOR;
19use semver::Version;
20use std::path::{Path, PathBuf};
21use strum::IntoEnumIterator;
22use tracing::info;
23
24use super::migration_map::MigrationOperation;
25
26pub(super) struct Migration0_25_3_0_26_0 {
27 from: Version,
28 to: Version,
29}
30
31impl MigrationOperation for Migration0_25_3_0_26_0 {
33 fn new(from: Version, to: Version) -> Self
34 where
35 Self: Sized,
36 {
37 Self { from, to }
38 }
39
40 fn from(&self) -> &Version {
41 &self.from
42 }
43
44 fn to(&self) -> &Version {
45 &self.to
46 }
47
48 fn migrate_core(&self, chain_data_path: &Path, _: &Config) -> anyhow::Result<PathBuf> {
49 let old_db = self.old_db_path(chain_data_path);
50 let temp_db = self.temporary_db_path(chain_data_path);
51
52 let old_car_db_path = old_db.join(CAR_DB_DIR_NAME);
53 let temp_car_db_path = temp_db.join(CAR_DB_DIR_NAME);
54
55 if old_car_db_path.is_dir() {
58 info!(
59 "Copying snapshot from {} to {}",
60 old_db.display(),
61 temp_db.display()
62 );
63
64 fs_extra::copy_items(
65 &[old_car_db_path.as_path()],
66 temp_car_db_path,
67 &CopyOptions::default().copy_inside(true),
68 )?;
69 }
70
71 let db = ParityDb::open(old_db)?;
72
73 let new_db = paritydb_0_26_0::ParityDb::open(&temp_db)?;
75
76 for col in DbColumn::iter() {
77 info!("Migrating column {}", col);
78 let mut res = anyhow::Ok(());
79 match col {
80 DbColumn::GraphDagCborBlake2b256 | DbColumn::PersistentGraph => {
81 db.db.iter_column_while(col as u8, |val| {
82 let hash = MultihashCode::Blake2b256.digest(&val.value);
83 let cid = Cid::new_v1(DAG_CBOR, hash);
84 res = new_db
85 .db
86 .commit_changes([Db::set_operation(
87 col as u8,
88 cid.to_bytes(),
89 val.value,
90 )])
91 .context("failed to commit");
92
93 if res.is_err() {
94 return false;
95 }
96
97 true
98 })?;
99 res?;
100 }
101 DbColumn::EthMappings => {
102 db.db.iter_column_while(col as u8, |val| {
103 let tsk: Result<TipsetKey, fvm_ipld_encoding::Error> =
104 fvm_ipld_encoding::from_slice(&val.value);
105 if tsk.is_err() {
106 res = Err(tsk.context("serde error").unwrap_err());
107 return false;
108 }
109 let cid = tsk.unwrap().cid();
110
111 if cid.is_err() {
112 res = Err(cid.context("serde error").unwrap_err());
113 return false;
114 }
115
116 let hash: EthHash = cid.unwrap().into();
117 res = new_db
118 .db
119 .commit_changes([Db::set_operation(
120 col as u8,
121 hash.0.as_bytes().to_vec(),
122 val.value,
123 )])
124 .context("failed to commit");
125
126 if res.is_err() {
127 return false;
128 }
129
130 true
131 })?;
132 res?;
133 }
134 _ => {
135 let mut iter = db.db.iter(col as u8)?;
136 while let Some((key, value)) = iter.next()? {
137 new_db
138 .db
139 .commit_changes([Db::set_operation(col as u8, key, value)])
140 .context("failed to commit")?;
141 }
142 }
143 }
144 }
145
146 drop(new_db);
147
148 Ok(temp_db)
149 }
150}
151
152mod paritydb_0_25_3 {
154 use parity_db::{CompressionType, Db, Options};
155 use std::path::PathBuf;
156 use strum::{Display, EnumIter, IntoEnumIterator};
157
158 #[derive(Copy, Clone, Debug, PartialEq, EnumIter, Display)]
159 #[repr(u8)]
160 pub(super) enum DbColumn {
161 GraphDagCborBlake2b256,
162 GraphFull,
163 Settings,
164 EthMappings,
165 PersistentGraph,
166 }
167
168 impl DbColumn {
169 fn create_column_options(compression: CompressionType) -> Vec<parity_db::ColumnOptions> {
170 DbColumn::iter()
171 .map(|col| {
172 match col {
173 DbColumn::GraphDagCborBlake2b256 | DbColumn::PersistentGraph => {
174 parity_db::ColumnOptions {
175 preimage: true,
176 compression,
177 ..Default::default()
178 }
179 }
180 DbColumn::GraphFull => parity_db::ColumnOptions {
181 preimage: true,
182 btree_index: true,
184 compression,
185 ..Default::default()
186 },
187 DbColumn::Settings => {
188 parity_db::ColumnOptions {
189 preimage: false,
192 btree_index: true,
194 compression,
195 ..Default::default()
196 }
197 }
198 DbColumn::EthMappings => parity_db::ColumnOptions {
199 preimage: false,
200 btree_index: false,
201 compression,
202 ..Default::default()
203 },
204 }
205 })
206 .collect()
207 }
208 }
209
210 pub(super) struct ParityDb {
211 pub db: parity_db::Db,
212 }
213
214 impl ParityDb {
215 pub(super) fn to_options(path: PathBuf) -> Options {
216 Options {
217 path,
218 sync_wal: true,
219 sync_data: true,
220 stats: false,
221 salt: None,
222 columns: DbColumn::create_column_options(CompressionType::Lz4),
223 compression_threshold: [(0, 128)].into_iter().collect(),
224 }
225 }
226
227 pub(super) fn open(path: impl Into<PathBuf>) -> anyhow::Result<Self> {
228 let opts = Self::to_options(path.into());
229 Ok(Self {
230 db: Db::open_or_create(&opts)?,
231 })
232 }
233 }
234}
235
236mod paritydb_0_26_0 {
238 use parity_db::{CompressionType, Db, Options};
239 use std::path::PathBuf;
240 use strum::{Display, EnumIter, IntoEnumIterator};
241
242 #[derive(Copy, Clone, Debug, PartialEq, EnumIter, Display)]
243 #[repr(u8)]
244 pub(super) enum DbColumn {
245 GraphDagCborBlake2b256,
246 GraphFull,
247 Settings,
248 EthMappings,
249 PersistentGraph,
250 Indices,
251 }
252
253 impl DbColumn {
254 fn create_column_options(compression: CompressionType) -> Vec<parity_db::ColumnOptions> {
255 DbColumn::iter()
256 .map(|col| {
257 match col {
258 DbColumn::GraphDagCborBlake2b256 | DbColumn::PersistentGraph => {
259 parity_db::ColumnOptions {
260 preimage: true,
261 compression,
262 ..Default::default()
263 }
264 }
265 DbColumn::GraphFull => parity_db::ColumnOptions {
266 preimage: true,
267 btree_index: true,
269 compression,
270 ..Default::default()
271 },
272 DbColumn::Settings => {
273 parity_db::ColumnOptions {
274 preimage: false,
277 btree_index: true,
279 compression,
280 ..Default::default()
281 }
282 }
283 DbColumn::EthMappings => parity_db::ColumnOptions {
284 preimage: false,
285 btree_index: false,
286 compression,
287 ..Default::default()
288 },
289 DbColumn::Indices => parity_db::ColumnOptions {
290 preimage: false,
291 btree_index: false,
292 compression,
293 ..Default::default()
294 },
295 }
296 })
297 .collect()
298 }
299 }
300
301 pub(super) struct ParityDb {
302 pub db: parity_db::Db,
303 }
304
305 impl ParityDb {
306 pub(super) fn to_options(path: PathBuf) -> Options {
307 Options {
308 path,
309 sync_wal: true,
310 sync_data: true,
311 stats: false,
312 salt: None,
313 columns: DbColumn::create_column_options(CompressionType::Lz4),
314 compression_threshold: [(0, 128)].into_iter().collect(),
315 }
316 }
317
318 pub(super) fn open(path: impl Into<PathBuf>) -> anyhow::Result<Self> {
319 let opts = Self::to_options(path.into());
320 Ok(Self {
321 db: Db::open_or_create(&opts)?,
322 })
323 }
324 }
325}