2020-09-22 14:53:52 +02:00
|
|
|
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
|
|
|
// This file is part of OpenEthereum.
|
2016-04-10 15:12:20 +02:00
|
|
|
|
2020-09-22 14:53:52 +02:00
|
|
|
// OpenEthereum is free software: you can redistribute it and/or modify
|
2016-04-10 15:12:20 +02:00
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
|
2020-09-22 14:53:52 +02:00
|
|
|
// OpenEthereum is distributed in the hope that it will be useful,
|
2016-04-10 15:12:20 +02:00
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
|
|
|
// You should have received a copy of the GNU General Public License
|
2020-09-22 14:53:52 +02:00
|
|
|
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
2016-04-10 15:12:20 +02:00
|
|
|
|
|
|
|
//! Parity upgrade logic
|
|
|
|
|
2020-08-05 06:08:03 +02:00
|
|
|
use dir::{default_data_path, helpers::replace_home, home_dir, DatabaseDirectories};
|
2017-10-17 06:41:05 +02:00
|
|
|
use journaldb::Algorithm;
|
2020-08-05 06:08:03 +02:00
|
|
|
use semver::{SemVerError, Version};
|
|
|
|
use std::{
|
|
|
|
collections::*,
|
|
|
|
fs::{self, create_dir_all, File},
|
|
|
|
io,
|
|
|
|
io::{Read, Write},
|
|
|
|
path::{Path, PathBuf},
|
|
|
|
};
|
2016-04-10 15:12:20 +02:00
|
|
|
|
2016-04-10 16:15:40 +02:00
|
|
|
#[derive(Debug)]
|
2016-04-10 15:42:33 +02:00
|
|
|
pub enum Error {
|
2020-08-05 06:08:03 +02:00
|
|
|
CannotCreateConfigPath(io::Error),
|
|
|
|
CannotWriteVersionFile(io::Error),
|
|
|
|
CannotUpdateVersionFile(io::Error),
|
|
|
|
SemVer(SemVerError),
|
2018-04-04 11:54:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<SemVerError> for Error {
|
2020-08-05 06:08:03 +02:00
|
|
|
fn from(err: SemVerError) -> Self {
|
|
|
|
Error::SemVer(err)
|
|
|
|
}
|
2016-04-10 15:42:33 +02:00
|
|
|
}
|
2016-04-10 15:12:20 +02:00
|
|
|
|
|
|
|
const CURRENT_VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
|
|
|
|
|
|
|
#[derive(Hash, PartialEq, Eq)]
|
|
|
|
struct UpgradeKey {
|
2020-08-05 06:08:03 +02:00
|
|
|
pub old_version: Version,
|
|
|
|
pub new_version: Version,
|
2016-04-10 15:12:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type UpgradeList = HashMap<UpgradeKey, fn() -> Result<(), Error>>;
|
|
|
|
|
|
|
|
impl UpgradeKey {
|
2020-08-05 06:08:03 +02:00
|
|
|
// given the following config exist
|
|
|
|
// ver.lock 1.1 (`previous_version`)
|
|
|
|
//
|
|
|
|
// current_version 1.4 (`current_version`)
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//upgrades (set of `UpgradeKey`)
|
|
|
|
// 1.0 -> 1.1 (u1)
|
|
|
|
// 1.1 -> 1.2 (u2)
|
|
|
|
// 1.2 -> 1.3 (u3)
|
|
|
|
// 1.3 -> 1.4 (u4)
|
|
|
|
// 1.4 -> 1.5 (u5)
|
|
|
|
//
|
|
|
|
// then the following upgrades should be applied:
|
|
|
|
// u2, u3, u4
|
|
|
|
fn is_applicable(&self, previous_version: &Version, current_version: &Version) -> bool {
|
|
|
|
self.old_version >= *previous_version && self.new_version <= *current_version
|
|
|
|
}
|
2016-04-10 15:12:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// dummy upgrade (remove when the first one is in)
|
|
|
|
fn dummy_upgrade() -> Result<(), Error> {
|
2020-08-05 06:08:03 +02:00
|
|
|
Ok(())
|
2016-04-10 15:12:20 +02:00
|
|
|
}
|
|
|
|
|
2020-08-05 06:08:03 +02:00
|
|
|
fn push_upgrades(upgrades: &mut UpgradeList) {
|
|
|
|
// dummy upgrade (remove when the first one is in)
|
|
|
|
upgrades.insert(
|
|
|
|
UpgradeKey {
|
|
|
|
old_version: Version::new(0, 9, 0),
|
|
|
|
new_version: Version::new(1, 0, 0),
|
|
|
|
},
|
|
|
|
dummy_upgrade,
|
|
|
|
);
|
2016-04-10 15:12:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fn upgrade_from_version(previous_version: &Version) -> Result<usize, Error> {
|
2020-08-05 06:08:03 +02:00
|
|
|
let mut upgrades = HashMap::new();
|
|
|
|
push_upgrades(&mut upgrades);
|
|
|
|
|
|
|
|
let current_version = Version::parse(CURRENT_VERSION)?;
|
|
|
|
|
|
|
|
let mut count = 0;
|
|
|
|
for upgrade_key in upgrades.keys() {
|
|
|
|
if upgrade_key.is_applicable(previous_version, ¤t_version) {
|
|
|
|
let upgrade_script = upgrades[upgrade_key];
|
|
|
|
upgrade_script()?;
|
|
|
|
count += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(count)
|
2016-04-10 15:12:20 +02:00
|
|
|
}
|
2016-04-10 15:42:33 +02:00
|
|
|
|
2018-08-03 09:58:59 +02:00
|
|
|
fn with_locked_version<F>(db_path: &str, script: F) -> Result<usize, Error>
|
2020-08-05 06:08:03 +02:00
|
|
|
where
|
|
|
|
F: Fn(&Version) -> Result<usize, Error>,
|
2016-04-10 15:42:33 +02:00
|
|
|
{
|
2020-08-05 06:08:03 +02:00
|
|
|
let mut path = PathBuf::from(db_path);
|
|
|
|
create_dir_all(&path).map_err(Error::CannotCreateConfigPath)?;
|
|
|
|
path.push("ver.lock");
|
|
|
|
|
|
|
|
let version = File::open(&path)
|
|
|
|
.ok()
|
|
|
|
.and_then(|ref mut file| {
|
|
|
|
let mut version_string = String::new();
|
|
|
|
file.read_to_string(&mut version_string)
|
|
|
|
.ok()
|
|
|
|
.and_then(|_| Version::parse(&version_string).ok())
|
|
|
|
})
|
|
|
|
.unwrap_or(Version::new(0, 9, 0));
|
|
|
|
|
|
|
|
let mut lock = File::create(&path).map_err(Error::CannotWriteVersionFile)?;
|
|
|
|
let result = script(&version);
|
|
|
|
|
|
|
|
let written_version = Version::parse(CURRENT_VERSION)?;
|
|
|
|
lock.write_all(written_version.to_string().as_bytes())
|
|
|
|
.map_err(Error::CannotUpdateVersionFile)?;
|
|
|
|
result
|
2016-04-10 15:42:33 +02:00
|
|
|
}
|
|
|
|
|
2018-08-03 09:58:59 +02:00
|
|
|
pub fn upgrade(db_path: &str) -> Result<usize, Error> {
|
2020-08-05 06:08:03 +02:00
|
|
|
with_locked_version(db_path, |ver| upgrade_from_version(ver))
|
2016-04-10 15:42:33 +02:00
|
|
|
}
|
2016-12-12 16:51:07 +01:00
|
|
|
|
|
|
|
fn file_exists(path: &Path) -> bool {
|
2020-08-05 06:08:03 +02:00
|
|
|
match fs::metadata(&path) {
|
|
|
|
Err(ref e) if e.kind() == io::ErrorKind::NotFound => false,
|
|
|
|
_ => true,
|
|
|
|
}
|
2016-12-12 16:51:07 +01:00
|
|
|
}
|
|
|
|
|
2019-02-14 15:54:44 +01:00
|
|
|
#[cfg(any(test, feature = "accounts"))]
|
2016-12-12 16:51:07 +01:00
|
|
|
pub fn upgrade_key_location(from: &PathBuf, to: &PathBuf) {
|
2020-08-05 06:08:03 +02:00
|
|
|
match fs::create_dir_all(&to).and_then(|()| fs::read_dir(from)) {
|
|
|
|
Ok(entries) => {
|
|
|
|
let files: Vec<_> = entries
|
|
|
|
.filter_map(|f| {
|
|
|
|
f.ok().and_then(|f| {
|
|
|
|
if f.file_type().ok().map_or(false, |f| f.is_file()) {
|
|
|
|
f.file_name().to_str().map(|s| s.to_owned())
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
let mut num: usize = 0;
|
|
|
|
for name in files {
|
|
|
|
let mut from = from.clone();
|
|
|
|
from.push(&name);
|
|
|
|
let mut to = to.clone();
|
|
|
|
to.push(&name);
|
|
|
|
if !file_exists(&to) {
|
|
|
|
if let Err(e) = fs::rename(&from, &to) {
|
|
|
|
debug!("Error upgrading key {:?}: {:?}", from, e);
|
|
|
|
} else {
|
|
|
|
num += 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
debug!("Skipped upgrading key {:?}", from);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if num > 0 {
|
|
|
|
info!(
|
|
|
|
"Moved {} keys from {} to {}",
|
|
|
|
num,
|
|
|
|
from.to_string_lossy(),
|
|
|
|
to.to_string_lossy()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
debug!("Error moving keys from {:?} to {:?}: {:?}", from, to, e);
|
|
|
|
}
|
|
|
|
}
|
2016-12-12 16:51:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn upgrade_dir_location(source: &PathBuf, dest: &PathBuf) {
|
2020-08-05 06:08:03 +02:00
|
|
|
if file_exists(&source) {
|
|
|
|
if !file_exists(&dest) {
|
|
|
|
let mut parent = dest.clone();
|
|
|
|
parent.pop();
|
|
|
|
if let Err(e) = fs::create_dir_all(&parent).and_then(|()| fs::rename(&source, &dest)) {
|
|
|
|
debug!("Skipped path {:?} -> {:?} :{:?}", source, dest, e);
|
|
|
|
} else {
|
|
|
|
info!(
|
|
|
|
"Moved {} to {}",
|
|
|
|
source.to_string_lossy(),
|
|
|
|
dest.to_string_lossy()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
debug!(
|
|
|
|
"Skipped upgrading directory {:?}, Destination already exists at {:?}",
|
|
|
|
source, dest
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2016-12-12 16:51:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn upgrade_user_defaults(dirs: &DatabaseDirectories) {
|
2020-08-05 06:08:03 +02:00
|
|
|
let source = dirs.legacy_user_defaults_path();
|
|
|
|
let dest = dirs.user_defaults_path();
|
|
|
|
if file_exists(&source) {
|
|
|
|
if !file_exists(&dest) {
|
|
|
|
if let Err(e) = fs::rename(&source, &dest) {
|
|
|
|
debug!("Skipped upgrading user defaults {:?}:{:?}", dest, e);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
debug!(
|
|
|
|
"Skipped upgrading user defaults {:?}, File exists at {:?}",
|
|
|
|
source, dest
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2016-12-12 16:51:07 +01:00
|
|
|
}
|
|
|
|
|
2016-12-15 21:56:45 +01:00
|
|
|
pub fn upgrade_data_paths(base_path: &str, dirs: &DatabaseDirectories, pruning: Algorithm) {
|
2020-08-05 06:08:03 +02:00
|
|
|
if home_dir().is_none() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let legacy_root_path = replace_home("", "$HOME/.parity");
|
|
|
|
let default_path = default_data_path();
|
|
|
|
if legacy_root_path != base_path && base_path == default_path {
|
|
|
|
upgrade_dir_location(&PathBuf::from(legacy_root_path), &PathBuf::from(&base_path));
|
|
|
|
}
|
|
|
|
upgrade_dir_location(&dirs.legacy_version_path(pruning), &dirs.db_path(pruning));
|
|
|
|
upgrade_dir_location(&dirs.legacy_snapshot_path(), &dirs.snapshot_path());
|
|
|
|
upgrade_dir_location(&dirs.legacy_network_path(), &dirs.network_path());
|
|
|
|
upgrade_user_defaults(&dirs);
|
2016-12-12 16:51:07 +01:00
|
|
|
}
|