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