Encapsulate access to the client for secret store (#11232)

* Move all client usages into trusted_client

* Move confirmed hash method to trusted client

* Tree route and logs encapsuluted

* Remove not used method for keys sharing

* NodeKeyPair renamed and moved to trusted client

* Use public key error in trusted client

* Move contract address definition into trusted client

* Block id and number types from ethcore wrapped

* Trusted client renamed to more general Blockchain

* Trusted client implementation moved to parity code

* Move node key pair under secret store feature as well

* Registar crate removed from deps

* Accounts feature removed from secret store

* Fix after merge

* Blockchain renamed to SecretStoreChain

* Module documentations added
This commit is contained in:
Anton Gavrilov 2020-01-07 14:37:02 +01:00 committed by GitHub
parent 5bd6b208af
commit 424b38a8d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 633 additions and 477 deletions

9
Cargo.lock generated
View File

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

View File

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

View File

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

View File

@ -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 <http://www.gnu.org/licenses/>.
//! 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<dyn SigningKeyPair>,
/// Blockchain client.
client: Weak<Client>,
/// Sync provider.
sync: Weak<dyn SyncProvider>,
/// Miner service.
miner: Weak<Miner>,
/// Chain new blocks listeners
listeners: RwLock<Vec<Weak<dyn NewBlocksNotify>>>,
}
impl TrustedClient {
/// Create new trusted client.
pub fn new(self_key_pair: Arc<dyn SigningKeyPair>, client: Arc<Client>, sync: Arc<dyn SyncProvider>, miner: Arc<Miner>) -> Arc<Self> {
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<Arc<Client>> {
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<TreeRoute> {
if let Some(client) = self.get_trusted() {
client.tree_route(from, to)
} else {
None
}
}
fn logs(&self, filter: BlockchainFilter) -> Option<Vec<LocalizedLogEntry>> {
if let Some(client) = self.get_trusted() {
client.logs(filter).ok()
} else {
None
}
}
}
impl SecretStoreChain for TrustedClient {
fn add_listener(&self, target: Arc<dyn NewBlocksNotify>) {
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<Address> {
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<Bytes, String> {
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<H256> {
if let Some(client) = self.get_trusted() {
client.block_hash(into_ethcore_block_id(id))
} else {
None
}
}
fn block_number(&self, id: BlockId) -> Option<BlockNumber> {
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<Vec<RawLog>> {
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::<Vec<_>>()
})
}
fn get_confirmed_block_hash(&self) -> Option<H256> {
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);
}
}
}

View File

@ -14,18 +14,18 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
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<H256> {
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;

View File

@ -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 <http://www.gnu.org/licenses/>.
//! 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<AccountProvider>,
address: Address,
public: Public,
password: Password,
}
impl KeyStoreNodeKeyPair {
pub fn new(account_provider: Arc<AccountProvider>, address: Address, password: Password) -> Result<Self, EthKeyError> {
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<Signature, EthKeyError> {
self.account_provider.sign(self.address.clone(), Some(self.password.clone()), data.clone())
.map_err(|e| EthKeyError::Custom(format!("{}", e)))
}
}

View File

@ -14,6 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
//! 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<Self, String> {
let self_secret: Arc<dyn ethcore_secretstore::NodeKeyPair> = match conf.self_secret.take() {
let self_secret: Arc<dyn ethcore_secretstore::SigningKeyPair> = 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 {

View File

@ -7,15 +7,9 @@ authors = ["Parity Technologies <admin@parity.io>"]
[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"]

View File

@ -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<dyn SecretStoreChain>,
/// 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<Arc<Self>, Error> {
let client = trusted_client.get_untrusted();
pub fn new(trusted_client: Arc<dyn SecretStoreChain>, address_source: ContractAddress) -> Result<Arc<Self>, 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<dyn SecretStoreChain>, 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<bool, Error> {
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())))

View File

@ -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 <http://www.gnu.org/licenses/>.
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<Signature, EthKeyError>;
}
/// 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<Vec<Address>>,
/// Search topics.
///
/// If None, match all.
/// If specified, log must contain one of these topics.
pub topics: Vec<Option<Vec<H256>>>,
}
/// Blockchain representation for Secret Store
pub trait SecretStoreChain: Send + Sync + 'static {
/// Adds listener for chain's NewBlocks event
fn add_listener(&self, target: Arc<dyn NewBlocksNotify>);
/// 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<Address>;
/// Call contract in the blockchain
fn call_contract(&self, block_id: BlockId, contract_address: Address, data: Bytes) -> Result<Bytes, String>;
/// Returns blockhash for block id
fn block_hash(&self, id: BlockId) -> Option<H256>;
/// Returns block number for block id
fn block_number(&self, id: BlockId) -> Option<BlockNumber>;
/// Retrieve last blockchain logs for the filter
fn retrieve_last_logs(&self, filter: Filter) -> Option<Vec<RawLog>>;
/// Get hash of the last block with predefined number of confirmations (depends on the chain).
fn get_confirmed_block_hash(&self) -> Option<H256>;
}

View File

@ -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<dyn KeyServerSet>, self_key_pair: Arc<dyn NodeKeyPair>,
pub fn new(config: &ClusterConfiguration, key_server_set: Arc<dyn KeyServerSet>, self_key_pair: Arc<dyn SigningKeyPair>,
acl_storage: Arc<dyn AclStorage>, key_storage: Arc<dyn KeyStorage>, executor: Executor) -> Result<Self, Error>
{
Ok(KeyServerImpl {
@ -269,7 +270,7 @@ impl MessageSigner for KeyServerImpl {
}
impl KeyServerCore {
pub fn new(config: &ClusterConfiguration, key_server_set: Arc<dyn KeyServerSet>, self_key_pair: Arc<dyn NodeKeyPair>,
pub fn new(config: &ClusterConfiguration, key_server_set: Arc<dyn KeyServerSet>, self_key_pair: Arc<dyn SigningKeyPair>,
acl_storage: Arc<dyn AclStorage>, key_storage: Arc<dyn KeyStorage>, executor: Executor) -> Result<Self, Error>
{
let cconfig = NetClusterConfiguration {

View File

@ -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};

View File

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

View File

@ -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<dyn NodeKeyPair>,
pub self_key_pair: Arc<dyn SigningKeyPair>,
/// Cluster nodes set.
pub key_server_set: Arc<dyn KeyServerSet>,
/// Reference to key storage
@ -173,7 +174,7 @@ pub struct ClusterView {
configured_nodes_count: usize,
connected_nodes: BTreeSet<NodeId>,
connections: Arc<dyn ConnectionProvider>,
self_key_pair: Arc<dyn NodeKeyPair>,
self_key_pair: Arc<dyn SigningKeyPair>,
}
/// Cross-thread shareable cluster data.
@ -181,7 +182,7 @@ pub struct ClusterData<C: ConnectionManager> {
/// Cluster configuration.
pub config: ClusterConfiguration,
/// KeyPair this node holds.
pub self_key_pair: Arc<dyn NodeKeyPair>,
pub self_key_pair: Arc<dyn SigningKeyPair>,
/// Connections data.
pub connections: C,
/// Active sessions data.
@ -311,7 +312,7 @@ impl<C: ConnectionManager> ClusterCore<C> {
impl ClusterView {
pub fn new(
self_key_pair: Arc<dyn NodeKeyPair>,
self_key_pair: Arc<dyn SigningKeyPair>,
connections: Arc<dyn ConnectionProvider>,
nodes: BTreeSet<NodeId>,
configured_nodes_count: usize
@ -597,7 +598,7 @@ pub struct ServersSetChangeParams {
}
pub fn new_servers_set_change_session(
self_key_pair: Arc<dyn NodeKeyPair>,
self_key_pair: Arc<dyn SigningKeyPair>,
sessions: &ClusterSessions,
connections: Arc<dyn ConnectionProvider>,
servers_set_change_creator_connector: Arc<dyn ServersSetChangeSessionCreatorConnector>,
@ -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;

View File

@ -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<dyn NodeKeyPair>,
self_key_pair: Arc<dyn SigningKeyPair>,
/// Network messages processor.
message_processor: Arc<dyn MessageProcessor>,
/// Connections trigger.

View File

@ -15,7 +15,8 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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<dyn NodeKeyPair>,
self_key_pair: Arc<dyn SigningKeyPair>,
servers_set_change_creator_connector: Arc<dyn ServersSetChangeSessionCreatorConnector>,
sessions: Arc<ClusterSessions>,
connections: Arc<dyn ConnectionProvider>,
@ -58,7 +59,7 @@ pub struct SessionsMessageProcessor {
impl SessionsMessageProcessor {
/// Create new instance of SessionsMessageProcessor.
pub fn new(
self_key_pair: Arc<dyn NodeKeyPair>,
self_key_pair: Arc<dyn SigningKeyPair>,
servers_set_change_creator_connector: Arc<dyn ServersSetChangeSessionCreatorConnector>,
sessions: Arc<ClusterSessions>,
connections: Arc<dyn ConnectionProvider>,

View File

@ -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<T> CompletionSignal<T> {
}
}
pub fn create_cluster_view(self_key_pair: Arc<dyn NodeKeyPair>, connections: Arc<dyn ConnectionProvider>, requires_all_connections: bool) -> Result<Arc<dyn Cluster>, Error> {
pub fn create_cluster_view(self_key_pair: Arc<dyn SigningKeyPair>, connections: Arc<dyn ConnectionProvider>, requires_all_connections: bool) -> Result<Arc<dyn Cluster>, Error> {
let mut connected_nodes = connections.connected_nodes()?;
let disconnected_nodes = connections.disconnected_nodes();

View File

@ -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<dyn NodeKeyPair>,
pub self_key_pair: Arc<dyn SigningKeyPair>,
}
impl SimpleConnectionTrigger {
@ -103,7 +103,7 @@ impl SimpleConnectionTrigger {
}
/// Create new simple connection trigger.
pub fn new(key_server_set: Arc<dyn KeyServerSet>, self_key_pair: Arc<dyn NodeKeyPair>, admin_public: Option<Public>) -> Self {
pub fn new(key_server_set: Arc<dyn KeyServerSet>, self_key_pair: Arc<dyn SigningKeyPair>, admin_public: Option<Public>) -> Self {
SimpleConnectionTrigger {
key_server_set: key_server_set,
connections: TriggerConnections {

View File

@ -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<dyn NodeKeyPair>,
self_key_pair: Arc<dyn SigningKeyPair>,
/// Key server set.
key_server_set: Arc<dyn KeyServerSet>,
/// Last server set state.
@ -105,7 +105,7 @@ struct TriggerSession {
/// Servers set change session creator connector.
connector: Arc<ServersSetChangeSessionCreatorConnectorWithMigration>,
/// This node key pair.
self_key_pair: Arc<dyn NodeKeyPair>,
self_key_pair: Arc<dyn SigningKeyPair>,
/// Key server set.
key_server_set: Arc<dyn KeyServerSet>,
}
@ -117,7 +117,7 @@ impl ConnectionTriggerWithMigration {
}
/// Create new trigge with migration.
pub fn new(key_server_set: Arc<dyn KeyServerSet>, self_key_pair: Arc<dyn NodeKeyPair>) -> Self {
pub fn new(key_server_set: Arc<dyn KeyServerSet>, self_key_pair: Arc<dyn SigningKeyPair>) -> Self {
let snapshot = key_server_set.snapshot();
let migration = snapshot.migration.clone();

View File

@ -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: A, self_key_pair: Arc<dyn NodeKeyPair>, trusted_nodes: BTreeSet<NodeId>) -> Handshake<A> where A: AsyncWrite + AsyncRead {
pub fn handshake<A>(a: A, self_key_pair: Arc<dyn SigningKeyPair>, trusted_nodes: BTreeSet<NodeId>) -> Handshake<A> 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: A, init_data: Result<(H256, KeyPair), Error>, self_key_pair: Arc<dyn NodeKeyPair>, trusted_nodes: BTreeSet<NodeId>) -> Handshake<A> where A: AsyncWrite + AsyncRead {
pub fn handshake_with_init_data<A>(a: A, init_data: Result<(H256, KeyPair), Error>, self_key_pair: Arc<dyn SigningKeyPair>, trusted_nodes: BTreeSet<NodeId>) -> Handshake<A> 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::<A>::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: 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: A, self_key_pair: Arc<dyn NodeKeyPair>) -> Handshake<A> where A: AsyncWrite + AsyncRead {
pub fn accept_handshake<A>(a: A, self_key_pair: Arc<dyn SigningKeyPair>) -> Handshake<A> 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<A> {
is_active: bool,
error: Option<(A, Result<HandshakeResult, Error>)>,
state: HandshakeState<A>,
self_key_pair: Arc<dyn NodeKeyPair>,
self_key_pair: Arc<dyn SigningKeyPair>,
self_session_key_pair: Option<KeyPair>,
self_confirmation_plain: H256,
trusted_nodes: Option<BTreeSet<NodeId>>,
@ -156,7 +157,7 @@ impl<A> Handshake<A> where A: AsyncRead + AsyncWrite {
})))
}
fn make_private_key_signature_message(self_key_pair: &dyn NodeKeyPair, confirmation_plain: &H256) -> Result<Message, Error> {
fn make_private_key_signature_message(self_key_pair: &dyn SigningKeyPair, confirmation_plain: &H256) -> Result<Message, Error> {
Ok(Message::Cluster(ClusterMessage::NodePrivateKeySignature(NodePrivateKeySignature {
confirmation_signed: self_key_pair.sign(confirmation_plain)?.into(),
})))

View File

@ -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};

View File

@ -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<dyn NodeKeyPair>) -> Deadline<AcceptConnection> {
pub fn accept_connection(stream: TcpStream, self_key_pair: Arc<dyn SigningKeyPair>) -> Deadline<AcceptConnection> {
// 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");

View File

@ -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<dyn NodeKeyPair>, trusted_nodes: BTreeSet<NodeId>) -> Deadline<Connect> {
pub fn connect(address: &SocketAddr, self_key_pair: Arc<dyn SigningKeyPair>, trusted_nodes: BTreeSet<NodeId>) -> Deadline<Connect> {
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<dyn NodeKeyPair>,
self_key_pair: Arc<dyn SigningKeyPair>,
trusted_nodes: BTreeSet<NodeId>,
}

View File

@ -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<dyn SecretStoreChain>,
/// Contract address source.
contract_address_source: Option<ContractAddress>,
/// Current contract address.
@ -121,18 +113,15 @@ struct CachedContract {
/// Previous confirm migration transaction.
confirm_migration_tx: Option<PreviousMigrationTransaction>,
/// This node key pair.
self_key_pair: Arc<dyn NodeKeyPair>,
self_key_pair: Arc<dyn SigningKeyPair>,
}
impl OnChainKeyServerSet {
pub fn new(trusted_client: TrustedClient, contract_address_source: Option<ContractAddress>, self_key_pair: Arc<dyn NodeKeyPair>, auto_migrate_enabled: bool, key_servers: BTreeMap<Public, NodeAddress>) -> Result<Arc<Self>, Error> {
let client = trusted_client.get_untrusted();
pub fn new(trusted_client: Arc<dyn SecretStoreChain>, contract_address_source: Option<ContractAddress>, self_key_pair: Arc<dyn SigningKeyPair>, auto_migrate_enabled: bool, key_servers: BTreeMap<Public, NodeAddress>) -> Result<Arc<Self>, 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 <F: Fn(Vec<u8>) -> Result<Vec<u8>, String>> KeyServerSubset<F> for NewKeySe
}
impl CachedContract {
pub fn new(client: TrustedClient, contract_address_source: Option<ContractAddress>, self_key_pair: Arc<dyn NodeKeyPair>, auto_migrate_enabled: bool, key_servers: BTreeMap<Public, NodeAddress>) -> Result<Self, Error> {
pub fn new(client: Arc<dyn SecretStoreChain>, contract_address_source: Option<ContractAddress>, self_key_pair: Arc<dyn SigningKeyPair>, auto_migrate_enabled: bool, key_servers: BTreeMap<Public, NodeAddress>) -> Result<Self, Error> {
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<H256>, retracted: Vec<H256>) {
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<F1: Fn() -> H256, F2: Fn(H256) -> Option<u64>>
snapshot.new_set = future_new_set.new_set;
}
fn update_last_transaction_block(client: &Client, migration_id: &H256, previous_transaction: &mut Option<PreviousMigrationTransaction>) -> bool {
fn update_last_transaction_block(client: &dyn SecretStoreChain, migration_id: &H256, previous_transaction: &mut Option<PreviousMigrationTransaction>) -> 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<u64> {
fn block_confirmations(client: &dyn SecretStoreChain, block: H256) -> Option<u64> {
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)

View File

@ -15,12 +15,7 @@
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
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<Arc<dyn KeyValueDB>, String> {
@ -109,10 +93,9 @@ pub fn open_secretstore_db(data_path: &str) -> Result<Arc<dyn KeyValueDB>, Strin
}
/// Start new key server instance
pub fn start(client: Arc<Client>, sync: Arc<dyn SyncProvider>, miner: Arc<Miner>, self_key_pair: Arc<dyn NodeKeyPair>, mut config: ServiceConfiguration,
pub fn start(trusted_client: Arc<dyn SecretStoreChain>, self_key_pair: Arc<dyn SigningKeyPair>, mut config: ServiceConfiguration,
db: Arc<dyn KeyValueDB>, executor: Executor) -> Result<Box<dyn KeyServer>, Error>
{
let trusted_client = trusted_client::TrustedClient::new(self_key_pair.clone(), client.clone(), sync, miner);
let acl_storage: Arc<dyn acl_storage::AclStorage> = 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<Client>, sync: Arc<dyn SyncProvider>, miner: Arc<Miner>
key_storage: key_storage,
}
)?;
client.add_notify(listener.clone());
trusted_client.add_listener(listener.clone());
listener
}),
None => None,

View File

@ -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<dyn SecretStoreChain>,
/// This node key pair.
self_key_pair: Arc<dyn NodeKeyPair>,
self_key_pair: Arc<dyn SigningKeyPair>,
/// 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<dyn NodeKeyPair>) -> Self {
pub fn new(mask: ApiMask, client: Arc<dyn SecretStoreChain>, name: String, address_source: ContractAddress, self_key_pair: Arc<dyn SigningKeyPair>) -> Self {
let contract = OnChainServiceContract {
mask: mask,
client: client,
@ -157,24 +151,23 @@ impl OnChainServiceContract {
/// Send transaction to the service contract.
fn send_contract_transaction<C, P>(&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<Bytes, String> {
where C: FnOnce(&dyn SecretStoreChain, &Address, &ServerKeyId, &Address) -> bool,
P: FnOnce(&dyn SecretStoreChain, &Address) -> Result<Bytes, String> {
// 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<U256, String>,
R: 'static + Fn(&dyn NodeKeyPair, &Client, &Address, &BlockId, U256) -> Result<(bool, ServiceTask), String>
>(&self, client: Arc<Client>, contract_address: &Address, block: &BlockId, get_count: C, read_item: R) -> Box<dyn Iterator<Item=(bool, ServiceTask)>> {
C: 'static + Fn(&dyn SecretStoreChain, &Address, &BlockId) -> Result<U256, String>,
R: 'static + Fn(&dyn SigningKeyPair, &dyn SecretStoreChain, &Address, &BlockId, U256) -> Result<(bool, ServiceTask), String>
>(&self, client: Arc<dyn SecretStoreChain>, contract_address: &Address, block: &BlockId, get_count: C, read_item: R) -> Box<dyn Iterator<Item=(bool, ServiceTask)>> {
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<dyn Iterator<Item=ServiceTask>> {
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<dyn Iterator<Item=(bool, ServiceTask)>> {
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<dyn Iterator<Item=(bool, ServiceTask)>>,
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<U256, String> {
fn read_pending_requests_count(client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId) -> Result<U256, String> {
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<U256, String> {
fn read_pending_requests_count(client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId) -> Result<U256, String> {
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<U256, String> {
fn read_pending_requests_count(client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId) -> Result<U256, String> {
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<Bytes, String> {
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<Bytes, String> {
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<U256, String> {
fn read_pending_requests_count(client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId) -> Result<U256, String> {
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<u8, String> {
fn map_key_server_address(client: &dyn SecretStoreChain, contract_address: &Address, key_server: Address) -> Result<u8, String> {
// 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)?)

View File

@ -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<dyn ServiceContract>,
/// This node key pair.
pub self_key_pair: Arc<dyn NodeKeyPair>,
pub self_key_pair: Arc<dyn SigningKeyPair>,
/// Key servers set.
pub key_server_set: Arc<dyn KeyServerSet>,
/// ACL storage reference.
@ -90,7 +89,7 @@ struct ServiceContractListenerData {
/// Cluster client reference.
pub cluster: Arc<dyn ClusterClient>,
/// This node key pair.
pub self_key_pair: Arc<dyn NodeKeyPair>,
pub self_key_pair: Arc<dyn SigningKeyPair>,
/// Key servers set.
pub key_server_set: Arc<dyn KeyServerSet>,
/// 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;

View File

@ -14,10 +14,9 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
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<Signature, EthKeyError> {
sign(self.key_pair.secret(), data)
}
fn compute_shared_key(&self, peer_public: &Public) -> Result<KeyPair, EthKeyError> {
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<AccountProvider>,
address: Address,
public: Public,
password: Password,
}
impl KeyStoreNodeKeyPair {
pub fn new(account_provider: Arc<AccountProvider>, address: Address, password: Password) -> Result<Self, EthKeyError> {
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<Signature, EthKeyError> {
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, EthKeyError> {
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;

View File

@ -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<Signature, EthKeyError>;
/// Compute shared key to encrypt channel between two nodes.
fn compute_shared_key(&self, peer_public: &Public) -> Result<KeyPair, EthKeyError>;
}
/// Server key (SK) generator.
pub trait ServerKeyGenerator {
/// Generate new SK.

View File

@ -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 <http://www.gnu.org/licenses/>.
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<dyn NodeKeyPair>,
/// Blockchain client.
client: Weak<Client>,
/// Sync provider.
sync: Weak<dyn SyncProvider>,
/// Miner service.
miner: Weak<Miner>,
}
impl TrustedClient {
/// Create new trusted client.
pub fn new(self_key_pair: Arc<dyn NodeKeyPair>, client: Arc<Client>, sync: Arc<dyn SyncProvider>, miner: Arc<Miner>) -> 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<Arc<Client>> {
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<Arc<Client>> {
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<Address> {
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)
})
),
}
}
}

View File

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