ethabi version 5 (#7723)

* Refactor updater to use ethabi-derive

* Grumble: do_call type alias

* Empty commit to trigger test re-run

* migration to ethabi-5.0

* migration to ethabi-5.0 in progress

* use ethabi_deriven to generate TransactAcl contract

* use ethabi_deriven to generate Registry contract

* hash-fetch uses ethabi_derive, removed retain cycle from updater, fixed #7720

* node-filter crate uses ethabi_derive to generate peer_set contract interface

* use LruCache in node-filter instead of HashMap

* validator_set engine uses ethabi_derive

* ethcore does not depend on native_contracts

* miner does no depend on native_contracts

* secret_store does not use native_contracts (in progress)

* removed native-contracts

* ethcore and updater does not depend on futures

* updated ethereum-types

* fixed all warnings caused by using new version of ethereum-types

* updated ethabi_derive && ethabi_contract to get rid of warnings

* removed another retain cycle in updater, fixed following minor version on update

* moved contracts out of native_contracts res

* updated ethabi_contract

* fixed failing test

* fixed failing test

* there is no need to create two contracts of the same kind any more

* simplify updater::ReleaseTrack conversion into u8 and add several tests for it

* applied review suggestions

* applied review suggestions
This commit is contained in:
Marek Kotewicz
2018-02-09 09:32:06 +01:00
committed by GitHub
parent 2c60a53fef
commit c060d9584d
129 changed files with 1678 additions and 2173 deletions

View File

@@ -24,7 +24,7 @@ tokio-proto = "0.1"
url = "1.0"
ethcore = { path = "../ethcore" }
ethcore-bytes = { path = "../util/bytes" }
ethereum-types = "0.1"
ethereum-types = "0.2"
ethsync = { path = "../sync" }
kvdb = { path = "../util/kvdb" }
kvdb-rocksdb = { path = "../util/kvdb-rocksdb" }
@@ -32,8 +32,10 @@ keccak-hash = { path = "../util/hash" }
ethcore-logger = { path = "../logger" }
ethcrypto = { path = "../ethcrypto" }
ethkey = { path = "../ethkey" }
native-contracts = { path = "../ethcore/native_contracts" }
lazy_static = "1.0"
ethabi = "5.1"
ethabi-derive = "5.0"
ethabi-contract = "5.0"
[dev-dependencies]
tempdir = "0.3"

View File

@@ -0,0 +1,3 @@
[
{"constant":true,"inputs":[{"name":"user","type":"address"},{"name":"document","type":"bytes32"}],"name":"checkPermissions","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"}
]

View File

@@ -0,0 +1 @@
[{"constant":true,"inputs":[],"name":"getMigrationMaster","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getMigrationKeyServerPublic","outputs":[{"name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"id","type":"bytes32"}],"name":"startMigration","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getMigrationKeyServerAddress","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getMigrationId","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getNewKeyServers","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"id","type":"bytes32"}],"name":"confirmMigration","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getMigrationKeyServers","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"isMigrationConfirmed","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getCurrentKeyServers","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getCurrentKeyServerPublic","outputs":[{"name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getNewKeyServerAddress","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getCurrentKeyServerAddress","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getNewKeyServerPublic","outputs":[{"name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"keyServer","type":"address"}],"name":"KeyServerAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"keyServer","type":"address"}],"name":"KeyServerRemoved","type":"event"},{"anonymous":false,"inputs":[],"name":"MigrationStarted","type":"event"},{"anonymous":false,"inputs":[],"name":"MigrationCompleted","type":"event"}]

View File

@@ -0,0 +1,8 @@
[
{"constant":true,"inputs":[],"name":"serverKeyGenerationRequestsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},
{"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"getServerKeyId","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},
{"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"},
{"constant":true,"inputs":[{"name":"serverKeyId","type":"bytes32"}],"name":"getServerKeyThreshold","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},
{"constant":true,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"authority","type":"address"}],"name":"getServerKeyConfirmationStatus","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},
{"anonymous":false,"inputs":[{"indexed":true,"name":"serverKeyId","type":"bytes32"},{"indexed":true,"name":"threshold","type":"uint256"}],"name":"ServerKeyRequested","type":"event"}
]

View File

@@ -16,16 +16,16 @@
use std::sync::Arc;
use std::collections::{HashMap, HashSet};
use futures::{future, Future};
use parking_lot::{Mutex, RwLock};
use ethkey::public_to_address;
use ethcore::client::{BlockChainClient, BlockId, ChainNotify};
use native_contracts::SecretStoreAclStorage;
use ethereum_types::{H256, Address};
use bytes::Bytes;
use trusted_client::TrustedClient;
use types::all::{Error, ServerKeyId, Public};
use_contract!(acl_storage, "AclStorage", "res/acl_storage.json");
const ACL_CHECKER_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_acl_checker";
/// ACL storage of Secret Store
@@ -47,7 +47,7 @@ struct CachedContract {
/// Contract address.
contract_addr: Option<Address>,
/// Contract at given address.
contract: Option<SecretStoreAclStorage>,
contract: acl_storage::AclStorage,
}
/// Dummy ACL storage implementation (check always passed).
@@ -86,23 +86,20 @@ impl ChainNotify for OnChainAclStorage {
impl CachedContract {
pub fn new(client: TrustedClient) -> Self {
CachedContract {
client: client,
client,
contract_addr: None,
contract: None,
contract: acl_storage::AclStorage::default(),
}
}
pub fn update(&mut self) {
if let Some(client) = self.client.get() {
let new_contract_addr = client.registry_address(ACL_CHECKER_CONTRACT_REGISTRY_NAME.to_owned());
if self.contract_addr.as_ref() != new_contract_addr.as_ref() {
self.contract = new_contract_addr.map(|contract_addr| {
trace!(target: "secretstore", "Configuring for ACL checker contract from {}", contract_addr);
SecretStoreAclStorage::new(contract_addr)
});
self.contract_addr = new_contract_addr;
match client.registry_address(ACL_CHECKER_CONTRACT_REGISTRY_NAME.to_owned()) {
Some(new_contract_addr) if Some(new_contract_addr).as_ref() != self.contract_addr.as_ref() => {
trace!(target: "secretstore", "Configuring for ACL checker contract from {}", new_contract_addr);
self.contract_addr = Some(new_contract_addr);
},
Some(_) | None => ()
}
}
}
@@ -110,13 +107,14 @@ impl CachedContract {
pub fn check(&mut self, public: &Public, document: &ServerKeyId) -> Result<bool, Error> {
if let Some(client) = self.client.get() {
// call contract to check accesss
match self.contract.as_ref() {
Some(contract) => {
match self.contract_addr {
Some(contract_address) => {
let address = public_to_address(&public);
let do_call = |a, d| future::done(client.call_contract(BlockId::Latest, a, d));
contract.check_permissions(do_call, address, document.clone())
.map_err(|err| Error::Internal(err))
.wait()
let do_call = |data| client.call_contract(BlockId::Latest, contract_address, data);
self.contract.functions()
.check_permissions()
.call(address, document.clone(), &do_call)
.map_err(|e| Error::Internal(e.to_string()))
},
None => Err(Error::Internal("ACL checker contract is not configured".to_owned())),
}

View File

@@ -17,12 +17,10 @@
use std::sync::Arc;
use std::net::SocketAddr;
use std::collections::{BTreeMap, HashSet};
use futures::{future, Future, IntoFuture};
use parking_lot::Mutex;
use ethcore::filter::Filter;
use ethcore::client::{Client, BlockChainClient, BlockId, ChainNotify};
use ethkey::public_to_address;
use native_contracts::KeyServerSet as KeyServerSetContract;
use hash::keccak;
use ethereum_types::{H256, Address};
use bytes::Bytes;
@@ -30,7 +28,7 @@ use types::all::{Error, Public, NodeAddress, NodeId};
use trusted_client::TrustedClient;
use {NodeKeyPair};
type BoxFuture<A, B> = Box<Future<Item = A, Error = B> + Send>;
use_contract!(key_server, "KeyServerSet", "res/key_server_set.json");
/// Name of KeyServerSet contract in registry.
const KEY_SERVER_SET_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_server_set";
@@ -118,7 +116,9 @@ struct CachedContract {
/// Blockchain client.
client: TrustedClient,
/// Contract address.
contract: Option<KeyServerSetContract>,
contract_address: Option<Address>,
/// Contract interface.
contract: key_server::KeyServerSet,
/// Is auto-migrate enabled?
auto_migrate_enabled: bool,
/// Current contract state.
@@ -168,6 +168,77 @@ impl ChainNotify for OnChainKeyServerSet {
}
}
trait KeyServerSubset<F: Fn(Vec<u8>) -> Result<Vec<u8>, String>> {
fn read_list(&self, f: &F) -> Result<Vec<Address>, String>;
fn read_public(&self, address: Address, f: &F) -> Result<Bytes, String>;
fn read_address(&self, address: Address, f: &F) -> Result<String, String>;
}
#[derive(Default)]
struct CurrentKeyServerSubset {
read_list: key_server::functions::GetCurrentKeyServers,
read_public: key_server::functions::GetCurrentKeyServerPublic,
read_address: key_server::functions::GetCurrentKeyServerAddress,
}
impl <F: Fn(Vec<u8>) -> Result<Vec<u8>, String>> KeyServerSubset<F> for CurrentKeyServerSubset {
fn read_list(&self, f: &F) -> Result<Vec<Address>, String> {
self.read_list.call(f).map_err(|e| e.to_string())
}
fn read_public(&self, address: Address, f: &F) -> Result<Bytes, String> {
self.read_public.call(address, f).map_err(|e| e.to_string())
}
fn read_address(&self, address: Address, f: &F) -> Result<String, String> {
self.read_address.call(address, f).map_err(|e| e.to_string())
}
}
#[derive(Default)]
struct MigrationKeyServerSubset {
read_list: key_server::functions::GetMigrationKeyServers,
read_public: key_server::functions::GetMigrationKeyServerPublic,
read_address: key_server::functions::GetMigrationKeyServerAddress,
}
impl <F: Fn(Vec<u8>) -> Result<Vec<u8>, String>> KeyServerSubset<F> for MigrationKeyServerSubset {
fn read_list(&self, f: &F) -> Result<Vec<Address>, String> {
self.read_list.call(f).map_err(|e| e.to_string())
}
fn read_public(&self, address: Address, f: &F) -> Result<Bytes, String> {
self.read_public.call(address, f).map_err(|e| e.to_string())
}
fn read_address(&self, address: Address, f: &F) -> Result<String, String> {
self.read_address.call(address, f).map_err(|e| e.to_string())
}
}
#[derive(Default)]
struct NewKeyServerSubset {
read_list: key_server::functions::GetNewKeyServers,
read_public: key_server::functions::GetNewKeyServerPublic,
read_address: key_server::functions::GetNewKeyServerAddress,
}
impl <F: Fn(Vec<u8>) -> Result<Vec<u8>, String>> KeyServerSubset<F> for NewKeyServerSubset {
fn read_list(&self, f: &F) -> Result<Vec<Address>, String> {
self.read_list.call(f).map_err(|e| e.to_string())
}
fn read_public(&self, address: Address, f: &F) -> Result<Bytes, String> {
self.read_public.call(address, f).map_err(|e| e.to_string())
}
fn read_address(&self, address: Address, f: &F) -> Result<String, String> {
self.read_address.call(address, f).map_err(|e| e.to_string())
}
}
impl CachedContract {
pub fn new(client: TrustedClient, self_key_pair: Arc<NodeKeyPair>, auto_migrate_enabled: bool, key_servers: BTreeMap<Public, NodeAddress>) -> Result<Self, Error> {
let server_set = key_servers.into_iter()
@@ -179,7 +250,8 @@ impl CachedContract {
.collect::<Result<BTreeMap<_, _>, Error>>()?;
Ok(CachedContract {
client: client,
contract: None,
contract_address: None,
contract: key_server::KeyServerSet::default(),
auto_migrate_enabled: auto_migrate_enabled,
future_new_set: None,
confirm_migration_tx: None,
@@ -208,54 +280,40 @@ 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)) = (self.client.get_untrusted(), self.contract.as_ref()) {
// 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) {
// check if we need to send start migration transaction
if !update_last_transaction_block(&*client, &migration_id, &mut self.start_migration_tx) {
return;
}
// prepare transaction data
let transaction_data = match contract.encode_start_migration_input(migration_id) {
Ok(transaction_data) => transaction_data,
Err(error) => {
warn!(target: "secretstore_net", "{}: failed to prepare auto-migration start transaction: {}",
self.self_key_pair.public(), error);
return;
},
};
let transaction_data = self.contract.functions().start_migration().input(migration_id);
// send transaction
if let Err(error) = client.transact_contract(contract.address.clone(), transaction_data) {
if let Err(error) = client.transact_contract(contract_address, transaction_data) {
warn!(target: "secretstore_net", "{}: failed to submit auto-migration start transaction: {}",
self.self_key_pair.public(), error);
} else {
trace!(target: "secretstore_net", "{}: sent auto-migration start transaction",
self.self_key_pair.public(), );
self.self_key_pair.public());
}
}
}
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)) = (self.client.get(), self.contract.as_ref()) {
if let (Some(client), Some(contract_address)) = (self.client.get(), 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) {
return;
}
// prepare transaction data
let transaction_data = match contract.encode_confirm_migration_input(migration_id) {
Ok(transaction_data) => transaction_data,
Err(error) => {
warn!(target: "secretstore_net", "{}: failed to prepare auto-migration confirmation transaction: {}",
self.self_key_pair.public(), error);
return;
},
};
let transaction_data = self.contract.functions().confirm_migration().input(migration_id);
// send transaction
if let Err(error) = client.transact_contract(contract.address.clone(), transaction_data) {
if let Err(error) = client.transact_contract(contract_address, transaction_data) {
warn!(target: "secretstore_net", "{}: failed to submit auto-migration confirmation transaction: {}",
self.self_key_pair.public(), error);
} else {
@@ -270,18 +328,18 @@ impl CachedContract {
let new_contract_addr = client.registry_address(KEY_SERVER_SET_CONTRACT_REGISTRY_NAME.to_owned());
// new contract installed => read nodes set from the contract
if self.contract.as_ref().map(|c| &c.address) != new_contract_addr.as_ref() {
if self.contract_address.as_ref() != new_contract_addr.as_ref() {
self.read_from_registry(&*client, new_contract_addr);
return;
}
// check for contract events
let is_set_changed = self.contract.is_some() && enacted.iter()
let is_set_changed = self.contract_address.is_some() && enacted.iter()
.chain(retracted.iter())
.any(|block_hash| !client.logs(Filter {
from_block: BlockId::Hash(block_hash.clone()),
to_block: BlockId::Hash(block_hash.clone()),
address: self.contract.as_ref().map(|c| vec![c.address.clone()]),
address: self.contract_address.map(|address| vec![address]),
topics: vec![
Some(vec![*ADDED_EVENT_NAME_HASH, *REMOVED_EVENT_NAME_HASH,
*MIGRATION_STARTED_EVENT_NAME_HASH, *MIGRATION_COMPLETED_EVENT_NAME_HASH]),
@@ -299,14 +357,13 @@ impl CachedContract {
}
fn read_from_registry(&mut self, client: &Client, new_contract_address: Option<Address>) {
self.contract = new_contract_address.map(|contract_addr| {
if let Some(ref contract_addr) = new_contract_address {
trace!(target: "secretstore", "Configuring for key server set contract from {}", contract_addr);
}
self.contract_address = new_contract_address;
KeyServerSetContract::new(contract_addr)
});
let contract = match self.contract.as_ref() {
Some(contract) => contract,
let contract_address = match self.contract_address {
Some(contract_address) => contract_address,
None => {
// no contract installed => empty snapshot
// WARNING: after restart current_set will be reset to the set from configuration file
@@ -318,28 +375,25 @@ impl CachedContract {
},
};
let do_call = |a, d| future::done(client.call_contract(BlockId::Latest, a, d));
let do_call = |data| client.call_contract(BlockId::Latest, contract_address, data);
let current_set = Self::read_key_server_set(&contract, &do_call, &KeyServerSetContract::get_current_key_servers,
&KeyServerSetContract::get_current_key_server_public, &KeyServerSetContract::get_current_key_server_address);
let current_set = Self::read_key_server_set(CurrentKeyServerSubset::default(), &do_call);
// read migration-related data if auto migration is enabled
let (new_set, migration) = match self.auto_migrate_enabled {
true => {
let new_set = Self::read_key_server_set(&contract, &do_call, &KeyServerSetContract::get_new_key_servers,
&KeyServerSetContract::get_new_key_server_public, &KeyServerSetContract::get_new_key_server_address);
let migration_set = Self::read_key_server_set(&contract, &do_call, &KeyServerSetContract::get_migration_key_servers,
&KeyServerSetContract::get_migration_key_server_public, &KeyServerSetContract::get_migration_key_server_address);
let new_set = Self::read_key_server_set(NewKeyServerSubset::default(), &do_call);
let migration_set = Self::read_key_server_set(MigrationKeyServerSubset::default(), &do_call);
let migration_id = match migration_set.is_empty() {
false => contract.get_migration_id(&do_call).wait()
false => self.contract.functions().get_migration_id().call(&do_call)
.map_err(|err| { trace!(target: "secretstore", "Error {} reading migration id from contract", err); err })
.ok(),
true => None,
};
let migration_master = match migration_set.is_empty() {
false => contract.get_migration_master(&do_call).wait()
false => self.contract.functions().get_migration_master().call(&do_call)
.map_err(|err| { trace!(target: "secretstore", "Error {} reading migration master from contract", err); err })
.ok()
.and_then(|address| current_set.keys().chain(migration_set.keys())
@@ -350,7 +404,7 @@ impl CachedContract {
let is_migration_confirmed = match migration_set.is_empty() {
false if current_set.contains_key(self.self_key_pair.public()) || migration_set.contains_key(self.self_key_pair.public()) =>
contract.is_migration_confirmed(&do_call, self.self_key_pair.address()).wait()
self.contract.functions().is_migration_confirmed().call(self.self_key_pair.address(), &do_call)
.map_err(|err| { trace!(target: "secretstore", "Error {} reading migration confirmation from contract", err); err })
.ok(),
_ => None,
@@ -387,23 +441,19 @@ impl CachedContract {
self.snapshot = new_snapshot;
}
fn read_key_server_set<F, U, FL, FP, FA>(contract: &KeyServerSetContract, do_call: F, read_list: FL, read_public: FP, read_address: FA) -> BTreeMap<Public, SocketAddr>
fn read_key_server_set<T, F>(subset: T, do_call: F) -> BTreeMap<Public, SocketAddr>
where
F: FnOnce(Address, Vec<u8>) -> U + Copy,
U: IntoFuture<Item=Vec<u8>, Error=String>,
U::Future: Send + 'static,
FL: Fn(&KeyServerSetContract, F) -> BoxFuture<Vec<Address>, String>,
FP: Fn(&KeyServerSetContract, F, Address) -> BoxFuture<Vec<u8>, String>,
FA: Fn(&KeyServerSetContract, F, Address) -> BoxFuture<String, String> {
T: KeyServerSubset<F>,
F: Fn(Vec<u8>) -> Result<Vec<u8>, String> {
let mut key_servers = BTreeMap::new();
let mut key_servers_addresses = HashSet::new();
let key_servers_list = read_list(contract, do_call).wait()
let key_servers_list = subset.read_list(&do_call)
.map_err(|err| { warn!(target: "secretstore_net", "error {} reading list of key servers from contract", err); err })
.unwrap_or_default();
for key_server in key_servers_list {
let key_server_public = read_public(contract, do_call, key_server).wait()
let key_server_public = subset.read_public(key_server, &do_call)
.and_then(|p| if p.len() == 64 { Ok(Public::from_slice(&p)) } else { Err(format!("Invalid public length {}", p.len())) });
let key_server_address: Result<SocketAddr, _> = read_address(contract, do_call, key_server).wait()
let key_server_address: Result<SocketAddr, _> = subset.read_address(key_server, &do_call)
.and_then(|a| a.parse().map_err(|e| format!("Invalid ip address: {}", e)));
// only add successfully parsed nodes

View File

@@ -15,38 +15,42 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
extern crate byteorder;
#[macro_use]
extern crate log;
#[macro_use]
extern crate futures;
extern crate ethabi;
extern crate ethcore;
extern crate ethcore_bytes as bytes;
extern crate ethcore_logger as logger;
extern crate ethcrypto;
extern crate ethereum_types;
extern crate ethkey;
extern crate ethsync;
extern crate futures_cpupool;
extern crate hyper;
#[macro_use]
extern crate lazy_static;
extern crate keccak_hash as hash;
extern crate kvdb;
extern crate kvdb_rocksdb;
extern crate parking_lot;
extern crate rustc_hex;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
extern crate tiny_keccak;
extern crate tokio_io;
extern crate tokio_core;
extern crate tokio_service;
extern crate tokio_io;
extern crate tokio_proto;
extern crate tokio_service;
extern crate url;
extern crate ethcore;
extern crate ethcore_bytes as bytes;
extern crate ethcore_logger as logger;
extern crate ethereum_types;
extern crate ethcrypto;
extern crate ethkey;
extern crate ethsync;
extern crate native_contracts;
extern crate keccak_hash as hash;
extern crate kvdb;
extern crate kvdb_rocksdb;
#[macro_use]
extern crate ethabi_derive;
#[macro_use]
extern crate ethabi_contract;
#[macro_use]
extern crate futures;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
mod key_server_cluster;
mod types;

View File

@@ -15,18 +15,18 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use futures::{future, Future};
use parking_lot::RwLock;
use ethcore::filter::Filter;
use ethcore::client::{Client, BlockChainClient, BlockId};
use ethkey::{Public, Signature, public_to_address};
use native_contracts::SecretStoreService;
use hash::keccak;
use ethereum_types::{H256, U256};
use ethereum_types::{H256, U256, Address};
use listener::service_contract_listener::ServiceTask;
use trusted_client::TrustedClient;
use {ServerKeyId, NodeKeyPair, ContractAddress};
use_contract!(service, "Service", "res/service.json");
/// Name of the SecretStore contract in the registry.
const SERVICE_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_service";
@@ -61,13 +61,15 @@ pub struct OnChainServiceContract {
/// Contract addresss.
address: ContractAddress,
/// Contract.
data: RwLock<SecretStoreServiceData>,
data: RwLock<ServiceData>,
}
/// On-chain service contract data.
struct SecretStoreServiceData {
struct ServiceData {
/// Contract.
pub contract: Arc<SecretStoreService>,
pub contract: service::Service,
/// Contract address.
pub contract_address: Address,
/// Last block we have read logs from.
pub last_log_block: Option<H256>,
}
@@ -77,7 +79,9 @@ struct PendingRequestsIterator {
/// Blockchain client.
client: Arc<Client>,
/// Contract.
contract: Arc<SecretStoreService>,
contract: service::Service,
/// Contract address.
contract_address: Address,
/// This node key pair.
self_key_pair: Arc<NodeKeyPair>,
/// Block, this iterator is created for.
@@ -110,8 +114,9 @@ impl OnChainServiceContract {
client: client,
self_key_pair: self_key_pair,
address: address,
data: RwLock::new(SecretStoreServiceData {
contract: Arc::new(SecretStoreService::new(contract_addr)),
data: RwLock::new(ServiceData {
contract: service::Service::default(),
contract_address: contract_addr,
last_log_block: None,
}),
}
@@ -126,15 +131,15 @@ impl ServiceContract for OnChainServiceContract {
if let Some(client) = self.client.get() {
// update contract address from registry
let service_contract_addr = client.registry_address(SERVICE_CONTRACT_REGISTRY_NAME.to_owned()).unwrap_or_default();
if self.data.read().contract.address != service_contract_addr {
if self.data.read().contract_address != service_contract_addr {
trace!(target: "secretstore", "{}: installing service contract from address {}",
self.self_key_pair.public(), service_contract_addr);
self.data.write().contract = Arc::new(SecretStoreService::new(service_contract_addr));
self.data.write().contract_address = service_contract_addr;
}
}
}
self.data.read().contract.address != Default::default()
self.data.read().contract_address != Default::default()
&& self.client.get().is_some()
}
@@ -151,7 +156,7 @@ impl ServiceContract for OnChainServiceContract {
// prepare range of blocks to read logs from
let (address, first_block, last_block) = {
let mut data = self.data.write();
let address = data.contract.address.clone();
let address = data.contract_address;
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
@@ -197,12 +202,13 @@ impl ServiceContract for OnChainServiceContract {
// 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 == Default::default() {
match data.contract_address == Default::default() {
true => Box::new(::std::iter::empty()),
false => get_confirmed_block_hash(&*client, REQUEST_CONFIRMATIONS_REQUIRED + 1)
.and_then(|b| {
let do_call = |a, d| future::done(client.call_contract(BlockId::Hash(b.clone()), a, d));
data.contract.server_key_generation_requests_count(&do_call).wait()
let contract_address = data.contract_address;
let do_call = |data| client.call_contract(BlockId::Hash(b), contract_address, data);
data.contract.functions().server_key_generation_requests_count().call(&do_call)
.map_err(|error| {
warn!(target: "secretstore", "{}: call to server_key_generation_requests_count failed: {}",
self.self_key_pair.public(), error);
@@ -213,7 +219,8 @@ impl ServiceContract for OnChainServiceContract {
})
.map(|(b, l)| Box::new(PendingRequestsIterator {
client: client,
contract: data.contract.clone(),
contract: service::Service::default(),
contract_address: data.contract_address,
self_key_pair: self.self_key_pair.clone(),
block: b,
index: 0.into(),
@@ -226,7 +233,7 @@ impl ServiceContract for OnChainServiceContract {
fn publish_server_key(&self, server_key_id: &ServerKeyId, server_key: &Public) -> Result<(), String> {
// only publish if contract address is set && client is online
let data = self.data.read();
if data.contract.address == Default::default() {
if data.contract_address == Default::default() {
// it is not an error, because key could be generated even without contract
return Ok(());
}
@@ -239,9 +246,13 @@ impl ServiceContract for OnChainServiceContract {
// 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 do_call = |a, d| future::done(client.call_contract(BlockId::Latest, a, d));
let contract_address = data.contract_address;
let do_call = |data| client.call_contract(BlockId::Latest, contract_address, data);
let self_address = public_to_address(self.self_key_pair.public());
if data.contract.get_server_key_confirmation_status(&do_call, server_key_id.clone(), self_address).wait().unwrap_or(false) {
if data.contract.functions()
.get_server_key_confirmation_status()
.call(*server_key_id, self_address, &do_call)
.unwrap_or(false) {
return Ok(());
}
@@ -249,16 +260,18 @@ impl ServiceContract for OnChainServiceContract {
let server_key_hash = keccak(server_key);
let signed_server_key = self.self_key_pair.sign(&server_key_hash).map_err(|e| format!("{}", e))?;
let signed_server_key: Signature = signed_server_key.into_electrum().into();
let transaction_data = data.contract.encode_server_key_generated_input(server_key_id.clone(),
server_key.to_vec(),
signed_server_key.v(),
signed_server_key.r().into(),
signed_server_key.s().into()
)?;
let transaction_data = data.contract.functions()
.server_key_generated()
.input(*server_key_id,
server_key.to_vec(),
signed_server_key.v(),
signed_server_key.r(),
signed_server_key.s(),
);
// send transaction
client.transact_contract(
data.contract.address.clone(),
data.contract_address,
transaction_data
).map_err(|e| format!("{}", e))?;
@@ -278,13 +291,14 @@ impl Iterator for PendingRequestsIterator {
self.index = self.index + 1.into();
let self_address = public_to_address(self.self_key_pair.public());
let do_call = |a, d| future::done(self.client.call_contract(BlockId::Hash(self.block.clone()), a, d));
self.contract.get_server_key_id(&do_call, index).wait()
let contract_address = self.contract_address;
let do_call = |data| self.client.call_contract(BlockId::Hash(self.block.clone()), contract_address, data);
self.contract.functions().get_server_key_id().call(index, &do_call)
.and_then(|server_key_id|
self.contract.get_server_key_threshold(&do_call, server_key_id.clone()).wait()
self.contract.functions().get_server_key_threshold().call(server_key_id, &do_call)
.map(|threshold| (server_key_id, threshold)))
.and_then(|(server_key_id, threshold)|
self.contract.get_server_key_confirmation_status(&do_call, server_key_id.clone(), self_address).wait()
self.contract.functions().get_server_key_confirmation_status().call(server_key_id, self_address, &do_call)
.map(|is_confirmed| (server_key_id, threshold, is_confirmed)))
.map(|(server_key_id, threshold, is_confirmed)|
Some((is_confirmed, ServiceTask::GenerateServerKey(server_key_id, threshold.into()))))

View File

@@ -376,7 +376,7 @@ fn is_processed_by_this_key_server(key_server_set: &KeyServerSet, self_key_pair:
let server_key_id_value: U256 = server_key_id.into();
let range_interval = U256::max_value() / total_servers_count.into();
let range_begin = (range_interval + 1.into()) * this_server_index.into();
let range_begin = (range_interval + 1.into()) * this_server_index as u32;
let range_end = range_begin.saturating_add(range_interval);
server_key_id_value >= range_begin && server_key_id_value <= range_end