Trivial journal for private transactions (#10056)

* Journal for private txs added

* Tests after adding logging to private tx fixed

* Logs getter and tests added

* Time and amount limit for logs added

* RPC method for log retrieving added

* Correct path name and time validation implemented

* References for parameters added, redundant cloning reworked

* References for parameters added, redundant cloning reworked

* Work with json moved to the separate struct

* Serialization test added

* Fixed build after the merge with head

* Documentation for methods fixed, redundant field removed

* Fixed error usages

* Timestamp trait implemented for std struct

* Commented code removed

* Remove timestamp source, rework serialization test

* u64 replaced with SystemTime

* Path made mandatory for logging

* Source of monotonic time added

* into_system_time method renamed

* Initialize time source by max from current system time and max creation time from already saved logs

* Redundant conversions removed, code a little bit reworked according to review comments

* One more redundant conversion removed, rpc call simplified
This commit is contained in:
Anton Gavrilov 2019-05-14 11:21:22 +02:00 committed by Niklas Adolfsson
parent 87699f8de0
commit 5a581c1c90
13 changed files with 596 additions and 17 deletions

1
Cargo.lock generated
View File

@ -1050,6 +1050,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)",

View File

@ -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"

View File

@ -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<<VerifiedPrivateTransaction as VerifiedTransaction>::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,15 @@ 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,
/// Path for logging not set.
#[display(fmt = "Path for logging not set.")]
LoggingPathNotSet,
/// Timestamp overflow error.
#[display(fmt = "Timestamp overflow error.")]
TimestampOverflow,
/// VM execution error.
#[display(fmt = "VM execution error {}", _0)]
Execution(ExecutionError),
@ -123,6 +136,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),
@ -187,6 +201,12 @@ impl From<TxPoolError> for Error {
}
}
impl From<SerdeError> for Error {
fn from(err: SerdeError) -> Self {
Error::Json(err).into()
}
}
impl From<EthcoreError> for Error {
fn from(err: EthcoreError) -> Self {
Error::Ethcore(err).into()

View File

@ -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]
@ -58,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)]
@ -68,6 +76,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;
pub use log::{Logging, TransactionLog, ValidatorLog, PrivateTxStatus, FileLogsSerializer};
use std::sync::{Arc, Weak};
use std::collections::{HashMap, HashSet, BTreeMap};
@ -117,6 +126,8 @@ pub struct ProviderConfig {
pub validator_accounts: Vec<Address>,
/// Account used for signing public transactions created from private transactions
pub signer_account: Option<Address>,
/// Path to private tx logs
pub logs_path: Option<String>,
}
#[derive(Debug)]
@ -177,6 +188,7 @@ pub struct Provider {
accounts: Arc<Signer>,
channel: IoChannel<ClientIoMessage>,
keys_provider: Arc<KeyProvider>,
logging: Option<Logging>,
}
#[derive(Debug)]
@ -211,6 +223,7 @@ impl Provider {
accounts,
channel,
keys_provider,
logging: config.logs_path.map(|path| Logging::new(Arc::new(FileLogsSerializer::with_path(path)))),
}
}
@ -257,8 +270,11 @@ 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());
if let Some(ref logging) = self.logging {
logging.private_tx_created(&tx_hash, &contract_validators);
}
Ok(Receipt {
hash: tx_hash,
contract_address: contract,
@ -354,8 +370,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<Signature> = signatures.into_iter().map(|sign| sign.into_electrum().into()).collect();
@ -373,8 +390,8 @@ 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 signature = self.accounts.sign(signer_account, hash)?;
let public_tx_hash = public_tx.hash(chain_id);
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"),
@ -392,6 +409,11 @@ impl Provider {
Err(err) => warn!(target: "privatetx", "Failed to send private state changed notification, error: {:?}", err),
}
}
// Store logs
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);
@ -400,7 +422,12 @@ 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");
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);
return Err(err);
@ -420,17 +447,14 @@ impl Provider {
}
}
fn last_required_signature(&self, desc: &PrivateTransactionSigningDesc, sign: Signature) -> Result<bool, Error> {
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");
@ -674,6 +698,14 @@ impl Provider {
Ok(result.result)
}
/// Retrieves log information about private transaction
pub fn private_log(&self, tx_hash: H256) -> Result<TransactionLog, Error> {
match self.logging {
Some(ref logging) => logging.tx_log(&tx_hash).ok_or(Error::TxNotFoundInLog),
None => Err(Error::LoggingPathNotSet),
}
}
/// Returns private validators for a contract.
pub fn get_validators(&self, block: BlockId, address: &Address) -> Result<Vec<Address>, Error> {
let (data, decoder) = private_contract::functions::get_validators::call();

View File

@ -0,0 +1,408 @@
// 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 <http://www.gnu.org/licenses/>.
//! 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::time::{SystemTime, Duration, Instant};
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: 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 to_system_time(&self) -> SystemTime {
self.start_time + self.elapsed()
}
}
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 {
/// Private tx was created but no validation received yet
Created,
/// Several validators (but not all) validated the transaction
Validating,
/// All validators has validated the private tx
/// Corresponding public tx was created and added into the pool
Deployed,
}
/// Information about private tx validation
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidatorLog {
/// Account of the validator
pub account: Address,
/// Validation timestamp, None if the transaction is not validated
pub validation_timestamp: Option<SystemTime>,
}
#[cfg(test)]
impl PartialEq for ValidatorLog {
fn eq(&self, other: &Self) -> bool {
self.account == other.account
}
}
/// Information about the private transaction
#[derive(Debug, Clone, Serialize, Deserialize)]
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: PrivateTxStatus,
/// Creation timestamp
pub creation_timestamp: SystemTime,
/// List of validations
pub validators: Vec<ValidatorLog>,
/// Timestamp of the resulting public tx deployment
pub deployment_timestamp: Option<SystemTime>,
/// Hash of the resulting public tx
pub public_tx_hash: Option<H256>,
}
#[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
fn read_logs(&self) -> Result<Vec<TransactionLog>, Error>;
/// Write all logs to the source
fn flush_logs(&self, logs: &HashMap<H256, TransactionLog>) -> Result<(), Error>;
}
/// Logs serializer to the json file
pub struct FileLogsSerializer {
logs_dir: PathBuf,
}
impl FileLogsSerializer {
pub fn with_path<P: Into<PathBuf>>(logs_dir: P) -> Self {
FileLogsSerializer {
logs_dir: logs_dir.into(),
}
}
fn open_file(&self, to_create: bool) -> Result<File, Error> {
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<Vec<TransactionLog>, Error> {
let log_file = self.open_file(false)?;
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());
}
}
}
fn flush_logs(&self, logs: &HashMap<H256, TransactionLog>) -> Result<(), Error> {
if logs.is_empty() {
// Do not create empty file
return Ok(());
}
let log_file = self.open_file(true)?;
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<HashMap<H256, TransactionLog>>,
logs_serializer: Arc<LogsSerializer>,
mono_time: MonoTime,
}
impl Logging {
/// Creates the logging object
pub fn new(logs_serializer: Arc<LogsSerializer>) -> Self {
let mut logging = Logging {
logs: RwLock::new(HashMap::new()),
logs_serializer,
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 = MonoTime::new(initial_time),
Err(err) => warn!(target: "privatetx", "Cannot read logs: {:?}", err),
}
logging
}
/// Retrieves log for the corresponding tx hash
pub fn tx_log(&self, tx_hash: &H256) -> Option<TransactionLog> {
self.logs.read().get(&tx_hash).cloned()
}
/// 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,
validation_timestamp: None,
});
}
let mut logs = self.logs.write();
if logs.len() > MAX_JOURNAL_LEN {
// Remove the oldest log
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: *tx_hash,
status: PrivateTxStatus::Created,
creation_timestamp: self.mono_time.to_system_time(),
validators: validator_logs,
deployment_timestamp: None,
public_tx_hash: None,
});
}
/// 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) {
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.to_system_time());
}
}
}
/// 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) {
log.status = PrivateTxStatus::Deployed;
log.deployment_timestamp = Some(self.mono_time.to_system_time());
log.public_tx_hash = Some(*public_tx_hash);
}
}
fn read_logs(&self) -> Result<SystemTime, Error> {
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_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);
}
Ok(initial_timestamp)
}
fn flush_logs(&self) -> Result<(), Error> {
let logs = self.logs.read();
self.logs_serializer.flush_logs(&logs)
}
}
// Flush all logs on drop
impl Drop for Logging {
fn drop(&mut self) {
if let Err(err) = self.flush_logs() {
warn!(target: "privatetx", "Cannot write logs: {:?}", err);
}
}
}
#[cfg(test)]
mod tests {
use serde_json;
use error::Error;
use ethereum_types::H256;
use std::collections::{HashMap, BTreeMap};
use std::sync::Arc;
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<String>,
}
impl StringLogSerializer {
fn new(source: String) -> Self {
StringLogSerializer {
string_log: RwLock::new(source),
}
}
fn log(&self) -> String {
let log = self.string_log.read();
log.clone()
}
}
impl LogsSerializer for StringLogSerializer {
fn read_logs(&self) -> Result<Vec<TransactionLog>, 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<H256, TransactionLog>) -> Result<(), Error> {
// 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::<Vec<&&TransactionLog>>())?;
Ok(())
}
}
#[test]
fn private_log_format() {
let s = r#"{
"tx_hash":"0x64f648ca7ae7f4138014f860ae56164d8d5732969b1cea54d8be9d144d8aa6f6",
"status":"Deployed",
"creation_timestamp":{"secs_since_epoch":1557220355,"nanos_since_epoch":196382053},
"validators":[{
"account":"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1",
"validation_timestamp":{"secs_since_epoch":1557220355,"nanos_since_epoch":196382053}
}],
"deployment_timestamp":{"secs_since_epoch":1557220355,"nanos_since_epoch":196382053},
"public_tx_hash":"0x69b9c691ede7993effbcc88911c309af1c82be67b04b3882dd446b808ae146da"
}"#;
let _deserialized: TransactionLog = serde_json::from_str(s).unwrap();
}
#[test]
fn private_log_status() {
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()]);
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);
}
#[test]
fn serialization() {
let current_timestamp = SystemTime::now();
let initial_validator_log = ValidatorLog {
account: "0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1".into(),
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.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()));
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 added_validator_log = ValidatorLog {
account: "0x7ffbe3512782069be388f41be4d8eb350672d3a5".into(),
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.checked_add(Duration::from_secs(6)).unwrap(),
validators: vec![added_validator_log],
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];
let deserialized_logs: Vec<TransactionLog> = serde_json::from_str(&serializer.log()).unwrap();
assert_eq!(deserialized_logs, should_be_final);
}
}

