Backport core PRs to stable (#7892)
* update back-references more aggressively after answering from cache (#7578) * Flush keyfiles. Resolves #7632 (#7868) * Fix wallet import (#7873) * rpc: generate new account id for imported wallets * ethstore: handle duplicate wallet filenames * ethstore: simplify deduplication of wallet file names * ethstore: do not dedup wallet filenames on update * ethstore: fix minor grumbles * parity-version pr reopen (#7136) * parity-version module split from util removed unused util deps and features trigger buildbot again only kvdb links rocksdb snappy linker issues * rm snappy * fixed old version imports * Move updater metadata to Cargo.toml of parity-version. (#7832) * Update version. * Bump parity version. * Fix version. * Fix compilation.
This commit is contained in:
parent
5e2258c6de
commit
590362b23c
@ -1,6 +1,7 @@
|
||||
[package]
|
||||
description = "Parity Ethereum client"
|
||||
name = "parity"
|
||||
# NOTE Make sure to update util/version/Cargo.toml as well
|
||||
version = "1.8.10"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
@ -60,6 +61,7 @@ parity-reactor = { path = "util/reactor" }
|
||||
parity-rpc = { path = "rpc" }
|
||||
parity-rpc-client = { path = "rpc_client" }
|
||||
parity-updater = { path = "updater" }
|
||||
parity-version = { path = "util/version" }
|
||||
parity-whisper = { path = "whisper" }
|
||||
path = { path = "util/path" }
|
||||
panic_hook = { path = "panic_hook" }
|
||||
|
@ -37,6 +37,7 @@ parity-hash-fetch = { path = "../hash-fetch" }
|
||||
parity-reactor = { path = "../util/reactor" }
|
||||
parity-ui = { path = "./ui" }
|
||||
hash = { path = "../util/hash" }
|
||||
parity-version = { path = "../util/version" }
|
||||
|
||||
clippy = { version = "0.0.103", optional = true}
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
use hyper::{self, mime, header};
|
||||
use hyper::StatusCode;
|
||||
|
||||
use util::version;
|
||||
use parity_version::version;
|
||||
|
||||
use handlers::add_security_headers;
|
||||
use Embeddable;
|
||||
|
@ -44,6 +44,7 @@ extern crate parity_dapps_glue as parity_dapps;
|
||||
extern crate parity_hash_fetch as hash_fetch;
|
||||
extern crate parity_ui;
|
||||
extern crate hash;
|
||||
extern crate parity_version;
|
||||
|
||||
#[macro_use]
|
||||
extern crate futures;
|
||||
|
@ -74,7 +74,6 @@ vm = { path = "vm" }
|
||||
wasm = { path = "wasm" }
|
||||
hash = { path = "../util/hash" }
|
||||
triehash = { path = "../util/triehash" }
|
||||
semantic_version = { path = "../util/semantic_version" }
|
||||
unexpected = { path = "../util/unexpected" }
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -90,7 +90,14 @@ impl Pending {
|
||||
match self.requests[idx].respond_local(cache) {
|
||||
Some(response) => {
|
||||
self.requests.supply_response_unchecked(&response);
|
||||
|
||||
// update header and back-references after each from-cache
|
||||
// response to ensure that the requests are left in a consistent
|
||||
// state and increase the likelihood of being able to answer
|
||||
// the next request from cache.
|
||||
self.update_header_refs(idx, &response);
|
||||
self.fill_unanswered();
|
||||
|
||||
self.responses.push(response);
|
||||
}
|
||||
None => break,
|
||||
|
@ -257,9 +257,9 @@ impl AccountProvider {
|
||||
Ok(Address::from(account.address).into())
|
||||
}
|
||||
|
||||
/// Import a new presale wallet.
|
||||
pub fn import_wallet(&self, json: &[u8], password: &str) -> Result<Address, Error> {
|
||||
let account = self.sstore.import_wallet(SecretVaultRef::Root, json, password)?;
|
||||
/// Import a new wallet.
|
||||
pub fn import_wallet(&self, json: &[u8], password: &str, gen_id: bool) -> Result<Address, Error> {
|
||||
let account = self.sstore.import_wallet(SecretVaultRef::Root, json, password, gen_id)?;
|
||||
if self.blacklisted_accounts.contains(&account.address) {
|
||||
self.sstore.remove_account(&account, password)?;
|
||||
return Err(SSError::InvalidAccount.into());
|
||||
|
@ -41,7 +41,6 @@ use itertools::{self, Itertools};
|
||||
use rlp::{UntrustedRlp, encode};
|
||||
use bigint::prelude::{U256, U128};
|
||||
use bigint::hash::{H256, H520};
|
||||
use semantic_version::SemanticVersion;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use unexpected::{Mismatch, OutOfBounds};
|
||||
use util::Address;
|
||||
@ -483,8 +482,6 @@ impl IoHandler<()> for TransitionHandler {
|
||||
impl Engine<EthereumMachine> for AuthorityRound {
|
||||
fn name(&self) -> &str { "AuthorityRound" }
|
||||
|
||||
fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) }
|
||||
|
||||
fn machine(&self) -> &EthereumMachine { &self.machine }
|
||||
|
||||
/// Two fields - consensus step and the corresponding proposer signature.
|
||||
@ -919,7 +916,6 @@ mod tests {
|
||||
fn has_valid_metadata() {
|
||||
let engine = Spec::new_test_round().engine;
|
||||
assert!(!engine.name().is_empty());
|
||||
assert!(engine.version().major >= 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -29,7 +29,6 @@ use ethjson;
|
||||
use header::Header;
|
||||
use client::EngineClient;
|
||||
use machine::{AuxiliaryData, Call, EthereumMachine};
|
||||
use semantic_version::SemanticVersion;
|
||||
use super::signer::EngineSigner;
|
||||
use super::validator_set::{ValidatorSet, SimpleList, new_validator_set};
|
||||
|
||||
@ -95,7 +94,6 @@ impl BasicAuthority {
|
||||
|
||||
impl Engine<EthereumMachine> for BasicAuthority {
|
||||
fn name(&self) -> &str { "BasicAuthority" }
|
||||
fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) }
|
||||
|
||||
fn machine(&self) -> &EthereumMachine { &self.machine }
|
||||
|
||||
@ -218,7 +216,6 @@ mod tests {
|
||||
fn has_valid_metadata() {
|
||||
let engine = new_test_authority().engine;
|
||||
assert!(!engine.name().is_empty());
|
||||
assert!(engine.version().major >= 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -54,7 +54,6 @@ use ethkey::Signature;
|
||||
use parity_machine::{Machine, LocalizedMachine as Localized};
|
||||
use bigint::prelude::U256;
|
||||
use bigint::hash::H256;
|
||||
use semantic_version::SemanticVersion;
|
||||
use util::*;
|
||||
use unexpected::{Mismatch, OutOfBounds};
|
||||
use bytes::Bytes;
|
||||
@ -178,8 +177,6 @@ pub enum EpochChange<M: Machine> {
|
||||
pub trait Engine<M: Machine>: Sync + Send {
|
||||
/// The name of this engine.
|
||||
fn name(&self) -> &str;
|
||||
/// The version of this engine. Should be of the form
|
||||
fn version(&self) -> SemanticVersion { SemanticVersion::new(0, 0, 0) }
|
||||
|
||||
/// Get access to the underlying state machine.
|
||||
// TODO: decouple.
|
||||
|
@ -50,7 +50,6 @@ use super::transition::TransitionHandler;
|
||||
use super::vote_collector::VoteCollector;
|
||||
use self::message::*;
|
||||
use self::params::TendermintParams;
|
||||
use semantic_version::SemanticVersion;
|
||||
use machine::{AuxiliaryData, EthereumMachine};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||
@ -443,8 +442,6 @@ impl Tendermint {
|
||||
impl Engine<EthereumMachine> for Tendermint {
|
||||
fn name(&self) -> &str { "Tendermint" }
|
||||
|
||||
fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) }
|
||||
|
||||
/// (consensus view, proposal signature, authority signatures)
|
||||
fn seal_fields(&self) -> usize { 3 }
|
||||
|
||||
@ -857,7 +854,6 @@ mod tests {
|
||||
fn has_valid_metadata() {
|
||||
let engine = Spec::new_test_tendermint().engine;
|
||||
assert!(!engine.name().is_empty());
|
||||
assert!(engine.version().major >= 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -31,7 +31,6 @@ use engines::{self, Engine};
|
||||
use ethjson;
|
||||
use rlp::{self, UntrustedRlp};
|
||||
use machine::EthereumMachine;
|
||||
use semantic_version::SemanticVersion;
|
||||
|
||||
/// Number of blocks in an ethash snapshot.
|
||||
// make dependent on difficulty incrment divisor?
|
||||
@ -169,7 +168,6 @@ impl engines::EpochVerifier<EthereumMachine> for Arc<Ethash> {
|
||||
|
||||
impl Engine<EthereumMachine> for Arc<Ethash> {
|
||||
fn name(&self) -> &str { "Ethash" }
|
||||
fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) }
|
||||
fn machine(&self) -> &EthereumMachine { &self.machine }
|
||||
|
||||
// Two fields - nonce and mix.
|
||||
@ -577,7 +575,6 @@ mod tests {
|
||||
fn has_valid_metadata() {
|
||||
let engine = test_spec().engine;
|
||||
assert!(!engine.name().is_empty());
|
||||
assert!(engine.version().major >= 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -30,15 +30,6 @@ pub use self::denominations::*;
|
||||
use machine::EthereumMachine;
|
||||
use super::spec::*;
|
||||
|
||||
/// Most recent fork block that we support on Mainnet.
|
||||
pub const FORK_SUPPORTED_FOUNDATION: u64 = 4370000;
|
||||
|
||||
/// Most recent fork block that we support on Ropsten.
|
||||
pub const FORK_SUPPORTED_ROPSTEN: u64 = 10;
|
||||
|
||||
/// Most recent fork block that we support on Kovan.
|
||||
pub const FORK_SUPPORTED_KOVAN: u64 = 0;
|
||||
|
||||
fn load<'a, T: Into<Option<SpecParams<'a>>>>(params: T, b: &[u8]) -> Spec {
|
||||
match params.into() {
|
||||
Some(params) => Spec::load(params, b),
|
||||
|
@ -110,7 +110,6 @@ extern crate memorydb;
|
||||
extern crate patricia_trie as trie;
|
||||
extern crate triehash;
|
||||
extern crate ansi_term;
|
||||
extern crate semantic_version;
|
||||
extern crate unexpected;
|
||||
extern crate kvdb;
|
||||
extern crate kvdb_rocksdb;
|
||||
|
@ -35,6 +35,15 @@ use std::cell::{RefCell, Cell};
|
||||
|
||||
const STORAGE_CACHE_ITEMS: usize = 8192;
|
||||
|
||||
/// Boolean type for clean/dirty status.
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum Filth {
|
||||
/// Data has not been changed.
|
||||
Clean,
|
||||
/// Data has been changed.
|
||||
Dirty,
|
||||
}
|
||||
|
||||
/// Single account in the system.
|
||||
/// Keeps track of changes to the code and storage.
|
||||
/// The changes are applied in `commit_storage` and `commit_code`
|
||||
|
@ -15,6 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{fs, io};
|
||||
use std::io::Write;
|
||||
use std::path::{PathBuf, Path};
|
||||
use std::collections::HashMap;
|
||||
use time;
|
||||
@ -152,31 +153,39 @@ impl<T> DiskDirectory<T> where T: KeyFileManager {
|
||||
)
|
||||
}
|
||||
|
||||
/// insert account with given file name
|
||||
pub fn insert_with_filename(&self, account: SafeAccount, filename: String) -> Result<SafeAccount, Error> {
|
||||
|
||||
/// insert account with given filename. if the filename is a duplicate of any stored account and dedup is set to
|
||||
/// true, a random suffix is appended to the filename.
|
||||
pub fn insert_with_filename(&self, account: SafeAccount, mut filename: String, dedup: bool) -> Result<SafeAccount, Error> {
|
||||
// path to keyfile
|
||||
let mut keyfile_path = self.path.join(filename.as_str());
|
||||
|
||||
// check for duplicate filename and append random suffix
|
||||
if dedup && keyfile_path.exists() {
|
||||
let suffix = ::random::random_string(4);
|
||||
filename.push_str(&format!("-{}", suffix));
|
||||
keyfile_path.set_file_name(&filename);
|
||||
}
|
||||
|
||||
// update account filename
|
||||
let original_account = account.clone();
|
||||
let mut account = account;
|
||||
account.filename = Some(filename.clone());
|
||||
account.filename = Some(filename);
|
||||
|
||||
{
|
||||
// Path to keyfile
|
||||
let mut keyfile_path = self.path.clone();
|
||||
keyfile_path.push(filename.as_str());
|
||||
|
||||
// save the file
|
||||
let mut file = fs::File::create(&keyfile_path)?;
|
||||
if let Err(err) = self.key_manager.write(original_account, &mut file).map_err(|e| Error::Custom(format!("{:?}", e))) {
|
||||
drop(file);
|
||||
fs::remove_file(keyfile_path).expect("Expected to remove recently created file");
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
// write key content
|
||||
self.key_manager.write(original_account, &mut file).map_err(|e| Error::Custom(format!("{:?}", e)))?;
|
||||
|
||||
file.flush()?;
|
||||
|
||||
if let Err(_) = restrict_permissions_to_owner(keyfile_path.as_path()) {
|
||||
drop(file);
|
||||
fs::remove_file(keyfile_path).expect("Expected to remove recently created file");
|
||||
return Err(Error::Io(io::Error::last_os_error()));
|
||||
}
|
||||
|
||||
file.sync_all()?;
|
||||
}
|
||||
|
||||
Ok(account)
|
||||
@ -199,17 +208,13 @@ impl<T> KeyDirectory for DiskDirectory<T> where T: KeyFileManager {
|
||||
|
||||
fn update(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
|
||||
// Disk store handles updates correctly iff filename is the same
|
||||
self.insert(account)
|
||||
let filename = account_filename(&account);
|
||||
self.insert_with_filename(account, filename, false)
|
||||
}
|
||||
|
||||
fn insert(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
|
||||
// build file path
|
||||
let filename = account.filename.as_ref().cloned().unwrap_or_else(|| {
|
||||
let timestamp = time::strftime("%Y-%m-%dT%H-%M-%S", &time::now_utc()).expect("Time-format string is valid.");
|
||||
format!("UTC--{}Z--{}", timestamp, Uuid::from(account.id))
|
||||
});
|
||||
|
||||
self.insert_with_filename(account, filename)
|
||||
let filename = account_filename(&account);
|
||||
self.insert_with_filename(account, filename, true)
|
||||
}
|
||||
|
||||
fn remove(&self, account: &SafeAccount) -> Result<(), Error> {
|
||||
@ -285,6 +290,14 @@ impl KeyFileManager for DiskKeyFileManager {
|
||||
}
|
||||
}
|
||||
|
||||
fn account_filename(account: &SafeAccount) -> String {
|
||||
// build file path
|
||||
account.filename.clone().unwrap_or_else(|| {
|
||||
let timestamp = time::strftime("%Y-%m-%dT%H-%M-%S", &time::now_utc()).expect("Time-format string is valid.");
|
||||
format!("UTC--{}Z--{}", timestamp, Uuid::from(account.id))
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
extern crate tempdir;
|
||||
@ -317,6 +330,38 @@ mod test {
|
||||
let _ = fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_handle_duplicate_filenames() {
|
||||
// given
|
||||
let mut dir = env::temp_dir();
|
||||
dir.push("ethstore_should_handle_duplicate_filenames");
|
||||
let keypair = Random.generate().unwrap();
|
||||
let password = "hello world";
|
||||
let directory = RootDiskDirectory::create(dir.clone()).unwrap();
|
||||
|
||||
// when
|
||||
let account = SafeAccount::create(&keypair, [0u8; 16], password, 1024, "Test".to_owned(), "{}".to_owned());
|
||||
let filename = "test".to_string();
|
||||
let dedup = true;
|
||||
|
||||
directory.insert_with_filename(account.clone(), "foo".to_string(), dedup).unwrap();
|
||||
let file1 = directory.insert_with_filename(account.clone(), filename.clone(), dedup).unwrap().filename.unwrap();
|
||||
let file2 = directory.insert_with_filename(account.clone(), filename.clone(), dedup).unwrap().filename.unwrap();
|
||||
let file3 = directory.insert_with_filename(account.clone(), filename.clone(), dedup).unwrap().filename.unwrap();
|
||||
|
||||
// then
|
||||
// the first file should have the original names
|
||||
assert_eq!(file1, filename);
|
||||
|
||||
// the following duplicate files should have a suffix appended
|
||||
assert!(file2 != file3);
|
||||
assert_eq!(file2.len(), filename.len() + 5);
|
||||
assert_eq!(file3.len(), filename.len() + 5);
|
||||
|
||||
// cleanup
|
||||
let _ = fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_manage_vaults() {
|
||||
// given
|
||||
|
@ -106,7 +106,7 @@ impl VaultDiskDirectory {
|
||||
fn copy_to_vault(&self, vault: &VaultDiskDirectory) -> Result<(), Error> {
|
||||
for account in self.load()? {
|
||||
let filename = account.filename.clone().expect("self is instance of DiskDirectory; DiskDirectory fills filename in load; qed");
|
||||
vault.insert_with_filename(account, filename)?;
|
||||
vault.insert_with_filename(account, filename, true)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -158,9 +158,14 @@ impl SecretStore for EthStore {
|
||||
self.insert_account(vault, keypair.secret().clone(), password)
|
||||
}
|
||||
|
||||
fn import_wallet(&self, vault: SecretVaultRef, json: &[u8], password: &str) -> Result<StoreAccountRef, Error> {
|
||||
fn import_wallet(&self, vault: SecretVaultRef, json: &[u8], password: &str, gen_id: bool) -> Result<StoreAccountRef, Error> {
|
||||
let json_keyfile = json::KeyFile::load(json).map_err(|_| Error::InvalidKeyFile("Invalid JSON format".to_owned()))?;
|
||||
let mut safe_account = SafeAccount::from_file(json_keyfile, None);
|
||||
|
||||
if gen_id {
|
||||
safe_account.id = Random::random();
|
||||
}
|
||||
|
||||
let secret = safe_account.crypto.secret(password).map_err(|_| Error::InvalidPassword)?;
|
||||
safe_account.address = KeyPair::from_secret(secret)?.address();
|
||||
self.store.import(vault, safe_account)
|
||||
|
@ -116,7 +116,7 @@ pub trait SecretStore: SimpleSecretStore {
|
||||
/// Imports presale wallet
|
||||
fn import_presale(&self, vault: SecretVaultRef, json: &[u8], password: &str) -> Result<StoreAccountRef, Error>;
|
||||
/// Imports existing JSON wallet
|
||||
fn import_wallet(&self, vault: SecretVaultRef, json: &[u8], password: &str) -> Result<StoreAccountRef, Error>;
|
||||
fn import_wallet(&self, vault: SecretVaultRef, json: &[u8], password: &str, gen_id: bool) -> Result<StoreAccountRef, Error>;
|
||||
/// Copies account between stores and vaults.
|
||||
fn copy_account(&self, new_store: &SimpleSecretStore, new_vault: SecretVaultRef, account: &StoreAccountRef, password: &str, new_password: &str) -> Result<(), Error>;
|
||||
/// Checks if password matches given account.
|
||||
|
@ -14,3 +14,4 @@ semver = "0.6"
|
||||
ethcore-ipc = { path = "../ipc/rpc" }
|
||||
ethcore-util = { path = "../util" }
|
||||
ethcore-bigint = { path = "../util/bigint" }
|
||||
parity-version = { path = "../util/version" }
|
||||
|
@ -20,6 +20,7 @@ extern crate semver;
|
||||
extern crate ethcore_util as util;
|
||||
extern crate ethcore_bigint as bigint;
|
||||
extern crate ethcore_ipc as ipc;
|
||||
extern crate parity_version as version;
|
||||
|
||||
mod types;
|
||||
|
||||
|
@ -20,7 +20,7 @@ use std::fmt;
|
||||
use std::str::FromStr;
|
||||
use semver::{Version};
|
||||
use bigint::hash::H160;
|
||||
use util::misc::raw_package_info;
|
||||
use version::raw_package_info;
|
||||
use release_track::ReleaseTrack;
|
||||
|
||||
/// Version information of a particular release.
|
||||
|
@ -152,7 +152,7 @@ macro_rules! usage {
|
||||
use toml;
|
||||
use std::{fs, io, process, cmp};
|
||||
use std::io::{Read, Write};
|
||||
use util::version;
|
||||
use parity_version::version;
|
||||
use clap::{Arg, App, SubCommand, AppSettings, ArgSettings, Error as ClapError, ErrorKind as ClapErrorKind};
|
||||
use helpers::replace_home;
|
||||
use std::ffi::OsStr;
|
||||
|
@ -25,7 +25,8 @@ use cli::{Args, ArgsError};
|
||||
use hash::keccak;
|
||||
use bigint::prelude::U256;
|
||||
use bigint::hash::H256;
|
||||
use util::{version_data, Address};
|
||||
use util::Address;
|
||||
use parity_version::{version_data, version};
|
||||
use bytes::Bytes;
|
||||
use ansi_term::Colour;
|
||||
use ethsync::{NetworkConfiguration, is_valid_node_url};
|
||||
|
@ -73,6 +73,7 @@ extern crate parity_local_store as local_store;
|
||||
extern crate parity_reactor;
|
||||
extern crate parity_rpc;
|
||||
extern crate parity_updater as updater;
|
||||
extern crate parity_version;
|
||||
extern crate parity_whisper;
|
||||
extern crate path;
|
||||
extern crate rpc_cli;
|
||||
|
@ -17,8 +17,9 @@
|
||||
use std::{str, fs, fmt};
|
||||
use std::time::Duration;
|
||||
use bigint::prelude::U256;
|
||||
use util::{Address, version_data};
|
||||
use util::Address;
|
||||
use util::journaldb::Algorithm;
|
||||
use parity_version::version_data;
|
||||
use ethcore::spec::{Spec, SpecParams};
|
||||
use ethcore::ethereum;
|
||||
use ethcore::client::Mode;
|
||||
|
@ -41,7 +41,7 @@ use parity_reactor::EventLoop;
|
||||
use parity_rpc::{NetworkSettings, informant, is_major_importing};
|
||||
use updater::{UpdatePolicy, Updater};
|
||||
use ansi_term::Colour;
|
||||
use util::version;
|
||||
use parity_version::version;
|
||||
use parking_lot::{Condvar, Mutex};
|
||||
use node_filter::NodeFilter;
|
||||
use util::journaldb::Algorithm;
|
||||
|
@ -54,6 +54,7 @@ fetch = { path = "../util/fetch" }
|
||||
node-health = { path = "../dapps/node-health" }
|
||||
parity-reactor = { path = "../util/reactor" }
|
||||
parity-updater = { path = "../updater" }
|
||||
parity-version = { path = "../util/version" }
|
||||
rlp = { path = "../util/rlp" }
|
||||
stats = { path = "../util/stats" }
|
||||
vm = { path = "../ethcore/vm" }
|
||||
|
@ -61,6 +61,7 @@ extern crate fetch;
|
||||
extern crate node_health;
|
||||
extern crate parity_reactor;
|
||||
extern crate parity_updater as updater;
|
||||
extern crate parity_version as version;
|
||||
extern crate rlp;
|
||||
extern crate stats;
|
||||
extern crate hash;
|
||||
|
@ -18,7 +18,7 @@
|
||||
use std::sync::Arc;
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
|
||||
use util::misc::version_data;
|
||||
use version::version_data;
|
||||
|
||||
use crypto::{ecies, DEFAULT_MAC};
|
||||
use ethkey::{Brain, Generator};
|
||||
|
@ -20,7 +20,7 @@ use std::str::FromStr;
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
|
||||
use util::Address;
|
||||
use util::misc::version_data;
|
||||
use version::version_data;
|
||||
|
||||
use crypto::{DEFAULT_MAC, ecies};
|
||||
use ethkey::{Brain, Generator};
|
||||
|
@ -95,7 +95,7 @@ impl ParityAccounts for ParityAccountsClient {
|
||||
let store = self.account_provider()?;
|
||||
|
||||
store.import_presale(json.as_bytes(), &pass)
|
||||
.or_else(|_| store.import_wallet(json.as_bytes(), &pass))
|
||||
.or_else(|_| store.import_wallet(json.as_bytes(), &pass, true))
|
||||
.map(Into::into)
|
||||
.map_err(|e| errors::account("Could not create account.", e))
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
//! Web3 rpc implementation.
|
||||
use hash::keccak;
|
||||
use jsonrpc_core::Error;
|
||||
use util::version;
|
||||
use version::version;
|
||||
use v1::traits::Web3;
|
||||
use v1::types::{H256, Bytes};
|
||||
|
||||
|
@ -234,14 +234,14 @@ fn rpc_parity_chain_id() {
|
||||
|
||||
#[test]
|
||||
fn rpc_parity_default_extra_data() {
|
||||
use util::misc;
|
||||
use version::version_data;
|
||||
use bytes::ToPretty;
|
||||
|
||||
let deps = Dependencies::new();
|
||||
let io = deps.default_client();
|
||||
|
||||
let request = r#"{"jsonrpc": "2.0", "method": "parity_defaultExtraData", "params": [], "id": 1}"#;
|
||||
let response = format!(r#"{{"jsonrpc":"2.0","result":"0x{}","id":1}}"#, misc::version_data().to_hex());
|
||||
let response = format!(r#"{{"jsonrpc":"2.0","result":"0x{}","id":1}}"#, version_data().to_hex());
|
||||
|
||||
assert_eq!(io.handle_request_sync(request), Some(response));
|
||||
}
|
||||
|
@ -480,7 +480,7 @@ fn should_export_account() {
|
||||
// given
|
||||
let tester = setup();
|
||||
let wallet = r#"{"id":"6a186c80-7797-cff2-bc2e-7c1d6a6cc76e","version":3,"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"a1c6ff99070f8032ca1c4e8add006373"},"ciphertext":"df27e3db64aa18d984b6439443f73660643c2d119a6f0fa2fa9a6456fc802d75","kdf":"pbkdf2","kdfparams":{"c":10240,"dklen":32,"prf":"hmac-sha256","salt":"ddc325335cda5567a1719313e73b4842511f3e4a837c9658eeb78e51ebe8c815"},"mac":"3dc888ae79cbb226ff9c455669f6cf2d79be72120f2298f6cb0d444fddc0aa3d"},"address":"0042e5d2a662eeaca8a7e828c174f98f35d8925b","name":"parity-export-test","meta":"{\"passwordHint\":\"parity-export-test\",\"timestamp\":1490017814987}"}"#;
|
||||
tester.accounts.import_wallet(wallet.as_bytes(), "parity-export-test").unwrap();
|
||||
tester.accounts.import_wallet(wallet.as_bytes(), "parity-export-test", false).unwrap();
|
||||
let accounts = tester.accounts.accounts().unwrap();
|
||||
assert_eq!(accounts.len(), 1);
|
||||
|
||||
@ -501,6 +501,26 @@ fn should_export_account() {
|
||||
assert_eq!(result, Some(response.into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_import_wallet() {
|
||||
let tester = setup();
|
||||
|
||||
let id = "6a186c80-7797-cff2-bc2e-7c1d6a6cc76e";
|
||||
let request = r#"{"jsonrpc":"2.0","method":"parity_newAccountFromWallet","params":["{\"id\":\"<ID>\",\"version\":3,\"crypto\":{\"cipher\":\"aes-128-ctr\",\"cipherparams\":{\"iv\":\"478736fb55872c1baf01b27b1998c90b\"},\"ciphertext\":\"fe5a63cc0055d7b0b3b57886f930ad9b63f48950d1348145d95996c41e05f4e0\",\"kdf\":\"pbkdf2\",\"kdfparams\":{\"c\":10240,\"dklen\":32,\"prf\":\"hmac-sha256\",\"salt\":\"658436d6738a19731149a98744e5cf02c8d5aa1f8e80c1a43cc9351c70a984e4\"},\"mac\":\"c7384b26ecf25539d942030230062af9b69de5766cbcc4690bffce1536644631\"},\"address\":\"00bac56a8a27232baa044c03f43bf3648c961735\",\"name\":\"hello world\",\"meta\":\"{}\"}", "himom"],"id":1}"#;
|
||||
let request = request.replace("<ID>", id);
|
||||
let response = r#"{"jsonrpc":"2.0","result":"0x00bac56a8a27232baa044c03f43bf3648c961735","id":1}"#;
|
||||
|
||||
let res = tester.io.handle_request_sync(&request).unwrap();
|
||||
|
||||
assert_eq!(res, response);
|
||||
|
||||
let account_meta = tester.accounts.account_meta("0x00bac56a8a27232baa044c03f43bf3648c961735".into()).unwrap();
|
||||
let account_uuid: String = account_meta.uuid.unwrap().into();
|
||||
|
||||
// the RPC should import the account with a new id
|
||||
assert!(account_uuid != id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_sign_message() {
|
||||
let tester = setup();
|
||||
|
@ -15,7 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use jsonrpc_core::IoHandler;
|
||||
use util::version;
|
||||
use version::version;
|
||||
use v1::{Web3, Web3Client};
|
||||
|
||||
#[test]
|
||||
|
@ -24,4 +24,5 @@ parity-hash-fetch = { path = "../hash-fetch" }
|
||||
ipc-common-types = { path = "../ipc-common-types" }
|
||||
ethcore-ipc = { path = "../ipc/rpc" }
|
||||
parity-reactor = { path = "../util/reactor" }
|
||||
parity-version = { path = "../util/version" }
|
||||
path = { path = "../util/path" }
|
||||
|
@ -30,6 +30,7 @@ extern crate ethcore_ipc as ipc;
|
||||
extern crate futures;
|
||||
extern crate target_info;
|
||||
extern crate parity_reactor;
|
||||
extern crate parity_version as version;
|
||||
extern crate path;
|
||||
|
||||
mod updater;
|
||||
|
68
updater/src/types/version_info.rs
Normal file
68
updater/src/types/version_info.rs
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Types used in the public API
|
||||
|
||||
use std::fmt;
|
||||
use semver::{Version};
|
||||
use bigint::hash::H160;
|
||||
use version::raw_package_info;
|
||||
use types::ReleaseTrack;
|
||||
|
||||
/// Version information of a particular release.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct VersionInfo {
|
||||
/// The track on which it was released.
|
||||
pub track: ReleaseTrack,
|
||||
/// The version.
|
||||
pub version: Version,
|
||||
/// The (SHA1?) 160-bit hash of this build's code base.
|
||||
pub hash: H160,
|
||||
}
|
||||
|
||||
impl fmt::Display for VersionInfo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(f, "{}.{}.{}-{}-{}", self.version.major, self.version.minor, self.version.patch, self.track, self.hash)
|
||||
}
|
||||
}
|
||||
|
||||
impl VersionInfo {
|
||||
/// Get information for this (currently running) binary.
|
||||
pub fn this() -> Self {
|
||||
let raw = raw_package_info();
|
||||
VersionInfo {
|
||||
track: raw.0.into(),
|
||||
version: { let mut v = Version::parse(raw.1).expect("Environment variables are known to be valid; qed"); v.build = vec![]; v.pre = vec![]; v },
|
||||
hash: raw.2.parse::<H160>().unwrap_or_else(|_| H160::zero()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Compose the information from the provided raw fields.
|
||||
pub fn from_raw(semver: u32, track: u8, hash: H160) -> Self {
|
||||
let t = track.into();
|
||||
VersionInfo {
|
||||
version: Version {
|
||||
major: (semver >> 16) as u64,
|
||||
minor: ((semver >> 8) & 0xff) as u64,
|
||||
patch: (semver & 0xff) as u64,
|
||||
build: vec![],
|
||||
pre: vec![],
|
||||
},
|
||||
track: t,
|
||||
hash: hash,
|
||||
}
|
||||
}
|
||||
}
|
@ -35,7 +35,7 @@ use bigint::hash::{H160, H256};
|
||||
use util::Address;
|
||||
use bytes::Bytes;
|
||||
use parking_lot::Mutex;
|
||||
use util::misc;
|
||||
use version;
|
||||
|
||||
/// Filter for releases.
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
@ -116,7 +116,7 @@ fn platform() -> String {
|
||||
} else if cfg!(target_os = "linux") {
|
||||
format!("{}-unknown-linux-gnu", Target::arch())
|
||||
} else {
|
||||
misc::platform()
|
||||
version::platform()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,6 @@ license = "GPL-3.0"
|
||||
name = "ethcore-util"
|
||||
version = "1.8.10"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
log = "0.3"
|
||||
|
@ -1,37 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate vergen;
|
||||
extern crate rustc_version;
|
||||
|
||||
use vergen::*;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
vergen(OutputFns::all()).unwrap();
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let dest_path = Path::new(&out_dir).join("rustc_version.rs");
|
||||
let mut f = File::create(&dest_path).unwrap();
|
||||
f.write_all(format!("
|
||||
/// Returns compiler version.
|
||||
pub fn rustc_version() -> &'static str {{
|
||||
\"{}\"
|
||||
}}
|
||||
", rustc_version::version()).as_bytes()).unwrap();
|
||||
}
|
@ -36,6 +36,7 @@ hash = { path = "../hash" }
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
serde_derive = "1.0"
|
||||
parity-version = { path = "../version" }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
@ -30,7 +30,7 @@ use mio::*;
|
||||
use mio::deprecated::{EventLoop};
|
||||
use mio::tcp::*;
|
||||
use bigint::hash::*;
|
||||
use util::version;
|
||||
use version::version;
|
||||
use rlp::*;
|
||||
use session::{Session, SessionInfo, SessionData};
|
||||
use error::*;
|
||||
|
@ -82,6 +82,7 @@ extern crate ipnetwork;
|
||||
extern crate hash;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
extern crate parity_version as version;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
@ -1,6 +0,0 @@
|
||||
[package]
|
||||
name = "semantic_version"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
@ -1,47 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Semantic version formatting and comparing.
|
||||
|
||||
/// A version value with strict meaning. Use `as_u32` to convert to a simple integer.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// extern crate semantic_version;
|
||||
/// use semantic_version::*;
|
||||
///
|
||||
/// fn main() {
|
||||
/// assert_eq!(SemanticVersion::new(1, 2, 3).as_u32(), 0x010203);
|
||||
/// }
|
||||
/// ```
|
||||
pub struct SemanticVersion {
|
||||
/// Major version - API/feature removals & breaking changes.
|
||||
pub major: u8,
|
||||
/// Minor version - API/feature additions.
|
||||
pub minor: u8,
|
||||
/// Tiny version - bug fixes.
|
||||
pub tiny: u8,
|
||||
}
|
||||
|
||||
impl SemanticVersion {
|
||||
/// Create a new object.
|
||||
pub fn new(major: u8, minor: u8, tiny: u8) -> SemanticVersion { SemanticVersion{major: major, minor: minor, tiny: tiny} }
|
||||
|
||||
/// Convert to a `u32` representation.
|
||||
pub fn as_u32(&self) -> u32 { ((self.major as u32) << 16) + ((self.minor as u32) << 8) + self.tiny as u32 }
|
||||
}
|
||||
|
||||
// TODO: implement Eq, Comparison and Debug/Display for SemanticVersion.
|
@ -87,6 +87,7 @@
|
||||
//! cargo build --release
|
||||
//! ```
|
||||
|
||||
extern crate util_error as error;
|
||||
extern crate rustc_hex;
|
||||
extern crate rocksdb;
|
||||
extern crate env_logger;
|
||||
@ -106,9 +107,7 @@ extern crate ethcore_logger;
|
||||
extern crate hash as keccak;
|
||||
extern crate hashdb;
|
||||
extern crate memorydb;
|
||||
extern crate patricia_trie as trie;
|
||||
extern crate kvdb;
|
||||
extern crate util_error as error;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate kvdb_memorydb;
|
||||
@ -116,12 +115,10 @@ extern crate kvdb_memorydb;
|
||||
#[macro_use]
|
||||
extern crate log as rlog;
|
||||
|
||||
pub mod misc;
|
||||
pub mod overlaydb;
|
||||
pub mod journaldb;
|
||||
pub mod cache;
|
||||
|
||||
pub use misc::*;
|
||||
pub use hashdb::*;
|
||||
pub use memorydb::MemoryDB;
|
||||
pub use overlaydb::*;
|
||||
|
@ -28,7 +28,7 @@ target_info = "0.1"
|
||||
|
||||
[build-dependencies]
|
||||
vergen = "0.1"
|
||||
rustc_version = "0.2.0"
|
||||
rustc_version = "0.1.0"
|
||||
toml = "0.4"
|
||||
|
||||
[features]
|
||||
|
57
util/version/build.rs
Normal file
57
util/version/build.rs
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright 2015-2017 Parity Technologies (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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate rustc_version;
|
||||
extern crate toml;
|
||||
extern crate vergen;
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use vergen::{vergen, OutputFns};
|
||||
|
||||
const ERROR_MSG: &'static str = "Failed to generate metadata files";
|
||||
|
||||
fn main() {
|
||||
vergen(OutputFns::all()).expect(ERROR_MSG);
|
||||
|
||||
let version = rustc_version::version();
|
||||
|
||||
let cargo: toml::Value = toml::from_str(include_str!("./Cargo.toml")).expect(ERROR_MSG);
|
||||
let track = cargo["package"]["metadata"]["track"].as_str().expect("'track' has to be a string!");
|
||||
|
||||
create_file("meta.rs", format!("
|
||||
/// This versions track.
|
||||
#[allow(unused)]
|
||||
pub const TRACK: &str = {track:?};
|
||||
|
||||
/// Returns compiler version.
|
||||
pub fn rustc_version() -> &'static str {{
|
||||
\"{version}\"
|
||||
}}
|
||||
",
|
||||
track = track,
|
||||
version = version,
|
||||
));
|
||||
}
|
||||
|
||||
fn create_file(filename: &str, data: String) {
|
||||
let out_dir = env::var("OUT_DIR").expect(ERROR_MSG);
|
||||
let dest_path = Path::new(&out_dir).join(filename);
|
||||
let mut f = File::create(&dest_path).expect(ERROR_MSG);
|
||||
f.write_all(data.as_bytes()).expect(ERROR_MSG);
|
||||
}
|
@ -14,32 +14,33 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Diff misc.
|
||||
//! Parity version specific information.
|
||||
|
||||
extern crate target_info;
|
||||
extern crate ethcore_bytes as bytes;
|
||||
extern crate rlp;
|
||||
|
||||
use rlp::RlpStream;
|
||||
use target_info::Target;
|
||||
use bytes::Bytes;
|
||||
use rlp::RlpStream;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/version.rs"));
|
||||
include!(concat!(env!("OUT_DIR"), "/rustc_version.rs"));
|
||||
mod vergen {
|
||||
#![allow(unused)]
|
||||
include!(concat!(env!("OUT_DIR"), "/version.rs"));
|
||||
}
|
||||
|
||||
mod generated {
|
||||
include!(concat!(env!("OUT_DIR"), "/meta.rs"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "final")]
|
||||
const THIS_TRACK: &'static str = "stable";
|
||||
// ^^^ should be reset to "stable" or "beta" according to the release branch.
|
||||
const THIS_TRACK: &'static str = generated::TRACK;
|
||||
// ^^^ should be reset in Cargo.toml to "stable" or "beta" according to the release branch.
|
||||
|
||||
#[cfg(not(feature = "final"))]
|
||||
const THIS_TRACK: &'static str = "unstable";
|
||||
// ^^^ This gets used when we're not building a final release; should stay as "unstable".
|
||||
|
||||
/// Boolean type for clean/dirty status.
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum Filth {
|
||||
/// Data has not been changed.
|
||||
Clean,
|
||||
/// Data has been changed.
|
||||
Dirty,
|
||||
}
|
||||
|
||||
/// Get the platform identifier.
|
||||
pub fn platform() -> String {
|
||||
let env = Target::env();
|
||||
@ -49,11 +50,11 @@ pub fn platform() -> String {
|
||||
|
||||
/// Get the standard version string for this software.
|
||||
pub fn version() -> String {
|
||||
let sha3 = short_sha();
|
||||
let sha3 = vergen::short_sha();
|
||||
let sha3_dash = if sha3.is_empty() { "" } else { "-" };
|
||||
let commit_date = commit_date().replace("-", "");
|
||||
let commit_date = vergen::commit_date().replace("-", "");
|
||||
let date_dash = if commit_date.is_empty() { "" } else { "-" };
|
||||
format!("Parity/v{}-{}{}{}{}{}/{}/rustc{}", env!("CARGO_PKG_VERSION"), THIS_TRACK, sha3_dash, sha3, date_dash, commit_date, platform(), rustc_version())
|
||||
format!("Parity/v{}-{}{}{}{}{}/{}/rustc{}", env!("CARGO_PKG_VERSION"), THIS_TRACK, sha3_dash, sha3, date_dash, commit_date, platform(), generated::rustc_version())
|
||||
}
|
||||
|
||||
/// Get the standard version data for this software.
|
||||
@ -65,12 +66,12 @@ pub fn version_data() -> Bytes {
|
||||
env!("CARGO_PKG_VERSION_PATCH").parse::<u32>().expect("Environment variables are known to be valid; qed");
|
||||
s.append(&v);
|
||||
s.append(&"Parity");
|
||||
s.append(&rustc_version());
|
||||
s.append(&generated::rustc_version());
|
||||
s.append(&&Target::os()[0..2]);
|
||||
s.out()
|
||||
}
|
||||
|
||||
/// Provide raw information on the package.
|
||||
pub fn raw_package_info() -> (&'static str, &'static str, &'static str) {
|
||||
(THIS_TRACK, env!["CARGO_PKG_VERSION"], sha())
|
||||
(THIS_TRACK, env!["CARGO_PKG_VERSION"], vergen::sha())
|
||||
}
|
Loading…
Reference in New Issue
Block a user