From d7650e2b9c43de7d9916e0fa56f0849b706d3696 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 20 Dec 2017 16:02:21 +0300 Subject: [PATCH] SecretStore: TrustedClient --- secret_store/src/acl_storage.rs | 62 +++++++++---------- secret_store/src/key_server_set.rs | 52 ++++++++-------- secret_store/src/lib.rs | 8 ++- secret_store/src/listener/service_contract.rs | 41 +++++------- secret_store/src/trusted_client.rs | 57 +++++++++++++++++ 5 files changed, 131 insertions(+), 89 deletions(-) create mode 100644 secret_store/src/trusted_client.rs diff --git a/secret_store/src/acl_storage.rs b/secret_store/src/acl_storage.rs index 8d629f022..e5637f81c 100644 --- a/secret_store/src/acl_storage.rs +++ b/secret_store/src/acl_storage.rs @@ -14,17 +14,17 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::sync::{Arc, Weak}; +use std::sync::Arc; use std::collections::{HashMap, HashSet}; use futures::{future, Future}; use parking_lot::{Mutex, RwLock}; use ethkey::public_to_address; -use ethcore::client::{Client, BlockChainClient, BlockId, ChainNotify}; -use ethsync::SyncProvider; +use ethcore::client::{BlockChainClient, BlockId, ChainNotify}; use native_contracts::SecretStoreAclStorage; use bigint::hash::H256; use util::Address; use bytes::Bytes; +use trusted_client::TrustedClient; use types::all::{Error, ServerKeyId, Public}; const ACL_CHECKER_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_acl_checker"; @@ -44,9 +44,7 @@ pub struct OnChainAclStorage { /// Cached on-chain ACL storage contract. struct CachedContract { /// Blockchain client. - client: Weak, - /// Sync provider. - sync: Weak, + client: TrustedClient, /// Contract address. contract_addr: Option
, /// Contract at given address. @@ -60,12 +58,15 @@ pub struct DummyAclStorage { } impl OnChainAclStorage { - pub fn new(client: &Arc, sync: &Arc) -> Arc { + pub fn new(trusted_client: TrustedClient) -> Result, Error> { + let client = trusted_client.get_untrusted(); let acl_storage = Arc::new(OnChainAclStorage { - contract: Mutex::new(CachedContract::new(client, sync)), + contract: Mutex::new(CachedContract::new(trusted_client)), }); - client.add_notify(acl_storage.clone()); - acl_storage + client + .ok_or(Error::Internal("Constructing OnChainAclStorage without active Client".into()))? + .add_notify(acl_storage.clone()); + Ok(acl_storage) } } @@ -84,17 +85,16 @@ impl ChainNotify for OnChainAclStorage { } impl CachedContract { - pub fn new(client: &Arc, sync: &Arc) -> Self { + pub fn new(client: TrustedClient) -> Self { CachedContract { - client: Arc::downgrade(client), - sync: Arc::downgrade(sync), + client: client, contract_addr: None, contract: None, } } pub fn update(&mut self) { - if let Some(client) = self.client.upgrade() { + if let Some(client) = self.client.get() { let new_contract_addr = client.registry_address(ACL_CHECKER_CONTRACT_REGISTRY_NAME.to_owned()); if self.contract_addr.as_ref() != new_contract_addr.as_ref() { self.contract = new_contract_addr.map(|contract_addr| { @@ -109,26 +109,20 @@ impl CachedContract { } pub fn check(&mut self, public: &Public, document: &ServerKeyId) -> Result { - match (self.client.upgrade(), self.sync.upgrade()) { - (Some(client), Some(sync)) => { - // we can not tell if access to document is allowed until fully synchronized - if sync.status().is_syncing(client.queue_info()) { - return Err(Error::Internal("Trying to check access by non-synchronized client".to_owned())); - } - - // call contract to check accesss - match self.contract.as_ref() { - Some(contract) => { - let address = public_to_address(&public); - let do_call = |a, d| future::done(client.call_contract(BlockId::Latest, a, d)); - contract.check_permissions(do_call, address, document.clone()) - .map_err(|err| Error::Internal(err)) - .wait() - }, - None => Err(Error::Internal("ACL checker contract is not configured".to_owned())), - } - }, - _ => Err(Error::Internal("Calling ACL contract without client".into())), + if let Some(client) = self.client.get() { + // call contract to check accesss + match self.contract.as_ref() { + Some(contract) => { + let address = public_to_address(&public); + let do_call = |a, d| future::done(client.call_contract(BlockId::Latest, a, d)); + contract.check_permissions(do_call, address, document.clone()) + .map_err(|err| Error::Internal(err)) + .wait() + }, + None => Err(Error::Internal("ACL checker contract is not configured".to_owned())), + } + } else { + Err(Error::Internal("Calling ACL contract without trusted blockchain client".into())) } } } diff --git a/secret_store/src/key_server_set.rs b/secret_store/src/key_server_set.rs index 04647db41..e64123a7f 100644 --- a/secret_store/src/key_server_set.rs +++ b/secret_store/src/key_server_set.rs @@ -14,20 +14,20 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::sync::{Arc, Weak}; +use std::sync::Arc; use std::net::SocketAddr; use std::collections::BTreeMap; use futures::{future, Future}; use parking_lot::Mutex; use ethcore::filter::Filter; use ethcore::client::{Client, BlockChainClient, BlockId, ChainNotify}; -use ethsync::SyncProvider; use native_contracts::KeyServerSet as KeyServerSetContract; use hash::keccak; use bigint::hash::H256; use util::Address; use bytes::Bytes; use types::all::{Error, Public, NodeAddress}; +use trusted_client::TrustedClient; const KEY_SERVER_SET_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_server_set"; @@ -56,9 +56,7 @@ pub struct OnChainKeyServerSet { /// Cached on-chain Key Server set contract. struct CachedContract { /// Blockchain client. - client: Weak, - /// Sync provider. - sync: Weak, + client: TrustedClient, /// Contract address. contract_addr: Option
, /// Active set of key servers. @@ -66,19 +64,14 @@ struct CachedContract { } impl OnChainKeyServerSet { - pub fn new(client: &Arc, sync: &Arc, key_servers: BTreeMap) -> Result, Error> { - let mut cached_contract = CachedContract::new(client, sync, key_servers)?; - let key_server_contract_address = client.registry_address(KEY_SERVER_SET_CONTRACT_REGISTRY_NAME.to_owned()); - // only initialize from contract if it is installed. otherwise - use default nodes - // once the contract is installed, all default nodes are lost (if not in the contract' set) - if key_server_contract_address.is_some() { - cached_contract.read_from_registry(&*client, key_server_contract_address); - } - + pub fn new(trusted_client: TrustedClient, key_servers: BTreeMap) -> Result, Error> { + let client = trusted_client.get_untrusted(); let key_server_set = Arc::new(OnChainKeyServerSet { - contract: Mutex::new(cached_contract), + contract: Mutex::new(CachedContract::new(trusted_client, key_servers)?), }); - client.add_notify(key_server_set.clone()); + client + .ok_or(Error::Internal("Constructing OnChainKeyServerSet without active Client".into()))? + .add_notify(key_server_set.clone()); Ok(key_server_set) } } @@ -98,10 +91,9 @@ impl ChainNotify for OnChainKeyServerSet { } impl CachedContract { - pub fn new(client: &Arc, sync: &Arc, key_servers: BTreeMap) -> Result { - Ok(CachedContract { - client: Arc::downgrade(client), - sync: Arc::downgrade(sync), + pub fn new(client: TrustedClient, key_servers: BTreeMap) -> Result { + let mut cached_contract = CachedContract { + client: client, contract_addr: None, key_servers: key_servers.into_iter() .map(|(p, addr)| { @@ -110,16 +102,22 @@ impl CachedContract { Ok((p, addr)) }) .collect::, Error>>()?, - }) + }; + + if let Some(client) = cached_contract.client.get() { + let key_server_contract_address = client.registry_address(KEY_SERVER_SET_CONTRACT_REGISTRY_NAME.to_owned()); + // only initialize from contract if it is installed. otherwise - use default nodes + // once the contract is installed, all default nodes are lost (if not in the contract' set) + if key_server_contract_address.is_some() { + cached_contract.read_from_registry(&*client, key_server_contract_address); + } + } + + Ok(cached_contract) } pub fn update(&mut self, enacted: Vec, retracted: Vec) { - if let (Some(client), Some(sync)) = (self.client.upgrade(), self.sync.upgrade()) { - // do not update initial server set until fully synchronized - if sync.status().is_syncing(client.queue_info()) { - return; - } - + if let Some(client) = self.client.get() { let new_contract_addr = client.registry_address(KEY_SERVER_SET_CONTRACT_REGISTRY_NAME.to_owned()); // new contract installed => read nodes set from the contract diff --git a/secret_store/src/lib.rs b/secret_store/src/lib.rs index b2139c000..d2eb7b125 100644 --- a/secret_store/src/lib.rs +++ b/secret_store/src/lib.rs @@ -60,6 +60,7 @@ mod serialization; mod key_server_set; mod node_key_pair; mod listener; +mod trusted_client; use std::sync::Arc; use ethcore::client::Client; @@ -72,12 +73,13 @@ pub use self::node_key_pair::{PlainNodeKeyPair, KeyStoreNodeKeyPair}; /// Start new key server instance pub fn start(client: Arc, sync: Arc, self_key_pair: Arc, config: ServiceConfiguration) -> Result, Error> { + let trusted_client = trusted_client::TrustedClient::new(client.clone(), sync); let acl_storage: Arc = if config.acl_check_enabled { - acl_storage::OnChainAclStorage::new(&client, &sync) + acl_storage::OnChainAclStorage::new(trusted_client.clone())? } else { Arc::new(acl_storage::DummyAclStorage::default()) }; - let key_server_set = key_server_set::OnChainKeyServerSet::new(&client, &sync, config.cluster_config.nodes.clone())?; + let key_server_set = key_server_set::OnChainKeyServerSet::new(trusted_client.clone(), config.cluster_config.nodes.clone())?; let key_storage = Arc::new(key_storage::PersistentKeyStorage::new(&config)?); let key_server = Arc::new(key_server::KeyServerImpl::new(&config.cluster_config, key_server_set.clone(), self_key_pair.clone(), acl_storage, key_storage.clone())?); let cluster = key_server.cluster(); @@ -88,7 +90,7 @@ pub fn start(client: Arc, sync: Arc, self_key_pair: Arc None, }; let contract_listener = config.service_contract_address.map(|service_contract_address| { - let service_contract = Arc::new(listener::service_contract::OnChainServiceContract::new(&client, &sync, service_contract_address, self_key_pair.clone())); + let service_contract = Arc::new(listener::service_contract::OnChainServiceContract::new(trusted_client, service_contract_address, self_key_pair.clone())); let contract_listener = listener::service_contract_listener::ServiceContractListener::new(listener::service_contract_listener::ServiceContractListenerParams { contract: service_contract, key_server: key_server.clone(), diff --git a/secret_store/src/listener/service_contract.rs b/secret_store/src/listener/service_contract.rs index 7330571f6..0104836ac 100644 --- a/secret_store/src/listener/service_contract.rs +++ b/secret_store/src/listener/service_contract.rs @@ -14,18 +14,18 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::sync::{Arc, Weak}; +use std::sync::Arc; use futures::{future, Future}; use parking_lot::RwLock; use ethcore::filter::Filter; use ethcore::client::{Client, BlockChainClient, BlockId}; use ethkey::{Public, Signature, public_to_address}; -use ethsync::SyncProvider; use native_contracts::SecretStoreService; use hash::keccak; use bigint::hash::H256; use bigint::prelude::U256; use listener::service_contract_listener::ServiceTask; +use trusted_client::TrustedClient; use {ServerKeyId, NodeKeyPair, ContractAddress}; /// Name of the SecretStore contract in the registry. @@ -55,9 +55,7 @@ pub trait ServiceContract: Send + Sync { /// On-chain service contract. pub struct OnChainServiceContract { /// Blockchain client. - client: Weak, - /// Sync provider. - sync: Weak, + client: TrustedClient, /// This node key pair. self_key_pair: Arc, /// Contract addresss. @@ -82,14 +80,14 @@ struct PendingRequestsIterator { impl OnChainServiceContract { /// Create new on-chain service contract. - pub fn new(client: &Arc, sync: &Arc, address: ContractAddress, self_key_pair: Arc) -> Self { + pub fn new(client: TrustedClient, address: ContractAddress, self_key_pair: Arc) -> Self { let contract_addr = match address { - ContractAddress::Registry => client.registry_address(SERVICE_CONTRACT_REGISTRY_NAME.to_owned()) + ContractAddress::Registry => client.get().and_then(|c| c.registry_address(SERVICE_CONTRACT_REGISTRY_NAME.to_owned()) .map(|address| { trace!(target: "secretstore", "{}: installing service contract from address {}", self_key_pair.public(), address); address - }) + })) .unwrap_or_default(), ContractAddress::Address(ref address) => { trace!(target: "secretstore", "{}: installing service contract from address {}", @@ -99,8 +97,7 @@ impl OnChainServiceContract { }; OnChainServiceContract { - client: Arc::downgrade(client), - sync: Arc::downgrade(sync), + client: client, self_key_pair: self_key_pair, address: address, contract: RwLock::new(Arc::new(SecretStoreService::new(contract_addr))), @@ -111,12 +108,7 @@ impl OnChainServiceContract { impl ServiceContract for OnChainServiceContract { fn update(&self) { if let &ContractAddress::Registry = &self.address { - if let (Some(client), Some(sync)) = (self.client.upgrade(), self.sync.upgrade()) { - // do nothing until synced - if sync.status().is_syncing(client.queue_info()) { - return; - } - + if let Some(client) = self.client.get() { // update contract address from registry let service_contract_addr = client.registry_address(SERVICE_CONTRACT_REGISTRY_NAME.to_owned()).unwrap_or_default(); if self.contract.read().address != service_contract_addr { @@ -130,14 +122,12 @@ impl ServiceContract for OnChainServiceContract { fn is_actual(&self) -> bool { self.contract.read().address != Default::default() - && match (self.client.upgrade(), self.sync.upgrade()) { - (Some(client), Some(sync)) => !sync.status().is_syncing(client.queue_info()), - _ => false, - } + && self.client.get().is_some() } fn read_logs(&self, first_block: H256, last_block: H256) -> Box>> { - let client = match self.client.upgrade() { + // already bound to specific blocks => do not care about trust here + let client = match self.client.get_untrusted() { Some(client) => client, None => { warn!(target: "secretstore", "{}: client is offline during read_pending_requests call", @@ -165,10 +155,10 @@ impl ServiceContract for OnChainServiceContract { } fn read_pending_requests(&self) -> Box> { - let client = match self.client.upgrade() { + let client = match self.client.get() { Some(client) => client, None => { - warn!(target: "secretstore", "{}: client is offline during read_pending_requests call", + warn!(target: "secretstore", "{}: client is untrusted during read_pending_requests call", self.self_key_pair.public()); return Box::new(::std::iter::empty()); }, @@ -205,9 +195,10 @@ impl ServiceContract for OnChainServiceContract { // it is not an error, because key could be generated even without contract return Ok(()); } - let client = match self.client.upgrade() { + + let client = match self.client.get() { Some(client) => client, - None => return Err("client is required to publish key".into()), + None => return Err("trusted client is required to publish key".into()), }; // only publish key if contract waits for publication diff --git a/secret_store/src/trusted_client.rs b/secret_store/src/trusted_client.rs new file mode 100644 index 000000000..688e4099d --- /dev/null +++ b/secret_store/src/trusted_client.rs @@ -0,0 +1,57 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::sync::{Arc, Weak}; +use ethcore::client::{Client, BlockChainClient}; +use ethsync::SyncProvider; + +#[derive(Clone)] +/// 'Trusted' client weak reference. +pub struct TrustedClient { + /// Blockchain client. + client: Weak, + /// Sync provider. + sync: Weak, +} + +impl TrustedClient { + /// Create new trusted client. + pub fn new(client: Arc, sync: Arc) -> Self { + TrustedClient { + client: Arc::downgrade(&client), + sync: Arc::downgrade(&sync), + } + } + + /// 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.status().is_syncing(client.queue_info()); + 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() + } +}