diff --git a/Cargo.lock b/Cargo.lock
index da52e9b5f..ca0f20449 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -22,6 +22,7 @@ dependencies = [
"rpassword 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
]
diff --git a/Cargo.toml b/Cargo.toml
index 3fe923db7..2f0d0789c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -29,7 +29,7 @@ ethminer = { path = "miner" }
ethcore-devtools = { path = "devtools" }
ethcore-rpc = { path = "rpc", optional = true }
ethcore-webapp = { path = "webapp", optional = true }
-
+semver = "0.2"
[dependencies.hyper]
version = "0.8"
diff --git a/parity/main.rs b/parity/main.rs
index e2e4c9c37..87e92cde3 100644
--- a/parity/main.rs
+++ b/parity/main.rs
@@ -36,6 +36,7 @@ extern crate daemonize;
extern crate time;
extern crate number_prefix;
extern crate rpassword;
+extern crate semver;
// for price_info.rs
#[macro_use] extern crate hyper;
@@ -71,6 +72,7 @@ use rpc::Server as RpcServer;
use webapp::Listening as WebappServer;
mod price_info;
+mod upgrade;
fn die_with_message(msg: &str) -> ! {
println!("ERROR: {}", msg);
@@ -785,6 +787,17 @@ fn die_with_io_error(e: std::io::Error) -> ! {
}
fn main() {
+ match ::upgrade::upgrade() {
+ Ok(upgrades_applied) => {
+ if upgrades_applied > 0 {
+ println!("Executed {} upgrade scripts - ok", upgrades_applied);
+ }
+ },
+ Err(e) => {
+ die!("Error upgrading parity data: {:?}", e);
+ }
+ }
+
Configuration::parse().execute();
}
diff --git a/parity/upgrade.rs b/parity/upgrade.rs
new file mode 100644
index 000000000..c9c98e174
--- /dev/null
+++ b/parity/upgrade.rs
@@ -0,0 +1,126 @@
+// Copyright 2015, 2016 Ethcore (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// 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.
+
+// Parity is distributed in the hope that it will be useful,
+// 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
+// along with Parity. If not, see .
+
+//! Parity upgrade logic
+
+use semver::Version;
+use std::collections::*;
+use std::fs::File;
+use std::env;
+use std::io::{Read, Write};
+
+#[derive(Debug)]
+pub enum Error {
+ CannotLockVersionFile,
+ CannotUpdateVersionFile,
+}
+
+const CURRENT_VERSION: &'static str = env!("CARGO_PKG_VERSION");
+
+#[derive(Hash, PartialEq, Eq)]
+struct UpgradeKey {
+ pub old_version: Version,
+ pub new_version: Version,
+}
+
+type UpgradeList = HashMap Result<(), Error>>;
+
+impl UpgradeKey {
+ // 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
+ }
+}
+
+// dummy upgrade (remove when the first one is in)
+fn dummy_upgrade() -> Result<(), Error> {
+ println!("Adding ver.lock");
+ Ok(())
+}
+
+fn push_updrades(upgrades: &mut UpgradeList)
+{
+ // dummy upgrade (remove when the first one is in)
+ upgrades.insert(
+ UpgradeKey { old_version: Version::parse("0.9.0").unwrap(), new_version: Version::parse("1.0.0").unwrap() },
+ dummy_upgrade);
+}
+
+fn upgrade_from_version(previous_version: &Version) -> Result {
+ let mut upgrades = HashMap::new();
+ push_updrades(&mut upgrades);
+
+ let current_version = Version::parse(CURRENT_VERSION).unwrap();
+
+ 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];
+ try!(upgrade_script());
+ count = count + 1;
+ }
+ }
+ Ok(count)
+}
+
+fn with_locked_version(script: F) -> Result
+ where F: Fn(&Version) -> Result
+{
+ let mut path = env::home_dir().expect("Applications should have a home dir");
+ path.push(".parity");
+ 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_else(|| Version::parse("0.9.0").unwrap());
+
+ let script_result = {
+ let mut lock = try!(File::create(&path).map_err(|_| Error::CannotLockVersionFile));
+ let result = script(&version);
+
+ let written_version = Version::parse(CURRENT_VERSION).unwrap();
+ try!(lock.write_all(written_version.to_string().as_bytes()).map_err(|_| Error::CannotUpdateVersionFile));
+ result
+ };
+
+ script_result
+}
+
+pub fn upgrade() -> Result {
+ with_locked_version(|ver| {
+ upgrade_from_version(ver)
+ })
+}