From c607080b8d53543cd8d822782c8275299276f848 Mon Sep 17 00:00:00 2001 From: James Hinshelwood Date: Wed, 18 Dec 2024 09:24:03 +0000 Subject: [PATCH] Write database version file to disk (#2034) This will let us make breaking changes to the database format in the future, without having to write ugly code which has to guess what version of data it is reading. --- zilliqa/src/db.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/zilliqa/src/db.rs b/zilliqa/src/db.rs index 3bab5f1be..e366075b4 100644 --- a/zilliqa/src/db.rs +++ b/zilliqa/src/db.rs @@ -1,7 +1,7 @@ use std::{ collections::BTreeMap, fmt::Debug, - fs::{self, File}, + fs::{self, File, OpenOptions}, io::{BufReader, BufWriter, Read, Write}, ops::Range, path::{Path, PathBuf}, @@ -179,6 +179,11 @@ enum BlockFilter { const CHECKPOINT_HEADER_BYTES: [u8; 8] = *b"ZILCHKPT"; +/// Version string that is written to disk along with the persisted database. This should be bumped whenever we make a +/// backwards incompatible change to our database format. This should be done rarely, since it forces all node +/// operators to re-sync. +const CURRENT_DB_VERSION: &str = "1"; + #[derive(Debug)] pub struct Db { db: Arc>, @@ -195,6 +200,22 @@ impl Db { Some(path) => { let path = path.as_ref().join(shard_id.to_string()); fs::create_dir_all(&path).context(format!("Unable to create {path:?}"))?; + + let mut version_file = OpenOptions::new() + .create(true) + .truncate(false) + .read(true) + .write(true) + .open(path.join("version"))?; + let mut version = String::new(); + version_file.read_to_string(&mut version)?; + + if !version.is_empty() && version != CURRENT_DB_VERSION { + return Err(anyhow!("data is incompatible with this version - please delete the data and re-sync")); + } + + version_file.write_all(CURRENT_DB_VERSION.as_bytes())?; + let db_path = path.join("db.sqlite3"); ( Connection::open(&db_path)