870 lines
39 KiB
Rust
870 lines
39 KiB
Rust
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
|
// This file is part of Parity.
|
|
|
|
// Parity is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
|
|
// Parity is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
use std::sync::Arc;
|
|
use parking_lot::RwLock;
|
|
use ethabi::RawLog;
|
|
use ethcore::filter::Filter;
|
|
use ethcore::client::{Client, BlockChainClient, BlockId, CallContract};
|
|
use ethkey::{Public, public_to_address};
|
|
use hash::keccak;
|
|
use bytes::Bytes;
|
|
use ethereum_types::{H256, U256, Address};
|
|
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_contract!(service, "Service", "res/service.json");
|
|
|
|
/// Name of the general SecretStore contract in the registry.
|
|
pub const SERVICE_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_service";
|
|
/// Name of the server key generation SecretStore contract in the registry.
|
|
pub const SRV_KEY_GEN_SERVICE_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_service_srv_gen";
|
|
/// Name of the server key retrieval SecretStore contract in the registry.
|
|
pub const SRV_KEY_RETR_SERVICE_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_service_srv_retr";
|
|
/// Name of the document key store SecretStore contract in the registry.
|
|
pub const DOC_KEY_STORE_SERVICE_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_service_doc_store";
|
|
/// Name of the document key retrieval SecretStore contract in the registry.
|
|
pub const DOC_KEY_SRETR_SERVICE_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_service_doc_sretr";
|
|
|
|
/// Server key generation has been requested.
|
|
const SERVER_KEY_GENERATION_REQUESTED_EVENT_NAME: &'static [u8] = &*b"ServerKeyGenerationRequested(bytes32,address,uint8)";
|
|
/// Server key retrieval has been requested.
|
|
const SERVER_KEY_RETRIEVAL_REQUESTED_EVENT_NAME: &'static [u8] = &*b"ServerKeyRetrievalRequested(bytes32)";
|
|
/// Document key store has been requested.
|
|
const DOCUMENT_KEY_STORE_REQUESTED_EVENT_NAME: &'static [u8] = &*b"DocumentKeyStoreRequested(bytes32,address,bytes,bytes)";
|
|
/// Document key common part retrieval has been requested.
|
|
const DOCUMENT_KEY_COMMON_PART_RETRIEVAL_REQUESTED_EVENT_NAME: &'static [u8] = &*b"DocumentKeyCommonRetrievalRequested(bytes32,address)";
|
|
/// Document key personal part retrieval has been requested.
|
|
const DOCUMENT_KEY_PERSONAL_PART_RETRIEVAL_REQUESTED_EVENT_NAME: &'static [u8] = &*b"DocumentKeyPersonalRetrievalRequested(bytes32,bytes)";
|
|
|
|
lazy_static! {
|
|
pub static ref SERVER_KEY_GENERATION_REQUESTED_EVENT_NAME_HASH: H256 = keccak(SERVER_KEY_GENERATION_REQUESTED_EVENT_NAME);
|
|
pub static ref SERVER_KEY_RETRIEVAL_REQUESTED_EVENT_NAME_HASH: H256 = keccak(SERVER_KEY_RETRIEVAL_REQUESTED_EVENT_NAME);
|
|
pub static ref DOCUMENT_KEY_STORE_REQUESTED_EVENT_NAME_HASH: H256 = keccak(DOCUMENT_KEY_STORE_REQUESTED_EVENT_NAME);
|
|
pub static ref DOCUMENT_KEY_COMMON_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH: H256 = keccak(DOCUMENT_KEY_COMMON_PART_RETRIEVAL_REQUESTED_EVENT_NAME);
|
|
pub static ref DOCUMENT_KEY_PERSONAL_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH: H256 = keccak(DOCUMENT_KEY_PERSONAL_PART_RETRIEVAL_REQUESTED_EVENT_NAME);
|
|
}
|
|
|
|
/// Service contract trait.
|
|
pub trait ServiceContract: Send + Sync {
|
|
/// Update contract when new blocks are enacted. Returns true if contract is installed && up-to-date (i.e. chain is synced).
|
|
fn update(&self) -> bool;
|
|
/// Read recent contract logs. Returns topics of every entry.
|
|
fn read_logs(&self) -> Box<Iterator<Item=ServiceTask>>;
|
|
/// Publish generated key.
|
|
fn read_pending_requests(&self) -> Box<Iterator<Item=(bool, ServiceTask)>>;
|
|
/// Publish generated server key.
|
|
fn publish_generated_server_key(&self, origin: &Address, server_key_id: &ServerKeyId, server_key: Public) -> Result<(), String>;
|
|
/// Publish server key generation error.
|
|
fn publish_server_key_generation_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String>;
|
|
/// Publish retrieved server key.
|
|
fn publish_retrieved_server_key(&self, origin: &Address, server_key_id: &ServerKeyId, server_key: Public, threshold: usize) -> Result<(), String>;
|
|
/// Publish server key retrieval error.
|
|
fn publish_server_key_retrieval_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String>;
|
|
/// Publish stored document key.
|
|
fn publish_stored_document_key(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String>;
|
|
/// Publish document key store error.
|
|
fn publish_document_key_store_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String>;
|
|
/// Publish retrieved document key common.
|
|
fn publish_retrieved_document_key_common(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address, common_point: Public, threshold: usize) -> Result<(), String>;
|
|
/// Publish retrieved document key personal.
|
|
fn publish_retrieved_document_key_personal(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address, participants: &[Address], decrypted_secret: Public, shadow: Bytes) -> Result<(), String>;
|
|
/// Publish document key store error.
|
|
fn publish_document_key_retrieval_error(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address) -> Result<(), String>;
|
|
}
|
|
|
|
/// On-chain service contract.
|
|
pub struct OnChainServiceContract {
|
|
/// Requests mask.
|
|
mask: ApiMask,
|
|
/// Blockchain client.
|
|
client: TrustedClient,
|
|
/// This node key pair.
|
|
self_key_pair: Arc<NodeKeyPair>,
|
|
/// Contract registry name (if any).
|
|
name: String,
|
|
/// Contract address source.
|
|
address_source: ContractAddress,
|
|
/// Contract.
|
|
contract: service::Service,
|
|
/// Contract.
|
|
data: RwLock<ServiceData>,
|
|
}
|
|
|
|
/// On-chain service contract data.
|
|
struct ServiceData {
|
|
/// Current contract address.
|
|
pub contract_address: Option<Address>,
|
|
/// Last block we have read logs from.
|
|
pub last_log_block: Option<H256>,
|
|
}
|
|
|
|
/// Pending requests iterator.
|
|
struct PendingRequestsIterator<F: Fn(U256) -> Option<(bool, ServiceTask)>> {
|
|
/// Pending request read function.
|
|
read_request: F,
|
|
/// Current request index.
|
|
index: U256,
|
|
/// Requests length.
|
|
length: U256,
|
|
}
|
|
|
|
/// Server key generation related functions.
|
|
struct ServerKeyGenerationService;
|
|
/// Server key retrieval related functions.
|
|
struct ServerKeyRetrievalService;
|
|
/// Document key store related functions.
|
|
struct DocumentKeyStoreService;
|
|
/// Document key shadow retrievalrelated functions.
|
|
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<NodeKeyPair>) -> Self {
|
|
let contract = OnChainServiceContract {
|
|
mask: mask,
|
|
client: client,
|
|
self_key_pair: self_key_pair,
|
|
name: name,
|
|
address_source: address_source,
|
|
contract: service::Service::default(),
|
|
data: RwLock::new(ServiceData {
|
|
contract_address: None,
|
|
last_log_block: None,
|
|
}),
|
|
};
|
|
|
|
contract.update_contract_address();
|
|
contract
|
|
}
|
|
|
|
/// 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, &service::Service, &ServerKeyId, &Address) -> bool,
|
|
P: FnOnce(&Client, &Address, &service::Service) -> 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()),
|
|
};
|
|
|
|
// 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, &self.contract, server_key_id, &self_address) {
|
|
return Ok(());
|
|
}
|
|
|
|
// prepare transaction data
|
|
let transaction_data = prepare_tx(&*client, origin, &self.contract)?;
|
|
|
|
// send transaction
|
|
self.client.transact_contract(
|
|
origin.clone(),
|
|
transaction_data
|
|
).map_err(|e| format!("{}", e))?;
|
|
|
|
trace!(target: "secretstore", "{}: transaction {} sent to service contract",
|
|
self.self_key_pair.public(), tx_name);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Create task-specific pending requests iterator.
|
|
fn create_pending_requests_iterator<
|
|
C: 'static + Fn(&Client, &Address, &service::Service, &BlockId) -> Result<U256, String>,
|
|
R: 'static + Fn(&NodeKeyPair, &Client, &Address, &service::Service, &BlockId, U256) -> Result<(bool, ServiceTask), String>
|
|
>(&self, client: Arc<Client>, contract_address: &Address, block: &BlockId, get_count: C, read_item: R) -> Box<Iterator<Item=(bool, ServiceTask)>> {
|
|
let contract = service::Service::default();
|
|
get_count(&*client, contract_address, &contract, block)
|
|
.map(|count| {
|
|
let client = client.clone();
|
|
let self_key_pair = self.self_key_pair.clone();
|
|
let contract_address = contract_address.clone();
|
|
let block = block.clone();
|
|
Box::new(PendingRequestsIterator {
|
|
read_request: move |index| read_item(&*self_key_pair, &*client, &contract_address, &contract, &block, index)
|
|
.map_err(|error| {
|
|
warn!(target: "secretstore", "{}: reading pending request failed: {}",
|
|
self_key_pair.public(), error);
|
|
error
|
|
})
|
|
.ok(),
|
|
index: 0.into(),
|
|
length: count,
|
|
}) as Box<Iterator<Item=(bool, ServiceTask)>>
|
|
})
|
|
.map_err(|error| {
|
|
warn!(target: "secretstore", "{}: creating pending requests iterator failed: {}",
|
|
self.self_key_pair.public(), error);
|
|
error
|
|
})
|
|
.ok()
|
|
.unwrap_or_else(|| Box::new(::std::iter::empty()))
|
|
}
|
|
|
|
/// Update service contract address.
|
|
fn update_contract_address(&self) -> bool {
|
|
let contract_address = self.client.read_contract_address(self.name.clone(), &self.address_source);
|
|
let mut data = self.data.write();
|
|
if contract_address != data.contract_address {
|
|
trace!(target: "secretstore", "{}: installing {} service contract from address {:?}",
|
|
self.self_key_pair.public(), self.name, contract_address);
|
|
|
|
data.contract_address = contract_address;
|
|
}
|
|
|
|
data.contract_address.is_some()
|
|
}
|
|
}
|
|
|
|
impl ServiceContract for OnChainServiceContract {
|
|
fn update(&self) -> bool {
|
|
self.update_contract_address() && self.client.get().is_some()
|
|
}
|
|
|
|
fn read_logs(&self) -> Box<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());
|
|
},
|
|
};
|
|
|
|
// 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),
|
|
address: Some(vec![address]),
|
|
topics: vec![Some(mask_topics(&self.mask))],
|
|
limit: None,
|
|
}).unwrap_or_default();
|
|
|
|
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, &self.contract, raw_log)
|
|
} else if raw_log.topics[0] == *SERVER_KEY_RETRIEVAL_REQUESTED_EVENT_NAME_HASH {
|
|
ServerKeyRetrievalService::parse_log(&address, &self.contract, raw_log)
|
|
} else if raw_log.topics[0] == *DOCUMENT_KEY_STORE_REQUESTED_EVENT_NAME_HASH {
|
|
DocumentKeyStoreService::parse_log(&address, &self.contract, raw_log)
|
|
} else if raw_log.topics[0] == *DOCUMENT_KEY_COMMON_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH {
|
|
DocumentKeyShadowRetrievalService::parse_common_request_log(&address, &self.contract, raw_log)
|
|
} else if raw_log.topics[0] == *DOCUMENT_KEY_PERSONAL_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH {
|
|
DocumentKeyShadowRetrievalService::parse_personal_request_log(&address, &self.contract, raw_log)
|
|
} else {
|
|
Err("unknown type of log entry".into())
|
|
}
|
|
.map_err(|error| {
|
|
warn!(target: "secretstore", "{}: error parsing log entry from service contract: {}",
|
|
self.self_key_pair.public(), error);
|
|
error
|
|
})
|
|
.ok()
|
|
}).collect::<Vec<_>>().into_iter())
|
|
}
|
|
|
|
fn read_pending_requests(&self) -> Box<Iterator<Item=(bool, ServiceTask)>> {
|
|
let client = match self.client.get() {
|
|
Some(client) => client,
|
|
None => 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
|
|
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)
|
|
.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,
|
|
&ServerKeyGenerationService::read_pending_requests_count,
|
|
&ServerKeyGenerationService::read_pending_request)) as Box<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,
|
|
&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,
|
|
&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,
|
|
&DocumentKeyShadowRetrievalService::read_pending_requests_count,
|
|
&DocumentKeyShadowRetrievalService::read_pending_request))),
|
|
false => iter
|
|
};
|
|
|
|
iter
|
|
})
|
|
.unwrap_or_else(|| Box::new(::std::iter::empty()))
|
|
}
|
|
}
|
|
|
|
fn publish_generated_server_key(&self, origin: &Address, server_key_id: &ServerKeyId, server_key: Public) -> Result<(), String> {
|
|
self.send_contract_transaction("publish_generated_server_key", origin, server_key_id, ServerKeyGenerationService::is_response_required,
|
|
|_, _, service| Ok(ServerKeyGenerationService::prepare_pubish_tx_data(service, server_key_id, &server_key)))
|
|
}
|
|
|
|
fn publish_server_key_generation_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> {
|
|
self.send_contract_transaction("publish_server_key_generation_error", origin, server_key_id, ServerKeyGenerationService::is_response_required,
|
|
|_, _, service| Ok(ServerKeyGenerationService::prepare_error_tx_data(service, server_key_id)))
|
|
}
|
|
|
|
fn publish_retrieved_server_key(&self, origin: &Address, server_key_id: &ServerKeyId, server_key: Public, threshold: usize) -> Result<(), String> {
|
|
let threshold = serialize_threshold(threshold)?;
|
|
self.send_contract_transaction("publish_retrieved_server_key", origin, server_key_id, ServerKeyRetrievalService::is_response_required,
|
|
|_, _, service| Ok(ServerKeyRetrievalService::prepare_pubish_tx_data(service, server_key_id, server_key, threshold)))
|
|
}
|
|
|
|
fn publish_server_key_retrieval_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> {
|
|
self.send_contract_transaction("publish_server_key_retrieval_error", origin, server_key_id, ServerKeyRetrievalService::is_response_required,
|
|
|_, _, service| Ok(ServerKeyRetrievalService::prepare_error_tx_data(service, server_key_id)))
|
|
}
|
|
|
|
fn publish_stored_document_key(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> {
|
|
self.send_contract_transaction("publish_stored_document_key", origin, server_key_id, DocumentKeyStoreService::is_response_required,
|
|
|_, _, service| Ok(DocumentKeyStoreService::prepare_pubish_tx_data(service, server_key_id)))
|
|
}
|
|
|
|
fn publish_document_key_store_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> {
|
|
self.send_contract_transaction("publish_document_key_store_error", origin, server_key_id, DocumentKeyStoreService::is_response_required,
|
|
|_, _, service| Ok(DocumentKeyStoreService::prepare_error_tx_data(service, server_key_id)))
|
|
}
|
|
|
|
fn publish_retrieved_document_key_common(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address, common_point: Public, threshold: usize) -> Result<(), String> {
|
|
let threshold = serialize_threshold(threshold)?;
|
|
self.send_contract_transaction("publish_retrieved_document_key_common", origin, server_key_id,
|
|
|client, contract_address, contract, server_key_id, key_server|
|
|
DocumentKeyShadowRetrievalService::is_response_required(client, contract_address, contract, server_key_id, requester, key_server),
|
|
|_, _, service|
|
|
Ok(DocumentKeyShadowRetrievalService::prepare_pubish_common_tx_data(service, server_key_id, requester, common_point, threshold))
|
|
)
|
|
}
|
|
|
|
fn publish_retrieved_document_key_personal(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address, participants: &[Address], decrypted_secret: Public, shadow: Bytes) -> Result<(), String> {
|
|
self.send_contract_transaction("publish_retrieved_document_key_personal", origin, server_key_id, |_, _, _, _, _| true,
|
|
move |client, address, service|
|
|
DocumentKeyShadowRetrievalService::prepare_pubish_personal_tx_data(client, address, service, server_key_id, requester, participants, decrypted_secret, shadow)
|
|
)
|
|
}
|
|
|
|
fn publish_document_key_retrieval_error(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address) -> Result<(), String> {
|
|
self.send_contract_transaction("publish_document_key_retrieval_error", origin, server_key_id,
|
|
|client, contract_address, contract, server_key_id, key_server|
|
|
DocumentKeyShadowRetrievalService::is_response_required(client, contract_address, contract, server_key_id, requester, key_server),
|
|
|_, _, service|
|
|
Ok(DocumentKeyShadowRetrievalService::prepare_error_tx_data(service, server_key_id, requester))
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<F> Iterator for PendingRequestsIterator<F> where F: Fn(U256) -> Option<(bool, ServiceTask)> {
|
|
type Item = (bool, ServiceTask);
|
|
|
|
fn next(&mut self) -> Option<(bool, ServiceTask)> {
|
|
if self.index >= self.length {
|
|
return None;
|
|
}
|
|
|
|
let index = self.index.clone();
|
|
self.index = self.index + 1;
|
|
|
|
(self.read_request)(index)
|
|
}
|
|
}
|
|
|
|
/// Returns vector of logs topics to listen to.
|
|
pub fn mask_topics(mask: &ApiMask) -> Vec<H256> {
|
|
let mut topics = Vec::new();
|
|
if mask.server_key_generation_requests {
|
|
topics.push(*SERVER_KEY_GENERATION_REQUESTED_EVENT_NAME_HASH);
|
|
}
|
|
if mask.server_key_retrieval_requests {
|
|
topics.push(*SERVER_KEY_RETRIEVAL_REQUESTED_EVENT_NAME_HASH);
|
|
}
|
|
if mask.document_key_store_requests {
|
|
topics.push(*DOCUMENT_KEY_STORE_REQUESTED_EVENT_NAME_HASH);
|
|
}
|
|
if mask.document_key_shadow_retrieval_requests {
|
|
topics.push(*DOCUMENT_KEY_COMMON_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH);
|
|
topics.push(*DOCUMENT_KEY_PERSONAL_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH);
|
|
}
|
|
topics
|
|
}
|
|
|
|
impl ServerKeyGenerationService {
|
|
/// Parse request log entry.
|
|
pub fn parse_log(origin: &Address, contract: &service::Service, raw_log: RawLog) -> Result<ServiceTask, String> {
|
|
let event = contract.events().server_key_generation_requested();
|
|
match event.parse_log(raw_log) {
|
|
Ok(l) => Ok(ServiceTask::GenerateServerKey(origin.clone(), l.server_key_id, l.author, parse_threshold(l.threshold)?)),
|
|
Err(e) => Err(format!("{}", e)),
|
|
}
|
|
}
|
|
|
|
/// Check if response from key server is required.
|
|
pub fn is_response_required(client: &Client, contract_address: &Address, contract: &service::Service, 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 do_call = |data| client.call_contract(BlockId::Latest, *contract_address, data);
|
|
contract.functions()
|
|
.is_server_key_generation_response_required()
|
|
.call(*server_key_id, key_server.clone(), &do_call)
|
|
.unwrap_or(true)
|
|
}
|
|
|
|
/// Prepare publish key transaction data.
|
|
pub fn prepare_pubish_tx_data(contract: &service::Service, server_key_id: &ServerKeyId, server_key_public: &Public) -> Bytes {
|
|
contract.functions()
|
|
.server_key_generated()
|
|
.input(*server_key_id, server_key_public.to_vec())
|
|
}
|
|
|
|
/// Prepare error transaction data.
|
|
pub fn prepare_error_tx_data(contract: &service::Service, server_key_id: &ServerKeyId) -> Bytes {
|
|
contract.functions()
|
|
.server_key_generation_error()
|
|
.input(*server_key_id)
|
|
}
|
|
|
|
/// Read pending requests count.
|
|
fn read_pending_requests_count(client: &Client, contract_address: &Address, _contract: &service::Service, block: &BlockId) -> Result<U256, String> {
|
|
let do_call = |data| client.call_contract(block.clone(), contract_address.clone(), data);
|
|
let contract = service::Service::default();
|
|
contract.functions()
|
|
.server_key_generation_requests_count()
|
|
.call(&do_call)
|
|
.map_err(|error| format!("{}", error))
|
|
}
|
|
|
|
/// Read pending request.
|
|
fn read_pending_request(self_key_pair: &NodeKeyPair, client: &Client, contract_address: &Address, contract: &service::Service, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> {
|
|
let self_address = public_to_address(self_key_pair.public());
|
|
let do_call = |d| client.call_contract(block.clone(), contract_address.clone(), d);
|
|
contract.functions()
|
|
.get_server_key_generation_request()
|
|
.call(index, &do_call)
|
|
.map_err(|error| format!("{}", error))
|
|
.and_then(|(server_key_id, author, threshold)| parse_threshold(threshold)
|
|
.map(|threshold| (server_key_id, author, threshold)))
|
|
.and_then(|(server_key_id, author, threshold)| contract.functions()
|
|
.is_server_key_generation_response_required()
|
|
.call(server_key_id.clone(), self_address, &do_call)
|
|
.map(|not_confirmed| (
|
|
not_confirmed,
|
|
ServiceTask::GenerateServerKey(
|
|
contract_address.clone(),
|
|
server_key_id,
|
|
author,
|
|
threshold,
|
|
)))
|
|
.map_err(|error| format!("{}", error)))
|
|
}
|
|
}
|
|
|
|
impl ServerKeyRetrievalService {
|
|
/// Parse request log entry.
|
|
pub fn parse_log(origin: &Address, contract: &service::Service, raw_log: RawLog) -> Result<ServiceTask, String> {
|
|
let event = contract.events().server_key_retrieval_requested();
|
|
match event.parse_log(raw_log) {
|
|
Ok(l) => Ok(ServiceTask::RetrieveServerKey(origin.clone(), l.server_key_id)),
|
|
Err(e) => Err(format!("{}", e)),
|
|
}
|
|
}
|
|
|
|
/// Check if response from key server is required.
|
|
pub fn is_response_required(client: &Client, contract_address: &Address, contract: &service::Service, 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 do_call = |data| client.call_contract(BlockId::Latest, *contract_address, data);
|
|
contract.functions()
|
|
.is_server_key_retrieval_response_required()
|
|
.call(*server_key_id, key_server.clone(), &do_call)
|
|
.unwrap_or(true)
|
|
}
|
|
|
|
/// Prepare publish key transaction data.
|
|
pub fn prepare_pubish_tx_data(contract: &service::Service, server_key_id: &ServerKeyId, server_key_public: Public, threshold: U256) -> Bytes {
|
|
contract.functions()
|
|
.server_key_retrieved()
|
|
.input(*server_key_id, server_key_public.to_vec(), threshold)
|
|
}
|
|
|
|
/// Prepare error transaction data.
|
|
pub fn prepare_error_tx_data(contract: &service::Service, server_key_id: &ServerKeyId) -> Bytes {
|
|
contract.functions()
|
|
.server_key_retrieval_error()
|
|
.input(*server_key_id)
|
|
}
|
|
|
|
/// Read pending requests count.
|
|
fn read_pending_requests_count(client: &Client, contract_address: &Address, _contract: &service::Service, block: &BlockId) -> Result<U256, String> {
|
|
let do_call = |data| client.call_contract(block.clone(), contract_address.clone(), data);
|
|
let contract = service::Service::default();
|
|
contract.functions()
|
|
.server_key_retrieval_requests_count()
|
|
.call(&do_call)
|
|
.map_err(|error| format!("{}", error))
|
|
}
|
|
|
|
/// Read pending request.
|
|
fn read_pending_request(self_key_pair: &NodeKeyPair, client: &Client, contract_address: &Address, contract: &service::Service, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> {
|
|
let self_address = public_to_address(self_key_pair.public());
|
|
let do_call = |d| client.call_contract(block.clone(), contract_address.clone(), d);
|
|
contract.functions()
|
|
.get_server_key_retrieval_request()
|
|
.call(index, &do_call)
|
|
.map_err(|error| format!("{}", error))
|
|
.and_then(|server_key_id| contract.functions()
|
|
.is_server_key_retrieval_response_required()
|
|
.call(server_key_id.clone(), self_address, &do_call)
|
|
.map(|not_confirmed| (
|
|
not_confirmed,
|
|
ServiceTask::RetrieveServerKey(
|
|
contract_address.clone(),
|
|
server_key_id,
|
|
)))
|
|
.map_err(|error| format!("{}", error)))
|
|
}
|
|
}
|
|
|
|
impl DocumentKeyStoreService {
|
|
/// Parse request log entry.
|
|
pub fn parse_log(origin: &Address, contract: &service::Service, raw_log: RawLog) -> Result<ServiceTask, String> {
|
|
let event = contract.events().document_key_store_requested();
|
|
match event.parse_log(raw_log) {
|
|
Ok(l) => Ok(ServiceTask::StoreDocumentKey(origin.clone(), l.server_key_id, l.author, (*l.common_point).into(), (*l.encrypted_point).into())),
|
|
Err(e) => Err(format!("{}", e)),
|
|
}
|
|
}
|
|
|
|
/// Check if response from key server is required.
|
|
pub fn is_response_required(client: &Client, contract_address: &Address, contract: &service::Service, 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 do_call = |data| client.call_contract(BlockId::Latest, *contract_address, data);
|
|
contract.functions()
|
|
.is_document_key_store_response_required()
|
|
.call(*server_key_id, key_server.clone(), &do_call)
|
|
.unwrap_or(true)
|
|
}
|
|
|
|
/// Prepare publish key transaction data.
|
|
pub fn prepare_pubish_tx_data(contract: &service::Service, server_key_id: &ServerKeyId) -> Bytes {
|
|
contract.functions()
|
|
.document_key_stored()
|
|
.input(*server_key_id)
|
|
}
|
|
|
|
/// Prepare error transaction data.
|
|
pub fn prepare_error_tx_data(contract: &service::Service, server_key_id: &ServerKeyId) -> Bytes {
|
|
contract.functions()
|
|
.document_key_store_error()
|
|
.input(*server_key_id)
|
|
}
|
|
|
|
/// Read pending requests count.
|
|
fn read_pending_requests_count(client: &Client, contract_address: &Address, _contract: &service::Service, block: &BlockId) -> Result<U256, String> {
|
|
let do_call = |data| client.call_contract(block.clone(), contract_address.clone(), data);
|
|
let contract = service::Service::default();
|
|
contract.functions()
|
|
.document_key_store_requests_count()
|
|
.call(&do_call)
|
|
.map_err(|error| format!("{}", error))
|
|
}
|
|
|
|
/// Read pending request.
|
|
fn read_pending_request(self_key_pair: &NodeKeyPair, client: &Client, contract_address: &Address, contract: &service::Service, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> {
|
|
let self_address = public_to_address(self_key_pair.public());
|
|
let do_call = |d| client.call_contract(block.clone(), contract_address.clone(), d);
|
|
contract.functions()
|
|
.get_document_key_store_request()
|
|
.call(index, &do_call)
|
|
.map_err(|error| format!("{}", error))
|
|
.and_then(|(server_key_id, author, common_point, encrypted_point)| contract.functions()
|
|
.is_document_key_store_response_required()
|
|
.call(server_key_id.clone(), self_address, &do_call)
|
|
.map(|not_confirmed| (
|
|
not_confirmed,
|
|
ServiceTask::StoreDocumentKey(
|
|
contract_address.clone(),
|
|
server_key_id,
|
|
author,
|
|
Public::from_slice(&common_point),
|
|
Public::from_slice(&encrypted_point),
|
|
)))
|
|
.map_err(|error| format!("{}", error)))
|
|
}
|
|
}
|
|
|
|
impl DocumentKeyShadowRetrievalService {
|
|
/// Parse common request log entry.
|
|
pub fn parse_common_request_log(origin: &Address, contract: &service::Service, raw_log: RawLog) -> Result<ServiceTask, String> {
|
|
let event = contract.events().document_key_common_retrieval_requested();
|
|
match event.parse_log(raw_log) {
|
|
Ok(l) => Ok(ServiceTask::RetrieveShadowDocumentKeyCommon(origin.clone(), l.server_key_id, l.requester)),
|
|
Err(e) => Err(format!("{}", e)),
|
|
}
|
|
}
|
|
|
|
/// Parse personal request log entry.
|
|
pub fn parse_personal_request_log(origin: &Address, contract: &service::Service, raw_log: RawLog) -> Result<ServiceTask, String> {
|
|
let event = contract.events().document_key_personal_retrieval_requested();
|
|
match event.parse_log(raw_log) {
|
|
Ok(l) => Ok(ServiceTask::RetrieveShadowDocumentKeyPersonal(origin.clone(), l.server_key_id, (*l.requester_public).into())),
|
|
Err(e) => Err(format!("{}", e)),
|
|
}
|
|
}
|
|
|
|
/// Check if response from key server is required.
|
|
pub fn is_response_required(client: &Client, contract_address: &Address, contract: &service::Service, 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 do_call = |data| client.call_contract(BlockId::Latest, *contract_address, data);
|
|
contract.functions()
|
|
.is_document_key_shadow_retrieval_response_required()
|
|
.call(*server_key_id, *requester, key_server.clone(), &do_call)
|
|
.unwrap_or(true)
|
|
}
|
|
|
|
/// Prepare publish common key transaction data.
|
|
pub fn prepare_pubish_common_tx_data(contract: &service::Service, server_key_id: &ServerKeyId, requester: &Address, common_point: Public, threshold: U256) -> Bytes {
|
|
contract.functions()
|
|
.document_key_common_retrieved()
|
|
.input(*server_key_id, *requester, common_point.to_vec(), threshold)
|
|
}
|
|
|
|
/// Prepare publish personal key transaction data.
|
|
pub fn prepare_pubish_personal_tx_data(client: &Client, contract_address: &Address, contract: &service::Service, 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, contract, participant.clone())
|
|
.map_err(|e| format!("Error searching for {} participant: {}", participant, e))?;
|
|
participants_mask = participants_mask | (U256::one() << participant_index);
|
|
}
|
|
Ok(contract.functions()
|
|
.document_key_personal_retrieved()
|
|
.input(*server_key_id, *requester, participants_mask, decrypted_secret.to_vec(), shadow))
|
|
}
|
|
|
|
/// Prepare error transaction data.
|
|
pub fn prepare_error_tx_data(contract: &service::Service, server_key_id: &ServerKeyId, requester: &Address) -> Bytes {
|
|
contract.functions()
|
|
.document_key_shadow_retrieval_error()
|
|
.input(*server_key_id, *requester)
|
|
}
|
|
|
|
/// Read pending requests count.
|
|
fn read_pending_requests_count(client: &Client, contract_address: &Address, _contract: &service::Service, block: &BlockId) -> Result<U256, String> {
|
|
let do_call = |data| client.call_contract(block.clone(), contract_address.clone(), data);
|
|
let contract = service::Service::default();
|
|
contract.functions()
|
|
.document_key_shadow_retrieval_requests_count()
|
|
.call(&do_call)
|
|
.map_err(|error| format!("{}", error))
|
|
}
|
|
|
|
/// Read pending request.
|
|
fn read_pending_request(self_key_pair: &NodeKeyPair, client: &Client, contract_address: &Address, contract: &service::Service, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> {
|
|
let self_address = public_to_address(self_key_pair.public());
|
|
let do_call = |d| client.call_contract(block.clone(), contract_address.clone(), d);
|
|
contract.functions()
|
|
.get_document_key_shadow_retrieval_request()
|
|
.call(index, &do_call)
|
|
.map_err(|error| format!("{}", error))
|
|
.and_then(|(server_key_id, requester, is_common_retrieval_completed)| {
|
|
let requester = Public::from_slice(&requester);
|
|
contract.functions()
|
|
.is_document_key_shadow_retrieval_response_required()
|
|
.call(server_key_id.clone(), public_to_address(&requester), self_address, &do_call)
|
|
.map(|not_confirmed| (
|
|
not_confirmed,
|
|
match is_common_retrieval_completed {
|
|
true => ServiceTask::RetrieveShadowDocumentKeyPersonal(
|
|
contract_address.clone(),
|
|
server_key_id,
|
|
requester,
|
|
),
|
|
false => ServiceTask::RetrieveShadowDocumentKeyCommon(
|
|
contract_address.clone(),
|
|
server_key_id,
|
|
public_to_address(&requester),
|
|
),
|
|
},
|
|
))
|
|
.map_err(|error| format!("{}", error))
|
|
})
|
|
}
|
|
|
|
/// Map from key server address to key server index.
|
|
fn map_key_server_address(client: &Client, contract_address: &Address, contract: &service::Service, key_server: Address) -> Result<u8, String> {
|
|
// we're checking confirmation in Latest block, because tx ,ust be appended to the latest state
|
|
let do_call = |data| client.call_contract(BlockId::Latest, *contract_address, data);
|
|
contract.functions()
|
|
.require_key_server()
|
|
.call(key_server, &do_call)
|
|
.map_err(|e| format!("{}", e))
|
|
.and_then(|index| if index > ::std::u8::MAX.into() {
|
|
Err(format!("key server index is too big: {}", index))
|
|
} else {
|
|
let index: u32 = index.into();
|
|
Ok(index as u8)
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Parse threshold (we only supposrt 256 KS at max).
|
|
fn parse_threshold(threshold: U256) -> Result<usize, String> {
|
|
let threshold_num = threshold.low_u64();
|
|
if threshold != threshold_num.into() || threshold_num >= ::std::u8::MAX as u64 {
|
|
return Err(format!("invalid threshold to use in service contract: {}", threshold));
|
|
}
|
|
|
|
Ok(threshold_num as usize)
|
|
}
|
|
|
|
/// Serialize threshold (we only support 256 KS at max).
|
|
fn serialize_threshold(threshold: usize) -> Result<U256, String> {
|
|
if threshold > ::std::u8::MAX as usize {
|
|
return Err(format!("invalid threshold to use in service contract: {}", threshold));
|
|
}
|
|
Ok(threshold.into())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
pub mod tests {
|
|
use parking_lot::Mutex;
|
|
use bytes::Bytes;
|
|
use ethkey::Public;
|
|
use ethereum_types::Address;
|
|
use listener::service_contract_listener::ServiceTask;
|
|
use {ServerKeyId};
|
|
use super::ServiceContract;
|
|
|
|
#[derive(Default)]
|
|
pub struct DummyServiceContract {
|
|
pub is_actual: bool,
|
|
pub logs: Vec<ServiceTask>,
|
|
pub pending_requests: Vec<(bool, ServiceTask)>,
|
|
pub generated_server_keys: Mutex<Vec<(ServerKeyId, Public)>>,
|
|
pub server_keys_generation_failures: Mutex<Vec<ServerKeyId>>,
|
|
pub retrieved_server_keys: Mutex<Vec<(ServerKeyId, Public, usize)>>,
|
|
pub server_keys_retrieval_failures: Mutex<Vec<ServerKeyId>>,
|
|
pub stored_document_keys: Mutex<Vec<ServerKeyId>>,
|
|
pub document_keys_store_failures: Mutex<Vec<ServerKeyId>>,
|
|
pub common_shadow_retrieved_document_keys: Mutex<Vec<(ServerKeyId, Address, Public, usize)>>,
|
|
pub personal_shadow_retrieved_document_keys: Mutex<Vec<(ServerKeyId, Address, Vec<Address>, Public, Bytes)>>,
|
|
pub document_keys_shadow_retrieval_failures: Mutex<Vec<(ServerKeyId, Address)>>,
|
|
}
|
|
|
|
impl ServiceContract for DummyServiceContract {
|
|
fn update(&self) -> bool {
|
|
true
|
|
}
|
|
|
|
fn read_logs(&self) -> Box<Iterator<Item=ServiceTask>> {
|
|
Box::new(self.logs.clone().into_iter())
|
|
}
|
|
|
|
fn read_pending_requests(&self) -> Box<Iterator<Item=(bool, ServiceTask)>> {
|
|
Box::new(self.pending_requests.clone().into_iter())
|
|
}
|
|
|
|
fn publish_generated_server_key(&self, _origin: &Address, server_key_id: &ServerKeyId, server_key: Public) -> Result<(), String> {
|
|
self.generated_server_keys.lock().push((server_key_id.clone(), server_key.clone()));
|
|
Ok(())
|
|
}
|
|
|
|
fn publish_server_key_generation_error(&self, _origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> {
|
|
self.server_keys_generation_failures.lock().push(server_key_id.clone());
|
|
Ok(())
|
|
}
|
|
|
|
fn publish_retrieved_server_key(&self, _origin: &Address, server_key_id: &ServerKeyId, server_key: Public, threshold: usize) -> Result<(), String> {
|
|
self.retrieved_server_keys.lock().push((server_key_id.clone(), server_key.clone(), threshold));
|
|
Ok(())
|
|
}
|
|
|
|
fn publish_server_key_retrieval_error(&self, _origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> {
|
|
self.server_keys_retrieval_failures.lock().push(server_key_id.clone());
|
|
Ok(())
|
|
}
|
|
|
|
fn publish_stored_document_key(&self, _origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> {
|
|
self.stored_document_keys.lock().push(server_key_id.clone());
|
|
Ok(())
|
|
}
|
|
|
|
fn publish_document_key_store_error(&self, _origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> {
|
|
self.document_keys_store_failures.lock().push(server_key_id.clone());
|
|
Ok(())
|
|
}
|
|
|
|
fn publish_retrieved_document_key_common(&self, _origin: &Address, server_key_id: &ServerKeyId, requester: &Address, common_point: Public, threshold: usize) -> Result<(), String> {
|
|
self.common_shadow_retrieved_document_keys.lock().push((server_key_id.clone(), requester.clone(), common_point.clone(), threshold));
|
|
Ok(())
|
|
}
|
|
|
|
fn publish_retrieved_document_key_personal(&self, _origin: &Address, server_key_id: &ServerKeyId, requester: &Address, participants: &[Address], decrypted_secret: Public, shadow: Bytes) -> Result<(), String> {
|
|
self.personal_shadow_retrieved_document_keys.lock().push((server_key_id.clone(), requester.clone(), participants.iter().cloned().collect(), decrypted_secret, shadow));
|
|
Ok(())
|
|
}
|
|
|
|
fn publish_document_key_retrieval_error(&self, _origin: &Address, server_key_id: &ServerKeyId, requester: &Address) -> Result<(), String> {
|
|
self.document_keys_shadow_retrieval_failures.lock().push((server_key_id.clone(), requester.clone()));
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|