diff --git a/Cargo.lock b/Cargo.lock index 8d60c2289..f7ab1ad9e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1477,16 +1477,10 @@ name = "ethcore-secretstore" version = "1.0.0" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "client-traits 0.1.0", - "common-types 0.1.0", "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", "ethabi 9.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "ethabi-contract 9.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethabi-derive 9.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.12.0", - "ethcore-accounts 0.1.0", - "ethcore-call-contract 0.1.0", - "ethcore-sync 1.12.0", "ethereum-types 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethkey 0.4.0", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1502,7 +1496,6 @@ dependencies = [ "parity-runtime 0.1.0", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "registrar 0.0.1", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3085,9 +3078,11 @@ dependencies = [ "dir 0.1.2", "docopt 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "engine 0.1.0", + "ethabi 9.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore 1.12.0", "ethcore-accounts 0.1.0", "ethcore-blockchain 0.1.0", + "ethcore-call-contract 0.1.0", "ethcore-db 0.1.0", "ethcore-io 1.12.0", "ethcore-light 1.12.0", diff --git a/Cargo.toml b/Cargo.toml index 53ffb641e..9d667e9ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,9 +18,11 @@ ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" } dir = { path = "util/dir" } docopt = "1.0" engine = { path = "ethcore/engine" } +ethabi = { version = "9.0.1", optional = true } ethcore = { path = "ethcore", features = ["parity"] } ethcore-accounts = { path = "accounts", optional = true } ethcore-blockchain = { path = "ethcore/blockchain" } +ethcore-call-contract = { path = "ethcore/call-contract", optional = true } ethcore-db = { path = "ethcore/db" } ethcore-io = { path = "util/io" } ethcore-light = { path = "ethcore/light" } @@ -97,7 +99,7 @@ test-heavy = ["ethcore/test-heavy"] evm-debug = ["ethcore/evm-debug"] evm-debug-tests = ["ethcore/evm-debug-tests"] slow-blocks = ["ethcore/slow-blocks"] -secretstore = ["ethcore-secretstore", "ethcore-secretstore/accounts"] +secretstore = ["ethcore-secretstore", "accounts", "ethabi", "ethcore-call-contract"] final = ["parity-version/final"] deadlock_detection = ["parking_lot/deadlock_detection"] # to create a memory profile (requires nightly rust), use e.g. diff --git a/parity/lib.rs b/parity/lib.rs index 0af8ce419..c814200bb 100644 --- a/parity/lib.rs +++ b/parity/lib.rs @@ -86,6 +86,12 @@ extern crate ethcore_accounts as accounts; #[cfg(feature = "secretstore")] extern crate ethcore_secretstore; +#[cfg(feature = "secretstore")] +extern crate ethabi; + +#[cfg(feature = "secretstore")] +extern crate ethcore_call_contract as call_contract; + #[cfg(test)] #[macro_use] extern crate pretty_assertions; diff --git a/parity/secretstore/blockchain.rs b/parity/secretstore/blockchain.rs new file mode 100644 index 000000000..6c0587538 --- /dev/null +++ b/parity/secretstore/blockchain.rs @@ -0,0 +1,246 @@ +// 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 . + +//! SecretStoreChain implementation with information about blockchain, retrieved from the client + +use std::sync::{Arc, Weak}; +use ethereum_types::{H256, Address}; +use parking_lot::RwLock; +use types::{ + ids::BlockId as EthcoreBlockId, + transaction::{Transaction, SignedTransaction, Action}, + chain_notify::NewBlocks, + tree_route::TreeRoute, + filter::Filter as BlockchainFilter, + log_entry::LocalizedLogEntry, +}; +use ethcore::client::Client; +use bytes::Bytes; +use ethabi::RawLog; +use client_traits::BlockChainClient; +use call_contract::CallContract; +use client_traits::{ChainInfo, Nonce, ChainNotify}; +use ethcore::miner::{Miner, MinerService}; +use parity_crypto::publickey::Error as EthKeyError; +use sync::SyncProvider; +use registrar::RegistrarClient; +use ethcore_secretstore::{BlockId, BlockNumber, SecretStoreChain, NewBlocksNotify, SigningKeyPair, ContractAddress, Filter}; + +// TODO: Instead of a constant, make this based on consensus finality. +/// Number of confirmations required before request can be processed. +const REQUEST_CONFIRMATIONS_REQUIRED: u64 = 3; + +fn into_ethcore_block_id(id: BlockId) -> EthcoreBlockId { + match id { + BlockId::Hash(hash) => EthcoreBlockId::Hash(hash), + BlockId::Number(number) => EthcoreBlockId::Number(number), + BlockId::Earliest => EthcoreBlockId::Earliest, + BlockId::Latest => EthcoreBlockId::Latest, + } +} + +/// SecretStore blockchain implementation (client's wrapper) +/// This implementation is trusted, when underlying client is synced and chain's security level is full +/// This trust is guaranteed by return result in get_trusted method (if it's not trusted, None is returned) +pub struct TrustedClient { + /// This key server node key pair. + self_key_pair: Arc, + /// Blockchain client. + client: Weak, + /// Sync provider. + sync: Weak, + /// Miner service. + miner: Weak, + /// Chain new blocks listeners + listeners: RwLock>>, +} + +impl TrustedClient { + /// Create new trusted client. + pub fn new(self_key_pair: Arc, client: Arc, sync: Arc, miner: Arc) -> Arc { + let trusted_client = Arc::new(TrustedClient { + self_key_pair, + client: Arc::downgrade(&client), + sync: Arc::downgrade(&sync), + miner: Arc::downgrade(&miner), + listeners: RwLock::default(), + }); + client.add_notify(trusted_client.clone()); + trusted_client + } + + fn notify_listeners(&self, new_enacted_len: usize) { + for listener_pointer in self.listeners.read().iter() { + if let Some(listener) = listener_pointer.upgrade() { + listener.new_blocks(new_enacted_len); + } + } + } + + /// Get 'trusted' `Client` reference only if it is synchronized && trusted. + fn get_trusted(&self) -> Option> { + self.client.upgrade() + .and_then(|client| self.sync.upgrade().map(|sync| (client, sync))) + .and_then(|(client, sync)| { + let is_synced = !sync.is_major_syncing(); + let is_trusted = client.chain_info().security_level().is_full(); + match is_synced && is_trusted { + true => Some(client), + false => None, + } + }) + } + + fn tree_route(&self, from: &H256, to: &H256) -> Option { + if let Some(client) = self.get_trusted() { + client.tree_route(from, to) + } else { + None + } + } + + fn logs(&self, filter: BlockchainFilter) -> Option> { + if let Some(client) = self.get_trusted() { + client.logs(filter).ok() + } else { + None + } + } + +} + +impl SecretStoreChain for TrustedClient { + fn add_listener(&self, target: Arc) { + self.listeners.write().push(Arc::downgrade(&target)); + } + + fn is_trusted(&self) -> bool { + self.get_trusted().is_some() + } + + fn transact_contract(&self, contract: Address, tx_data: Bytes) -> Result<(), EthKeyError> { + let client = self.client.upgrade().ok_or_else(|| EthKeyError::Custom("cannot submit tx when client is offline".into()))?; + let miner = self.miner.upgrade().ok_or_else(|| EthKeyError::Custom("cannot submit tx when miner is offline".into()))?; + let engine = client.engine(); + let transaction = Transaction { + nonce: client.latest_nonce(&self.self_key_pair.address()), + action: Action::Call(contract), + gas: miner.authoring_params().gas_range_target.0, + gas_price: miner.sensible_gas_price(), + value: Default::default(), + data: tx_data, + }; + let chain_id = engine.signing_chain_id(&client.latest_env_info()); + let signature = self.self_key_pair.sign(&transaction.hash(chain_id))?; + let signed = SignedTransaction::new(transaction.with_signature(signature, chain_id))?; + miner.import_own_transaction(&*client, signed.into()) + .map_err(|e| EthKeyError::Custom(format!("failed to import tx: {}", e))) + } + + fn read_contract_address( + &self, + registry_name: &str, + address: &ContractAddress + ) -> Option
{ + match *address { + ContractAddress::Address(ref address) => Some(address.clone()), + ContractAddress::Registry => self.get_trusted().and_then(|client| + self.get_confirmed_block_hash() + .and_then(|block| { + client.get_address(registry_name, EthcoreBlockId::Hash(block)) + .unwrap_or(None) + }) + ), + } + } + + fn call_contract(&self, block_id: BlockId, contract_address: Address, data: Bytes) -> Result { + if let Some(client) = self.get_trusted() { + client.call_contract(into_ethcore_block_id(block_id), contract_address, data) + } else { + Err("Calling ACL contract without trusted blockchain client".into()) + } + } + + fn block_hash(&self, id: BlockId) -> Option { + if let Some(client) = self.get_trusted() { + client.block_hash(into_ethcore_block_id(id)) + } else { + None + } + } + + fn block_number(&self, id: BlockId) -> Option { + if let Some(client) = self.get_trusted() { + client.block_number(into_ethcore_block_id(id)) + } else { + None + } + } + + fn retrieve_last_logs(&self, filter: Filter) -> Option> { + let confirmed_block = match self.get_confirmed_block_hash() { + Some(confirmed_block) => confirmed_block, + None => return None, // no block with enough confirmations + }; + + let from_block = self.block_hash(filter.from_block).unwrap_or_else(|| confirmed_block); + let first_block = match self.tree_route(&from_block, &confirmed_block) { + // if we have a route from last_log_block to confirmed_block => search for logs on this route + // + // potentially this could lead us to reading same logs twice when reorganizing to the fork, which + // already has been canonical previosuly + // the worst thing that can happen in this case is spending some time reading unneeded data from SS db + Some(ref route) if route.index < route.blocks.len() => route.blocks[route.index], + // else we care only about confirmed block + _ => confirmed_block.clone(), + }; + + self.logs(BlockchainFilter { + from_block: EthcoreBlockId::Hash(first_block), + to_block: EthcoreBlockId::Hash(confirmed_block), + address: filter.address, + topics: filter.topics, + limit: None, + }) + .map(|blockchain_logs| { + blockchain_logs + .into_iter() + .map(|log| { + let raw_log: RawLog = (log.entry.topics.into_iter().map(|t| t.0.into()).collect(), log.entry.data).into(); + raw_log + }) + .collect::>() + }) + } + + fn get_confirmed_block_hash(&self) -> Option { + self.block_number(BlockId::Latest) + .map(|b| b.saturating_sub(REQUEST_CONFIRMATIONS_REQUIRED)) + .and_then(|b| self.block_hash(BlockId::Number(b))) + } +} + +impl ChainNotify for TrustedClient { + fn new_blocks(&self, new_blocks: NewBlocks) { + if new_blocks.has_more_blocks_to_import { return } + if !new_blocks.route.enacted().is_empty() || !new_blocks.route.retracted().is_empty() { + let enacted_len = new_blocks.route.enacted().len(); + self.notify_listeners(enacted_len); + } + } +} + diff --git a/secret-store/src/helpers.rs b/parity/secretstore/mod.rs similarity index 55% rename from secret-store/src/helpers.rs rename to parity/secretstore/mod.rs index cdb235ac9..2b16965fa 100644 --- a/secret-store/src/helpers.rs +++ b/parity/secretstore/mod.rs @@ -14,18 +14,18 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use ethcore::client::Client; -use client_traits::BlockChainClient; -use common_types::ids::BlockId; -use ethereum_types::H256; +//! Secret store related components. -// TODO: Instead of a constant, make this based on consensus finality. -/// Number of confirmations required before request can be processed. -pub const REQUEST_CONFIRMATIONS_REQUIRED: u64 = 3; +mod server; -/// Get hash of the last block with at least n confirmations. -pub fn get_confirmed_block_hash(client: &Client, confirmations: u64) -> Option { - client.block_number(BlockId::Latest) - .map(|b| b.saturating_sub(confirmations)) - .and_then(|b| client.block_hash(BlockId::Number(b))) -} +#[cfg(feature = "secretstore")] +mod blockchain; + +#[cfg(all(feature = "accounts", feature = "secretstore"))] +mod nodekeypair; + +pub use self::server::{Configuration, NodeSecretKey, ContractAddress, Dependencies, start}; +#[cfg(feature = "secretstore")] +use self::blockchain::TrustedClient; +#[cfg(all(feature = "accounts", feature = "secretstore"))] +use self::nodekeypair::KeyStoreNodeKeyPair; \ No newline at end of file diff --git a/parity/secretstore/nodekeypair.rs b/parity/secretstore/nodekeypair.rs new file mode 100644 index 000000000..67ad3d1ec --- /dev/null +++ b/parity/secretstore/nodekeypair.rs @@ -0,0 +1,59 @@ +// 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 . + +//! Key pair with signing ability + +use std::sync::Arc; +use accounts::AccountProvider; +use ethkey::Password; +use parity_crypto::publickey::public_to_address; +use ethereum_types::{H256, Address, Public}; +use parity_crypto::publickey::{Signature, Error as EthKeyError}; +use ethcore_secretstore::SigningKeyPair; + +pub struct KeyStoreNodeKeyPair { + account_provider: Arc, + address: Address, + public: Public, + password: Password, +} + +impl KeyStoreNodeKeyPair { + pub fn new(account_provider: Arc, address: Address, password: Password) -> Result { + let public = account_provider.account_public(address.clone(), &password).map_err(|e| EthKeyError::Custom(format!("{}", e)))?; + Ok(KeyStoreNodeKeyPair { + account_provider, + address, + public, + password, + }) + } +} + +impl SigningKeyPair for KeyStoreNodeKeyPair { + fn public(&self) -> &Public { + &self.public + } + + fn address(&self) -> Address { + public_to_address(&self.public) + } + + fn sign(&self, data: &H256) -> Result { + self.account_provider.sign(self.address.clone(), Some(self.password.clone()), data.clone()) + .map_err(|e| EthKeyError::Custom(format!("{}", e))) + } +} diff --git a/parity/secretstore.rs b/parity/secretstore/server.rs similarity index 93% rename from parity/secretstore.rs rename to parity/secretstore/server.rs index 25af61e87..033f290e7 100644 --- a/parity/secretstore.rs +++ b/parity/secretstore/server.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . +//! Secret store server's launcher, contains required configuration parameters and launch method + use std::collections::BTreeMap; use std::sync::Arc; use account_utils::AccountProvider; @@ -125,6 +127,9 @@ mod server { use parity_crypto::publickey::KeyPair; use ansi_term::Colour::{Red, White}; use super::{Configuration, Dependencies, NodeSecretKey, ContractAddress, Executor}; + use super::super::TrustedClient; + #[cfg(feature = "accounts")] + use super::super::KeyStoreNodeKeyPair; fn into_service_contract_address(address: ContractAddress) -> ethcore_secretstore::ContractAddress { match address { @@ -141,7 +146,7 @@ mod server { impl KeyServer { /// Create new key server pub fn new(mut conf: Configuration, deps: Dependencies, executor: Executor) -> Result { - let self_secret: Arc = match conf.self_secret.take() { + let self_secret: Arc = match conf.self_secret.take() { Some(NodeSecretKey::Plain(secret)) => Arc::new(ethcore_secretstore::PlainNodeKeyPair::new( KeyPair::from_secret(secret).map_err(|e| format!("invalid secret: {}", e))?)), #[cfg(feature = "accounts")] @@ -160,7 +165,7 @@ mod server { let password = deps.accounts_passwords.iter() .find(|p| deps.account_provider.sign(account.clone(), Some((*p).clone()), Default::default()).is_ok()) .ok_or_else(|| format!("No valid password for the secret store node account {}", account))?; - Arc::new(ethcore_secretstore::KeyStoreNodeKeyPair::new(deps.account_provider, account, password.clone()) + Arc::new(KeyStoreNodeKeyPair::new(deps.account_provider, account, password.clone()) .map_err(|e| format!("{}", e))?) }, None => return Err("self secret is required when using secretstore".into()), @@ -203,7 +208,8 @@ mod server { cconf.cluster_config.nodes.insert(self_secret.public().clone(), cconf.cluster_config.listener_address.clone()); let db = ethcore_secretstore::open_secretstore_db(&conf.data_path)?; - let key_server = ethcore_secretstore::start(deps.client, deps.sync, deps.miner, self_secret, cconf, db, executor) + let trusted_client = TrustedClient::new(self_secret.clone(), deps.client, deps.sync, deps.miner); + let key_server = ethcore_secretstore::start(trusted_client, self_secret, cconf, db, executor) .map_err(|e| format!("Error starting KeyServer {}: {}", key_server_name, e))?; Ok(KeyServer { diff --git a/secret-store/Cargo.toml b/secret-store/Cargo.toml index 11cad8ba2..609e5ff62 100644 --- a/secret-store/Cargo.toml +++ b/secret-store/Cargo.toml @@ -7,15 +7,9 @@ authors = ["Parity Technologies "] [dependencies] byteorder = "1.0" -client-traits = { path = "../ethcore/client-traits" } -common-types = { path = "../ethcore/types" } ethabi = "9.0.1" ethabi-contract = "9.0.0" ethabi-derive = "9.0.1" -ethcore = { path = "../ethcore" } -ethcore-accounts = { path = "../accounts", optional = true} -ethcore-call-contract = { path = "../ethcore/call-contract" } -ethcore-sync = { path = "../ethcore/sync" } ethereum-types = "0.8.0" ethkey = { path = "../accounts/ethkey", optional = true } futures = "0.1" @@ -30,7 +24,6 @@ parity-crypto = { version = "0.4.2", features = ["publickey"] } parity-runtime = { path = "../util/runtime" } parking_lot = "0.9" percent-encoding = "2.1.0" -registrar = { path = "../util/registrar" } rustc-hex = "1.0" serde = "1.0" serde_derive = "1.0" @@ -44,10 +37,5 @@ jsonrpc-server-utils = "14.0.3" [dev-dependencies] env_logger = "0.5" -ethkey = { path = "../accounts/ethkey" } -ethcore = { path = "../ethcore", features = ["test-helpers"] } tempdir = "0.3" kvdb-rocksdb = "0.3.0" - -[features] -accounts = ["ethcore-accounts", "ethkey"] diff --git a/secret-store/src/acl_storage.rs b/secret-store/src/acl_storage.rs index d3f8bddde..0da50e310 100644 --- a/secret-store/src/acl_storage.rs +++ b/secret-store/src/acl_storage.rs @@ -16,17 +16,11 @@ use std::sync::Arc; use std::collections::{HashMap, HashSet}; -use common_types::{ - chain_notify::NewBlocks, - ids::BlockId -}; use parking_lot::{Mutex, RwLock}; -use call_contract::CallContract; -use client_traits::ChainNotify; use ethereum_types::Address; use ethabi::FunctionOutputDecoder; -use trusted_client::TrustedClient; -use types::{Error, ServerKeyId, ContractAddress}; +use blockchain::{SecretStoreChain, NewBlocksNotify, ContractAddress, BlockId}; +use types::{Error, ServerKeyId}; use_contract!(acl_storage, "res/acl_storage.json"); @@ -47,7 +41,7 @@ pub struct OnChainAclStorage { /// Cached on-chain ACL storage contract. struct CachedContract { /// Blockchain client. - client: TrustedClient, + client: Arc, /// Contract address source. address_source: ContractAddress, /// Current contract address. @@ -61,14 +55,11 @@ pub struct DummyAclStorage { } impl OnChainAclStorage { - pub fn new(trusted_client: TrustedClient, address_source: ContractAddress) -> Result, Error> { - let client = trusted_client.get_untrusted(); + pub fn new(trusted_client: Arc, address_source: ContractAddress) -> Result, Error> { let acl_storage = Arc::new(OnChainAclStorage { - contract: Mutex::new(CachedContract::new(trusted_client, address_source)), + contract: Mutex::new(CachedContract::new(trusted_client.clone(), address_source)), }); - client - .ok_or_else(|| Error::Internal("Constructing OnChainAclStorage without active Client".into()))? - .add_notify(acl_storage.clone()); + trusted_client.add_listener(acl_storage.clone()); Ok(acl_storage) } } @@ -79,17 +70,14 @@ impl AclStorage for OnChainAclStorage { } } -impl ChainNotify for OnChainAclStorage { - fn new_blocks(&self, new_blocks: NewBlocks) { - if new_blocks.has_more_blocks_to_import { return } - if !new_blocks.route.enacted().is_empty() || !new_blocks.route.retracted().is_empty() { - self.contract.lock().update_contract_address() - } +impl NewBlocksNotify for OnChainAclStorage { + fn new_blocks(&self, _new_enacted_len: usize) { + self.contract.lock().update_contract_address() } } impl CachedContract { - pub fn new(client: TrustedClient, address_source: ContractAddress) -> Self { + pub fn new(client: Arc, address_source: ContractAddress) -> Self { let mut contract = CachedContract { client, address_source, @@ -113,12 +101,12 @@ impl CachedContract { } pub fn check(&mut self, requester: Address, document: &ServerKeyId) -> Result { - if let Some(client) = self.client.get() { + if self.client.is_trusted() { // call contract to check accesss match self.contract_address { Some(contract_address) => { let (encoded, decoder) = acl_storage::functions::check_permissions::call(requester, document.clone()); - let d = client.call_contract(BlockId::Latest, contract_address, encoded) + let d = self.client.call_contract(BlockId::Latest, contract_address, encoded) .map_err(|e| Error::Internal(format!("ACL checker call error: {}", e.to_string())))?; decoder.decode(&d) .map_err(|e| Error::Internal(format!("ACL checker call error: {}", e.to_string()))) diff --git a/secret-store/src/blockchain.rs b/secret-store/src/blockchain.rs new file mode 100644 index 000000000..d2ad28d7e --- /dev/null +++ b/secret-store/src/blockchain.rs @@ -0,0 +1,119 @@ +// 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 bytes::Bytes; +use ethereum_types::{H256, Address, Public}; +use ethabi::RawLog; +use crypto::publickey::{Signature, Error as EthKeyError}; + +/// Type for block number. +/// Duplicated from ethcore types +pub type BlockNumber = u64; + +/// Uniquely identifies block. +/// Duplicated from ethcore types +#[derive(Debug, PartialEq, Copy, Clone, Hash, Eq)] +pub enum BlockId { + /// Block's sha3. + /// Querying by hash is always faster. + Hash(H256), + /// Block number within canon blockchain. + Number(BlockNumber), + /// Earliest block (genesis). + Earliest, + /// Latest mined block. + Latest, +} + +/// Contract address. +#[derive(Debug, Clone)] +pub enum ContractAddress { + /// Address is read from registry. + Registry, + /// Address is specified. + Address(ethereum_types::Address), +} + +/// Key pair with signing ability. +pub trait SigningKeyPair: Send + Sync { + /// Public portion of key. + fn public(&self) -> &Public; + /// Address of key owner. + fn address(&self) -> Address; + /// Sign data with the key. + fn sign(&self, data: &H256) -> Result; +} + +/// Wrapps client ChainNotify in order to send signal about new blocks +pub trait NewBlocksNotify: Send + Sync { + /// Fires when chain has new blocks. + /// Sends this signal only, if contracts' update required + fn new_blocks(&self, _new_enacted_len: usize) { + // does nothing by default + } +} + +/// Blockchain logs Filter. +#[derive(Debug, PartialEq)] +pub struct Filter { + /// Blockchain will be searched from this block. + pub from_block: BlockId, + + /// Search addresses. + /// + /// If None, match all. + /// If specified, log must be produced by one of these addresses. + pub address: Option>, + + /// Search topics. + /// + /// If None, match all. + /// If specified, log must contain one of these topics. + pub topics: Vec>>, +} + +/// Blockchain representation for Secret Store +pub trait SecretStoreChain: Send + Sync + 'static { + /// Adds listener for chain's NewBlocks event + fn add_listener(&self, target: Arc); + + /// Check if the underlying chain is in the trusted state + fn is_trusted(&self) -> bool; + + /// Transact contract. + fn transact_contract(&self, contract: Address, tx_data: Bytes) -> Result<(), EthKeyError>; + + /// Read contract address. If address source is registry, address only returned if current client state is + /// trusted. Address from registry is read from registry from block latest block with + /// REQUEST_CONFIRMATIONS_REQUIRED confirmations. + fn read_contract_address(&self, registry_name: &str, address: &ContractAddress) -> Option
; + + /// Call contract in the blockchain + fn call_contract(&self, block_id: BlockId, contract_address: Address, data: Bytes) -> Result; + + /// Returns blockhash for block id + fn block_hash(&self, id: BlockId) -> Option; + + /// Returns block number for block id + fn block_number(&self, id: BlockId) -> Option; + + /// Retrieve last blockchain logs for the filter + fn retrieve_last_logs(&self, filter: Filter) -> Option>; + + /// Get hash of the last block with predefined number of confirmations (depends on the chain). + fn get_confirmed_block_hash(&self) -> Option; +} diff --git a/secret-store/src/key_server.rs b/secret-store/src/key_server.rs index bbb45b5af..6cb901dd8 100644 --- a/secret-store/src/key_server.rs +++ b/secret-store/src/key_server.rs @@ -24,8 +24,9 @@ use parity_runtime::Executor; use super::acl_storage::AclStorage; use super::key_storage::KeyStorage; use super::key_server_set::KeyServerSet; +use blockchain::SigningKeyPair; use key_server_cluster::{math, new_network_cluster, ClusterSession, WaitableSession}; -use traits::{AdminSessionsServer, ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer, NodeKeyPair}; +use traits::{AdminSessionsServer, ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer}; use types::{Error, Public, RequestSignature, Requester, ServerKeyId, EncryptedDocumentKey, EncryptedDocumentKeyShadow, ClusterConfiguration, MessageHash, EncryptedMessageSignature, NodeId}; use key_server_cluster::{ClusterClient, ClusterConfiguration as NetClusterConfiguration, NetConnectionsManagerConfig}; @@ -42,7 +43,7 @@ pub struct KeyServerCore { impl KeyServerImpl { /// Create new key server instance - pub fn new(config: &ClusterConfiguration, key_server_set: Arc, self_key_pair: Arc, + pub fn new(config: &ClusterConfiguration, key_server_set: Arc, self_key_pair: Arc, acl_storage: Arc, key_storage: Arc, executor: Executor) -> Result { Ok(KeyServerImpl { @@ -269,7 +270,7 @@ impl MessageSigner for KeyServerImpl { } impl KeyServerCore { - pub fn new(config: &ClusterConfiguration, key_server_set: Arc, self_key_pair: Arc, + pub fn new(config: &ClusterConfiguration, key_server_set: Arc, self_key_pair: Arc, acl_storage: Arc, key_storage: Arc, executor: Executor) -> Result { let cconfig = NetClusterConfiguration { diff --git a/secret-store/src/key_server_cluster/admin_sessions/servers_set_change_session.rs b/secret-store/src/key_server_cluster/admin_sessions/servers_set_change_session.rs index 9bc6d1df8..0d35457de 100644 --- a/secret-store/src/key_server_cluster/admin_sessions/servers_set_change_session.rs +++ b/secret-store/src/key_server_cluster/admin_sessions/servers_set_change_session.rs @@ -1051,7 +1051,8 @@ pub mod tests { use std::collections::{VecDeque, BTreeMap, BTreeSet}; use ethereum_types::H256; use crypto::publickey::{Random, Generator, Public, Signature, KeyPair, sign}; - use key_server_cluster::{NodeId, SessionId, Error, KeyStorage, NodeKeyPair, PlainNodeKeyPair}; + use blockchain::SigningKeyPair; + use key_server_cluster::{NodeId, SessionId, Error, KeyStorage, PlainNodeKeyPair}; use key_server_cluster::cluster_sessions::ClusterSession; use key_server_cluster::cluster::tests::MessageLoop as ClusterMessageLoop; use key_server_cluster::generation_session::tests::{MessageLoop as GenerationMessageLoop}; diff --git a/secret-store/src/key_server_cluster/admin_sessions/share_add_session.rs b/secret-store/src/key_server_cluster/admin_sessions/share_add_session.rs index 94e6e9893..b6d1d5a2e 100644 --- a/secret-store/src/key_server_cluster/admin_sessions/share_add_session.rs +++ b/secret-store/src/key_server_cluster/admin_sessions/share_add_session.rs @@ -889,7 +889,8 @@ impl SessionTransport for IsolatedSessionTransport { pub mod tests { use std::collections::BTreeSet; use crypto::publickey::{Random, Generator, Public}; - use key_server_cluster::{NodeId, Error, KeyStorage, NodeKeyPair}; + use blockchain::SigningKeyPair; + use key_server_cluster::{NodeId, Error, KeyStorage}; use key_server_cluster::cluster::tests::MessageLoop as ClusterMessageLoop; use key_server_cluster::servers_set_change_session::tests::{MessageLoop, AdminSessionAdapter, generate_key}; use key_server_cluster::admin_sessions::ShareChangeSessionMeta; diff --git a/secret-store/src/key_server_cluster/cluster.rs b/secret-store/src/key_server_cluster/cluster.rs index 44e6a22fe..741d69635 100644 --- a/secret-store/src/key_server_cluster/cluster.rs +++ b/secret-store/src/key_server_cluster/cluster.rs @@ -20,7 +20,8 @@ use parking_lot::RwLock; use crypto::publickey::{Public, Signature, Random, Generator}; use ethereum_types::{Address, H256}; use parity_runtime::Executor; -use key_server_cluster::{Error, NodeId, SessionId, Requester, AclStorage, KeyStorage, KeyServerSet, NodeKeyPair}; +use blockchain::SigningKeyPair; +use key_server_cluster::{Error, NodeId, SessionId, Requester, AclStorage, KeyStorage, KeyServerSet}; use key_server_cluster::cluster_sessions::{WaitableSession, ClusterSession, AdminSession, ClusterSessions, SessionIdWithSubSession, ClusterSessionsContainer, SERVERS_SET_CHANGE_SESSION_ID, create_cluster_view, AdminSessionCreationData, ClusterSessionsListener}; @@ -143,7 +144,7 @@ pub trait Cluster: Send + Sync { #[derive(Clone)] pub struct ClusterConfiguration { /// KeyPair this node holds. - pub self_key_pair: Arc, + pub self_key_pair: Arc, /// Cluster nodes set. pub key_server_set: Arc, /// Reference to key storage @@ -173,7 +174,7 @@ pub struct ClusterView { configured_nodes_count: usize, connected_nodes: BTreeSet, connections: Arc, - self_key_pair: Arc, + self_key_pair: Arc, } /// Cross-thread shareable cluster data. @@ -181,7 +182,7 @@ pub struct ClusterData { /// Cluster configuration. pub config: ClusterConfiguration, /// KeyPair this node holds. - pub self_key_pair: Arc, + pub self_key_pair: Arc, /// Connections data. pub connections: C, /// Active sessions data. @@ -311,7 +312,7 @@ impl ClusterCore { impl ClusterView { pub fn new( - self_key_pair: Arc, + self_key_pair: Arc, connections: Arc, nodes: BTreeSet, configured_nodes_count: usize @@ -597,7 +598,7 @@ pub struct ServersSetChangeParams { } pub fn new_servers_set_change_session( - self_key_pair: Arc, + self_key_pair: Arc, sessions: &ClusterSessions, connections: Arc, servers_set_change_creator_connector: Arc, @@ -656,8 +657,9 @@ pub mod tests { use parking_lot::{Mutex, RwLock}; use ethereum_types::{Address, H256}; use crypto::publickey::{Random, Generator, Public, Signature, sign}; + use blockchain::SigningKeyPair; use key_server_cluster::{NodeId, SessionId, Requester, Error, DummyAclStorage, DummyKeyStorage, - MapKeyServerSet, PlainNodeKeyPair, NodeKeyPair}; + MapKeyServerSet, PlainNodeKeyPair}; use key_server_cluster::message::Message; use key_server_cluster::cluster::{new_test_cluster, Cluster, ClusterCore, ClusterConfiguration, ClusterClient}; use key_server_cluster::cluster_connections::ConnectionManager; diff --git a/secret-store/src/key_server_cluster/cluster_connections_net.rs b/secret-store/src/key_server_cluster/cluster_connections_net.rs index b402d0018..dcd7daeed 100644 --- a/secret-store/src/key_server_cluster/cluster_connections_net.rs +++ b/secret-store/src/key_server_cluster/cluster_connections_net.rs @@ -27,7 +27,8 @@ use tokio::timer::{Interval, timeout::Error as TimeoutError}; use tokio_io::IoFuture; use crypto::publickey::KeyPair; use parity_runtime::Executor; -use key_server_cluster::{Error, NodeId, ClusterConfiguration, NodeKeyPair}; +use blockchain::SigningKeyPair; +use key_server_cluster::{Error, NodeId, ClusterConfiguration}; use key_server_cluster::cluster_connections::{ConnectionProvider, Connection, ConnectionManager}; use key_server_cluster::connection_trigger::{Maintain, ConnectionTrigger}; use key_server_cluster::cluster_message_processor::MessageProcessor; @@ -79,7 +80,7 @@ struct NetConnectionsData { /// Reference to tokio task executor. executor: Executor, /// Key pair of this node. - self_key_pair: Arc, + self_key_pair: Arc, /// Network messages processor. message_processor: Arc, /// Connections trigger. diff --git a/secret-store/src/key_server_cluster/cluster_message_processor.rs b/secret-store/src/key_server_cluster/cluster_message_processor.rs index d0ab712b6..ac21e7602 100644 --- a/secret-store/src/key_server_cluster/cluster_message_processor.rs +++ b/secret-store/src/key_server_cluster/cluster_message_processor.rs @@ -15,7 +15,8 @@ // along with Parity. If not, see . use std::sync::Arc; -use key_server_cluster::{Error, NodeId, NodeKeyPair}; +use blockchain::SigningKeyPair; +use key_server_cluster::{Error, NodeId}; use key_server_cluster::cluster::{ServersSetChangeParams, new_servers_set_change_session}; use key_server_cluster::cluster_sessions::{AdminSession}; use key_server_cluster::cluster_connections::{ConnectionProvider, Connection}; @@ -49,7 +50,7 @@ pub trait MessageProcessor: Send + Sync { /// Bridge between ConnectionManager and ClusterSessions. pub struct SessionsMessageProcessor { - self_key_pair: Arc, + self_key_pair: Arc, servers_set_change_creator_connector: Arc, sessions: Arc, connections: Arc, @@ -58,7 +59,7 @@ pub struct SessionsMessageProcessor { impl SessionsMessageProcessor { /// Create new instance of SessionsMessageProcessor. pub fn new( - self_key_pair: Arc, + self_key_pair: Arc, servers_set_change_creator_connector: Arc, sessions: Arc, connections: Arc, diff --git a/secret-store/src/key_server_cluster/cluster_sessions.rs b/secret-store/src/key_server_cluster/cluster_sessions.rs index 1e5a046b9..1e2510a36 100644 --- a/secret-store/src/key_server_cluster/cluster_sessions.rs +++ b/secret-store/src/key_server_cluster/cluster_sessions.rs @@ -22,7 +22,8 @@ use futures::{oneshot, Oneshot, Complete, Future}; use parking_lot::{Mutex, RwLock, Condvar}; use ethereum_types::H256; use crypto::publickey::Secret; -use key_server_cluster::{Error, NodeId, SessionId, NodeKeyPair}; +use blockchain::SigningKeyPair; +use key_server_cluster::{Error, NodeId, SessionId}; use key_server_cluster::cluster::{Cluster, ClusterConfiguration, ClusterView}; use key_server_cluster::cluster_connections::ConnectionProvider; use key_server_cluster::connection_trigger::ServersSetChangeSessionCreatorConnector; @@ -647,7 +648,7 @@ impl CompletionSignal { } } -pub fn create_cluster_view(self_key_pair: Arc, connections: Arc, requires_all_connections: bool) -> Result, Error> { +pub fn create_cluster_view(self_key_pair: Arc, connections: Arc, requires_all_connections: bool) -> Result, Error> { let mut connected_nodes = connections.connected_nodes()?; let disconnected_nodes = connections.disconnected_nodes(); diff --git a/secret-store/src/key_server_cluster/connection_trigger.rs b/secret-store/src/key_server_cluster/connection_trigger.rs index 40d415dff..34578946f 100644 --- a/secret-store/src/key_server_cluster/connection_trigger.rs +++ b/secret-store/src/key_server_cluster/connection_trigger.rs @@ -26,7 +26,7 @@ use key_server_cluster::cluster_sessions::AdminSession; use key_server_cluster::cluster_connections::{Connection}; use key_server_cluster::cluster_connections_net::{NetConnectionsContainer}; use types::{Error, NodeId}; -use NodeKeyPair; +use blockchain::SigningKeyPair; #[derive(Debug, Clone, Copy, PartialEq)] /// Describes which maintain() call is required. @@ -93,7 +93,7 @@ pub enum ConnectionsAction { /// Trigger connections. pub struct TriggerConnections { /// This node key pair. - pub self_key_pair: Arc, + pub self_key_pair: Arc, } impl SimpleConnectionTrigger { @@ -103,7 +103,7 @@ impl SimpleConnectionTrigger { } /// Create new simple connection trigger. - pub fn new(key_server_set: Arc, self_key_pair: Arc, admin_public: Option) -> Self { + pub fn new(key_server_set: Arc, self_key_pair: Arc, admin_public: Option) -> Self { SimpleConnectionTrigger { key_server_set: key_server_set, connections: TriggerConnections { diff --git a/secret-store/src/key_server_cluster/connection_trigger_with_migration.rs b/secret-store/src/key_server_cluster/connection_trigger_with_migration.rs index 92db652d5..dc6d112c9 100644 --- a/secret-store/src/key_server_cluster/connection_trigger_with_migration.rs +++ b/secret-store/src/key_server_cluster/connection_trigger_with_migration.rs @@ -28,12 +28,12 @@ use key_server_cluster::jobs::servers_set_change_access_job::ordered_nodes_hash; use key_server_cluster::connection_trigger::{Maintain, ConnectionsAction, ConnectionTrigger, ServersSetChangeSessionCreatorConnector, TriggerConnections}; use types::{Error, NodeId}; -use {NodeKeyPair}; +use blockchain::SigningKeyPair; /// Key servers set change trigger with automated migration procedure. pub struct ConnectionTriggerWithMigration { /// This node key pair. - self_key_pair: Arc, + self_key_pair: Arc, /// Key server set. key_server_set: Arc, /// Last server set state. @@ -105,7 +105,7 @@ struct TriggerSession { /// Servers set change session creator connector. connector: Arc, /// This node key pair. - self_key_pair: Arc, + self_key_pair: Arc, /// Key server set. key_server_set: Arc, } @@ -117,7 +117,7 @@ impl ConnectionTriggerWithMigration { } /// Create new trigge with migration. - pub fn new(key_server_set: Arc, self_key_pair: Arc) -> Self { + pub fn new(key_server_set: Arc, self_key_pair: Arc) -> Self { let snapshot = key_server_set.snapshot(); let migration = snapshot.migration.clone(); diff --git a/secret-store/src/key_server_cluster/io/handshake.rs b/secret-store/src/key_server_cluster/io/handshake.rs index a09e035e4..e8febe2e9 100644 --- a/secret-store/src/key_server_cluster/io/handshake.rs +++ b/secret-store/src/key_server_cluster/io/handshake.rs @@ -40,20 +40,21 @@ use tokio_io::{AsyncRead, AsyncWrite}; use crypto::publickey::ecdh::agree; use crypto::publickey::{Random, Generator, KeyPair, Public, Signature, verify_public, sign, recover}; use ethereum_types::H256; -use key_server_cluster::{NodeId, Error, NodeKeyPair}; +use blockchain::SigningKeyPair; +use key_server_cluster::{NodeId, Error}; use key_server_cluster::message::{Message, ClusterMessage, NodePublicKey, NodePrivateKeySignature}; use key_server_cluster::io::{write_message, write_encrypted_message, WriteMessage, ReadMessage, read_message, read_encrypted_message, fix_shared_key}; /// Start handshake procedure with another node from the cluster. -pub fn handshake(a: A, self_key_pair: Arc, trusted_nodes: BTreeSet) -> Handshake where A: AsyncWrite + AsyncRead { +pub fn handshake(a: A, self_key_pair: Arc, trusted_nodes: BTreeSet) -> Handshake where A: AsyncWrite + AsyncRead { let init_data = Random.generate().map(|kp| *kp.secret().clone()).map_err(Into::into) .and_then(|cp| Random.generate().map(|kp| (cp, kp)).map_err(Into::into)); handshake_with_init_data(a, init_data, self_key_pair, trusted_nodes) } /// Start handshake procedure with another node from the cluster and given plain confirmation + session key pair. -pub fn handshake_with_init_data(a: A, init_data: Result<(H256, KeyPair), Error>, self_key_pair: Arc, trusted_nodes: BTreeSet) -> Handshake where A: AsyncWrite + AsyncRead { +pub fn handshake_with_init_data(a: A, init_data: Result<(H256, KeyPair), Error>, self_key_pair: Arc, trusted_nodes: BTreeSet) -> Handshake where A: AsyncWrite + AsyncRead { let handshake_input_data = init_data .and_then(|(cp, kp)| sign(kp.secret(), &cp).map(|sp| (cp, kp, sp)).map_err(Into::into)) .and_then(|(cp, kp, sp)| Handshake::::make_public_key_message(self_key_pair.public().clone(), cp.clone(), sp).map(|msg| (cp, kp, msg))); @@ -79,7 +80,7 @@ pub fn handshake_with_init_data(a: A, init_data: Result<(H256, KeyPair), Erro } /// Wait for handshake procedure to be started by another node from the cluster. -pub fn accept_handshake(a: A, self_key_pair: Arc) -> Handshake where A: AsyncWrite + AsyncRead { +pub fn accept_handshake(a: A, self_key_pair: Arc) -> Handshake where A: AsyncWrite + AsyncRead { let self_confirmation_plain = Random.generate().map(|kp| *kp.secret().clone()).map_err(Into::into); let handshake_input_data = self_confirmation_plain .and_then(|cp| Random.generate().map(|kp| (cp, kp)).map_err(Into::into)); @@ -118,7 +119,7 @@ pub struct Handshake { is_active: bool, error: Option<(A, Result)>, state: HandshakeState, - self_key_pair: Arc, + self_key_pair: Arc, self_session_key_pair: Option, self_confirmation_plain: H256, trusted_nodes: Option>, @@ -156,7 +157,7 @@ impl Handshake where A: AsyncRead + AsyncWrite { }))) } - fn make_private_key_signature_message(self_key_pair: &dyn NodeKeyPair, confirmation_plain: &H256) -> Result { + fn make_private_key_signature_message(self_key_pair: &dyn SigningKeyPair, confirmation_plain: &H256) -> Result { Ok(Message::Cluster(ClusterMessage::NodePrivateKeySignature(NodePrivateKeySignature { confirmation_signed: self_key_pair.sign(confirmation_plain)?.into(), }))) diff --git a/secret-store/src/key_server_cluster/mod.rs b/secret-store/src/key_server_cluster/mod.rs index c1c91ef8a..5286c323f 100644 --- a/secret-store/src/key_server_cluster/mod.rs +++ b/secret-store/src/key_server_cluster/mod.rs @@ -16,7 +16,7 @@ use super::types::ServerKeyId; -pub use super::traits::NodeKeyPair; +pub use super::blockchain::SigningKeyPair; pub use super::types::{Error, NodeId, Requester, EncryptedDocumentKeyShadow}; pub use super::acl_storage::AclStorage; pub use super::key_storage::{KeyStorage, DocumentKeyShare, DocumentKeyShareVersion}; diff --git a/secret-store/src/key_server_cluster/net/accept_connection.rs b/secret-store/src/key_server_cluster/net/accept_connection.rs index 88ee9b3d5..ea3452c6e 100644 --- a/secret-store/src/key_server_cluster/net/accept_connection.rs +++ b/secret-store/src/key_server_cluster/net/accept_connection.rs @@ -20,12 +20,13 @@ use std::net::SocketAddr; use std::time::Duration; use futures::{Future, Poll}; use tokio::net::TcpStream; -use key_server_cluster::{Error, NodeKeyPair}; +use blockchain::SigningKeyPair; +use key_server_cluster::Error; use key_server_cluster::io::{accept_handshake, Handshake, Deadline, deadline}; use key_server_cluster::net::Connection; /// Create future for accepting incoming connection. -pub fn accept_connection(stream: TcpStream, self_key_pair: Arc) -> Deadline { +pub fn accept_connection(stream: TcpStream, self_key_pair: Arc) -> Deadline { // TODO: This could fail so it would be better either to accept the // address as a separate argument or return a result. let address = stream.peer_addr().expect("Unable to determine tcp peer address"); diff --git a/secret-store/src/key_server_cluster/net/connect.rs b/secret-store/src/key_server_cluster/net/connect.rs index c5be9be0e..448b78243 100644 --- a/secret-store/src/key_server_cluster/net/connect.rs +++ b/secret-store/src/key_server_cluster/net/connect.rs @@ -21,12 +21,13 @@ use std::time::Duration; use std::net::SocketAddr; use futures::{Future, Poll, Async}; use tokio::net::{TcpStream, tcp::ConnectFuture}; -use key_server_cluster::{Error, NodeId, NodeKeyPair}; +use blockchain::SigningKeyPair; +use key_server_cluster::{Error, NodeId}; use key_server_cluster::io::{handshake, Handshake, Deadline, deadline}; use key_server_cluster::net::Connection; /// Create future for connecting to other node. -pub fn connect(address: &SocketAddr, self_key_pair: Arc, trusted_nodes: BTreeSet) -> Deadline { +pub fn connect(address: &SocketAddr, self_key_pair: Arc, trusted_nodes: BTreeSet) -> Deadline { let connect = Connect { state: ConnectState::TcpConnect(TcpStream::connect(address)), address: address.clone(), @@ -47,7 +48,7 @@ enum ConnectState { pub struct Connect { state: ConnectState, address: SocketAddr, - self_key_pair: Arc, + self_key_pair: Arc, trusted_nodes: BTreeSet, } diff --git a/secret-store/src/key_server_set.rs b/secret-store/src/key_server_set.rs index 4b09e69ca..a558fbeec 100644 --- a/secret-store/src/key_server_set.rs +++ b/secret-store/src/key_server_set.rs @@ -18,20 +18,12 @@ use std::sync::Arc; use std::net::SocketAddr; use std::collections::{BTreeMap, HashSet}; use parking_lot::Mutex; -use call_contract::CallContract; use ethabi::FunctionOutputDecoder; -use ethcore::client::Client; -use client_traits::{BlockChainClient, ChainNotify}; -use common_types::{ - chain_notify::NewBlocks, - ids::BlockId, -}; use ethereum_types::{H256, Address}; use crypto::publickey::public_to_address; use bytes::Bytes; use types::{Error, Public, NodeAddress, NodeId}; -use trusted_client::TrustedClient; -use {NodeKeyPair, ContractAddress}; +use blockchain::{SecretStoreChain, NewBlocksNotify, SigningKeyPair, ContractAddress, BlockId}; use_contract!(key_server, "res/key_server_set.json"); @@ -105,7 +97,7 @@ struct PreviousMigrationTransaction { /// Cached on-chain Key Server set contract. struct CachedContract { /// Blockchain client. - client: TrustedClient, + client: Arc, /// Contract address source. contract_address_source: Option, /// Current contract address. @@ -121,18 +113,15 @@ struct CachedContract { /// Previous confirm migration transaction. confirm_migration_tx: Option, /// This node key pair. - self_key_pair: Arc, + self_key_pair: Arc, } impl OnChainKeyServerSet { - pub fn new(trusted_client: TrustedClient, contract_address_source: Option, self_key_pair: Arc, auto_migrate_enabled: bool, key_servers: BTreeMap) -> Result, Error> { - let client = trusted_client.get_untrusted(); + pub fn new(trusted_client: Arc, contract_address_source: Option, self_key_pair: Arc, auto_migrate_enabled: bool, key_servers: BTreeMap) -> Result, Error> { let key_server_set = Arc::new(OnChainKeyServerSet { - contract: Mutex::new(CachedContract::new(trusted_client, contract_address_source, self_key_pair, auto_migrate_enabled, key_servers)?), + contract: Mutex::new(CachedContract::new(trusted_client.clone(), contract_address_source, self_key_pair, auto_migrate_enabled, key_servers)?), }); - client - .ok_or_else(|| Error::Internal("Constructing OnChainKeyServerSet without active Client".into()))? - .add_notify(key_server_set.clone()); + trusted_client.add_listener(key_server_set.clone()); Ok(key_server_set) } } @@ -155,14 +144,9 @@ impl KeyServerSet for OnChainKeyServerSet { } } -impl ChainNotify for OnChainKeyServerSet { - fn new_blocks(&self, new_blocks: NewBlocks) { - if new_blocks.has_more_blocks_to_import { return } - let (enacted, retracted) = new_blocks.route.into_enacted_retracted(); - - if !enacted.is_empty() || !retracted.is_empty() { - self.contract.lock().update(enacted, retracted) - } +impl NewBlocksNotify for OnChainKeyServerSet { + fn new_blocks(&self, _new_enacted_len: usize) { + self.contract.lock().update() } } @@ -232,7 +216,7 @@ impl ) -> Result, String>> KeyServerSubset for NewKeySe } impl CachedContract { - pub fn new(client: TrustedClient, contract_address_source: Option, self_key_pair: Arc, auto_migrate_enabled: bool, key_servers: BTreeMap) -> Result { + pub fn new(client: Arc, contract_address_source: Option, self_key_pair: Arc, auto_migrate_enabled: bool, key_servers: BTreeMap) -> Result { let server_set = match contract_address_source.is_none() { true => key_servers.into_iter() .map(|(p, addr)| { @@ -279,21 +263,19 @@ impl CachedContract { } } - pub fn update(&mut self, enacted: Vec, retracted: Vec) { + pub fn update(&mut self) { // no need to update when servers set is hardcoded if self.contract_address_source.is_none() { return; } - if let Some(client) = self.client.get() { - // read new snapshot from reqistry (if something has chnaged) - if !enacted.is_empty() || !retracted.is_empty() { - self.update_contract_address(); - self.read_from_registry(&*client); - } + if self.client.is_trusted() { + // read new snapshot from reqistry + self.update_contract_address(); + self.read_from_registry(); // update number of confirmations (if there's future new set) - self.update_number_of_confirmations_if_required(&*client); + self.update_number_of_confirmations_if_required(); } } @@ -307,9 +289,9 @@ impl CachedContract { fn start_migration(&mut self, migration_id: H256) { // trust is not needed here, because it is the reaction to the read of the trusted client - if let (Some(client), Some(contract_address)) = (self.client.get_untrusted(), self.contract_address.as_ref()) { + if let Some(contract_address) = self.contract_address.as_ref() { // check if we need to send start migration transaction - if !update_last_transaction_block(&*client, &migration_id, &mut self.start_migration_tx) { + if !update_last_transaction_block(&*self.client, &migration_id, &mut self.start_migration_tx) { return; } @@ -328,9 +310,9 @@ impl CachedContract { fn confirm_migration(&mut self, migration_id: H256) { // trust is not needed here, because we have already completed the action - if let (Some(client), Some(contract_address)) = (self.client.get(), self.contract_address) { + if let (true, Some(contract_address)) = (self.client.is_trusted(), self.contract_address) { // check if we need to send start migration transaction - if !update_last_transaction_block(&*client, &migration_id, &mut self.confirm_migration_tx) { + if !update_last_transaction_block(&*self.client, &migration_id, &mut self.confirm_migration_tx) { return; } @@ -347,7 +329,7 @@ impl CachedContract { } } - fn read_from_registry(&mut self, client: &Client) { + fn read_from_registry(&mut self) { let contract_address = match self.contract_address { Some(contract_address) => contract_address, None => { @@ -361,7 +343,7 @@ impl CachedContract { }, }; - let do_call = |data| client.call_contract(BlockId::Latest, contract_address, data); + let do_call = |data| self.client.call_contract(BlockId::Latest, contract_address, data); let current_set = Self::read_key_server_set(CurrentKeyServerSubset, &do_call); @@ -434,7 +416,7 @@ impl CachedContract { // we might want to adjust new_set if auto migration is enabled if self.auto_migrate_enabled { - let block = client.block_hash(BlockId::Latest).unwrap_or_default(); + let block = self.client.block_hash(BlockId::Latest).unwrap_or_default(); update_future_set(&mut self.future_new_set, &mut new_snapshot, block); } @@ -474,14 +456,14 @@ impl CachedContract { key_servers } - fn update_number_of_confirmations_if_required(&mut self, client: &dyn BlockChainClient) { + fn update_number_of_confirmations_if_required(&mut self) { if !self.auto_migrate_enabled { return; } - + let client = &*self.client; update_number_of_confirmations( - &|| latest_block_hash(&*client), - &|block| block_confirmations(&*client, block), + &|| latest_block_hash(client), + &|block| block_confirmations(client, block), &mut self.future_new_set, &mut self.snapshot); } } @@ -549,7 +531,7 @@ fn update_number_of_confirmations H256, F2: Fn(H256) -> Option> snapshot.new_set = future_new_set.new_set; } -fn update_last_transaction_block(client: &Client, migration_id: &H256, previous_transaction: &mut Option) -> bool { +fn update_last_transaction_block(client: &dyn SecretStoreChain, migration_id: &H256, previous_transaction: &mut Option) -> bool { let last_block = client.block_number(BlockId::Latest).unwrap_or_default(); match previous_transaction.as_ref() { // no previous transaction => send immediately @@ -577,11 +559,11 @@ fn update_last_transaction_block(client: &Client, migration_id: &H256, previous_ true } -fn latest_block_hash(client: &dyn BlockChainClient) -> H256 { +fn latest_block_hash(client: &dyn SecretStoreChain) -> H256 { client.block_hash(BlockId::Latest).unwrap_or_default() } -fn block_confirmations(client: &dyn BlockChainClient, block: H256) -> Option { +fn block_confirmations(client: &dyn SecretStoreChain, block: H256) -> Option { client.block_number(BlockId::Hash(block)) .and_then(|block| client.block_number(BlockId::Latest).map(|last_block| (block, last_block))) .map(|(block, last_block)| last_block - block) diff --git a/secret-store/src/lib.rs b/secret-store/src/lib.rs index ebf0228e9..a8fe7141e 100644 --- a/secret-store/src/lib.rs +++ b/secret-store/src/lib.rs @@ -15,12 +15,7 @@ // along with Parity Ethereum. If not, see . extern crate byteorder; -extern crate client_traits; -extern crate common_types; extern crate ethabi; -extern crate ethcore; -extern crate ethcore_call_contract as call_contract; -extern crate ethcore_sync as sync; extern crate ethereum_types; extern crate hyper; extern crate keccak_hash as hash; @@ -31,7 +26,6 @@ extern crate parity_crypto as crypto; extern crate parity_runtime; extern crate parking_lot; extern crate percent_encoding; -extern crate registrar; extern crate rustc_hex; extern crate serde; extern crate serde_json; @@ -54,19 +48,13 @@ extern crate lazy_static; #[macro_use] extern crate log; -#[cfg(any(test, feature = "accounts"))] -extern crate ethkey; #[cfg(test)] extern crate env_logger; #[cfg(test)] extern crate tempdir; -#[cfg(feature = "accounts")] -extern crate ethcore_accounts as accounts; - mod key_server_cluster; mod types; -mod helpers; mod traits; mod acl_storage; @@ -76,23 +64,19 @@ mod serialization; mod key_server_set; mod node_key_pair; mod listener; -mod trusted_client; +mod blockchain; mod migration; use std::sync::Arc; use kvdb::KeyValueDB; use kvdb_rocksdb::{Database, DatabaseConfig}; -use ethcore::client::Client; -use ethcore::miner::Miner; -use sync::SyncProvider; use parity_runtime::Executor; pub use types::{ServerKeyId, EncryptedDocumentKey, RequestSignature, Public, - Error, NodeAddress, ContractAddress, ServiceConfiguration, ClusterConfiguration}; -pub use traits::{NodeKeyPair, KeyServer}; + Error, NodeAddress, ServiceConfiguration, ClusterConfiguration}; +pub use traits::KeyServer; +pub use blockchain::{SecretStoreChain, SigningKeyPair, ContractAddress, BlockId, BlockNumber, NewBlocksNotify, Filter}; pub use self::node_key_pair::PlainNodeKeyPair; -#[cfg(feature = "accounts")] -pub use self::node_key_pair::KeyStoreNodeKeyPair; /// Open a secret store DB using the given secret store data path. The DB path is one level beneath the data path. pub fn open_secretstore_db(data_path: &str) -> Result, String> { @@ -109,10 +93,9 @@ pub fn open_secretstore_db(data_path: &str) -> Result, Strin } /// Start new key server instance -pub fn start(client: Arc, sync: Arc, miner: Arc, self_key_pair: Arc, mut config: ServiceConfiguration, +pub fn start(trusted_client: Arc, self_key_pair: Arc, mut config: ServiceConfiguration, db: Arc, executor: Executor) -> Result, Error> { - let trusted_client = trusted_client::TrustedClient::new(self_key_pair.clone(), client.clone(), sync, miner); let acl_storage: Arc = match config.acl_check_contract_address.take() { Some(acl_check_contract_address) => acl_storage::OnChainAclStorage::new(trusted_client.clone(), acl_check_contract_address)?, None => Arc::new(acl_storage::DummyAclStorage::default()), @@ -186,7 +169,7 @@ pub fn start(client: Arc, sync: Arc, miner: Arc key_storage: key_storage, } )?; - client.add_notify(listener.clone()); + trusted_client.add_listener(listener.clone()); listener }), None => None, diff --git a/secret-store/src/listener/service_contract.rs b/secret-store/src/listener/service_contract.rs index 78acdaf58..d09f267b2 100644 --- a/secret-store/src/listener/service_contract.rs +++ b/secret-store/src/listener/service_contract.rs @@ -16,22 +16,16 @@ use std::sync::Arc; use parking_lot::RwLock; -use common_types::filter::Filter; use ethabi::RawLog; use ethabi::FunctionOutputDecoder; -use call_contract::CallContract; -use ethcore::client::Client; -use client_traits::BlockChainClient; -use common_types::ids::BlockId; use crypto::publickey::{Public, public_to_address}; use hash::keccak; use bytes::Bytes; use ethereum_types::{H256, U256, Address, H512}; use listener::ApiMask; use listener::service_contract_listener::ServiceTask; -use trusted_client::TrustedClient; -use helpers::{get_confirmed_block_hash, REQUEST_CONFIRMATIONS_REQUIRED}; -use {ServerKeyId, NodeKeyPair, ContractAddress}; +use blockchain::{SecretStoreChain, Filter, SigningKeyPair, ContractAddress, BlockId}; +use ServerKeyId; use_contract!(service, "res/service.json"); @@ -98,9 +92,9 @@ pub struct OnChainServiceContract { /// Requests mask. mask: ApiMask, /// Blockchain client. - client: TrustedClient, + client: Arc, /// This node key pair. - self_key_pair: Arc, + self_key_pair: Arc, /// Contract registry name (if any). name: String, /// Contract address source. @@ -138,7 +132,7 @@ struct DocumentKeyShadowRetrievalService; impl OnChainServiceContract { /// Create new on-chain service contract. - pub fn new(mask: ApiMask, client: TrustedClient, name: String, address_source: ContractAddress, self_key_pair: Arc) -> Self { + pub fn new(mask: ApiMask, client: Arc, name: String, address_source: ContractAddress, self_key_pair: Arc) -> Self { let contract = OnChainServiceContract { mask: mask, client: client, @@ -157,24 +151,23 @@ impl OnChainServiceContract { /// Send transaction to the service contract. fn send_contract_transaction(&self, tx_name: &str, origin: &Address, server_key_id: &ServerKeyId, is_response_required: C, prepare_tx: P) -> Result<(), String> - where C: FnOnce(&Client, &Address, &ServerKeyId, &Address) -> bool, - P: FnOnce(&Client, &Address) -> Result { + where C: FnOnce(&dyn SecretStoreChain, &Address, &ServerKeyId, &Address) -> bool, + P: FnOnce(&dyn SecretStoreChain, &Address) -> Result { // only publish if contract address is set && client is online - let client = match self.client.get() { - Some(client) => client, - None => return Err("trusted client is required to publish key".into()), - }; + if !self.client.is_trusted() { + return Err("trusted client is required to publish key".into()) + } // only publish key if contract waits for publication // failing is ok here - it could be that enough confirmations have been recevied // or key has been requested using HTTP API let self_address = public_to_address(self.self_key_pair.public()); - if !is_response_required(&*client, origin, server_key_id, &self_address) { + if !is_response_required(&*self.client, origin, server_key_id, &self_address) { return Ok(()); } // prepare transaction data - let transaction_data = prepare_tx(&*client, origin)?; + let transaction_data = prepare_tx(&*self.client, origin)?; // send transaction self.client.transact_contract( @@ -190,9 +183,9 @@ impl OnChainServiceContract { /// Create task-specific pending requests iterator. fn create_pending_requests_iterator< - C: 'static + Fn(&Client, &Address, &BlockId) -> Result, - R: 'static + Fn(&dyn NodeKeyPair, &Client, &Address, &BlockId, U256) -> Result<(bool, ServiceTask), String> - >(&self, client: Arc, contract_address: &Address, block: &BlockId, get_count: C, read_item: R) -> Box> { + C: 'static + Fn(&dyn SecretStoreChain, &Address, &BlockId) -> Result, + R: 'static + Fn(&dyn SigningKeyPair, &dyn SecretStoreChain, &Address, &BlockId, U256) -> Result<(bool, ServiceTask), String> + >(&self, client: Arc, contract_address: &Address, block: &BlockId, get_count: C, read_item: R) -> Box> { get_count(&*client, contract_address, block) .map(|count| { let client = client.clone(); @@ -237,67 +230,46 @@ impl OnChainServiceContract { impl ServiceContract for OnChainServiceContract { fn update(&self) -> bool { - self.update_contract_address() && self.client.get().is_some() + self.update_contract_address() && self.client.is_trusted() } fn read_logs(&self) -> Box> { - let client = match self.client.get() { - Some(client) => client, - None => { - warn!(target: "secretstore", "{}: client is offline during read_logs call", - self.self_key_pair.public()); - return Box::new(::std::iter::empty()); - }, + if !self.client.is_trusted() { + warn!(target: "secretstore", "{}: client is offline during read_logs call", + self.self_key_pair.public()); + return Box::new(::std::iter::empty()); + } + + let address = match self.data.read().contract_address { + Some(address) => address, + None => return Box::new(::std::iter::empty()), // no contract installed + }; + let confirmed_block = match self.client.get_confirmed_block_hash() { + Some(confirmed_block) => confirmed_block, + None => return Box::new(::std::iter::empty()), // no block with enough confirmations }; - // prepare range of blocks to read logs from - let (address, first_block, last_block) = { - let mut data = self.data.write(); - let address = match data.contract_address { - Some(address) => address, - None => return Box::new(::std::iter::empty()), // no contract installed - }; - let confirmed_block = match get_confirmed_block_hash(&*client, REQUEST_CONFIRMATIONS_REQUIRED) { - Some(confirmed_block) => confirmed_block, - None => return Box::new(::std::iter::empty()), // no block with enough confirmations - }; - let first_block = match data.last_log_block.take().and_then(|b| client.tree_route(&b, &confirmed_block)) { - // if we have a route from last_log_block to confirmed_block => search for logs on this route - // - // potentially this could lead us to reading same logs twice when reorganizing to the fork, which - // already has been canonical previosuly - // the worst thing that can happen in this case is spending some time reading unneeded data from SS db - Some(ref route) if route.index < route.blocks.len() => route.blocks[route.index], - // else we care only about confirmed block - _ => confirmed_block.clone(), - }; - - data.last_log_block = Some(confirmed_block.clone()); - (address, first_block, confirmed_block) - }; - - // read server key generation requests - let request_logs = client.logs(Filter { - from_block: BlockId::Hash(first_block), - to_block: BlockId::Hash(last_block), + let request_logs = self.client.retrieve_last_logs(Filter { + from_block: BlockId::Hash(self.data.read().last_log_block.unwrap_or_else(|| confirmed_block)), address: Some(vec![address]), topics: vec![Some(mask_topics(&self.mask))], - limit: None, }).unwrap_or_default(); + let mut data = self.data.write(); + data.last_log_block = Some(confirmed_block.clone()); + Box::new(request_logs.into_iter() .filter_map(|log| { - let raw_log: RawLog = (log.entry.topics.into_iter().map(|t| t.0.into()).collect(), log.entry.data).into(); - if raw_log.topics[0] == *SERVER_KEY_GENERATION_REQUESTED_EVENT_NAME_HASH { - ServerKeyGenerationService::parse_log(&address, raw_log) - } else if raw_log.topics[0] == *SERVER_KEY_RETRIEVAL_REQUESTED_EVENT_NAME_HASH { - ServerKeyRetrievalService::parse_log(&address, raw_log) - } else if raw_log.topics[0] == *DOCUMENT_KEY_STORE_REQUESTED_EVENT_NAME_HASH { - DocumentKeyStoreService::parse_log(&address, raw_log) - } else if raw_log.topics[0] == *DOCUMENT_KEY_COMMON_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH { - DocumentKeyShadowRetrievalService::parse_common_request_log(&address, raw_log) - } else if raw_log.topics[0] == *DOCUMENT_KEY_PERSONAL_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH { - DocumentKeyShadowRetrievalService::parse_personal_request_log(&address, raw_log) + if log.topics[0] == *SERVER_KEY_GENERATION_REQUESTED_EVENT_NAME_HASH { + ServerKeyGenerationService::parse_log(&address, log) + } else if log.topics[0] == *SERVER_KEY_RETRIEVAL_REQUESTED_EVENT_NAME_HASH { + ServerKeyRetrievalService::parse_log(&address, log) + } else if log.topics[0] == *DOCUMENT_KEY_STORE_REQUESTED_EVENT_NAME_HASH { + DocumentKeyStoreService::parse_log(&address, log) + } else if log.topics[0] == *DOCUMENT_KEY_COMMON_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH { + DocumentKeyShadowRetrievalService::parse_common_request_log(&address, log) + } else if log.topics[0] == *DOCUMENT_KEY_PERSONAL_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH { + DocumentKeyShadowRetrievalService::parse_personal_request_log(&address, log) } else { Err("unknown type of log entry".into()) } @@ -311,39 +283,37 @@ impl ServiceContract for OnChainServiceContract { } fn read_pending_requests(&self) -> Box> { - let client = match self.client.get() { - Some(client) => client, - None => return Box::new(::std::iter::empty()), - }; + if !self.client.is_trusted() { + return Box::new(::std::iter::empty()) + } - // we only need requests that are here for more than REQUEST_CONFIRMATIONS_REQUIRED blocks - // => we're reading from Latest - (REQUEST_CONFIRMATIONS_REQUIRED + 1) block + // we only need requests that are here from the last confirm block let data = self.data.read(); match data.contract_address { None => Box::new(::std::iter::empty()), - Some(contract_address) => get_confirmed_block_hash(&*client, REQUEST_CONFIRMATIONS_REQUIRED + 1) + Some(contract_address) => self.client.get_confirmed_block_hash() .map(|b| { let block = BlockId::Hash(b); let iter = match self.mask.server_key_generation_requests { - true => Box::new(self.create_pending_requests_iterator(client.clone(), &contract_address, &block, + true => Box::new(self.create_pending_requests_iterator(self.client.clone(), &contract_address, &block, &ServerKeyGenerationService::read_pending_requests_count, &ServerKeyGenerationService::read_pending_request)) as Box>, false => Box::new(::std::iter::empty()), }; let iter = match self.mask.server_key_retrieval_requests { - true => Box::new(iter.chain(self.create_pending_requests_iterator(client.clone(), &contract_address, &block, + true => Box::new(iter.chain(self.create_pending_requests_iterator(self.client.clone(), &contract_address, &block, &ServerKeyRetrievalService::read_pending_requests_count, &ServerKeyRetrievalService::read_pending_request))), false => iter, }; let iter = match self.mask.document_key_store_requests { - true => Box::new(iter.chain(self.create_pending_requests_iterator(client.clone(), &contract_address, &block, + true => Box::new(iter.chain(self.create_pending_requests_iterator(self.client.clone(), &contract_address, &block, &DocumentKeyStoreService::read_pending_requests_count, &DocumentKeyStoreService::read_pending_request))), false => iter, }; let iter = match self.mask.document_key_shadow_retrieval_requests { - true => Box::new(iter.chain(self.create_pending_requests_iterator(client, &contract_address, &block, + true => Box::new(iter.chain(self.create_pending_requests_iterator(self.client.clone(), &contract_address, &block, &DocumentKeyShadowRetrievalService::read_pending_requests_count, &DocumentKeyShadowRetrievalService::read_pending_request))), false => iter @@ -457,7 +427,7 @@ impl ServerKeyGenerationService { } /// Check if response from key server is required. - pub fn is_response_required(client: &Client, contract_address: &Address, server_key_id: &ServerKeyId, key_server: &Address) -> bool { + pub fn is_response_required(client: &dyn SecretStoreChain, contract_address: &Address, server_key_id: &ServerKeyId, key_server: &Address) -> bool { // we're checking confirmation in Latest block, because we're interested in latest contract state here let (encoded, decoder) = service::functions::is_server_key_generation_response_required::call(*server_key_id, *key_server); match client.call_contract(BlockId::Latest, *contract_address, encoded) { @@ -477,14 +447,14 @@ impl ServerKeyGenerationService { } /// Read pending requests count. - fn read_pending_requests_count(client: &Client, contract_address: &Address, block: &BlockId) -> Result { + fn read_pending_requests_count(client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId) -> Result { let (encoded, decoder) = service::functions::server_key_generation_requests_count::call(); decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) .map_err(|e| e.to_string()) } /// Read pending request. - fn read_pending_request(self_key_pair: &dyn NodeKeyPair, client: &Client, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { + fn read_pending_request(self_key_pair: &dyn SigningKeyPair, client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { let self_address = public_to_address(self_key_pair.public()); let (encoded, decoder) = service::functions::get_server_key_generation_request::call(index); @@ -517,7 +487,7 @@ impl ServerKeyRetrievalService { } /// Check if response from key server is required. - pub fn is_response_required(client: &Client, contract_address: &Address, server_key_id: &ServerKeyId, key_server: &Address) -> bool { + pub fn is_response_required(client: &dyn SecretStoreChain, contract_address: &Address, server_key_id: &ServerKeyId, key_server: &Address) -> bool { // we're checking confirmation in Latest block, because we're interested in latest contract state here let (encoded, decoder) = service::functions::is_server_key_retrieval_response_required::call(*server_key_id, *key_server); match client.call_contract(BlockId::Latest, *contract_address, encoded) { @@ -537,14 +507,14 @@ impl ServerKeyRetrievalService { } /// Read pending requests count. - fn read_pending_requests_count(client: &Client, contract_address: &Address, block: &BlockId) -> Result { + fn read_pending_requests_count(client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId) -> Result { let (encoded, decoder) = service::functions::server_key_retrieval_requests_count::call(); decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) .map_err(|e| e.to_string()) } /// Read pending request. - fn read_pending_request(self_key_pair: &dyn NodeKeyPair, client: &Client, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { + fn read_pending_request(self_key_pair: &dyn SigningKeyPair, client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { let self_address = public_to_address(self_key_pair.public()); let (encoded, decoder) = service::functions::get_server_key_retrieval_request::call(index); @@ -580,7 +550,7 @@ impl DocumentKeyStoreService { } /// Check if response from key server is required. - pub fn is_response_required(client: &Client, contract_address: &Address, server_key_id: &ServerKeyId, key_server: &Address) -> bool { + pub fn is_response_required(client: &dyn SecretStoreChain, contract_address: &Address, server_key_id: &ServerKeyId, key_server: &Address) -> bool { // we're checking confirmation in Latest block, because we're interested in latest contract state here let (encoded, decoder) = service::functions::is_document_key_store_response_required::call(*server_key_id, *key_server); match client.call_contract(BlockId::Latest, *contract_address, encoded) { @@ -600,14 +570,14 @@ impl DocumentKeyStoreService { } /// Read pending requests count. - fn read_pending_requests_count(client: &Client, contract_address: &Address, block: &BlockId) -> Result { + fn read_pending_requests_count(client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId) -> Result { let (encoded, decoder) = service::functions::document_key_store_requests_count::call(); decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) .map_err(|e| e.to_string()) } /// Read pending request. - fn read_pending_request(self_key_pair: &dyn NodeKeyPair, client: &Client, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { + fn read_pending_request(self_key_pair: &dyn SigningKeyPair, client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { let self_address = public_to_address(self_key_pair.public()); let (encoded, decoder) = service::functions::get_document_key_store_request::call(index); let (server_key_id, author, common_point, encrypted_point) = decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) @@ -647,7 +617,7 @@ impl DocumentKeyShadowRetrievalService { } /// Check if response from key server is required. - pub fn is_response_required(client: &Client, contract_address: &Address, server_key_id: &ServerKeyId, requester: &Address, key_server: &Address) -> bool { + pub fn is_response_required(client: &dyn SecretStoreChain, contract_address: &Address, server_key_id: &ServerKeyId, requester: &Address, key_server: &Address) -> bool { // we're checking confirmation in Latest block, because we're interested in latest contract state here let (encoded, decoder) = service::functions::is_document_key_shadow_retrieval_response_required::call(*server_key_id, *requester, *key_server); match client.call_contract(BlockId::Latest, *contract_address, encoded) { @@ -662,7 +632,7 @@ impl DocumentKeyShadowRetrievalService { } /// Prepare publish personal key transaction data. - pub fn prepare_pubish_personal_tx_data(client: &Client, contract_address: &Address, server_key_id: &ServerKeyId, requester: &Address, participants: &[Address], decrypted_secret: Public, shadow: Bytes) -> Result { + pub fn prepare_pubish_personal_tx_data(client: &dyn SecretStoreChain, contract_address: &Address, server_key_id: &ServerKeyId, requester: &Address, participants: &[Address], decrypted_secret: Public, shadow: Bytes) -> Result { let mut participants_mask = U256::default(); for participant in participants { let participant_index = Self::map_key_server_address(client, contract_address, participant.clone()) @@ -680,14 +650,14 @@ impl DocumentKeyShadowRetrievalService { } /// Read pending requests count. - fn read_pending_requests_count(client: &Client, contract_address: &Address, block: &BlockId) -> Result { + fn read_pending_requests_count(client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId) -> Result { let (encoded, decoder) = service::functions::document_key_shadow_retrieval_requests_count::call(); decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) .map_err(|e| e.to_string()) } /// Read pending request. - fn read_pending_request(self_key_pair: &dyn NodeKeyPair, client: &Client, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { + fn read_pending_request(self_key_pair: &dyn SigningKeyPair, client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { let self_address = public_to_address(self_key_pair.public()); let (encoded, decoder) = service::functions::get_document_key_shadow_retrieval_request::call(index); @@ -717,7 +687,7 @@ impl DocumentKeyShadowRetrievalService { } /// Map from key server address to key server index. - fn map_key_server_address(client: &Client, contract_address: &Address, key_server: Address) -> Result { + fn map_key_server_address(client: &dyn SecretStoreChain, contract_address: &Address, key_server: Address) -> Result { // we're checking confirmation in Latest block, because tx ,ust be appended to the latest state let (encoded, decoder) = service::functions::require_key_server::call(key_server); let index = decoder.decode(&client.call_contract(BlockId::Latest, *contract_address, encoded)?) diff --git a/secret-store/src/listener/service_contract_listener.rs b/secret-store/src/listener/service_contract_listener.rs index 63cacf01d..9ed44fbb4 100644 --- a/secret-store/src/listener/service_contract_listener.rs +++ b/secret-store/src/listener/service_contract_listener.rs @@ -18,8 +18,6 @@ use std::collections::HashSet; use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread; -use client_traits::ChainNotify; -use common_types::chain_notify::NewBlocks; use bytes::Bytes; use crypto::publickey::{Public, public_to_address}; use ethereum_types::{H256, U256, Address, BigEndianHash as _}; @@ -36,7 +34,8 @@ use parking_lot::Mutex; use acl_storage::AclStorage; use listener::service_contract::ServiceContract; use listener::tasks_queue::TasksQueue; -use {ServerKeyId, NodeKeyPair, Error}; +use {ServerKeyId, Error}; +use blockchain::{NewBlocksNotify, SigningKeyPair}; /// Retry interval (in blocks). Every RETRY_INTERVAL_BLOCKS blocks each KeyServer reads pending requests from /// service contract && tries to re-execute. The reason to have this mechanism is primarily because keys @@ -64,7 +63,7 @@ pub struct ServiceContractListenerParams { /// Service contract. pub contract: Arc, /// This node key pair. - pub self_key_pair: Arc, + pub self_key_pair: Arc, /// Key servers set. pub key_server_set: Arc, /// ACL storage reference. @@ -90,7 +89,7 @@ struct ServiceContractListenerData { /// Cluster client reference. pub cluster: Arc, /// This node key pair. - pub self_key_pair: Arc, + pub self_key_pair: Arc, /// Key servers set. pub key_server_set: Arc, /// Key storage reference. @@ -434,14 +433,8 @@ impl Drop for ServiceContractListener { } } -impl ChainNotify for ServiceContractListener { - fn new_blocks(&self, new_blocks: NewBlocks) { - if new_blocks.has_more_blocks_to_import { return } - let enacted_len = new_blocks.route.enacted().len(); - if enacted_len == 0 && new_blocks.route.retracted().is_empty() { - return; - } - +impl NewBlocksNotify for ServiceContractListener { + fn new_blocks(&self, new_enacted_len: usize) { if !self.data.contract.update() { return; } @@ -450,7 +443,7 @@ impl ChainNotify for ServiceContractListener { // schedule retry if received enough blocks since last retry // it maybe inaccurate when switching syncing/synced states, but that's ok - if self.data.last_retry.fetch_add(enacted_len, Ordering::Relaxed) >= RETRY_INTERVAL_BLOCKS { + if self.data.last_retry.fetch_add(new_enacted_len, Ordering::Relaxed) >= RETRY_INTERVAL_BLOCKS { // shortcut: do not retry if we're isolated from the cluster if !self.data.key_server_set.is_isolated() { self.data.tasks_queue.push(ServiceTask::Retry); @@ -596,7 +589,8 @@ mod tests { use key_storage::tests::DummyKeyStorage; use key_server_set::KeyServerSet; use key_server_set::tests::MapKeyServerSet; - use {NodeKeyPair, PlainNodeKeyPair, ServerKeyId}; + use blockchain::SigningKeyPair; + use {PlainNodeKeyPair, ServerKeyId}; use super::{ServiceTask, ServiceContractListener, ServiceContractListenerParams, is_processed_by_this_key_server}; use ethereum_types::Address; diff --git a/secret-store/src/node_key_pair.rs b/secret-store/src/node_key_pair.rs index c3f02c3f6..f31c46c86 100644 --- a/secret-store/src/node_key_pair.rs +++ b/secret-store/src/node_key_pair.rs @@ -14,10 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use crypto::publickey::ecdh::agree; use crypto::publickey::{KeyPair, Public, Signature, Error as EthKeyError, sign, public_to_address}; use ethereum_types::{H256, Address}; -use traits::NodeKeyPair; +use blockchain::SigningKeyPair; pub struct PlainNodeKeyPair { key_pair: KeyPair, @@ -36,7 +35,7 @@ impl PlainNodeKeyPair { } } -impl NodeKeyPair for PlainNodeKeyPair { +impl SigningKeyPair for PlainNodeKeyPair { fn public(&self) -> &Public { self.key_pair.public() } @@ -48,60 +47,4 @@ impl NodeKeyPair for PlainNodeKeyPair { fn sign(&self, data: &H256) -> Result { sign(self.key_pair.secret(), data) } - - fn compute_shared_key(&self, peer_public: &Public) -> Result { - agree(self.key_pair.secret(), peer_public) - .map_err(|e| EthKeyError::Custom(e.to_string())) - .and_then(KeyPair::from_secret) - } } - -#[cfg(feature = "accounts")] -mod accounts { - use super::*; - use std::sync::Arc; - use ethkey::Password; - use accounts::AccountProvider; - - pub struct KeyStoreNodeKeyPair { - account_provider: Arc, - address: Address, - public: Public, - password: Password, - } - - impl KeyStoreNodeKeyPair { - pub fn new(account_provider: Arc, address: Address, password: Password) -> Result { - let public = account_provider.account_public(address.clone(), &password).map_err(|e| EthKeyError::Custom(format!("{}", e)))?; - Ok(KeyStoreNodeKeyPair { - account_provider, - address, - public, - password, - }) - } - } - - impl NodeKeyPair for KeyStoreNodeKeyPair { - fn public(&self) -> &Public { - &self.public - } - - fn address(&self) -> Address { - public_to_address(&self.public) - } - - fn sign(&self, data: &H256) -> Result { - self.account_provider.sign(self.address.clone(), Some(self.password.clone()), data.clone()) - .map_err(|e| EthKeyError::Custom(format!("{}", e))) - } - - fn compute_shared_key(&self, peer_public: &Public) -> Result { - KeyPair::from_secret(self.account_provider.agree(self.address.clone(), Some(self.password.clone()), peer_public) - .map_err(|e| EthKeyError::Custom(format!("{}", e)))?) - } - } -} - -#[cfg(feature = "accounts")] -pub use self::accounts::KeyStoreNodeKeyPair; diff --git a/secret-store/src/traits.rs b/secret-store/src/traits.rs index d62210ed1..de112fc9f 100644 --- a/secret-store/src/traits.rs +++ b/secret-store/src/traits.rs @@ -16,23 +16,9 @@ use std::collections::BTreeSet; use futures::Future; -use crypto::publickey::{KeyPair, Signature, Error as EthKeyError}; -use ethereum_types::{H256, Address}; use types::{Error, Public, ServerKeyId, MessageHash, EncryptedMessageSignature, RequestSignature, Requester, EncryptedDocumentKey, EncryptedDocumentKeyShadow, NodeId}; -/// Node key pair. -pub trait NodeKeyPair: Send + Sync { - /// Public portion of key. - fn public(&self) -> &Public; - /// Address of key owner. - fn address(&self) -> Address; - /// Sign data with node key. - fn sign(&self, data: &H256) -> Result; - /// Compute shared key to encrypt channel between two nodes. - fn compute_shared_key(&self, peer_public: &Public) -> Result; -} - /// Server key (SK) generator. pub trait ServerKeyGenerator { /// Generate new SK. diff --git a/secret-store/src/trusted_client.rs b/secret-store/src/trusted_client.rs deleted file mode 100644 index 32df03c4b..000000000 --- a/secret-store/src/trusted_client.rs +++ /dev/null @@ -1,114 +0,0 @@ -// 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, Weak}; -use bytes::Bytes; -use common_types::{ - ids::BlockId, - transaction::{Transaction, SignedTransaction, Action}, -}; -use ethereum_types::Address; -use ethcore::client::Client; -use client_traits::{ChainInfo, Nonce}; -use ethcore::miner::{Miner, MinerService}; -use sync::SyncProvider; -use helpers::{get_confirmed_block_hash, REQUEST_CONFIRMATIONS_REQUIRED}; -use {Error, NodeKeyPair, ContractAddress}; -use registrar::RegistrarClient; - -#[derive(Clone)] -/// 'Trusted' client weak reference. -pub struct TrustedClient { - /// This key server node key pair. - self_key_pair: Arc, - /// Blockchain client. - client: Weak, - /// Sync provider. - sync: Weak, - /// Miner service. - miner: Weak, -} - -impl TrustedClient { - /// Create new trusted client. - pub fn new(self_key_pair: Arc, client: Arc, sync: Arc, miner: Arc) -> Self { - TrustedClient { - self_key_pair, - client: Arc::downgrade(&client), - sync: Arc::downgrade(&sync), - miner: Arc::downgrade(&miner), - } - } - - /// Get 'trusted' `Client` reference only if it is synchronized && trusted. - pub fn get(&self) -> Option> { - self.client.upgrade() - .and_then(|client| self.sync.upgrade().map(|sync| (client, sync))) - .and_then(|(client, sync)| { - let is_synced = !sync.is_major_syncing(); - let is_trusted = client.chain_info().security_level().is_full(); - match is_synced && is_trusted { - true => Some(client), - false => None, - } - }) - } - - /// Get untrusted `Client` reference. - pub fn get_untrusted(&self) -> Option> { - self.client.upgrade() - } - - /// Transact contract. - pub fn transact_contract(&self, contract: Address, tx_data: Bytes) -> Result<(), Error> { - let client = self.client.upgrade().ok_or_else(|| Error::Internal("cannot submit tx when client is offline".into()))?; - let miner = self.miner.upgrade().ok_or_else(|| Error::Internal("cannot submit tx when miner is offline".into()))?; - let engine = client.engine(); - let transaction = Transaction { - nonce: client.latest_nonce(&self.self_key_pair.address()), - action: Action::Call(contract), - gas: miner.authoring_params().gas_range_target.0, - gas_price: miner.sensible_gas_price(), - value: Default::default(), - data: tx_data, - }; - let chain_id = engine.signing_chain_id(&client.latest_env_info()); - let signature = self.self_key_pair.sign(&transaction.hash(chain_id))?; - let signed = SignedTransaction::new(transaction.with_signature(signature, chain_id))?; - miner.import_own_transaction(&*client, signed.into()) - .map_err(|e| Error::Internal(format!("failed to import tx: {}", e))) - } - - /// Read contract address. If address source is registry, address only returned if current client state is - /// trusted. Address from registry is read from registry from block latest block with - /// REQUEST_CONFIRMATIONS_REQUIRED confirmations. - pub fn read_contract_address( - &self, - registry_name: &str, - address: &ContractAddress - ) -> Option
{ - match *address { - ContractAddress::Address(ref address) => Some(address.clone()), - ContractAddress::Registry => self.get().and_then(|client| - get_confirmed_block_hash(&*client, REQUEST_CONFIRMATIONS_REQUIRED) - .and_then(|block| { - client.get_address(registry_name, BlockId::Hash(block)) - .unwrap_or(None) - }) - ), - } - } -} diff --git a/secret-store/src/types/all.rs b/secret-store/src/types/all.rs index 65ebbbc96..b37ffe6b0 100644 --- a/secret-store/src/types/all.rs +++ b/secret-store/src/types/all.rs @@ -16,6 +16,7 @@ use std::collections::BTreeMap; +use blockchain::ContractAddress; use {bytes, ethereum_types}; /// Node id. @@ -42,15 +43,6 @@ pub struct NodeAddress { pub port: u16, } -/// Contract address. -#[derive(Debug, Clone)] -pub enum ContractAddress { - /// Address is read from registry. - Registry, - /// Address is specified. - Address(crypto::publickey::Address), -} - /// Secret store configuration #[derive(Debug)] pub struct ServiceConfiguration {