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
// 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 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<Client>,
/// Sync provider.
sync: Weak<SyncProvider>,
client: TrustedClient,
/// Contract address.
contract_addr: Option<Address>,
/// Contract at given address.
@ -60,12 +58,15 @@ pub struct DummyAclStorage {
}
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 {
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<Client>, sync: &Arc<SyncProvider>) -> 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<bool, Error> {
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()))
}
}
}

View File

@ -14,20 +14,20 @@
// 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 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<Client>,
/// Sync provider.
sync: Weak<SyncProvider>,
client: TrustedClient,
/// Contract address.
contract_addr: Option<Address>,
/// Active set of key servers.
@ -66,19 +64,14 @@ struct CachedContract {
}
impl OnChainKeyServerSet {
pub fn new(client: &Arc<Client>, sync: &Arc<SyncProvider>, key_servers: BTreeMap<Public, NodeAddress>) -> Result<Arc<Self>, 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<Public, NodeAddress>) -> Result<Arc<Self>, 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<Client>, sync: &Arc<SyncProvider>, key_servers: BTreeMap<Public, NodeAddress>) -> Result<Self, Error> {
Ok(CachedContract {
client: Arc::downgrade(client),
sync: Arc::downgrade(sync),
pub fn new(client: TrustedClient, key_servers: BTreeMap<Public, NodeAddress>) -> Result<Self, Error> {
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::<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>) {
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

View File

@ -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<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 {
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<Client>, sync: Arc<SyncProvider>, self_key_pair: Arc<No
None => 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(),

View File

@ -14,18 +14,18 @@
// 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 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<Client>,
/// Sync provider.
sync: Weak<SyncProvider>,
client: TrustedClient,
/// This node key pair.
self_key_pair: Arc<NodeKeyPair>,
/// Contract addresss.
@ -82,14 +80,14 @@ struct PendingRequestsIterator {
impl OnChainServiceContract {
/// 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 {
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<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,
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<Iterator<Item=(bool, ServiceTask)>> {
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

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()
}
}