From 4bc36424d51fb5386db500ab687e168b89494bcc Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Wed, 12 Sep 2018 12:36:22 +0200 Subject: [PATCH 01/35] Temp storage for the private state added --- ethcore/private-tx/src/error.rs | 3 +++ ethcore/private-tx/src/lib.rs | 38 ++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/ethcore/private-tx/src/error.rs b/ethcore/private-tx/src/error.rs index 4d26599a52c..6e8a804f4fc 100644 --- a/ethcore/private-tx/src/error.rs +++ b/ethcore/private-tx/src/error.rs @@ -99,6 +99,9 @@ pub enum Error { /// Account for signing requests to key server not set. #[display(fmt = "Account for signing requests to key server not set.")] KeyServerAccountNotSet, + /// Private state for the contract was not found in the local storage. + #[display(fmt = "Private state for the contract was not found in the local storage.")] + PrivateStateNotFound, /// Encryption key is not found on key server. #[display(fmt = "Encryption key is not found on key server for {}", _0)] EncryptionKeyNotFound(Address), diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 283df9b7a8e..18e78c367dd 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -198,6 +198,8 @@ pub struct Provider { channel: IoChannel, keys_provider: Arc, logging: Option, + use_offchain_storage: bool, + temp_offchain_storage: RwLock>, } #[derive(Debug)] @@ -233,6 +235,8 @@ impl Provider { channel, keys_provider, logging: config.logs_path.map(|path| Logging::new(Arc::new(FileLogsSerializer::with_path(path)))), + use_offchain_storage: false, + temp_offchain_storage: RwLock::default(), } } @@ -273,7 +277,17 @@ impl Provider { // best would be to change the API and only allow H256 instead of BlockID // in private-tx to avoid such mistakes. let contract_nonce = self.get_contract_nonce(&contract, BlockId::Latest)?; - let private_state = self.execute_private_transaction(BlockId::Latest, &signed_transaction)?; + let private_state = self.execute_private_transaction(BlockId::Latest, &signed_transaction); + if let Err(err) = private_state { + match err { + Error(ErrorKind::PrivateTransactionNotFound, _) => { + + } + _ => {} + } + bail!(err); + } + let private_state = private_state.expect("Valid state since this"); trace!(target: "privatetx", "Private transaction created, encrypted transaction: {:?}, private state: {:?}", private, private_state); let contract_validators = self.get_validators(BlockId::Latest, &contract)?; trace!(target: "privatetx", "Required validators: {:?}", contract_validators); @@ -512,6 +526,27 @@ impl Provider { } fn get_decrypted_state(&self, address: &Address, block: BlockId) -> Result { + match self.use_offchain_storage { + true => self.get_decrypted_state_from_storage(address), + false => self.get_decrypted_state_from_contract(address, block), + } + } + + fn get_decrypted_state_from_storage(&self, address: &Address) -> Result { + let offchain_storage = self.temp_offchain_storage.read(); + match offchain_storage.get(address) { + Some(state) => Ok(state.to_vec()), + None => bail!(ErrorKind::PrivateStateNotFound), + } + } + + fn save_state_to_storage(&self, address: &Address, storage: &HashMap) -> Result<(), Error> { + let mut offchain_storage = self.temp_offchain_storage.write(); + offchain_storage.insert(*address, Self::snapshot_from_storage(storage)); + Ok(()) + } + + fn get_decrypted_state_from_contract(&self, address: &Address, block: BlockId) -> Result { let (data, decoder) = private_contract::functions::state::call(); let value = self.client.call_contract(block, *address, data)?; let state = decoder.decode(&value).map_err(|e| Error::Call(format!("Contract call failed {:?}", e)))?; @@ -608,6 +643,7 @@ impl Provider { Some(c) => Some(self.encrypt(&contract_address, &Self::iv_from_address(&contract_address), &c)?), None => None, }; + self.save_state_to_storage(&address, &storage)?; (enc_code, self.encrypt(&contract_address, &Self::iv_from_transaction(transaction), &Self::snapshot_from_storage(&storage))?) }; Ok(PrivateExecutionResult { From b45ab28628df601a6acff27748c5ebf59935afcd Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Wed, 12 Sep 2018 13:03:04 +0200 Subject: [PATCH 02/35] Temp storage for the private state added --- ethcore/private-tx/src/lib.rs | 9 +++++++-- ethcore/src/client/chain_notify.rs | 4 +++- ethcore/sync/src/api.rs | 1 + 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 18e78c367dd..8efe4933a43 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -280,8 +280,9 @@ impl Provider { let private_state = self.execute_private_transaction(BlockId::Latest, &signed_transaction); if let Err(err) = private_state { match err { - Error(ErrorKind::PrivateTransactionNotFound, _) => { - + Error(ErrorKind::PrivateStateNotFound, _) => { + trace!(target: "privatetx", "Private state for the contract not found, requesting from peers"); + self.request_private_state(&contract); } _ => {} } @@ -503,6 +504,10 @@ impl Provider { self.notify(|notify| notify.broadcast(ChainMessageType::SignedPrivateTransaction(transaction_hash, message.clone()))); } + fn request_private_state(&self, address: &Address) { + self.notify(|notify| notify.broadcast(ChainMessageType::PrivateStateRequest(*address))) + } + fn iv_from_transaction(transaction: &SignedTransaction) -> H128 { let nonce = keccak(&transaction.nonce.rlp_bytes()); let (iv, _) = nonce.as_bytes().split_at(INIT_VEC_LEN); diff --git a/ethcore/src/client/chain_notify.rs b/ethcore/src/client/chain_notify.rs index 5f9b8ed314c..a1bb51b9979 100644 --- a/ethcore/src/client/chain_notify.rs +++ b/ethcore/src/client/chain_notify.rs @@ -15,7 +15,7 @@ // along with Parity Ethereum. If not, see . use bytes::Bytes; -use ethereum_types::{H256, U256}; +use ethereum_types::{H256, U256, Address}; use types::transaction::UnverifiedTransaction; use blockchain::ImportRoute; use std::time::Duration; @@ -29,6 +29,8 @@ pub enum ChainMessageType { PrivateTransaction(H256, Vec), /// Message with signed private transaction SignedPrivateTransaction(H256, Vec), + /// Private state request for the particular private contract + PrivateStateRequest(Address), } /// Route type to indicate whether it is enacted or retracted. diff --git a/ethcore/sync/src/api.rs b/ethcore/sync/src/api.rs index 3a662f8cb6a..6f545b412c2 100644 --- a/ethcore/sync/src/api.rs +++ b/ethcore/sync/src/api.rs @@ -600,6 +600,7 @@ impl ChainNotify for EthSync { self.eth_handler.sync.write().propagate_private_transaction(&mut sync_io, transaction_hash, PrivateTransactionPacket, message), ChainMessageType::SignedPrivateTransaction(transaction_hash, message) => self.eth_handler.sync.write().propagate_private_transaction(&mut sync_io, transaction_hash, SignedPrivateTransactionPacket, message), + ChainMessageType::PrivateStateRequest(address) => {} } }); } From e500400404fce9b43305e138044b2940573aeaad Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Wed, 12 Sep 2018 13:12:52 +0200 Subject: [PATCH 03/35] Request message added --- ethcore/src/test_helpers.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/ethcore/src/test_helpers.rs b/ethcore/src/test_helpers.rs index 32a5f7778da..4ab63846a0a 100644 --- a/ethcore/src/test_helpers.rs +++ b/ethcore/src/test_helpers.rs @@ -514,6 +514,7 @@ impl ChainNotify for TestNotify { ChainMessageType::Consensus(data) => data, ChainMessageType::SignedPrivateTransaction(_, data) => data, ChainMessageType::PrivateTransaction(_, data) => data, + ChainMessageType::PrivateStateRequest(_) => Vec::new(), }; self.messages.write().push(data); } From 89e85fb1bc2d8996115d43a5ba59344db0a2b9e8 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Mon, 18 Mar 2019 12:44:55 +0100 Subject: [PATCH 04/35] Store and retrieve offchain state logic --- ethcore/private-tx/src/lib.rs | 152 +++++++++++++++----------- ethcore/private-tx/src/state_store.rs | 69 ++++++++++++ ethcore/src/client/chain_notify.rs | 2 +- ethcore/sync/src/api.rs | 2 +- 4 files changed, 160 insertions(+), 65 deletions(-) create mode 100644 ethcore/private-tx/src/state_store.rs diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 8efe4933a43..3fa0df49977 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -22,6 +22,7 @@ mod private_transactions; mod messages; mod error; mod log; +mod state_store; extern crate account_state; extern crate client_traits; @@ -79,6 +80,7 @@ pub use private_transactions::{VerifiedPrivateTransaction, VerificationStore, Pr pub use messages::{PrivateTransaction, SignedPrivateTransaction}; pub use error::Error; pub use log::{Logging, TransactionLog, ValidatorLog, PrivateTxStatus, FileLogsSerializer}; +use state_store::{PrivateStateStore}; use std::sync::{Arc, Weak}; use std::collections::{HashMap, HashSet, BTreeMap}; @@ -199,7 +201,7 @@ pub struct Provider { keys_provider: Arc, logging: Option, use_offchain_storage: bool, - temp_offchain_storage: RwLock>, + state_storage: PrivateStateStore, } #[derive(Debug)] @@ -236,7 +238,7 @@ impl Provider { keys_provider, logging: config.logs_path.map(|path| Logging::new(Arc::new(FileLogsSerializer::with_path(path)))), use_offchain_storage: false, - temp_offchain_storage: RwLock::default(), + state_storage: PrivateStateStore::new(), } } @@ -282,6 +284,7 @@ impl Provider { match err { Error(ErrorKind::PrivateStateNotFound, _) => { trace!(target: "privatetx", "Private state for the contract not found, requesting from peers"); + self.state_storage.add_creation_request(signed_transaction); self.request_private_state(&contract); } _ => {} @@ -327,55 +330,51 @@ impl Provider { ) } - /// Retrieve and verify the first available private transaction for every sender - fn process_verification_queue(&self) -> Result<(), Error> { - let process_transaction = |transaction: &VerifiedPrivateTransaction| -> Result<_, String> { - let private_hash = transaction.private_transaction.hash(); - match transaction.validator_account { - None => { + fn process_verification_transaction(&self, transaction: &VerifiedPrivateTransaction) -> Result<(), Error> { + let private_hash = transaction.private_transaction.hash(); + match transaction.validator_account { + None => { + trace!(target: "privatetx", "Propagating transaction further"); + self.broadcast_private_transaction(private_hash, transaction.private_transaction.rlp_bytes()); + return Ok(()); + } + Some(validator_account) => { + if !self.validator_accounts.contains(&validator_account) { trace!(target: "privatetx", "Propagating transaction further"); self.broadcast_private_transaction(private_hash, transaction.private_transaction.rlp_bytes()); return Ok(()); } - Some(validator_account) => { - if !self.validator_accounts.contains(&validator_account) { - trace!(target: "privatetx", "Propagating transaction further"); - self.broadcast_private_transaction(private_hash, transaction.private_transaction.rlp_bytes()); - return Ok(()); - } - let contract = Self::contract_address_from_transaction(&transaction.transaction) - .map_err(|_| "Incorrect type of action for the transaction")?; - // TODO #9825 [ToDr] Usage of BlockId::Latest - let contract_nonce = self.get_contract_nonce(&contract, BlockId::Latest); - if let Err(e) = contract_nonce { - return Err(format!("Cannot retrieve contract nonce: {:?}", e).into()); - } - let contract_nonce = contract_nonce.expect("Error was checked before"); - let private_state = self.execute_private_transaction(BlockId::Latest, &transaction.transaction); - if let Err(e) = private_state { - return Err(format!("Cannot retrieve private state: {:?}", e).into()); - } - let private_state = private_state.expect("Error was checked before"); - let private_state_hash = self.calculate_state_hash(&private_state, contract_nonce); - trace!(target: "privatetx", "Hashed effective private state for validator: {:?}", private_state_hash); - let signed_state = self.accounts.sign(validator_account, private_state_hash); - if let Err(e) = signed_state { - return Err(format!("Cannot sign the state: {:?}", e).into()); - } - let signed_state = signed_state.expect("Error was checked before"); - let signed_private_transaction = SignedPrivateTransaction::new(private_hash, signed_state, None); - trace!(target: "privatetx", "Sending signature for private transaction: {:?}", signed_private_transaction); - self.broadcast_signed_private_transaction(signed_private_transaction.hash(), signed_private_transaction.rlp_bytes()); - } + let contract = Self::contract_address_from_transaction(&transaction.transaction)?; + // TODO #9825 [ToDr] Usage of BlockId::Latest + let contract_nonce = self.get_contract_nonce(&contract, BlockId::Latest)?; + let private_state = self.execute_private_transaction(BlockId::Latest, &transaction.transaction)?; + let private_state_hash = self.calculate_state_hash(&private_state, contract_nonce); + trace!(target: "privatetx", "Hashed effective private state for validator: {:?}", private_state_hash); + let signed_state = self.accounts.sign(validator_account, private_state_hash)?; + let signed_private_transaction = SignedPrivateTransaction::new(private_hash, signed_state, None); + trace!(target: "privatetx", "Sending signature for private transaction: {:?}", signed_private_transaction); + self.broadcast_signed_private_transaction(signed_private_transaction.hash(), signed_private_transaction.rlp_bytes()); } - Ok(()) - }; + } + Ok(()) + } + + /// Retrieve and verify the first available private transaction for every sender + fn process_verification_queue(&self) -> Result<(), Error> { let nonce_cache = NonceCache::new(NONCE_CACHE_SIZE); let local_accounts = HashSet::new(); let ready_transactions = self.transactions_for_verification.drain(self.pool_client(&nonce_cache, &local_accounts)); for transaction in ready_transactions { - if let Err(e) = process_transaction(&transaction) { - warn!(target: "privatetx", "Error: {:?}", e); + if let Err(err) = self.process_verification_transaction(&transaction) { + warn!(target: "privatetx", "Error: {:?}", err); + match err { + Error(ErrorKind::PrivateStateNotFound, _) => { + trace!(target: "privatetx", "Private state for the contract not found, requesting from peers"); + self.state_storage.add_verification_request(transaction); + //self.request_private_state(&contract); + } + _ => {} + } } } Ok(()) @@ -398,6 +397,14 @@ impl Provider { let original_tx_hash = desc.original_transaction.hash(); if last.0 { + let mut saved_state = desc.state; + let contract = Self::contract_address_from_transaction(&desc.original_transaction)?; + if self.use_offchain_storage { + // Save state into the storage and store its hash in the contract + let original_state = saved_state.clone(); + saved_state = keccak(&saved_state).to_vec(); + self.state_storage.save_state(&contract, original_state); + } 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(); @@ -406,7 +413,7 @@ impl Provider { let state = self.client.state_at(BlockId::Latest).ok_or(Error::StatePruned)?; let nonce = state.nonce(&signer_account)?; let public_tx = self.public_transaction( - desc.state.clone(), + saved_state.clone(), &desc.original_transaction, &rsv, nonce, @@ -426,7 +433,6 @@ impl Provider { } } // Notify about state changes - let contract = Self::contract_address_from_transaction(&desc.original_transaction)?; // TODO #9825 Usage of BlockId::Latest if self.get_contract_version(BlockId::Latest, &contract) >= PRIVATE_CONTRACT_WITH_NOTIFICATION_VER { match self.state_changes_notify(BlockId::Latest, &contract, &desc.original_transaction.sender(), desc.original_transaction.hash()) { @@ -505,7 +511,36 @@ impl Provider { } fn request_private_state(&self, address: &Address) { - self.notify(|notify| notify.broadcast(ChainMessageType::PrivateStateRequest(*address))) + // Define the list of available contracts + let mut private_contracts = Vec::new(); + private_contracts.push(*address); + if let Some(key_server_account) = self.keys_provider.key_server_account() { + if let Some(available_contracts) = self.keys_provider.available_keys(BlockId::Latest, &key_server_account) { + for private_contract in available_contracts { + if private_contract == *address { + continue; + } + private_contracts.push(private_contract); + } + } + } + // Check states for the avaialble contracts, if they're outdated + let mut stalled_contracts = Vec::new(); + for address in private_contracts { + if let Ok(on_chain_state) = self.get_decrypted_state_from_contract(&address, BlockId::Latest) { + match self.state_storage.state(&address) { + Ok(stored_state) => { + let stored_state_hash = keccak(&stored_state).to_vec(); + if stored_state_hash != on_chain_state { + stalled_contracts.push(address); + } + } + Err(_) => stalled_contracts.push(address), + } + } + } + trace!(target: "privatetx", "Requesting states for the following contracts: {:?}", stalled_contracts); + self.notify(|notify| notify.broadcast(ChainMessageType::PrivateStateRequest(stalled_contracts.clone()))) } fn iv_from_transaction(transaction: &SignedTransaction) -> H128 { @@ -532,25 +567,11 @@ impl Provider { fn get_decrypted_state(&self, address: &Address, block: BlockId) -> Result { match self.use_offchain_storage { - true => self.get_decrypted_state_from_storage(address), + true => self.state_storage.state(address), false => self.get_decrypted_state_from_contract(address, block), } } - fn get_decrypted_state_from_storage(&self, address: &Address) -> Result { - let offchain_storage = self.temp_offchain_storage.read(); - match offchain_storage.get(address) { - Some(state) => Ok(state.to_vec()), - None => bail!(ErrorKind::PrivateStateNotFound), - } - } - - fn save_state_to_storage(&self, address: &Address, storage: &HashMap) -> Result<(), Error> { - let mut offchain_storage = self.temp_offchain_storage.write(); - offchain_storage.insert(*address, Self::snapshot_from_storage(storage)); - Ok(()) - } - fn get_decrypted_state_from_contract(&self, address: &Address, block: BlockId) -> Result { let (data, decoder) = private_contract::functions::state::call(); let value = self.client.call_contract(block, *address, data)?; @@ -648,7 +669,6 @@ impl Provider { Some(c) => Some(self.encrypt(&contract_address, &Self::iv_from_address(&contract_address), &c)?), None => None, }; - self.save_state_to_storage(&address, &storage)?; (enc_code, self.encrypt(&contract_address, &Self::iv_from_transaction(transaction), &Self::snapshot_from_storage(&storage))?) }; Ok(PrivateExecutionResult { @@ -695,7 +715,13 @@ impl Provider { .ok_or(Error::StatePruned) .and_then(|h| h.decode().map_err(|_| Error::StateIncorrect).into())?; let (executed_code, executed_state) = (executed.code.unwrap_or_default(), executed.state); - let tx_data = Self::generate_constructor(validators, executed_code.clone(), executed_state.clone()); + let mut saved_state = executed_state; + if self.use_offchain_storage { + // Save state into the storage and store its hash in the contract + saved_state = keccak(&saved_state).to_vec(); + self.state_storage.save_state(&executed.contract_address, executed_code.clone()); + } + let tx_data = Self::generate_constructor(validators, executed_code.clone(), saved_state.clone()); let mut tx = Transaction { nonce: nonce, action: Action::Create, @@ -706,7 +732,7 @@ impl Provider { }; tx.gas = match self.client.estimate_gas(&tx.clone().fake_sign(sender), &state, &header) { Ok(estimated_gas) => estimated_gas, - Err(_) => self.estimate_tx_gas(validators, &executed_code, &executed_state, &[]), + Err(_) => self.estimate_tx_gas(validators, &executed_code, &saved_state, &[]), }; Ok((tx, executed.contract_address)) diff --git a/ethcore/private-tx/src/state_store.rs b/ethcore/private-tx/src/state_store.rs new file mode 100644 index 00000000000..245fef1d0a5 --- /dev/null +++ b/ethcore/private-tx/src/state_store.rs @@ -0,0 +1,69 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum 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 Ethereum 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 Ethereum. If not, see . + +use std::collections::{HashMap}; +use std::sync::{Arc}; +use parking_lot::{RwLock}; +use bytes::Bytes; +use ethereum_types::{Address}; +use error::{Error, ErrorKind}; +use types::transaction::{SignedTransaction}; +use private_transactions::{VerifiedPrivateTransaction}; + +/// Wrapper over storage for the private states +pub struct PrivateStateStore { + verification_requests: RwLock>>, + creation_requests: RwLock>, + temp_offchain_storage: RwLock>, +} + +impl PrivateStateStore { + /// Constructs the object + pub fn new() -> Self { + PrivateStateStore { + verification_requests: RwLock::new(Vec::new()), + creation_requests: RwLock::new(Vec::new()), + temp_offchain_storage: RwLock::default(), + } + } + + /// Returns saved state for the address + pub fn state(&self, address: &Address) -> Result { + let offchain_storage = self.temp_offchain_storage.read(); + match offchain_storage.get(address) { + Some(state) => Ok(state.to_vec()), + None => bail!(ErrorKind::PrivateStateNotFound), + } + } + + /// Stores state for the address + pub fn save_state(&self, address: &Address, storage: Bytes) { + let mut offchain_storage = self.temp_offchain_storage.write(); + offchain_storage.insert(*address, storage); + } + + /// Stores verification request for the later verification + pub fn add_verification_request(&self, transaction: Arc) { + let mut verification_requests = self.verification_requests.write(); + verification_requests.push(transaction); + } + + /// Stores creation request for the later creation + pub fn add_creation_request(&self, transaction: SignedTransaction) { + let mut creation_requests = self.creation_requests.write(); + creation_requests.push(transaction); + } +} \ No newline at end of file diff --git a/ethcore/src/client/chain_notify.rs b/ethcore/src/client/chain_notify.rs index a1bb51b9979..4b3f421cb9a 100644 --- a/ethcore/src/client/chain_notify.rs +++ b/ethcore/src/client/chain_notify.rs @@ -30,7 +30,7 @@ pub enum ChainMessageType { /// Message with signed private transaction SignedPrivateTransaction(H256, Vec), /// Private state request for the particular private contract - PrivateStateRequest(Address), + PrivateStateRequest(Vec
), } /// Route type to indicate whether it is enacted or retracted. diff --git a/ethcore/sync/src/api.rs b/ethcore/sync/src/api.rs index 6f545b412c2..e4e488aa060 100644 --- a/ethcore/sync/src/api.rs +++ b/ethcore/sync/src/api.rs @@ -600,7 +600,7 @@ impl ChainNotify for EthSync { self.eth_handler.sync.write().propagate_private_transaction(&mut sync_io, transaction_hash, PrivateTransactionPacket, message), ChainMessageType::SignedPrivateTransaction(transaction_hash, message) => self.eth_handler.sync.write().propagate_private_transaction(&mut sync_io, transaction_hash, SignedPrivateTransactionPacket, message), - ChainMessageType::PrivateStateRequest(address) => {} + ChainMessageType::PrivateStateRequest(addresses) => {} } }); } From 7e41d50dffc57a421d0621e50f88fcef6e83a248 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Wed, 10 Apr 2019 16:05:37 +0200 Subject: [PATCH 05/35] State sync cache --- ethcore/private-tx/src/lib.rs | 36 ++++++---- ethcore/private-tx/src/state_store.rs | 97 ++++++++++++++++++++++++--- 2 files changed, 108 insertions(+), 25 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 3fa0df49977..97f6efbc148 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -369,9 +369,10 @@ impl Provider { warn!(target: "privatetx", "Error: {:?}", err); match err { Error(ErrorKind::PrivateStateNotFound, _) => { - trace!(target: "privatetx", "Private state for the contract not found, requesting from peers"); + let contract = transaction.private_transaction.contract(); + trace!(target: "privatetx", "Private state for the contract {:?} not found, requesting from peers", &contract); self.state_storage.add_verification_request(transaction); - //self.request_private_state(&contract); + self.request_private_state(&contract); } _ => {} } @@ -401,9 +402,10 @@ impl Provider { let contract = Self::contract_address_from_transaction(&desc.original_transaction)?; if self.use_offchain_storage { // Save state into the storage and store its hash in the contract + let current_nonce = self.get_contract_nonce(&contract, BlockId::Latest)?; let original_state = saved_state.clone(); saved_state = keccak(&saved_state).to_vec(); - self.state_storage.save_state(&contract, original_state); + self.state_storage.save_state(&contract, original_state, current_nonce); } let mut signatures = desc.received_signatures.clone(); signatures.push(signed_tx.signature()); @@ -525,22 +527,24 @@ impl Provider { } } // Check states for the avaialble contracts, if they're outdated - let mut stalled_contracts = Vec::new(); + let mut stalled_contracts: Vec<(Address, U256)> = Vec::new(); for address in private_contracts { - if let Ok(on_chain_state) = self.get_decrypted_state_from_contract(&address, BlockId::Latest) { + if let Ok(on_chain_nonce) = self.get_contract_nonce(&address, BlockId::Latest) { match self.state_storage.state(&address) { - Ok(stored_state) => { - let stored_state_hash = keccak(&stored_state).to_vec(); - if stored_state_hash != on_chain_state { - stalled_contracts.push(address); + Ok(stored_state_data) => { + if stored_state_data.nonce < on_chain_nonce { + stalled_contracts.push((address, on_chain_nonce)); } } - Err(_) => stalled_contracts.push(address), + Err(_) => stalled_contracts.push((address, on_chain_nonce)), } } } - trace!(target: "privatetx", "Requesting states for the following contracts: {:?}", stalled_contracts); - self.notify(|notify| notify.broadcast(ChainMessageType::PrivateStateRequest(stalled_contracts.clone()))) + let contracts_to_sync = self.state_storage.start_states_sync(&stalled_contracts); + if !contracts_to_sync.is_empty() { + trace!(target: "privatetx", "Requesting states for the following contracts: {:?}", stalled_contracts); + self.notify(|notify| notify.broadcast(ChainMessageType::PrivateStateRequest(contracts_to_sync.clone()))); + } } fn iv_from_transaction(transaction: &SignedTransaction) -> H128 { @@ -567,7 +571,10 @@ impl Provider { fn get_decrypted_state(&self, address: &Address, block: BlockId) -> Result { match self.use_offchain_storage { - true => self.state_storage.state(address), + true => { + let stored_state_data = self.state_storage.state(address)?; + Ok(stored_state_data.data) + } false => self.get_decrypted_state_from_contract(address, block), } } @@ -718,8 +725,9 @@ impl Provider { let mut saved_state = executed_state; if self.use_offchain_storage { // Save state into the storage and store its hash in the contract + let private_contract_nonce = self.get_contract_nonce(&executed.contract_address, BlockId::Latest)?; saved_state = keccak(&saved_state).to_vec(); - self.state_storage.save_state(&executed.contract_address, executed_code.clone()); + self.state_storage.save_state(&executed.contract_address, executed_code.clone(), private_contract_nonce); } let tx_data = Self::generate_constructor(validators, executed_code.clone(), saved_state.clone()); let mut tx = Transaction { diff --git a/ethcore/private-tx/src/state_store.rs b/ethcore/private-tx/src/state_store.rs index 245fef1d0a5..decd5c46460 100644 --- a/ethcore/private-tx/src/state_store.rs +++ b/ethcore/private-tx/src/state_store.rs @@ -14,20 +14,41 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use std::collections::{HashMap}; -use std::sync::{Arc}; -use parking_lot::{RwLock}; +use std::collections::HashMap; +use std::collections::hash_map::Entry; +use std::sync::Arc; +use parking_lot::RwLock; use bytes::Bytes; -use ethereum_types::{Address}; +use ethereum_types::{Address, U256}; use error::{Error, ErrorKind}; -use types::transaction::{SignedTransaction}; -use private_transactions::{VerifiedPrivateTransaction}; +use types::transaction::SignedTransaction; +use private_transactions::VerifiedPrivateTransaction; + +/// State of the private state sync +#[derive(Clone)] +pub enum SyncState { + /// No sync is running + Idle, + /// Private state sync is running + Syncing, +} + +/// Private state saved in the storage +#[derive(Clone)] +pub struct StoredPrivateState { + /// State data + pub data: Bytes, + /// Corresponding nonce + pub nonce: U256, +} /// Wrapper over storage for the private states pub struct PrivateStateStore { verification_requests: RwLock>>, creation_requests: RwLock>, - temp_offchain_storage: RwLock>, + temp_offchain_storage: RwLock>, + sync_state: RwLock, + syncing_private_states: RwLock>, } impl PrivateStateStore { @@ -37,22 +58,76 @@ impl PrivateStateStore { verification_requests: RwLock::new(Vec::new()), creation_requests: RwLock::new(Vec::new()), temp_offchain_storage: RwLock::default(), + sync_state: RwLock::new(SyncState::Idle), + syncing_private_states: RwLock::default(), + } + } + + /// Current sync state + pub fn current_sync_state(&self) -> SyncState { + (*self.sync_state.read()).clone() + } + + /// Adds information about states being synced now + pub fn start_states_sync(&self, states_to_sync: &Vec<(Address, U256)>) -> Vec
{ + *self.sync_state.write() = SyncState::Syncing; + let mut addresses_to_sync = Vec::new(); + for state in states_to_sync { + if let Some(old_nonce) = self.syncing_private_states.write().insert(state.0, state.1) { + if old_nonce < state.1 { + // Required nonce for the private contract is greater, when requested before, so it needs to be requested again + addresses_to_sync.push(state.0); + } + } else { + // State for this contract was not requested yet + addresses_to_sync.push(state.0); + } + } + addresses_to_sync + } + + pub fn state_sync_completed(&self, synced_states: &Vec<(Address, U256)>) -> Vec
{ + let mut syncing_states = self.syncing_private_states.write(); + let mut addresses_to_store = Vec::new(); + for state in synced_states { + let synced_state = syncing_states.entry(state.0); + match synced_state { + Entry::Occupied(syncing_state) => { + if *syncing_state.get() <= state.1 { + // Received private state is good (nonce as requested or newer), store it + addresses_to_store.push(state.0); + syncing_state.remove_entry(); + } + } + Entry::Vacant(_) => { + warn!(target: "privatetx", "Synced state was not stored for syncing"); + } + } + } + if syncing_states.is_empty() { + // All states were downloaded + *self.sync_state.write() = SyncState::Idle; } + addresses_to_store } /// Returns saved state for the address - pub fn state(&self, address: &Address) -> Result { + pub fn state(&self, address: &Address) -> Result { let offchain_storage = self.temp_offchain_storage.read(); match offchain_storage.get(address) { - Some(state) => Ok(state.to_vec()), + Some(state) => Ok(state.clone()), None => bail!(ErrorKind::PrivateStateNotFound), } } /// Stores state for the address - pub fn save_state(&self, address: &Address, storage: Bytes) { + pub fn save_state(&self, address: &Address, storage: Bytes, nonce: U256) { let mut offchain_storage = self.temp_offchain_storage.write(); - offchain_storage.insert(*address, storage); + let state_to_store = StoredPrivateState { + data: storage, + nonce, + }; + offchain_storage.insert(*address, state_to_store); } /// Stores verification request for the later verification From 53e063d517971c00cb99d6388f9a1cba4aa8436f Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Tue, 23 Apr 2019 12:53:47 +0200 Subject: [PATCH 06/35] Private state column added to key value db --- Cargo.lock | 3 +++ ethcore/db/src/db.rs | 4 +++- ethcore/private-tx/Cargo.toml | 3 +++ ethcore/private-tx/src/lib.rs | 7 ++++++- ethcore/private-tx/src/state_store.rs | 9 ++++++++- ethcore/service/src/service.rs | 1 + parity/db/rocksdb/migration.rs | 11 ++++++++++- 7 files changed, 34 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index be413453a7b..3c9861d095d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1230,6 +1230,7 @@ dependencies = [ "ethabi-derive 8.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore 1.12.0", "ethcore-call-contract 0.1.0", + "ethcore-db 0.1.0", "ethcore-io 1.12.0", "ethcore-miner 1.12.0", "ethereum-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1237,7 +1238,9 @@ dependencies = [ "ethkey 0.3.0", "fetch 0.1.0", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "journaldb 0.2.0", "keccak-hash 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "machine 0.1.0", "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ethcore/db/src/db.rs b/ethcore/db/src/db.rs index d451a7e1e20..8f539063957 100644 --- a/ethcore/db/src/db.rs +++ b/ethcore/db/src/db.rs @@ -41,8 +41,10 @@ pub const COL_ACCOUNT_BLOOM: Option = Some(5); pub const COL_NODE_INFO: Option = Some(6); /// Column for the light client chain. pub const COL_LIGHT_CHAIN: Option = Some(7); +/// Column for the private transactions state. +pub const COL_PRIVATE_TRANSACTIONS_STATE: Option = Some(8); /// Number of columns in DB -pub const NUM_COLUMNS: Option = Some(8); +pub const NUM_COLUMNS: Option = Some(9); /// Modes for updating caches. #[derive(Clone, Copy)] diff --git a/ethcore/private-tx/Cargo.toml b/ethcore/private-tx/Cargo.toml index c0050bbb22c..a3952048e0b 100644 --- a/ethcore/private-tx/Cargo.toml +++ b/ethcore/private-tx/Cargo.toml @@ -14,6 +14,7 @@ ethabi = "8.0" ethabi-contract = "8.0" ethabi-derive = "8.0" ethcore = { path = ".." } +ethcore-db = { path = "../db" } ethcore-call-contract = { path = "../call-contract" } ethcore-io = { path = "../../util/io" } ethcore-miner = { path = "../../miner" } @@ -24,8 +25,10 @@ fetch = { path = "../../util/fetch" } futures = "0.1" parity-util-mem = "0.2.0" keccak-hash = "0.2.0" +kvdb = "0.1" log = "0.4" machine = { path = "../machine" } +journaldb = { path = "../../util/journaldb" } parity-bytes = "0.1" parity-crypto = "0.4.0" parking_lot = "0.8" diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 97f6efbc148..ea9b1b0b7bb 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -30,6 +30,7 @@ extern crate common_types as types; extern crate ethabi; extern crate ethcore; extern crate ethcore_call_contract as call_contract; +extern crate ethcore_db; extern crate ethcore_io as io; extern crate ethcore_miner; extern crate ethereum_types; @@ -39,7 +40,9 @@ extern crate fetch; extern crate futures; extern crate parity_util_mem; extern crate keccak_hash as hash; +extern crate kvdb; extern crate machine; +extern crate journaldb; extern crate parity_bytes as bytes; extern crate parity_crypto as crypto; extern crate parking_lot; @@ -109,6 +112,7 @@ use state_db::StateDB; use account_state::State; use trace::{Tracer, VMTracer}; use call_contract::CallContract; +use kvdb::KeyValueDB; use rustc_hex::FromHex; use ethabi::FunctionOutputDecoder; use vm::CreateContractAddress; @@ -222,6 +226,7 @@ impl Provider { config: ProviderConfig, channel: IoChannel, keys_provider: Arc, + db: Arc, ) -> Self { keys_provider.update_acl_contract(); Provider { @@ -238,7 +243,7 @@ impl Provider { keys_provider, logging: config.logs_path.map(|path| Logging::new(Arc::new(FileLogsSerializer::with_path(path)))), use_offchain_storage: false, - state_storage: PrivateStateStore::new(), + state_storage: PrivateStateStore::new(db), } } diff --git a/ethcore/private-tx/src/state_store.rs b/ethcore/private-tx/src/state_store.rs index decd5c46460..fa8b24b29e7 100644 --- a/ethcore/private-tx/src/state_store.rs +++ b/ethcore/private-tx/src/state_store.rs @@ -19,7 +19,10 @@ use std::collections::hash_map::Entry; use std::sync::Arc; use parking_lot::RwLock; use bytes::Bytes; +use ethcore_db::COL_PRIVATE_TRANSACTIONS_STATE; use ethereum_types::{Address, U256}; +use journaldb::overlaydb::OverlayDB; +use kvdb::KeyValueDB; use error::{Error, ErrorKind}; use types::transaction::SignedTransaction; use private_transactions::VerifiedPrivateTransaction; @@ -47,17 +50,21 @@ pub struct PrivateStateStore { verification_requests: RwLock>>, creation_requests: RwLock>, temp_offchain_storage: RwLock>, + private_state: RwLock, + db: Arc, sync_state: RwLock, syncing_private_states: RwLock>, } impl PrivateStateStore { /// Constructs the object - pub fn new() -> Self { + pub fn new(db: Arc) -> Self { PrivateStateStore { verification_requests: RwLock::new(Vec::new()), creation_requests: RwLock::new(Vec::new()), temp_offchain_storage: RwLock::default(), + private_state: RwLock::new(OverlayDB::new(db.clone(), COL_PRIVATE_TRANSACTIONS_STATE)), + db, sync_state: RwLock::new(SyncState::Idle), syncing_private_states: RwLock::default(), } diff --git a/ethcore/service/src/service.rs b/ethcore/service/src/service.rs index 737d77254f2..a76f1650c23 100644 --- a/ethcore/service/src/service.rs +++ b/ethcore/service/src/service.rs @@ -139,6 +139,7 @@ impl ClientService { private_tx_conf, io_service.channel(), private_keys, + blockchain_db.key_value().clone(), )); let private_tx = Arc::new(PrivateTxService::new(provider)); diff --git a/parity/db/rocksdb/migration.rs b/parity/db/rocksdb/migration.rs index a054f36893a..b6e1db1be21 100644 --- a/parity/db/rocksdb/migration.rs +++ b/parity/db/rocksdb/migration.rs @@ -42,10 +42,18 @@ pub const TO_V12: ChangeColumns = ChangeColumns { version: 12, }; +/// The migration from v12 to v13. +/// Adds a column for private transactions state storage. +pub const TO_V13: ChangeColumns = ChangeColumns { + pre_columns: Some(8), + post_columns: Some(9), + version: 13, +}; + /// Database is assumed to be at default version, when no version file is found. const DEFAULT_VERSION: u32 = 5; /// Current version of database models. -const CURRENT_VERSION: u32 = 13; +const CURRENT_VERSION: u32 = 14; /// A version of database at which blooms-db was introduced const BLOOMS_DB_VERSION: u32 = 13; /// Defines how many items are migrated to the new version of database at once. @@ -147,6 +155,7 @@ fn consolidated_database_migrations(compaction_profile: &CompactionProfile) -> R let mut manager = MigrationManager::new(default_migration_settings(compaction_profile)); manager.add_migration(TO_V11).map_err(|_| Error::MigrationImpossible)?; manager.add_migration(TO_V12).map_err(|_| Error::MigrationImpossible)?; + manager.add_migration(TO_V13).map_err(|_| Error::MigrationImpossible)?; Ok(manager) } From 36f7af1298744cb5307780f757fcbe90afcdeefe Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Tue, 23 Apr 2019 13:54:35 +0200 Subject: [PATCH 07/35] Private state column added to key value db --- ethcore/private-tx/src/lib.rs | 35 ++++++------- ethcore/private-tx/src/state_store.rs | 75 +++++++++------------------ ethcore/src/client/chain_notify.rs | 2 +- 3 files changed, 41 insertions(+), 71 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index ea9b1b0b7bb..acb70663bc7 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -407,10 +407,9 @@ impl Provider { let contract = Self::contract_address_from_transaction(&desc.original_transaction)?; if self.use_offchain_storage { // Save state into the storage and store its hash in the contract - let current_nonce = self.get_contract_nonce(&contract, BlockId::Latest)?; let original_state = saved_state.clone(); saved_state = keccak(&saved_state).to_vec(); - self.state_storage.save_state(&contract, original_state, current_nonce); + self.state_storage.save_state(original_state); } let mut signatures = desc.received_signatures.clone(); signatures.push(signed_tx.signature()); @@ -532,23 +531,21 @@ impl Provider { } } // Check states for the avaialble contracts, if they're outdated - let mut stalled_contracts: Vec<(Address, U256)> = Vec::new(); + let mut stalled_contracts_hashes: Vec = Vec::new(); for address in private_contracts { - if let Ok(on_chain_nonce) = self.get_contract_nonce(&address, BlockId::Latest) { - match self.state_storage.state(&address) { - Ok(stored_state_data) => { - if stored_state_data.nonce < on_chain_nonce { - stalled_contracts.push((address, on_chain_nonce)); - } - } - Err(_) => stalled_contracts.push((address, on_chain_nonce)), + if let Ok(state_hash) = self.get_decrypted_state_from_contract(&address, BlockId::Latest) { + if let Err(_) = self.state_storage.state(&state_hash) { + // State not found in the local db + stalled_contracts_hashes.push(state_hash); } } } - let contracts_to_sync = self.state_storage.start_states_sync(&stalled_contracts); - if !contracts_to_sync.is_empty() { - trace!(target: "privatetx", "Requesting states for the following contracts: {:?}", stalled_contracts); - self.notify(|notify| notify.broadcast(ChainMessageType::PrivateStateRequest(contracts_to_sync.clone()))); + let hashes_to_sync = self.state_storage.start_states_sync(&stalled_contracts_hashes); + if !hashes_to_sync.is_empty() { + trace!(target: "privatetx", "Requesting states for the following hashes: {:?}", hashes_to_sync); + for hash in hashes_to_sync { + self.notify(|notify| notify.broadcast(ChainMessageType::PrivateStateRequest(hash))); + } } } @@ -577,8 +574,9 @@ impl Provider { fn get_decrypted_state(&self, address: &Address, block: BlockId) -> Result { match self.use_offchain_storage { true => { - let stored_state_data = self.state_storage.state(address)?; - Ok(stored_state_data.data) + let hashed_state = self.get_decrypted_state_from_contract(address, block)?; + let stored_state_data = self.state_storage.state(hashed_state)?; + Ok(stored_state_data) } false => self.get_decrypted_state_from_contract(address, block), } @@ -730,9 +728,8 @@ impl Provider { let mut saved_state = executed_state; if self.use_offchain_storage { // Save state into the storage and store its hash in the contract - let private_contract_nonce = self.get_contract_nonce(&executed.contract_address, BlockId::Latest)?; saved_state = keccak(&saved_state).to_vec(); - self.state_storage.save_state(&executed.contract_address, executed_code.clone(), private_contract_nonce); + self.state_storage.save_state(executed_code.clone()); } let tx_data = Self::generate_constructor(validators, executed_code.clone(), saved_state.clone()); let mut tx = Transaction { diff --git a/ethcore/private-tx/src/state_store.rs b/ethcore/private-tx/src/state_store.rs index fa8b24b29e7..d7a796b8c84 100644 --- a/ethcore/private-tx/src/state_store.rs +++ b/ethcore/private-tx/src/state_store.rs @@ -17,10 +17,11 @@ use std::collections::HashMap; use std::collections::hash_map::Entry; use std::sync::Arc; +use hash::keccak; use parking_lot::RwLock; use bytes::Bytes; use ethcore_db::COL_PRIVATE_TRANSACTIONS_STATE; -use ethereum_types::{Address, U256}; +use ethereum_types::H256; use journaldb::overlaydb::OverlayDB; use kvdb::KeyValueDB; use error::{Error, ErrorKind}; @@ -36,24 +37,15 @@ pub enum SyncState { Syncing, } -/// Private state saved in the storage -#[derive(Clone)] -pub struct StoredPrivateState { - /// State data - pub data: Bytes, - /// Corresponding nonce - pub nonce: U256, -} - /// Wrapper over storage for the private states pub struct PrivateStateStore { verification_requests: RwLock>>, creation_requests: RwLock>, - temp_offchain_storage: RwLock>, + temp_offchain_storage: RwLock>, private_state: RwLock, db: Arc, sync_state: RwLock, - syncing_private_states: RwLock>, + syncing_hashes: RwLock>, } impl PrivateStateStore { @@ -66,7 +58,7 @@ impl PrivateStateStore { private_state: RwLock::new(OverlayDB::new(db.clone(), COL_PRIVATE_TRANSACTIONS_STATE)), db, sync_state: RwLock::new(SyncState::Idle), - syncing_private_states: RwLock::default(), + syncing_hashes: RwLock::default(), } } @@ -76,65 +68,46 @@ impl PrivateStateStore { } /// Adds information about states being synced now - pub fn start_states_sync(&self, states_to_sync: &Vec<(Address, U256)>) -> Vec
{ + pub fn start_states_sync(&self, hashes_to_sync: &Vec) -> Vec { *self.sync_state.write() = SyncState::Syncing; - let mut addresses_to_sync = Vec::new(); - for state in states_to_sync { - if let Some(old_nonce) = self.syncing_private_states.write().insert(state.0, state.1) { - if old_nonce < state.1 { - // Required nonce for the private contract is greater, when requested before, so it needs to be requested again - addresses_to_sync.push(state.0); - } - } else { - // State for this contract was not requested yet - addresses_to_sync.push(state.0); + let mut new_hashes = Vec::new(); + for hash in hashes_to_sync { + let mut hashes = self.syncing_hashes.write(); + if hashes.iter().find(|h| h == hash).is_none() { + hashes.push(hash); + new_hashes.push(hash); } } - addresses_to_sync + new_hashes } - pub fn state_sync_completed(&self, synced_states: &Vec<(Address, U256)>) -> Vec
{ - let mut syncing_states = self.syncing_private_states.write(); - let mut addresses_to_store = Vec::new(); - for state in synced_states { - let synced_state = syncing_states.entry(state.0); - match synced_state { - Entry::Occupied(syncing_state) => { - if *syncing_state.get() <= state.1 { - // Received private state is good (nonce as requested or newer), store it - addresses_to_store.push(state.0); - syncing_state.remove_entry(); - } - } - Entry::Vacant(_) => { - warn!(target: "privatetx", "Synced state was not stored for syncing"); - } + pub fn state_sync_completed(&self, synced_states_hashes: &Vec) { + let mut syncing_hashes = self.syncing_hashes.write(); + for hash in synced_states_hashes { + if let Some(index) = syncing_hashes.iter().position(|h| h == hash) { + syncing_hashes.remove(index); } } - if syncing_states.is_empty() { + if syncing_hashes.is_empty() { // All states were downloaded *self.sync_state.write() = SyncState::Idle; } - addresses_to_store } /// Returns saved state for the address - pub fn state(&self, address: &Address) -> Result { + pub fn state(&self, state_hash: &H256) -> Result { let offchain_storage = self.temp_offchain_storage.read(); - match offchain_storage.get(address) { + match offchain_storage.get(state_hash) { Some(state) => Ok(state.clone()), None => bail!(ErrorKind::PrivateStateNotFound), } } /// Stores state for the address - pub fn save_state(&self, address: &Address, storage: Bytes, nonce: U256) { + pub fn save_state(&self, storage: Bytes) { let mut offchain_storage = self.temp_offchain_storage.write(); - let state_to_store = StoredPrivateState { - data: storage, - nonce, - }; - offchain_storage.insert(*address, state_to_store); + let state_hash = keccak(storage); + offchain_storage.insert(state_hash, storage); } /// Stores verification request for the later verification diff --git a/ethcore/src/client/chain_notify.rs b/ethcore/src/client/chain_notify.rs index 4b3f421cb9a..c43dfa1e0b2 100644 --- a/ethcore/src/client/chain_notify.rs +++ b/ethcore/src/client/chain_notify.rs @@ -30,7 +30,7 @@ pub enum ChainMessageType { /// Message with signed private transaction SignedPrivateTransaction(H256, Vec), /// Private state request for the particular private contract - PrivateStateRequest(Vec
), + PrivateStateRequest(H256), } /// Route type to indicate whether it is enacted or retracted. From 3fbd248bb3816d9913d06cca57c556535befa9e8 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Tue, 23 Apr 2019 14:07:37 +0200 Subject: [PATCH 08/35] Indexing stored states via its hash --- ethcore/private-tx/src/lib.rs | 4 +++- ethcore/private-tx/src/state_store.rs | 9 ++++----- ethcore/src/client/chain_notify.rs | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index acb70663bc7..ea70c2816ef 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -534,6 +534,7 @@ impl Provider { let mut stalled_contracts_hashes: Vec = Vec::new(); for address in private_contracts { if let Ok(state_hash) = self.get_decrypted_state_from_contract(&address, BlockId::Latest) { + let state_hash = H256::from_slice(&state_hash); if let Err(_) = self.state_storage.state(&state_hash) { // State not found in the local db stalled_contracts_hashes.push(state_hash); @@ -575,7 +576,8 @@ impl Provider { match self.use_offchain_storage { true => { let hashed_state = self.get_decrypted_state_from_contract(address, block)?; - let stored_state_data = self.state_storage.state(hashed_state)?; + let hashed_state = H256::from_slice(&hashed_state); + let stored_state_data = self.state_storage.state(&hashed_state)?; Ok(stored_state_data) } false => self.get_decrypted_state_from_contract(address, block), diff --git a/ethcore/private-tx/src/state_store.rs b/ethcore/private-tx/src/state_store.rs index d7a796b8c84..5ea59ff0313 100644 --- a/ethcore/private-tx/src/state_store.rs +++ b/ethcore/private-tx/src/state_store.rs @@ -15,7 +15,6 @@ // along with Parity Ethereum. If not, see . use std::collections::HashMap; -use std::collections::hash_map::Entry; use std::sync::Arc; use hash::keccak; use parking_lot::RwLock; @@ -73,9 +72,9 @@ impl PrivateStateStore { let mut new_hashes = Vec::new(); for hash in hashes_to_sync { let mut hashes = self.syncing_hashes.write(); - if hashes.iter().find(|h| h == hash).is_none() { - hashes.push(hash); - new_hashes.push(hash); + if hashes.iter().find(|&h| h == hash).is_none() { + hashes.push(*hash); + new_hashes.push(*hash); } } new_hashes @@ -106,7 +105,7 @@ impl PrivateStateStore { /// Stores state for the address pub fn save_state(&self, storage: Bytes) { let mut offchain_storage = self.temp_offchain_storage.write(); - let state_hash = keccak(storage); + let state_hash = keccak(&storage); offchain_storage.insert(state_hash, storage); } diff --git a/ethcore/src/client/chain_notify.rs b/ethcore/src/client/chain_notify.rs index c43dfa1e0b2..1567f6c5c9e 100644 --- a/ethcore/src/client/chain_notify.rs +++ b/ethcore/src/client/chain_notify.rs @@ -15,7 +15,7 @@ // along with Parity Ethereum. If not, see . use bytes::Bytes; -use ethereum_types::{H256, U256, Address}; +use ethereum_types::{H256, U256}; use types::transaction::UnverifiedTransaction; use blockchain::ImportRoute; use std::time::Duration; From 698ca5f0e6d558bbd5afe9d5c5faffe81092c3ab Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Fri, 26 Apr 2019 12:18:50 +0200 Subject: [PATCH 09/35] Works with errors changed --- ethcore/private-tx/src/lib.rs | 6 +++--- ethcore/private-tx/src/state_store.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index ea70c2816ef..24ec0e80b74 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -287,14 +287,14 @@ impl Provider { let private_state = self.execute_private_transaction(BlockId::Latest, &signed_transaction); if let Err(err) = private_state { match err { - Error(ErrorKind::PrivateStateNotFound, _) => { + Error::PrivateStateNotFound => { trace!(target: "privatetx", "Private state for the contract not found, requesting from peers"); self.state_storage.add_creation_request(signed_transaction); self.request_private_state(&contract); } _ => {} } - bail!(err); + return Err(err); } let private_state = private_state.expect("Valid state since this"); trace!(target: "privatetx", "Private transaction created, encrypted transaction: {:?}, private state: {:?}", private, private_state); @@ -373,7 +373,7 @@ impl Provider { if let Err(err) = self.process_verification_transaction(&transaction) { warn!(target: "privatetx", "Error: {:?}", err); match err { - Error(ErrorKind::PrivateStateNotFound, _) => { + Error::PrivateStateNotFound => { let contract = transaction.private_transaction.contract(); trace!(target: "privatetx", "Private state for the contract {:?} not found, requesting from peers", &contract); self.state_storage.add_verification_request(transaction); diff --git a/ethcore/private-tx/src/state_store.rs b/ethcore/private-tx/src/state_store.rs index 5ea59ff0313..928f3faa00f 100644 --- a/ethcore/private-tx/src/state_store.rs +++ b/ethcore/private-tx/src/state_store.rs @@ -23,7 +23,7 @@ use ethcore_db::COL_PRIVATE_TRANSACTIONS_STATE; use ethereum_types::H256; use journaldb::overlaydb::OverlayDB; use kvdb::KeyValueDB; -use error::{Error, ErrorKind}; +use error::Error; use types::transaction::SignedTransaction; use private_transactions::VerifiedPrivateTransaction; @@ -98,7 +98,7 @@ impl PrivateStateStore { let offchain_storage = self.temp_offchain_storage.read(); match offchain_storage.get(state_hash) { Some(state) => Ok(state.clone()), - None => bail!(ErrorKind::PrivateStateNotFound), + None => Err(Error::PrivateStateNotFound), } } From 55d8af13c98d9bc1d66b345b8723fb297ba22612 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Fri, 26 Apr 2019 12:40:20 +0200 Subject: [PATCH 10/35] Private state stored into the local db --- Cargo.lock | 2 ++ ethcore/private-tx/Cargo.toml | 2 ++ ethcore/private-tx/src/error.rs | 5 ++++- ethcore/private-tx/src/lib.rs | 6 ++++-- ethcore/private-tx/src/state_store.rs | 25 +++++++++++-------------- 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3c9861d095d..97eb18549c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1238,6 +1238,8 @@ dependencies = [ "ethkey 0.3.0", "fetch 0.1.0", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "journaldb 0.2.0", "keccak-hash 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "kvdb 0.1.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 a3952048e0b..5a38da1762b 100644 --- a/ethcore/private-tx/Cargo.toml +++ b/ethcore/private-tx/Cargo.toml @@ -24,6 +24,8 @@ ethkey = { path = "../../accounts/ethkey" } fetch = { path = "../../util/fetch" } futures = "0.1" parity-util-mem = "0.2.0" +hash-db = "0.11.0" +heapsize = "0.4" keccak-hash = "0.2.0" kvdb = "0.1" log = "0.4" diff --git a/ethcore/private-tx/src/error.rs b/ethcore/private-tx/src/error.rs index 6e8a804f4fc..dada209246b 100644 --- a/ethcore/private-tx/src/error.rs +++ b/ethcore/private-tx/src/error.rs @@ -101,7 +101,10 @@ pub enum Error { KeyServerAccountNotSet, /// Private state for the contract was not found in the local storage. #[display(fmt = "Private state for the contract was not found in the local storage.")] - PrivateStateNotFound, + PrivateStateNotFound, + /// Cannot write state to the local database. + #[display(fmt = "Cannot write state to the local database.")] + DatabaseWriteError, /// Encryption key is not found on key server. #[display(fmt = "Encryption key is not found on key server for {}", _0)] EncryptionKeyNotFound(Address), diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 24ec0e80b74..eee394c8d9a 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -39,6 +39,8 @@ extern crate ethkey; extern crate fetch; extern crate futures; extern crate parity_util_mem; +extern crate hash_db; +extern crate heapsize; extern crate keccak_hash as hash; extern crate kvdb; extern crate machine; @@ -409,7 +411,7 @@ impl Provider { // Save state into the storage and store its hash in the contract let original_state = saved_state.clone(); saved_state = keccak(&saved_state).to_vec(); - self.state_storage.save_state(original_state); + self.state_storage.save_state(&original_state)?; } let mut signatures = desc.received_signatures.clone(); signatures.push(signed_tx.signature()); @@ -731,7 +733,7 @@ impl Provider { if self.use_offchain_storage { // Save state into the storage and store its hash in the contract saved_state = keccak(&saved_state).to_vec(); - self.state_storage.save_state(executed_code.clone()); + self.state_storage.save_state(&executed_code)?; } let tx_data = Self::generate_constructor(validators, executed_code.clone(), saved_state.clone()); let mut tx = Transaction { diff --git a/ethcore/private-tx/src/state_store.rs b/ethcore/private-tx/src/state_store.rs index 928f3faa00f..32d63bb6307 100644 --- a/ethcore/private-tx/src/state_store.rs +++ b/ethcore/private-tx/src/state_store.rs @@ -14,15 +14,14 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use std::collections::HashMap; use std::sync::Arc; -use hash::keccak; use parking_lot::RwLock; use bytes::Bytes; use ethcore_db::COL_PRIVATE_TRANSACTIONS_STATE; use ethereum_types::H256; use journaldb::overlaydb::OverlayDB; -use kvdb::KeyValueDB; +use kvdb::{KeyValueDB, DBTransaction}; +use hash_db::HashDB; use error::Error; use types::transaction::SignedTransaction; use private_transactions::VerifiedPrivateTransaction; @@ -40,7 +39,6 @@ pub enum SyncState { pub struct PrivateStateStore { verification_requests: RwLock>>, creation_requests: RwLock>, - temp_offchain_storage: RwLock>, private_state: RwLock, db: Arc, sync_state: RwLock, @@ -53,7 +51,6 @@ impl PrivateStateStore { PrivateStateStore { verification_requests: RwLock::new(Vec::new()), creation_requests: RwLock::new(Vec::new()), - temp_offchain_storage: RwLock::default(), private_state: RwLock::new(OverlayDB::new(db.clone(), COL_PRIVATE_TRANSACTIONS_STATE)), db, sync_state: RwLock::new(SyncState::Idle), @@ -95,18 +92,18 @@ impl PrivateStateStore { /// Returns saved state for the address pub fn state(&self, state_hash: &H256) -> Result { - let offchain_storage = self.temp_offchain_storage.read(); - match offchain_storage.get(state_hash) { - Some(state) => Ok(state.clone()), - None => Err(Error::PrivateStateNotFound), - } + let private_state = self.private_state.read(); + private_state.get(state_hash).map(|s| s.to_vec()).ok_or(Error::PrivateStateNotFound) } /// Stores state for the address - pub fn save_state(&self, storage: Bytes) { - let mut offchain_storage = self.temp_offchain_storage.write(); - let state_hash = keccak(&storage); - offchain_storage.insert(state_hash, storage); + pub fn save_state(&self, storage: &Bytes) -> Result { + let mut private_state = self.private_state.write(); + let state_hash = private_state.insert(storage); + let mut transaction = DBTransaction::new(); + private_state.commit_to_batch(&mut transaction)?; + self.db.write(transaction).map_err(|_| Error::DatabaseWriteError)?; + Ok(state_hash) } /// Stores verification request for the later verification From 763105cf63f31d1c880ea0a2e462afdc0fda01ab Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Fri, 10 May 2019 16:27:14 +0200 Subject: [PATCH 11/35] Access to private state db added to sync io --- ethcore/private-tx/src/lib.rs | 21 ++++++--- ethcore/private-tx/src/state_store.rs | 37 ++++----------- ethcore/private-tx/tests/private_contract.rs | 4 ++ ethcore/sync/Cargo.toml | 2 +- ethcore/sync/src/api.rs | 48 ++++++++++++++++---- ethcore/sync/src/lib.rs | 2 +- ethcore/sync/src/sync_io.rs | 21 +++++++-- ethcore/sync/src/tests/helpers.rs | 6 +++ ethcore/sync/src/tests/private.rs | 6 ++- parity/modules.rs | 3 ++ parity/run.rs | 7 +-- 11 files changed, 103 insertions(+), 54 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index eee394c8d9a..088568b4bfb 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -23,6 +23,7 @@ mod messages; mod error; mod log; mod state_store; +mod private_state_db; extern crate account_state; extern crate client_traits; @@ -83,9 +84,10 @@ 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 private_state_db::PrivateStateDB; pub use error::Error; pub use log::{Logging, TransactionLog, ValidatorLog, PrivateTxStatus, FileLogsSerializer}; -use state_store::{PrivateStateStore}; +use state_store::PrivateStateStorage; use std::sync::{Arc, Weak}; use std::collections::{HashMap, HashSet, BTreeMap}; @@ -207,7 +209,7 @@ pub struct Provider { keys_provider: Arc, logging: Option, use_offchain_storage: bool, - state_storage: PrivateStateStore, + state_storage: PrivateStateStorage, } #[derive(Debug)] @@ -245,10 +247,15 @@ impl Provider { keys_provider, logging: config.logs_path.map(|path| Logging::new(Arc::new(FileLogsSerializer::with_path(path)))), use_offchain_storage: false, - state_storage: PrivateStateStore::new(db), + state_storage: PrivateStateStorage::new(db), } } + /// Returns private state DB + pub fn private_state_db(&self) -> Arc { + self.state_storage.private_state_db() + } + // TODO [ToDr] Don't use `ChainNotify` here! // Better to create a separate notification type for this. /// Adds an actor to be notified on certain events @@ -411,7 +418,7 @@ impl Provider { // Save state into the storage and store its hash in the contract let original_state = saved_state.clone(); saved_state = keccak(&saved_state).to_vec(); - self.state_storage.save_state(&original_state)?; + self.state_storage.private_state_db().save_state(&original_state)?; } let mut signatures = desc.received_signatures.clone(); signatures.push(signed_tx.signature()); @@ -537,7 +544,7 @@ impl Provider { for address in private_contracts { if let Ok(state_hash) = self.get_decrypted_state_from_contract(&address, BlockId::Latest) { let state_hash = H256::from_slice(&state_hash); - if let Err(_) = self.state_storage.state(&state_hash) { + if let Err(_) = self.state_storage.private_state_db().state(&state_hash) { // State not found in the local db stalled_contracts_hashes.push(state_hash); } @@ -579,7 +586,7 @@ impl Provider { true => { let hashed_state = self.get_decrypted_state_from_contract(address, block)?; let hashed_state = H256::from_slice(&hashed_state); - let stored_state_data = self.state_storage.state(&hashed_state)?; + let stored_state_data = self.state_storage.private_state_db().state(&hashed_state)?; Ok(stored_state_data) } false => self.get_decrypted_state_from_contract(address, block), @@ -733,7 +740,7 @@ impl Provider { if self.use_offchain_storage { // Save state into the storage and store its hash in the contract saved_state = keccak(&saved_state).to_vec(); - self.state_storage.save_state(&executed_code)?; + self.state_storage.private_state_db().save_state(&executed_code)?; } let tx_data = Self::generate_constructor(validators, executed_code.clone(), saved_state.clone()); let mut tx = Transaction { diff --git a/ethcore/private-tx/src/state_store.rs b/ethcore/private-tx/src/state_store.rs index 32d63bb6307..67c384d186a 100644 --- a/ethcore/private-tx/src/state_store.rs +++ b/ethcore/private-tx/src/state_store.rs @@ -16,15 +16,11 @@ use std::sync::Arc; use parking_lot::RwLock; -use bytes::Bytes; -use ethcore_db::COL_PRIVATE_TRANSACTIONS_STATE; use ethereum_types::H256; -use journaldb::overlaydb::OverlayDB; -use kvdb::{KeyValueDB, DBTransaction}; -use hash_db::HashDB; -use error::Error; +use kvdb::KeyValueDB; use types::transaction::SignedTransaction; use private_transactions::VerifiedPrivateTransaction; +use private_state_db::PrivateStateDB; /// State of the private state sync #[derive(Clone)] @@ -36,23 +32,21 @@ pub enum SyncState { } /// Wrapper over storage for the private states -pub struct PrivateStateStore { +pub struct PrivateStateStorage { verification_requests: RwLock>>, creation_requests: RwLock>, - private_state: RwLock, - db: Arc, + private_state_db: Arc, sync_state: RwLock, syncing_hashes: RwLock>, } -impl PrivateStateStore { +impl PrivateStateStorage { /// Constructs the object pub fn new(db: Arc) -> Self { - PrivateStateStore { + PrivateStateStorage { verification_requests: RwLock::new(Vec::new()), creation_requests: RwLock::new(Vec::new()), - private_state: RwLock::new(OverlayDB::new(db.clone(), COL_PRIVATE_TRANSACTIONS_STATE)), - db, + private_state_db: Arc::new(PrivateStateDB::new(db)), sync_state: RwLock::new(SyncState::Idle), syncing_hashes: RwLock::default(), } @@ -90,20 +84,9 @@ impl PrivateStateStore { } } - /// Returns saved state for the address - pub fn state(&self, state_hash: &H256) -> Result { - let private_state = self.private_state.read(); - private_state.get(state_hash).map(|s| s.to_vec()).ok_or(Error::PrivateStateNotFound) - } - - /// Stores state for the address - pub fn save_state(&self, storage: &Bytes) -> Result { - let mut private_state = self.private_state.write(); - let state_hash = private_state.insert(storage); - let mut transaction = DBTransaction::new(); - private_state.commit_to_batch(&mut transaction)?; - self.db.write(transaction).map_err(|_| Error::DatabaseWriteError)?; - Ok(state_hash) + /// Returns underlying DB + pub fn private_state_db(&self) -> Arc { + self.private_state_db.clone() } /// Stores verification request for the later verification diff --git a/ethcore/private-tx/tests/private_contract.rs b/ethcore/private-tx/tests/private_contract.rs index e4ee445df11..dc114f6dae1 100644 --- a/ethcore/private-tx/tests/private_contract.rs +++ b/ethcore/private-tx/tests/private_contract.rs @@ -70,6 +70,7 @@ fn private_contract() { let io = ethcore_io::IoChannel::disconnected(); let miner = Arc::new(Miner::new_for_tests(&spec::new_test(), None)); let private_keys = Arc::new(StoringKeyProvider::default()); + let db = new_db(); let pm = Arc::new(Provider::new( client.clone(), miner, @@ -78,6 +79,7 @@ fn private_contract() { config, io, private_keys, + db.key_value().clone(), )); let (address, _) = contract_address(CreateContractAddress::FromSenderAndNonce, &key1.address(), &0.into(), &[]); @@ -205,6 +207,7 @@ fn call_other_private_contract() { let io = ethcore_io::IoChannel::disconnected(); let miner = Arc::new(Miner::new_for_tests(&spec::new_test(), None)); let private_keys = Arc::new(StoringKeyProvider::default()); + let db = new_db(); let pm = Arc::new(Provider::new( client.clone(), miner, @@ -213,6 +216,7 @@ fn call_other_private_contract() { config, io, private_keys.clone(), + db.key_value().clone(), )); // Deploy contract A diff --git a/ethcore/sync/Cargo.toml b/ethcore/sync/Cargo.toml index df35a4bab96..e0f1a3fe5f1 100644 --- a/ethcore/sync/Cargo.toml +++ b/ethcore/sync/Cargo.toml @@ -18,6 +18,7 @@ ethcore-light = { path = "../light" } ethcore-network = { path = "../../util/network" } ethcore-network-devp2p = { path = "../../util/network-devp2p" } ethereum-types = "0.6.0" +ethcore-private-tx = { path = "../private-tx" } ethkey = { path = "../../accounts/ethkey" } ethstore = { path = "../../accounts/ethstore" } fastmap = { path = "../../util/fastmap" } @@ -42,7 +43,6 @@ parity-runtime = { path = "../../util/runtime" } env_logger = "0.5" ethcore = { path = "..", features = ["test-helpers"] } ethcore-io = { path = "../../util/io", features = ["mio"] } -ethcore-private-tx = { path = "../private-tx" } kvdb-memorydb = "0.1" rustc-hex = "1.0" rand_xorshift = "0.1.1" diff --git a/ethcore/sync/src/api.rs b/ethcore/sync/src/api.rs index e4e488aa060..dbe66876335 100644 --- a/ethcore/sync/src/api.rs +++ b/ethcore/sync/src/api.rs @@ -35,6 +35,7 @@ use ethkey::Secret; use ethcore::client::{ChainNotify, NewBlocks, ChainMessageType}; use client_traits::BlockChainClient; use ethcore::snapshot::SnapshotService; +use ethcore_private_tx::PrivateStateDB; use types::BlockNumber; use sync_io::NetSyncIo; use chain::{ChainSyncApi, SyncStatus as EthSyncStatus}; @@ -263,6 +264,8 @@ pub struct Params { pub snapshot_service: Arc, /// Private tx service. pub private_tx_handler: Option>, + /// Private state wrapper + pub private_state: Option>, /// Light data provider. pub provider: Arc, /// Network layer configuration. @@ -377,6 +380,7 @@ impl EthSync { chain: params.chain, snapshot_service: params.snapshot_service, overlay: RwLock::new(HashMap::new()), + private_state: params.private_state, }), light_proto: light_proto, subprotocol_name: params.config.subprotocol_name, @@ -460,6 +464,8 @@ struct SyncProtocolHandler { sync: ChainSyncApi, /// Chain overlay used to cache data such as fork block. overlay: RwLock>, + /// Private state db + private_state: Option>, } impl NetworkProtocolHandler for SyncProtocolHandler { @@ -475,7 +481,12 @@ impl NetworkProtocolHandler for SyncProtocolHandler { } fn read(&self, io: &dyn NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) { - self.sync.dispatch_packet(&mut NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay), *peer, packet_id, data); + self.sync.dispatch_packet(&mut NetSyncIo::new(io, + &*self.chain, + &*self.snapshot_service, + &self.overlay, + &self.private_state), + *peer, packet_id, data); } fn connected(&self, io: &dyn NetworkContext, peer: &PeerId) { @@ -484,20 +495,30 @@ impl NetworkProtocolHandler for SyncProtocolHandler { let warp_protocol = io.protocol_version(WARP_SYNC_PROTOCOL_ID, *peer).unwrap_or(0) != 0; let warp_context = io.subprotocol_name() == WARP_SYNC_PROTOCOL_ID; if warp_protocol == warp_context { - self.sync.write().on_peer_connected(&mut NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay), *peer); + self.sync.write().on_peer_connected(&mut NetSyncIo::new(io, + &*self.chain, + &*self.snapshot_service, + &self.overlay, + &self.private_state), + *peer); } } fn disconnected(&self, io: &dyn NetworkContext, peer: &PeerId) { trace_time!("sync::disconnected"); if io.subprotocol_name() != WARP_SYNC_PROTOCOL_ID { - self.sync.write().on_peer_aborting(&mut NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay), *peer); + self.sync.write().on_peer_aborting(&mut NetSyncIo::new(io, + &*self.chain, + &*self.snapshot_service, + &self.overlay, + &self.private_state), + *peer); } } fn timeout(&self, io: &dyn NetworkContext, timer: TimerToken) { trace_time!("sync::timeout"); - let mut io = NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay); + let mut io = NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay, &self.private_state); match timer { PEERS_TIMER => self.sync.write().maintain_peers(&mut io), MAINTAIN_SYNC_TIMER => self.sync.write().maintain_sync(&mut io), @@ -528,8 +549,11 @@ impl ChainNotify for EthSync { use light::net::Announcement; self.network.with_context(self.subprotocol_name, |context| { - let mut sync_io = NetSyncIo::new(context, &*self.eth_handler.chain, &*self.eth_handler.snapshot_service, - &self.eth_handler.overlay); + let mut sync_io = NetSyncIo::new(context, + &*self.eth_handler.chain, + &*self.eth_handler.snapshot_service, + &self.eth_handler.overlay, + &self.eth_handler.private_state); self.eth_handler.sync.write().chain_new_blocks( &mut sync_io, &new_blocks.imported, @@ -593,7 +617,11 @@ impl ChainNotify for EthSync { fn broadcast(&self, message_type: ChainMessageType) { self.network.with_context(WARP_SYNC_PROTOCOL_ID, |context| { - let mut sync_io = NetSyncIo::new(context, &*self.eth_handler.chain, &*self.eth_handler.snapshot_service, &self.eth_handler.overlay); + let mut sync_io = NetSyncIo::new(context, + &*self.eth_handler.chain, + &*self.eth_handler.snapshot_service, + &self.eth_handler.overlay, + &self.eth_handler.private_state); match message_type { ChainMessageType::Consensus(message) => self.eth_handler.sync.write().propagate_consensus_packet(&mut sync_io, message), ChainMessageType::PrivateTransaction(transaction_hash, message) => @@ -665,7 +693,11 @@ impl ManageNetwork for EthSync { fn stop_network(&self) { self.network.with_context(self.subprotocol_name, |context| { - let mut sync_io = NetSyncIo::new(context, &*self.eth_handler.chain, &*self.eth_handler.snapshot_service, &self.eth_handler.overlay); + let mut sync_io = NetSyncIo::new(context, + &*self.eth_handler.chain, + &*self.eth_handler.snapshot_service, + &self.eth_handler.overlay, + &self.eth_handler.private_state); self.eth_handler.sync.write().abort(&mut sync_io); }); diff --git a/ethcore/sync/src/lib.rs b/ethcore/sync/src/lib.rs index 674f0db4443..eb1cdb98ab3 100644 --- a/ethcore/sync/src/lib.rs +++ b/ethcore/sync/src/lib.rs @@ -35,6 +35,7 @@ extern crate keccak_hash as hash; extern crate parity_bytes as bytes; extern crate parity_runtime; extern crate parking_lot; +extern crate ethcore_private_tx; extern crate rand; extern crate rlp; extern crate triehash_ethereum; @@ -43,7 +44,6 @@ extern crate futures; extern crate ethcore_light as light; #[cfg(test)] extern crate env_logger; -#[cfg(test)] extern crate ethcore_private_tx; #[cfg(test)] extern crate kvdb_memorydb; #[cfg(test)] extern crate rustc_hex; #[cfg(test)] extern crate rand_xorshift; diff --git a/ethcore/sync/src/sync_io.rs b/ethcore/sync/src/sync_io.rs index b7a25bafda8..f2803828721 100644 --- a/ethcore/sync/src/sync_io.rs +++ b/ethcore/sync/src/sync_io.rs @@ -14,12 +14,14 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . +use std::sync::Arc; use std::collections::HashMap; use chain::sync_packet::{PacketInfo, SyncPacket}; use network::{NetworkContext, PeerId, PacketId, Error, SessionInfo, ProtocolId}; use network::client_version::ClientVersion; use bytes::Bytes; use client_traits::BlockChainClient; +use ethcore_private_tx::PrivateStateDB; use types::BlockNumber; use ethcore::snapshot::SnapshotService; use parking_lot::RwLock; @@ -40,6 +42,8 @@ pub trait SyncIo { fn chain(&self) -> &dyn BlockChainClient; /// Get the snapshot service. fn snapshot_service(&self) -> &dyn SnapshotService; + /// Get the private state wrapper + fn private_state(&self) -> &Option>; /// Returns peer version identifier fn peer_version(&self, peer_id: PeerId) -> ClientVersion { ClientVersion::from(peer_id.to_string()) @@ -68,6 +72,7 @@ pub struct NetSyncIo<'s> { chain: &'s dyn BlockChainClient, snapshot_service: &'s dyn SnapshotService, chain_overlay: &'s RwLock>, + private_state: &'s Option>, } impl<'s> NetSyncIo<'s> { @@ -75,12 +80,14 @@ impl<'s> NetSyncIo<'s> { pub fn new(network: &'s dyn NetworkContext, chain: &'s dyn BlockChainClient, snapshot_service: &'s dyn SnapshotService, - chain_overlay: &'s RwLock>) -> NetSyncIo<'s> { + chain_overlay: &'s RwLock>, + private_state: &'s Option>) -> NetSyncIo<'s> { NetSyncIo { - network: network, - chain: chain, - snapshot_service: snapshot_service, - chain_overlay: chain_overlay, + network, + chain, + snapshot_service, + chain_overlay, + private_state, } } } @@ -114,6 +121,10 @@ impl<'s> SyncIo for NetSyncIo<'s> { self.snapshot_service } + fn private_state(&self) -> &Option> { + self.private_state + } + fn peer_session_info(&self, peer_id: PeerId) -> Option { self.network.session_info(peer_id) } diff --git a/ethcore/sync/src/tests/helpers.rs b/ethcore/sync/src/tests/helpers.rs index 017e8b3ee24..5ac807fc4ef 100644 --- a/ethcore/sync/src/tests/helpers.rs +++ b/ethcore/sync/src/tests/helpers.rs @@ -27,6 +27,7 @@ use ethcore::client::{TestBlockChainClient, Client as EthcoreClient, ClientConfig, ChainNotify, NewBlocks, ChainMessageType, ClientIoMessage}; use ethcore::snapshot::SnapshotService; use ethcore::spec::{self, Spec}; +use ethcore_private_tx::PrivateStateDB; use ethcore::miner::Miner; use ethcore::test_helpers; use sync_io::SyncIo; @@ -131,6 +132,10 @@ impl<'p, C> SyncIo for TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p { self.snapshot_service } + fn private_state(&self) -> &Option> { + &None + } + fn peer_session_info(&self, _peer_id: PeerId) -> Option { None } @@ -239,6 +244,7 @@ impl EthPeer where C: FlushingBlockChainClient { self.sync.write().propagate_private_transaction(&mut io, transaction_hash, PrivateTransactionPacket, data), ChainMessageType::SignedPrivateTransaction(transaction_hash, data) => self.sync.write().propagate_private_transaction(&mut io, transaction_hash, SignedPrivateTransactionPacket, data), + ChainMessageType::PrivateStateRequest(addresses) => {} } } diff --git a/ethcore/sync/src/tests/private.rs b/ethcore/sync/src/tests/private.rs index 1d91e212403..1c600024dab 100644 --- a/ethcore/sync/src/tests/private.rs +++ b/ethcore/sync/src/tests/private.rs @@ -26,7 +26,7 @@ use ethcore::{ client::ClientIoMessage, miner::{self, MinerService}, spec::Spec, - test_helpers::push_block_with_transactions, + test_helpers::{push_block_with_transactions, new_db}, }; use ethcore_private_tx::{Provider, ProviderConfig, NoopEncryptor, Importer, SignedPrivateTransaction, StoringKeyProvider}; use ethkey::KeyPair; @@ -82,7 +82,7 @@ fn send_private_transaction() { }; let private_keys = Arc::new(StoringKeyProvider::default()); - + let db = new_db(); let pm0 = Arc::new(Provider::new( client0.clone(), net.peer(0).miner.clone(), @@ -91,6 +91,7 @@ fn send_private_transaction() { signer_config, IoChannel::to_handler(Arc::downgrade(&io_handler0)), private_keys.clone(), + db.key_value().clone(), )); pm0.add_notify(net.peers[0].clone()); @@ -102,6 +103,7 @@ fn send_private_transaction() { validator_config, IoChannel::to_handler(Arc::downgrade(&io_handler1)), private_keys.clone(), + db.key_value().clone(), )); pm1.add_notify(net.peers[1].clone()); diff --git a/parity/modules.rs b/parity/modules.rs index f8607b827ff..7b56fa6d5f3 100644 --- a/parity/modules.rs +++ b/parity/modules.rs @@ -19,6 +19,7 @@ use std::sync::{Arc, mpsc}; use client_traits::BlockChainClient; use sync::{self, SyncConfig, NetworkConfiguration, Params, ConnectionFilter}; use ethcore::snapshot::SnapshotService; +use ethcore_private_tx::PrivateStateDB; use light::Provider; use parity_runtime::Executor; @@ -40,6 +41,7 @@ pub fn sync( chain: Arc, snapshot_service: Arc, private_tx_handler: Option>, + private_state: Option>, provider: Arc, _log_settings: &LogConfig, connection_filter: Option>, @@ -51,6 +53,7 @@ pub fn sync( provider, snapshot_service, private_tx_handler, + private_state, network_config, }, connection_filter)?; diff --git a/parity/run.rs b/parity/run.rs index 82ae9694f73..efd0781f3f5 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -626,9 +626,9 @@ fn execute_impl(cmd: RunCmd, logger: Arc, on_client_rq: .map_err(|e| format!("Stratum start error: {:?}", e))?; } - let private_tx_sync: Option> = match cmd.private_tx_enabled { - true => Some(private_tx_service.clone() as Arc), - false => None, + let (private_tx_sync, private_state) = match cmd.private_tx_enabled { + true => (Some(private_tx_service.clone() as Arc), Some(private_tx_provider.private_state_db())), + false => (None, None), }; // create sync object @@ -639,6 +639,7 @@ fn execute_impl(cmd: RunCmd, logger: Arc, on_client_rq: client.clone(), snapshot_service.clone(), private_tx_sync, + private_state, client.clone(), &cmd.logger_config, connection_filter.clone().map(|f| f as Arc<::sync::ConnectionFilter + 'static>), From c85a87c996209db5b1c0731971dc701b26fa3def Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Fri, 10 May 2019 16:36:36 +0200 Subject: [PATCH 12/35] Private state db file added --- ethcore/private-tx/src/private_state_db.rs | 57 ++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 ethcore/private-tx/src/private_state_db.rs diff --git a/ethcore/private-tx/src/private_state_db.rs b/ethcore/private-tx/src/private_state_db.rs new file mode 100644 index 00000000000..90f99fbc62a --- /dev/null +++ b/ethcore/private-tx/src/private_state_db.rs @@ -0,0 +1,57 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum 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 Ethereum 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 Ethereum. If not, see . + +use std::sync::Arc; +use parking_lot::RwLock; +use ethereum_types::H256; +use bytes::Bytes; +use journaldb::overlaydb::OverlayDB; +use kvdb::{KeyValueDB, DBTransaction}; +use hash_db::HashDB; +use ethcore_db::COL_PRIVATE_TRANSACTIONS_STATE; +use error::Error; + +/// Wrapper around local db with private state for sync purposes +pub struct PrivateStateDB { + private_state: RwLock, + db: Arc, +} + +impl PrivateStateDB { + /// Constructs the object + pub fn new(db: Arc) -> Self { + PrivateStateDB { + private_state: RwLock::new(OverlayDB::new(db.clone(), COL_PRIVATE_TRANSACTIONS_STATE)), + db, + } + } + + /// Returns saved state for the hash + pub fn state(&self, state_hash: &H256) -> Result { + let private_state = self.private_state.read(); + private_state.get(state_hash).map(|s| s.to_vec()).ok_or(Error::PrivateStateNotFound) + } + + /// Stores state for the hash + pub fn save_state(&self, storage: &Bytes) -> Result { + let mut private_state = self.private_state.write(); + let state_hash = private_state.insert(storage); + let mut transaction = DBTransaction::new(); + private_state.commit_to_batch(&mut transaction)?; + self.db.write(transaction).map_err(|_| Error::DatabaseWriteError)?; + Ok(state_hash) + } +} \ No newline at end of file From 82165034583f7d2717103fab96a46c73cfa73b57 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Fri, 31 May 2019 15:14:37 +0200 Subject: [PATCH 13/35] Rlp packets for retrieiving private state data added --- ethcore/sync/src/api.rs | 21 +++++++-------- ethcore/sync/src/chain/handler.rs | 37 +++++++++++++++++++++++++++ ethcore/sync/src/chain/mod.rs | 29 +++++++++++++++++++++ ethcore/sync/src/chain/requester.rs | 7 +++++ ethcore/sync/src/chain/supplier.rs | 27 +++++++++++++++++++ ethcore/sync/src/chain/sync_packet.rs | 6 ++++- ethcore/sync/src/sync_io.rs | 10 ++++---- ethcore/sync/src/tests/helpers.rs | 2 +- 8 files changed, 122 insertions(+), 17 deletions(-) diff --git a/ethcore/sync/src/api.rs b/ethcore/sync/src/api.rs index dbe66876335..eeb6cb41d42 100644 --- a/ethcore/sync/src/api.rs +++ b/ethcore/sync/src/api.rs @@ -43,7 +43,7 @@ use std::net::{SocketAddr, AddrParseError}; use std::str::FromStr; use parking_lot::{RwLock, Mutex}; use chain::{ETH_PROTOCOL_VERSION_63, ETH_PROTOCOL_VERSION_62, - PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2, PAR_PROTOCOL_VERSION_3, SyncState}; + PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2, PAR_PROTOCOL_VERSION_3, PAR_PROTOCOL_VERSION_4, SyncState}; use chain::sync_packet::SyncPacket::{PrivateTransactionPacket, SignedPrivateTransactionPacket}; use light::client::AsLightClient; use light::Provider; @@ -485,7 +485,7 @@ impl NetworkProtocolHandler for SyncProtocolHandler { &*self.chain, &*self.snapshot_service, &self.overlay, - &self.private_state), + self.private_state.clone()), *peer, packet_id, data); } @@ -499,7 +499,7 @@ impl NetworkProtocolHandler for SyncProtocolHandler { &*self.chain, &*self.snapshot_service, &self.overlay, - &self.private_state), + self.private_state.clone()), *peer); } } @@ -511,14 +511,14 @@ impl NetworkProtocolHandler for SyncProtocolHandler { &*self.chain, &*self.snapshot_service, &self.overlay, - &self.private_state), + self.private_state.clone()), *peer); } } fn timeout(&self, io: &dyn NetworkContext, timer: TimerToken) { trace_time!("sync::timeout"); - let mut io = NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay, &self.private_state); + let mut io = NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay, self.private_state.clone()); match timer { PEERS_TIMER => self.sync.write().maintain_peers(&mut io), MAINTAIN_SYNC_TIMER => self.sync.write().maintain_sync(&mut io), @@ -553,7 +553,7 @@ impl ChainNotify for EthSync { &*self.eth_handler.chain, &*self.eth_handler.snapshot_service, &self.eth_handler.overlay, - &self.eth_handler.private_state); + self.eth_handler.private_state.clone()); self.eth_handler.sync.write().chain_new_blocks( &mut sync_io, &new_blocks.imported, @@ -600,7 +600,7 @@ impl ChainNotify for EthSync { self.network.register_protocol(self.eth_handler.clone(), self.subprotocol_name, &[ETH_PROTOCOL_VERSION_62, ETH_PROTOCOL_VERSION_63]) .unwrap_or_else(|e| warn!("Error registering ethereum protocol: {:?}", e)); // register the warp sync subprotocol - self.network.register_protocol(self.eth_handler.clone(), WARP_SYNC_PROTOCOL_ID, &[PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2, PAR_PROTOCOL_VERSION_3]) + self.network.register_protocol(self.eth_handler.clone(), WARP_SYNC_PROTOCOL_ID, &[PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2, PAR_PROTOCOL_VERSION_3, PAR_PROTOCOL_VERSION_4]) .unwrap_or_else(|e| warn!("Error registering snapshot sync protocol: {:?}", e)); // register the light protocol. @@ -621,14 +621,15 @@ impl ChainNotify for EthSync { &*self.eth_handler.chain, &*self.eth_handler.snapshot_service, &self.eth_handler.overlay, - &self.eth_handler.private_state); + self.eth_handler.private_state.clone()); match message_type { ChainMessageType::Consensus(message) => self.eth_handler.sync.write().propagate_consensus_packet(&mut sync_io, message), ChainMessageType::PrivateTransaction(transaction_hash, message) => self.eth_handler.sync.write().propagate_private_transaction(&mut sync_io, transaction_hash, PrivateTransactionPacket, message), ChainMessageType::SignedPrivateTransaction(transaction_hash, message) => self.eth_handler.sync.write().propagate_private_transaction(&mut sync_io, transaction_hash, SignedPrivateTransactionPacket, message), - ChainMessageType::PrivateStateRequest(addresses) => {} + ChainMessageType::PrivateStateRequest(hash) => + self.eth_handler.sync.write().request_private_state(&mut sync_io, &hash), } }); } @@ -697,7 +698,7 @@ impl ManageNetwork for EthSync { &*self.eth_handler.chain, &*self.eth_handler.snapshot_service, &self.eth_handler.overlay, - &self.eth_handler.private_state); + self.eth_handler.private_state.clone()); self.eth_handler.sync.write().abort(&mut sync_io); }); diff --git a/ethcore/sync/src/chain/handler.rs b/ethcore/sync/src/chain/handler.rs index 659720e3f6d..4a69cb51327 100644 --- a/ethcore/sync/src/chain/handler.rs +++ b/ethcore/sync/src/chain/handler.rs @@ -48,6 +48,7 @@ use super::sync_packet::SyncPacket::{ SnapshotDataPacket, PrivateTransactionPacket, SignedPrivateTransactionPacket, + PrivateStatePacket, }; use super::{ @@ -86,6 +87,7 @@ impl SyncHandler { SnapshotDataPacket => SyncHandler::on_snapshot_data(sync, io, peer, &rlp), PrivateTransactionPacket => SyncHandler::on_private_transaction(sync, io, peer, &rlp), SignedPrivateTransactionPacket => SyncHandler::on_signed_private_transaction(sync, io, peer, &rlp), + PrivateStatePacket => SyncHandler::on_private_state_data(sync, io, peer, &rlp), _ => { debug!(target: "sync", "{}: Unknown packet {}", peer, packet_id.id()); Ok(()) @@ -738,6 +740,41 @@ impl SyncHandler { } Ok(()) } + + fn on_private_state_data(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { + if !sync.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { + trace!(target: "sync", "{} Ignoring packet from unconfirmed/unknown peer", peer_id); + return Ok(()); + } + if !sync.reset_peer_asking(peer_id, PeerAsking::PrivateState) { + trace!(target: "sync", "{}: Ignored unexpected private state data", peer_id); + return Ok(()); + } + let private_handler = match sync.private_tx_handler { + Some(ref handler) => handler, + None => { + trace!(target: "sync", "{} Ignoring private tx packet from peer", peer_id); + return Ok(()); + } + }; + let private_state_data: Bytes = r.val_at(0)?; + match io.private_state() { + Some(db) => { + match db.save_state(&private_state_data) { + Ok(hash) => { + // Signal handler + } + Err(e) => { + error!(target: "sync", "Cannot save received private state {:?}", e); + } + } + } + None => { + trace!(target: "sync", "{} Ignoring private tx packet from peer", peer_id); + } + }; + Ok(()) + } } #[cfg(test)] diff --git a/ethcore/sync/src/chain/mod.rs b/ethcore/sync/src/chain/mod.rs index 1b40649ecb5..0a0b934cc37 100644 --- a/ethcore/sync/src/chain/mod.rs +++ b/ethcore/sync/src/chain/mod.rs @@ -152,6 +152,8 @@ pub const PAR_PROTOCOL_VERSION_1: (u8, u8) = (1, 0x15); pub const PAR_PROTOCOL_VERSION_2: (u8, u8) = (2, 0x16); /// 3 version of Parity protocol (private transactions messages added). pub const PAR_PROTOCOL_VERSION_3: (u8, u8) = (3, 0x18); +/// 4 version of Parity protocol (private state sync added). +pub const PAR_PROTOCOL_VERSION_4: (u8, u8) = (4, 0x20); pub const MAX_BODIES_TO_SEND: usize = 256; pub const MAX_HEADERS_TO_SEND: usize = 512; @@ -179,6 +181,7 @@ const RECEIPTS_TIMEOUT: Duration = Duration::from_secs(10); const FORK_HEADER_TIMEOUT: Duration = Duration::from_secs(3); const SNAPSHOT_MANIFEST_TIMEOUT: Duration = Duration::from_secs(5); const SNAPSHOT_DATA_TIMEOUT: Duration = Duration::from_secs(120); +const PRIVATE_STATE_TIMEOUT: Duration = Duration::from_secs(120); /// Defines how much time we have to complete priority transaction or block propagation. /// after the deadline is reached the task is considered finished @@ -277,6 +280,7 @@ pub enum PeerAsking { BlockReceipts, SnapshotManifest, SnapshotData, + PrivateState, } #[derive(PartialEq, Eq, Debug, Clone, Copy, MallocSizeOf)] @@ -1185,6 +1189,7 @@ impl ChainSync { PeerAsking::ForkHeader => elapsed > FORK_HEADER_TIMEOUT, PeerAsking::SnapshotManifest => elapsed > SNAPSHOT_MANIFEST_TIMEOUT, PeerAsking::SnapshotData => elapsed > SNAPSHOT_DATA_TIMEOUT, + PeerAsking::PrivateState => elapsed > PRIVATE_STATE_TIMEOUT, }; if timeout { debug!(target:"sync", "Timeout {}", peer_id); @@ -1291,6 +1296,17 @@ impl ChainSync { ).collect() } + fn get_private_state_peers(&self) -> Vec { + self.peers.iter().filter_map( + |(id, p)| if p.protocol_version >= PAR_PROTOCOL_VERSION_4.0 + && p.private_tx_enabled { + Some(*id) + } else { + None + } + ).collect() + } + /// Maintain other peers. Send out any new blocks and transactions pub fn maintain_sync(&mut self, io: &mut dyn SyncIo) { self.maybe_start_snapshot_sync(io); @@ -1360,6 +1376,19 @@ impl ChainSync { pub fn propagate_private_transaction(&mut self, io: &mut dyn SyncIo, transaction_hash: H256, packet_id: SyncPacket, packet: Bytes) { SyncPropagator::propagate_private_transaction(self, io, transaction_hash, packet_id, packet); } + + /// Request private state from peers + pub fn request_private_state(&mut self, io: &mut SyncIo, hash: &H256) { + let private_state_peers = self.get_private_state_peers(); + if private_state_peers.is_empty() { + error!(target: "privatetx", "Cannot request private state, no peers with private tx enabled connected"); + } else { + trace!(target: "privatetx", "Requesting private stats from {:?}", private_state_peers); + for peer_id in private_state_peers { + SyncRequester::request_private_state(self, io, peer_id, hash); + } + } + } } #[cfg(test)] diff --git a/ethcore/sync/src/chain/requester.rs b/ethcore/sync/src/chain/requester.rs index 6ff72082305..53376eacc4f 100644 --- a/ethcore/sync/src/chain/requester.rs +++ b/ethcore/sync/src/chain/requester.rs @@ -30,6 +30,7 @@ use super::sync_packet::SyncPacket::{ GetReceiptsPacket, GetSnapshotManifestPacket, GetSnapshotDataPacket, + GetPrivateStatePacket, }; use super::{ @@ -99,6 +100,12 @@ impl SyncRequester { SyncRequester::send_request(sync, io, peer_id, PeerAsking::SnapshotManifest, GetSnapshotManifestPacket, rlp.out()); } + pub fn request_private_state(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, hash: &H256) { + let mut rlp = RlpStream::new_list(1); + rlp.append(hash); + SyncRequester::send_request(sync, io, peer_id, PeerAsking::PrivateState, GetPrivateStatePacket, rlp.out()); + } + /// Request headers from a peer by block hash fn request_headers_by_hash(sync: &mut ChainSync, io: &mut dyn SyncIo, peer_id: PeerId, h: &H256, count: u64, skip: u64, reverse: bool, set: BlockSet) { trace!(target: "sync", "{} <- GetBlockHeaders: {} entries starting from {}, set = {:?}", peer_id, count, h, set); diff --git a/ethcore/sync/src/chain/supplier.rs b/ethcore/sync/src/chain/supplier.rs index e802cb0e0a5..99c52b52101 100644 --- a/ethcore/sync/src/chain/supplier.rs +++ b/ethcore/sync/src/chain/supplier.rs @@ -43,6 +43,8 @@ use super::sync_packet::SyncPacket::{ GetSnapshotDataPacket, SnapshotDataPacket, ConsensusDataPacket, + GetPrivateStatePacket, + PrivateStatePacket, }; use super::{ @@ -98,6 +100,11 @@ impl SyncSupplier { SyncSupplier::return_snapshot_data, |e| format!("Error sending snapshot data: {:?}", e)), + GetPrivateStatePacket => SyncSupplier::return_rlp( + io, &rlp, peer, + SyncSupplier::return_private_state, + |e| format!("Error sending private state data: {:?}", e)), + StatusPacket => { sync.write().on_packet(io, peer, packet_id, data); Ok(()) @@ -348,6 +355,26 @@ impl SyncSupplier { Ok(Some((SnapshotDataPacket.id(), rlp))) } + /// Respond to GetPrivateStatePacket + fn return_private_state(io: &SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { + let hash: H256 = r.val_at(0)?; + trace!(target: "privatetx", "{} -> GetSnapshotData {:?}", peer_id, hash); + io.private_state().map_or(Ok(None), |db| { + let state = db.state(&hash); + match state { + Err(err) => { + trace!(target: "privatetx", "Cannot retrieve state from db {:?}", err); + Ok(None) + } + Ok(bytes) => { + let mut rlp = RlpStream::new_list(1); + rlp.append(&bytes); + Ok(Some((PrivateStatePacket.id(), rlp))) + } + } + }) + } + fn return_rlp(io: &mut dyn SyncIo, rlp: &Rlp, peer: PeerId, rlp_func: FRlp, error_func: FError) -> Result<(), PacketDecodeError> where FRlp : Fn(&dyn SyncIo, &Rlp, PeerId) -> RlpResponseResult, FError : FnOnce(network::Error) -> String diff --git a/ethcore/sync/src/chain/sync_packet.rs b/ethcore/sync/src/chain/sync_packet.rs index f3aa11119f8..c31ce78760a 100644 --- a/ethcore/sync/src/chain/sync_packet.rs +++ b/ethcore/sync/src/chain/sync_packet.rs @@ -55,6 +55,8 @@ enum_from_primitive! { ConsensusDataPacket = 0x15, PrivateTransactionPacket = 0x16, SignedPrivateTransactionPacket = 0x17, + GetPrivateStatePacket = 0x18, + PrivateStatePacket = 0x19, } } @@ -94,7 +96,9 @@ impl PacketInfo for SyncPacket { SnapshotDataPacket | ConsensusDataPacket | PrivateTransactionPacket | - SignedPrivateTransactionPacket + SignedPrivateTransactionPacket | + GetPrivateStatePacket | + PrivateStatePacket => WARP_SYNC_PROTOCOL_ID, } diff --git a/ethcore/sync/src/sync_io.rs b/ethcore/sync/src/sync_io.rs index f2803828721..5c974e712fe 100644 --- a/ethcore/sync/src/sync_io.rs +++ b/ethcore/sync/src/sync_io.rs @@ -43,7 +43,7 @@ pub trait SyncIo { /// Get the snapshot service. fn snapshot_service(&self) -> &dyn SnapshotService; /// Get the private state wrapper - fn private_state(&self) -> &Option>; + fn private_state(&self) -> Option>; /// Returns peer version identifier fn peer_version(&self, peer_id: PeerId) -> ClientVersion { ClientVersion::from(peer_id.to_string()) @@ -72,7 +72,7 @@ pub struct NetSyncIo<'s> { chain: &'s dyn BlockChainClient, snapshot_service: &'s dyn SnapshotService, chain_overlay: &'s RwLock>, - private_state: &'s Option>, + private_state: Option>, } impl<'s> NetSyncIo<'s> { @@ -81,7 +81,7 @@ impl<'s> NetSyncIo<'s> { chain: &'s dyn BlockChainClient, snapshot_service: &'s dyn SnapshotService, chain_overlay: &'s RwLock>, - private_state: &'s Option>) -> NetSyncIo<'s> { + private_state: Option>) -> NetSyncIo<'s> { NetSyncIo { network, chain, @@ -121,8 +121,8 @@ impl<'s> SyncIo for NetSyncIo<'s> { self.snapshot_service } - fn private_state(&self) -> &Option> { - self.private_state + fn private_state(&self) -> Option> { + self.private_state.clone() } fn peer_session_info(&self, peer_id: PeerId) -> Option { diff --git a/ethcore/sync/src/tests/helpers.rs b/ethcore/sync/src/tests/helpers.rs index 5ac807fc4ef..5ac2fd9ef99 100644 --- a/ethcore/sync/src/tests/helpers.rs +++ b/ethcore/sync/src/tests/helpers.rs @@ -244,7 +244,7 @@ impl EthPeer where C: FlushingBlockChainClient { self.sync.write().propagate_private_transaction(&mut io, transaction_hash, PrivateTransactionPacket, data), ChainMessageType::SignedPrivateTransaction(transaction_hash, data) => self.sync.write().propagate_private_transaction(&mut io, transaction_hash, SignedPrivateTransactionPacket, data), - ChainMessageType::PrivateStateRequest(addresses) => {} + ChainMessageType::PrivateStateRequest(_hash) => {} } } From 847df3c71fa95b99f8a3c841ab8710c57c68ff49 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Fri, 31 May 2019 17:02:28 +0200 Subject: [PATCH 14/35] Handling of private sync completed message --- ethcore/private-tx/src/lib.rs | 50 ++++++++++++++++++++++++++- ethcore/private-tx/src/state_store.rs | 25 ++++++++++---- ethcore/service/src/service.rs | 10 ++++++ ethcore/sync/src/chain/handler.rs | 4 ++- ethcore/sync/src/private_tx.rs | 11 ++++++ 5 files changed, 92 insertions(+), 8 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 088568b4bfb..fbcb0d3f211 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -87,7 +87,7 @@ pub use messages::{PrivateTransaction, SignedPrivateTransaction}; pub use private_state_db::PrivateStateDB; pub use error::Error; pub use log::{Logging, TransactionLog, ValidatorLog, PrivateTxStatus, FileLogsSerializer}; -use state_store::PrivateStateStorage; +use state_store::{PrivateStateStorage, SyncState}; use std::sync::{Arc, Weak}; use std::collections::{HashMap, HashSet, BTreeMap}; @@ -559,6 +559,34 @@ impl Provider { } } + fn private_state_sync_completed(&self, hash: &H256) -> Result<(), Error> { + self.state_storage.state_sync_completed(hash); + if self.state_storage.current_sync_state() == SyncState::Idle { + trace!(target: "privatetx", "Private state sync completed, processing pending requests"); + let creation_requests = self.state_storage.drain_creation_queue(); + for request in creation_requests { + match self.create_private_transaction(request) { + Ok(receipt) => trace!(target: "privatetx", "Creation request processed, receipt: {:?}", receipt), + Err(e) => error!(target: "privatetx", "Cannot process creation request with error: {:?}", e), + } + } + let verification_requests = self.state_storage.drain_verification_queue(); + for request in verification_requests { + if let Err(err) = self.process_verification_transaction(&request) { + warn!(target: "privatetx", "Error while processing pending verification request: {:?}", err); + match err { + Error::PrivateStateNotFound => { + let contract = request.private_transaction.contract(); + error!(target: "privatetx", "Cannot retrieve private state after sync for {:?}", &contract); + } + _ => {} + } + } + } + } + Ok(()) + } + fn iv_from_transaction(transaction: &SignedTransaction) -> H128 { let nonce = keccak(&transaction.nonce.rlp_bytes()); let (iv, _) = nonce.as_bytes().split_at(INIT_VEC_LEN); @@ -835,6 +863,9 @@ pub trait Importer { /// /// Creates corresponding public transaction if last required signature collected and sends it to the chain fn import_signed_private_transaction(&self, _rlp: &[u8]) -> Result; + + /// Function called when requested private state retrieved from peer and saved to DB. + fn private_state_synced(&self, hash: &H256) -> Result<(), String>; } // TODO [ToDr] Offload more heavy stuff to the IoService thread. @@ -898,6 +929,23 @@ impl Importer for Arc { } Ok(private_hash) } + + fn private_state_synced(&self, hash: &H256) -> Result<(), String> { + trace!(target: "privatetx", "Private state synced, hash: {:?}", hash); + let provider = Arc::downgrade(self); + let completed_hash = *hash; + let result = self.channel.send(ClientIoMessage::execute(move |_| { + if let Some(provider) = provider.upgrade() { + if let Err(e) = provider.private_state_sync_completed(&completed_hash) { + warn!(target: "privatetx", "Unable to process the state synced signal: {}", e); + } + } + })); + if let Err(e) = result { + warn!(target: "privatetx", "Error sending private state synced message: {:?}", e); + } + Ok(()) + } } impl ChainNotify for Provider { diff --git a/ethcore/private-tx/src/state_store.rs b/ethcore/private-tx/src/state_store.rs index 67c384d186a..d1008baaabe 100644 --- a/ethcore/private-tx/src/state_store.rs +++ b/ethcore/private-tx/src/state_store.rs @@ -23,7 +23,7 @@ use private_transactions::VerifiedPrivateTransaction; use private_state_db::PrivateStateDB; /// State of the private state sync -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub enum SyncState { /// No sync is running Idle, @@ -71,12 +71,11 @@ impl PrivateStateStorage { new_hashes } - pub fn state_sync_completed(&self, synced_states_hashes: &Vec) { + /// Signals that corresponding private state retrieved and added into the local db + pub fn state_sync_completed(&self, synced_state_hash: &H256) { let mut syncing_hashes = self.syncing_hashes.write(); - for hash in synced_states_hashes { - if let Some(index) = syncing_hashes.iter().position(|h| h == hash) { - syncing_hashes.remove(index); - } + if let Some(index) = syncing_hashes.iter().position(|h| h == synced_state_hash) { + syncing_hashes.remove(index); } if syncing_hashes.is_empty() { // All states were downloaded @@ -95,9 +94,23 @@ impl PrivateStateStorage { verification_requests.push(transaction); } + /// Drains all verification requests to process + pub fn drain_verification_queue(&self) -> Vec> { + let mut requests_queue = self.verification_requests.write(); + let requests = requests_queue.drain(..).collect::>(); + requests + } + /// Stores creation request for the later creation pub fn add_creation_request(&self, transaction: SignedTransaction) { let mut creation_requests = self.creation_requests.write(); creation_requests.push(transaction); } + + /// Drains all creation requests to process + pub fn drain_creation_queue(&self) -> Vec { + let mut requests_queue = self.creation_requests.write(); + let requests = requests_queue.drain(..).collect::>(); + requests + } } \ No newline at end of file diff --git a/ethcore/service/src/service.rs b/ethcore/service/src/service.rs index a76f1650c23..ed5169087dd 100644 --- a/ethcore/service/src/service.rs +++ b/ethcore/service/src/service.rs @@ -73,6 +73,16 @@ impl PrivateTxHandler for PrivateTxService { } } } + + fn private_state_synced(&self, hash: &H256) -> Result<(), String> { + match self.provider.private_state_synced(hash) { + Ok(handle_result) => Ok(handle_result), + Err(err) => { + warn!(target: "privatetx", "Unable to handle private state synced message: {}", err); + bail!(err.to_string()) + } + } + } } /// Client service setup. Creates and registers client and network services with the IO subsystem. diff --git a/ethcore/sync/src/chain/handler.rs b/ethcore/sync/src/chain/handler.rs index 4a69cb51327..4f7b603d402 100644 --- a/ethcore/sync/src/chain/handler.rs +++ b/ethcore/sync/src/chain/handler.rs @@ -762,7 +762,9 @@ impl SyncHandler { Some(db) => { match db.save_state(&private_state_data) { Ok(hash) => { - // Signal handler + if let Err(err) = private_handler.private_state_synced(&hash) { + trace!(target: "sync", "Ignoring the message, error queueing: {}", err); + } } Err(e) => { error!(target: "sync", "Cannot save received private state {:?}", e); diff --git a/ethcore/sync/src/private_tx.rs b/ethcore/sync/src/private_tx.rs index 121ad081fb1..60aa4c90d84 100644 --- a/ethcore/sync/src/private_tx.rs +++ b/ethcore/sync/src/private_tx.rs @@ -26,6 +26,9 @@ pub trait PrivateTxHandler: Send + Sync + 'static { /// Function called on new signed private transaction received. /// Returns the hash of the imported transaction fn import_signed_private_transaction(&self, rlp: &[u8]) -> Result; + + /// Function called when requested private state retrieved from peer and saved to DB. + fn private_state_synced(&self, hash: &H256) -> Result<(), String>; } /// Nonoperative private transaction handler. @@ -39,6 +42,10 @@ impl PrivateTxHandler for NoopPrivateTxHandler { fn import_signed_private_transaction(&self, _rlp: &[u8]) -> Result { Ok(H256::zero()) } + + fn private_state_synced(&self, _hash: &H256) -> Result<(), String> { + Ok(()) + } } /// Simple private transaction handler. Used for tests. @@ -60,4 +67,8 @@ impl PrivateTxHandler for SimplePrivateTxHandler { self.signed_txs.lock().push(rlp.to_vec()); Ok(H256::zero()) } + + fn private_state_synced(&self, _hash: &H256) -> Result<(), String> { + Ok(()) + } } From 960bf259a2f8337e8128a46d65f7d0b4bb65f2ca Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Tue, 4 Jun 2019 11:26:57 +0200 Subject: [PATCH 15/35] Test code fixed --- ethcore/sync/src/tests/helpers.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ethcore/sync/src/tests/helpers.rs b/ethcore/sync/src/tests/helpers.rs index 5ac2fd9ef99..02f9ac01e6f 100644 --- a/ethcore/sync/src/tests/helpers.rs +++ b/ethcore/sync/src/tests/helpers.rs @@ -132,8 +132,8 @@ impl<'p, C> SyncIo for TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p { self.snapshot_service } - fn private_state(&self) -> &Option> { - &None + fn private_state(&self) -> Option> { + None } fn peer_session_info(&self, _peer_id: PeerId) -> Option { From e4ab1eee1e3434a196a34edb7dc7df15fb6665aa Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Tue, 4 Jun 2019 12:01:32 +0200 Subject: [PATCH 16/35] External flag for offchain storing added --- ethcore/private-tx/src/lib.rs | 4 +++- ethcore/private-tx/tests/private_contract.rs | 2 ++ ethcore/sync/src/tests/private.rs | 2 ++ parity/cli/mod.rs | 6 ++++++ parity/configuration.rs | 1 + 5 files changed, 14 insertions(+), 1 deletion(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index fbcb0d3f211..b5133a2f078 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -147,6 +147,8 @@ pub struct ProviderConfig { pub signer_account: Option
, /// Path to private tx logs pub logs_path: Option, + /// Provider should store the state of the private contract offchain (in DB) + pub use_offchain_storage: bool, } #[derive(Debug)] @@ -246,7 +248,7 @@ impl Provider { channel, keys_provider, logging: config.logs_path.map(|path| Logging::new(Arc::new(FileLogsSerializer::with_path(path)))), - use_offchain_storage: false, + use_offchain_storage: config.use_offchain_storage, state_storage: PrivateStateStorage::new(db), } } diff --git a/ethcore/private-tx/tests/private_contract.rs b/ethcore/private-tx/tests/private_contract.rs index dc114f6dae1..db06f899ce8 100644 --- a/ethcore/private-tx/tests/private_contract.rs +++ b/ethcore/private-tx/tests/private_contract.rs @@ -65,6 +65,7 @@ fn private_contract() { validator_accounts: vec![key3.address(), key4.address()], signer_account: None, logs_path: None, + use_offchain_storage: false, }; let io = ethcore_io::IoChannel::disconnected(); @@ -202,6 +203,7 @@ fn call_other_private_contract() { validator_accounts: vec![key3.address(), key4.address()], signer_account: None, logs_path: None, + use_offchain_storage: false, }; let io = ethcore_io::IoChannel::disconnected(); diff --git a/ethcore/sync/src/tests/private.rs b/ethcore/sync/src/tests/private.rs index 1c600024dab..a833ed54888 100644 --- a/ethcore/sync/src/tests/private.rs +++ b/ethcore/sync/src/tests/private.rs @@ -73,12 +73,14 @@ fn send_private_transaction() { validator_accounts: vec![s1.address()], signer_account: None, logs_path: None, + use_offchain_storage: false, }; let signer_config = ProviderConfig{ validator_accounts: Vec::new(), signer_account: Some(s0.address()), logs_path: None, + use_offchain_storage: false, }; let private_keys = Arc::new(StoringKeyProvider::default()); diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index 96e68056d34..a38f2b944d5 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -357,6 +357,10 @@ usage! { "--private-tx-enabled", "Enable private transactions.", + FLAG flag_private_state_offchain: (bool) = false, or |c: &Config| c.private_tx.as_ref()?.state_offchain, + "--private-state-offchain", + "Store private state offchain (in the local DB).", + ARG arg_private_signer: (Option) = None, or |c: &Config| c.private_tx.as_ref()?.signer.clone(), "--private-signer=[ACCOUNT]", "Specify the account for signing public transaction created upon verified private transaction.", @@ -1200,6 +1204,7 @@ struct Account { #[serde(deny_unknown_fields)] struct PrivateTransactions { enabled: Option, + state_offchain: Option, signer: Option, validators: Option>, account: Option, @@ -1766,6 +1771,7 @@ mod tests { // -- Private Transactions Options flag_private_enabled: true, + flag_private_state_offchain: false, arg_private_signer: Some("0xdeadbeefcafe0000000000000000000000000000".into()), arg_private_validators: Some("0xdeadbeefcafe0000000000000000000000000000".into()), arg_private_passwords: Some("~/.safe/password.file".into()), diff --git a/parity/configuration.rs b/parity/configuration.rs index b9a8d37d27e..82bf15ac795 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -927,6 +927,7 @@ impl Configuration { true => Some(dirs.base), false => None, } + use_offchain_storage: self.args.flag_private_state_offchain, }; let encryptor_conf = EncryptorConfig { From 8e763f96c308212b576cd8200f2a74a2c2167635 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Wed, 5 Jun 2019 12:52:54 +0200 Subject: [PATCH 17/35] Test for private state sync added --- ethcore/private-tx/src/lib.rs | 3 +- ethcore/sync/src/block_sync.rs | 12 +-- ethcore/sync/src/chain/handler.rs | 26 +++--- ethcore/sync/src/chain/mod.rs | 10 +-- ethcore/sync/src/chain/propagator.rs | 24 ++--- ethcore/sync/src/chain/requester.rs | 1 + ethcore/sync/src/chain/supplier.rs | 12 +-- ethcore/sync/src/private_tx.rs | 5 +- ethcore/sync/src/tests/helpers.rs | 56 ++++++++---- ethcore/sync/src/tests/private.rs | 129 +++++++++++++++++++++++++++ 10 files changed, 217 insertions(+), 61 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index b5133a2f078..5ab406d3f11 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -769,8 +769,7 @@ impl Provider { let mut saved_state = executed_state; if self.use_offchain_storage { // Save state into the storage and store its hash in the contract - saved_state = keccak(&saved_state).to_vec(); - self.state_storage.private_state_db().save_state(&executed_code)?; + saved_state = self.state_storage.private_state_db().save_state(&executed_code)?.to_vec(); } let tx_data = Self::generate_constructor(validators, executed_code.clone(), saved_state.clone()); let mut tx = Transaction { diff --git a/ethcore/sync/src/block_sync.rs b/ethcore/sync/src/block_sync.rs index b17334b48a4..a2b5650ed7b 100644 --- a/ethcore/sync/src/block_sync.rs +++ b/ethcore/sync/src/block_sync.rs @@ -691,7 +691,7 @@ mod tests { let mut chain = TestBlockChainClient::new(); let snapshot_service = TestSnapshotService::new(); let queue = RwLock::new(VecDeque::new()); - let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None); + let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None, None); // Valid headers sequence. let valid_headers = [ @@ -757,7 +757,7 @@ mod tests { let mut chain = TestBlockChainClient::new(); let snapshot_service = TestSnapshotService::new(); let queue = RwLock::new(VecDeque::new()); - let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None); + let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None, None); let mut headers = Vec::with_capacity(3); let parent_hash = H256::random(); @@ -807,7 +807,7 @@ mod tests { let mut chain = TestBlockChainClient::new(); let snapshot_service = TestSnapshotService::new(); let queue = RwLock::new(VecDeque::new()); - let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None); + let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None, None); // Import block headers. let mut headers = Vec::with_capacity(4); @@ -875,7 +875,7 @@ mod tests { let mut chain = TestBlockChainClient::new(); let snapshot_service = TestSnapshotService::new(); let queue = RwLock::new(VecDeque::new()); - let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None); + let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None, None); // Import block headers. let mut headers = Vec::with_capacity(4); @@ -940,7 +940,7 @@ mod tests { let mut chain = TestBlockChainClient::new(); let snapshot_service = TestSnapshotService::new(); let queue = RwLock::new(VecDeque::new()); - let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None); + let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None, None); let heads = [ spec.genesis_header(), @@ -980,7 +980,7 @@ mod tests { let mut chain = TestBlockChainClient::new(); let snapshot_service = TestSnapshotService::new(); let queue = RwLock::new(VecDeque::new()); - let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None); + let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None, None); let heads = [ spec.genesis_header() diff --git a/ethcore/sync/src/chain/handler.rs b/ethcore/sync/src/chain/handler.rs index 4f7b603d402..f38858afda6 100644 --- a/ethcore/sync/src/chain/handler.rs +++ b/ethcore/sync/src/chain/handler.rs @@ -66,6 +66,7 @@ use super::{ MAX_NEW_HASHES, PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_3, + PAR_PROTOCOL_VERSION_4, }; /// The Chain Sync Handler: handles responses from peers @@ -637,7 +638,7 @@ impl SyncHandler { } if false - || (warp_protocol && (peer.protocol_version < PAR_PROTOCOL_VERSION_1.0 || peer.protocol_version > PAR_PROTOCOL_VERSION_3.0)) + || (warp_protocol && (peer.protocol_version < PAR_PROTOCOL_VERSION_1.0 || peer.protocol_version > PAR_PROTOCOL_VERSION_4.0)) || (!warp_protocol && (peer.protocol_version < ETH_PROTOCOL_VERSION_62.0 || peer.protocol_version > ETH_PROTOCOL_VERSION_63.0)) { trace!(target: "sync", "Peer {} unsupported eth protocol ({})", peer_id, peer.protocol_version); @@ -698,7 +699,7 @@ impl SyncHandler { return Ok(()); } }; - trace!(target: "sync", "Received signed private transaction packet from {:?}", peer_id); + trace!(target: "privatetx", "Received signed private transaction packet from {:?}", peer_id); match private_handler.import_signed_private_transaction(r.as_raw()) { Ok(transaction_hash) => { //don't send the packet back @@ -707,7 +708,7 @@ impl SyncHandler { } }, Err(e) => { - trace!(target: "sync", "Ignoring the message, error queueing: {}", e); + trace!(target: "privatetx", "Ignoring the message, error queueing: {}", e); } } Ok(()) @@ -726,7 +727,7 @@ impl SyncHandler { return Ok(()); } }; - trace!(target: "sync", "Received private transaction packet from {:?}", peer_id); + trace!(target: "privatetx", "Received private transaction packet from {:?}", peer_id); match private_handler.import_private_transaction(r.as_raw()) { Ok(transaction_hash) => { //don't send the packet back @@ -735,7 +736,7 @@ impl SyncHandler { } }, Err(e) => { - trace!(target: "sync", "Ignoring the message, error queueing: {}", e); + trace!(target: "privatetx", "Ignoring the message, error queueing: {}", e); } } Ok(()) @@ -757,17 +758,18 @@ impl SyncHandler { return Ok(()); } }; + trace!(target: "privatetx", "Received private state data packet from {:?}", peer_id); let private_state_data: Bytes = r.val_at(0)?; match io.private_state() { Some(db) => { match db.save_state(&private_state_data) { Ok(hash) => { if let Err(err) = private_handler.private_state_synced(&hash) { - trace!(target: "sync", "Ignoring the message, error queueing: {}", err); + trace!(target: "privatetx", "Ignoring received private state message, error queueing: {}", err); } } Err(e) => { - error!(target: "sync", "Cannot save received private state {:?}", e); + error!(target: "privatetx", "Cannot save received private state {:?}", e); } } } @@ -804,7 +806,7 @@ mod tests { let queue = RwLock::new(VecDeque::new()); let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); let hashes_data = get_dummy_hashes(); let hashes_rlp = Rlp::new(&hashes_data); @@ -825,7 +827,7 @@ mod tests { let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); //sync.have_common_block = true; let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); let block = Rlp::new(&block_data); @@ -844,7 +846,7 @@ mod tests { let queue = RwLock::new(VecDeque::new()); let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); let block = Rlp::new(&block_data); @@ -858,7 +860,7 @@ mod tests { let queue = RwLock::new(VecDeque::new()); let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); let empty_data = vec![]; let block = Rlp::new(&empty_data); @@ -875,7 +877,7 @@ mod tests { let queue = RwLock::new(VecDeque::new()); let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); let empty_hashes_data = vec![]; let hashes_rlp = Rlp::new(&empty_hashes_data); diff --git a/ethcore/sync/src/chain/mod.rs b/ethcore/sync/src/chain/mod.rs index 0a0b934cc37..a6cf62b7dcd 100644 --- a/ethcore/sync/src/chain/mod.rs +++ b/ethcore/sync/src/chain/mod.rs @@ -1562,7 +1562,7 @@ pub mod tests { let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); let chain_info = client.chain_info(); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); let peers = sync.get_lagging_peers(&chain_info); SyncPropagator::propagate_new_hashes(&mut sync, &chain_info, &mut io, &peers); @@ -1582,7 +1582,7 @@ pub mod tests { let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); let chain_info = client.chain_info(); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); let peers = sync.get_lagging_peers(&chain_info); SyncPropagator::propagate_blocks(&mut sync, &chain_info, &mut io, &[], &peers); @@ -1620,7 +1620,7 @@ pub mod tests { { let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); io.chain.miner.chain_new_blocks(io.chain, &[], &[], &[], &good_blocks, false); sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks, &[], &[]); assert_eq!(io.chain.miner.ready_transactions(io.chain, 10, PendingOrdering::Priority).len(), 1); @@ -1633,7 +1633,7 @@ pub mod tests { { let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&client, &ss, &queue, None); + let mut io = TestIo::new(&client, &ss, &queue, None, None); io.chain.miner.chain_new_blocks(io.chain, &[], &[], &good_blocks, &retracted_blocks, false); sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks, &[], &[]); } @@ -1656,7 +1656,7 @@ pub mod tests { let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); // when sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks, &[], &[]); diff --git a/ethcore/sync/src/chain/propagator.rs b/ethcore/sync/src/chain/propagator.rs index e347d81c8d9..e3db0ef8c6b 100644 --- a/ethcore/sync/src/chain/propagator.rs +++ b/ethcore/sync/src/chain/propagator.rs @@ -355,7 +355,7 @@ mod tests { let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); let chain_info = client.chain_info(); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); let peers = sync.get_lagging_peers(&chain_info); let peer_count = SyncPropagator::propagate_new_hashes(&mut sync, &chain_info, &mut io, &peers); @@ -376,7 +376,7 @@ mod tests { let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); let chain_info = client.chain_info(); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); let peers = sync.get_lagging_peers(&chain_info); let peer_count = SyncPropagator::propagate_blocks(&mut sync, &chain_info, &mut io, &[], &peers); @@ -397,7 +397,7 @@ mod tests { let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); let chain_info = client.chain_info(); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); let peers = sync.get_lagging_peers(&chain_info); let peer_count = SyncPropagator::propagate_blocks(&mut sync ,&chain_info, &mut io, &[hash.clone()], &peers); @@ -440,7 +440,7 @@ mod tests { client_version: ClientVersion::from(""), }); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); SyncPropagator::propagate_proposed_blocks(&mut sync, &mut io, &[block]); // 1 message should be sent @@ -457,7 +457,7 @@ mod tests { let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(1), &client); let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); let peer_count = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); // Try to propagate same transactions for the second time let peer_count2 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); @@ -484,7 +484,7 @@ mod tests { let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(1), &client); let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); let peer_count = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); io.chain.insert_transaction_to_queue(); // New block import should not trigger propagation. @@ -508,7 +508,7 @@ mod tests { let mut sync = ChainSync::new(SyncConfig::default(), &client, None); let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); let peer_count = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); sync.chain_new_blocks(&mut io, &[], &[], &[], &[], &[], &[]); // Try to propagate same transactions for the second time @@ -529,7 +529,7 @@ mod tests { let ss = TestSnapshotService::new(); // should sent some { - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); let peer_count = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); assert_eq!(1, io.packets.len()); assert_eq!(1, peer_count); @@ -537,7 +537,7 @@ mod tests { // Insert some more client.insert_transaction_to_queue(); let (peer_count2, peer_count3) = { - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); // Propagate new transactions let peer_count2 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); // And now the peer should have all transactions @@ -563,7 +563,7 @@ mod tests { let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(1), &client); let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); let stats = sync.transactions_stats(); @@ -578,7 +578,7 @@ mod tests { let mut sync = ChainSync::new(SyncConfig::default(), &client, None); let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); // when peer#1 is Geth insert_dummy_peer(&mut sync, 1, block_hash); @@ -608,7 +608,7 @@ mod tests { let mut sync = ChainSync::new(SyncConfig::default(), &client, None); let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); // when peer#1 is Parity, accepting service transactions insert_dummy_peer(&mut sync, 1, block_hash); diff --git a/ethcore/sync/src/chain/requester.rs b/ethcore/sync/src/chain/requester.rs index 53376eacc4f..196f96705b1 100644 --- a/ethcore/sync/src/chain/requester.rs +++ b/ethcore/sync/src/chain/requester.rs @@ -101,6 +101,7 @@ impl SyncRequester { } pub fn request_private_state(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, hash: &H256) { + trace!(target: "privatetx", "{} <- GetPrivateStatePacket", peer_id); let mut rlp = RlpStream::new_list(1); rlp.append(hash); SyncRequester::send_request(sync, io, peer_id, PeerAsking::PrivateState, GetPrivateStatePacket, rlp.out()); diff --git a/ethcore/sync/src/chain/supplier.rs b/ethcore/sync/src/chain/supplier.rs index 99c52b52101..a6a95f07a12 100644 --- a/ethcore/sync/src/chain/supplier.rs +++ b/ethcore/sync/src/chain/supplier.rs @@ -358,7 +358,7 @@ impl SyncSupplier { /// Respond to GetPrivateStatePacket fn return_private_state(io: &SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { let hash: H256 = r.val_at(0)?; - trace!(target: "privatetx", "{} -> GetSnapshotData {:?}", peer_id, hash); + trace!(target: "privatetx", "{} -> GetPrivateStatePacket {:?}", peer_id, hash); io.private_state().map_or(Ok(None), |db| { let state = db.state(&hash); match state { @@ -439,7 +439,7 @@ mod test { let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); - let io = TestIo::new(&mut client, &ss, &queue, None); + let io = TestIo::new(&mut client, &ss, &queue, None, None); let unknown: H256 = H256::zero(); let result = SyncSupplier::return_block_headers(&io, &Rlp::new(&make_hash_req(&unknown, 1, 0, false)), 0); @@ -497,7 +497,7 @@ mod test { let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); - let io = TestIo::new(&mut client, &ss, &queue, None); + let io = TestIo::new(&mut client, &ss, &queue, None, None); let small_result = SyncSupplier::return_block_bodies(&io, &Rlp::new(&small_rlp_request.out()), 0); let small_result = small_result.unwrap().unwrap().1; @@ -514,7 +514,7 @@ mod test { let queue = RwLock::new(VecDeque::new()); let sync = dummy_sync_with_peer(H256::zero(), &client); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); let mut node_list = RlpStream::new_list(3); node_list.append(&H256::from_str("0000000000000000000000000000000000000000000000005555555555555555").unwrap()); @@ -545,7 +545,7 @@ mod test { let mut client = TestBlockChainClient::new(); let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); - let io = TestIo::new(&mut client, &ss, &queue, None); + let io = TestIo::new(&mut client, &ss, &queue, None, None); let result = SyncSupplier::return_receipts(&io, &Rlp::new(&[0xc0]), 0); @@ -558,7 +558,7 @@ mod test { let queue = RwLock::new(VecDeque::new()); let sync = dummy_sync_with_peer(H256::zero(), &client); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); let mut receipt_list = RlpStream::new_list(4); receipt_list.append(&H256::from_str("0000000000000000000000000000000000000000000000005555555555555555").unwrap()); diff --git a/ethcore/sync/src/private_tx.rs b/ethcore/sync/src/private_tx.rs index 60aa4c90d84..5da3b172d9c 100644 --- a/ethcore/sync/src/private_tx.rs +++ b/ethcore/sync/src/private_tx.rs @@ -55,6 +55,8 @@ pub struct SimplePrivateTxHandler { pub txs: Mutex>>, /// imported signed private transactions pub signed_txs: Mutex>>, + /// synced private state hash + pub synced_hash: Mutex, } impl PrivateTxHandler for SimplePrivateTxHandler { @@ -68,7 +70,8 @@ impl PrivateTxHandler for SimplePrivateTxHandler { Ok(H256::zero()) } - fn private_state_synced(&self, _hash: &H256) -> Result<(), String> { + fn private_state_synced(&self, hash: &H256) -> Result<(), String> { + *self.synced_hash.lock() = *hash; Ok(()) } } diff --git a/ethcore/sync/src/tests/helpers.rs b/ethcore/sync/src/tests/helpers.rs index 02f9ac01e6f..c4b6c7c4388 100644 --- a/ethcore/sync/src/tests/helpers.rs +++ b/ethcore/sync/src/tests/helpers.rs @@ -33,7 +33,7 @@ use ethcore::test_helpers; use sync_io::SyncIo; use io::{IoChannel, IoContext, IoHandler}; use api::WARP_SYNC_PROTOCOL_ID; -use chain::{ChainSync, SyncSupplier, ETH_PROTOCOL_VERSION_63, PAR_PROTOCOL_VERSION_3}; +use chain::{ChainSync, SyncSupplier, ETH_PROTOCOL_VERSION_63, PAR_PROTOCOL_VERSION_4}; use chain::sync_packet::{PacketInfo, SyncPacket}; use chain::sync_packet::SyncPacket::{PrivateTransactionPacket, SignedPrivateTransactionPacket}; @@ -61,20 +61,28 @@ pub struct TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p { pub to_disconnect: HashSet, pub packets: Vec, pub peers_info: HashMap, + pub private_state_db: Option>, overlay: RwLock>, } impl<'p, C> TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p { - pub fn new(chain: &'p C, ss: &'p TestSnapshotService, queue: &'p RwLock>, sender: Option) -> TestIo<'p, C> { + pub fn new( + chain: &'p C, + ss: &'p TestSnapshotService, + queue: &'p RwLock>, + sender: Option, + private_state_db: Option> + ) -> TestIo<'p, C> { TestIo { - chain: chain, + chain, snapshot_service: ss, - queue: queue, - sender: sender, + queue, + sender, to_disconnect: HashSet::new(), - overlay: RwLock::new(HashMap::new()), packets: Vec::new(), peers_info: HashMap::new(), + private_state_db, + overlay: RwLock::new(HashMap::new()), } } } @@ -133,7 +141,7 @@ impl<'p, C> SyncIo for TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p { } fn private_state(&self) -> Option> { - None + self.private_state_db.clone() } fn peer_session_info(&self, _peer_id: PeerId) -> Option { @@ -145,7 +153,7 @@ impl<'p, C> SyncIo for TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p { } fn protocol_version(&self, protocol: &ProtocolId, peer_id: PeerId) -> u8 { - if protocol == &WARP_SYNC_PROTOCOL_ID { PAR_PROTOCOL_VERSION_3.0 } else { self.eth_protocol_version(peer_id) } + if protocol == &WARP_SYNC_PROTOCOL_ID { PAR_PROTOCOL_VERSION_4.0 } else { self.eth_protocol_version(peer_id) } } fn chain_overlay(&self) -> &RwLock> { @@ -225,6 +233,7 @@ pub struct EthPeer where C: FlushingBlockChainClient { pub private_tx_handler: Arc, pub io_queue: RwLock>, new_blocks_queue: RwLock>, + private_state_db: RwLock>>, } impl EthPeer where C: FlushingBlockChainClient { @@ -237,19 +246,20 @@ impl EthPeer where C: FlushingBlockChainClient { } fn process_io_message(&self, message: ChainMessageType) { - let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None); + let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None, self.private_state_db()); match message { ChainMessageType::Consensus(data) => self.sync.write().propagate_consensus_packet(&mut io, data), ChainMessageType::PrivateTransaction(transaction_hash, data) => self.sync.write().propagate_private_transaction(&mut io, transaction_hash, PrivateTransactionPacket, data), ChainMessageType::SignedPrivateTransaction(transaction_hash, data) => self.sync.write().propagate_private_transaction(&mut io, transaction_hash, SignedPrivateTransactionPacket, data), - ChainMessageType::PrivateStateRequest(_hash) => {} + ChainMessageType::PrivateStateRequest(hash) => + self.sync.write().request_private_state(&mut io, &hash), } } fn process_new_block_message(&self, message: NewBlockMessage) { - let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None); + let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None, self.private_state_db()); self.sync.write().chain_new_blocks( &mut io, &message.imported, @@ -260,6 +270,15 @@ impl EthPeer where C: FlushingBlockChainClient { &message.proposed ); } + + pub fn set_private_state_db(&self, db: Arc) { + *self.private_state_db.write() = Some(db); + } + + fn private_state_db(&self) -> Option> { + let db = self.private_state_db.read(); + db.clone() + } } impl Peer for EthPeer { @@ -271,17 +290,18 @@ impl Peer for EthPeer { &*self.chain, &self.snapshot_service, &self.queue, - Some(other)), + Some(other), + self.private_state_db()), other); } fn on_disconnect(&self, other: PeerId) { - let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, Some(other)); + let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, Some(other), self.private_state_db()); self.sync.write().on_peer_aborting(&mut io, other); } fn receive_message(&self, from: PeerId, msg: TestPacket) -> HashSet { - let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, Some(from)); + let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, Some(from), self.private_state_db()); SyncSupplier::dispatch_packet(&self.sync, &mut io, from, msg.packet_id, &msg.data); self.chain.flush(); io.to_disconnect.clone() @@ -297,7 +317,7 @@ impl Peer for EthPeer { } fn sync_step(&self) { - let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None); + let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None, self.private_state_db()); self.chain.flush(); self.sync.write().maintain_peers(&mut io); self.sync.write().maintain_sync(&mut io); @@ -306,7 +326,7 @@ impl Peer for EthPeer { } fn restart_sync(&self) { - self.sync.write().restart(&mut TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None)); + self.sync.write().restart(&mut TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None, self.private_state_db())); } fn process_all_io_messages(&self) { @@ -363,6 +383,7 @@ impl TestNet> { private_tx_handler, io_queue: RwLock::new(VecDeque::new()), new_blocks_queue: RwLock::new(VecDeque::new()), + private_state_db: RwLock::new(None), })); } net @@ -416,6 +437,7 @@ impl TestNet> { private_tx_handler, io_queue: RwLock::new(VecDeque::new()), new_blocks_queue: RwLock::new(VecDeque::new()), + private_state_db: RwLock::new(None), }); peer.chain.add_notify(peer.clone()); //private_provider.add_notify(peer.clone()); @@ -514,7 +536,7 @@ impl

TestNet

where P: Peer { impl TestNet> { pub fn trigger_chain_new_blocks(&mut self, peer_id: usize) { let peer = &mut self.peers[peer_id]; - peer.sync.write().chain_new_blocks(&mut TestIo::new(&*peer.chain, &peer.snapshot_service, &peer.queue, None), &[], &[], &[], &[], &[], &[]); + peer.sync.write().chain_new_blocks(&mut TestIo::new(&*peer.chain, &peer.snapshot_service, &peer.queue, None, None), &[], &[], &[], &[], &[], &[]); } } diff --git a/ethcore/sync/src/tests/private.rs b/ethcore/sync/src/tests/private.rs index a833ed54888..597ee78bc5f 100644 --- a/ethcore/sync/src/tests/private.rs +++ b/ethcore/sync/src/tests/private.rs @@ -161,3 +161,132 @@ fn send_private_transaction() { let local_transactions = net.peer(0).miner.local_transactions(); assert_eq!(local_transactions.len(), 1); } + +#[test] +fn sync_private_state() { + // Setup two clients + let s0 = KeyPair::from_secret_slice(&keccak("1")).unwrap(); + let s1 = KeyPair::from_secret_slice(&keccak("0")).unwrap(); + + let signer = Arc::new(ethcore_private_tx::KeyPairSigner(vec![s0.clone(), s1.clone()])); + + let mut net = TestNet::with_spec(2, SyncConfig::default(), seal_spec); + let client0 = net.peer(0).chain.clone(); + let client1 = net.peer(1).chain.clone(); + let io_handler0: Arc> = Arc::new(TestIoHandler::new(net.peer(0).chain.clone())); + let io_handler1: Arc> = Arc::new(TestIoHandler::new(net.peer(1).chain.clone())); + + net.peer(0).miner.set_author(miner::Author::Sealer(engines::signer::from_keypair(s0.clone()))); + net.peer(1).miner.set_author(miner::Author::Sealer(engines::signer::from_keypair(s1.clone()))); + net.peer(0).chain.engine().register_client(Arc::downgrade(&net.peer(0).chain) as _); + net.peer(1).chain.engine().register_client(Arc::downgrade(&net.peer(1).chain) as _); + net.peer(0).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler0))); + net.peer(1).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler1))); + + let (address, _) = contract_address(CreateContractAddress::FromSenderAndNonce, &s0.address(), &0.into(), &[]); + let chain_id = client0.signing_chain_id(); + + // Exhange statuses + net.sync(); + + // Setup private providers + let validator_config = ProviderConfig{ + validator_accounts: vec![s1.address()], + signer_account: None, + use_offchain_storage: true, + }; + + let signer_config = ProviderConfig{ + validator_accounts: Vec::new(), + signer_account: Some(s0.address()), + use_offchain_storage: true, + }; + + let private_keys = Arc::new(StoringKeyProvider::default()); + let db0 = new_db(); + let pm0 = Arc::new(Provider::new( + client0.clone(), + net.peer(0).miner.clone(), + signer.clone(), + Box::new(NoopEncryptor::default()), + signer_config, + IoChannel::to_handler(Arc::downgrade(&io_handler0)), + private_keys.clone(), + db0.key_value().clone(), + )); + pm0.add_notify(net.peers[0].clone()); + + let db1 = new_db(); + let pm1 = Arc::new(Provider::new( + client1.clone(), + net.peer(1).miner.clone(), + signer.clone(), + Box::new(NoopEncryptor::default()), + validator_config, + IoChannel::to_handler(Arc::downgrade(&io_handler1)), + private_keys.clone(), + db1.key_value().clone(), + )); + pm1.add_notify(net.peers[1].clone()); + + net.peer(0).set_private_state_db(pm0.private_state_db()); + net.peer(1).set_private_state_db(pm1.private_state_db()); + + // Create and deploy contract + let private_contract_test = "6060604052341561000f57600080fd5b60d88061001d6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630c55699c146046578063bc64b76d14607457600080fd5b3415605057600080fd5b60566098565b60405180826000191660001916815260200191505060405180910390f35b3415607e57600080fd5b6096600480803560001916906020019091905050609e565b005b60005481565b8060008160001916905550505600a165627a7a723058206acbdf4b15ca4c2d43e1b1879b830451a34f1e9d02ff1f2f394d8d857e79d2080029".from_hex().unwrap(); + let mut private_create_tx = Transaction::default(); + private_create_tx.action = Action::Create; + private_create_tx.data = private_contract_test; + private_create_tx.gas = 200000.into(); + let private_create_tx_signed = private_create_tx.sign(&s0.secret(), None); + let validators = vec![s1.address()]; + let (public_tx, _) = pm0.public_creation_transaction(BlockId::Latest, &private_create_tx_signed, &validators, 0.into()).unwrap(); + let public_tx = public_tx.sign(&s0.secret(), chain_id); + + let public_tx_copy = public_tx.clone(); + push_block_with_transactions(&client0, &[public_tx]); + push_block_with_transactions(&client1, &[public_tx_copy]); + + net.sync(); + + //Create private transaction for modifying state + let mut private_tx = Transaction::default(); + private_tx.action = Action::Call(address.clone()); + private_tx.data = "bc64b76d2a00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap(); //setX(42) + private_tx.gas = 120000.into(); + private_tx.nonce = 1.into(); + let private_tx = private_tx.sign(&s0.secret(), None); + let _create_res = pm0.create_private_transaction(private_tx); + + //send private transaction message to validator + net.sync(); + + let validator_handler = net.peer(1).private_tx_handler.clone(); + let received_private_transactions = validator_handler.txs.lock().clone(); + assert_eq!(received_private_transactions.len(), 1); + + //process received private transaction message + let private_transaction = received_private_transactions[0].clone(); + let _import_res = pm1.import_private_transaction(&private_transaction); + + // Second node requests the state from the first one + net.sync(); + + let synced_hash = validator_handler.synced_hash.lock().clone(); + assert!(pm1.private_state_synced(&synced_hash).is_ok()); + + // Second node has private state up-to-date and can verify the private transaction + // Further should work the standard flow + net.sync(); + let sender_handler = net.peer(0).private_tx_handler.clone(); + let received_signed_private_transactions = sender_handler.signed_txs.lock().clone(); + assert_eq!(received_signed_private_transactions.len(), 1); + + //process signed response + let signed_private_transaction = received_signed_private_transactions[0].clone(); + assert!(pm0.import_signed_private_transaction(&signed_private_transaction).is_ok()); + let signature: SignedPrivateTransaction = Rlp::new(&signed_private_transaction).as_val().unwrap(); + assert!(pm0.process_signature(&signature).is_ok()); + let local_transactions = net.peer(0).miner.local_transactions(); + assert_eq!(local_transactions.len(), 1); +} From c1aa67447b0ce7c5df8e3a801098e5be07a4a302 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Fri, 7 Jun 2019 14:09:28 +0200 Subject: [PATCH 18/35] Saving private state logic corrected --- ethcore/private-tx/src/lib.rs | 32 ++++++++++------------ ethcore/private-tx/src/private_state_db.rs | 2 ++ parity/db/rocksdb/migration.rs | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 5ab406d3f11..8cd3386f559 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -416,12 +416,6 @@ impl Provider { if last.0 { let mut saved_state = desc.state; let contract = Self::contract_address_from_transaction(&desc.original_transaction)?; - if self.use_offchain_storage { - // Save state into the storage and store its hash in the contract - let original_state = saved_state.clone(); - saved_state = keccak(&saved_state).to_vec(); - self.state_storage.private_state_db().save_state(&original_state)?; - } 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(); @@ -430,7 +424,7 @@ impl Provider { let state = self.client.state_at(BlockId::Latest).ok_or(Error::StatePruned)?; let nonce = state.nonce(&signer_account)?; let public_tx = self.public_transaction( - saved_state.clone(), + desc.state.clone(), &desc.original_transaction, &rsv, nonce, @@ -617,7 +611,7 @@ impl Provider { let hashed_state = self.get_decrypted_state_from_contract(address, block)?; let hashed_state = H256::from_slice(&hashed_state); let stored_state_data = self.state_storage.private_state_db().state(&hashed_state)?; - Ok(stored_state_data) + self.decrypt(address, &stored_state_data) } false => self.get_decrypted_state_from_contract(address, block), } @@ -627,7 +621,10 @@ impl Provider { let (data, decoder) = private_contract::functions::state::call(); let value = self.client.call_contract(block, *address, data)?; let state = decoder.decode(&value).map_err(|e| Error::Call(format!("Contract call failed {:?}", e)))?; - self.decrypt(address, &state) + match self.use_offchain_storage { + true => Ok(state), + false => self.decrypt(address, &state), + } } fn get_decrypted_code(&self, address: &Address, block: BlockId) -> Result { @@ -722,9 +719,15 @@ impl Provider { }; (enc_code, self.encrypt(&contract_address, &Self::iv_from_transaction(transaction), &Self::snapshot_from_storage(&storage))?) }; + let mut saved_state = encrypted_storage; + if self.use_offchain_storage { + // Save state into the storage and return its hash + let original_state = saved_state.clone(); + saved_state = self.state_storage.private_state_db().save_state(&original_state)?.to_vec(); + } Ok(PrivateExecutionResult { code: encrypted_code, - state: encrypted_storage, + state: saved_state, contract_address: contract_address, result, }) @@ -766,12 +769,7 @@ impl Provider { .ok_or(Error::StatePruned) .and_then(|h| h.decode().map_err(|_| Error::StateIncorrect).into())?; let (executed_code, executed_state) = (executed.code.unwrap_or_default(), executed.state); - let mut saved_state = executed_state; - if self.use_offchain_storage { - // Save state into the storage and store its hash in the contract - saved_state = self.state_storage.private_state_db().save_state(&executed_code)?.to_vec(); - } - let tx_data = Self::generate_constructor(validators, executed_code.clone(), saved_state.clone()); + let tx_data = Self::generate_constructor(validators, executed_code.clone(), executed_state.clone()); let mut tx = Transaction { nonce: nonce, action: Action::Create, @@ -782,7 +780,7 @@ impl Provider { }; tx.gas = match self.client.estimate_gas(&tx.clone().fake_sign(sender), &state, &header) { Ok(estimated_gas) => estimated_gas, - Err(_) => self.estimate_tx_gas(validators, &executed_code, &saved_state, &[]), + Err(_) => self.estimate_tx_gas(validators, &executed_code, &executed_state, &[]), }; Ok((tx, executed.contract_address)) diff --git a/ethcore/private-tx/src/private_state_db.rs b/ethcore/private-tx/src/private_state_db.rs index 90f99fbc62a..9fda33c4d1a 100644 --- a/ethcore/private-tx/src/private_state_db.rs +++ b/ethcore/private-tx/src/private_state_db.rs @@ -42,6 +42,7 @@ impl PrivateStateDB { /// Returns saved state for the hash pub fn state(&self, state_hash: &H256) -> Result { let private_state = self.private_state.read(); + trace!(target: "privatetx", "Retrieve private state from db with hash: {:?}", state_hash); private_state.get(state_hash).map(|s| s.to_vec()).ok_or(Error::PrivateStateNotFound) } @@ -52,6 +53,7 @@ impl PrivateStateDB { let mut transaction = DBTransaction::new(); private_state.commit_to_batch(&mut transaction)?; self.db.write(transaction).map_err(|_| Error::DatabaseWriteError)?; + trace!(target: "privatetx", "Private state saved to db, its hash: {:?}", state_hash); Ok(state_hash) } } \ No newline at end of file diff --git a/parity/db/rocksdb/migration.rs b/parity/db/rocksdb/migration.rs index b6e1db1be21..e1b121b52ba 100644 --- a/parity/db/rocksdb/migration.rs +++ b/parity/db/rocksdb/migration.rs @@ -53,7 +53,7 @@ pub const TO_V13: ChangeColumns = ChangeColumns { /// Database is assumed to be at default version, when no version file is found. const DEFAULT_VERSION: u32 = 5; /// Current version of database models. -const CURRENT_VERSION: u32 = 14; +const CURRENT_VERSION: u32 = 13; /// A version of database at which blooms-db was introduced const BLOOMS_DB_VERSION: u32 = 13; /// Defines how many items are migrated to the new version of database at once. From 073f0533aa0afc7cb341693d11edbe497aa0ba0b Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Thu, 13 Jun 2019 16:48:29 +0200 Subject: [PATCH 19/35] Migration code corrected --- parity/db/rocksdb/migration.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/parity/db/rocksdb/migration.rs b/parity/db/rocksdb/migration.rs index e1b121b52ba..f9e92570b5e 100644 --- a/parity/db/rocksdb/migration.rs +++ b/parity/db/rocksdb/migration.rs @@ -42,18 +42,18 @@ pub const TO_V12: ChangeColumns = ChangeColumns { version: 12, }; -/// The migration from v12 to v13. +/// The migration from v12 to v14. /// Adds a column for private transactions state storage. -pub const TO_V13: ChangeColumns = ChangeColumns { +pub const TO_V14: ChangeColumns = ChangeColumns { pre_columns: Some(8), post_columns: Some(9), - version: 13, + version: 14, }; /// Database is assumed to be at default version, when no version file is found. const DEFAULT_VERSION: u32 = 5; /// Current version of database models. -const CURRENT_VERSION: u32 = 13; +const CURRENT_VERSION: u32 = 14; /// A version of database at which blooms-db was introduced const BLOOMS_DB_VERSION: u32 = 13; /// Defines how many items are migrated to the new version of database at once. @@ -155,7 +155,7 @@ fn consolidated_database_migrations(compaction_profile: &CompactionProfile) -> R let mut manager = MigrationManager::new(default_migration_settings(compaction_profile)); manager.add_migration(TO_V11).map_err(|_| Error::MigrationImpossible)?; manager.add_migration(TO_V12).map_err(|_| Error::MigrationImpossible)?; - manager.add_migration(TO_V13).map_err(|_| Error::MigrationImpossible)?; + manager.add_migration(TO_V14).map_err(|_| Error::MigrationImpossible)?; Ok(manager) } From a0561e7a4dc48a194f049eeee81b772fb365f2b6 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Fri, 14 Jun 2019 12:21:58 +0200 Subject: [PATCH 20/35] Fixes after merge with master --- ethcore/private-tx/src/lib.rs | 3 +-- ethcore/sync/src/tests/private.rs | 6 ++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 8cd3386f559..5e5016e7c67 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -414,7 +414,6 @@ impl Provider { let original_tx_hash = desc.original_transaction.hash(); if last.0 { - let mut saved_state = desc.state; let contract = Self::contract_address_from_transaction(&desc.original_transaction)?; let mut signatures = desc.received_signatures.clone(); signatures.push(signed_tx.signature()); @@ -723,7 +722,7 @@ impl Provider { if self.use_offchain_storage { // Save state into the storage and return its hash let original_state = saved_state.clone(); - saved_state = self.state_storage.private_state_db().save_state(&original_state)?.to_vec(); + saved_state = self.state_storage.private_state_db().save_state(&original_state)?.0.to_vec(); } Ok(PrivateExecutionResult { code: encrypted_code, diff --git a/ethcore/sync/src/tests/private.rs b/ethcore/sync/src/tests/private.rs index 597ee78bc5f..c9c6b15008c 100644 --- a/ethcore/sync/src/tests/private.rs +++ b/ethcore/sync/src/tests/private.rs @@ -165,8 +165,8 @@ fn send_private_transaction() { #[test] fn sync_private_state() { // Setup two clients - let s0 = KeyPair::from_secret_slice(&keccak("1")).unwrap(); - let s1 = KeyPair::from_secret_slice(&keccak("0")).unwrap(); + let s0 = KeyPair::from_secret_slice(&keccak("1").as_bytes()).unwrap(); + let s1 = KeyPair::from_secret_slice(&keccak("0").as_bytes()).unwrap(); let signer = Arc::new(ethcore_private_tx::KeyPairSigner(vec![s0.clone(), s1.clone()])); @@ -193,12 +193,14 @@ fn sync_private_state() { let validator_config = ProviderConfig{ validator_accounts: vec![s1.address()], signer_account: None, + logs_path: None, use_offchain_storage: true, }; let signer_config = ProviderConfig{ validator_accounts: Vec::new(), signer_account: Some(s0.address()), + logs_path: None, use_offchain_storage: true, }; From 3e832c7512805ea8b1f4daf73bc8badeaa6e7a24 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Wed, 3 Jul 2019 16:54:02 +0200 Subject: [PATCH 21/35] Merge with head --- Cargo.lock | 2 +- ethcore/private-tx/src/private_state_db.rs | 6 +++--- ethcore/service/src/service.rs | 2 +- parity/configuration.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 97eb18549c4..0d80b1f61ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1238,7 +1238,7 @@ dependencies = [ "ethkey 0.3.0", "fetch 0.1.0", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "journaldb 0.2.0", "keccak-hash 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ethcore/private-tx/src/private_state_db.rs b/ethcore/private-tx/src/private_state_db.rs index 9fda33c4d1a..d136ebb34ed 100644 --- a/ethcore/private-tx/src/private_state_db.rs +++ b/ethcore/private-tx/src/private_state_db.rs @@ -20,7 +20,7 @@ use ethereum_types::H256; use bytes::Bytes; use journaldb::overlaydb::OverlayDB; use kvdb::{KeyValueDB, DBTransaction}; -use hash_db::HashDB; +use hash_db::{HashDB, EMPTY_PREFIX}; use ethcore_db::COL_PRIVATE_TRANSACTIONS_STATE; use error::Error; @@ -43,13 +43,13 @@ impl PrivateStateDB { pub fn state(&self, state_hash: &H256) -> Result { let private_state = self.private_state.read(); trace!(target: "privatetx", "Retrieve private state from db with hash: {:?}", state_hash); - private_state.get(state_hash).map(|s| s.to_vec()).ok_or(Error::PrivateStateNotFound) + private_state.get(state_hash, EMPTY_PREFIX).map(|s| s.to_vec()).ok_or(Error::PrivateStateNotFound) } /// Stores state for the hash pub fn save_state(&self, storage: &Bytes) -> Result { let mut private_state = self.private_state.write(); - let state_hash = private_state.insert(storage); + let state_hash = private_state.insert(storage, EMPTY_PREFIX); let mut transaction = DBTransaction::new(); private_state.commit_to_batch(&mut transaction)?; self.db.write(transaction).map_err(|_| Error::DatabaseWriteError)?; diff --git a/ethcore/service/src/service.rs b/ethcore/service/src/service.rs index ed5169087dd..b97d359ce7b 100644 --- a/ethcore/service/src/service.rs +++ b/ethcore/service/src/service.rs @@ -79,7 +79,7 @@ impl PrivateTxHandler for PrivateTxService { Ok(handle_result) => Ok(handle_result), Err(err) => { warn!(target: "privatetx", "Unable to handle private state synced message: {}", err); - bail!(err.to_string()) + return Err(err.to_string()) } } } diff --git a/parity/configuration.rs b/parity/configuration.rs index 82bf15ac795..007436b1e23 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -926,7 +926,7 @@ impl Configuration { logs_path: match self.args.flag_private_enabled { true => Some(dirs.base), false => None, - } + }, use_offchain_storage: self.args.flag_private_state_offchain, }; From 901162654f288da1ff224d65e30ca5c1cc8af6db Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Wed, 3 Jul 2019 17:38:38 +0200 Subject: [PATCH 22/35] Additional checks for slices --- ethcore/private-tx/src/lib.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 5e5016e7c67..12dbb6bf652 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -538,7 +538,7 @@ impl Provider { let mut stalled_contracts_hashes: Vec = Vec::new(); for address in private_contracts { if let Ok(state_hash) = self.get_decrypted_state_from_contract(&address, BlockId::Latest) { - let state_hash = H256::from_slice(&state_hash); + let state_hash = H256::from_slice(&state_hash[0..32]); if let Err(_) = self.state_storage.private_state_db().state(&state_hash) { // State not found in the local db stalled_contracts_hashes.push(state_hash); @@ -608,7 +608,7 @@ impl Provider { match self.use_offchain_storage { true => { let hashed_state = self.get_decrypted_state_from_contract(address, block)?; - let hashed_state = H256::from_slice(&hashed_state); + let hashed_state = H256::from_slice(&hashed_state[0..32]); let stored_state_data = self.state_storage.private_state_db().state(&hashed_state)?; self.decrypt(address, &stored_state_data) } @@ -721,8 +721,7 @@ impl Provider { let mut saved_state = encrypted_storage; if self.use_offchain_storage { // Save state into the storage and return its hash - let original_state = saved_state.clone(); - saved_state = self.state_storage.private_state_db().save_state(&original_state)?.0.to_vec(); + saved_state = self.state_storage.private_state_db().save_state(&saved_state)?.0.to_vec(); } Ok(PrivateExecutionResult { code: encrypted_code, From 75fced273d28bc017195cab69d802332c44ef08e Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Fri, 5 Jul 2019 12:07:43 +0200 Subject: [PATCH 23/35] Log for private state retrieval added --- ethcore/private-tx/src/lib.rs | 5 +++++ ethcore/private-tx/src/log.rs | 32 +++++++++++++++++++++++++++++++- rpc/src/v1/types/private_log.rs | 6 ++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 12dbb6bf652..18189ecf0f7 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -301,6 +301,11 @@ impl Provider { Error::PrivateStateNotFound => { trace!(target: "privatetx", "Private state for the contract not found, requesting from peers"); self.state_storage.add_creation_request(signed_transaction); + if let Some(ref logging) = self.logging { + let contract_validators = self.get_validators(BlockId::Latest, &contract)?; + logging.private_tx_created(&tx_hash, &contract_validators); + logging.private_state_request(&tx_hash); + } self.request_private_state(&contract); } _ => {} diff --git a/ethcore/private-tx/src/log.rs b/ethcore/private-tx/src/log.rs index 2d2be018160..0d09797529e 100644 --- a/ethcore/private-tx/src/log.rs +++ b/ethcore/private-tx/src/log.rs @@ -70,6 +70,10 @@ impl Default for MonoTime { pub enum PrivateTxStatus { /// Private tx was created but no validation received yet Created, + /// Private state not found locally and being retrived from peers + PrivateStateSync, + /// Retrieval of the private state failed, transaction not created + PrivateStateSyncFailed, /// Several validators (but not all) validated the transaction Validating, /// All validators has validated the private tx @@ -209,6 +213,17 @@ impl Logging { /// Logs the creation of the private transaction pub fn private_tx_created(&self, tx_hash: &H256, validators: &[Address]) { + let mut logs = self.logs.write(); + if let Some(transaction_log) = logs.get_mut(&tx_hash) { + if transaction_log.status == PrivateTxStatus::PrivateStateSync { + // Transaction was already created before, its private state was being retrieved + transaction_log.status = PrivateTxStatus::Created; + return; + } else { + error!(target: "privatetx", "Attempt to create a duplicate transaction"); + return; + } + } let mut validator_logs = Vec::new(); for account in validators { validator_logs.push(ValidatorLog { @@ -216,7 +231,6 @@ impl Logging { 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() @@ -236,6 +250,22 @@ impl Logging { }); } + /// Private state retrieval started + pub fn private_state_request(&self, tx_hash: &H256) { + let mut logs = self.logs.write(); + if let Some(transaction_log) = logs.get_mut(&tx_hash) { + transaction_log.status = PrivateTxStatus::PrivateStateSync; + } + } + + /// Private state retrieval failed + pub fn private_state_sync_failed(&self, tx_hash: &H256) { + let mut logs = self.logs.write(); + if let Some(transaction_log) = logs.get_mut(&tx_hash) { + transaction_log.status = PrivateTxStatus::PrivateStateSyncFailed; + } + } + /// 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(); diff --git a/rpc/src/v1/types/private_log.rs b/rpc/src/v1/types/private_log.rs index 2c90bcfa905..788036c38e1 100644 --- a/rpc/src/v1/types/private_log.rs +++ b/rpc/src/v1/types/private_log.rs @@ -24,6 +24,10 @@ use ethcore_private_tx::{TransactionLog as EthTransactionLog, ValidatorLog as Et pub enum Status { /// Private tx was created but no validation received yet Created, + /// Private state not found locally and being retrived from peers + PrivateStateSync, + /// Retrieval of the private state failed, transaction not created + PrivateStateSyncFailed, /// Several validators (but not all) validated the transaction Validating, /// All validators validated the private tx @@ -35,6 +39,8 @@ impl From for Status { fn from(c: EthStatus) -> Self { match c { EthStatus::Created => Status::Created, + EthStatus::PrivateStateSync => Status::PrivateStateSync, + EthStatus::PrivateStateSyncFailed => Status::PrivateStateSyncFailed, EthStatus::Validating => Status::Validating, EthStatus::Deployed => Status::Deployed, } From 349b7a1ba3559a77486ddd744814fb977cb1ddcc Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Tue, 23 Jul 2019 13:38:48 +0200 Subject: [PATCH 24/35] Limit time of retrieving private states --- ethcore/private-tx/src/lib.rs | 24 ++++++++++++++++++++- ethcore/private-tx/src/state_store.rs | 31 +++++++++++++++++++++++---- ethcore/service/src/service.rs | 3 ++- 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 18189ecf0f7..2b88844002a 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -91,13 +91,14 @@ use state_store::{PrivateStateStorage, SyncState}; use std::sync::{Arc, Weak}; use std::collections::{HashMap, HashSet, BTreeMap}; +use std::time::Duration; use ethereum_types::{H128, H256, U256, Address, BigEndianHash}; use hash::keccak; use rlp::*; use parking_lot::RwLock; use bytes::Bytes; use ethkey::{Signature, recover, public_to_address}; -use io::IoChannel; +use io::{IoChannel, IoHandler, IoContext, TimerToken}; use machine::{ executive::{Executive, TransactOptions, contract_address as ethcore_contract_address}, executed::Executed as FlatExecuted, @@ -138,6 +139,12 @@ const INITIAL_PRIVATE_CONTRACT_VER: usize = 1; /// Version for the private contract notification about private state changes added const PRIVATE_CONTRACT_WITH_NOTIFICATION_VER: usize = 2; +/// Timer for private state retrieval +const STATE_RETRIEVAL_TIMER: TimerToken = 0; + +/// Timer for private state retrieval, 5 secs duration +const STATE_RETRIEVAL_TICK: Duration = Duration::from_secs(5); + /// Configurtion for private transaction provider #[derive(Default, PartialEq, Debug, Clone)] pub struct ProviderConfig { @@ -857,6 +864,21 @@ impl Provider { } } +impl IoHandler for Provider { + fn initialize(&self, io: &IoContext) { + if self.use_offchain_storage { + io.register_timer(STATE_RETRIEVAL_TIMER, STATE_RETRIEVAL_TICK).expect("Error registering state retrieval timer"); + } + } + + fn timeout(&self, _io: &IoContext, timer: TimerToken) { + match timer { + STATE_RETRIEVAL_TIMER => self.state_storage.tick(), + _ => warn!("IO service triggered unregistered timer '{}'", timer), + } + } +} + pub trait Importer { /// Process received private transaction fn import_private_transaction(&self, _rlp: &[u8]) -> Result; diff --git a/ethcore/private-tx/src/state_store.rs b/ethcore/private-tx/src/state_store.rs index d1008baaabe..a8b36f3f74a 100644 --- a/ethcore/private-tx/src/state_store.rs +++ b/ethcore/private-tx/src/state_store.rs @@ -15,6 +15,7 @@ // along with Parity Ethereum. If not, see . use std::sync::Arc; +use std::time::{Instant, Duration}; use parking_lot::RwLock; use ethereum_types::H256; use kvdb::KeyValueDB; @@ -22,6 +23,9 @@ use types::transaction::SignedTransaction; use private_transactions::VerifiedPrivateTransaction; use private_state_db::PrivateStateDB; +/// Max duration of retrieving state (in ms) +const MAX_REQUEST_SESSION_DURATION: u64 = 120 * 1000; + /// State of the private state sync #[derive(Clone, PartialEq)] pub enum SyncState { @@ -31,13 +35,18 @@ pub enum SyncState { Syncing, } +struct HashRequestSession { + hash: H256, + expiration_time: Instant, +} + /// Wrapper over storage for the private states pub struct PrivateStateStorage { verification_requests: RwLock>>, creation_requests: RwLock>, private_state_db: Arc, sync_state: RwLock, - syncing_hashes: RwLock>, + syncing_hashes: RwLock>, } impl PrivateStateStorage { @@ -63,8 +72,12 @@ impl PrivateStateStorage { let mut new_hashes = Vec::new(); for hash in hashes_to_sync { let mut hashes = self.syncing_hashes.write(); - if hashes.iter().find(|&h| h == hash).is_none() { - hashes.push(*hash); + if hashes.iter().find(|&h| h.hash == *hash).is_none() { + let hash_session = HashRequestSession { + hash: *hash, + expiration_time: Instant::now() + Duration::from_millis(MAX_REQUEST_SESSION_DURATION), + }; + hashes.push(hash_session); new_hashes.push(*hash); } } @@ -74,7 +87,7 @@ impl PrivateStateStorage { /// Signals that corresponding private state retrieved and added into the local db pub fn state_sync_completed(&self, synced_state_hash: &H256) { let mut syncing_hashes = self.syncing_hashes.write(); - if let Some(index) = syncing_hashes.iter().position(|h| h == synced_state_hash) { + if let Some(index) = syncing_hashes.iter().position(|h| h.hash == *synced_state_hash) { syncing_hashes.remove(index); } if syncing_hashes.is_empty() { @@ -113,4 +126,14 @@ impl PrivateStateStorage { let requests = requests_queue.drain(..).collect::>(); requests } + + /// State retrieval timer's tick + pub fn tick(&self) { + let mut syncing_hashes = self.syncing_hashes.write(); + syncing_hashes.retain(|hash| hash.expiration_time < Instant::now()); + if syncing_hashes.is_empty() { + // All states were downloaded + *self.sync_state.write() = SyncState::Idle; + } + } } \ No newline at end of file diff --git a/ethcore/service/src/service.rs b/ethcore/service/src/service.rs index b97d359ce7b..9d3c8703b4d 100644 --- a/ethcore/service/src/service.rs +++ b/ethcore/service/src/service.rs @@ -151,7 +151,8 @@ impl ClientService { private_keys, blockchain_db.key_value().clone(), )); - let private_tx = Arc::new(PrivateTxService::new(provider)); + let private_tx = Arc::new(PrivateTxService::new(provider.clone())); + io_service.register_handler(provider)?; let client_io = Arc::new(ClientIoHandler { client: client.clone(), From 44f55a45fa7ac23c7296c33385e4081aad354dea Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Mon, 29 Jul 2019 16:02:44 +0200 Subject: [PATCH 25/35] Store required hashes for every request, mark them stale if needed --- ethcore/private-tx/src/lib.rs | 55 +++++----- ethcore/private-tx/src/state_store.rs | 151 +++++++++++++++----------- 2 files changed, 116 insertions(+), 90 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 2b88844002a..992f96d77f7 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -87,7 +87,7 @@ pub use messages::{PrivateTransaction, SignedPrivateTransaction}; pub use private_state_db::PrivateStateDB; pub use error::Error; pub use log::{Logging, TransactionLog, ValidatorLog, PrivateTxStatus, FileLogsSerializer}; -use state_store::{PrivateStateStorage, SyncState}; +use state_store::{PrivateStateStorage, RequestType}; use std::sync::{Arc, Weak}; use std::collections::{HashMap, HashSet, BTreeMap}; @@ -307,13 +307,13 @@ impl Provider { match err { Error::PrivateStateNotFound => { trace!(target: "privatetx", "Private state for the contract not found, requesting from peers"); - self.state_storage.add_creation_request(signed_transaction); if let Some(ref logging) = self.logging { let contract_validators = self.get_validators(BlockId::Latest, &contract)?; logging.private_tx_created(&tx_hash, &contract_validators); logging.private_state_request(&tx_hash); } - self.request_private_state(&contract); + let request = RequestType::Creation(signed_transaction); + self.request_private_state(&contract, request); } _ => {} } @@ -399,8 +399,8 @@ impl Provider { Error::PrivateStateNotFound => { let contract = transaction.private_transaction.contract(); trace!(target: "privatetx", "Private state for the contract {:?} not found, requesting from peers", &contract); - self.state_storage.add_verification_request(transaction); - self.request_private_state(&contract); + let request = RequestType::Verification(transaction); + self.request_private_state(&contract, request); } _ => {} } @@ -532,7 +532,7 @@ impl Provider { self.notify(|notify| notify.broadcast(ChainMessageType::SignedPrivateTransaction(transaction_hash, message.clone()))); } - fn request_private_state(&self, address: &Address) { + fn request_private_state(&self, address: &Address, request_type: RequestType) { // Define the list of available contracts let mut private_contracts = Vec::new(); private_contracts.push(*address); @@ -547,17 +547,17 @@ impl Provider { } } // Check states for the avaialble contracts, if they're outdated - let mut stalled_contracts_hashes: Vec = Vec::new(); + let mut stalled_contracts_hashes: HashSet = HashSet::new(); for address in private_contracts { if let Ok(state_hash) = self.get_decrypted_state_from_contract(&address, BlockId::Latest) { let state_hash = H256::from_slice(&state_hash[0..32]); if let Err(_) = self.state_storage.private_state_db().state(&state_hash) { // State not found in the local db - stalled_contracts_hashes.push(state_hash); + stalled_contracts_hashes.insert(state_hash); } } } - let hashes_to_sync = self.state_storage.start_states_sync(&stalled_contracts_hashes); + let hashes_to_sync = self.state_storage.add_request(request_type, stalled_contracts_hashes); if !hashes_to_sync.is_empty() { trace!(target: "privatetx", "Requesting states for the following hashes: {:?}", hashes_to_sync); for hash in hashes_to_sync { @@ -568,25 +568,28 @@ impl Provider { fn private_state_sync_completed(&self, hash: &H256) -> Result<(), Error> { self.state_storage.state_sync_completed(hash); - if self.state_storage.current_sync_state() == SyncState::Idle { + if self.state_storage.requests_ready() { trace!(target: "privatetx", "Private state sync completed, processing pending requests"); - let creation_requests = self.state_storage.drain_creation_queue(); - for request in creation_requests { - match self.create_private_transaction(request) { - Ok(receipt) => trace!(target: "privatetx", "Creation request processed, receipt: {:?}", receipt), - Err(e) => error!(target: "privatetx", "Cannot process creation request with error: {:?}", e), - } - } - let verification_requests = self.state_storage.drain_verification_queue(); - for request in verification_requests { - if let Err(err) = self.process_verification_transaction(&request) { - warn!(target: "privatetx", "Error while processing pending verification request: {:?}", err); - match err { - Error::PrivateStateNotFound => { - let contract = request.private_transaction.contract(); - error!(target: "privatetx", "Cannot retrieve private state after sync for {:?}", &contract); + let ready_requests = self.state_storage.drain_ready_requests(); + for request in ready_requests { + match request { + RequestType::Creation(transaction) => { + match self.create_private_transaction(transaction) { + Ok(receipt) => trace!(target: "privatetx", "Creation request processed, receipt: {:?}", receipt), + Err(e) => error!(target: "privatetx", "Cannot process creation request with error: {:?}", e), + } + } + RequestType::Verification(transaction) => { + if let Err(err) = self.process_verification_transaction(&transaction) { + warn!(target: "privatetx", "Error while processing pending verification request: {:?}", err); + match err { + Error::PrivateStateNotFound => { + let contract = transaction.private_transaction.contract(); + error!(target: "privatetx", "Cannot retrieve private state after sync for {:?}", &contract); + } + _ => {} + } } - _ => {} } } } diff --git a/ethcore/private-tx/src/state_store.rs b/ethcore/private-tx/src/state_store.rs index a8b36f3f74a..8bac7afc7be 100644 --- a/ethcore/private-tx/src/state_store.rs +++ b/ethcore/private-tx/src/state_store.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . +use std::collections::HashSet; use std::sync::Arc; use std::time::{Instant, Duration}; use parking_lot::RwLock; @@ -26,26 +27,36 @@ use private_state_db::PrivateStateDB; /// Max duration of retrieving state (in ms) const MAX_REQUEST_SESSION_DURATION: u64 = 120 * 1000; -/// State of the private state sync +struct HashRequestSession { + hash: H256, + expiration_time: Instant, +} + +/// Type of the stored reques +pub enum RequestType { + /// Verification of private transaction + Verification(Arc), + /// Creation of the private transaction + Creation(SignedTransaction), +} + #[derive(Clone, PartialEq)] -pub enum SyncState { - /// No sync is running - Idle, - /// Private state sync is running +enum RequestState { Syncing, + Ready, + Stale, } -struct HashRequestSession { - hash: H256, - expiration_time: Instant, +struct StateRequest { + request_type: RequestType, + request_hashes: HashSet, + state: RequestState, } /// Wrapper over storage for the private states pub struct PrivateStateStorage { - verification_requests: RwLock>>, - creation_requests: RwLock>, private_state_db: Arc, - sync_state: RwLock, + requests: RwLock>, syncing_hashes: RwLock>, } @@ -53,35 +64,16 @@ impl PrivateStateStorage { /// Constructs the object pub fn new(db: Arc) -> Self { PrivateStateStorage { - verification_requests: RwLock::new(Vec::new()), - creation_requests: RwLock::new(Vec::new()), private_state_db: Arc::new(PrivateStateDB::new(db)), - sync_state: RwLock::new(SyncState::Idle), + requests: RwLock::new(Vec::new()), syncing_hashes: RwLock::default(), } } - /// Current sync state - pub fn current_sync_state(&self) -> SyncState { - (*self.sync_state.read()).clone() - } - - /// Adds information about states being synced now - pub fn start_states_sync(&self, hashes_to_sync: &Vec) -> Vec { - *self.sync_state.write() = SyncState::Syncing; - let mut new_hashes = Vec::new(); - for hash in hashes_to_sync { - let mut hashes = self.syncing_hashes.write(); - if hashes.iter().find(|&h| h.hash == *hash).is_none() { - let hash_session = HashRequestSession { - hash: *hash, - expiration_time: Instant::now() + Duration::from_millis(MAX_REQUEST_SESSION_DURATION), - }; - hashes.push(hash_session); - new_hashes.push(*hash); - } - } - new_hashes + /// Checks if ready for processing requests exist in queue + pub fn requests_ready(&self) -> bool { + let requests = self.requests.read(); + requests.iter().find(|r| r.state == RequestState::Ready).is_some() } /// Signals that corresponding private state retrieved and added into the local db @@ -90,10 +82,7 @@ impl PrivateStateStorage { if let Some(index) = syncing_hashes.iter().position(|h| h.hash == *synced_state_hash) { syncing_hashes.remove(index); } - if syncing_hashes.is_empty() { - // All states were downloaded - *self.sync_state.write() = SyncState::Idle; - } + self.mark_hash_ready(synced_state_hash); } /// Returns underlying DB @@ -101,39 +90,73 @@ impl PrivateStateStorage { self.private_state_db.clone() } - /// Stores verification request for the later verification - pub fn add_verification_request(&self, transaction: Arc) { - let mut verification_requests = self.verification_requests.write(); - verification_requests.push(transaction); - } - - /// Drains all verification requests to process - pub fn drain_verification_queue(&self) -> Vec> { - let mut requests_queue = self.verification_requests.write(); - let requests = requests_queue.drain(..).collect::>(); - requests - } - - /// Stores creation request for the later creation - pub fn add_creation_request(&self, transaction: SignedTransaction) { - let mut creation_requests = self.creation_requests.write(); - creation_requests.push(transaction); + /// Store a request for state's sync and later processing, returns new hashes, which sync is required + pub fn add_request(&self, request_type: RequestType, request_hashes: HashSet) -> Vec { + let request = StateRequest { + request_type: request_type, + request_hashes: request_hashes.clone(), + state: RequestState::Syncing, + }; + let mut requests = self.requests.write(); + requests.push(request); + let mut new_hashes = Vec::new(); + for hash in request_hashes { + let mut hashes = self.syncing_hashes.write(); + if hashes.iter().find(|&h| h.hash == hash).is_none() { + let hash_session = HashRequestSession { + hash, + expiration_time: Instant::now() + Duration::from_millis(MAX_REQUEST_SESSION_DURATION), + }; + hashes.push(hash_session); + new_hashes.push(hash); + } + } + new_hashes } - /// Drains all creation requests to process - pub fn drain_creation_queue(&self) -> Vec { - let mut requests_queue = self.creation_requests.write(); - let requests = requests_queue.drain(..).collect::>(); - requests + /// Drains ready requests to process + pub fn drain_ready_requests(&self) -> Vec { + let mut requests_queue = self.requests.write(); + let mut drained = Vec::new(); + let mut i = 0; + while i != requests_queue.len() { + if requests_queue[i].state == RequestState::Ready { + let request = requests_queue.remove(i); + drained.push(request.request_type); + } else { + i += 1; + } + } + drained } /// State retrieval timer's tick pub fn tick(&self) { let mut syncing_hashes = self.syncing_hashes.write(); + for hash in syncing_hashes.iter() { + if hash.expiration_time >= Instant::now() { + self.mark_hash_stale(&hash.hash); + } + } syncing_hashes.retain(|hash| hash.expiration_time < Instant::now()); - if syncing_hashes.is_empty() { - // All states were downloaded - *self.sync_state.write() = SyncState::Idle; + } + + fn mark_hash_ready(&self, ready_hash: &H256) { + let mut requests = self.requests.write(); + for request in requests.iter_mut() { + request.request_hashes.remove(ready_hash); + if request.request_hashes.is_empty() && request.state == RequestState::Syncing { + request.state = RequestState::Ready; + } + } + } + + fn mark_hash_stale(&self, stale_hash: &H256) { + let mut requests = self.requests.write(); + for request in requests.iter_mut() { + if request.request_hashes.contains(stale_hash) { + request.state = RequestState::Stale; + } } } } \ No newline at end of file From 86b91387fe248f2fa93a52fb95240473885ce90e Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Tue, 30 Jul 2019 16:46:23 +0200 Subject: [PATCH 26/35] Store requested private state hashes and check received data --- Cargo.lock | 1 + ethcore/private-tx/Cargo.toml | 1 + ethcore/private-tx/src/lib.rs | 1 + ethcore/private-tx/src/private_state_db.rs | 8 +++++++- ethcore/sync/src/chain/handler.rs | 15 +++++++++++++++ ethcore/sync/src/chain/mod.rs | 9 +++++++-- ethcore/sync/src/chain/propagator.rs | 1 + ethcore/sync/src/chain/requester.rs | 2 ++ 8 files changed, 35 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0d80b1f61ca..ba6c147407f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1242,6 +1242,7 @@ dependencies = [ "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "journaldb 0.2.0", "keccak-hash 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak-hasher 0.1.1", "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "machine 0.1.0", diff --git a/ethcore/private-tx/Cargo.toml b/ethcore/private-tx/Cargo.toml index 5a38da1762b..419843953dc 100644 --- a/ethcore/private-tx/Cargo.toml +++ b/ethcore/private-tx/Cargo.toml @@ -27,6 +27,7 @@ parity-util-mem = "0.2.0" hash-db = "0.11.0" heapsize = "0.4" keccak-hash = "0.2.0" +keccak-hasher = { path = "../../util/keccak-hasher" } kvdb = "0.1" log = "0.4" machine = { path = "../machine" } diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 992f96d77f7..5eef1416e4c 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -43,6 +43,7 @@ extern crate parity_util_mem; extern crate hash_db; extern crate heapsize; extern crate keccak_hash as hash; +extern crate keccak_hasher; extern crate kvdb; extern crate machine; extern crate journaldb; diff --git a/ethcore/private-tx/src/private_state_db.rs b/ethcore/private-tx/src/private_state_db.rs index d136ebb34ed..19c2f84da13 100644 --- a/ethcore/private-tx/src/private_state_db.rs +++ b/ethcore/private-tx/src/private_state_db.rs @@ -20,7 +20,8 @@ use ethereum_types::H256; use bytes::Bytes; use journaldb::overlaydb::OverlayDB; use kvdb::{KeyValueDB, DBTransaction}; -use hash_db::{HashDB, EMPTY_PREFIX}; +use keccak_hasher::KeccakHasher; +use hash_db::{HashDB, EMPTY_PREFIX, Hasher}; use ethcore_db::COL_PRIVATE_TRANSACTIONS_STATE; use error::Error; @@ -56,4 +57,9 @@ impl PrivateStateDB { trace!(target: "privatetx", "Private state saved to db, its hash: {:?}", state_hash); Ok(state_hash) } + + /// Returns state's hash without committing it to DB + pub fn state_hash(&self, state: &Bytes) -> Result { + Ok(KeccakHasher::hash(state)) + } } \ No newline at end of file diff --git a/ethcore/sync/src/chain/handler.rs b/ethcore/sync/src/chain/handler.rs index f38858afda6..27da6931f6f 100644 --- a/ethcore/sync/src/chain/handler.rs +++ b/ethcore/sync/src/chain/handler.rs @@ -588,6 +588,7 @@ impl SyncHandler { asking: PeerAsking::Nothing, asking_blocks: Vec::new(), asking_hash: None, + asking_private_state: None, ask_time: Instant::now(), last_sent_transactions: Default::default(), last_sent_private_transactions: Default::default(), @@ -751,6 +752,14 @@ impl SyncHandler { trace!(target: "sync", "{}: Ignored unexpected private state data", peer_id); return Ok(()); } + let requested_hash = sync.peers.get(&peer_id).and_then(|p| p.asking_private_state); + let requested_hash = match requested_hash { + Some(hash) => hash, + None => { + debug!(target: "sync", "{}: Ignored unexpected private state (requested_hash is None)", peer_id); + return Ok(()); + } + }; let private_handler = match sync.private_tx_handler { Some(ref handler) => handler, None => { @@ -762,6 +771,12 @@ impl SyncHandler { let private_state_data: Bytes = r.val_at(0)?; match io.private_state() { Some(db) => { + // Check hash of the rececived data before submitting it to DB + let received_hash = db.state_hash(&private_state_data).unwrap_or_default(); + if received_hash != requested_hash { + trace!(target: "sync", "{} Ignoring private state data with unexpected hash from peer", peer_id); + return Ok(()); + } match db.save_state(&private_state_data) { Ok(hash) => { if let Err(err) = private_handler.private_state_synced(&hash) { diff --git a/ethcore/sync/src/chain/mod.rs b/ethcore/sync/src/chain/mod.rs index a6cf62b7dcd..d71cd9f8497 100644 --- a/ethcore/sync/src/chain/mod.rs +++ b/ethcore/sync/src/chain/mod.rs @@ -320,6 +320,8 @@ pub struct PeerInfo { asking_blocks: Vec, /// Holds requested header hash if currently requesting block header by hash asking_hash: Option, + /// Holds requested private state hash + asking_private_state: Option, /// Holds requested snapshot chunk hash if any. asking_snapshot_data: Option, /// Request timestamp @@ -356,6 +358,7 @@ impl PeerInfo { fn reset_asking(&mut self) { self.asking_blocks.clear(); self.asking_hash = None; + self.asking_private_state = None; // mark any pending requests as expired if self.asking != PeerAsking::Nothing && self.is_allowed() { self.expired = true; @@ -1299,7 +1302,8 @@ impl ChainSync { fn get_private_state_peers(&self) -> Vec { self.peers.iter().filter_map( |(id, p)| if p.protocol_version >= PAR_PROTOCOL_VERSION_4.0 - && p.private_tx_enabled { + && p.private_tx_enabled + && self.active_peers.contains(id) { Some(*id) } else { None @@ -1381,7 +1385,7 @@ impl ChainSync { pub fn request_private_state(&mut self, io: &mut SyncIo, hash: &H256) { let private_state_peers = self.get_private_state_peers(); if private_state_peers.is_empty() { - error!(target: "privatetx", "Cannot request private state, no peers with private tx enabled connected"); + error!(target: "privatetx", "Cannot request private state, no peers with private tx enabled available"); } else { trace!(target: "privatetx", "Requesting private stats from {:?}", private_state_peers); for peer_id in private_state_peers { @@ -1509,6 +1513,7 @@ pub mod tests { asking: PeerAsking::Nothing, asking_blocks: Vec::new(), asking_hash: None, + asking_private_state: None, ask_time: Instant::now(), last_sent_transactions: Default::default(), last_sent_private_transactions: Default::default(), diff --git a/ethcore/sync/src/chain/propagator.rs b/ethcore/sync/src/chain/propagator.rs index e3db0ef8c6b..1afb5e92b21 100644 --- a/ethcore/sync/src/chain/propagator.rs +++ b/ethcore/sync/src/chain/propagator.rs @@ -427,6 +427,7 @@ mod tests { asking: PeerAsking::Nothing, asking_blocks: Vec::new(), asking_hash: None, + asking_private_state: None, ask_time: Instant::now(), last_sent_transactions: Default::default(), last_sent_private_transactions: Default::default(), diff --git a/ethcore/sync/src/chain/requester.rs b/ethcore/sync/src/chain/requester.rs index 196f96705b1..b47c3930149 100644 --- a/ethcore/sync/src/chain/requester.rs +++ b/ethcore/sync/src/chain/requester.rs @@ -105,6 +105,8 @@ impl SyncRequester { let mut rlp = RlpStream::new_list(1); rlp.append(hash); SyncRequester::send_request(sync, io, peer_id, PeerAsking::PrivateState, GetPrivateStatePacket, rlp.out()); + let peer = sync.peers.get_mut(&peer_id).expect("peer_id may originate either from on_packet, where it is already validated or from enumerating self.peers. qed"); + peer.asking_private_state = Some(hash.clone()); } /// Request headers from a peer by block hash From 70cd04558f657b3589d0240835262e8fc0a87966 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Wed, 31 Jul 2019 12:38:16 +0200 Subject: [PATCH 27/35] Log stale requests --- ethcore/private-tx/src/lib.rs | 7 ++++--- ethcore/private-tx/src/state_store.rs | 27 ++++++++++++++++++++++----- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 5eef1416e4c..f6f56583c72 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -217,7 +217,7 @@ pub struct Provider { accounts: Arc, channel: IoChannel, keys_provider: Arc, - logging: Option, + logging: Option>, use_offchain_storage: bool, state_storage: PrivateStateStorage, } @@ -243,6 +243,7 @@ impl Provider { db: Arc, ) -> Self { keys_provider.update_acl_contract(); + let logging = config.logs_path.map(|path| Arc::new(Logging::new(Arc::new(FileLogsSerializer::with_path(path))))); Provider { encryptor, validator_accounts: config.validator_accounts.into_iter().collect(), @@ -255,9 +256,9 @@ impl Provider { accounts, channel, keys_provider, - logging: config.logs_path.map(|path| Logging::new(Arc::new(FileLogsSerializer::with_path(path)))), + logging: logging.clone(), use_offchain_storage: config.use_offchain_storage, - state_storage: PrivateStateStorage::new(db), + state_storage: PrivateStateStorage::new(db, logging), } } diff --git a/ethcore/private-tx/src/state_store.rs b/ethcore/private-tx/src/state_store.rs index 8bac7afc7be..a99fe45a4f8 100644 --- a/ethcore/private-tx/src/state_store.rs +++ b/ethcore/private-tx/src/state_store.rs @@ -23,6 +23,7 @@ use kvdb::KeyValueDB; use types::transaction::SignedTransaction; use private_transactions::VerifiedPrivateTransaction; use private_state_db::PrivateStateDB; +use log::Logging; /// Max duration of retrieving state (in ms) const MAX_REQUEST_SESSION_DURATION: u64 = 120 * 1000; @@ -44,7 +45,6 @@ pub enum RequestType { enum RequestState { Syncing, Ready, - Stale, } struct StateRequest { @@ -58,15 +58,17 @@ pub struct PrivateStateStorage { private_state_db: Arc, requests: RwLock>, syncing_hashes: RwLock>, + logging: Option>, } impl PrivateStateStorage { /// Constructs the object - pub fn new(db: Arc) -> Self { + pub fn new(db: Arc, logging: Option>) -> Self { PrivateStateStorage { private_state_db: Arc::new(PrivateStateDB::new(db)), requests: RwLock::new(Vec::new()), syncing_hashes: RwLock::default(), + logging, } } @@ -153,10 +155,25 @@ impl PrivateStateStorage { fn mark_hash_stale(&self, stale_hash: &H256) { let mut requests = self.requests.write(); - for request in requests.iter_mut() { + requests.retain(|request| { + let mut delete_request = false; if request.request_hashes.contains(stale_hash) { - request.state = RequestState::Stale; + let tx_hash; + match &request.request_type { + RequestType::Verification(transaction) => { + tx_hash = transaction.transaction_hash; + } + RequestType::Creation(transaction) => { + tx_hash = transaction.hash(); + if let Some(ref logging) = self.logging { + logging.private_state_sync_failed(&tx_hash); + } + } + } + trace!(target: "privatetx", "Private state request for {:?} staled due to timeout", &tx_hash); + delete_request = true; } - } + !delete_request + }); } } \ No newline at end of file From 90f17d2f3d7af1c7fdd24648b5664c0ad311fe58 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Mon, 5 Aug 2019 15:02:09 +0200 Subject: [PATCH 28/35] State insertion fix --- ethcore/private-tx/src/private_state_db.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethcore/private-tx/src/private_state_db.rs b/ethcore/private-tx/src/private_state_db.rs index 19c2f84da13..c43c066d18a 100644 --- a/ethcore/private-tx/src/private_state_db.rs +++ b/ethcore/private-tx/src/private_state_db.rs @@ -50,7 +50,7 @@ impl PrivateStateDB { /// Stores state for the hash pub fn save_state(&self, storage: &Bytes) -> Result { let mut private_state = self.private_state.write(); - let state_hash = private_state.insert(storage, EMPTY_PREFIX); + let state_hash = private_state.insert(EMPTY_PREFIX, storage); let mut transaction = DBTransaction::new(); private_state.commit_to_batch(&mut transaction)?; self.db.write(transaction).map_err(|_| Error::DatabaseWriteError)?; From f8456f8a1356a2b9ec03bce471a07e357d99f64a Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Fri, 9 Aug 2019 12:14:11 +0200 Subject: [PATCH 29/35] Refactoring of how logging passed to state store --- ethcore/private-tx/src/lib.rs | 70 ++++++++++++++------------- ethcore/private-tx/src/state_store.rs | 12 ++--- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index f6f56583c72..1f667081be3 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -217,7 +217,7 @@ pub struct Provider { accounts: Arc, channel: IoChannel, keys_provider: Arc, - logging: Option>, + logging: Option, use_offchain_storage: bool, state_storage: PrivateStateStorage, } @@ -243,7 +243,6 @@ impl Provider { db: Arc, ) -> Self { keys_provider.update_acl_contract(); - let logging = config.logs_path.map(|path| Arc::new(Logging::new(Arc::new(FileLogsSerializer::with_path(path))))); Provider { encryptor, validator_accounts: config.validator_accounts.into_iter().collect(), @@ -256,9 +255,9 @@ impl Provider { accounts, channel, keys_provider, - logging: logging.clone(), + logging: config.logs_path.map(|path| Logging::new(Arc::new(FileLogsSerializer::with_path(path)))), use_offchain_storage: config.use_offchain_storage, - state_storage: PrivateStateStorage::new(db, logging), + state_storage: PrivateStateStorage::new(db), } } @@ -305,38 +304,41 @@ impl Provider { // in private-tx to avoid such mistakes. let contract_nonce = self.get_contract_nonce(&contract, BlockId::Latest)?; let private_state = self.execute_private_transaction(BlockId::Latest, &signed_transaction); - if let Err(err) = private_state { - match err { - Error::PrivateStateNotFound => { - trace!(target: "privatetx", "Private state for the contract not found, requesting from peers"); - if let Some(ref logging) = self.logging { - let contract_validators = self.get_validators(BlockId::Latest, &contract)?; - logging.private_tx_created(&tx_hash, &contract_validators); - logging.private_state_request(&tx_hash); - } - let request = RequestType::Creation(signed_transaction); - self.request_private_state(&contract, request); + match private_state { + Err(err) => { + match err { + Error::PrivateStateNotFound => { + trace!(target: "privatetx", "Private state for the contract not found, requesting from peers"); + if let Some(ref logging) = self.logging { + let contract_validators = self.get_validators(BlockId::Latest, &contract)?; + logging.private_tx_created(&tx_hash, &contract_validators); + logging.private_state_request(&tx_hash); + } + let request = RequestType::Creation(signed_transaction); + self.request_private_state(&contract, request); + }, + _ => {}, } - _ => {} + Err(err) + } + Ok(private_state) => { + trace!(target: "privatetx", "Private transaction created, encrypted transaction: {:?}, private state: {:?}", private, private_state); + let contract_validators = self.get_validators(BlockId::Latest, &contract)?; + 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.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, + status_code: 0, + }) } - return Err(err); - } - let private_state = private_state.expect("Valid state since this"); - trace!(target: "privatetx", "Private transaction created, encrypted transaction: {:?}, private state: {:?}", private, private_state); - let contract_validators = self.get_validators(BlockId::Latest, &contract)?; - 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.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, - status_code: 0, - }) } /// Calculate hash from united private state and contract nonce @@ -878,7 +880,7 @@ impl IoHandler for Provider { fn timeout(&self, _io: &IoContext, timer: TimerToken) { match timer { - STATE_RETRIEVAL_TIMER => self.state_storage.tick(), + STATE_RETRIEVAL_TIMER => self.state_storage.tick(&self.logging), _ => warn!("IO service triggered unregistered timer '{}'", timer), } } diff --git a/ethcore/private-tx/src/state_store.rs b/ethcore/private-tx/src/state_store.rs index a99fe45a4f8..2f2e893cbb2 100644 --- a/ethcore/private-tx/src/state_store.rs +++ b/ethcore/private-tx/src/state_store.rs @@ -58,17 +58,15 @@ pub struct PrivateStateStorage { private_state_db: Arc, requests: RwLock>, syncing_hashes: RwLock>, - logging: Option>, } impl PrivateStateStorage { /// Constructs the object - pub fn new(db: Arc, logging: Option>) -> Self { + pub fn new(db: Arc) -> Self { PrivateStateStorage { private_state_db: Arc::new(PrivateStateDB::new(db)), requests: RwLock::new(Vec::new()), syncing_hashes: RwLock::default(), - logging, } } @@ -133,11 +131,11 @@ impl PrivateStateStorage { } /// State retrieval timer's tick - pub fn tick(&self) { + pub fn tick(&self, logging: &Option) { let mut syncing_hashes = self.syncing_hashes.write(); for hash in syncing_hashes.iter() { if hash.expiration_time >= Instant::now() { - self.mark_hash_stale(&hash.hash); + self.mark_hash_stale(&hash.hash, logging); } } syncing_hashes.retain(|hash| hash.expiration_time < Instant::now()); @@ -153,7 +151,7 @@ impl PrivateStateStorage { } } - fn mark_hash_stale(&self, stale_hash: &H256) { + fn mark_hash_stale(&self, stale_hash: &H256, logging: &Option) { let mut requests = self.requests.write(); requests.retain(|request| { let mut delete_request = false; @@ -165,7 +163,7 @@ impl PrivateStateStorage { } RequestType::Creation(transaction) => { tx_hash = transaction.hash(); - if let Some(ref logging) = self.logging { + if let Some(ref logging) = logging { logging.private_state_sync_failed(&tx_hash); } } From 7b9370e60f5c5a3f36a241f1a8d6864b2e864bbd Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Fri, 9 Aug 2019 12:47:01 +0200 Subject: [PATCH 30/35] Heapsize removed, syncing hashes structure reworked --- Cargo.lock | 1 - ethcore/private-tx/Cargo.toml | 1 - ethcore/private-tx/src/lib.rs | 1 - ethcore/private-tx/src/state_store.rs | 32 +++++++++------------------ 4 files changed, 10 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ba6c147407f..6833cb71f80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1239,7 +1239,6 @@ dependencies = [ "fetch 0.1.0", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "hash-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "journaldb 0.2.0", "keccak-hash 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "keccak-hasher 0.1.1", diff --git a/ethcore/private-tx/Cargo.toml b/ethcore/private-tx/Cargo.toml index 419843953dc..f67659805ce 100644 --- a/ethcore/private-tx/Cargo.toml +++ b/ethcore/private-tx/Cargo.toml @@ -25,7 +25,6 @@ fetch = { path = "../../util/fetch" } futures = "0.1" parity-util-mem = "0.2.0" hash-db = "0.11.0" -heapsize = "0.4" keccak-hash = "0.2.0" keccak-hasher = { path = "../../util/keccak-hasher" } kvdb = "0.1" diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 1f667081be3..ce250449617 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -41,7 +41,6 @@ extern crate fetch; extern crate futures; extern crate parity_util_mem; extern crate hash_db; -extern crate heapsize; extern crate keccak_hash as hash; extern crate keccak_hasher; extern crate kvdb; diff --git a/ethcore/private-tx/src/state_store.rs b/ethcore/private-tx/src/state_store.rs index 2f2e893cbb2..002dca36505 100644 --- a/ethcore/private-tx/src/state_store.rs +++ b/ethcore/private-tx/src/state_store.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use std::collections::HashSet; +use std::collections::{HashSet, HashMap}; use std::sync::Arc; use std::time::{Instant, Duration}; use parking_lot::RwLock; @@ -28,11 +28,6 @@ use log::Logging; /// Max duration of retrieving state (in ms) const MAX_REQUEST_SESSION_DURATION: u64 = 120 * 1000; -struct HashRequestSession { - hash: H256, - expiration_time: Instant, -} - /// Type of the stored reques pub enum RequestType { /// Verification of private transaction @@ -57,7 +52,7 @@ struct StateRequest { pub struct PrivateStateStorage { private_state_db: Arc, requests: RwLock>, - syncing_hashes: RwLock>, + syncing_hashes: RwLock>, } impl PrivateStateStorage { @@ -79,9 +74,7 @@ impl PrivateStateStorage { /// Signals that corresponding private state retrieved and added into the local db pub fn state_sync_completed(&self, synced_state_hash: &H256) { let mut syncing_hashes = self.syncing_hashes.write(); - if let Some(index) = syncing_hashes.iter().position(|h| h.hash == *synced_state_hash) { - syncing_hashes.remove(index); - } + syncing_hashes.remove(synced_state_hash); self.mark_hash_ready(synced_state_hash); } @@ -102,12 +95,7 @@ impl PrivateStateStorage { let mut new_hashes = Vec::new(); for hash in request_hashes { let mut hashes = self.syncing_hashes.write(); - if hashes.iter().find(|&h| h.hash == hash).is_none() { - let hash_session = HashRequestSession { - hash, - expiration_time: Instant::now() + Duration::from_millis(MAX_REQUEST_SESSION_DURATION), - }; - hashes.push(hash_session); + if hashes.insert(hash, Instant::now() + Duration::from_millis(MAX_REQUEST_SESSION_DURATION)).is_none() { new_hashes.push(hash); } } @@ -133,12 +121,12 @@ impl PrivateStateStorage { /// State retrieval timer's tick pub fn tick(&self, logging: &Option) { let mut syncing_hashes = self.syncing_hashes.write(); - for hash in syncing_hashes.iter() { - if hash.expiration_time >= Instant::now() { - self.mark_hash_stale(&hash.hash, logging); - } - } - syncing_hashes.retain(|hash| hash.expiration_time < Instant::now()); + let current_time = Instant::now(); + syncing_hashes + .iter() + .filter(|&(_, expiration_time)| *expiration_time >= current_time) + .for_each(|(hash, _)| self.mark_hash_stale(&hash, logging)); + syncing_hashes.retain(|_, expiration_time| *expiration_time < current_time); } fn mark_hash_ready(&self, ready_hash: &H256) { From 97bd6d6fceb6f55f9d0ded48e7cce604a0f40d1c Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Fri, 9 Aug 2019 13:00:57 +0200 Subject: [PATCH 31/35] Check state length returned by contract --- ethcore/private-tx/src/lib.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index ce250449617..087f85b63d7 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -314,7 +314,7 @@ impl Provider { logging.private_state_request(&tx_hash); } let request = RequestType::Creation(signed_transaction); - self.request_private_state(&contract, request); + self.request_private_state(&contract, request)?; }, _ => {}, } @@ -403,7 +403,7 @@ impl Provider { let contract = transaction.private_transaction.contract(); trace!(target: "privatetx", "Private state for the contract {:?} not found, requesting from peers", &contract); let request = RequestType::Verification(transaction); - self.request_private_state(&contract, request); + self.request_private_state(&contract, request)?; } _ => {} } @@ -535,7 +535,7 @@ impl Provider { self.notify(|notify| notify.broadcast(ChainMessageType::SignedPrivateTransaction(transaction_hash, message.clone()))); } - fn request_private_state(&self, address: &Address, request_type: RequestType) { + fn request_private_state(&self, address: &Address, request_type: RequestType) -> Result<(), Error> { // Define the list of available contracts let mut private_contracts = Vec::new(); private_contracts.push(*address); @@ -553,7 +553,10 @@ impl Provider { let mut stalled_contracts_hashes: HashSet = HashSet::new(); for address in private_contracts { if let Ok(state_hash) = self.get_decrypted_state_from_contract(&address, BlockId::Latest) { - let state_hash = H256::from_slice(&state_hash[0..32]); + if state_hash.len() != H256::len_bytes() { + return Err(Error::StateIncorrect); + } + let state_hash = H256::from_slice(&state_hash); if let Err(_) = self.state_storage.private_state_db().state(&state_hash) { // State not found in the local db stalled_contracts_hashes.insert(state_hash); @@ -567,6 +570,7 @@ impl Provider { self.notify(|notify| notify.broadcast(ChainMessageType::PrivateStateRequest(hash))); } } + Ok(()) } fn private_state_sync_completed(&self, hash: &H256) -> Result<(), Error> { @@ -626,7 +630,10 @@ impl Provider { match self.use_offchain_storage { true => { let hashed_state = self.get_decrypted_state_from_contract(address, block)?; - let hashed_state = H256::from_slice(&hashed_state[0..32]); + if hashed_state.len() != H256::len_bytes() { + return Err(Error::StateIncorrect); + } + let hashed_state = H256::from_slice(&hashed_state); let stored_state_data = self.state_storage.private_state_db().state(&hashed_state)?; self.decrypt(address, &stored_state_data) } From 3d95ab88ccc2e04ec0924fe02eb57bdf9e6881dc Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Mon, 12 Aug 2019 11:44:26 +0200 Subject: [PATCH 32/35] Get rid of OverlayDB --- ethcore/private-tx/src/private_state_db.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/ethcore/private-tx/src/private_state_db.rs b/ethcore/private-tx/src/private_state_db.rs index c43c066d18a..a701f7330ef 100644 --- a/ethcore/private-tx/src/private_state_db.rs +++ b/ethcore/private-tx/src/private_state_db.rs @@ -15,19 +15,16 @@ // along with Parity Ethereum. If not, see . use std::sync::Arc; -use parking_lot::RwLock; use ethereum_types::H256; use bytes::Bytes; -use journaldb::overlaydb::OverlayDB; use kvdb::{KeyValueDB, DBTransaction}; use keccak_hasher::KeccakHasher; -use hash_db::{HashDB, EMPTY_PREFIX, Hasher}; +use hash_db::Hasher; use ethcore_db::COL_PRIVATE_TRANSACTIONS_STATE; use error::Error; /// Wrapper around local db with private state for sync purposes pub struct PrivateStateDB { - private_state: RwLock, db: Arc, } @@ -35,24 +32,24 @@ impl PrivateStateDB { /// Constructs the object pub fn new(db: Arc) -> Self { PrivateStateDB { - private_state: RwLock::new(OverlayDB::new(db.clone(), COL_PRIVATE_TRANSACTIONS_STATE)), db, } } /// Returns saved state for the hash pub fn state(&self, state_hash: &H256) -> Result { - let private_state = self.private_state.read(); trace!(target: "privatetx", "Retrieve private state from db with hash: {:?}", state_hash); - private_state.get(state_hash, EMPTY_PREFIX).map(|s| s.to_vec()).ok_or(Error::PrivateStateNotFound) + self.db.get(COL_PRIVATE_TRANSACTIONS_STATE, state_hash.as_bytes()) + .expect("Low-level database error. Some issue with your hard disk?") + .map(|s| s.to_vec()) + .ok_or(Error::PrivateStateNotFound) } /// Stores state for the hash pub fn save_state(&self, storage: &Bytes) -> Result { - let mut private_state = self.private_state.write(); - let state_hash = private_state.insert(EMPTY_PREFIX, storage); + let state_hash = self.state_hash(storage)?; let mut transaction = DBTransaction::new(); - private_state.commit_to_batch(&mut transaction)?; + transaction.put(COL_PRIVATE_TRANSACTIONS_STATE, state_hash.as_bytes(), storage); self.db.write(transaction).map_err(|_| Error::DatabaseWriteError)?; trace!(target: "privatetx", "Private state saved to db, its hash: {:?}", state_hash); Ok(state_hash) From ad000e3df3eb126b19bc1b6f79abcdd3472e934f Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Fri, 16 Aug 2019 11:22:33 +0200 Subject: [PATCH 33/35] hash-db version updated --- Cargo.lock | 2 +- ethcore/private-tx/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6833cb71f80..152da922359 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1238,7 +1238,7 @@ dependencies = [ "ethkey 0.3.0", "fetch 0.1.0", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "journaldb 0.2.0", "keccak-hash 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "keccak-hasher 0.1.1", diff --git a/ethcore/private-tx/Cargo.toml b/ethcore/private-tx/Cargo.toml index f67659805ce..2c793b04271 100644 --- a/ethcore/private-tx/Cargo.toml +++ b/ethcore/private-tx/Cargo.toml @@ -24,7 +24,7 @@ ethkey = { path = "../../accounts/ethkey" } fetch = { path = "../../util/fetch" } futures = "0.1" parity-util-mem = "0.2.0" -hash-db = "0.11.0" +hash-db = "0.15.0" keccak-hash = "0.2.0" keccak-hasher = { path = "../../util/keccak-hasher" } kvdb = "0.1" From cdc11aedaec91a502cd31e61d460ec4695178504 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Fri, 16 Aug 2019 11:55:59 +0200 Subject: [PATCH 34/35] Test fixed --- ethcore/private-tx/tests/private_contract.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethcore/private-tx/tests/private_contract.rs b/ethcore/private-tx/tests/private_contract.rs index db06f899ce8..77a32c049fb 100644 --- a/ethcore/private-tx/tests/private_contract.rs +++ b/ethcore/private-tx/tests/private_contract.rs @@ -37,7 +37,7 @@ use types::ids::BlockId; use types::transaction::{Transaction, Action}; use ethcore::{ CreateContractAddress, - test_helpers::{generate_dummy_client, push_block_with_transactions}, + test_helpers::{generate_dummy_client, push_block_with_transactions, new_db}, miner::Miner, spec, }; From d003df87b6f19f541cbdb064297c57fabc08b7c5 Mon Sep 17 00:00:00 2001 From: Anton Gavrilov Date: Fri, 16 Aug 2019 12:44:07 +0200 Subject: [PATCH 35/35] One more test fixed --- ethcore/sync/src/tests/private.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ethcore/sync/src/tests/private.rs b/ethcore/sync/src/tests/private.rs index c9c6b15008c..27fa6f01339 100644 --- a/ethcore/sync/src/tests/private.rs +++ b/ethcore/sync/src/tests/private.rs @@ -176,8 +176,8 @@ fn sync_private_state() { let io_handler0: Arc> = Arc::new(TestIoHandler::new(net.peer(0).chain.clone())); let io_handler1: Arc> = Arc::new(TestIoHandler::new(net.peer(1).chain.clone())); - net.peer(0).miner.set_author(miner::Author::Sealer(engines::signer::from_keypair(s0.clone()))); - net.peer(1).miner.set_author(miner::Author::Sealer(engines::signer::from_keypair(s1.clone()))); + net.peer(0).miner.set_author(miner::Author::Sealer(signer::from_keypair(s0.clone()))); + net.peer(1).miner.set_author(miner::Author::Sealer(signer::from_keypair(s1.clone()))); net.peer(0).chain.engine().register_client(Arc::downgrade(&net.peer(0).chain) as _); net.peer(1).chain.engine().register_client(Arc::downgrade(&net.peer(1).chain) as _); net.peer(0).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler0)));