SecretStore: TrustedClient

This commit is contained in:
Svyatoslav Nikolsky 2017-12-20 16:02:21 +03:00
parent 794de9f743
commit d7650e2b9c
5 changed files with 131 additions and 89 deletions

View File

@ -14,17 +14,17 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::sync::{Arc, Weak}; use std::sync::Arc;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use futures::{future, Future}; use futures::{future, Future};
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
use ethkey::public_to_address; use ethkey::public_to_address;
use ethcore::client::{Client, BlockChainClient, BlockId, ChainNotify}; use ethcore::client::{BlockChainClient, BlockId, ChainNotify};
use ethsync::SyncProvider;
use native_contracts::SecretStoreAclStorage; use native_contracts::SecretStoreAclStorage;
use bigint::hash::H256; use bigint::hash::H256;
use util::Address; use util::Address;
use bytes::Bytes; use bytes::Bytes;
use trusted_client::TrustedClient;
use types::all::{Error, ServerKeyId, Public}; use types::all::{Error, ServerKeyId, Public};
const ACL_CHECKER_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_acl_checker"; const ACL_CHECKER_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_acl_checker";
@ -44,9 +44,7 @@ pub struct OnChainAclStorage {
/// Cached on-chain ACL storage contract. /// Cached on-chain ACL storage contract.
struct CachedContract { struct CachedContract {
/// Blockchain client. /// Blockchain client.
client: Weak<Client>, client: TrustedClient,
/// Sync provider.
sync: Weak<SyncProvider>,
/// Contract address. /// Contract address.
contract_addr: Option<Address>, contract_addr: Option<Address>,
/// Contract at given address. /// Contract at given address.
@ -60,12 +58,15 @@ pub struct DummyAclStorage {
} }
impl OnChainAclStorage { impl OnChainAclStorage {
pub fn new(client: &Arc<Client>, sync: &Arc<SyncProvider>) -> Arc<Self> { pub fn new(trusted_client: TrustedClient) -> Result<Arc<Self>, Error> {
let client = trusted_client.get_untrusted();
let acl_storage = Arc::new(OnChainAclStorage { 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()); client
acl_storage .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 { impl CachedContract {
pub fn new(client: &Arc<Client>, sync: &Arc<SyncProvider>) -> Self { pub fn new(client: TrustedClient) -> Self {
CachedContract { CachedContract {
client: Arc::downgrade(client), client: client,
sync: Arc::downgrade(sync),
contract_addr: None, contract_addr: None,
contract: None, contract: None,
} }
} }
pub fn update(&mut self) { 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()); 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() { if self.contract_addr.as_ref() != new_contract_addr.as_ref() {
self.contract = new_contract_addr.map(|contract_addr| { self.contract = new_contract_addr.map(|contract_addr| {
@ -109,26 +109,20 @@ impl CachedContract {
} }
pub fn check(&mut self, public: &Public, document: &ServerKeyId) -> Result<bool, Error> { pub fn check(&mut self, public: &Public, document: &ServerKeyId) -> Result<bool, Error> {
match (self.client.upgrade(), self.sync.upgrade()) { if let Some(client) = self.client.get() {
(Some(client), Some(sync)) => { // call contract to check accesss
// we can not tell if access to document is allowed until fully synchronized match self.contract.as_ref() {
if sync.status().is_syncing(client.queue_info()) { Some(contract) => {
return Err(Error::Internal("Trying to check access by non-synchronized client".to_owned())); 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())
// call contract to check accesss .map_err(|err| Error::Internal(err))
match self.contract.as_ref() { .wait()
Some(contract) => { },
let address = public_to_address(&public); None => Err(Error::Internal("ACL checker contract is not configured".to_owned())),
let do_call = |a, d| future::done(client.call_contract(BlockId::Latest, a, d)); }
contract.check_permissions(do_call, address, document.clone()) } else {
.map_err(|err| Error::Internal(err)) Err(Error::Internal("Calling ACL contract without trusted blockchain client".into()))
.wait()
},
None => Err(Error::Internal("ACL checker contract is not configured".to_owned())),
}
},
_ => Err(Error::Internal("Calling ACL contract without client".into())),
} }
} }
} }

View File

@ -14,20 +14,20 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::sync::{Arc, Weak}; use std::sync::Arc;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use futures::{future, Future}; use futures::{future, Future};
use parking_lot::Mutex; use parking_lot::Mutex;
use ethcore::filter::Filter; use ethcore::filter::Filter;
use ethcore::client::{Client, BlockChainClient, BlockId, ChainNotify}; use ethcore::client::{Client, BlockChainClient, BlockId, ChainNotify};
use ethsync::SyncProvider;
use native_contracts::KeyServerSet as KeyServerSetContract; use native_contracts::KeyServerSet as KeyServerSetContract;
use hash::keccak; use hash::keccak;
use bigint::hash::H256; use bigint::hash::H256;
use util::Address; use util::Address;
use bytes::Bytes; use bytes::Bytes;
use types::all::{Error, Public, NodeAddress}; use types::all::{Error, Public, NodeAddress};
use trusted_client::TrustedClient;
const KEY_SERVER_SET_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_server_set"; 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. /// Cached on-chain Key Server set contract.
struct CachedContract { struct CachedContract {
/// Blockchain client. /// Blockchain client.
client: Weak<Client>, client: TrustedClient,
/// Sync provider.
sync: Weak<SyncProvider>,
/// Contract address. /// Contract address.
contract_addr: Option<Address>, contract_addr: Option<Address>,
/// Active set of key servers. /// Active set of key servers.
@ -66,19 +64,14 @@ struct CachedContract {
} }
impl OnChainKeyServerSet { impl OnChainKeyServerSet {
pub fn new(client: &Arc<Client>, sync: &Arc<SyncProvider>, key_servers: BTreeMap<Public, NodeAddress>) -> Result<Arc<Self>, Error> { pub fn new(trusted_client: TrustedClient, key_servers: BTreeMap<Public, NodeAddress>) -> Result<Arc<Self>, Error> {
let mut cached_contract = CachedContract::new(client, sync, key_servers)?; let client = trusted_client.get_untrusted();
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);
}
let key_server_set = Arc::new(OnChainKeyServerSet { 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) Ok(key_server_set)
} }
} }
@ -98,10 +91,9 @@ impl ChainNotify for OnChainKeyServerSet {
} }
impl CachedContract { impl CachedContract {
pub fn new(client: &Arc<Client>, sync: &Arc<SyncProvider>, key_servers: BTreeMap<Public, NodeAddress>) -> Result<Self, Error> { pub fn new(client: TrustedClient, key_servers: BTreeMap<Public, NodeAddress>) -> Result<Self, Error> {
Ok(CachedContract { let mut cached_contract = CachedContract {
client: Arc::downgrade(client), client: client,
sync: Arc::downgrade(sync),
contract_addr: None, contract_addr: None,
key_servers: key_servers.into_iter() key_servers: key_servers.into_iter()
.map(|(p, addr)| { .map(|(p, addr)| {
@ -110,16 +102,22 @@ impl CachedContract {
Ok((p, addr)) Ok((p, addr))
}) })
.collect::<Result<BTreeMap<_, _>, Error>>()?, .collect::<Result<BTreeMap<_, _>, 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<H256>, retracted: Vec<H256>) { pub fn update(&mut self, enacted: Vec<H256>, retracted: Vec<H256>) {
if let (Some(client), Some(sync)) = (self.client.upgrade(), self.sync.upgrade()) { if let Some(client) = self.client.get() {
// do not update initial server set until fully synchronized
if sync.status().is_syncing(client.queue_info()) {
return;
}
let new_contract_addr = client.registry_address(KEY_SERVER_SET_CONTRACT_REGISTRY_NAME.to_owned()); let new_contract_addr = client.registry_address(KEY_SERVER_SET_CONTRACT_REGISTRY_NAME.to_owned());
// new contract installed => read nodes set from the contract // new contract installed => read nodes set from the contract

View File

@ -60,6 +60,7 @@ mod serialization;
mod key_server_set; mod key_server_set;
mod node_key_pair; mod node_key_pair;
mod listener; mod listener;
mod trusted_client;
use std::sync::Arc; use std::sync::Arc;
use ethcore::client::Client; use ethcore::client::Client;
@ -72,12 +73,13 @@ pub use self::node_key_pair::{PlainNodeKeyPair, KeyStoreNodeKeyPair};
/// Start new key server instance /// Start new key server instance
pub fn start(client: Arc<Client>, sync: Arc<SyncProvider>, self_key_pair: Arc<NodeKeyPair>, config: ServiceConfiguration) -> Result<Box<KeyServer>, Error> { pub fn start(client: Arc<Client>, sync: Arc<SyncProvider>, self_key_pair: Arc<NodeKeyPair>, config: ServiceConfiguration) -> Result<Box<KeyServer>, Error> {
let trusted_client = trusted_client::TrustedClient::new(client.clone(), sync);
let acl_storage: Arc<acl_storage::AclStorage> = if config.acl_check_enabled { let acl_storage: Arc<acl_storage::AclStorage> = if config.acl_check_enabled {
acl_storage::OnChainAclStorage::new(&client, &sync) acl_storage::OnChainAclStorage::new(trusted_client.clone())?
} else { } else {
Arc::new(acl_storage::DummyAclStorage::default()) 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_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 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(); let cluster = key_server.cluster();
@ -88,7 +90,7 @@ pub fn start(client: Arc<Client>, sync: Arc<SyncProvider>, self_key_pair: Arc<No
None => None, None => None,
}; };
let contract_listener = config.service_contract_address.map(|service_contract_address| { 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 { let contract_listener = listener::service_contract_listener::ServiceContractListener::new(listener::service_contract_listener::ServiceContractListenerParams {
contract: service_contract, contract: service_contract,
key_server: key_server.clone(), key_server: key_server.clone(),

View File

@ -14,18 +14,18 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::sync::{Arc, Weak}; use std::sync::Arc;
use futures::{future, Future}; use futures::{future, Future};
use parking_lot::RwLock; use parking_lot::RwLock;
use ethcore::filter::Filter; use ethcore::filter::Filter;
use ethcore::client::{Client, BlockChainClient, BlockId}; use ethcore::client::{Client, BlockChainClient, BlockId};
use ethkey::{Public, Signature, public_to_address}; use ethkey::{Public, Signature, public_to_address};
use ethsync::SyncProvider;
use native_contracts::SecretStoreService; use native_contracts::SecretStoreService;
use hash::keccak; use hash::keccak;
use bigint::hash::H256; use bigint::hash::H256;
use bigint::prelude::U256; use bigint::prelude::U256;
use listener::service_contract_listener::ServiceTask; use listener::service_contract_listener::ServiceTask;
use trusted_client::TrustedClient;
use {ServerKeyId, NodeKeyPair, ContractAddress}; use {ServerKeyId, NodeKeyPair, ContractAddress};
/// Name of the SecretStore contract in the registry. /// Name of the SecretStore contract in the registry.
@ -55,9 +55,7 @@ pub trait ServiceContract: Send + Sync {
/// On-chain service contract. /// On-chain service contract.
pub struct OnChainServiceContract { pub struct OnChainServiceContract {
/// Blockchain client. /// Blockchain client.
client: Weak<Client>, client: TrustedClient,
/// Sync provider.
sync: Weak<SyncProvider>,
/// This node key pair. /// This node key pair.
self_key_pair: Arc<NodeKeyPair>, self_key_pair: Arc<NodeKeyPair>,
/// Contract addresss. /// Contract addresss.
@ -82,14 +80,14 @@ struct PendingRequestsIterator {
impl OnChainServiceContract { impl OnChainServiceContract {
/// Create new on-chain service contract. /// Create new on-chain service contract.
pub fn new(client: &Arc<Client>, sync: &Arc<SyncProvider>, address: ContractAddress, self_key_pair: Arc<NodeKeyPair>) -> Self { pub fn new(client: TrustedClient, address: ContractAddress, self_key_pair: Arc<NodeKeyPair>) -> Self {
let contract_addr = match address { 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| { .map(|address| {
trace!(target: "secretstore", "{}: installing service contract from address {}", trace!(target: "secretstore", "{}: installing service contract from address {}",
self_key_pair.public(), address); self_key_pair.public(), address);
address address
}) }))
.unwrap_or_default(), .unwrap_or_default(),
ContractAddress::Address(ref address) => { ContractAddress::Address(ref address) => {
trace!(target: "secretstore", "{}: installing service contract from address {}", trace!(target: "secretstore", "{}: installing service contract from address {}",
@ -99,8 +97,7 @@ impl OnChainServiceContract {
}; };
OnChainServiceContract { OnChainServiceContract {
client: Arc::downgrade(client), client: client,
sync: Arc::downgrade(sync),
self_key_pair: self_key_pair, self_key_pair: self_key_pair,
address: address, address: address,
contract: RwLock::new(Arc::new(SecretStoreService::new(contract_addr))), contract: RwLock::new(Arc::new(SecretStoreService::new(contract_addr))),
@ -111,12 +108,7 @@ impl OnChainServiceContract {
impl ServiceContract for OnChainServiceContract { impl ServiceContract for OnChainServiceContract {
fn update(&self) { fn update(&self) {
if let &ContractAddress::Registry = &self.address { if let &ContractAddress::Registry = &self.address {
if let (Some(client), Some(sync)) = (self.client.upgrade(), self.sync.upgrade()) { if let Some(client) = self.client.get() {
// do nothing until synced
if sync.status().is_syncing(client.queue_info()) {
return;
}
// update contract address from registry // update contract address from registry
let service_contract_addr = client.registry_address(SERVICE_CONTRACT_REGISTRY_NAME.to_owned()).unwrap_or_default(); let service_contract_addr = client.registry_address(SERVICE_CONTRACT_REGISTRY_NAME.to_owned()).unwrap_or_default();
if self.contract.read().address != service_contract_addr { if self.contract.read().address != service_contract_addr {
@ -130,14 +122,12 @@ impl ServiceContract for OnChainServiceContract {
fn is_actual(&self) -> bool { fn is_actual(&self) -> bool {
self.contract.read().address != Default::default() self.contract.read().address != Default::default()
&& match (self.client.upgrade(), self.sync.upgrade()) { && self.client.get().is_some()
(Some(client), Some(sync)) => !sync.status().is_syncing(client.queue_info()),
_ => false,
}
} }
fn read_logs(&self, first_block: H256, last_block: H256) -> Box<Iterator<Item=Vec<H256>>> { fn read_logs(&self, first_block: H256, last_block: H256) -> Box<Iterator<Item=Vec<H256>>> {
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, Some(client) => client,
None => { None => {
warn!(target: "secretstore", "{}: client is offline during read_pending_requests call", 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<Iterator<Item=(bool, ServiceTask)>> { fn read_pending_requests(&self) -> Box<Iterator<Item=(bool, ServiceTask)>> {
let client = match self.client.upgrade() { let client = match self.client.get() {
Some(client) => client, Some(client) => client,
None => { 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()); self.self_key_pair.public());
return Box::new(::std::iter::empty()); 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 // it is not an error, because key could be generated even without contract
return Ok(()); return Ok(());
} }
let client = match self.client.upgrade() {
let client = match self.client.get() {
Some(client) => client, 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 // only publish key if contract waits for publication

View File

@ -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 <http://www.gnu.org/licenses/>.
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<Client>,
/// Sync provider.
sync: Weak<SyncProvider>,
}
impl TrustedClient {
/// Create new trusted client.
pub fn new(client: Arc<Client>, sync: Arc<SyncProvider>) -> 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<Arc<Client>> {
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<Arc<Client>> {
self.client.upgrade()
}
}