SecretStore: Kovan integration initial commit
This commit is contained in:
parent
361debd277
commit
abfb9fccd3
@ -26,6 +26,7 @@ const REGISTRY_ABI: &'static str = include_str!("res/registrar.json");
|
||||
const URLHINT_ABI: &'static str = include_str!("res/urlhint.json");
|
||||
const SERVICE_TRANSACTION_ABI: &'static str = include_str!("res/service_transaction.json");
|
||||
const SECRETSTORE_ACL_STORAGE_ABI: &'static str = include_str!("res/secretstore_acl_storage.json");
|
||||
const SECRETSTORE_SERVICE_ABI: &'static str = include_str!("res/secretstore_service.json");
|
||||
const VALIDATOR_SET_ABI: &'static str = include_str!("res/validator_set.json");
|
||||
const VALIDATOR_REPORT_ABI: &'static str = include_str!("res/validator_report.json");
|
||||
const PEER_SET_ABI: &'static str = include_str!("res/peer_set.json");
|
||||
@ -53,6 +54,7 @@ fn main() {
|
||||
build_file("Urlhint", URLHINT_ABI, "urlhint.rs");
|
||||
build_file("ServiceTransactionChecker", SERVICE_TRANSACTION_ABI, "service_transaction.rs");
|
||||
build_file("SecretStoreAclStorage", SECRETSTORE_ACL_STORAGE_ABI, "secretstore_acl_storage.rs");
|
||||
build_file("SecretStoreService", SECRETSTORE_SERVICE_ABI, "secretstore_service.rs");
|
||||
build_file("ValidatorSet", VALIDATOR_SET_ABI, "validator_set.rs");
|
||||
build_file("ValidatorReport", VALIDATOR_REPORT_ABI, "validator_report.rs");
|
||||
build_file("PeerSet", PEER_SET_ABI, "peer_set.rs");
|
||||
|
@ -46,7 +46,7 @@ pub fn generate_module(struct_name: &str, abi: &str) -> Result<String, Error> {
|
||||
Ok(format!(r##"
|
||||
use byteorder::{{BigEndian, ByteOrder}};
|
||||
use futures::{{future, Future, IntoFuture}};
|
||||
use ethabi::{{Contract, Token, Event}};
|
||||
use ethabi::{{Bytes, Contract, Token, Event}};
|
||||
use bigint;
|
||||
|
||||
type BoxFuture<A, B> = Box<Future<Item = A, Error = B> + Send>;
|
||||
@ -96,7 +96,7 @@ fn generate_functions(contract: &Contract) -> Result<String, Error> {
|
||||
let inputs: Vec<_> = function.inputs.iter().map(|i| i.kind.clone()).collect();
|
||||
let outputs: Vec<_> = function.outputs.iter().map(|i| i.kind.clone()).collect();
|
||||
|
||||
let (input_params, to_tokens) = input_params_codegen(&inputs)
|
||||
let (input_params, input_names, to_tokens) = input_params_codegen(&inputs)
|
||||
.map_err(|bad_type| Error::UnsupportedType(name.clone(), bad_type))?;
|
||||
|
||||
let (output_type, decode_outputs) = output_params_codegen(&outputs)
|
||||
@ -113,14 +113,14 @@ pub fn {snake_name}<F, U>(&self, call: F, {params}) -> BoxFuture<{output_type},
|
||||
U: IntoFuture<Item=Vec<u8>, Error=String>,
|
||||
U::Future: Send + 'static
|
||||
{{
|
||||
let call_addr = self.address;
|
||||
let call_future = match self.encode_{snake_name}_input({params_names}) {{
|
||||
Ok(call_data) => (call)(call_addr, call_data),
|
||||
Err(e) => return Box::new(future::err(e)),
|
||||
}};
|
||||
|
||||
let function = self.contract.function(r#"{abi_name}"#)
|
||||
.expect("function existence checked at compile-time; qed").clone();
|
||||
let call_addr = self.address;
|
||||
|
||||
let call_future = match function.encode_input(&{to_tokens}) {{
|
||||
Ok(call_data) => (call)(call_addr, call_data),
|
||||
Err(e) => return Box::new(future::err(format!("Error encoding call: {{:?}}", e))),
|
||||
}};
|
||||
|
||||
Box::new(call_future
|
||||
.into_future()
|
||||
@ -128,12 +128,22 @@ pub fn {snake_name}<F, U>(&self, call: F, {params}) -> BoxFuture<{output_type},
|
||||
.map(Vec::into_iter)
|
||||
.and_then(|mut outputs| {decode_outputs}))
|
||||
}}
|
||||
|
||||
/// Encode "{abi_name}" function arguments.
|
||||
/// Arguments: {abi_inputs:?}
|
||||
pub fn encode_{snake_name}_input(&self, {params}) -> Result<Bytes, String> {{
|
||||
self.contract.function(r#"{abi_name}"#)
|
||||
.expect("function existence checked at compile-time; qed")
|
||||
.encode_input(&{to_tokens})
|
||||
.map_err(|e| format!("Error encoding call: {{:?}}", e))
|
||||
}}
|
||||
"##,
|
||||
abi_name = name,
|
||||
abi_inputs = inputs,
|
||||
abi_outputs = outputs,
|
||||
snake_name = snake_name,
|
||||
params = input_params,
|
||||
params_names = input_names,
|
||||
output_type = output_type,
|
||||
to_tokens = to_tokens,
|
||||
decode_outputs = decode_outputs,
|
||||
@ -145,15 +155,17 @@ pub fn {snake_name}<F, U>(&self, call: F, {params}) -> BoxFuture<{output_type},
|
||||
|
||||
// generate code for params in function signature and turning them into tokens.
|
||||
//
|
||||
// two pieces of code are generated: the first gives input types for the function signature,
|
||||
// and the second gives code to tokenize those inputs.
|
||||
// three pieces of code are generated: the first gives input types for the function signature,
|
||||
// the second one gives input parameter names to pass to another method,
|
||||
// and the third gives code to tokenize those inputs.
|
||||
//
|
||||
// params of form `param_0: type_0, param_1: type_1, ...`
|
||||
// tokenizing code of form `{let mut tokens = Vec::new(); tokens.push({param_X}); tokens }`
|
||||
//
|
||||
// returns any unsupported param type encountered.
|
||||
fn input_params_codegen(inputs: &[ParamType]) -> Result<(String, String), ParamType> {
|
||||
fn input_params_codegen(inputs: &[ParamType]) -> Result<(String, String, String), ParamType> {
|
||||
let mut params = String::new();
|
||||
let mut params_names = String::new();
|
||||
let mut to_tokens = "{ let mut tokens = Vec::new();".to_string();
|
||||
|
||||
for (index, param_type) in inputs.iter().enumerate() {
|
||||
@ -164,11 +176,13 @@ fn input_params_codegen(inputs: &[ParamType]) -> Result<(String, String), ParamT
|
||||
params.push_str(&format!("{}{}: {}, ",
|
||||
if needs_mut { "mut " } else { "" }, param_name, rust_type));
|
||||
|
||||
params_names.push_str(&format!("{}, ", param_name));
|
||||
|
||||
to_tokens.push_str(&format!("tokens.push({{ {} }});", tokenize_code));
|
||||
}
|
||||
|
||||
to_tokens.push_str(" tokens }");
|
||||
Ok((params, to_tokens))
|
||||
Ok((params, params_names, to_tokens))
|
||||
}
|
||||
|
||||
// generate code for outputs of the function and detokenizing them.
|
||||
|
3
ethcore/native_contracts/res/secretstore_service.json
Normal file
3
ethcore/native_contracts/res/secretstore_service.json
Normal file
@ -0,0 +1,3 @@
|
||||
[
|
||||
{"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"serverKeyPublic","type":"bytes"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"}],"name":"serverKeyGenerated","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}
|
||||
]
|
@ -28,6 +28,7 @@ mod registry;
|
||||
mod urlhint;
|
||||
mod service_transaction;
|
||||
mod secretstore_acl_storage;
|
||||
mod secretstore_service;
|
||||
mod validator_set;
|
||||
mod validator_report;
|
||||
mod peer_set;
|
||||
@ -40,6 +41,7 @@ pub use self::registry::Registry;
|
||||
pub use self::urlhint::Urlhint;
|
||||
pub use self::service_transaction::ServiceTransactionChecker;
|
||||
pub use self::secretstore_acl_storage::SecretStoreAclStorage;
|
||||
pub use self::secretstore_service::SecretStoreService;
|
||||
pub use self::validator_set::ValidatorSet;
|
||||
pub use self::validator_report::ValidatorReport;
|
||||
pub use self::peer_set::PeerSet;
|
||||
|
21
ethcore/native_contracts/src/secretstore_service.rs
Normal file
21
ethcore/native_contracts/src/secretstore_service.rs
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#![allow(unused_mut, unused_variables, unused_imports)]
|
||||
|
||||
//! Secret store service contract.
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/secretstore_service.rs"));
|
@ -54,12 +54,12 @@ mod types;
|
||||
|
||||
mod traits;
|
||||
mod acl_storage;
|
||||
mod http_listener;
|
||||
mod key_server;
|
||||
mod key_storage;
|
||||
mod serialization;
|
||||
mod key_server_set;
|
||||
mod node_key_pair;
|
||||
mod listener;
|
||||
|
||||
use std::sync::Arc;
|
||||
use ethcore::client::Client;
|
||||
@ -71,8 +71,6 @@ pub use self::node_key_pair::{PlainNodeKeyPair, KeyStoreNodeKeyPair};
|
||||
|
||||
/// Start new key server instance
|
||||
pub fn start(client: Arc<Client>, self_key_pair: Arc<NodeKeyPair>, config: ServiceConfiguration) -> Result<Box<KeyServer>, Error> {
|
||||
use std::sync::Arc;
|
||||
|
||||
let acl_storage: Arc<acl_storage::AclStorage> = if config.acl_check_enabled {
|
||||
acl_storage::OnChainAclStorage::new(&client)
|
||||
} else {
|
||||
@ -80,7 +78,12 @@ pub fn start(client: Arc<Client>, self_key_pair: Arc<NodeKeyPair>, config: Servi
|
||||
};
|
||||
let key_server_set = key_server_set::OnChainKeyServerSet::new(&client, config.cluster_config.nodes.clone())?;
|
||||
let key_storage = Arc::new(key_storage::PersistentKeyStorage::new(&config)?);
|
||||
let key_server = key_server::KeyServerImpl::new(&config.cluster_config, key_server_set, self_key_pair, acl_storage, key_storage)?;
|
||||
let listener = http_listener::KeyServerHttpListener::start(config.listener_address, key_server)?;
|
||||
let key_server = Arc::new(key_server::KeyServerImpl::new(&config.cluster_config, key_server_set, self_key_pair.clone(), acl_storage, key_storage)?);
|
||||
let http_listener = match config.listener_address {
|
||||
Some(listener_address) => Some(listener::http_listener::KeyServerHttpListener::start(listener_address, key_server.clone())?),
|
||||
None => None,
|
||||
};
|
||||
let contract_listener = listener::service_contract_listener::ServiceContractListener::new(&client, key_server.clone(), self_key_pair);
|
||||
let listener = listener::Listener::new(key_server, Some(http_listener), Some(contract_listener));
|
||||
Ok(Box::new(listener))
|
||||
}
|
||||
|
@ -25,9 +25,9 @@ use serde::Serialize;
|
||||
use serde_json;
|
||||
use url::percent_encoding::percent_decode;
|
||||
|
||||
use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer};
|
||||
use traits::KeyServer;
|
||||
use serialization::{SerializableEncryptedDocumentKeyShadow, SerializableBytes, SerializablePublic};
|
||||
use types::all::{Error, Public, MessageHash, EncryptedMessageSignature, NodeAddress, RequestSignature, ServerKeyId,
|
||||
use types::all::{Error, Public, MessageHash, NodeAddress, RequestSignature, ServerKeyId,
|
||||
EncryptedDocumentKey, EncryptedDocumentKeyShadow};
|
||||
|
||||
/// Key server http-requests listener. Available requests:
|
||||
@ -38,9 +38,9 @@ use types::all::{Error, Public, MessageHash, EncryptedMessageSignature, NodeAddr
|
||||
/// To get document key shadow: GET /shadow/{server_key_id}/{signature}
|
||||
/// To sign message with server key: GET /{server_key_id}/{signature}/{message_hash}
|
||||
|
||||
pub struct KeyServerHttpListener<T: KeyServer + 'static> {
|
||||
http_server: Option<HttpListening>,
|
||||
handler: Arc<KeyServerSharedHttpHandler<T>>,
|
||||
pub struct KeyServerHttpListener {
|
||||
http_server: HttpListening,
|
||||
_handler: Arc<KeyServerSharedHttpHandler>,
|
||||
}
|
||||
|
||||
/// Parsed http request
|
||||
@ -63,77 +63,44 @@ enum Request {
|
||||
}
|
||||
|
||||
/// Cloneable http handler
|
||||
struct KeyServerHttpHandler<T: KeyServer + 'static> {
|
||||
handler: Arc<KeyServerSharedHttpHandler<T>>,
|
||||
struct KeyServerHttpHandler {
|
||||
handler: Arc<KeyServerSharedHttpHandler>,
|
||||
}
|
||||
|
||||
/// Shared http handler
|
||||
struct KeyServerSharedHttpHandler<T: KeyServer + 'static> {
|
||||
key_server: T,
|
||||
struct KeyServerSharedHttpHandler {
|
||||
key_server: Arc<KeyServer>,
|
||||
}
|
||||
|
||||
impl<T> KeyServerHttpListener<T> where T: KeyServer + 'static {
|
||||
impl KeyServerHttpListener {
|
||||
/// Start KeyServer http listener
|
||||
pub fn start(listener_address: Option<NodeAddress>, key_server: T) -> Result<Self, Error> {
|
||||
pub fn start(listener_address: NodeAddress, key_server: Arc<KeyServer>) -> Result<Self, Error> {
|
||||
let shared_handler = Arc::new(KeyServerSharedHttpHandler {
|
||||
key_server: key_server,
|
||||
});
|
||||
|
||||
let http_server = listener_address
|
||||
.map(|listener_address| format!("{}:{}", listener_address.address, listener_address.port))
|
||||
.map(|listener_address| HttpServer::http(&listener_address).expect("cannot start HttpServer"))
|
||||
.map(|http_server| http_server.handle(KeyServerHttpHandler {
|
||||
let listener_address = format!("{}:{}", listener_address.address, listener_address.port);
|
||||
let http_server = HttpServer::http(&listener_address).expect("cannot start HttpServer");
|
||||
let http_server = http_server.handle(KeyServerHttpHandler {
|
||||
handler: shared_handler.clone(),
|
||||
}).expect("cannot start HttpServer"));
|
||||
}).expect("cannot start HttpServer");
|
||||
|
||||
let listener = KeyServerHttpListener {
|
||||
http_server: http_server,
|
||||
handler: shared_handler,
|
||||
_handler: shared_handler,
|
||||
};
|
||||
Ok(listener)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> KeyServer for KeyServerHttpListener<T> where T: KeyServer + 'static {}
|
||||
|
||||
impl<T> ServerKeyGenerator for KeyServerHttpListener<T> where T: KeyServer + 'static {
|
||||
fn generate_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, threshold: usize) -> Result<Public, Error> {
|
||||
self.handler.key_server.generate_key(key_id, signature, threshold)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DocumentKeyServer for KeyServerHttpListener<T> where T: KeyServer + 'static {
|
||||
fn store_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, common_point: Public, encrypted_document_key: Public) -> Result<(), Error> {
|
||||
self.handler.key_server.store_document_key(key_id, signature, common_point, encrypted_document_key)
|
||||
}
|
||||
|
||||
fn generate_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, threshold: usize) -> Result<EncryptedDocumentKey, Error> {
|
||||
self.handler.key_server.generate_document_key(key_id, signature, threshold)
|
||||
}
|
||||
|
||||
fn restore_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature) -> Result<EncryptedDocumentKey, Error> {
|
||||
self.handler.key_server.restore_document_key(key_id, signature)
|
||||
}
|
||||
|
||||
fn restore_document_key_shadow(&self, key_id: &ServerKeyId, signature: &RequestSignature) -> Result<EncryptedDocumentKeyShadow, Error> {
|
||||
self.handler.key_server.restore_document_key_shadow(key_id, signature)
|
||||
}
|
||||
}
|
||||
|
||||
impl <T> MessageSigner for KeyServerHttpListener<T> where T: KeyServer + 'static {
|
||||
fn sign_message(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result<EncryptedMessageSignature, Error> {
|
||||
self.handler.key_server.sign_message(key_id, signature, message)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for KeyServerHttpListener<T> where T: KeyServer + 'static {
|
||||
impl Drop for KeyServerHttpListener {
|
||||
fn drop(&mut self) {
|
||||
// ignore error as we are dropping anyway
|
||||
self.http_server.take().map(|mut s| { let _ = s.close(); });
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> HttpHandler for KeyServerHttpHandler<T> where T: KeyServer + 'static {
|
||||
impl HttpHandler for KeyServerHttpHandler {
|
||||
fn handle(&self, req: HttpRequest, mut res: HttpResponse) {
|
||||
if req.headers.has::<header::Origin>() {
|
||||
warn!(target: "secretstore", "Ignoring {}-request {} with Origin header", req.method, req.uri);
|
||||
@ -310,6 +277,7 @@ fn parse_request(method: &HttpMethod, uri_path: &str) -> Request {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
use hyper::method::Method as HttpMethod;
|
||||
use key_server::tests::DummyKeyServer;
|
||||
use types::all::NodeAddress;
|
||||
@ -317,12 +285,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn http_listener_successfully_drops() {
|
||||
let key_server = DummyKeyServer;
|
||||
let key_server = Arc::new(DummyKeyServer);
|
||||
let address = NodeAddress { address: "127.0.0.1".into(), port: 9000 };
|
||||
let listener = KeyServerHttpListener::start(Some(address), key_server).unwrap();
|
||||
drop(listener);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn parse_request_successful() {
|
||||
// POST /shadow/{server_key_id}/{signature}/{threshold} => generate server key
|
55
secret_store/src/listener/mod.rs
Normal file
55
secret_store/src/listener/mod.rs
Normal file
@ -0,0 +1,55 @@
|
||||
pub mod http_listener;
|
||||
pub mod service_contract_listener;
|
||||
|
||||
use std::sync::Arc;
|
||||
use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer};
|
||||
use types::all::{Error, Public, MessageHash, EncryptedMessageSignature, RequestSignature, ServerKeyId,
|
||||
EncryptedDocumentKey, EncryptedDocumentKeyShadow};
|
||||
|
||||
pub struct Listener {
|
||||
key_server: Arc<KeyServer>,
|
||||
_http: Option<http_listener::KeyServerHttpListener>,
|
||||
_contract: Option<Arc<service_contract_listener::ServiceContractListener>>,
|
||||
}
|
||||
|
||||
impl Listener {
|
||||
pub fn new(key_server: Arc<KeyServer>, http: Option<http_listener::KeyServerHttpListener>, contract: Option<Arc<service_contract_listener::ServiceContractListener>>) -> Self {
|
||||
Self {
|
||||
key_server: key_server,
|
||||
_http: http,
|
||||
_contract: contract,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyServer for Listener {}
|
||||
|
||||
impl ServerKeyGenerator for Listener {
|
||||
fn generate_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, threshold: usize) -> Result<Public, Error> {
|
||||
self.key_server.generate_key(key_id, signature, threshold)
|
||||
}
|
||||
}
|
||||
|
||||
impl DocumentKeyServer for Listener {
|
||||
fn store_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, common_point: Public, encrypted_document_key: Public) -> Result<(), Error> {
|
||||
self.key_server.store_document_key(key_id, signature, common_point, encrypted_document_key)
|
||||
}
|
||||
|
||||
fn generate_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, threshold: usize) -> Result<EncryptedDocumentKey, Error> {
|
||||
self.key_server.generate_document_key(key_id, signature, threshold)
|
||||
}
|
||||
|
||||
fn restore_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature) -> Result<EncryptedDocumentKey, Error> {
|
||||
self.key_server.restore_document_key(key_id, signature)
|
||||
}
|
||||
|
||||
fn restore_document_key_shadow(&self, key_id: &ServerKeyId, signature: &RequestSignature) -> Result<EncryptedDocumentKeyShadow, Error> {
|
||||
self.key_server.restore_document_key_shadow(key_id, signature)
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageSigner for Listener {
|
||||
fn sign_message(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result<EncryptedMessageSignature, Error> {
|
||||
self.key_server.sign_message(key_id, signature, message)
|
||||
}
|
||||
}
|
138
secret_store/src/listener/service_contract_listener.rs
Normal file
138
secret_store/src/listener/service_contract_listener.rs
Normal file
@ -0,0 +1,138 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::sync::{Arc, Weak};
|
||||
use parking_lot::Mutex;
|
||||
use ethcore::filter::Filter;
|
||||
use ethcore::client::{Client, BlockChainClient, BlockId, ChainNotify};
|
||||
use native_contracts::SecretStoreService;
|
||||
use ethkey::{Random, Generator, sign};
|
||||
use bytes::Bytes;
|
||||
use hash::keccak;
|
||||
use bigint::hash::H256;
|
||||
use util::Address;
|
||||
use {NodeKeyPair, KeyServer};
|
||||
|
||||
/// Name of the SecretStore contract in the registry.
|
||||
const SERVICE_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_service";
|
||||
|
||||
/// Key server has been added to the set.
|
||||
const SERVER_KEY_REQUESTED_EVENT_NAME: &'static [u8] = &*b"ServerKeyRequested(bytes32)";
|
||||
|
||||
lazy_static! {
|
||||
static ref SERVER_KEY_REQUESTED_EVENT_NAME_HASH: H256 = keccak(SERVER_KEY_REQUESTED_EVENT_NAME);
|
||||
}
|
||||
|
||||
/// SecretStore <-> Authority connector. Duties:
|
||||
/// 1. Listen for new requests on SecretStore contract
|
||||
/// 2. Redirects requests for key server
|
||||
/// 3. Publishes response on SecretStore contract
|
||||
pub struct ServiceContractListener {
|
||||
/// Cached on-chain contract.
|
||||
contract: Mutex<CachedContract>,
|
||||
}
|
||||
|
||||
/// Cached on-chain Key Server set contract.
|
||||
struct CachedContract {
|
||||
/// Blockchain client.
|
||||
client: Weak<Client>,
|
||||
/// Contract.
|
||||
contract: SecretStoreService,
|
||||
/// Contract address.
|
||||
contract_addr: Option<Address>,
|
||||
/// Key server reference.
|
||||
key_server: Arc<KeyServer>,
|
||||
/// This node key pair.
|
||||
self_key_pair: Arc<NodeKeyPair>,
|
||||
}
|
||||
|
||||
impl ServiceContractListener {
|
||||
pub fn new(client: &Arc<Client>, key_server: Arc<KeyServer>, self_key_pair: Arc<NodeKeyPair>) -> Arc<ServiceContractListener> {
|
||||
let contract = Arc::new(ServiceContractListener {
|
||||
contract: Mutex::new(CachedContract::new(client, key_server, self_key_pair)),
|
||||
});
|
||||
client.add_notify(contract.clone());
|
||||
contract
|
||||
}
|
||||
}
|
||||
|
||||
impl ChainNotify for ServiceContractListener {
|
||||
fn new_blocks(&self, _imported: Vec<H256>, _invalid: Vec<H256>, enacted: Vec<H256>, _retracted: Vec<H256>, _sealed: Vec<H256>, _proposed: Vec<Bytes>, _duration: u64) {
|
||||
if !enacted.is_empty() {
|
||||
self.contract.lock().update(enacted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CachedContract {
|
||||
pub fn new(client: &Arc<Client>, key_server: Arc<KeyServer>, self_key_pair: Arc<NodeKeyPair>) -> Self {
|
||||
CachedContract {
|
||||
client: Arc::downgrade(client),
|
||||
contract: SecretStoreService::new(Default::default()), // we aren't going to call contract => could use default address
|
||||
contract_addr: client.registry_address(SERVICE_CONTRACT_REGISTRY_NAME.to_owned()),
|
||||
key_server: key_server,
|
||||
self_key_pair: self_key_pair,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, enacted: Vec<H256>) {
|
||||
if let Some(client) = self.client.upgrade() {
|
||||
// update contract address
|
||||
self.contract_addr = client.registry_address(SERVICE_CONTRACT_REGISTRY_NAME.to_owned());
|
||||
|
||||
// check for new key requests.
|
||||
// NOTE: If contract is changed, or unregistered && there are several enacted blocks
|
||||
// in single update call, some requests in old contract can be abandoned (we get contract_address from latest block)
|
||||
// && check for requests in this contract for every enacted block.
|
||||
// The opposite is also true (we can process requests of contract, before it actually becames a SS contract).
|
||||
if let Some(contract_addr) = self.contract_addr.as_ref() {
|
||||
// TODO: in case of reorgs we might process requests for free (maybe wait for several confirmations???) && publish keys without request
|
||||
// TODO: in case of reorgs we might publish keys to forked branch (re-submit transaction???)
|
||||
for block in enacted {
|
||||
let request_logs = client.logs(Filter {
|
||||
from_block: BlockId::Hash(block.clone()),
|
||||
to_block: BlockId::Hash(block),
|
||||
address: Some(vec![contract_addr.clone()]),
|
||||
topics: vec![
|
||||
Some(vec![*SERVER_KEY_REQUESTED_EVENT_NAME_HASH]),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
],
|
||||
limit: None,
|
||||
});
|
||||
|
||||
// TODO: it actually should queue tasks to separate thread
|
||||
// + separate thread at the beginning should read all requests from contract
|
||||
// and then start processing logs
|
||||
for request in request_logs {
|
||||
// TODO: check if we are selected to process this request
|
||||
let key_id = request.entry.topics[1];
|
||||
let key = Random.generate().unwrap();
|
||||
let signature = sign(key.secret(), &key_id).unwrap();
|
||||
let server_key = self.key_server.generate_key(&key_id, &signature, 0).unwrap();
|
||||
println!("=== generated key: {:?}", server_key);
|
||||
// publish generated key
|
||||
let server_key_hash = keccak(server_key);
|
||||
let signed_key = self.self_key_pair.sign(&server_key_hash).unwrap();
|
||||
let transaction_data = self.contract.encode_server_key_generated_input(key_id, server_key.to_vec(), signed_key.v(), signed_key.r().into(), signed_key.s().into()).unwrap();
|
||||
client.transact_contract(contract_addr.clone(), transaction_data).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user