Skip to content
This repository was archived by the owner on Nov 6, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" }
dir = { path = "util/dir" }
docopt = "1.0"
engine = { path = "ethcore/engine" }
ethabi = { version = "9.0.1", optional = true }
ethcore = { path = "ethcore", features = ["parity"] }
ethcore-accounts = { path = "accounts", optional = true }
ethcore-blockchain = { path = "ethcore/blockchain" }
ethcore-call-contract = { path = "ethcore/call-contract", optional = true }
ethcore-db = { path = "ethcore/db" }
ethcore-io = { path = "util/io" }
ethcore-light = { path = "ethcore/light" }
Expand Down Expand Up @@ -97,7 +99,7 @@ test-heavy = ["ethcore/test-heavy"]
evm-debug = ["ethcore/evm-debug"]
evm-debug-tests = ["ethcore/evm-debug-tests"]
slow-blocks = ["ethcore/slow-blocks"]
secretstore = ["ethcore-secretstore", "ethcore-secretstore/accounts"]
secretstore = ["ethcore-secretstore", "accounts", "ethabi", "ethcore-call-contract"]
final = ["parity-version/final"]
deadlock_detection = ["parking_lot/deadlock_detection"]
# to create a memory profile (requires nightly rust), use e.g.
Expand Down
6 changes: 6 additions & 0 deletions parity/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ extern crate ethcore_accounts as accounts;
#[cfg(feature = "secretstore")]
extern crate ethcore_secretstore;

#[cfg(feature = "secretstore")]
extern crate ethabi;

#[cfg(feature = "secretstore")]
extern crate ethcore_call_contract as call_contract;

#[cfg(test)]
#[macro_use]
extern crate pretty_assertions;
Expand Down
246 changes: 246 additions & 0 deletions parity/secretstore/blockchain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
// This file is part of Parity Ethereum.

// Parity Ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity Ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.

//! SecretStoreChain implementation with information about blockchain, retrieved from the client

use std::sync::{Arc, Weak};
Comment thread
grbIzl marked this conversation as resolved.
use ethereum_types::{H256, Address};
use parking_lot::RwLock;
use types::{
ids::BlockId as EthcoreBlockId,
transaction::{Transaction, SignedTransaction, Action},
chain_notify::NewBlocks,
tree_route::TreeRoute,
filter::Filter as BlockchainFilter,
log_entry::LocalizedLogEntry,
};
use ethcore::client::Client;
use bytes::Bytes;
use ethabi::RawLog;
use client_traits::BlockChainClient;
use call_contract::CallContract;
use client_traits::{ChainInfo, Nonce, ChainNotify};
use ethcore::miner::{Miner, MinerService};
use parity_crypto::publickey::Error as EthKeyError;
use sync::SyncProvider;
use registrar::RegistrarClient;
use ethcore_secretstore::{BlockId, BlockNumber, SecretStoreChain, NewBlocksNotify, SigningKeyPair, ContractAddress, Filter};

// TODO: Instead of a constant, make this based on consensus finality.
/// Number of confirmations required before request can be processed.
const REQUEST_CONFIRMATIONS_REQUIRED: u64 = 3;

fn into_ethcore_block_id(id: BlockId) -> EthcoreBlockId {
match id {
BlockId::Hash(hash) => EthcoreBlockId::Hash(hash),
BlockId::Number(number) => EthcoreBlockId::Number(number),
BlockId::Earliest => EthcoreBlockId::Earliest,
BlockId::Latest => EthcoreBlockId::Latest,
}
}

/// SecretStore blockchain implementation (client's wrapper)
Comment thread
grbIzl marked this conversation as resolved.
/// This implementation is trusted, when underlying client is synced and chain's security level is full
/// This trust is guaranteed by return result in get_trusted method (if it's not trusted, None is returned)
pub struct TrustedClient {
/// This key server node key pair.
self_key_pair: Arc<dyn SigningKeyPair>,
/// Blockchain client.
client: Weak<Client>,
/// Sync provider.
sync: Weak<dyn SyncProvider>,
/// Miner service.
miner: Weak<Miner>,
/// Chain new blocks listeners
listeners: RwLock<Vec<Weak<dyn NewBlocksNotify>>>,
}

impl TrustedClient {
/// Create new trusted client.
pub fn new(self_key_pair: Arc<dyn SigningKeyPair>, client: Arc<Client>, sync: Arc<dyn SyncProvider>, miner: Arc<Miner>) -> Arc<Self> {
let trusted_client = Arc::new(TrustedClient {
self_key_pair,
client: Arc::downgrade(&client),
sync: Arc::downgrade(&sync),
miner: Arc::downgrade(&miner),
listeners: RwLock::default(),
});
client.add_notify(trusted_client.clone());
trusted_client
}

fn notify_listeners(&self, new_enacted_len: usize) {
for listener_pointer in self.listeners.read().iter() {
if let Some(listener) = listener_pointer.upgrade() {
listener.new_blocks(new_enacted_len);
}
}
}

/// Get 'trusted' `Client` reference only if it is synchronized && trusted.
fn get_trusted(&self) -> Option<Arc<Client>> {
self.client.upgrade()
.and_then(|client| self.sync.upgrade().map(|sync| (client, sync)))
.and_then(|(client, sync)| {
let is_synced = !sync.is_major_syncing();
let is_trusted = client.chain_info().security_level().is_full();
match is_synced && is_trusted {
true => Some(client),
false => None,
}
})
}

fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute> {
if let Some(client) = self.get_trusted() {
client.tree_route(from, to)
} else {
None
}
}

fn logs(&self, filter: BlockchainFilter) -> Option<Vec<LocalizedLogEntry>> {
if let Some(client) = self.get_trusted() {
client.logs(filter).ok()
} else {
None
}
}

}