View File

@ -224,7 +224,7 @@ impl SigningStore {
&mut self,
private_hash: H256,
transaction: SignedTransaction,
validators: Vec<Address>,
validators: &Vec<Address>,
state: Bytes,
contract_nonce: U256,
) -> Result<(), Error> {

View File

@ -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();
@ -193,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();

View File

@ -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());

View File

@ -914,9 +914,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 {
@ -1458,7 +1460,11 @@ 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(),
logs_path: Some(Directories::default().base),
},
private_encryptor_conf: Default::default(),
private_tx_enabled: false,
name: "".into(),

View File

@ -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,11 @@ 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<PrivateTransactionLog, Error> {
self.unwrap_manager()?
.private_log(tx_hash)
.map_err(errors::private_message)
.map(Into::into)
}
}

View File

@ -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<H256, Error>;
/// Retrieve log information about private transaction
#[rpc(name = "private_log")]
fn private_log(&self, H256) -> Result<PrivateTransactionLog, Error>;
}

View File

@ -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.

View File

@ -0,0 +1,93 @@
// 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 <http://www.gnu.org/licenses/>.
use std::time::SystemTime;
use ethereum_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<EthStatus> 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 timestamp, None, if the transaction is not validated yet
pub validation_timestamp: Option<u64>,
}
impl From<EthValidatorLog> for ValidatorLog {
fn from(r: EthValidatorLog) -> Self {
ValidatorLog {
account: r.account,
validation_timestamp: r.validation_timestamp.map(|t| t.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default().as_secs()),
}
}
}
/// 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<ValidatorLog>,
/// Timestamp of the resulting public tx deployment
pub deployment_timestamp: Option<u64>,
/// Hash of the resulting public tx
pub public_tx_hash: Option<H256>,
}
impl From<EthTransactionLog> for PrivateTransactionLog {
fn from(r: EthTransactionLog) -> Self {
PrivateTransactionLog {
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,
}
}
}