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:
parent
5bd6b208af
commit
424b38a8d7
9
Cargo.lock
generated
9
Cargo.lock
generated
@ -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",
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
246
parity/secretstore/blockchain.rs
Normal file
246
parity/secretstore/blockchain.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
59
parity/secretstore/nodekeypair.rs
Normal file
59
parity/secretstore/nodekeypair.rs
Normal 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)))
|
||||
}
|
||||
}
|
@ -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 {
|
@ -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"]
|
||||
|
@ -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())))
|
||||
|
119
secret-store/src/blockchain.rs
Normal file
119
secret-store/src/blockchain.rs
Normal 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>;
|
||||
}
|
@ -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 {
|
||||
|
@ -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};
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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>,
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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(),
|
||||
})))
|
||||
|
@ -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};
|
||||
|
@ -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");
|
||||
|
@ -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>,
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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)?)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
})
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user