impl SecretStoreChain for TrustedClient {
fn add_listener(&self, target: Arc<dyn NewBlocksNotify>) {
self.listeners.write().push(Arc::downgrade(&target));
}

fn is_trusted(&self) -> bool {
self.get_trusted().is_some()
}

fn transact_contract(&self, contract: Address, tx_data: Bytes) -> Result<(), EthKeyError> {
let client = self.client.upgrade().ok_or_else(|| EthKeyError::Custom("cannot submit tx when client is offline".into()))?;
let miner = self.miner.upgrade().ok_or_else(|| EthKeyError::Custom("cannot submit tx when miner is offline".into()))?;
let engine = client.engine();
let transaction = Transaction {
nonce: client.latest_nonce(&self.self_key_pair.address()),
action: Action::Call(contract),
gas: miner.authoring_params().gas_range_target.0,
gas_price: miner.sensible_gas_price(),
value: Default::default(),
data: tx_data,
};
let chain_id = engine.signing_chain_id(&client.latest_env_info());
let signature = self.self_key_pair.sign(&transaction.hash(chain_id))?;
let signed = SignedTransaction::new(transaction.with_signature(signature, chain_id))?;
miner.import_own_transaction(&*client, signed.into())
.map_err(|e| EthKeyError::Custom(format!("failed to import tx: {}", e)))
}

fn read_contract_address(
&self,
registry_name: &str,
address: &ContractAddress
) -> Option<Address> {
match *address {
ContractAddress::Address(ref address) => Some(address.clone()),
ContractAddress::Registry => self.get_trusted().and_then(|client|
self.get_confirmed_block_hash()
.and_then(|block| {
client.get_address(registry_name, EthcoreBlockId::Hash(block))
.unwrap_or(None)
})
),
}
}

fn call_contract(&self, block_id: BlockId, contract_address: Address, data: Bytes) -> Result<Bytes, String> {
if let Some(client) = self.get_trusted() {
client.call_contract(into_ethcore_block_id(block_id), contract_address, data)
} else {
Err("Calling ACL contract without trusted blockchain client".into())
}
}

fn block_hash(&self, id: BlockId) -> Option<H256> {
if let Some(client) = self.get_trusted() {
client.block_hash(into_ethcore_block_id(id))
} else {
None
}
}

fn block_number(&self, id: BlockId) -> Option<BlockNumber> {
if let Some(client) = self.get_trusted() {
client.block_number(into_ethcore_block_id(id))
} else {
None
}
}

fn retrieve_last_logs(&self, filter: Filter) -> Option<Vec<RawLog>> {
let confirmed_block = match self.get_confirmed_block_hash() {
Some(confirmed_block) => confirmed_block,
None => return None, // no block with enough confirmations
};

let from_block = self.block_hash(filter.from_block).unwrap_or_else(|| confirmed_block);
let first_block = match self.tree_route(&from_block, &confirmed_block) {
// if we have a route from last_log_block to confirmed_block => search for logs on this route
//
// potentially this could lead us to reading same logs twice when reorganizing to the fork, which
// already has been canonical previosuly
// the worst thing that can happen in this case is spending some time reading unneeded data from SS db
Some(ref route) if route.index < route.blocks.len() => route.blocks[route.index],
// else we care only about confirmed block
_ => confirmed_block.clone(),
};

self.logs(BlockchainFilter {
from_block: EthcoreBlockId::Hash(first_block),
to_block: EthcoreBlockId::Hash(confirmed_block),
address: filter.address,
topics: filter.topics,
limit: None,
})
.map(|blockchain_logs| {
blockchain_logs
.into_iter()
.map(|log| {
let raw_log: RawLog = (log.entry.topics.into_iter().map(|t| t.0.into()).collect(), log.entry.data).into();
raw_log
})
.collect::<Vec<_>>()
})
}

fn get_confirmed_block_hash(&self) -> Option<H256> {
self.block_number(BlockId::Latest)
.map(|b| b.saturating_sub(REQUEST_CONFIRMATIONS_REQUIRED))
.and_then(|b| self.block_hash(BlockId::Number(b)))
}
}

impl ChainNotify for TrustedClient {
fn new_blocks(&self, new_blocks: NewBlocks) {
if new_blocks.has_more_blocks_to_import { return }
if !new_blocks.route.enacted().is_empty() || !new_blocks.route.retracted().is_empty() {
let enacted_len = new_blocks.route.enacted().len();
self.notify_listeners(enacted_len);
}
}
}

30 changes: 15 additions & 15 deletions secret-store/src/helpers.rs → parity/secretstore/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.

use ethcore::client::Client;
use client_traits::BlockChainClient;
use common_types::ids::BlockId;
use ethereum_types::H256;

// TODO: Instead of a constant, make this based on consensus finality.
/// Number of confirmations required before request can be processed.
pub const REQUEST_CONFIRMATIONS_REQUIRED: u64 = 3;

/// Get hash of the last block with at least n confirmations.
pub fn get_confirmed_block_hash(client: &Client, confirmations: u64) -> Option<H256> {
client.block_number(BlockId::Latest)
.map(|b| b.saturating_sub(confirmations))
.and_then(|b| client.block_hash(BlockId::Number(b)))
}
//! Secret store related components.

mod server;

#[cfg(feature = "secretstore")]
mod blockchain;

#[cfg(all(feature = "accounts", feature = "secretstore"))]
mod nodekeypair;

pub use self::server::{Configuration, NodeSecretKey, ContractAddress, Dependencies, start};
#[cfg(feature = "secretstore")]
use self::blockchain::TrustedClient;
#[cfg(all(feature = "accounts", feature = "secretstore"))]
use self::nodekeypair::KeyStoreNodeKeyPair;
Loading