From eee735df7ce056052e0bfe59959bc3c67bbaacd1 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Fri, 7 Dec 2018 17:53:17 +0100 Subject: [PATCH 01/23] Journal for private txs added --- ethcore/private-tx/src/lib.rs | 37 ++-- ethcore/private-tx/src/log.rs | 166 ++++++++++++++++++ .../private-tx/src/private_transactions.rs | 2 +- parity/configuration.rs | 2 + 4 files changed, 195 insertions(+), 12 deletions(-) create mode 100644 ethcore/private-tx/src/log.rs diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index d487b4d835b..3b1c25751ff 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -25,6 +25,7 @@ mod key_server_keys; mod private_transactions; mod messages; mod error; +mod log; extern crate common_types as types; extern crate ethabi; @@ -45,11 +46,15 @@ extern crate parking_lot; extern crate trie_db as trie; extern crate patricia_trie_ethereum as ethtrie; extern crate rlp; +#[macro_use] +extern crate serde_derive; +extern crate serde; +extern crate serde_json; extern crate rustc_hex; extern crate transaction_pool as txpool; extern crate url; #[macro_use] -extern crate log; +extern crate log as ethlog; #[macro_use] extern crate ethabi_derive; #[macro_use] @@ -67,7 +72,8 @@ pub use encryptor::{Encryptor, SecretStoreEncryptor, EncryptorConfig, NoopEncryp pub use key_server_keys::{KeyProvider, SecretStoreKeys, StoringKeyProvider}; pub use private_transactions::{VerifiedPrivateTransaction, VerificationStore, PrivateTransactionSigningDesc, SigningStore}; pub use messages::{PrivateTransaction, SignedPrivateTransaction}; -pub use error::Error; +pub use error::{Error, ErrorKind}; +use log::{Logging}; use std::sync::{Arc, Weak}; use std::collections::{HashMap, HashSet, BTreeMap}; @@ -117,6 +123,8 @@ pub struct ProviderConfig { pub validator_accounts: Vec
, /// Account used for signing public transactions created from private transactions pub signer_account: Option
, + /// Path to private tx logs + pub logs_path: Option, } #[derive(Debug)] @@ -177,6 +185,7 @@ pub struct Provider { accounts: Arc, channel: IoChannel, keys_provider: Arc, + logging: Logging, } #[derive(Debug)] @@ -211,6 +220,7 @@ impl Provider { accounts, channel, keys_provider, + logging: Logging::new(config.logs_path), } } @@ -257,8 +267,9 @@ impl Provider { trace!(target: "privatetx", "Required validators: {:?}", contract_validators); let private_state_hash = self.calculate_state_hash(&private_state, contract_nonce); trace!(target: "privatetx", "Hashed effective private state for sender: {:?}", private_state_hash); - self.transactions_for_signing.write().add_transaction(private.hash(), signed_transaction, contract_validators, private_state, contract_nonce)?; + self.transactions_for_signing.write().add_transaction(private.hash(), signed_transaction, &contract_validators, private_state, contract_nonce)?; self.broadcast_private_transaction(private.hash(), private.rlp_bytes()); + self.logging.private_tx_created(tx_hash, &contract_validators); Ok(Receipt { hash: tx_hash, contract_address: contract, @@ -354,8 +365,9 @@ impl Provider { Some(desc) => desc, }; let last = self.last_required_signature(&desc, signed_tx.signature())?; + let original_tx_hash = desc.original_transaction.hash(); - if last { + if last.0 { let mut signatures = desc.received_signatures.clone(); signatures.push(signed_tx.signature()); let rsv: Vec = signatures.into_iter().map(|sign| sign.into_electrum().into()).collect(); @@ -373,7 +385,7 @@ impl Provider { trace!(target: "privatetx", "Last required signature received, public transaction created: {:?}", public_tx); // Sign and add it to the queue let chain_id = desc.original_transaction.chain_id(); - let hash = public_tx.hash(chain_id); + let public_tx_hash = public_tx.hash(chain_id); let signature = self.accounts.sign(signer_account, hash)?; let signed = SignedTransaction::new(public_tx.with_signature(signature, chain_id))?; match self.miner.import_own_transaction(&*self.client, signed.into()) { @@ -392,6 +404,9 @@ impl Provider { Err(err) => warn!(target: "privatetx", "Failed to send private state changed notification, error: {:?}", err), } } + // Store logs + self.logging.signature_added(original_tx_hash, last.1); + self.logging.tx_deployed(original_tx_hash, public_tx_hash); // Remove from store for signing if let Err(err) = self.transactions_for_signing.write().remove(&private_hash) { warn!(target: "privatetx", "Failed to remove transaction from signing store, error: {:?}", err); @@ -400,7 +415,10 @@ impl Provider { } else { // Add signature to the store match self.transactions_for_signing.write().add_signature(&private_hash, signed_tx.signature()) { - Ok(_) => trace!(target: "privatetx", "Signature stored for private transaction"), + Ok(_) => { + trace!(target: "privatetx", "Signature stored for private transaction"); + self.logging.signature_added(original_tx_hash, last.1); + } Err(err) => { warn!(target: "privatetx", "Failed to add signature to signing store, error: {:?}", err); return Err(err); @@ -420,17 +438,14 @@ impl Provider { } } - fn last_required_signature(&self, desc: &PrivateTransactionSigningDesc, sign: Signature) -> Result { - if desc.received_signatures.contains(&sign) { - return Ok(false); - } + fn last_required_signature(&self, desc: &PrivateTransactionSigningDesc, sign: Signature) -> Result<(bool, Address), Error> { let state_hash = self.calculate_state_hash(&desc.state, desc.contract_nonce); match recover(&sign, &state_hash) { Ok(public) => { let sender = public_to_address(&public); match desc.validators.contains(&sender) { true => { - Ok(desc.received_signatures.len() + 1 == desc.validators.len()) + Ok((desc.received_signatures.len() + 1 == desc.validators.len(), sender)) } false => { warn!(target: "privatetx", "Sender's state doesn't correspond to validator's"); diff --git a/ethcore/private-tx/src/log.rs b/ethcore/private-tx/src/log.rs new file mode 100644 index 00000000000..d1f7f3df0c2 --- /dev/null +++ b/ethcore/private-tx/src/log.rs @@ -0,0 +1,166 @@ +// Copyright 2015-2018 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 . + +//! Private transactions logs. + +use ethereum_types::{H256, Address}; +use std::collections::{HashMap}; +use std::fs::{File}; +use std::time::{SystemTime, UNIX_EPOCH}; +use parking_lot::{RwLock}; + +#[derive(Clone, Serialize, Deserialize)] +enum Status { + Created, + Validating, + Deployed, +} + +#[derive(Clone, Serialize, Deserialize)] +struct ValidatorLog { + account: Address, + validated: bool, + validation_timestamp: Option, +} + +#[derive(Clone, Serialize, Deserialize)] +struct TransactionLog { + tx_hash: H256, + status: Status, + creation_timestamp: u64, + validators: Vec, + deployment_timestamp: Option, + public_tx_hash: Option, +} + +/// Private transactions logging +pub struct Logging { + logs: RwLock>, + logs_dir: Option, +} + +impl Logging { + pub fn new(logs_dir: Option) -> Self { + let logging = Logging { + logs: RwLock::new(HashMap::new()), + logs_dir, + }; + logging.read_logs(); + logging + } + + pub fn private_tx_created(&self, tx_hash: H256, validators: &Vec
) { + let mut validator_logs = Vec::new(); + for account in validators { + validator_logs.push(ValidatorLog { + account: *account, + validated: false, + validation_timestamp: None, + }); + } + let mut logs = self.logs.write(); + logs.insert(tx_hash, TransactionLog { + tx_hash, + status: Status::Created, + creation_timestamp: SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(), + validators: validator_logs, + deployment_timestamp: None, + public_tx_hash: None, + }); + } + + pub fn signature_added(&self, tx_hash: H256, validator: Address) { + let mut logs = self.logs.write(); + if let Some(transaction_log) = logs.get_mut(&tx_hash) { + transaction_log.status = Status::Validating; + if let Some(ref mut validator_log) = transaction_log.validators.iter_mut().find(|log| log.account == validator) { + validator_log.validated = true; + validator_log.validation_timestamp = Some(SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs()); + } + } + } + + pub fn tx_deployed(&self, tx_hash: H256, public_tx_hash: H256) { + let mut logs = self.logs.write(); + if let Some(log) = logs.get_mut(&tx_hash) { + log.status = Status::Deployed; + log.deployment_timestamp = Some(SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs()); + log.public_tx_hash = Some(public_tx_hash); + } + } + + fn read_logs(&self) { + let log_file = match self.logs_dir { + Some(ref path) => { + let mut file_path = path.clone(); + file_path.push_str("private_tx.log"); + match File::open(&file_path) { + Ok(file) => file, + Err(err) => { + trace!(target: "privatetx", "Cannot open logs file: {}", err); + return; + } + } + } + None => { + error!(target: "privatetx", "Logs path is not defined"); + return; + } + }; + let transaction_logs: Vec = match serde_json::from_reader(log_file) { + Ok(logs) => logs, + Err(err) => { + error!(target: "privatetx", "Cannot deserialize logs from file: {}", err); + return; + } + }; + let mut logs = self.logs.write(); + for log in transaction_logs { + logs.insert(log.tx_hash, log); + } + } + + fn flush_logs(&self) { + let log_file = match self.logs_dir { + Some(ref path) => { + let mut file_path = path.clone(); + file_path.push_str("private_tx.log"); + match File::open(&file_path) { + Ok(file) => Some(file), + Err(_) => File::create(&file_path).ok() + } + } + None => None, + }; + let log_file = match log_file { + Some(file) => file, + None => { + error!(target: "privatetx", "Cannot open logs file"); + return; + } + }; + if let Err(err) = serde_json::to_writer(log_file, &self.logs.read().values().cloned().collect::>()) { + error!(target: "privatetx", "Error during logs serialisation: {}", err); + } + } +} + +// Flush all logs on drop +impl Drop for Logging { + fn drop(&mut self) { + self.flush_logs(); + } +} diff --git a/ethcore/private-tx/src/private_transactions.rs b/ethcore/private-tx/src/private_transactions.rs index d0456657b06..dd263ee1868 100644 --- a/ethcore/private-tx/src/private_transactions.rs +++ b/ethcore/private-tx/src/private_transactions.rs @@ -224,7 +224,7 @@ impl SigningStore { &mut self, private_hash: H256, transaction: SignedTransaction, - validators: Vec
, + validators: &Vec
, state: Bytes, contract_nonce: U256, ) -> Result<(), Error> { diff --git a/parity/configuration.rs b/parity/configuration.rs index 198a58c3f99..85400840092 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -913,9 +913,11 @@ impl Configuration { } fn private_provider_config(&self) -> Result<(ProviderConfig, EncryptorConfig, bool), String> { + let dirs = self.directories(); let provider_conf = ProviderConfig { validator_accounts: to_addresses(&self.args.arg_private_validators)?, signer_account: self.args.arg_private_signer.clone().and_then(|account| to_address(Some(account)).ok()), + logs_path: Some(dirs.base), }; let encryptor_conf = EncryptorConfig { From 552e1303b7c91c5c313d9d3255b99d9ad086f7f1 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Tue, 11 Dec 2018 11:58:33 +0100 Subject: [PATCH 02/23] Tests after adding logging to private tx fixed --- ethcore/private-tx/src/log.rs | 6 +++++- ethcore/private-tx/tests/private_contract.rs | 1 + ethcore/sync/src/tests/private.rs | 2 ++ parity/configuration.rs | 7 ++++++- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/ethcore/private-tx/src/log.rs b/ethcore/private-tx/src/log.rs index d1f7f3df0c2..dc8a8690de7 100644 --- a/ethcore/private-tx/src/log.rs +++ b/ethcore/private-tx/src/log.rs @@ -116,7 +116,7 @@ impl Logging { } } None => { - error!(target: "privatetx", "Logs path is not defined"); + warn!(target: "privatetx", "Logs path is not defined"); return; } }; @@ -134,6 +134,10 @@ impl Logging { } fn flush_logs(&self) { + if self.logs.read().is_empty() { + // Do not create empty file + return; + } let log_file = match self.logs_dir { Some(ref path) => { let mut file_path = path.clone(); diff --git a/ethcore/private-tx/tests/private_contract.rs b/ethcore/private-tx/tests/private_contract.rs index 6365b10eecd..bf40ada0ec4 100644 --- a/ethcore/private-tx/tests/private_contract.rs +++ b/ethcore/private-tx/tests/private_contract.rs @@ -59,6 +59,7 @@ fn private_contract() { let config = ProviderConfig{ validator_accounts: vec![key3.address(), key4.address()], signer_account: None, + logs_path: None, }; let io = ethcore_io::IoChannel::disconnected(); diff --git a/ethcore/sync/src/tests/private.rs b/ethcore/sync/src/tests/private.rs index 24de14d936d..b56f0ecc218 100644 --- a/ethcore/sync/src/tests/private.rs +++ b/ethcore/sync/src/tests/private.rs @@ -69,11 +69,13 @@ fn send_private_transaction() { let validator_config = ProviderConfig{ validator_accounts: vec![s1.address()], signer_account: None, + logs_path: None, }; let signer_config = ProviderConfig{ validator_accounts: Vec::new(), signer_account: Some(s0.address()), + logs_path: None, }; let private_keys = Arc::new(StoringKeyProvider::default()); diff --git a/parity/configuration.rs b/parity/configuration.rs index 85400840092..bef02ca25cd 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -1455,7 +1455,12 @@ mod tests { net_settings: Default::default(), ipfs_conf: Default::default(), secretstore_conf: Default::default(), - private_provider_conf: Default::default(), + private_provider_conf: ProviderConfig { + validator_accounts: Default::default(), + signer_account: Default::default(), + passwords: Default::default(), + logs_path: Some(Directories::default().base), + }, private_encryptor_conf: Default::default(), private_tx_enabled: false, name: "".into(), From 81dc28f0b6a47f8512162d4c4a90831d5748e8f2 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Tue, 11 Dec 2018 13:51:31 +0100 Subject: [PATCH 03/23] Logs getter and tests added --- ethcore/private-tx/src/log.rs | 88 +++++++++++++++++++++++++++++------ 1 file changed, 75 insertions(+), 13 deletions(-) diff --git a/ethcore/private-tx/src/log.rs b/ethcore/private-tx/src/log.rs index dc8a8690de7..8b19e53ac5f 100644 --- a/ethcore/private-tx/src/log.rs +++ b/ethcore/private-tx/src/log.rs @@ -22,28 +22,44 @@ use std::fs::{File}; use std::time::{SystemTime, UNIX_EPOCH}; use parking_lot::{RwLock}; -#[derive(Clone, Serialize, Deserialize)] -enum Status { +/// Current status of the private transaction +#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] +pub enum Status { + /// Private tx was created but no validation received yet Created, + /// Several validators (but not all) validated the transaction Validating, + /// All validators validated the private tx + /// Corresponding public tx was created and added into the pool Deployed, } +/// Information about private tx validation #[derive(Clone, Serialize, Deserialize)] -struct ValidatorLog { - account: Address, - validated: bool, - validation_timestamp: Option, +pub struct ValidatorLog { + /// Account of the validator + pub account: Address, + /// Validation flag + pub validated: bool, + /// Validation timestamp + pub validation_timestamp: Option, } +/// Information about the private transaction #[derive(Clone, Serialize, Deserialize)] -struct TransactionLog { - tx_hash: H256, - status: Status, - creation_timestamp: u64, - validators: Vec, - deployment_timestamp: Option, - public_tx_hash: Option, +pub struct TransactionLog { + /// Original signed transaction hash (used as a source for private tx) + pub tx_hash: H256, + /// Current status of the private transaction + pub status: Status, + /// Creation timestamp + pub creation_timestamp: u64, + /// List of validations + pub validators: Vec, + /// Timestamp of the resulting public tx deployment + pub deployment_timestamp: Option, + /// Hash of the resulting public tx + pub public_tx_hash: Option, } /// Private transactions logging @@ -53,6 +69,7 @@ pub struct Logging { } impl Logging { + /// Creates the logging object pub fn new(logs_dir: Option) -> Self { let logging = Logging { logs: RwLock::new(HashMap::new()), @@ -62,6 +79,12 @@ impl Logging { logging } + /// Retrieves log for the corresponding tx hash + pub fn tx_log(&self, tx_hash: H256) -> Option { + self.logs.read().get(&tx_hash).cloned() + } + + /// Logs the creation of private transaction pub fn private_tx_created(&self, tx_hash: H256, validators: &Vec
) { let mut validator_logs = Vec::new(); for account in validators { @@ -82,6 +105,7 @@ impl Logging { }); } + /// Logs the obtaining of the signature for the private transaction pub fn signature_added(&self, tx_hash: H256, validator: Address) { let mut logs = self.logs.write(); if let Some(transaction_log) = logs.get_mut(&tx_hash) { @@ -93,6 +117,7 @@ impl Logging { } } + /// Logs the final deployment of the resulting public transaction pub fn tx_deployed(&self, tx_hash: H256, public_tx_hash: H256) { let mut logs = self.logs.write(); if let Some(log) = logs.get_mut(&tx_hash) { @@ -168,3 +193,40 @@ impl Drop for Logging { self.flush_logs(); } } + +#[cfg(test)] +mod tests { + use serde_json; + use transaction::{Transaction}; + use super::{TransactionLog, Logging, Status}; + + #[test] + fn private_log_format() { + let s = r#"{ + "tx_hash":"0x64f648ca7ae7f4138014f860ae56164d8d5732969b1cea54d8be9d144d8aa6f6", + "status":"Deployed", + "creation_timestamp":1544528180, + "validators":[{ + "account":"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1", + "validated":true, + "validation_timestamp":1544528181 + }], + "deployment_timestamp":1544528181, + "public_tx_hash":"0x69b9c691ede7993effbcc88911c309af1c82be67b04b3882dd446b808ae146da" + }"#; + + let _deserialized: TransactionLog = serde_json::from_str(s).unwrap(); + } + + #[test] + fn private_log_status() { + let logger = Logging::new(None); + let private_tx = Transaction::default(); + let hash = private_tx.hash(None); + logger.private_tx_created(hash, &vec!["0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1".into()]); + logger.signature_added(hash, "0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1".into()); + logger.tx_deployed(hash, hash); + let tx_log = logger.tx_log(hash).unwrap(); + assert_eq!(tx_log.status, Status::Deployed); + } +} \ No newline at end of file From 48363392b0bd9691e9f43d4fa63cca0b0b91eda6 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Tue, 11 Dec 2018 16:12:11 +0100 Subject: [PATCH 04/23] Time and amount limit for logs added --- ethcore/private-tx/src/log.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/ethcore/private-tx/src/log.rs b/ethcore/private-tx/src/log.rs index 8b19e53ac5f..f1dad1f35e0 100644 --- a/ethcore/private-tx/src/log.rs +++ b/ethcore/private-tx/src/log.rs @@ -22,6 +22,13 @@ use std::fs::{File}; use std::time::{SystemTime, UNIX_EPOCH}; use parking_lot::{RwLock}; +/// Maximum amount of stored private transaction logs. +const MAX_JOURNAL_LEN: usize = 1000; + +/// Maximum period for storing private transaction logs. +/// Older logs will not be processed, 20 days +const MAX_STORING_TIME: u64 = 60 * 60 * 24 * 20; + /// Current status of the private transaction #[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] pub enum Status { @@ -95,6 +102,12 @@ impl Logging { }); } let mut logs = self.logs.write(); + if logs.len() > MAX_JOURNAL_LEN { + // Remove the oldest log + let mut sorted_logs = logs.values().cloned().collect::>(); + sorted_logs.sort_by(|a, b| a.creation_timestamp.cmp(&b.creation_timestamp)); + logs.remove(&sorted_logs[0].tx_hash); + } logs.insert(tx_hash, TransactionLog { tx_hash, status: Status::Created, @@ -145,13 +158,16 @@ impl Logging { return; } }; - let transaction_logs: Vec = match serde_json::from_reader(log_file) { + let mut transaction_logs: Vec = match serde_json::from_reader(log_file) { Ok(logs) => logs, Err(err) => { error!(target: "privatetx", "Cannot deserialize logs from file: {}", err); return; } }; + // Drop old logs + let current_timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(); + transaction_logs.retain(|tx_log| tx_log.creation_timestamp - current_timestamp < MAX_STORING_TIME); let mut logs = self.logs.write(); for log in transaction_logs { logs.insert(log.tx_hash, log); From cd66d0652a1a2e9a74968d87696a897986be037e Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Wed, 12 Dec 2018 12:21:35 +0100 Subject: [PATCH 05/23] RPC method for log retrieving added --- ethcore/private-tx/src/error.rs | 2 + ethcore/private-tx/src/lib.rs | 7 ++- ethcore/private-tx/src/log.rs | 14 ++--- rpc/src/v1/impls/private.rs | 9 +++- rpc/src/v1/traits/private.rs | 6 ++- rpc/src/v1/types/mod.rs | 6 ++- rpc/src/v1/types/private_log.rs | 95 +++++++++++++++++++++++++++++++++ 7 files changed, 127 insertions(+), 12 deletions(-) create mode 100644 rpc/src/v1/types/private_log.rs diff --git a/ethcore/private-tx/src/error.rs b/ethcore/private-tx/src/error.rs index eda08b2a567..17cad9d7775 100644 --- a/ethcore/private-tx/src/error.rs +++ b/ethcore/private-tx/src/error.rs @@ -99,6 +99,8 @@ pub enum Error { /// Key server URL is not set. #[display(fmt = "Key server URL is not set.")] KeyServerNotSet, + #[display(fmt = "Private transaction not found in logs.")] + TxNotFoundInLog, /// VM execution error. #[display(fmt = "VM execution error {}", _0)] Execution(ExecutionError), diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 3b1c25751ff..563e2fbfbdb 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -73,7 +73,7 @@ pub use key_server_keys::{KeyProvider, SecretStoreKeys, StoringKeyProvider}; pub use private_transactions::{VerifiedPrivateTransaction, VerificationStore, PrivateTransactionSigningDesc, SigningStore}; pub use messages::{PrivateTransaction, SignedPrivateTransaction}; pub use error::{Error, ErrorKind}; -use log::{Logging}; +pub use log::{Logging, TransactionLog, ValidatorLog, PrivateTxStatus}; use std::sync::{Arc, Weak}; use std::collections::{HashMap, HashSet, BTreeMap}; @@ -689,6 +689,11 @@ impl Provider { Ok(result.result) } + /// Retrieves log information about private transaction + pub fn private_log(&self, tx_hash: H256) -> Result { + self.logging.tx_log(tx_hash).ok_or(ErrorKind::TxNotFoundInLog.into()) + } + /// Returns private validators for a contract. pub fn get_validators(&self, block: BlockId, address: &Address) -> Result, Error> { let (data, decoder) = private_contract::functions::get_validators::call(); diff --git a/ethcore/private-tx/src/log.rs b/ethcore/private-tx/src/log.rs index f1dad1f35e0..8b738bced2f 100644 --- a/ethcore/private-tx/src/log.rs +++ b/ethcore/private-tx/src/log.rs @@ -31,7 +31,7 @@ const MAX_STORING_TIME: u64 = 60 * 60 * 24 * 20; /// Current status of the private transaction #[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] -pub enum Status { +pub enum PrivateTxStatus { /// Private tx was created but no validation received yet Created, /// Several validators (but not all) validated the transaction @@ -58,7 +58,7 @@ pub struct TransactionLog { /// Original signed transaction hash (used as a source for private tx) pub tx_hash: H256, /// Current status of the private transaction - pub status: Status, + pub status: PrivateTxStatus, /// Creation timestamp pub creation_timestamp: u64, /// List of validations @@ -110,7 +110,7 @@ impl Logging { } logs.insert(tx_hash, TransactionLog { tx_hash, - status: Status::Created, + status: PrivateTxStatus::Created, creation_timestamp: SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(), validators: validator_logs, deployment_timestamp: None, @@ -122,7 +122,7 @@ impl Logging { pub fn signature_added(&self, tx_hash: H256, validator: Address) { let mut logs = self.logs.write(); if let Some(transaction_log) = logs.get_mut(&tx_hash) { - transaction_log.status = Status::Validating; + transaction_log.status = PrivateTxStatus::Validating; if let Some(ref mut validator_log) = transaction_log.validators.iter_mut().find(|log| log.account == validator) { validator_log.validated = true; validator_log.validation_timestamp = Some(SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs()); @@ -134,7 +134,7 @@ impl Logging { pub fn tx_deployed(&self, tx_hash: H256, public_tx_hash: H256) { let mut logs = self.logs.write(); if let Some(log) = logs.get_mut(&tx_hash) { - log.status = Status::Deployed; + log.status = PrivateTxStatus::Deployed; log.deployment_timestamp = Some(SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs()); log.public_tx_hash = Some(public_tx_hash); } @@ -214,7 +214,7 @@ impl Drop for Logging { mod tests { use serde_json; use transaction::{Transaction}; - use super::{TransactionLog, Logging, Status}; + use super::{TransactionLog, Logging, PrivateTxStatus}; #[test] fn private_log_format() { @@ -243,6 +243,6 @@ mod tests { logger.signature_added(hash, "0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1".into()); logger.tx_deployed(hash, hash); let tx_log = logger.tx_log(hash).unwrap(); - assert_eq!(tx_log.status, Status::Deployed); + assert_eq!(tx_log.status, PrivateTxStatus::Deployed); } } \ No newline at end of file diff --git a/rpc/src/v1/impls/private.rs b/rpc/src/v1/impls/private.rs index c3be3f91506..12d0ebc8f49 100644 --- a/rpc/src/v1/impls/private.rs +++ b/rpc/src/v1/impls/private.rs @@ -26,7 +26,8 @@ use types::transaction::SignedTransaction; use jsonrpc_core::{Error}; use v1::types::{Bytes, PrivateTransactionReceipt, TransactionRequest, - BlockNumber, PrivateTransactionReceiptAndTransaction, CallRequest, block_number_to_id}; + BlockNumber, PrivateTransactionReceiptAndTransaction, CallRequest, + block_number_to_id, PrivateTransactionLog}; use v1::traits::Private; use v1::metadata::Metadata; use v1::helpers::{errors, fake_sign}; @@ -119,4 +120,10 @@ impl Private for PrivateClient { let key = client.contract_key_id(&contract_address).map_err(errors::private_message)?; Ok(key) } + + fn private_log(&self, tx_hash: H256) -> Result { + let client = self.unwrap_manager()?; + let log = client.private_log(tx_hash.into()).map_err(|e| errors::private_message(e))?; + Ok(log.into()) + } } diff --git a/rpc/src/v1/traits/private.rs b/rpc/src/v1/traits/private.rs index 732e3914bda..887e7126780 100644 --- a/rpc/src/v1/traits/private.rs +++ b/rpc/src/v1/traits/private.rs @@ -21,7 +21,7 @@ use jsonrpc_core::Error; use jsonrpc_derive::rpc; use v1::types::{Bytes, PrivateTransactionReceipt, BlockNumber, - PrivateTransactionReceiptAndTransaction, CallRequest}; + PrivateTransactionReceiptAndTransaction, CallRequest, PrivateTransactionLog}; /// Private transaction management RPC interface. #[rpc] @@ -44,4 +44,8 @@ pub trait Private { /// Retrieve the id of the key associated with the contract #[rpc(name = "private_contractKey")] fn private_contract_key(&self, H160) -> Result; + + /// Retrieve log information about private transaction + #[rpc(name = "private_log")] + fn private_log(&self, H256) -> Result; } diff --git a/rpc/src/v1/types/mod.rs b/rpc/src/v1/types/mod.rs index a41f49fab18..3518aea56ee 100644 --- a/rpc/src/v1/types/mod.rs +++ b/rpc/src/v1/types/mod.rs @@ -32,6 +32,8 @@ mod histogram; mod index; mod log; mod node_kind; +mod private_receipt; +mod private_log; mod provenance; mod receipt; mod rpc_settings; @@ -43,7 +45,6 @@ mod transaction; mod transaction_request; mod transaction_condition; mod work; -mod private_receipt; mod eip191; pub mod pubsub; @@ -65,6 +66,8 @@ pub use self::histogram::Histogram; pub use self::index::Index; pub use self::log::Log; pub use self::node_kind::{NodeKind, Availability, Capability}; +pub use self::private_receipt::{PrivateTransactionReceipt, PrivateTransactionReceiptAndTransaction}; +pub use self::private_log::PrivateTransactionLog; pub use self::provenance::Origin; pub use self::receipt::Receipt; pub use self::rpc_settings::RpcSettings; @@ -79,7 +82,6 @@ pub use self::transaction::{Transaction, RichRawTransaction, LocalTransactionSta pub use self::transaction_request::TransactionRequest; pub use self::transaction_condition::TransactionCondition; pub use self::work::Work; -pub use self::private_receipt::{PrivateTransactionReceipt, PrivateTransactionReceiptAndTransaction}; // TODO [ToDr] Refactor to a proper type Vec of enums? /// Expected tracing type. diff --git a/rpc/src/v1/types/private_log.rs b/rpc/src/v1/types/private_log.rs new file mode 100644 index 00000000000..bf4f0764f47 --- /dev/null +++ b/rpc/src/v1/types/private_log.rs @@ -0,0 +1,95 @@ +// Copyright 2015-2018 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 . + +use v1::types::{H160, H256}; +use ethcore_private_tx::{TransactionLog as EthTransactionLog, ValidatorLog as EthValidatorLog, PrivateTxStatus as EthStatus}; + +/// Current status of the private transaction +#[derive(Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub enum Status { + /// Private tx was created but no validation received yet + Created, + /// Several validators (but not all) validated the transaction + Validating, + /// All validators validated the private tx + /// Corresponding public tx was created and added into the pool + Deployed, +} + +impl From for Status { + fn from(c: EthStatus) -> Self { + match c { + EthStatus::Created => Status::Created, + EthStatus::Validating => Status::Validating, + EthStatus::Deployed => Status::Deployed, + } + } +} + +/// Information about private tx validation +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ValidatorLog { + /// Account of the validator + pub account: H160, + /// Validation flag + pub validated: bool, + /// Validation timestamp + pub validation_timestamp: Option, +} + +impl From for ValidatorLog { + fn from(r: EthValidatorLog) -> Self { + ValidatorLog { + account: r.account.into(), + validated: r.validated.into(), + validation_timestamp: r.validation_timestamp.into(), + } + } +} + +/// Information about the private transaction +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PrivateTransactionLog { + /// Original signed transaction hash (used as a source for private tx) + pub tx_hash: H256, + /// Current status of the private transaction + pub status: Status, + /// Creation timestamp + pub creation_timestamp: u64, + /// List of validations + pub validators: Vec, + /// Timestamp of the resulting public tx deployment + pub deployment_timestamp: Option, + /// Hash of the resulting public tx + pub public_tx_hash: Option, +} + +impl From for PrivateTransactionLog { + fn from(r: EthTransactionLog) -> Self { + PrivateTransactionLog { + tx_hash: r.tx_hash.into(), + status: r.status.into(), + creation_timestamp: r.creation_timestamp.into(), + validators: r.validators.into_iter().map(Into::into).collect(), + deployment_timestamp: r.deployment_timestamp.into(), + public_tx_hash: r.public_tx_hash.map(Into::into), + } + } +} + From 488624e739dd74679e04d9bd8d24244b81bfaf3e Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Wed, 12 Dec 2018 16:09:35 +0100 Subject: [PATCH 06/23] Correct path name and time validation implemented --- ethcore/private-tx/src/log.rs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/ethcore/private-tx/src/log.rs b/ethcore/private-tx/src/log.rs index 8b738bced2f..03748486111 100644 --- a/ethcore/private-tx/src/log.rs +++ b/ethcore/private-tx/src/log.rs @@ -19,6 +19,7 @@ use ethereum_types::{H256, Address}; use std::collections::{HashMap}; use std::fs::{File}; +use std::path::{PathBuf}; use std::time::{SystemTime, UNIX_EPOCH}; use parking_lot::{RwLock}; @@ -72,7 +73,7 @@ pub struct TransactionLog { /// Private transactions logging pub struct Logging { logs: RwLock>, - logs_dir: Option, + logs_dir: Option, } impl Logging { @@ -80,7 +81,7 @@ impl Logging { pub fn new(logs_dir: Option) -> Self { let logging = Logging { logs: RwLock::new(HashMap::new()), - logs_dir, + logs_dir: logs_dir.map(|dir| PathBuf::from(dir)), }; logging.read_logs(); logging @@ -144,7 +145,7 @@ impl Logging { let log_file = match self.logs_dir { Some(ref path) => { let mut file_path = path.clone(); - file_path.push_str("private_tx.log"); + file_path.push("private_tx.log"); match File::open(&file_path) { Ok(file) => file, Err(err) => { @@ -167,7 +168,7 @@ impl Logging { }; // Drop old logs let current_timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(); - transaction_logs.retain(|tx_log| tx_log.creation_timestamp - current_timestamp < MAX_STORING_TIME); + transaction_logs.retain(|tx_log| current_timestamp - tx_log.creation_timestamp < MAX_STORING_TIME); let mut logs = self.logs.write(); for log in transaction_logs { logs.insert(log.tx_hash, log); @@ -182,18 +183,16 @@ impl Logging { let log_file = match self.logs_dir { Some(ref path) => { let mut file_path = path.clone(); - file_path.push_str("private_tx.log"); - match File::open(&file_path) { - Ok(file) => Some(file), - Err(_) => File::create(&file_path).ok() + file_path.push("private_tx.log"); + match File::create(&file_path) { + Ok(file) => file, + Err(err) => { + trace!(target: "privatetx", "Cannot open logs file for writing: {}", err); + return; + } } } - None => None, - }; - let log_file = match log_file { - Some(file) => file, None => { - error!(target: "privatetx", "Cannot open logs file"); return; } }; From 22c7aff59c3e28e1bf84452de6e2ce2de61fdae3 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Wed, 6 Feb 2019 10:09:56 +0100 Subject: [PATCH 07/23] References for parameters added, redundant cloning reworked --- ethcore/private-tx/src/error.rs | 12 +++++++ ethcore/private-tx/src/lib.rs | 10 +++--- ethcore/private-tx/src/log.rs | 61 ++++++++++++++++++++------------- 3 files changed, 55 insertions(+), 28 deletions(-) diff --git a/ethcore/private-tx/src/error.rs b/ethcore/private-tx/src/error.rs index 17cad9d7775..32af1671f4d 100644 --- a/ethcore/private-tx/src/error.rs +++ b/ethcore/private-tx/src/error.rs @@ -25,6 +25,7 @@ use ethkey::Error as KeyError; use ethkey::crypto::Error as CryptoError; use txpool::VerifiedTransaction; use private_transactions::VerifiedPrivateTransaction; +use serde_json::{Error as SerdeError}; type TxPoolError = txpool::Error<::Hash>; @@ -45,6 +46,9 @@ pub enum Error { /// Crypto error. #[display(fmt = "Crypto Error {}", _0)] Crypto(CryptoError), + /// Serialization error. + #[display(fmt = "Serialization Error {}", _0)] + Json(SerdeError), /// Encryption error. #[display(fmt = "Encryption error. ({})", _0)] Encrypt(String), @@ -99,6 +103,7 @@ pub enum Error { /// Key server URL is not set. #[display(fmt = "Key server URL is not set.")] KeyServerNotSet, + /// Transaction not found in logs. #[display(fmt = "Private transaction not found in logs.")] TxNotFoundInLog, /// VM execution error. @@ -125,6 +130,7 @@ impl error::Error for Error { Error::Decoder(e) => Some(e), Error::Trie(e) => Some(e), Error::TxPool(e) => Some(e), + Error::Json(e) => Some(e), Error::Crypto(e) => Some(e), Error::Execution(e) => Some(e), Error::Key(e) => Some(e), @@ -189,6 +195,12 @@ impl From for Error { } } +impl From for Error { + fn from(err: SerdeError) -> Self { + Error::Json(err).into() + } +} + impl From for Error { fn from(err: EthcoreError) -> Self { Error::Ethcore(err).into() diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 563e2fbfbdb..3f596418509 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -269,7 +269,7 @@ impl Provider { trace!(target: "privatetx", "Hashed effective private state for sender: {:?}", private_state_hash); self.transactions_for_signing.write().add_transaction(private.hash(), signed_transaction, &contract_validators, private_state, contract_nonce)?; self.broadcast_private_transaction(private.hash(), private.rlp_bytes()); - self.logging.private_tx_created(tx_hash, &contract_validators); + self.logging.private_tx_created(&tx_hash, &contract_validators); Ok(Receipt { hash: tx_hash, contract_address: contract, @@ -405,8 +405,8 @@ impl Provider { } } // Store logs - self.logging.signature_added(original_tx_hash, last.1); - self.logging.tx_deployed(original_tx_hash, public_tx_hash); + self.logging.signature_added(&original_tx_hash, &last.1); + self.logging.tx_deployed(&original_tx_hash, &public_tx_hash); // Remove from store for signing if let Err(err) = self.transactions_for_signing.write().remove(&private_hash) { warn!(target: "privatetx", "Failed to remove transaction from signing store, error: {:?}", err); @@ -417,7 +417,7 @@ impl Provider { match self.transactions_for_signing.write().add_signature(&private_hash, signed_tx.signature()) { Ok(_) => { trace!(target: "privatetx", "Signature stored for private transaction"); - self.logging.signature_added(original_tx_hash, last.1); + self.logging.signature_added(&original_tx_hash, &last.1); } Err(err) => { warn!(target: "privatetx", "Failed to add signature to signing store, error: {:?}", err); @@ -691,7 +691,7 @@ impl Provider { /// Retrieves log information about private transaction pub fn private_log(&self, tx_hash: H256) -> Result { - self.logging.tx_log(tx_hash).ok_or(ErrorKind::TxNotFoundInLog.into()) + self.logging.tx_log(&tx_hash).ok_or(ErrorKind::TxNotFoundInLog.into()) } /// Returns private validators for a contract. diff --git a/ethcore/private-tx/src/log.rs b/ethcore/private-tx/src/log.rs index 03748486111..08c257e9edd 100644 --- a/ethcore/private-tx/src/log.rs +++ b/ethcore/private-tx/src/log.rs @@ -22,6 +22,8 @@ use std::fs::{File}; use std::path::{PathBuf}; use std::time::{SystemTime, UNIX_EPOCH}; use parking_lot::{RwLock}; +use serde::ser::{Serializer, SerializeSeq}; +use error::{Error}; /// Maximum amount of stored private transaction logs. const MAX_JOURNAL_LEN: usize = 1000; @@ -83,17 +85,19 @@ impl Logging { logs: RwLock::new(HashMap::new()), logs_dir: logs_dir.map(|dir| PathBuf::from(dir)), }; - logging.read_logs(); + if let Err(err) = logging.read_logs() { + warn!(target: "privatetx", "Cannot read logs: {:?}", err); + } logging } /// Retrieves log for the corresponding tx hash - pub fn tx_log(&self, tx_hash: H256) -> Option { + pub fn tx_log(&self, tx_hash: &H256) -> Option { self.logs.read().get(&tx_hash).cloned() } /// Logs the creation of private transaction - pub fn private_tx_created(&self, tx_hash: H256, validators: &Vec
) { + pub fn private_tx_created<'a>(&self, tx_hash: &H256, validators: &Vec
) { let mut validator_logs = Vec::new(); for account in validators { validator_logs.push(ValidatorLog { @@ -105,12 +109,15 @@ impl Logging { let mut logs = self.logs.write(); if logs.len() > MAX_JOURNAL_LEN { // Remove the oldest log - let mut sorted_logs = logs.values().cloned().collect::>(); - sorted_logs.sort_by(|a, b| a.creation_timestamp.cmp(&b.creation_timestamp)); - logs.remove(&sorted_logs[0].tx_hash); + if let Some(tx_hash) = logs.values() + .min_by(|x, y| x.creation_timestamp.cmp(&y.creation_timestamp)) + .map(|oldest| oldest.tx_hash) + { + logs.remove(&tx_hash); + } } - logs.insert(tx_hash, TransactionLog { - tx_hash, + logs.insert(*tx_hash, TransactionLog { + tx_hash: *tx_hash, status: PrivateTxStatus::Created, creation_timestamp: SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(), validators: validator_logs, @@ -120,11 +127,11 @@ impl Logging { } /// Logs the obtaining of the signature for the private transaction - pub fn signature_added(&self, tx_hash: H256, validator: Address) { + pub fn signature_added(&self, tx_hash: &H256, validator: &Address) { let mut logs = self.logs.write(); if let Some(transaction_log) = logs.get_mut(&tx_hash) { transaction_log.status = PrivateTxStatus::Validating; - if let Some(ref mut validator_log) = transaction_log.validators.iter_mut().find(|log| log.account == validator) { + if let Some(ref mut validator_log) = transaction_log.validators.iter_mut().find(|log| log.account == *validator) { validator_log.validated = true; validator_log.validation_timestamp = Some(SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs()); } @@ -132,16 +139,16 @@ impl Logging { } /// Logs the final deployment of the resulting public transaction - pub fn tx_deployed(&self, tx_hash: H256, public_tx_hash: H256) { + pub fn tx_deployed(&self, tx_hash: &H256, public_tx_hash: &H256) { let mut logs = self.logs.write(); if let Some(log) = logs.get_mut(&tx_hash) { log.status = PrivateTxStatus::Deployed; log.deployment_timestamp = Some(SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs()); - log.public_tx_hash = Some(public_tx_hash); + log.public_tx_hash = Some(*public_tx_hash); } } - fn read_logs(&self) { + fn read_logs(&self) -> Result<(), Error> { let log_file = match self.logs_dir { Some(ref path) => { let mut file_path = path.clone(); @@ -150,20 +157,20 @@ impl Logging { Ok(file) => file, Err(err) => { trace!(target: "privatetx", "Cannot open logs file: {}", err); - return; + bail!("Cannot open logs file: {:?}", err); } } } None => { warn!(target: "privatetx", "Logs path is not defined"); - return; + return Ok(()); } }; let mut transaction_logs: Vec = match serde_json::from_reader(log_file) { Ok(logs) => logs, Err(err) => { error!(target: "privatetx", "Cannot deserialize logs from file: {}", err); - return; + bail!("Cannot deserialize logs from file: {:?}", err); } }; // Drop old logs @@ -173,12 +180,13 @@ impl Logging { for log in transaction_logs { logs.insert(log.tx_hash, log); } + Ok(()) } - fn flush_logs(&self) { + fn flush_logs(&self) -> Result<(), Error> { if self.logs.read().is_empty() { // Do not create empty file - return; + return Ok(()); } let log_file = match self.logs_dir { Some(ref path) => { @@ -188,24 +196,31 @@ impl Logging { Ok(file) => file, Err(err) => { trace!(target: "privatetx", "Cannot open logs file for writing: {}", err); - return; + bail!("Cannot open logs file for writing: {:?}", err); } } } None => { - return; + return Ok(()); } }; - if let Err(err) = serde_json::to_writer(log_file, &self.logs.read().values().cloned().collect::>()) { - error!(target: "privatetx", "Error during logs serialisation: {}", err); + let logs = self.logs.read(); + let mut json = serde_json::Serializer::new(log_file); + let mut json_array = json.serialize_seq(Some(logs.len()))?; + for v in logs.values() { + json_array.serialize_element(v)?; } + json_array.end()?; + Ok(()) } } // Flush all logs on drop impl Drop for Logging { fn drop(&mut self) { - self.flush_logs(); + if let Err(err) = self.flush_logs() { + warn!(target: "privatetx", "Cannot write logs: {:?}", err); + } } } From 7ffcdb0f9c9907c2f735729c36fd2e21a34831ae Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Wed, 6 Feb 2019 10:38:25 +0100 Subject: [PATCH 08/23] References for parameters added, redundant cloning reworked --- ethcore/private-tx/src/log.rs | 141 +++++++++++++++++++++------------- 1 file changed, 86 insertions(+), 55 deletions(-) diff --git a/ethcore/private-tx/src/log.rs b/ethcore/private-tx/src/log.rs index 08c257e9edd..58134497728 100644 --- a/ethcore/private-tx/src/log.rs +++ b/ethcore/private-tx/src/log.rs @@ -21,7 +21,7 @@ use std::collections::{HashMap}; use std::fs::{File}; use std::path::{PathBuf}; use std::time::{SystemTime, UNIX_EPOCH}; -use parking_lot::{RwLock}; +use parking_lot::{RwLock, Mutex}; use serde::ser::{Serializer, SerializeSeq}; use error::{Error}; @@ -72,18 +72,98 @@ pub struct TransactionLog { pub public_tx_hash: Option, } +/// Wrapper other JSON serializer +pub trait LogsSerializer { + /// Read logs from the source + fn read_logs(&self) -> Result, Error>; + /// Write all logs to the source + fn flush_logs(&self, logs: &HashMap) -> Result<(), Error>; +} + +pub struct FileLogsSerializer { + logs_dir: Option, +} + +impl FileLogsSerializer { + pub fn new(logs_dir: Option) -> Self { + FileLogsSerializer { + logs_dir: logs_dir.map(|dir| PathBuf::from(dir)), + } + } +} + +impl LogsSerializer for FileLogsSerializer { + fn read_logs(&self) -> Result, Error> { + let log_file = match self.logs_dir { + Some(ref path) => { + let mut file_path = path.clone(); + file_path.push("private_tx.log"); + match File::open(&file_path) { + Ok(file) => file, + Err(err) => { + trace!(target: "privatetx", "Cannot open logs file: {}", err); + bail!("Cannot open logs file: {:?}", err); + } + } + } + None => { + warn!(target: "privatetx", "Logs path is not defined"); + return Ok(()); + } + }; + let mut transaction_logs: Vec = match serde_json::from_reader(log_file) { + Ok(logs) => logs, + Err(err) => { + error!(target: "privatetx", "Cannot deserialize logs from file: {}", err); + bail!("Cannot deserialize logs from file: {:?}", err); + } + }; + Ok(transaction_logs) + } + + fn flush_logs(&self, logs: &HashMap) -> Result<(), Error> { + if self.logs.read().is_empty() { + // Do not create empty file + return Ok(()); + } + let log_file = match self.logs_dir { + Some(ref path) => { + let mut file_path = path.clone(); + file_path.push("private_tx.log"); + match File::create(&file_path) { + Ok(file) => file, + Err(err) => { + trace!(target: "privatetx", "Cannot open logs file for writing: {}", err); + bail!("Cannot open logs file for writing: {:?}", err); + } + } + } + None => { + return Ok(()); + } + }; + let mut json = serde_json::Serializer::new(log_file); + let mut json_array = json.serialize_seq(Some(logs.len()))?; + for v in logs.values() { + json_array.serialize_element(v)?; + } + json_array.end()?; + Ok(()) + } +} + /// Private transactions logging pub struct Logging { logs: RwLock>, - logs_dir: Option, + logs_serializer: Mutex, } impl Logging { /// Creates the logging object - pub fn new(logs_dir: Option) -> Self { + pub fn new(serializer: FileLogsSerializer) -> Self { let logging = Logging { logs: RwLock::new(HashMap::new()), - logs_dir: logs_dir.map(|dir| PathBuf::from(dir)), + logs_serializer: Mutex::new(serializer), }; if let Err(err) = logging.read_logs() { warn!(target: "privatetx", "Cannot read logs: {:?}", err); @@ -149,30 +229,7 @@ impl Logging { } fn read_logs(&self) -> Result<(), Error> { - let log_file = match self.logs_dir { - Some(ref path) => { - let mut file_path = path.clone(); - file_path.push("private_tx.log"); - match File::open(&file_path) { - Ok(file) => file, - Err(err) => { - trace!(target: "privatetx", "Cannot open logs file: {}", err); - bail!("Cannot open logs file: {:?}", err); - } - } - } - None => { - warn!(target: "privatetx", "Logs path is not defined"); - return Ok(()); - } - }; - let mut transaction_logs: Vec = match serde_json::from_reader(log_file) { - Ok(logs) => logs, - Err(err) => { - error!(target: "privatetx", "Cannot deserialize logs from file: {}", err); - bail!("Cannot deserialize logs from file: {:?}", err); - } - }; + let mut transaction_logs = self.logs_serializer.lock().read_logs()?; // Drop old logs let current_timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(); transaction_logs.retain(|tx_log| current_timestamp - tx_log.creation_timestamp < MAX_STORING_TIME); @@ -184,34 +241,8 @@ impl Logging { } fn flush_logs(&self) -> Result<(), Error> { - if self.logs.read().is_empty() { - // Do not create empty file - return Ok(()); - } - let log_file = match self.logs_dir { - Some(ref path) => { - let mut file_path = path.clone(); - file_path.push("private_tx.log"); - match File::create(&file_path) { - Ok(file) => file, - Err(err) => { - trace!(target: "privatetx", "Cannot open logs file for writing: {}", err); - bail!("Cannot open logs file for writing: {:?}", err); - } - } - } - None => { - return Ok(()); - } - }; let logs = self.logs.read(); - let mut json = serde_json::Serializer::new(log_file); - let mut json_array = json.serialize_seq(Some(logs.len()))?; - for v in logs.values() { - json_array.serialize_element(v)?; - } - json_array.end()?; - Ok(()) + self.logs_serializer.lock().flush_logs(&logs) } } From 3e267648d4f3e2ff0ebc98b9fc0f1552453ba3c1 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Wed, 6 Feb 2019 14:08:39 +0100 Subject: [PATCH 09/23] Work with json moved to the separate struct --- ethcore/private-tx/src/lib.rs | 4 +- ethcore/private-tx/src/log.rs | 124 +++++++++++++++++++++------------- 2 files changed, 80 insertions(+), 48 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 3f596418509..9447571657b 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -73,7 +73,7 @@ pub use key_server_keys::{KeyProvider, SecretStoreKeys, StoringKeyProvider}; pub use private_transactions::{VerifiedPrivateTransaction, VerificationStore, PrivateTransactionSigningDesc, SigningStore}; pub use messages::{PrivateTransaction, SignedPrivateTransaction}; pub use error::{Error, ErrorKind}; -pub use log::{Logging, TransactionLog, ValidatorLog, PrivateTxStatus}; +pub use log::{Logging, TransactionLog, ValidatorLog, PrivateTxStatus, FileLogsSerializer}; use std::sync::{Arc, Weak}; use std::collections::{HashMap, HashSet, BTreeMap}; @@ -220,7 +220,7 @@ impl Provider { accounts, channel, keys_provider, - logging: Logging::new(config.logs_path), + logging: Logging::new(Box::new(FileLogsSerializer::new(config.logs_path))), } } diff --git a/ethcore/private-tx/src/log.rs b/ethcore/private-tx/src/log.rs index 58134497728..ef99e65dad9 100644 --- a/ethcore/private-tx/src/log.rs +++ b/ethcore/private-tx/src/log.rs @@ -21,7 +21,7 @@ use std::collections::{HashMap}; use std::fs::{File}; use std::path::{PathBuf}; use std::time::{SystemTime, UNIX_EPOCH}; -use parking_lot::{RwLock, Mutex}; +use parking_lot::{RwLock}; use serde::ser::{Serializer, SerializeSeq}; use error::{Error}; @@ -73,9 +73,10 @@ pub struct TransactionLog { } /// Wrapper other JSON serializer -pub trait LogsSerializer { +pub trait LogsSerializer: Send + Sync + 'static { /// Read logs from the source fn read_logs(&self) -> Result, Error>; + /// Write all logs to the source fn flush_logs(&self, logs: &HashMap) -> Result<(), Error>; } @@ -90,15 +91,13 @@ impl FileLogsSerializer { logs_dir: logs_dir.map(|dir| PathBuf::from(dir)), } } -} -impl LogsSerializer for FileLogsSerializer { - fn read_logs(&self) -> Result, Error> { + fn open_file(&self, to_create: bool) -> Result, Error> { let log_file = match self.logs_dir { Some(ref path) => { let mut file_path = path.clone(); file_path.push("private_tx.log"); - match File::open(&file_path) { + match if to_create { File::create(&file_path) } else { File::open(&file_path) } { Ok(file) => file, Err(err) => { trace!(target: "privatetx", "Cannot open logs file: {}", err); @@ -108,46 +107,47 @@ impl LogsSerializer for FileLogsSerializer { } None => { warn!(target: "privatetx", "Logs path is not defined"); - return Ok(()); + return Ok(None); } }; - let mut transaction_logs: Vec = match serde_json::from_reader(log_file) { - Ok(logs) => logs, - Err(err) => { - error!(target: "privatetx", "Cannot deserialize logs from file: {}", err); - bail!("Cannot deserialize logs from file: {:?}", err); + Ok(Some(log_file)) + } +} + +impl LogsSerializer for FileLogsSerializer { + fn read_logs(&self) -> Result, Error> { + let log_file = self.open_file(false)?; + match log_file { + Some(log_file) => { + let transaction_logs: Vec = match serde_json::from_reader(log_file) { + Ok(logs) => logs, + Err(err) => { + error!(target: "privatetx", "Cannot deserialize logs from file: {}", err); + bail!("Cannot deserialize logs from file: {:?}", err); + } + }; + Ok(transaction_logs) } - }; - Ok(transaction_logs) + None => { + Ok(Vec::new()) + } + } } fn flush_logs(&self, logs: &HashMap) -> Result<(), Error> { - if self.logs.read().is_empty() { + if logs.is_empty() { // Do not create empty file return Ok(()); } - let log_file = match self.logs_dir { - Some(ref path) => { - let mut file_path = path.clone(); - file_path.push("private_tx.log"); - match File::create(&file_path) { - Ok(file) => file, - Err(err) => { - trace!(target: "privatetx", "Cannot open logs file for writing: {}", err); - bail!("Cannot open logs file for writing: {:?}", err); - } - } - } - None => { - return Ok(()); + let log_file = self.open_file(true)?; + if let Some(log_file) = log_file { + let mut json = serde_json::Serializer::new(log_file); + let mut json_array = json.serialize_seq(Some(logs.len()))?; + for v in logs.values() { + json_array.serialize_element(v)?; } - }; - let mut json = serde_json::Serializer::new(log_file); - let mut json_array = json.serialize_seq(Some(logs.len()))?; - for v in logs.values() { - json_array.serialize_element(v)?; + json_array.end()?; } - json_array.end()?; Ok(()) } } @@ -155,15 +155,15 @@ impl LogsSerializer for FileLogsSerializer { /// Private transactions logging pub struct Logging { logs: RwLock>, - logs_serializer: Mutex, + logs_serializer: Box, } impl Logging { /// Creates the logging object - pub fn new(serializer: FileLogsSerializer) -> Self { + pub fn new(logs_serializer: Box) -> Self { let logging = Logging { logs: RwLock::new(HashMap::new()), - logs_serializer: Mutex::new(serializer), + logs_serializer, }; if let Err(err) = logging.read_logs() { warn!(target: "privatetx", "Cannot read logs: {:?}", err); @@ -229,7 +229,7 @@ impl Logging { } fn read_logs(&self) -> Result<(), Error> { - let mut transaction_logs = self.logs_serializer.lock().read_logs()?; + let mut transaction_logs = self.logs_serializer.read_logs()?; // Drop old logs let current_timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(); transaction_logs.retain(|tx_log| current_timestamp - tx_log.creation_timestamp < MAX_STORING_TIME); @@ -242,7 +242,7 @@ impl Logging { fn flush_logs(&self) -> Result<(), Error> { let logs = self.logs.read(); - self.logs_serializer.lock().flush_logs(&logs) + self.logs_serializer.flush_logs(&logs) } } @@ -258,8 +258,40 @@ impl Drop for Logging { #[cfg(test)] mod tests { use serde_json; + use error::{Error}; + use ethereum_types::{H256}; + use std::collections::{HashMap}; use transaction::{Transaction}; - use super::{TransactionLog, Logging, PrivateTxStatus}; + use parking_lot::{RwLock}; + use super::{TransactionLog, Logging, PrivateTxStatus, LogsSerializer}; + + struct StringLogSerializer { + string_log: RwLock, + } + + impl StringLogSerializer { + fn new(source: String) -> Self { + StringLogSerializer { + string_log: RwLock::new(source), + } + } + } + + impl LogsSerializer for StringLogSerializer { + fn read_logs(&self) -> Result, Error> { + let source = self.string_log.read(); + if source.is_empty() { + return Ok(Vec::new()) + } + let logs = serde_json::from_str(&source).unwrap(); + Ok(logs) + } + + fn flush_logs(&self, logs: &HashMap) -> Result<(), Error> { + *self.string_log.write() = serde_json::to_string(logs)?; + Ok(()) + } + } #[test] fn private_log_format() { @@ -281,13 +313,13 @@ mod tests { #[test] fn private_log_status() { - let logger = Logging::new(None); + let logger = Logging::new(Box::new(StringLogSerializer::new("".into()))); let private_tx = Transaction::default(); let hash = private_tx.hash(None); - logger.private_tx_created(hash, &vec!["0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1".into()]); - logger.signature_added(hash, "0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1".into()); - logger.tx_deployed(hash, hash); - let tx_log = logger.tx_log(hash).unwrap(); + logger.private_tx_created(&hash, &vec!["0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1".into()]); + logger.signature_added(&hash, &"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1".into()); + logger.tx_deployed(&hash, &hash); + let tx_log = logger.tx_log(&hash).unwrap(); assert_eq!(tx_log.status, PrivateTxStatus::Deployed); } } \ No newline at end of file From d2f8da9a4ec946e2699f88dc8d1ca1156ac83aaf Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Fri, 8 Feb 2019 16:57:03 +0100 Subject: [PATCH 10/23] Serialization test added --- ethcore/private-tx/src/lib.rs | 4 +- ethcore/private-tx/src/log.rs | 107 ++++++++++++++++++++++++++++++---- 2 files changed, 99 insertions(+), 12 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 9447571657b..36295b411ef 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -73,7 +73,7 @@ pub use key_server_keys::{KeyProvider, SecretStoreKeys, StoringKeyProvider}; pub use private_transactions::{VerifiedPrivateTransaction, VerificationStore, PrivateTransactionSigningDesc, SigningStore}; pub use messages::{PrivateTransaction, SignedPrivateTransaction}; pub use error::{Error, ErrorKind}; -pub use log::{Logging, TransactionLog, ValidatorLog, PrivateTxStatus, FileLogsSerializer}; +pub use log::{Logging, TransactionLog, ValidatorLog, PrivateTxStatus, FileLogsSerializer, SystemTimestamp}; use std::sync::{Arc, Weak}; use std::collections::{HashMap, HashSet, BTreeMap}; @@ -220,7 +220,7 @@ impl Provider { accounts, channel, keys_provider, - logging: Logging::new(Box::new(FileLogsSerializer::new(config.logs_path))), + logging: Logging::new(Arc::new(FileLogsSerializer::new(config.logs_path)), Box::new(SystemTimestamp {})), } } diff --git a/ethcore/private-tx/src/log.rs b/ethcore/private-tx/src/log.rs index ef99e65dad9..5c0e56ddb43 100644 --- a/ethcore/private-tx/src/log.rs +++ b/ethcore/private-tx/src/log.rs @@ -20,6 +20,7 @@ use ethereum_types::{H256, Address}; use std::collections::{HashMap}; use std::fs::{File}; use std::path::{PathBuf}; +use std::sync::{Arc}; use std::time::{SystemTime, UNIX_EPOCH}; use parking_lot::{RwLock}; use serde::ser::{Serializer, SerializeSeq}; @@ -152,18 +153,35 @@ impl LogsSerializer for FileLogsSerializer { } } +/// Timestamp source for logs +pub trait TimestampSource: Send + Sync + 'static { + /// Returns current timestamp in seconds + fn current_timestamp(&self) -> u64; +} + +/// Timesource on the base of system time +pub struct SystemTimestamp {} + +impl TimestampSource for SystemTimestamp { + fn current_timestamp(&self) -> u64 { + SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs() + } +} + /// Private transactions logging pub struct Logging { logs: RwLock>, - logs_serializer: Box, + logs_serializer: Arc, + timestamp_source: Box, } impl Logging { /// Creates the logging object - pub fn new(logs_serializer: Box) -> Self { + pub fn new(logs_serializer: Arc, timestamp_source: Box) -> Self { let logging = Logging { logs: RwLock::new(HashMap::new()), logs_serializer, + timestamp_source, }; if let Err(err) = logging.read_logs() { warn!(target: "privatetx", "Cannot read logs: {:?}", err); @@ -199,7 +217,7 @@ impl Logging { logs.insert(*tx_hash, TransactionLog { tx_hash: *tx_hash, status: PrivateTxStatus::Created, - creation_timestamp: SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(), + creation_timestamp: self.timestamp_source.current_timestamp(), validators: validator_logs, deployment_timestamp: None, public_tx_hash: None, @@ -213,7 +231,7 @@ impl Logging { transaction_log.status = PrivateTxStatus::Validating; if let Some(ref mut validator_log) = transaction_log.validators.iter_mut().find(|log| log.account == *validator) { validator_log.validated = true; - validator_log.validation_timestamp = Some(SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs()); + validator_log.validation_timestamp = Some(self.timestamp_source.current_timestamp()); } } } @@ -223,7 +241,7 @@ impl Logging { let mut logs = self.logs.write(); if let Some(log) = logs.get_mut(&tx_hash) { log.status = PrivateTxStatus::Deployed; - log.deployment_timestamp = Some(SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs()); + log.deployment_timestamp = Some(self.timestamp_source.current_timestamp()); log.public_tx_hash = Some(*public_tx_hash); } } @@ -231,7 +249,7 @@ impl Logging { fn read_logs(&self) -> Result<(), Error> { let mut transaction_logs = self.logs_serializer.read_logs()?; // Drop old logs - let current_timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(); + let current_timestamp = self.timestamp_source.current_timestamp(); transaction_logs.retain(|tx_log| current_timestamp - tx_log.creation_timestamp < MAX_STORING_TIME); let mut logs = self.logs.write(); for log in transaction_logs { @@ -260,10 +278,11 @@ mod tests { use serde_json; use error::{Error}; use ethereum_types::{H256}; - use std::collections::{HashMap}; + use std::collections::{HashMap, BTreeMap}; + use std::sync::{Arc}; use transaction::{Transaction}; use parking_lot::{RwLock}; - use super::{TransactionLog, Logging, PrivateTxStatus, LogsSerializer}; + use super::{TransactionLog, Logging, PrivateTxStatus, LogsSerializer, TimestampSource}; struct StringLogSerializer { string_log: RwLock, @@ -275,6 +294,11 @@ mod tests { string_log: RwLock::new(source), } } + + fn log(&self) -> String { + let log = self.string_log.read(); + log.clone() + } } impl LogsSerializer for StringLogSerializer { @@ -288,11 +312,25 @@ mod tests { } fn flush_logs(&self, logs: &HashMap) -> Result<(), Error> { - *self.string_log.write() = serde_json::to_string(logs)?; + // Sort logs in order to have the same order + let sorted_logs: BTreeMap<&H256, &TransactionLog> = logs.iter().collect(); + *self.string_log.write() = serde_json::to_string(&sorted_logs.values().collect::>())?; Ok(()) } } + struct CounterTimestamp { + counter: RwLock, + } + + impl TimestampSource for CounterTimestamp { + fn current_timestamp(&self) -> u64 { + let current = *self.counter.read(); + *self.counter.write() = current + 1; + current + } + } + #[test] fn private_log_format() { let s = r#"{ @@ -313,7 +351,7 @@ mod tests { #[test] fn private_log_status() { - let logger = Logging::new(Box::new(StringLogSerializer::new("".into()))); + let logger = Logging::new(Arc::new(StringLogSerializer::new("".into())), Box::new(CounterTimestamp { counter: RwLock::new(0), })); let private_tx = Transaction::default(); let hash = private_tx.hash(None); logger.private_tx_created(&hash, &vec!["0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1".into()]); @@ -322,4 +360,53 @@ mod tests { let tx_log = logger.tx_log(&hash).unwrap(); assert_eq!(tx_log.status, PrivateTxStatus::Deployed); } + + #[test] + fn serialization() { + let initial = r#"[{ + "tx_hash":"0x64f648ca7ae7f4138014f860ae56164d8d5732969b1cea54d8be9d144d8aa6f6", + "status":"Deployed", + "creation_timestamp":0, + "validators":[{ + "account":"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1", + "validated":true, + "validation_timestamp":1 + }], + "deployment_timestamp":2, + "public_tx_hash":"0x69b9c691ede7993effbcc88911c309af1c82be67b04b3882dd446b808ae146da" + }]"#; + let serializer = Arc::new(StringLogSerializer::new(initial.into())); + let logger = Logging::new(serializer.clone(), Box::new(CounterTimestamp { counter: RwLock::new(5) })); + let hash: H256 = "0x63c715e88f7291e66069302f6fcbb4f28a19ef5d7cbd1832d0c01e221c0061c6".into(); + logger.private_tx_created(&hash, &vec!["0x7ffbe3512782069be388f41be4d8eb350672d3a5".into()]); + logger.signature_added(&hash, &"0x7ffbe3512782069be388f41be4d8eb350672d3a5".into()); + logger.tx_deployed(&hash, &"0xde2209a8635b9cab9eceb67928b217c70ab53f6498e5144492ec01e6f43547d7".into()); + drop(logger); + let should_be_final = r#"[ + { + "tx_hash":"0x63c715e88f7291e66069302f6fcbb4f28a19ef5d7cbd1832d0c01e221c0061c6", + "status":"Deployed", + "creation_timestamp":6, + "validators":[{ + "account":"0x7ffbe3512782069be388f41be4d8eb350672d3a5", + "validated":true, + "validation_timestamp":7 + }], + "deployment_timestamp":8, + "public_tx_hash":"0xde2209a8635b9cab9eceb67928b217c70ab53f6498e5144492ec01e6f43547d7"}, + { + "tx_hash":"0x64f648ca7ae7f4138014f860ae56164d8d5732969b1cea54d8be9d144d8aa6f6", + "status":"Deployed", + "creation_timestamp":0, + "validators":[{ + "account":"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1", + "validated":true, + "validation_timestamp":1 + }], + "deployment_timestamp":2, + "public_tx_hash":"0x69b9c691ede7993effbcc88911c309af1c82be67b04b3882dd446b808ae146da" + }]"#; + let should_be_final = &should_be_final.replace("\t", "").replace("\n", ""); + assert_eq!(serializer.log(), *should_be_final); + } } \ No newline at end of file From 841548db2e3cd92da147b9a485caea4d66849dfb Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Fri, 8 Feb 2019 18:10:23 +0100 Subject: [PATCH 11/23] Fixed build after the merge with head --- ethcore/private-tx/src/lib.rs | 2 +- ethcore/private-tx/src/log.rs | 2 +- ethcore/private-tx/tests/private_contract.rs | 1 + parity/configuration.rs | 1 - 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 36295b411ef..9c986f15d47 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -386,7 +386,7 @@ impl Provider { // Sign and add it to the queue let chain_id = desc.original_transaction.chain_id(); let public_tx_hash = public_tx.hash(chain_id); - let signature = self.accounts.sign(signer_account, hash)?; + let signature = self.accounts.sign(signer_account, public_tx_hash)?; let signed = SignedTransaction::new(public_tx.with_signature(signature, chain_id))?; match self.miner.import_own_transaction(&*self.client, signed.into()) { Ok(_) => trace!(target: "privatetx", "Public transaction added to queue"), diff --git a/ethcore/private-tx/src/log.rs b/ethcore/private-tx/src/log.rs index 5c0e56ddb43..860ec9fdd9b 100644 --- a/ethcore/private-tx/src/log.rs +++ b/ethcore/private-tx/src/log.rs @@ -280,7 +280,7 @@ mod tests { use ethereum_types::{H256}; use std::collections::{HashMap, BTreeMap}; use std::sync::{Arc}; - use transaction::{Transaction}; + use types::transaction::{Transaction}; use parking_lot::{RwLock}; use super::{TransactionLog, Logging, PrivateTxStatus, LogsSerializer, TimestampSource}; diff --git a/ethcore/private-tx/tests/private_contract.rs b/ethcore/private-tx/tests/private_contract.rs index bf40ada0ec4..5d760fe0706 100644 --- a/ethcore/private-tx/tests/private_contract.rs +++ b/ethcore/private-tx/tests/private_contract.rs @@ -194,6 +194,7 @@ fn call_other_private_contract() { let config = ProviderConfig{ validator_accounts: vec![key3.address(), key4.address()], signer_account: None, + logs_path: None, }; let io = ethcore_io::IoChannel::disconnected(); diff --git a/parity/configuration.rs b/parity/configuration.rs index bef02ca25cd..216a3a1cadf 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -1458,7 +1458,6 @@ mod tests { private_provider_conf: ProviderConfig { validator_accounts: Default::default(), signer_account: Default::default(), - passwords: Default::default(), logs_path: Some(Directories::default().base), }, private_encryptor_conf: Default::default(), From d00ca5c56c195b47c91e04b1e3ace7d395ac60fc Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Thu, 28 Mar 2019 15:16:29 +0100 Subject: [PATCH 12/23] Documentation for methods fixed, redundant field removed --- ethcore/private-tx/src/lib.rs | 4 +-- ethcore/private-tx/src/log.rs | 62 ++++++++++++++------------------- rpc/src/v1/types/private_log.rs | 5 +-- 3 files changed, 30 insertions(+), 41 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 9c986f15d47..3dcc8bec05e 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -220,7 +220,7 @@ impl Provider { accounts, channel, keys_provider, - logging: Logging::new(Arc::new(FileLogsSerializer::new(config.logs_path)), Box::new(SystemTimestamp {})), + logging: Logging::new(Arc::new(FileLogsSerializer::with_path(config.logs_path)), Box::new(SystemTimestamp {})), } } @@ -691,7 +691,7 @@ impl Provider { /// Retrieves log information about private transaction pub fn private_log(&self, tx_hash: H256) -> Result { - self.logging.tx_log(&tx_hash).ok_or(ErrorKind::TxNotFoundInLog.into()) + self.logging.tx_log(&tx_hash).ok_or_else(|| ErrorKind::TxNotFoundInLog.into()) } /// Returns private validators for a contract. diff --git a/ethcore/private-tx/src/log.rs b/ethcore/private-tx/src/log.rs index 860ec9fdd9b..193c3f3d6a4 100644 --- a/ethcore/private-tx/src/log.rs +++ b/ethcore/private-tx/src/log.rs @@ -17,20 +17,20 @@ //! Private transactions logs. use ethereum_types::{H256, Address}; -use std::collections::{HashMap}; -use std::fs::{File}; -use std::path::{PathBuf}; -use std::sync::{Arc}; +use std::collections::HashMap; +use std::fs::File; +use std::path::PathBuf; +use std::sync::Arc; use std::time::{SystemTime, UNIX_EPOCH}; -use parking_lot::{RwLock}; +use parking_lot::RwLock; use serde::ser::{Serializer, SerializeSeq}; -use error::{Error}; +use error::Error; /// Maximum amount of stored private transaction logs. const MAX_JOURNAL_LEN: usize = 1000; /// Maximum period for storing private transaction logs. -/// Older logs will not be processed, 20 days +/// Logs older than 20 days will not be processed const MAX_STORING_TIME: u64 = 60 * 60 * 24 * 20; /// Current status of the private transaction @@ -40,7 +40,7 @@ pub enum PrivateTxStatus { Created, /// Several validators (but not all) validated the transaction Validating, - /// All validators validated the private tx + /// All validators has validated the private tx /// Corresponding public tx was created and added into the pool Deployed, } @@ -50,9 +50,7 @@ pub enum PrivateTxStatus { pub struct ValidatorLog { /// Account of the validator pub account: Address, - /// Validation flag - pub validated: bool, - /// Validation timestamp + /// Validation timestamp, None if the transaction is not validated pub validation_timestamp: Option, } @@ -87,7 +85,7 @@ pub struct FileLogsSerializer { } impl FileLogsSerializer { - pub fn new(logs_dir: Option) -> Self { + pub fn with_path(logs_dir: Option) -> Self { FileLogsSerializer { logs_dir: logs_dir.map(|dir| PathBuf::from(dir)), } @@ -154,16 +152,16 @@ impl LogsSerializer for FileLogsSerializer { } /// Timestamp source for logs -pub trait TimestampSource: Send + Sync + 'static { +pub trait CurrentTime: Send + Sync + 'static { /// Returns current timestamp in seconds - fn current_timestamp(&self) -> u64; + fn timestamp(&self) -> u64; } /// Timesource on the base of system time pub struct SystemTimestamp {} -impl TimestampSource for SystemTimestamp { - fn current_timestamp(&self) -> u64 { +impl CurrentTime for SystemTimestamp { + fn timestamp(&self) -> u64 { SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs() } } @@ -172,12 +170,12 @@ impl TimestampSource for SystemTimestamp { pub struct Logging { logs: RwLock>, logs_serializer: Arc, - timestamp_source: Box, + timestamp_source: Box, } impl Logging { /// Creates the logging object - pub fn new(logs_serializer: Arc, timestamp_source: Box) -> Self { + pub fn new(logs_serializer: Arc, timestamp_source: Box) -> Self { let logging = Logging { logs: RwLock::new(HashMap::new()), logs_serializer, @@ -194,13 +192,12 @@ impl Logging { self.logs.read().get(&tx_hash).cloned() } - /// Logs the creation of private transaction - pub fn private_tx_created<'a>(&self, tx_hash: &H256, validators: &Vec
) { + /// Logs the creation of the private transaction + pub fn private_tx_created(&self, tx_hash: &H256, validators: &[Address]) { let mut validator_logs = Vec::new(); for account in validators { validator_logs.push(ValidatorLog { account: *account, - validated: false, validation_timestamp: None, }); } @@ -217,21 +214,20 @@ impl Logging { logs.insert(*tx_hash, TransactionLog { tx_hash: *tx_hash, status: PrivateTxStatus::Created, - creation_timestamp: self.timestamp_source.current_timestamp(), + creation_timestamp: self.timestamp_source.timestamp(), validators: validator_logs, deployment_timestamp: None, public_tx_hash: None, }); } - /// Logs the obtaining of the signature for the private transaction + /// Logs the validation of the private transaction by one of its validators pub fn signature_added(&self, tx_hash: &H256, validator: &Address) { let mut logs = self.logs.write(); if let Some(transaction_log) = logs.get_mut(&tx_hash) { - transaction_log.status = PrivateTxStatus::Validating; if let Some(ref mut validator_log) = transaction_log.validators.iter_mut().find(|log| log.account == *validator) { - validator_log.validated = true; - validator_log.validation_timestamp = Some(self.timestamp_source.current_timestamp()); + transaction_log.status = PrivateTxStatus::Validating; + validator_log.validation_timestamp = Some(self.timestamp_source.timestamp()); } } } @@ -241,7 +237,7 @@ impl Logging { let mut logs = self.logs.write(); if let Some(log) = logs.get_mut(&tx_hash) { log.status = PrivateTxStatus::Deployed; - log.deployment_timestamp = Some(self.timestamp_source.current_timestamp()); + log.deployment_timestamp = Some(self.timestamp_source.timestamp()); log.public_tx_hash = Some(*public_tx_hash); } } @@ -249,7 +245,7 @@ impl Logging { fn read_logs(&self) -> Result<(), Error> { let mut transaction_logs = self.logs_serializer.read_logs()?; // Drop old logs - let current_timestamp = self.timestamp_source.current_timestamp(); + let current_timestamp = self.timestamp_source.timestamp(); transaction_logs.retain(|tx_log| current_timestamp - tx_log.creation_timestamp < MAX_STORING_TIME); let mut logs = self.logs.write(); for log in transaction_logs { @@ -282,7 +278,7 @@ mod tests { use std::sync::{Arc}; use types::transaction::{Transaction}; use parking_lot::{RwLock}; - use super::{TransactionLog, Logging, PrivateTxStatus, LogsSerializer, TimestampSource}; + use super::{TransactionLog, Logging, PrivateTxStatus, LogsSerializer, CurrentTime}; struct StringLogSerializer { string_log: RwLock, @@ -323,8 +319,8 @@ mod tests { counter: RwLock, } - impl TimestampSource for CounterTimestamp { - fn current_timestamp(&self) -> u64 { + impl CurrentTime for CounterTimestamp { + fn timestamp(&self) -> u64 { let current = *self.counter.read(); *self.counter.write() = current + 1; current @@ -339,7 +335,6 @@ mod tests { "creation_timestamp":1544528180, "validators":[{ "account":"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1", - "validated":true, "validation_timestamp":1544528181 }], "deployment_timestamp":1544528181, @@ -369,7 +364,6 @@ mod tests { "creation_timestamp":0, "validators":[{ "account":"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1", - "validated":true, "validation_timestamp":1 }], "deployment_timestamp":2, @@ -389,7 +383,6 @@ mod tests { "creation_timestamp":6, "validators":[{ "account":"0x7ffbe3512782069be388f41be4d8eb350672d3a5", - "validated":true, "validation_timestamp":7 }], "deployment_timestamp":8, @@ -400,7 +393,6 @@ mod tests { "creation_timestamp":0, "validators":[{ "account":"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1", - "validated":true, "validation_timestamp":1 }], "deployment_timestamp":2, diff --git a/rpc/src/v1/types/private_log.rs b/rpc/src/v1/types/private_log.rs index bf4f0764f47..c04047e5804 100644 --- a/rpc/src/v1/types/private_log.rs +++ b/rpc/src/v1/types/private_log.rs @@ -46,9 +46,7 @@ impl From for Status { pub struct ValidatorLog { /// Account of the validator pub account: H160, - /// Validation flag - pub validated: bool, - /// Validation timestamp + /// Validation timestamp, None, if the transaction is not validated yet pub validation_timestamp: Option, } @@ -56,7 +54,6 @@ impl From for ValidatorLog { fn from(r: EthValidatorLog) -> Self { ValidatorLog { account: r.account.into(), - validated: r.validated.into(), validation_timestamp: r.validation_timestamp.into(), } } From 23f279cc76e40511a8246a3dbe96d29dc03deadc Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Thu, 28 Mar 2019 17:33:10 +0100 Subject: [PATCH 13/23] Fixed error usages --- ethcore/private-tx/src/lib.rs | 4 ++-- ethcore/private-tx/src/log.rs | 4 ++-- rpc/src/v1/types/private_log.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 3dcc8bec05e..7a32f3b57f8 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -72,7 +72,7 @@ pub use encryptor::{Encryptor, SecretStoreEncryptor, EncryptorConfig, NoopEncryp pub use key_server_keys::{KeyProvider, SecretStoreKeys, StoringKeyProvider}; pub use private_transactions::{VerifiedPrivateTransaction, VerificationStore, PrivateTransactionSigningDesc, SigningStore}; pub use messages::{PrivateTransaction, SignedPrivateTransaction}; -pub use error::{Error, ErrorKind}; +pub use error::Error; pub use log::{Logging, TransactionLog, ValidatorLog, PrivateTxStatus, FileLogsSerializer, SystemTimestamp}; use std::sync::{Arc, Weak}; @@ -691,7 +691,7 @@ impl Provider { /// Retrieves log information about private transaction pub fn private_log(&self, tx_hash: H256) -> Result { - self.logging.tx_log(&tx_hash).ok_or_else(|| ErrorKind::TxNotFoundInLog.into()) + self.logging.tx_log(&tx_hash).ok_or_else(|| Error::TxNotFoundInLog.into()) } /// Returns private validators for a contract. diff --git a/ethcore/private-tx/src/log.rs b/ethcore/private-tx/src/log.rs index 193c3f3d6a4..49c661aabd2 100644 --- a/ethcore/private-tx/src/log.rs +++ b/ethcore/private-tx/src/log.rs @@ -100,7 +100,7 @@ impl FileLogsSerializer { Ok(file) => file, Err(err) => { trace!(target: "privatetx", "Cannot open logs file: {}", err); - bail!("Cannot open logs file: {:?}", err); + return Err(format!("Cannot open logs file: {:?}", err).into()); } } } @@ -122,7 +122,7 @@ impl LogsSerializer for FileLogsSerializer { Ok(logs) => logs, Err(err) => { error!(target: "privatetx", "Cannot deserialize logs from file: {}", err); - bail!("Cannot deserialize logs from file: {:?}", err); + return Err(format!("Cannot deserialize logs from file: {:?}", err).into()); } }; Ok(transaction_logs) diff --git a/rpc/src/v1/types/private_log.rs b/rpc/src/v1/types/private_log.rs index c04047e5804..4bf7389f985 100644 --- a/rpc/src/v1/types/private_log.rs +++ b/rpc/src/v1/types/private_log.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use v1::types::{H160, H256}; +use ethereum_types::{H160, H256}; use ethcore_private_tx::{TransactionLog as EthTransactionLog, ValidatorLog as EthValidatorLog, PrivateTxStatus as EthStatus}; /// Current status of the private transaction From 9cff57e798b34dfa3ee2a8f8d9975f8cda88ed86 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Thu, 4 Apr 2019 12:40:31 +0200 Subject: [PATCH 14/23] Timestamp trait implemented for std struct --- ethcore/private-tx/src/lib.rs | 5 +++-- ethcore/private-tx/src/log.rs | 9 ++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 7a32f3b57f8..7c7df4ecb57 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -73,9 +73,10 @@ pub use key_server_keys::{KeyProvider, SecretStoreKeys, StoringKeyProvider}; pub use private_transactions::{VerifiedPrivateTransaction, VerificationStore, PrivateTransactionSigningDesc, SigningStore}; pub use messages::{PrivateTransaction, SignedPrivateTransaction}; pub use error::Error; -pub use log::{Logging, TransactionLog, ValidatorLog, PrivateTxStatus, FileLogsSerializer, SystemTimestamp}; +pub use log::{Logging, TransactionLog, ValidatorLog, PrivateTxStatus, FileLogsSerializer}; use std::sync::{Arc, Weak}; +use std::time::SystemTime; use std::collections::{HashMap, HashSet, BTreeMap}; use ethereum_types::{H128, H256, U256, Address}; use hash::keccak; @@ -220,7 +221,7 @@ impl Provider { accounts, channel, keys_provider, - logging: Logging::new(Arc::new(FileLogsSerializer::with_path(config.logs_path)), Box::new(SystemTimestamp {})), + logging: Logging::new(Arc::new(FileLogsSerializer::with_path(config.logs_path)), Box::new(SystemTime::now())), } } diff --git a/ethcore/private-tx/src/log.rs b/ethcore/private-tx/src/log.rs index 49c661aabd2..47feea7f8fb 100644 --- a/ethcore/private-tx/src/log.rs +++ b/ethcore/private-tx/src/log.rs @@ -158,13 +158,20 @@ pub trait CurrentTime: Send + Sync + 'static { } /// Timesource on the base of system time +impl CurrentTime for SystemTime { + fn timestamp(&self) -> u64 { + self.duration_since(UNIX_EPOCH).unwrap_or_default().as_secs() + } +} + +/*/// Timesource on the base of system time pub struct SystemTimestamp {} impl CurrentTime for SystemTimestamp { fn timestamp(&self) -> u64 { SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs() } -} +}*/ /// Private transactions logging pub struct Logging { From 3b22ab8b9d0183f7355f6e6ad3a14ef005b57d87 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Wed, 10 Apr 2019 10:57:08 +0200 Subject: [PATCH 15/23] Commented code removed --- ethcore/private-tx/src/log.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/ethcore/private-tx/src/log.rs b/ethcore/private-tx/src/log.rs index 47feea7f8fb..2e456f7d4d3 100644 --- a/ethcore/private-tx/src/log.rs +++ b/ethcore/private-tx/src/log.rs @@ -164,15 +164,6 @@ impl CurrentTime for SystemTime { } } -/*/// Timesource on the base of system time -pub struct SystemTimestamp {} - -impl CurrentTime for SystemTimestamp { - fn timestamp(&self) -> u64 { - SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs() - } -}*/ - /// Private transactions logging pub struct Logging { logs: RwLock>, From 6c535a0021b09e8d084d00ba34932a1b7ea43f09 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Mon, 6 May 2019 17:05:53 +0200 Subject: [PATCH 16/23] Remove timestamp source, rework serialization test --- ethcore/private-tx/src/lib.rs | 3 +- ethcore/private-tx/src/log.rs | 130 +++++++++++++++------------------- 2 files changed, 58 insertions(+), 75 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 7c7df4ecb57..24997108191 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -76,7 +76,6 @@ pub use error::Error; pub use log::{Logging, TransactionLog, ValidatorLog, PrivateTxStatus, FileLogsSerializer}; use std::sync::{Arc, Weak}; -use std::time::SystemTime; use std::collections::{HashMap, HashSet, BTreeMap}; use ethereum_types::{H128, H256, U256, Address}; use hash::keccak; @@ -221,7 +220,7 @@ impl Provider { accounts, channel, keys_provider, - logging: Logging::new(Arc::new(FileLogsSerializer::with_path(config.logs_path)), Box::new(SystemTime::now())), + logging: Logging::new(Arc::new(FileLogsSerializer::with_path(config.logs_path))), } } diff --git a/ethcore/private-tx/src/log.rs b/ethcore/private-tx/src/log.rs index 2e456f7d4d3..25b4336951c 100644 --- a/ethcore/private-tx/src/log.rs +++ b/ethcore/private-tx/src/log.rs @@ -46,7 +46,7 @@ pub enum PrivateTxStatus { } /// Information about private tx validation -#[derive(Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct ValidatorLog { /// Account of the validator pub account: Address, @@ -54,8 +54,15 @@ pub struct ValidatorLog { pub validation_timestamp: Option, } +#[cfg(test)] +impl PartialEq for ValidatorLog { + fn eq(&self, other: &Self) -> bool { + self.account == other.account + } +} + /// Information about the private transaction -#[derive(Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct TransactionLog { /// Original signed transaction hash (used as a source for private tx) pub tx_hash: H256, @@ -71,6 +78,16 @@ pub struct TransactionLog { pub public_tx_hash: Option, } +#[cfg(test)] +impl PartialEq for TransactionLog { + fn eq(&self, other: &Self) -> bool { + self.tx_hash == other.tx_hash && + self.status == other.status && + self.validators == other.validators && + self.public_tx_hash == other.public_tx_hash + } +} + /// Wrapper other JSON serializer pub trait LogsSerializer: Send + Sync + 'static { /// Read logs from the source @@ -151,33 +168,18 @@ impl LogsSerializer for FileLogsSerializer { } } -/// Timestamp source for logs -pub trait CurrentTime: Send + Sync + 'static { - /// Returns current timestamp in seconds - fn timestamp(&self) -> u64; -} - -/// Timesource on the base of system time -impl CurrentTime for SystemTime { - fn timestamp(&self) -> u64 { - self.duration_since(UNIX_EPOCH).unwrap_or_default().as_secs() - } -} - /// Private transactions logging pub struct Logging { logs: RwLock>, logs_serializer: Arc, - timestamp_source: Box, } impl Logging { /// Creates the logging object - pub fn new(logs_serializer: Arc, timestamp_source: Box) -> Self { + pub fn new(logs_serializer: Arc) -> Self { let logging = Logging { logs: RwLock::new(HashMap::new()), logs_serializer, - timestamp_source, }; if let Err(err) = logging.read_logs() { warn!(target: "privatetx", "Cannot read logs: {:?}", err); @@ -212,7 +214,7 @@ impl Logging { logs.insert(*tx_hash, TransactionLog { tx_hash: *tx_hash, status: PrivateTxStatus::Created, - creation_timestamp: self.timestamp_source.timestamp(), + creation_timestamp: SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(), validators: validator_logs, deployment_timestamp: None, public_tx_hash: None, @@ -225,7 +227,7 @@ impl Logging { if let Some(transaction_log) = logs.get_mut(&tx_hash) { if let Some(ref mut validator_log) = transaction_log.validators.iter_mut().find(|log| log.account == *validator) { transaction_log.status = PrivateTxStatus::Validating; - validator_log.validation_timestamp = Some(self.timestamp_source.timestamp()); + validator_log.validation_timestamp = Some(SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs()); } } } @@ -235,7 +237,7 @@ impl Logging { let mut logs = self.logs.write(); if let Some(log) = logs.get_mut(&tx_hash) { log.status = PrivateTxStatus::Deployed; - log.deployment_timestamp = Some(self.timestamp_source.timestamp()); + log.deployment_timestamp = Some(SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs()); log.public_tx_hash = Some(*public_tx_hash); } } @@ -243,7 +245,7 @@ impl Logging { fn read_logs(&self) -> Result<(), Error> { let mut transaction_logs = self.logs_serializer.read_logs()?; // Drop old logs - let current_timestamp = self.timestamp_source.timestamp(); + let current_timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(); transaction_logs.retain(|tx_log| current_timestamp - tx_log.creation_timestamp < MAX_STORING_TIME); let mut logs = self.logs.write(); for log in transaction_logs { @@ -274,9 +276,10 @@ mod tests { use ethereum_types::{H256}; use std::collections::{HashMap, BTreeMap}; use std::sync::{Arc}; + use std::time::{SystemTime, UNIX_EPOCH}; use types::transaction::{Transaction}; use parking_lot::{RwLock}; - use super::{TransactionLog, Logging, PrivateTxStatus, LogsSerializer, CurrentTime}; + use super::{TransactionLog, Logging, PrivateTxStatus, LogsSerializer, ValidatorLog}; struct StringLogSerializer { string_log: RwLock, @@ -313,18 +316,6 @@ mod tests { } } - struct CounterTimestamp { - counter: RwLock, - } - - impl CurrentTime for CounterTimestamp { - fn timestamp(&self) -> u64 { - let current = *self.counter.read(); - *self.counter.write() = current + 1; - current - } - } - #[test] fn private_log_format() { let s = r#"{ @@ -344,7 +335,7 @@ mod tests { #[test] fn private_log_status() { - let logger = Logging::new(Arc::new(StringLogSerializer::new("".into())), Box::new(CounterTimestamp { counter: RwLock::new(0), })); + let logger = Logging::new(Arc::new(StringLogSerializer::new("".into()))); let private_tx = Transaction::default(); let hash = private_tx.hash(None); logger.private_tx_created(&hash, &vec!["0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1".into()]); @@ -356,47 +347,40 @@ mod tests { #[test] fn serialization() { - let initial = r#"[{ - "tx_hash":"0x64f648ca7ae7f4138014f860ae56164d8d5732969b1cea54d8be9d144d8aa6f6", - "status":"Deployed", - "creation_timestamp":0, - "validators":[{ - "account":"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1", - "validation_timestamp":1 - }], - "deployment_timestamp":2, - "public_tx_hash":"0x69b9c691ede7993effbcc88911c309af1c82be67b04b3882dd446b808ae146da" - }]"#; - let serializer = Arc::new(StringLogSerializer::new(initial.into())); - let logger = Logging::new(serializer.clone(), Box::new(CounterTimestamp { counter: RwLock::new(5) })); + let current_timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(); + let initial_validator_log = ValidatorLog { + account: "0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1".into(), + validation_timestamp: Some(current_timestamp + 1), + }; + let initial_log = TransactionLog { + tx_hash: "0x64f648ca7ae7f4138014f860ae56164d8d5732969b1cea54d8be9d144d8aa6f6".into(), + status: PrivateTxStatus::Deployed, + creation_timestamp: current_timestamp, + validators: vec![initial_validator_log], + deployment_timestamp: Some(current_timestamp + 2), + public_tx_hash: Some("0x69b9c691ede7993effbcc88911c309af1c82be67b04b3882dd446b808ae146da".into()), + }; + let serializer = Arc::new(StringLogSerializer::new(serde_json::to_string(&vec![initial_log.clone()]).unwrap())); + let logger = Logging::new(serializer.clone()); let hash: H256 = "0x63c715e88f7291e66069302f6fcbb4f28a19ef5d7cbd1832d0c01e221c0061c6".into(); logger.private_tx_created(&hash, &vec!["0x7ffbe3512782069be388f41be4d8eb350672d3a5".into()]); logger.signature_added(&hash, &"0x7ffbe3512782069be388f41be4d8eb350672d3a5".into()); logger.tx_deployed(&hash, &"0xde2209a8635b9cab9eceb67928b217c70ab53f6498e5144492ec01e6f43547d7".into()); drop(logger); - let should_be_final = r#"[ - { - "tx_hash":"0x63c715e88f7291e66069302f6fcbb4f28a19ef5d7cbd1832d0c01e221c0061c6", - "status":"Deployed", - "creation_timestamp":6, - "validators":[{ - "account":"0x7ffbe3512782069be388f41be4d8eb350672d3a5", - "validation_timestamp":7 - }], - "deployment_timestamp":8, - "public_tx_hash":"0xde2209a8635b9cab9eceb67928b217c70ab53f6498e5144492ec01e6f43547d7"}, - { - "tx_hash":"0x64f648ca7ae7f4138014f860ae56164d8d5732969b1cea54d8be9d144d8aa6f6", - "status":"Deployed", - "creation_timestamp":0, - "validators":[{ - "account":"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1", - "validation_timestamp":1 - }], - "deployment_timestamp":2, - "public_tx_hash":"0x69b9c691ede7993effbcc88911c309af1c82be67b04b3882dd446b808ae146da" - }]"#; - let should_be_final = &should_be_final.replace("\t", "").replace("\n", ""); - assert_eq!(serializer.log(), *should_be_final); + let added_validator_log = ValidatorLog { + account: "0x7ffbe3512782069be388f41be4d8eb350672d3a5".into(), + validation_timestamp: Some(current_timestamp + 7), + }; + let added_log = TransactionLog { + tx_hash: "0x63c715e88f7291e66069302f6fcbb4f28a19ef5d7cbd1832d0c01e221c0061c6".into(), + status: PrivateTxStatus::Deployed, + creation_timestamp: current_timestamp + 6, + validators: vec![added_validator_log], + deployment_timestamp: Some(current_timestamp + 8), + public_tx_hash: Some("0xde2209a8635b9cab9eceb67928b217c70ab53f6498e5144492ec01e6f43547d7".into()), + }; + let should_be_final = vec![added_log, initial_log]; + let deserialized_logs: Vec = serde_json::from_str(&serializer.log()).unwrap(); + assert_eq!(deserialized_logs, should_be_final); } } \ No newline at end of file From 91b62e4a4312c3f8f803e926aabaec58e2b848c6 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Tue, 7 May 2019 12:10:30 +0200 Subject: [PATCH 17/23] u64 replaced with SystemTime --- Cargo.lock | 1 + ethcore/private-tx/Cargo.toml | 1 + ethcore/private-tx/src/error.rs | 3 +++ ethcore/private-tx/src/lib.rs | 3 +++ ethcore/private-tx/src/log.rs | 46 +++++++++++++++++++-------------- rpc/src/v1/types/private_log.rs | 7 ++--- 6 files changed, 38 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a56d1b663fc..41d14ba54ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1045,6 +1045,7 @@ dependencies = [ "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "time-utils 0.1.0", "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "transaction-pool 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "trie-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ethcore/private-tx/Cargo.toml b/ethcore/private-tx/Cargo.toml index 2ce127a8b7a..cd484d53f7b 100644 --- a/ethcore/private-tx/Cargo.toml +++ b/ethcore/private-tx/Cargo.toml @@ -35,6 +35,7 @@ rustc-hex = "1.0" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" +time-utils = { path = "../../util/time-utils" } tiny-keccak = "1.4" transaction-pool = "2.0" url = "1" diff --git a/ethcore/private-tx/src/error.rs b/ethcore/private-tx/src/error.rs index 32af1671f4d..cb0964df060 100644 --- a/ethcore/private-tx/src/error.rs +++ b/ethcore/private-tx/src/error.rs @@ -106,6 +106,9 @@ pub enum Error { /// Transaction not found in logs. #[display(fmt = "Private transaction not found in logs.")] TxNotFoundInLog, + /// Timestamp overflow error. + #[display(fmt = "Timestamp overflow error.")] + TimestampOverflow, /// VM execution error. #[display(fmt = "VM execution error {}", _0)] Execution(ExecutionError), diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 24997108191..7d182544cf8 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -63,6 +63,9 @@ extern crate derive_more; #[macro_use] extern crate rlp_derive; +#[cfg(not(time_checked_add))] +extern crate time_utils; + #[cfg(test)] extern crate rand; #[cfg(test)] diff --git a/ethcore/private-tx/src/log.rs b/ethcore/private-tx/src/log.rs index 25b4336951c..c10796aa2da 100644 --- a/ethcore/private-tx/src/log.rs +++ b/ethcore/private-tx/src/log.rs @@ -21,17 +21,20 @@ use std::collections::HashMap; use std::fs::File; use std::path::PathBuf; use std::sync::Arc; -use std::time::{SystemTime, UNIX_EPOCH}; +use std::time::{SystemTime, Duration}; use parking_lot::RwLock; use serde::ser::{Serializer, SerializeSeq}; use error::Error; +#[cfg(not(time_checked_add))] +use time_utils::CheckedSystemTime; + /// Maximum amount of stored private transaction logs. const MAX_JOURNAL_LEN: usize = 1000; /// Maximum period for storing private transaction logs. /// Logs older than 20 days will not be processed -const MAX_STORING_TIME: u64 = 60 * 60 * 24 * 20; +const MAX_STORING_TIME: Duration = Duration::from_secs(60 * 60 * 24 * 20); /// Current status of the private transaction #[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] @@ -51,7 +54,7 @@ pub struct ValidatorLog { /// Account of the validator pub account: Address, /// Validation timestamp, None if the transaction is not validated - pub validation_timestamp: Option, + pub validation_timestamp: Option, } #[cfg(test)] @@ -69,11 +72,11 @@ pub struct TransactionLog { /// Current status of the private transaction pub status: PrivateTxStatus, /// Creation timestamp - pub creation_timestamp: u64, + pub creation_timestamp: SystemTime, /// List of validations pub validators: Vec, /// Timestamp of the resulting public tx deployment - pub deployment_timestamp: Option, + pub deployment_timestamp: Option, /// Hash of the resulting public tx pub public_tx_hash: Option, } @@ -214,7 +217,7 @@ impl Logging { logs.insert(*tx_hash, TransactionLog { tx_hash: *tx_hash, status: PrivateTxStatus::Created, - creation_timestamp: SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(), + creation_timestamp: SystemTime::now(), validators: validator_logs, deployment_timestamp: None, public_tx_hash: None, @@ -227,7 +230,7 @@ impl Logging { if let Some(transaction_log) = logs.get_mut(&tx_hash) { if let Some(ref mut validator_log) = transaction_log.validators.iter_mut().find(|log| log.account == *validator) { transaction_log.status = PrivateTxStatus::Validating; - validator_log.validation_timestamp = Some(SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs()); + validator_log.validation_timestamp = Some(SystemTime::now()); } } } @@ -237,7 +240,7 @@ impl Logging { let mut logs = self.logs.write(); if let Some(log) = logs.get_mut(&tx_hash) { log.status = PrivateTxStatus::Deployed; - log.deployment_timestamp = Some(SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs()); + log.deployment_timestamp = Some(SystemTime::now()); log.public_tx_hash = Some(*public_tx_hash); } } @@ -245,8 +248,8 @@ impl Logging { fn read_logs(&self) -> Result<(), Error> { let mut transaction_logs = self.logs_serializer.read_logs()?; // Drop old logs - let current_timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(); - transaction_logs.retain(|tx_log| current_timestamp - tx_log.creation_timestamp < MAX_STORING_TIME); + let earliest_possible = SystemTime::now().checked_sub(MAX_STORING_TIME).ok_or(Error::TimestampOverflow)?; + transaction_logs.retain(|tx_log| tx_log.creation_timestamp > earliest_possible); let mut logs = self.logs.write(); for log in transaction_logs { logs.insert(log.tx_hash, log); @@ -276,11 +279,14 @@ mod tests { use ethereum_types::{H256}; use std::collections::{HashMap, BTreeMap}; use std::sync::{Arc}; - use std::time::{SystemTime, UNIX_EPOCH}; + use std::time::{SystemTime, Duration}; use types::transaction::{Transaction}; use parking_lot::{RwLock}; use super::{TransactionLog, Logging, PrivateTxStatus, LogsSerializer, ValidatorLog}; + #[cfg(not(time_checked_add))] + use time_utils::CheckedSystemTime; + struct StringLogSerializer { string_log: RwLock, } @@ -321,12 +327,12 @@ mod tests { let s = r#"{ "tx_hash":"0x64f648ca7ae7f4138014f860ae56164d8d5732969b1cea54d8be9d144d8aa6f6", "status":"Deployed", - "creation_timestamp":1544528180, + "creation_timestamp":{"secs_since_epoch":1557220355,"nanos_since_epoch":196382053}, "validators":[{ "account":"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1", - "validation_timestamp":1544528181 + "validation_timestamp":{"secs_since_epoch":1557220355,"nanos_since_epoch":196382053} }], - "deployment_timestamp":1544528181, + "deployment_timestamp":{"secs_since_epoch":1557220355,"nanos_since_epoch":196382053}, "public_tx_hash":"0x69b9c691ede7993effbcc88911c309af1c82be67b04b3882dd446b808ae146da" }"#; @@ -347,17 +353,17 @@ mod tests { #[test] fn serialization() { - let current_timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(); + let current_timestamp = SystemTime::now(); let initial_validator_log = ValidatorLog { account: "0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1".into(), - validation_timestamp: Some(current_timestamp + 1), + validation_timestamp: Some(current_timestamp.checked_add(Duration::from_secs(1)).unwrap()), }; let initial_log = TransactionLog { tx_hash: "0x64f648ca7ae7f4138014f860ae56164d8d5732969b1cea54d8be9d144d8aa6f6".into(), status: PrivateTxStatus::Deployed, creation_timestamp: current_timestamp, validators: vec![initial_validator_log], - deployment_timestamp: Some(current_timestamp + 2), + deployment_timestamp: Some(current_timestamp.checked_add(Duration::from_secs(2)).unwrap()), public_tx_hash: Some("0x69b9c691ede7993effbcc88911c309af1c82be67b04b3882dd446b808ae146da".into()), }; let serializer = Arc::new(StringLogSerializer::new(serde_json::to_string(&vec![initial_log.clone()]).unwrap())); @@ -369,14 +375,14 @@ mod tests { drop(logger); let added_validator_log = ValidatorLog { account: "0x7ffbe3512782069be388f41be4d8eb350672d3a5".into(), - validation_timestamp: Some(current_timestamp + 7), + validation_timestamp: Some(current_timestamp.checked_add(Duration::from_secs(7)).unwrap()), }; let added_log = TransactionLog { tx_hash: "0x63c715e88f7291e66069302f6fcbb4f28a19ef5d7cbd1832d0c01e221c0061c6".into(), status: PrivateTxStatus::Deployed, - creation_timestamp: current_timestamp + 6, + creation_timestamp: current_timestamp.checked_add(Duration::from_secs(6)).unwrap(), validators: vec![added_validator_log], - deployment_timestamp: Some(current_timestamp + 8), + deployment_timestamp: Some(current_timestamp.checked_add(Duration::from_secs(8)).unwrap()), public_tx_hash: Some("0xde2209a8635b9cab9eceb67928b217c70ab53f6498e5144492ec01e6f43547d7".into()), }; let should_be_final = vec![added_log, initial_log]; diff --git a/rpc/src/v1/types/private_log.rs b/rpc/src/v1/types/private_log.rs index 4bf7389f985..4352d2576df 100644 --- a/rpc/src/v1/types/private_log.rs +++ b/rpc/src/v1/types/private_log.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +use std::time::SystemTime; use ethereum_types::{H160, H256}; use ethcore_private_tx::{TransactionLog as EthTransactionLog, ValidatorLog as EthValidatorLog, PrivateTxStatus as EthStatus}; @@ -54,7 +55,7 @@ impl From for ValidatorLog { fn from(r: EthValidatorLog) -> Self { ValidatorLog { account: r.account.into(), - validation_timestamp: r.validation_timestamp.into(), + validation_timestamp: r.validation_timestamp.map(|t| t.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default().as_secs()), } } } @@ -82,9 +83,9 @@ impl From for PrivateTransactionLog { PrivateTransactionLog { tx_hash: r.tx_hash.into(), status: r.status.into(), - creation_timestamp: r.creation_timestamp.into(), + creation_timestamp: r.creation_timestamp.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default().as_secs(), validators: r.validators.into_iter().map(Into::into).collect(), - deployment_timestamp: r.deployment_timestamp.into(), + deployment_timestamp: r.deployment_timestamp.map(|t| t.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default().as_secs()), public_tx_hash: r.public_tx_hash.map(Into::into), } } From f94905baebe87346326ac46b30d23f668757f8fb Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Tue, 7 May 2019 13:04:10 +0200 Subject: [PATCH 18/23] Path made mandatory for logging --- ethcore/private-tx/src/error.rs | 3 ++ ethcore/private-tx/src/lib.rs | 23 ++++++++---- ethcore/private-tx/src/log.rs | 65 +++++++++++---------------------- 3 files changed, 41 insertions(+), 50 deletions(-) diff --git a/ethcore/private-tx/src/error.rs b/ethcore/private-tx/src/error.rs index cb0964df060..b4fc4a3fad3 100644 --- a/ethcore/private-tx/src/error.rs +++ b/ethcore/private-tx/src/error.rs @@ -106,6 +106,9 @@ pub enum Error { /// Transaction not found in logs. #[display(fmt = "Private transaction not found in logs.")] TxNotFoundInLog, + /// Path for logging not set. + #[display(fmt = "Path for logging not set.")] + LoggingPathNotSet, /// Timestamp overflow error. #[display(fmt = "Timestamp overflow error.")] TimestampOverflow, diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 7d182544cf8..3dc82675be8 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -188,7 +188,7 @@ pub struct Provider { accounts: Arc, channel: IoChannel, keys_provider: Arc, - logging: Logging, + logging: Option, } #[derive(Debug)] @@ -223,7 +223,7 @@ impl Provider { accounts, channel, keys_provider, - logging: Logging::new(Arc::new(FileLogsSerializer::with_path(config.logs_path))), + logging: config.logs_path.map(|path| Logging::new(Arc::new(FileLogsSerializer::with_path(path)))), } } @@ -272,7 +272,9 @@ impl Provider { trace!(target: "privatetx", "Hashed effective private state for sender: {:?}", private_state_hash); self.transactions_for_signing.write().add_transaction(private.hash(), signed_transaction, &contract_validators, private_state, contract_nonce)?; self.broadcast_private_transaction(private.hash(), private.rlp_bytes()); - self.logging.private_tx_created(&tx_hash, &contract_validators); + if let Some(ref logging) = self.logging { + logging.private_tx_created(&tx_hash, &contract_validators); + } Ok(Receipt { hash: tx_hash, contract_address: contract, @@ -408,8 +410,10 @@ impl Provider { } } // Store logs - self.logging.signature_added(&original_tx_hash, &last.1); - self.logging.tx_deployed(&original_tx_hash, &public_tx_hash); + if let Some(ref logging) = self.logging { + logging.signature_added(&original_tx_hash, &last.1); + logging.tx_deployed(&original_tx_hash, &public_tx_hash); + } // Remove from store for signing if let Err(err) = self.transactions_for_signing.write().remove(&private_hash) { warn!(target: "privatetx", "Failed to remove transaction from signing store, error: {:?}", err); @@ -420,7 +424,9 @@ impl Provider { match self.transactions_for_signing.write().add_signature(&private_hash, signed_tx.signature()) { Ok(_) => { trace!(target: "privatetx", "Signature stored for private transaction"); - self.logging.signature_added(&original_tx_hash, &last.1); + if let Some(ref logging) = self.logging { + logging.signature_added(&original_tx_hash, &last.1); + } } Err(err) => { warn!(target: "privatetx", "Failed to add signature to signing store, error: {:?}", err); @@ -694,7 +700,10 @@ impl Provider { /// Retrieves log information about private transaction pub fn private_log(&self, tx_hash: H256) -> Result { - self.logging.tx_log(&tx_hash).ok_or_else(|| Error::TxNotFoundInLog.into()) + match self.logging { + Some(ref logging) => logging.tx_log(&tx_hash).ok_or_else(|| Error::TxNotFoundInLog.into()), + None => Err(Error::LoggingPathNotSet), + } } /// Returns private validators for a contract. diff --git a/ethcore/private-tx/src/log.rs b/ethcore/private-tx/src/log.rs index c10796aa2da..d24f7c4083a 100644 --- a/ethcore/private-tx/src/log.rs +++ b/ethcore/private-tx/src/log.rs @@ -101,56 +101,37 @@ pub trait LogsSerializer: Send + Sync + 'static { } pub struct FileLogsSerializer { - logs_dir: Option, + logs_dir: PathBuf, } impl FileLogsSerializer { - pub fn with_path(logs_dir: Option) -> Self { + pub fn with_path>(logs_dir: P) -> Self { FileLogsSerializer { - logs_dir: logs_dir.map(|dir| PathBuf::from(dir)), + logs_dir: logs_dir.into(), } } - fn open_file(&self, to_create: bool) -> Result, Error> { - let log_file = match self.logs_dir { - Some(ref path) => { - let mut file_path = path.clone(); - file_path.push("private_tx.log"); - match if to_create { File::create(&file_path) } else { File::open(&file_path) } { - Ok(file) => file, - Err(err) => { - trace!(target: "privatetx", "Cannot open logs file: {}", err); - return Err(format!("Cannot open logs file: {:?}", err).into()); - } - } - } - None => { - warn!(target: "privatetx", "Logs path is not defined"); - return Ok(None); - } - }; - Ok(Some(log_file)) + fn open_file(&self, to_create: bool) -> Result { + let file_path = self.logs_dir.with_file_name("private_tx.log"); + if to_create { + File::create(&file_path).map_err(From::from) + } else { + File::open(&file_path).map_err(From::from) + } } } impl LogsSerializer for FileLogsSerializer { fn read_logs(&self) -> Result, Error> { let log_file = self.open_file(false)?; - match log_file { - Some(log_file) => { - let transaction_logs: Vec = match serde_json::from_reader(log_file) { - Ok(logs) => logs, - Err(err) => { - error!(target: "privatetx", "Cannot deserialize logs from file: {}", err); - return Err(format!("Cannot deserialize logs from file: {:?}", err).into()); - } - }; - Ok(transaction_logs) + let transaction_logs: Vec = match serde_json::from_reader(log_file) { + Ok(logs) => logs, + Err(err) => { + error!(target: "privatetx", "Cannot deserialize logs from file: {}", err); + return Err(format!("Cannot deserialize logs from file: {:?}", err).into()); } - None => { - Ok(Vec::new()) - } - } + }; + Ok(transaction_logs) } fn flush_logs(&self, logs: &HashMap) -> Result<(), Error> { @@ -159,14 +140,12 @@ impl LogsSerializer for FileLogsSerializer { return Ok(()); } let log_file = self.open_file(true)?; - if let Some(log_file) = log_file { - let mut json = serde_json::Serializer::new(log_file); - let mut json_array = json.serialize_seq(Some(logs.len()))?; - for v in logs.values() { - json_array.serialize_element(v)?; - } - json_array.end()?; + let mut json = serde_json::Serializer::new(log_file); + let mut json_array = json.serialize_seq(Some(logs.len()))?; + for v in logs.values() { + json_array.serialize_element(v)?; } + json_array.end()?; Ok(()) } } From 541f5d287f8534bb1aef5e536dd8a90c8f893193 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Tue, 7 May 2019 14:05:23 +0200 Subject: [PATCH 19/23] Source of monotonic time added --- ethcore/private-tx/src/log.rs | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/ethcore/private-tx/src/log.rs b/ethcore/private-tx/src/log.rs index d24f7c4083a..58aff467180 100644 --- a/ethcore/private-tx/src/log.rs +++ b/ethcore/private-tx/src/log.rs @@ -21,8 +21,8 @@ use std::collections::HashMap; use std::fs::File; use std::path::PathBuf; use std::sync::Arc; -use std::time::{SystemTime, Duration}; -use parking_lot::RwLock; +use std::time::{SystemTime, Duration, Instant}; +use parking_lot::{RwLock, Mutex}; use serde::ser::{Serializer, SerializeSeq}; use error::Error; @@ -36,6 +36,29 @@ const MAX_JOURNAL_LEN: usize = 1000; /// Logs older than 20 days will not be processed const MAX_STORING_TIME: Duration = Duration::from_secs(60 * 60 * 24 * 20); +/// Source of monotonic time for log timestamps +struct MonoTime { + start_time: SystemTime, + start_inst: Instant +} + +impl MonoTime { + fn new(start: SystemTime) -> Self { + Self { + start_time: start, + start_inst: Instant::now() + } + } + + fn elapsed(&self) -> Duration { + self.start_inst.elapsed() + } + + fn into_system_time(&self) -> SystemTime { + self.start_time + self.elapsed() + } +} + /// Current status of the private transaction #[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] pub enum PrivateTxStatus { @@ -154,6 +177,7 @@ impl LogsSerializer for FileLogsSerializer { pub struct Logging { logs: RwLock>, logs_serializer: Arc, + mono_time: Mutex, } impl Logging { @@ -162,6 +186,7 @@ impl Logging { let logging = Logging { logs: RwLock::new(HashMap::new()), logs_serializer, + mono_time: Mutex::new(MonoTime::new(SystemTime::now())), }; if let Err(err) = logging.read_logs() { warn!(target: "privatetx", "Cannot read logs: {:?}", err); @@ -196,7 +221,7 @@ impl Logging { logs.insert(*tx_hash, TransactionLog { tx_hash: *tx_hash, status: PrivateTxStatus::Created, - creation_timestamp: SystemTime::now(), + creation_timestamp: self.mono_time.lock().into_system_time(), validators: validator_logs, deployment_timestamp: None, public_tx_hash: None, @@ -209,7 +234,7 @@ impl Logging { if let Some(transaction_log) = logs.get_mut(&tx_hash) { if let Some(ref mut validator_log) = transaction_log.validators.iter_mut().find(|log| log.account == *validator) { transaction_log.status = PrivateTxStatus::Validating; - validator_log.validation_timestamp = Some(SystemTime::now()); + validator_log.validation_timestamp = Some(self.mono_time.lock().into_system_time()); } } } @@ -219,7 +244,7 @@ impl Logging { let mut logs = self.logs.write(); if let Some(log) = logs.get_mut(&tx_hash) { log.status = PrivateTxStatus::Deployed; - log.deployment_timestamp = Some(SystemTime::now()); + log.deployment_timestamp = Some(self.mono_time.lock().into_system_time()); log.public_tx_hash = Some(*public_tx_hash); } } From ad2543219fc65097f7b7fc302dfb509b4afb74b5 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Wed, 8 May 2019 11:59:42 +0200 Subject: [PATCH 20/23] into_system_time method renamed --- ethcore/private-tx/src/log.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ethcore/private-tx/src/log.rs b/ethcore/private-tx/src/log.rs index 58aff467180..dbcebf9ce6c 100644 --- a/ethcore/private-tx/src/log.rs +++ b/ethcore/private-tx/src/log.rs @@ -54,7 +54,7 @@ impl MonoTime { self.start_inst.elapsed() } - fn into_system_time(&self) -> SystemTime { + fn to_system_time(&self) -> SystemTime { self.start_time + self.elapsed() } } @@ -221,7 +221,7 @@ impl Logging { logs.insert(*tx_hash, TransactionLog { tx_hash: *tx_hash, status: PrivateTxStatus::Created, - creation_timestamp: self.mono_time.lock().into_system_time(), + creation_timestamp: self.mono_time.lock().to_system_time(), validators: validator_logs, deployment_timestamp: None, public_tx_hash: None, @@ -234,7 +234,7 @@ impl Logging { if let Some(transaction_log) = logs.get_mut(&tx_hash) { if let Some(ref mut validator_log) = transaction_log.validators.iter_mut().find(|log| log.account == *validator) { transaction_log.status = PrivateTxStatus::Validating; - validator_log.validation_timestamp = Some(self.mono_time.lock().into_system_time()); + validator_log.validation_timestamp = Some(self.mono_time.lock().to_system_time()); } } } @@ -244,7 +244,7 @@ impl Logging { let mut logs = self.logs.write(); if let Some(log) = logs.get_mut(&tx_hash) { log.status = PrivateTxStatus::Deployed; - log.deployment_timestamp = Some(self.mono_time.lock().into_system_time()); + log.deployment_timestamp = Some(self.mono_time.lock().to_system_time()); log.public_tx_hash = Some(*public_tx_hash); } } From 9a274efb34eb24e093440b3188be1f63423faace Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Wed, 8 May 2019 12:35:51 +0200 Subject: [PATCH 21/23] Initialize time source by max from current system time and max creation time from already saved logs --- ethcore/private-tx/src/log.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/ethcore/private-tx/src/log.rs b/ethcore/private-tx/src/log.rs index dbcebf9ce6c..bd795f238ee 100644 --- a/ethcore/private-tx/src/log.rs +++ b/ethcore/private-tx/src/log.rs @@ -59,6 +59,12 @@ impl MonoTime { } } +impl Default for MonoTime { + fn default() -> Self { + MonoTime::new(SystemTime::now()) + } +} + /// Current status of the private transaction #[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] pub enum PrivateTxStatus { @@ -186,10 +192,12 @@ impl Logging { let logging = Logging { logs: RwLock::new(HashMap::new()), logs_serializer, - mono_time: Mutex::new(MonoTime::new(SystemTime::now())), + mono_time: Mutex::default(), }; - if let Err(err) = logging.read_logs() { - warn!(target: "privatetx", "Cannot read logs: {:?}", err); + match logging.read_logs() { + // Initialize time source by max from current system time and max creation time from already saved logs + Ok(initial_time) => *logging.mono_time.lock() = MonoTime::new(initial_time), + Err(err) => warn!(target: "privatetx", "Cannot read logs: {:?}", err), } logging } @@ -249,16 +257,21 @@ impl Logging { } } - fn read_logs(&self) -> Result<(), Error> { + fn read_logs(&self) -> Result { let mut transaction_logs = self.logs_serializer.read_logs()?; // Drop old logs let earliest_possible = SystemTime::now().checked_sub(MAX_STORING_TIME).ok_or(Error::TimestampOverflow)?; transaction_logs.retain(|tx_log| tx_log.creation_timestamp > earliest_possible); + // Sort logs by their creation time in order to find the most recent + transaction_logs.sort_by(|a, b| b.creation_timestamp.cmp(&a.creation_timestamp)); + let initial_timestamp = transaction_logs.first() + .map(|l| l.creation_timestamp) + .map_or(SystemTime::now(), |t| std::cmp::max(SystemTime::now(), t)); let mut logs = self.logs.write(); for log in transaction_logs { logs.insert(log.tx_hash, log); } - Ok(()) + Ok(initial_timestamp) } fn flush_logs(&self) -> Result<(), Error> { From d261c52a3d3f5a214ba34f413cccbf4c0f30fef1 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Fri, 10 May 2019 17:00:53 +0200 Subject: [PATCH 22/23] Redundant conversions removed, code a little bit reworked according to review comments --- ethcore/private-tx/src/lib.rs | 2 +- ethcore/private-tx/src/log.rs | 37 ++++++++++++++++----------------- rpc/src/v1/types/private_log.rs | 6 +++--- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 3dc82675be8..edf018b2e8b 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -701,7 +701,7 @@ impl Provider { /// Retrieves log information about private transaction pub fn private_log(&self, tx_hash: H256) -> Result { match self.logging { - Some(ref logging) => logging.tx_log(&tx_hash).ok_or_else(|| Error::TxNotFoundInLog.into()), + Some(ref logging) => logging.tx_log(&tx_hash).ok_or(Error::TxNotFoundInLog.into()), None => Err(Error::LoggingPathNotSet), } } diff --git a/ethcore/private-tx/src/log.rs b/ethcore/private-tx/src/log.rs index bd795f238ee..d836dfa3654 100644 --- a/ethcore/private-tx/src/log.rs +++ b/ethcore/private-tx/src/log.rs @@ -22,7 +22,7 @@ use std::fs::File; use std::path::PathBuf; use std::sync::Arc; use std::time::{SystemTime, Duration, Instant}; -use parking_lot::{RwLock, Mutex}; +use parking_lot::RwLock; use serde::ser::{Serializer, SerializeSeq}; use error::Error; @@ -129,6 +129,7 @@ pub trait LogsSerializer: Send + Sync + 'static { fn flush_logs(&self, logs: &HashMap) -> Result<(), Error>; } +/// Logs serializer to the json file pub struct FileLogsSerializer { logs_dir: PathBuf, } @@ -153,14 +154,13 @@ impl FileLogsSerializer { impl LogsSerializer for FileLogsSerializer { fn read_logs(&self) -> Result, Error> { let log_file = self.open_file(false)?; - let transaction_logs: Vec = match serde_json::from_reader(log_file) { - Ok(logs) => logs, + match serde_json::from_reader(log_file) { + Ok(logs) => Ok(logs), Err(err) => { error!(target: "privatetx", "Cannot deserialize logs from file: {}", err); return Err(format!("Cannot deserialize logs from file: {:?}", err).into()); } - }; - Ok(transaction_logs) + } } fn flush_logs(&self, logs: &HashMap) -> Result<(), Error> { @@ -183,20 +183,20 @@ impl LogsSerializer for FileLogsSerializer { pub struct Logging { logs: RwLock>, logs_serializer: Arc, - mono_time: Mutex, + mono_time: MonoTime, } impl Logging { /// Creates the logging object pub fn new(logs_serializer: Arc) -> Self { - let logging = Logging { + let mut logging = Logging { logs: RwLock::new(HashMap::new()), logs_serializer, - mono_time: Mutex::default(), + mono_time: MonoTime::default(), }; match logging.read_logs() { // Initialize time source by max from current system time and max creation time from already saved logs - Ok(initial_time) => *logging.mono_time.lock() = MonoTime::new(initial_time), + Ok(initial_time) => logging.mono_time = MonoTime::new(initial_time), Err(err) => warn!(target: "privatetx", "Cannot read logs: {:?}", err), } logging @@ -229,7 +229,7 @@ impl Logging { logs.insert(*tx_hash, TransactionLog { tx_hash: *tx_hash, status: PrivateTxStatus::Created, - creation_timestamp: self.mono_time.lock().to_system_time(), + creation_timestamp: self.mono_time.to_system_time(), validators: validator_logs, deployment_timestamp: None, public_tx_hash: None, @@ -242,7 +242,7 @@ impl Logging { if let Some(transaction_log) = logs.get_mut(&tx_hash) { if let Some(ref mut validator_log) = transaction_log.validators.iter_mut().find(|log| log.account == *validator) { transaction_log.status = PrivateTxStatus::Validating; - validator_log.validation_timestamp = Some(self.mono_time.lock().to_system_time()); + validator_log.validation_timestamp = Some(self.mono_time.to_system_time()); } } } @@ -252,7 +252,7 @@ impl Logging { let mut logs = self.logs.write(); if let Some(log) = logs.get_mut(&tx_hash) { log.status = PrivateTxStatus::Deployed; - log.deployment_timestamp = Some(self.mono_time.lock().to_system_time()); + log.deployment_timestamp = Some(self.mono_time.to_system_time()); log.public_tx_hash = Some(*public_tx_hash); } } @@ -265,8 +265,7 @@ impl Logging { // Sort logs by their creation time in order to find the most recent transaction_logs.sort_by(|a, b| b.creation_timestamp.cmp(&a.creation_timestamp)); let initial_timestamp = transaction_logs.first() - .map(|l| l.creation_timestamp) - .map_or(SystemTime::now(), |t| std::cmp::max(SystemTime::now(), t)); + .map_or(SystemTime::now(), |l| std::cmp::max(SystemTime::now(), l.creation_timestamp)); let mut logs = self.logs.write(); for log in transaction_logs { logs.insert(log.tx_hash, log); @@ -292,13 +291,13 @@ impl Drop for Logging { #[cfg(test)] mod tests { use serde_json; - use error::{Error}; - use ethereum_types::{H256}; + use error::Error; + use ethereum_types::H256; use std::collections::{HashMap, BTreeMap}; - use std::sync::{Arc}; + use std::sync::Arc; use std::time::{SystemTime, Duration}; - use types::transaction::{Transaction}; - use parking_lot::{RwLock}; + use types::transaction::Transaction; + use parking_lot::RwLock; use super::{TransactionLog, Logging, PrivateTxStatus, LogsSerializer, ValidatorLog}; #[cfg(not(time_checked_add))] diff --git a/rpc/src/v1/types/private_log.rs b/rpc/src/v1/types/private_log.rs index 4352d2576df..2c90bcfa905 100644 --- a/rpc/src/v1/types/private_log.rs +++ b/rpc/src/v1/types/private_log.rs @@ -54,7 +54,7 @@ pub struct ValidatorLog { impl From for ValidatorLog { fn from(r: EthValidatorLog) -> Self { ValidatorLog { - account: r.account.into(), + account: r.account, validation_timestamp: r.validation_timestamp.map(|t| t.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default().as_secs()), } } @@ -81,12 +81,12 @@ pub struct PrivateTransactionLog { impl From for PrivateTransactionLog { fn from(r: EthTransactionLog) -> Self { PrivateTransactionLog { - tx_hash: r.tx_hash.into(), + tx_hash: r.tx_hash, status: r.status.into(), creation_timestamp: r.creation_timestamp.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default().as_secs(), validators: r.validators.into_iter().map(Into::into).collect(), deployment_timestamp: r.deployment_timestamp.map(|t| t.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default().as_secs()), - public_tx_hash: r.public_tx_hash.map(Into::into), + public_tx_hash: r.public_tx_hash, } } } From a67dab572080f72aac4588eb4868fa109051c5bf Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Mon, 13 May 2019 11:12:02 +0200 Subject: [PATCH 23/23] One more redundant conversion removed, rpc call simplified --- ethcore/private-tx/src/lib.rs | 2 +- rpc/src/v1/impls/private.rs | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index edf018b2e8b..aaf44d572b6 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -701,7 +701,7 @@ impl Provider { /// Retrieves log information about private transaction pub fn private_log(&self, tx_hash: H256) -> Result { match self.logging { - Some(ref logging) => logging.tx_log(&tx_hash).ok_or(Error::TxNotFoundInLog.into()), + Some(ref logging) => logging.tx_log(&tx_hash).ok_or(Error::TxNotFoundInLog), None => Err(Error::LoggingPathNotSet), } } diff --git a/rpc/src/v1/impls/private.rs b/rpc/src/v1/impls/private.rs index 12d0ebc8f49..abd15500608 100644 --- a/rpc/src/v1/impls/private.rs +++ b/rpc/src/v1/impls/private.rs @@ -26,7 +26,7 @@ use types::transaction::SignedTransaction; use jsonrpc_core::{Error}; use v1::types::{Bytes, PrivateTransactionReceipt, TransactionRequest, - BlockNumber, PrivateTransactionReceiptAndTransaction, CallRequest, + BlockNumber, PrivateTransactionReceiptAndTransaction, CallRequest, block_number_to_id, PrivateTransactionLog}; use v1::traits::Private; use v1::metadata::Metadata; @@ -122,8 +122,9 @@ impl Private for PrivateClient { } fn private_log(&self, tx_hash: H256) -> Result { - let client = self.unwrap_manager()?; - let log = client.private_log(tx_hash.into()).map_err(|e| errors::private_message(e))?; - Ok(log.into()) + self.unwrap_manager()? + .private_log(tx_hash) + .map_err(errors::private_message) + .map(Into::into) } }