Merge branch 'master' of github.com:ethcore/parity into fixed_hash_util
This commit is contained in:
commit
cad7125acc
14
Cargo.lock
generated
14
Cargo.lock
generated
@ -279,7 +279,6 @@ dependencies = [
|
||||
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -346,7 +345,6 @@ dependencies = [
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -354,7 +352,6 @@ name = "ethcore-ipc-nano"
|
||||
version = "1.3.0"
|
||||
dependencies = [
|
||||
"ethcore-ipc 1.3.0",
|
||||
"jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||
]
|
||||
@ -521,7 +518,6 @@ dependencies = [
|
||||
"parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -928,7 +924,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
[[package]]
|
||||
name = "parity-dapps"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/ethcore/parity-ui.git#7120546d08d4d9eb648e255c04935002223d362f"
|
||||
source = "git+https://github.com/ethcore/parity-ui.git#697e860dedc45003909602a002e7743478ab173a"
|
||||
dependencies = [
|
||||
"aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -942,7 +938,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "parity-dapps-home"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/ethcore/parity-ui.git#7120546d08d4d9eb648e255c04935002223d362f"
|
||||
source = "git+https://github.com/ethcore/parity-ui.git#697e860dedc45003909602a002e7743478ab173a"
|
||||
dependencies = [
|
||||
"parity-dapps 0.6.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||
]
|
||||
@ -950,7 +946,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "parity-dapps-signer"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/ethcore/parity-ui.git#7120546d08d4d9eb648e255c04935002223d362f"
|
||||
source = "git+https://github.com/ethcore/parity-ui.git#697e860dedc45003909602a002e7743478ab173a"
|
||||
dependencies = [
|
||||
"parity-dapps 0.6.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||
]
|
||||
@ -958,7 +954,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "parity-dapps-status"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/ethcore/parity-ui.git#7120546d08d4d9eb648e255c04935002223d362f"
|
||||
source = "git+https://github.com/ethcore/parity-ui.git#697e860dedc45003909602a002e7743478ab173a"
|
||||
dependencies = [
|
||||
"parity-dapps 0.6.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||
]
|
||||
@ -966,7 +962,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "parity-dapps-wallet"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/ethcore/parity-ui.git#7120546d08d4d9eb648e255c04935002223d362f"
|
||||
source = "git+https://github.com/ethcore/parity-ui.git#697e860dedc45003909602a002e7743478ab173a"
|
||||
dependencies = [
|
||||
"parity-dapps 0.6.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||
]
|
||||
|
@ -57,8 +57,8 @@ default = ["ui", "use-precompiled-js"]
|
||||
ui = ["dapps", "ethcore-signer/ui"]
|
||||
use-precompiled-js = ["ethcore-dapps/use-precompiled-js", "ethcore-signer/use-precompiled-js"]
|
||||
dapps = ["ethcore-dapps"]
|
||||
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "ethcore-dapps/dev", "ethcore-signer/dev"]
|
||||
ipc = ["ethcore/ipc"]
|
||||
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "ethcore-dapps/dev", "ethcore-signer/dev"]
|
||||
json-tests = ["ethcore/json-tests"]
|
||||
|
||||
[[bin]]
|
||||
|
@ -25,12 +25,6 @@ use std::mem;
|
||||
use ipc::binary::BinaryConvertError;
|
||||
use std::collections::{VecDeque, HashMap, BTreeMap};
|
||||
|
||||
impl From<String> for Error {
|
||||
fn from(s: String) -> Error {
|
||||
Error::RocksDb(s)
|
||||
}
|
||||
}
|
||||
|
||||
enum WriteCacheEntry {
|
||||
Remove,
|
||||
Write(Vec<u8>),
|
||||
|
@ -41,6 +41,12 @@ pub struct KeyValue {
|
||||
UncommitedTransactions,
|
||||
}
|
||||
|
||||
impl From<String> for Error {
|
||||
fn from(s: String) -> Error {
|
||||
Error::RocksDb(s)
|
||||
}
|
||||
}
|
||||
|
||||
/// Database configuration
|
||||
#[derive(Binary)]
|
||||
pub struct DatabaseConfig {
|
||||
|
@ -8,7 +8,6 @@ authors = ["Ethcore <admin@ethcore.io>"]
|
||||
build = "build.rs"
|
||||
|
||||
[build-dependencies]
|
||||
syntex = "*"
|
||||
"ethcore-ipc-codegen" = { path = "../ipc/codegen" }
|
||||
|
||||
[dependencies]
|
||||
@ -35,7 +34,6 @@ ethcore-ipc = { path = "../ipc/rpc" }
|
||||
ethstore = { path = "../ethstore" }
|
||||
ethcore-ipc-nano = { path = "../ipc/nano" }
|
||||
|
||||
|
||||
[dependencies.hyper]
|
||||
git = "https://github.com/ethcore/hyper"
|
||||
default-features = false
|
||||
|
@ -14,48 +14,10 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate syntex;
|
||||
extern crate ethcore_ipc_codegen as codegen;
|
||||
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
extern crate ethcore_ipc_codegen;
|
||||
|
||||
fn main() {
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
// serialization pass
|
||||
{
|
||||
let src = Path::new("src/types/mod.rs.in");
|
||||
let dst = Path::new(&out_dir).join("types.rs");
|
||||
let mut registry = syntex::Registry::new();
|
||||
codegen::register(&mut registry);
|
||||
registry.expand("", &src, &dst).unwrap();
|
||||
}
|
||||
|
||||
// blockchain client interface
|
||||
{
|
||||
let src = Path::new("src/client/traits.rs");
|
||||
let intermediate = Path::new(&out_dir).join("traits.intermediate.rs.in");
|
||||
let mut registry = syntex::Registry::new();
|
||||
codegen::register(&mut registry);
|
||||
registry.expand("", &src, &intermediate).unwrap();
|
||||
|
||||
let dst = Path::new(&out_dir).join("traits.ipc.rs");
|
||||
let mut registry = syntex::Registry::new();
|
||||
codegen::register(&mut registry);
|
||||
registry.expand("", &intermediate, &dst).unwrap();
|
||||
}
|
||||
|
||||
// chain notify interface
|
||||
{
|
||||
let src = Path::new("src/client/chain_notify.rs");
|
||||
let intermediate = Path::new(&out_dir).join("chain_notify.intermediate.rs.in");
|
||||
let mut registry = syntex::Registry::new();
|
||||
codegen::register(&mut registry);
|
||||
registry.expand("", &src, &intermediate).unwrap();
|
||||
|
||||
let dst = Path::new(&out_dir).join("chain_notify.ipc.rs");
|
||||
let mut registry = syntex::Registry::new();
|
||||
codegen::register(&mut registry);
|
||||
registry.expand("", &intermediate, &dst).unwrap();
|
||||
}
|
||||
ethcore_ipc_codegen::derive_binary("src/types/mod.rs.in").unwrap();
|
||||
ethcore_ipc_codegen::derive_ipc("src/client/traits.rs").unwrap();
|
||||
ethcore_ipc_codegen::derive_ipc("src/client/chain_notify.rs").unwrap();
|
||||
}
|
||||
|
@ -665,8 +665,8 @@ impl BlockChain {
|
||||
let mut write_hashes = self.pending_block_hashes.write();
|
||||
let mut write_txs = self.pending_transaction_addresses.write();
|
||||
|
||||
batch.extend_with_cache(DB_COL_EXTRA, &mut *write_hashes, update.block_hashes, CacheUpdatePolicy::Remove);
|
||||
batch.extend_with_cache(DB_COL_EXTRA, &mut *write_txs, update.transactions_addresses, CacheUpdatePolicy::Remove);
|
||||
batch.extend_with_cache(DB_COL_EXTRA, &mut *write_hashes, update.block_hashes, CacheUpdatePolicy::Overwrite);
|
||||
batch.extend_with_cache(DB_COL_EXTRA, &mut *write_txs, update.transactions_addresses, CacheUpdatePolicy::Overwrite);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1111,6 +1111,7 @@ mod tests {
|
||||
let ir3b = bc.insert_block(&batch, &b3b, vec![]);
|
||||
bc.commit();
|
||||
db.write(batch).unwrap();
|
||||
assert_eq!(bc.block_hash(3).unwrap(), b3b_hash);
|
||||
let batch = db.transaction();
|
||||
let ir3a = bc.insert_block(&batch, &b3a, vec![]);
|
||||
bc.commit();
|
||||
|
@ -178,15 +178,15 @@ impl Client {
|
||||
db_config.compaction = config.db_compaction.compaction_profile();
|
||||
db_config.wal = config.db_wal;
|
||||
|
||||
let db = Arc::new(Database::open(&db_config, &path.to_str().unwrap()).expect("Error opening database"));
|
||||
let db = Arc::new(try!(Database::open(&db_config, &path.to_str().unwrap()).map_err(ClientError::Database)));
|
||||
let chain = Arc::new(BlockChain::new(config.blockchain, &gb, db.clone()));
|
||||
let tracedb = Arc::new(try!(TraceDB::new(config.tracing, db.clone(), chain.clone())));
|
||||
|
||||
let mut state_db = journaldb::new(db.clone(), config.pruning, DB_COL_STATE);
|
||||
if state_db.is_empty() && spec.ensure_db_good(state_db.as_hashdb_mut()) {
|
||||
let batch = DBTransaction::new(&db);
|
||||
state_db.commit(&batch, 0, &spec.genesis_header().hash(), None).expect("Error commiting genesis state to state DB");
|
||||
db.write(batch).expect("Error writing genesis state to state DB");
|
||||
try!(state_db.commit(&batch, 0, &spec.genesis_header().hash(), None));
|
||||
try!(db.write(batch).map_err(ClientError::Database));
|
||||
}
|
||||
|
||||
if !chain.block_header(&chain.best_block_hash()).map_or(true, |h| state_db.contains(h.state_root())) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
use trace::Error as TraceError;
|
||||
use util::UtilError;
|
||||
use std::fmt::{Display, Formatter, Error as FmtError};
|
||||
|
||||
/// Client configuration errors.
|
||||
@ -6,6 +7,10 @@ use std::fmt::{Display, Formatter, Error as FmtError};
|
||||
pub enum Error {
|
||||
/// TraceDB configuration error.
|
||||
Trace(TraceError),
|
||||
/// Database error
|
||||
Database(String),
|
||||
/// Util error
|
||||
Util(UtilError),
|
||||
}
|
||||
|
||||
impl From<TraceError> for Error {
|
||||
@ -14,10 +19,18 @@ impl From<TraceError> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UtilError> for Error {
|
||||
fn from(err: UtilError) -> Self {
|
||||
Error::Util(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
|
||||
match *self {
|
||||
Error::Trace(ref err) => write!(f, "{}", err)
|
||||
Error::Trace(ref err) => write!(f, "{}", err),
|
||||
Error::Util(ref err) => write!(f, "{}", err),
|
||||
Error::Database(ref s) => write!(f, "Database error: {}", s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,13 +40,13 @@ pub use self::traits::{BlockChainClient, MiningBlockChainClient, RemoteClient};
|
||||
|
||||
mod traits {
|
||||
#![allow(dead_code, unused_assignments, unused_variables, missing_docs)] // codegen issues
|
||||
include!(concat!(env!("OUT_DIR"), "/traits.ipc.rs"));
|
||||
include!(concat!(env!("OUT_DIR"), "/traits.rs"));
|
||||
}
|
||||
|
||||
pub mod chain_notify {
|
||||
//! Chain notify interface
|
||||
|
||||
#![allow(dead_code, unused_assignments, unused_variables, missing_docs)] // codegen issues
|
||||
include!(concat!(env!("OUT_DIR"), "/chain_notify.ipc.rs"));
|
||||
include!(concat!(env!("OUT_DIR"), "/chain_notify.rs"));
|
||||
}
|
||||
|
||||
|
@ -257,7 +257,7 @@ pub fn get_temp_journal_db() -> GuardedTempResult<Box<JournalDB>> {
|
||||
}
|
||||
|
||||
impl MiningBlockChainClient for TestBlockChainClient {
|
||||
fn prepare_open_block(&self, _author: Address, _gas_range_target: (U256, U256), _extra_data: Bytes) -> OpenBlock {
|
||||
fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock {
|
||||
let engine = &self.spec.engine;
|
||||
let genesis_header = self.spec.genesis_header();
|
||||
let mut db_result = get_temp_journal_db();
|
||||
@ -265,7 +265,7 @@ impl MiningBlockChainClient for TestBlockChainClient {
|
||||
self.spec.ensure_db_good(db.as_hashdb_mut());
|
||||
|
||||
let last_hashes = vec![genesis_header.hash()];
|
||||
OpenBlock::new(
|
||||
let mut open_block = OpenBlock::new(
|
||||
engine.deref(),
|
||||
self.vm_factory(),
|
||||
Default::default(),
|
||||
@ -273,10 +273,13 @@ impl MiningBlockChainClient for TestBlockChainClient {
|
||||
db,
|
||||
&genesis_header,
|
||||
last_hashes,
|
||||
Address::zero(),
|
||||
(3141562.into(), 31415620.into()),
|
||||
vec![]
|
||||
).expect("Opening block for tests will not fail.")
|
||||
author,
|
||||
gas_range_target,
|
||||
extra_data
|
||||
).expect("Opening block for tests will not fail.");
|
||||
// TODO [todr] Override timestamp for predictability (set_timestamp_now kind of sucks)
|
||||
open_block.set_timestamp(10_000_000);
|
||||
open_block
|
||||
}
|
||||
|
||||
fn vm_factory(&self) -> &EvmFactory {
|
||||
|
@ -16,7 +16,8 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use util::{RwLock, U256, H256};
|
||||
use std::time::{Instant, Duration};
|
||||
use util::{Mutex, U256, H256};
|
||||
|
||||
/// External miner interface.
|
||||
pub trait ExternalMinerService: Send + Sync {
|
||||
@ -25,50 +26,50 @@ pub trait ExternalMinerService: Send + Sync {
|
||||
|
||||
/// Total hashrate.
|
||||
fn hashrate(&self) -> U256;
|
||||
|
||||
/// Returns true if external miner is mining.
|
||||
fn is_mining(&self) -> bool;
|
||||
}
|
||||
|
||||
/// External Miner.
|
||||
pub struct ExternalMiner {
|
||||
hashrates: Arc<RwLock<HashMap<H256, U256>>>,
|
||||
hashrates: Arc<Mutex<HashMap<H256, (Instant, U256)>>>,
|
||||
}
|
||||
|
||||
impl Default for ExternalMiner {
|
||||
fn default() -> Self {
|
||||
ExternalMiner {
|
||||
hashrates: Arc::new(RwLock::new(HashMap::new())),
|
||||
hashrates: Arc::new(Mutex::new(HashMap::new())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExternalMiner {
|
||||
/// Creates new external miner with prefilled hashrates.
|
||||
pub fn new(hashrates: Arc<RwLock<HashMap<H256, U256>>>) -> Self {
|
||||
pub fn new(hashrates: Arc<Mutex<HashMap<H256, (Instant, U256)>>>) -> Self {
|
||||
ExternalMiner {
|
||||
hashrates: hashrates
|
||||
hashrates: hashrates,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ENTRY_TIMEOUT: u64 = 2;
|
||||
|
||||
impl ExternalMinerService for ExternalMiner {
|
||||
fn submit_hashrate(&self, hashrate: U256, id: H256) {
|
||||
self.hashrates.write().insert(id, hashrate);
|
||||
self.hashrates.lock().insert(id, (Instant::now() + Duration::from_secs(ENTRY_TIMEOUT), hashrate));
|
||||
}
|
||||
|
||||
fn hashrate(&self) -> U256 {
|
||||
self.hashrates.read().iter().fold(U256::from(0), |sum, (_, v)| sum + *v)
|
||||
}
|
||||
|
||||
fn is_mining(&self) -> bool {
|
||||
!self.hashrates.read().is_empty()
|
||||
let mut hashrates = self.hashrates.lock();
|
||||
let h = hashrates.drain().filter(|&(_, (t, _))| t > Instant::now()).collect();
|
||||
*hashrates = h;
|
||||
hashrates.iter().fold(U256::from(0), |sum, (_, &(_, v))| sum + v)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
use util::{H256, U256};
|
||||
|
||||
fn miner() -> ExternalMiner {
|
||||
@ -76,16 +77,18 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_that_is_mining_if_there_is_at_least_one_entry() {
|
||||
fn it_should_forget_old_hashrates() {
|
||||
// given
|
||||
let m = miner();
|
||||
assert_eq!(m.is_mining(), false);
|
||||
assert_eq!(m.hashrate(), U256::from(0));
|
||||
m.submit_hashrate(U256::from(10), H256::from(1));
|
||||
assert_eq!(m.hashrate(), U256::from(10));
|
||||
|
||||
// when
|
||||
m.submit_hashrate(U256::from(10), H256::from(1));
|
||||
sleep(Duration::from_secs(3));
|
||||
|
||||
// then
|
||||
assert_eq!(m.is_mining(), true);
|
||||
assert_eq!(m.hashrate(), U256::from(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -780,6 +780,10 @@ impl MinerService for Miner {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_sealing(&self) -> bool {
|
||||
self.sealing_work.lock().queue.is_in_use()
|
||||
}
|
||||
|
||||
fn map_sealing_work<F, T>(&self, chain: &MiningBlockChainClient, f: F) -> Option<T> where F: FnOnce(&ClosedBlock) -> T {
|
||||
trace!(target: "miner", "map_sealing_work: entering");
|
||||
self.enable_and_prepare_sealing(chain);
|
||||
|
@ -150,6 +150,9 @@ pub trait MinerService : Send + Sync {
|
||||
/// Returns highest transaction nonce for given address.
|
||||
fn last_nonce(&self, address: &Address) -> Option<U256>;
|
||||
|
||||
/// Is it currently sealing?
|
||||
fn is_sealing(&self) -> bool;
|
||||
|
||||
/// Suggested gas price.
|
||||
fn sensible_gas_price(&self) -> U256 { 20000000000u64.into() }
|
||||
|
||||
|
@ -17,4 +17,4 @@
|
||||
//! Types used in the public api
|
||||
|
||||
#![allow(dead_code, unused_assignments, unused_variables)] // codegen issues
|
||||
include!(concat!(env!("OUT_DIR"), "/types.rs"));
|
||||
include!(concat!(env!("OUT_DIR"), "/mod.rs.in"));
|
||||
|
@ -138,7 +138,7 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
|
||||
let ok = store.change_password(&address, &old_pwd, &new_pwd).is_ok();
|
||||
Ok(format!("{}", ok))
|
||||
} else if args.cmd_list {
|
||||
let accounts = store.accounts();
|
||||
let accounts = try!(store.accounts());
|
||||
Ok(format_accounts(&accounts))
|
||||
} else if args.cmd_import {
|
||||
let src = try!(key_dir(&args.flag_src));
|
||||
|
@ -68,7 +68,7 @@ pub mod aes {
|
||||
use rcrypto::blockmodes::{CtrMode, CbcDecryptor, PkcsPadding};
|
||||
use rcrypto::aessafe::{AesSafe128Encryptor, AesSafe128Decryptor};
|
||||
use rcrypto::symmetriccipher::{Encryptor, Decryptor, SymmetricCipherError};
|
||||
use rcrypto::buffer::{RefReadBuffer, RefWriteBuffer};
|
||||
use rcrypto::buffer::{RefReadBuffer, RefWriteBuffer, WriteBuffer};
|
||||
|
||||
/// Encrypt a message
|
||||
pub fn encrypt(k: &[u8], iv: &[u8], plain: &[u8], dest: &mut [u8]) {
|
||||
@ -83,10 +83,12 @@ pub mod aes {
|
||||
}
|
||||
|
||||
/// Decrypt a message using cbc mode
|
||||
pub fn decrypt_cbc(k: &[u8], iv: &[u8], encrypted: &[u8], dest: &mut [u8]) -> Result<(), SymmetricCipherError> {
|
||||
pub fn decrypt_cbc(k: &[u8], iv: &[u8], encrypted: &[u8], dest: &mut [u8]) -> Result<usize, SymmetricCipherError> {
|
||||
let mut encryptor = CbcDecryptor::new(AesSafe128Decryptor::new(k), PkcsPadding, iv.to_vec());
|
||||
try!(encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut RefWriteBuffer::new(dest), true));
|
||||
Ok(())
|
||||
let len = dest.len();
|
||||
let mut buffer = RefWriteBuffer::new(dest);
|
||||
try!(encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut buffer, true));
|
||||
Ok(len - buffer.remaining())
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -109,4 +109,3 @@ macro_rules! impl_hash {
|
||||
impl_hash!(H128, 16);
|
||||
impl_hash!(H160, 20);
|
||||
impl_hash!(H256, 32);
|
||||
impl_hash!(H768, 96);
|
||||
|
@ -11,10 +11,10 @@ mod version;
|
||||
pub use self::cipher::{Cipher, CipherSer, CipherSerParams, Aes128Ctr};
|
||||
pub use self::crypto::Crypto;
|
||||
pub use self::error::Error;
|
||||
pub use self::hash::{H128, H160, H256, H768};
|
||||
pub use self::hash::{H128, H160, H256};
|
||||
pub use self::id::UUID;
|
||||
pub use self::kdf::{Kdf, KdfSer, Prf, Pbkdf2, Scrypt, KdfSerParams};
|
||||
pub use self::key_file::KeyFile;
|
||||
pub use self::presale::PresaleWallet;
|
||||
pub use self::presale::{PresaleWallet, Encseed};
|
||||
pub use self::version::Version;
|
||||
|
||||
|
@ -1,10 +1,34 @@
|
||||
use std::io::Read;
|
||||
use std::ops::Deref;
|
||||
use serde_json;
|
||||
use super::{H160, H768};
|
||||
use serde::{Deserialize, Deserializer, Error};
|
||||
use rustc_serialize::hex::FromHex;
|
||||
use super::{H160};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Encseed(Vec<u8>);
|
||||
|
||||
impl Deref for Encseed {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Deserialize for Encseed {
|
||||
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
|
||||
where D: Deserializer
|
||||
{
|
||||
let s = try!(String::deserialize(deserializer));
|
||||
let data = try!(s.from_hex().map_err(|e| Error::custom(format!("Invalid hex value {}", e))));
|
||||
Ok(Encseed(data))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub struct PresaleWallet {
|
||||
pub encseed: H768,
|
||||
pub encseed: Encseed,
|
||||
#[serde(rename = "ethaddr")]
|
||||
pub address: H160,
|
||||
}
|
||||
@ -19,7 +43,8 @@ impl PresaleWallet {
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
use serde_json;
|
||||
use json::{PresaleWallet, H160, H768};
|
||||
use rustc_serialize::hex::FromHex;
|
||||
use json::{PresaleWallet, H160, Encseed};
|
||||
|
||||
#[test]
|
||||
fn presale_wallet() {
|
||||
@ -32,7 +57,27 @@ mod tests {
|
||||
} "#;
|
||||
|
||||
let expected = PresaleWallet {
|
||||
encseed: H768::from_str("137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066").unwrap(),
|
||||
encseed: Encseed("137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066".from_hex().unwrap()),
|
||||
address: H160::from_str("ede84640d1a1d3e06902048e67aa7db8d52c2ce1").unwrap(),
|
||||
};
|
||||
|
||||
let wallet: PresaleWallet = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(expected, wallet);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn long_presale_wallet() {
|
||||
let json = r#"
|
||||
{
|
||||
"encseed":
|
||||
"137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0d",
|
||||
"ethaddr": "ede84640d1a1d3e06902048e67aa7db8d52c2ce1",
|
||||
"email": "123@gmail.com",
|
||||
"btcaddr": "1JvqEc6WLhg6GnyrLBe2ztPAU28KRfuseH"
|
||||
} "#;
|
||||
|
||||
let expected = PresaleWallet {
|
||||
encseed: Encseed("137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0d".from_hex().unwrap()),
|
||||
address: H160::from_str("ede84640d1a1d3e06902048e67aa7db8d52c2ce1").unwrap(),
|
||||
};
|
||||
|
||||
|
@ -10,7 +10,7 @@ use {crypto, Error};
|
||||
|
||||
pub struct PresaleWallet {
|
||||
iv: [u8; 16],
|
||||
ciphertext: [u8; 80],
|
||||
ciphertext: Vec<u8>,
|
||||
address: Address,
|
||||
}
|
||||
|
||||
@ -19,8 +19,8 @@ impl From<json::PresaleWallet> for PresaleWallet {
|
||||
let mut iv = [0u8; 16];
|
||||
iv.copy_from_slice(&wallet.encseed[..16]);
|
||||
|
||||
let mut ciphertext = [0u8; 80];
|
||||
ciphertext.copy_from_slice(&wallet.encseed[16..]);
|
||||
let mut ciphertext = vec![];
|
||||
ciphertext.extend_from_slice(&wallet.encseed[16..]);
|
||||
|
||||
PresaleWallet {
|
||||
iv: iv,
|
||||
@ -42,10 +42,11 @@ impl PresaleWallet {
|
||||
let mut derived_key = vec![0u8; 16];
|
||||
pbkdf2(&mut h_mac, password.as_bytes(), 2000, &mut derived_key);
|
||||
|
||||
let mut key = [0u8; 64];
|
||||
try!(crypto::aes::decrypt_cbc(&derived_key, &self.iv, &self.ciphertext, &mut key).map_err(|_| Error::InvalidPassword));
|
||||
let mut key = vec![0; self.ciphertext.len()];
|
||||
let len = try!(crypto::aes::decrypt_cbc(&derived_key, &self.iv, &self.ciphertext, &mut key).map_err(|_| Error::InvalidPassword));
|
||||
let unpadded = &key[..len];
|
||||
|
||||
let secret = Secret::from(key.keccak256());
|
||||
let secret = Secret::from(unpadded.keccak256());
|
||||
if let Ok(kp) = KeyPair::from_secret(secret) {
|
||||
if kp.address() == self.address {
|
||||
return Ok(kp)
|
||||
|
@ -95,3 +95,58 @@ pub fn register(reg: &mut rustc_plugin::Registry) {
|
||||
|
||||
reg.register_attribute("ipc".to_owned(), AttributeType::Normal);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error { InvalidFileName, ExpandFailure }
|
||||
|
||||
pub fn derive_ipc(src_path: &str) -> Result<(), Error> {
|
||||
use std::env;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
let file_name = try!(PathBuf::from(src_path).file_name().ok_or(Error::InvalidFileName).map(|val| val.to_str().unwrap().to_owned()));
|
||||
|
||||
let mut intermediate_file_name = file_name.clone();
|
||||
intermediate_file_name.push_str(".rpc.in");
|
||||
|
||||
let intermediate_path = Path::new(&out_dir).join(&intermediate_file_name);
|
||||
let final_path = Path::new(&out_dir).join(&file_name);
|
||||
|
||||
{
|
||||
let mut registry = syntex::Registry::new();
|
||||
register(&mut registry);
|
||||
if let Err(_) = registry.expand("", &Path::new(src_path), &intermediate_path) {
|
||||
// will be reported by compiler
|
||||
return Err(Error::ExpandFailure)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut registry = syntex::Registry::new();
|
||||
register(&mut registry);
|
||||
if let Err(_) = registry.expand("", &intermediate_path, &final_path) {
|
||||
// will be reported by compiler
|
||||
return Err(Error::ExpandFailure)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn derive_binary(src_path: &str) -> Result<(), Error> {
|
||||
use std::env;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
let file_name = try!(PathBuf::from(src_path).file_name().ok_or(Error::InvalidFileName).map(|val| val.to_str().unwrap().to_owned()));
|
||||
let final_path = Path::new(&out_dir).join(&file_name);
|
||||
|
||||
let mut registry = syntex::Registry::new();
|
||||
register(&mut registry);
|
||||
if let Err(_) = registry.expand("", &Path::new(src_path), &final_path) {
|
||||
// will be reported by compiler
|
||||
return Err(Error::ExpandFailure)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -15,5 +15,4 @@ semver = "0.2"
|
||||
log = "0.3"
|
||||
|
||||
[build-dependencies]
|
||||
syntex = "*"
|
||||
ethcore-ipc-codegen = { path = "../codegen" }
|
||||
|
@ -14,30 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate syntex;
|
||||
extern crate ethcore_ipc_codegen as codegen;
|
||||
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
extern crate ethcore_ipc_codegen;
|
||||
|
||||
fn main() {
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
|
||||
// ipc pass
|
||||
{
|
||||
let src = Path::new("src/service.rs.in");
|
||||
let dst = Path::new(&out_dir).join("hypervisor_service_ipc.rs");
|
||||
let mut registry = syntex::Registry::new();
|
||||
codegen::register(&mut registry);
|
||||
registry.expand("", &src, &dst).unwrap();
|
||||
}
|
||||
|
||||
// serialization pass
|
||||
{
|
||||
let src = Path::new(&out_dir).join("hypervisor_service_ipc.rs");
|
||||
let dst = Path::new(&out_dir).join("hypervisor_service_cg.rs");
|
||||
let mut registry = syntex::Registry::new();
|
||||
codegen::register(&mut registry);
|
||||
registry.expand("", &src, &dst).unwrap();
|
||||
}
|
||||
ethcore_ipc_codegen::derive_ipc("src/service.rs.in").unwrap();
|
||||
}
|
||||
|
@ -17,4 +17,4 @@
|
||||
//! Parity interprocess hypervisor IPC service
|
||||
#![allow(dead_code, unused_assignments, unused_variables)] // codegen issues
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/hypervisor_service_cg.rs"));
|
||||
include!(concat!(env!("OUT_DIR"), "/service.rs.in"));
|
||||
|
@ -7,7 +7,6 @@ license = "GPL-3.0"
|
||||
[features]
|
||||
|
||||
[dependencies]
|
||||
jsonrpc-core = "2.0"
|
||||
ethcore-ipc = { path = "../rpc" }
|
||||
nanomsg = { git = "https://github.com/ethcore/nanomsg.rs.git" }
|
||||
log = "0.3"
|
||||
|
@ -19,14 +19,11 @@
|
||||
extern crate ethcore_ipc as ipc;
|
||||
extern crate nanomsg;
|
||||
#[macro_use] extern crate log;
|
||||
extern crate jsonrpc_core;
|
||||
use jsonrpc_core::IoHandler;
|
||||
|
||||
pub use ipc::{WithSocket, IpcInterface, IpcConfig};
|
||||
pub use nanomsg::Socket as NanoSocket;
|
||||
|
||||
use std::sync::*;
|
||||
use std::sync::atomic::*;
|
||||
use nanomsg::{Socket, Protocol, Error, Endpoint, PollRequest, PollFd, PollInOut};
|
||||
use std::ops::Deref;
|
||||
|
||||
@ -218,149 +215,14 @@ impl<S: ?Sized> Worker<S> where S: IpcInterface {
|
||||
}
|
||||
}
|
||||
|
||||
/// Error in handling JSON RPC request
|
||||
pub enum IoHandlerError {
|
||||
BadRequest,
|
||||
HandlerError,
|
||||
}
|
||||
|
||||
/// Worker to handle JSON RPC requests
|
||||
pub struct IoHandlerWorker {
|
||||
handler: Arc<IoHandler>,
|
||||
socket: Socket,
|
||||
_endpoint: Endpoint,
|
||||
poll: Vec<PollFd>,
|
||||
buf: Vec<u8>,
|
||||
}
|
||||
|
||||
/// IPC server for json-rpc handler (single thread)
|
||||
pub struct IoHandlerServer {
|
||||
is_stopping: Arc<AtomicBool>,
|
||||
is_stopped: Arc<AtomicBool>,
|
||||
handler: Arc<IoHandler>,
|
||||
socket_addr: String,
|
||||
}
|
||||
|
||||
impl IoHandlerServer {
|
||||
/// New IPC server for JSON RPC `handler` and ipc socket address `socket_addr`
|
||||
pub fn new(handler: &Arc<IoHandler>, socket_addr: &str) -> IoHandlerServer {
|
||||
IoHandlerServer {
|
||||
handler: handler.clone(),
|
||||
is_stopping: Arc::new(AtomicBool::new(false)),
|
||||
is_stopped: Arc::new(AtomicBool::new(true)),
|
||||
socket_addr: socket_addr.to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
/// IPC Server starts (non-blocking, in seprate thread)
|
||||
pub fn start(&self) -> Result<(), SocketError> {
|
||||
let mut worker = try!(IoHandlerWorker::new(&self.handler, &self.socket_addr));
|
||||
self.is_stopping.store(false, Ordering::Relaxed);
|
||||
let worker_is_stopping = self.is_stopping.clone();
|
||||
let worker_is_stopped = self.is_stopped.clone();
|
||||
|
||||
::std::thread::spawn(move || {
|
||||
worker_is_stopped.store(false, Ordering::Relaxed);
|
||||
while !worker_is_stopping.load(Ordering::Relaxed) {
|
||||
worker.poll()
|
||||
}
|
||||
worker_is_stopped.store(true, Ordering::Relaxed);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// IPC server stop (func will wait until effective stop)
|
||||
pub fn stop(&self) {
|
||||
self.is_stopping.store(true, Ordering::Relaxed);
|
||||
while !self.is_stopped.load(Ordering::Relaxed) {
|
||||
std::thread::sleep(std::time::Duration::from_millis(50));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for IoHandlerServer {
|
||||
fn drop(&mut self) {
|
||||
self.stop()
|
||||
}
|
||||
}
|
||||
|
||||
impl IoHandlerWorker {
|
||||
pub fn new(handler: &Arc<IoHandler>, socket_addr: &str) -> Result<IoHandlerWorker, SocketError> {
|
||||
let mut socket = try!(Socket::new(Protocol::Rep).map_err(|e| {
|
||||
warn!(target: "ipc", "Failed to create ipc socket: {:?}", e);
|
||||
SocketError::RequestLink
|
||||
}));
|
||||
|
||||
let endpoint = try!(socket.bind(socket_addr).map_err(|e| {
|
||||
warn!(target: "ipc", "Failed to bind socket to address '{}': {:?}", socket_addr, e);
|
||||
SocketError::RequestLink
|
||||
}));
|
||||
|
||||
let poll = vec![socket.new_pollfd(PollInOut::In)];
|
||||
|
||||
Ok(IoHandlerWorker {
|
||||
handler: handler.clone(),
|
||||
socket: socket,
|
||||
_endpoint: endpoint,
|
||||
poll: poll,
|
||||
buf: Vec::with_capacity(1024),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn poll(&mut self) {
|
||||
let mut request = PollRequest::new(&mut self.poll[..]);
|
||||
let _result_guard = Socket::poll(&mut request, POLL_TIMEOUT);
|
||||
let fd = request.get_fds()[0]; // guaranteed to exist and be the only one
|
||||
// because contains only immutable socket field as a member
|
||||
if !fd.can_read() {
|
||||
return;
|
||||
}
|
||||
|
||||
unsafe { self.buf.set_len(0); }
|
||||
match self.socket.nb_read_to_end(&mut self.buf) {
|
||||
Ok(0) => {
|
||||
warn!(target: "ipc", "RPC empty message received");
|
||||
return;
|
||||
},
|
||||
Ok(_) => {
|
||||
let rpc_msg = match String::from_utf8(self.buf.clone()) {
|
||||
Ok(val) => val,
|
||||
Err(e) => {
|
||||
warn!(target: "ipc", "RPC decoding error (utf-8): {:?}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let response: Option<String> = self.handler.handle_request(&rpc_msg);
|
||||
if let Some(response_str) = response {
|
||||
let response_bytes = response_str.into_bytes();
|
||||
if let Err(e) = self.socket.nb_write(&response_bytes) {
|
||||
warn!(target: "ipc", "Failed to write response: {:?}", e);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(Error::TryAgain) => {
|
||||
// no data
|
||||
},
|
||||
Err(x) => {
|
||||
warn!(target: "ipc", "Error polling connections {:?}", x);
|
||||
panic!("IPC RPC fatal error");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod service_tests {
|
||||
|
||||
use super::{Worker, IoHandlerServer};
|
||||
use super::Worker;
|
||||
use ipc::*;
|
||||
use std::io::{Read, Write};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use nanomsg::{Socket, Protocol, Endpoint};
|
||||
use jsonrpc_core;
|
||||
use jsonrpc_core::{IoHandler, Value, Params, MethodCommand};
|
||||
|
||||
struct TestInvoke {
|
||||
method_num: u16,
|
||||
@ -400,15 +262,6 @@ mod service_tests {
|
||||
(socket, endpoint)
|
||||
}
|
||||
|
||||
fn dummy_request(addr: &str, buf: &[u8]) -> Vec<u8> {
|
||||
let mut socket = Socket::new(Protocol::Req).unwrap();
|
||||
let _endpoint = socket.connect(addr).unwrap();
|
||||
socket.write(buf).unwrap();
|
||||
let mut buf = Vec::new();
|
||||
socket.read_to_end(&mut buf).unwrap();
|
||||
buf
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_create_worker() {
|
||||
let worker = Worker::<DummyService>::new(&Arc::new(DummyService::new()));
|
||||
@ -462,29 +315,4 @@ mod service_tests {
|
||||
assert_eq!(0, worker.service.methods_stack.read().unwrap()[0].method_num);
|
||||
assert_eq!(vec![0u8; 1024*1024-2], worker.service.methods_stack.read().unwrap()[0].params);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_jsonrpc_handler() {
|
||||
let url = "ipc:///tmp/parity-test50.ipc";
|
||||
|
||||
struct SayHello;
|
||||
impl MethodCommand for SayHello {
|
||||
fn execute(&self, _params: Params) -> Result<Value, jsonrpc_core::Error> {
|
||||
Ok(Value::String("hello".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
let io = Arc::new(IoHandler::new());
|
||||
io.add_method("say_hello", SayHello);
|
||||
|
||||
let request = r#"{"jsonrpc": "2.0", "method": "say_hello", "params": [42, 23], "id": 1}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":"hello","id":1}"#;
|
||||
|
||||
let server = IoHandlerServer::new(&io, url);
|
||||
server.start().unwrap();
|
||||
|
||||
assert_eq!(String::from_utf8(dummy_request(url, request.as_bytes())).unwrap(), response.to_string());
|
||||
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
@ -16,4 +16,4 @@
|
||||
|
||||
#![allow(dead_code, unused_assignments, unused_variables)] // codegen issues
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/binary.rs"));
|
||||
include!(concat!(env!("OUT_DIR"), "/binary.rs.in"));
|
||||
|
@ -14,76 +14,11 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate syntex;
|
||||
extern crate ethcore_ipc_codegen as codegen;
|
||||
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
use std::process::exit;
|
||||
|
||||
pub fn main() {
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
|
||||
// rpc pass
|
||||
if {
|
||||
let src = Path::new("nested.rs.in");
|
||||
let dst = Path::new(&out_dir).join("nested_ipc.rs");
|
||||
let mut registry = syntex::Registry::new();
|
||||
codegen::register(&mut registry);
|
||||
registry.expand("", &src, &dst).is_ok()
|
||||
}
|
||||
// serialization pass
|
||||
{
|
||||
let src = Path::new(&out_dir).join("nested_ipc.rs");
|
||||
let dst = Path::new(&out_dir).join("nested_cg.rs");
|
||||
let mut registry = syntex::Registry::new();
|
||||
codegen::register(&mut registry);
|
||||
registry.expand("", &src, &dst).unwrap();
|
||||
}
|
||||
|
||||
// rpc pass
|
||||
if {
|
||||
let src = Path::new("service.rs.in");
|
||||
let dst = Path::new(&out_dir).join("service_ipc.rs");
|
||||
let mut registry = syntex::Registry::new();
|
||||
codegen::register(&mut registry);
|
||||
registry.expand("", &src, &dst).is_ok()
|
||||
}
|
||||
// serialization pass
|
||||
{
|
||||
let src = Path::new(&out_dir).join("service_ipc.rs");
|
||||
let dst = Path::new(&out_dir).join("service_cg.rs");
|
||||
let mut registry = syntex::Registry::new();
|
||||
codegen::register(&mut registry);
|
||||
registry.expand("", &src, &dst).unwrap();
|
||||
}
|
||||
|
||||
// rpc pass
|
||||
if {
|
||||
let src = Path::new("with_attrs.rs.in");
|
||||
let dst = Path::new(&out_dir).join("with_attrs_ipc.rs");
|
||||
let mut registry = syntex::Registry::new();
|
||||
codegen::register(&mut registry);
|
||||
registry.expand("", &src, &dst).is_ok()
|
||||
}
|
||||
// serialization pass
|
||||
{
|
||||
let src = Path::new(&out_dir).join("with_attrs_ipc.rs");
|
||||
let dst = Path::new(&out_dir).join("with_attrs_cg.rs");
|
||||
let mut registry = syntex::Registry::new();
|
||||
codegen::register(&mut registry);
|
||||
registry.expand("", &src, &dst).unwrap();
|
||||
}
|
||||
|
||||
// rpc pass
|
||||
{
|
||||
let src = Path::new("binary.rs.in");
|
||||
let dst = Path::new(&out_dir).join("binary.rs");
|
||||
let mut registry = syntex::Registry::new();
|
||||
codegen::register(&mut registry);
|
||||
if let Err(err_msg) = registry.expand("", &src, &dst) {
|
||||
println!("error: {}", err_msg);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
codegen::derive_ipc("nested.rs.in").unwrap();
|
||||
codegen::derive_ipc("service.rs.in").unwrap();
|
||||
codegen::derive_ipc("with_attrs.rs.in").unwrap();
|
||||
codegen::derive_binary("binary.rs.in").unwrap();
|
||||
}
|
||||
|
@ -15,4 +15,4 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#![allow(dead_code, unused_assignments, unused_variables)] // codegen issues
|
||||
include!(concat!(env!("OUT_DIR"), "/nested_cg.rs"));
|
||||
include!(concat!(env!("OUT_DIR"), "/nested.rs.in"));
|
||||
|
@ -15,4 +15,4 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#![allow(dead_code, unused_assignments, unused_variables)] // codegen issues
|
||||
include!(concat!(env!("OUT_DIR"), "/service_cg.rs"));
|
||||
include!(concat!(env!("OUT_DIR"), "/service.rs.in"));
|
||||
|
@ -15,4 +15,4 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#![allow(dead_code, unused_assignments, unused_variables)] // codegen issues
|
||||
include!(concat!(env!("OUT_DIR"), "/with_attrs_cg.rs"));
|
||||
include!(concat!(env!("OUT_DIR"), "/with_attrs.rs.in"));
|
||||
|
@ -74,6 +74,9 @@ Account Options:
|
||||
[default: 8180].
|
||||
--signer-path PATH Specify directory where Signer UIs tokens should
|
||||
be stored. [default: $HOME/.parity/signer]
|
||||
--signer-no-validation Disable Origin and Host headers validation for
|
||||
Trusted Signer. WARNING: INSECURE. Used only for
|
||||
development.
|
||||
|
||||
Networking Options:
|
||||
--no-network Disable p2p networking.
|
||||
@ -337,6 +340,7 @@ pub struct Args {
|
||||
pub flag_no_signer: bool,
|
||||
pub flag_signer_port: u16,
|
||||
pub flag_signer_path: String,
|
||||
pub flag_signer_no_validation: bool,
|
||||
pub flag_force_sealing: bool,
|
||||
pub flag_reseal_on_txs: String,
|
||||
pub flag_reseal_min_period: u64,
|
||||
|
@ -303,6 +303,7 @@ impl Configuration {
|
||||
enabled: self.signer_enabled(),
|
||||
port: self.args.flag_signer_port,
|
||||
signer_path: self.directories().signer,
|
||||
skip_origin_validation: self.args.flag_signer_no_validation,
|
||||
}
|
||||
}
|
||||
|
||||
@ -789,6 +790,19 @@ mod tests {
|
||||
assert_eq!(conf0.signer_enabled(), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_parse_signer_allow_all_flag() {
|
||||
// given
|
||||
|
||||
// when
|
||||
let conf0 = parse(&["parity", "--signer-no-validation"]);
|
||||
let conf1 = parse(&["parity"]);
|
||||
|
||||
// then
|
||||
assert_eq!(conf0.args.flag_signer_no_validation, true);
|
||||
assert_eq!(conf1.args.flag_signer_no_validation, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_bail_on_empty_line_in_reserved_peers() {
|
||||
let temp = RandomTempPath::new();
|
||||
|
@ -102,7 +102,7 @@ pub fn to_address(s: Option<String>) -> Result<Address, String> {
|
||||
|
||||
pub fn to_addresses(s: &Option<String>) -> Result<Vec<Address>, String> {
|
||||
match *s {
|
||||
Some(ref adds) if adds.is_empty() => adds.split(',')
|
||||
Some(ref adds) if !adds.is_empty() => adds.split(',')
|
||||
.map(|a| clean_0x(a).parse().map_err(|_| format!("Invalid address: {:?}", a)))
|
||||
.collect(),
|
||||
_ => Ok(Vec::new()),
|
||||
@ -299,7 +299,7 @@ mod tests {
|
||||
use util::{U256};
|
||||
use ethcore::client::{Mode, BlockID};
|
||||
use ethcore::miner::PendingSet;
|
||||
use super::{to_duration, to_mode, to_block_id, to_u256, to_pending_set, to_address, to_price, geth_ipc_path, to_bootnodes};
|
||||
use super::{to_duration, to_mode, to_block_id, to_u256, to_pending_set, to_address, to_addresses, to_price, geth_ipc_path, to_bootnodes};
|
||||
|
||||
#[test]
|
||||
fn test_to_duration() {
|
||||
@ -370,6 +370,18 @@ mod tests {
|
||||
assert_eq!(to_address(None).unwrap(), Default::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_addresses() {
|
||||
let addresses = to_addresses(&Some("0xD9A111feda3f362f55Ef1744347CDC8Dd9964a41,D9A111feda3f362f55Ef1744347CDC8Dd9964a42".into())).unwrap();
|
||||
assert_eq!(
|
||||
addresses,
|
||||
vec![
|
||||
"D9A111feda3f362f55Ef1744347CDC8Dd9964a41".parse().unwrap(),
|
||||
"D9A111feda3f362f55Ef1744347CDC8Dd9964a42".parse().unwrap(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(feature = "dev", allow(float_cmp))]
|
||||
fn test_to_price() {
|
||||
|
@ -104,8 +104,8 @@ pub struct Dependencies {
|
||||
pub external_miner: Arc<ExternalMiner>,
|
||||
pub logger: Arc<RotatingLogger>,
|
||||
pub settings: Arc<NetworkSettings>,
|
||||
pub allow_pending_receipt_query: bool,
|
||||
pub net_service: Arc<ManageNetwork>,
|
||||
pub geth_compatibility: bool,
|
||||
}
|
||||
|
||||
fn to_modules(apis: &[Api]) -> BTreeMap<String, String> {
|
||||
@ -163,7 +163,10 @@ pub fn setup_rpc<T: Extendable>(server: T, deps: Arc<Dependencies>, apis: ApiSet
|
||||
&deps.secret_store,
|
||||
&deps.miner,
|
||||
&deps.external_miner,
|
||||
deps.allow_pending_receipt_query
|
||||
EthClientOptions {
|
||||
allow_pending_receipt_query: !deps.geth_compatibility,
|
||||
send_block_number_in_get_work: !deps.geth_compatibility,
|
||||
}
|
||||
);
|
||||
server.add_delegate(client.to_delegate());
|
||||
|
||||
|
@ -211,8 +211,8 @@ pub fn execute(cmd: RunCmd) -> Result<(), String> {
|
||||
external_miner: external_miner.clone(),
|
||||
logger: logger.clone(),
|
||||
settings: Arc::new(cmd.net_settings.clone()),
|
||||
allow_pending_receipt_query: !cmd.geth_compatibility,
|
||||
net_service: manage_network.clone()
|
||||
net_service: manage_network.clone(),
|
||||
geth_compatibility: cmd.geth_compatibility,
|
||||
});
|
||||
|
||||
let dependencies = rpc::Dependencies {
|
||||
@ -311,7 +311,7 @@ fn prepare_account_provider(dirs: &Directories, cfg: AccountsConfig) -> Result<A
|
||||
|
||||
for a in cfg.unlocked_accounts {
|
||||
if passwords.iter().find(|p| account_service.unlock_account_permanently(a, (*p).clone()).is_ok()).is_none() {
|
||||
return Err(format!("No password given to unlock account {}. Pass the password using `--password`.", a));
|
||||
return Err(format!("No password found to unlock account {}. Make sure valid password is present in files passed using `--password`.", a));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,7 @@ pub struct Configuration {
|
||||
pub enabled: bool,
|
||||
pub port: u16,
|
||||
pub signer_path: String,
|
||||
pub skip_origin_validation: bool,
|
||||
}
|
||||
|
||||
impl Default for Configuration {
|
||||
@ -40,6 +41,7 @@ impl Default for Configuration {
|
||||
enabled: true,
|
||||
port: 8180,
|
||||
signer_path: replace_home("$HOME/.parity/signer"),
|
||||
skip_origin_validation: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -89,6 +91,11 @@ fn do_start(conf: Configuration, deps: Dependencies) -> Result<SignerServer, Str
|
||||
deps.apis.signer_queue.clone(),
|
||||
codes_path(conf.signer_path),
|
||||
);
|
||||
if conf.skip_origin_validation {
|
||||
warn!("{}", Colour::Red.bold().paint("*** INSECURE *** Running Trusted Signer with no origin validation."));
|
||||
info!("If you do not intend this, exit now.");
|
||||
}
|
||||
let server = server.skip_origin_validation(conf.skip_origin_validation);
|
||||
let server = rpc_apis::setup_rpc(server, deps.apis, rpc_apis::ApiSet::SafeContext);
|
||||
server.start(addr)
|
||||
};
|
||||
|
@ -21,5 +21,5 @@ mod signing_queue;
|
||||
|
||||
pub use self::poll_manager::PollManager;
|
||||
pub use self::poll_filter::PollFilter;
|
||||
pub use self::requests::{TransactionRequest, TransactionConfirmation, CallRequest};
|
||||
pub use self::requests::{TransactionRequest, FilledTransactionRequest, ConfirmationRequest, ConfirmationPayload, CallRequest};
|
||||
pub use self::signing_queue::{ConfirmationsQueue, ConfirmationPromise, ConfirmationResult, SigningQueue, QueueEvent};
|
||||
|
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use util::{Address, U256};
|
||||
use util::{Address, U256, Bytes, H256};
|
||||
|
||||
/// Transaction request coming from RPC
|
||||
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
|
||||
@ -30,18 +30,42 @@ pub struct TransactionRequest {
|
||||
/// Value of transaction in wei
|
||||
pub value: Option<U256>,
|
||||
/// Additional data sent with transaction
|
||||
pub data: Option<Vec<u8>>,
|
||||
pub data: Option<Bytes>,
|
||||
/// Transaction's nonce
|
||||
pub nonce: Option<U256>,
|
||||
}
|
||||
|
||||
/// Transaction confirmation waiting in a queue
|
||||
/// Transaction request coming from RPC with default values filled in.
|
||||
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
|
||||
pub struct TransactionConfirmation {
|
||||
/// Id of this confirmation
|
||||
pub id: U256,
|
||||
/// TransactionRequest
|
||||
pub transaction: TransactionRequest,
|
||||
pub struct FilledTransactionRequest {
|
||||
/// Sender
|
||||
pub from: Address,
|
||||
/// Recipient
|
||||
pub to: Option<Address>,
|
||||
/// Gas Price
|
||||
pub gas_price: U256,
|
||||
/// Gas
|
||||
pub gas: U256,
|
||||
/// Value of transaction in wei
|
||||
pub value: U256,
|
||||
/// Additional data sent with transaction
|
||||
pub data: Bytes,
|
||||
/// Transaction's nonce
|
||||
pub nonce: Option<U256>,
|
||||
}
|
||||
|
||||
impl From<FilledTransactionRequest> for TransactionRequest {
|
||||
fn from(r: FilledTransactionRequest) -> Self {
|
||||
TransactionRequest {
|
||||
from: r.from,
|
||||
to: r.to,
|
||||
gas_price: Some(r.gas_price),
|
||||
gas: Some(r.gas),
|
||||
value: Some(r.value),
|
||||
data: Some(r.data),
|
||||
nonce: r.nonce,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Call request
|
||||
@ -62,3 +86,21 @@ pub struct CallRequest {
|
||||
/// Nonce
|
||||
pub nonce: Option<U256>,
|
||||
}
|
||||
|
||||
/// Confirmation object
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct ConfirmationRequest {
|
||||
/// Id of this confirmation
|
||||
pub id: U256,
|
||||
/// Payload to confirm
|
||||
pub payload: ConfirmationPayload,
|
||||
}
|
||||
|
||||
/// Payload to confirm in Trusted Signer
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum ConfirmationPayload {
|
||||
/// Transaction
|
||||
Transaction(FilledTransactionRequest),
|
||||
/// Sign request
|
||||
Sign(Address, H256),
|
||||
}
|
||||
|
@ -17,10 +17,10 @@
|
||||
use std::thread;
|
||||
use std::time::{Instant, Duration};
|
||||
use std::sync::{mpsc, Arc};
|
||||
use std::collections::HashMap;
|
||||
use std::collections::BTreeMap;
|
||||
use jsonrpc_core;
|
||||
use util::{Mutex, RwLock, U256};
|
||||
use v1::helpers::{TransactionRequest, TransactionConfirmation};
|
||||
use v1::helpers::{ConfirmationRequest, ConfirmationPayload};
|
||||
|
||||
/// Result that can be returned from JSON RPC.
|
||||
pub type RpcResult = Result<jsonrpc_core::Value, jsonrpc_core::Error>;
|
||||
@ -54,41 +54,41 @@ pub type QueueEventReceiver = mpsc::Receiver<QueueEvent>;
|
||||
pub trait SigningQueue: Send + Sync {
|
||||
/// Add new request to the queue.
|
||||
/// Returns a `ConfirmationPromise` that can be used to await for resolution of given request.
|
||||
fn add_request(&self, transaction: TransactionRequest) -> ConfirmationPromise;
|
||||
fn add_request(&self, request: ConfirmationPayload) -> ConfirmationPromise;
|
||||
|
||||
/// Removes a request from the queue.
|
||||
/// Notifies possible token holders that transaction was rejected.
|
||||
fn request_rejected(&self, id: U256) -> Option<TransactionConfirmation>;
|
||||
/// Notifies possible token holders that request was rejected.
|
||||
fn request_rejected(&self, id: U256) -> Option<ConfirmationRequest>;
|
||||
|
||||
/// Removes a request from the queue.
|
||||
/// Notifies possible token holders that transaction was confirmed and given hash was assigned.
|
||||
fn request_confirmed(&self, id: U256, result: RpcResult) -> Option<TransactionConfirmation>;
|
||||
/// Notifies possible token holders that request was confirmed and given hash was assigned.
|
||||
fn request_confirmed(&self, id: U256, result: RpcResult) -> Option<ConfirmationRequest>;
|
||||
|
||||
/// Returns a request if it is contained in the queue.
|
||||
fn peek(&self, id: &U256) -> Option<TransactionConfirmation>;
|
||||
fn peek(&self, id: &U256) -> Option<ConfirmationRequest>;
|
||||
|
||||
/// Return copy of all the requests in the queue.
|
||||
fn requests(&self) -> Vec<TransactionConfirmation>;
|
||||
fn requests(&self) -> Vec<ConfirmationRequest>;
|
||||
|
||||
/// Returns number of transactions awaiting confirmation.
|
||||
/// Returns number of requests awaiting confirmation.
|
||||
fn len(&self) -> usize;
|
||||
|
||||
/// Returns true if there are no transactions awaiting confirmation.
|
||||
/// Returns true if there are no requests awaiting confirmation.
|
||||
fn is_empty(&self) -> bool;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
/// Result of a pending transaction.
|
||||
/// Result of a pending confirmation request.
|
||||
pub enum ConfirmationResult {
|
||||
/// The transaction has not yet been confirmed nor rejected.
|
||||
/// The request has not yet been confirmed nor rejected.
|
||||
Waiting,
|
||||
/// The transaction has been rejected.
|
||||
/// The request has been rejected.
|
||||
Rejected,
|
||||
/// The transaction has been confirmed.
|
||||
/// The request has been confirmed.
|
||||
Confirmed(RpcResult),
|
||||
}
|
||||
|
||||
/// Time you need to confirm the transaction in UI.
|
||||
/// Time you need to confirm the request in UI.
|
||||
/// This is the amount of time token holder will wait before
|
||||
/// returning `None`.
|
||||
/// Unless we have a multi-threaded RPC this will lock
|
||||
@ -100,12 +100,14 @@ const QUEUE_TIMEOUT_DURATION_SEC : u64 = 20;
|
||||
pub struct ConfirmationToken {
|
||||
result: Arc<Mutex<ConfirmationResult>>,
|
||||
handle: thread::Thread,
|
||||
request: TransactionConfirmation,
|
||||
request: ConfirmationRequest,
|
||||
timeout: Duration,
|
||||
}
|
||||
|
||||
pub struct ConfirmationPromise {
|
||||
id: U256,
|
||||
result: Arc<Mutex<ConfirmationResult>>,
|
||||
timeout: Duration,
|
||||
}
|
||||
|
||||
impl ConfirmationToken {
|
||||
@ -121,6 +123,7 @@ impl ConfirmationToken {
|
||||
ConfirmationPromise {
|
||||
id: self.request.id,
|
||||
result: self.result.clone(),
|
||||
timeout: self.timeout,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -134,8 +137,7 @@ impl ConfirmationPromise {
|
||||
/// Returns `None` if transaction was rejected or timeout reached.
|
||||
/// Returns `Some(result)` if transaction was confirmed.
|
||||
pub fn wait_with_timeout(&self) -> Option<RpcResult> {
|
||||
let timeout = Duration::from_secs(QUEUE_TIMEOUT_DURATION_SEC);
|
||||
let res = self.wait_until(Instant::now() + timeout);
|
||||
let res = self.wait_until(Instant::now() + self.timeout);
|
||||
match res {
|
||||
ConfirmationResult::Confirmed(h) => Some(h),
|
||||
ConfirmationResult::Rejected | ConfirmationResult::Waiting => None,
|
||||
@ -146,11 +148,11 @@ impl ConfirmationPromise {
|
||||
pub fn result(&self) -> ConfirmationResult { self.wait_until(Instant::now()) }
|
||||
|
||||
/// Blocks current thread and awaits for
|
||||
/// resolution of the transaction (rejected / confirmed)
|
||||
/// Returns `None` if transaction was rejected or timeout reached.
|
||||
/// Returns `Some(result)` if transaction was confirmed.
|
||||
/// resolution of the request (rejected / confirmed)
|
||||
/// Returns `None` if request was rejected or timeout reached.
|
||||
/// Returns `Some(result)` if request was confirmed.
|
||||
pub fn wait_until(&self, deadline: Instant) -> ConfirmationResult {
|
||||
trace!(target: "own_tx", "Signer: Awaiting transaction confirmation... ({:?}).", self.id);
|
||||
trace!(target: "own_tx", "Signer: Awaiting confirmation... ({:?}).", self.id);
|
||||
loop {
|
||||
let now = Instant::now();
|
||||
// Check the result...
|
||||
@ -166,12 +168,13 @@ impl ConfirmationPromise {
|
||||
}
|
||||
}
|
||||
|
||||
/// Queue for all unconfirmed transactions.
|
||||
/// Queue for all unconfirmed requests.
|
||||
pub struct ConfirmationsQueue {
|
||||
id: Mutex<U256>,
|
||||
queue: RwLock<HashMap<U256, ConfirmationToken>>,
|
||||
queue: RwLock<BTreeMap<U256, ConfirmationToken>>,
|
||||
sender: Mutex<mpsc::Sender<QueueEvent>>,
|
||||
receiver: Mutex<Option<mpsc::Receiver<QueueEvent>>>,
|
||||
timeout: Duration,
|
||||
}
|
||||
|
||||
impl Default for ConfirmationsQueue {
|
||||
@ -180,14 +183,23 @@ impl Default for ConfirmationsQueue {
|
||||
|
||||
ConfirmationsQueue {
|
||||
id: Mutex::new(U256::from(0)),
|
||||
queue: RwLock::new(HashMap::new()),
|
||||
queue: RwLock::new(BTreeMap::new()),
|
||||
sender: Mutex::new(send),
|
||||
receiver: Mutex::new(Some(recv)),
|
||||
timeout: Duration::from_secs(QUEUE_TIMEOUT_DURATION_SEC),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfirmationsQueue {
|
||||
#[cfg(test)]
|
||||
/// Creates new confirmations queue with specified timeout
|
||||
pub fn with_timeout(timeout: Duration) -> Self {
|
||||
let mut queue = Self::default();
|
||||
queue.timeout = timeout;
|
||||
queue
|
||||
}
|
||||
|
||||
/// Blocks the thread and starts listening for notifications regarding all actions in the queue.
|
||||
/// For each event, `listener` callback will be invoked.
|
||||
/// This method can be used only once (only single consumer of events can exist).
|
||||
@ -221,9 +233,9 @@ impl ConfirmationsQueue {
|
||||
let _ = self.sender.lock().send(message);
|
||||
}
|
||||
|
||||
/// Removes transaction from this queue and notifies `ConfirmationPromise` holders about the result.
|
||||
/// Removes requests from this queue and notifies `ConfirmationPromise` holders about the result.
|
||||
/// Notifies also a receiver about that event.
|
||||
fn remove(&self, id: U256, result: Option<RpcResult>) -> Option<TransactionConfirmation> {
|
||||
fn remove(&self, id: U256, result: Option<RpcResult>) -> Option<ConfirmationRequest> {
|
||||
let token = self.queue.write().remove(&id);
|
||||
|
||||
if let Some(token) = token {
|
||||
@ -248,7 +260,7 @@ impl Drop for ConfirmationsQueue {
|
||||
}
|
||||
|
||||
impl SigningQueue for ConfirmationsQueue {
|
||||
fn add_request(&self, transaction: TransactionRequest) -> ConfirmationPromise {
|
||||
fn add_request(&self, request: ConfirmationPayload) -> ConfirmationPromise {
|
||||
// Increment id
|
||||
let id = {
|
||||
let mut last_id = self.id.lock();
|
||||
@ -257,16 +269,19 @@ impl SigningQueue for ConfirmationsQueue {
|
||||
};
|
||||
// Add request to queue
|
||||
let res = {
|
||||
debug!(target: "own_tx", "Signer: New entry ({:?}) in confirmation queue.", id);
|
||||
trace!(target: "own_tx", "Signer: ({:?}) : {:?}", id, request);
|
||||
|
||||
let mut queue = self.queue.write();
|
||||
queue.insert(id, ConfirmationToken {
|
||||
result: Arc::new(Mutex::new(ConfirmationResult::Waiting)),
|
||||
handle: thread::current(),
|
||||
request: TransactionConfirmation {
|
||||
request: ConfirmationRequest {
|
||||
id: id,
|
||||
transaction: transaction,
|
||||
payload: request,
|
||||
},
|
||||
timeout: self.timeout,
|
||||
});
|
||||
debug!(target: "own_tx", "Signer: New transaction ({:?}) in confirmation queue.", id);
|
||||
queue.get(&id).map(|token| token.as_promise()).expect("Token was just inserted.")
|
||||
};
|
||||
// Notify listeners
|
||||
@ -275,21 +290,21 @@ impl SigningQueue for ConfirmationsQueue {
|
||||
|
||||
}
|
||||
|
||||
fn peek(&self, id: &U256) -> Option<TransactionConfirmation> {
|
||||
fn peek(&self, id: &U256) -> Option<ConfirmationRequest> {
|
||||
self.queue.read().get(id).map(|token| token.request.clone())
|
||||
}
|
||||
|
||||
fn request_rejected(&self, id: U256) -> Option<TransactionConfirmation> {
|
||||
debug!(target: "own_tx", "Signer: Transaction rejected ({:?}).", id);
|
||||
fn request_rejected(&self, id: U256) -> Option<ConfirmationRequest> {
|
||||
debug!(target: "own_tx", "Signer: Request rejected ({:?}).", id);
|
||||
self.remove(id, None)
|
||||
}
|
||||
|
||||
fn request_confirmed(&self, id: U256, result: RpcResult) -> Option<TransactionConfirmation> {
|
||||
fn request_confirmed(&self, id: U256, result: RpcResult) -> Option<ConfirmationRequest> {
|
||||
debug!(target: "own_tx", "Signer: Transaction confirmed ({:?}).", id);
|
||||
self.remove(id, Some(result))
|
||||
}
|
||||
|
||||
fn requests(&self) -> Vec<TransactionConfirmation> {
|
||||
fn requests(&self) -> Vec<ConfirmationRequest> {
|
||||
let queue = self.queue.read();
|
||||
queue.values().map(|token| token.request.clone()).collect()
|
||||
}
|
||||
@ -312,20 +327,20 @@ mod test {
|
||||
use std::thread;
|
||||
use std::sync::Arc;
|
||||
use util::{Address, U256, H256, Mutex};
|
||||
use v1::helpers::{SigningQueue, ConfirmationsQueue, QueueEvent, TransactionRequest};
|
||||
use v1::helpers::{SigningQueue, ConfirmationsQueue, QueueEvent, FilledTransactionRequest, ConfirmationPayload};
|
||||
use v1::types::H256 as NH256;
|
||||
use jsonrpc_core::to_value;
|
||||
|
||||
fn request() -> TransactionRequest {
|
||||
TransactionRequest {
|
||||
fn request() -> ConfirmationPayload {
|
||||
ConfirmationPayload::Transaction(FilledTransactionRequest {
|
||||
from: Address::from(1),
|
||||
to: Some(Address::from(2)),
|
||||
gas_price: None,
|
||||
gas: None,
|
||||
value: Some(U256::from(10_000_000)),
|
||||
data: None,
|
||||
gas_price: 0.into(),
|
||||
gas: 10_000.into(),
|
||||
value: 10_000_000.into(),
|
||||
data: vec![],
|
||||
nonce: None,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -391,6 +406,6 @@ mod test {
|
||||
assert_eq!(all.len(), 1);
|
||||
let el = all.get(0).unwrap();
|
||||
assert_eq!(el.id, U256::from(1));
|
||||
assert_eq!(el.transaction, request);
|
||||
assert_eq!(el.payload, request);
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +47,23 @@ use v1::helpers::CallRequest as CRequest;
|
||||
use v1::impls::{default_gas_price, dispatch_transaction, error_codes};
|
||||
use serde;
|
||||
|
||||
/// Eth RPC options
|
||||
pub struct EthClientOptions {
|
||||
/// Returns receipt from pending blocks
|
||||
pub allow_pending_receipt_query: bool,
|
||||
/// Send additional block number when asking for work
|
||||
pub send_block_number_in_get_work: bool,
|
||||
}
|
||||
|
||||
impl Default for EthClientOptions {
|
||||
fn default() -> Self {
|
||||
EthClientOptions {
|
||||
allow_pending_receipt_query: true,
|
||||
send_block_number_in_get_work: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Eth rpc implementation.
|
||||
pub struct EthClient<C, S: ?Sized, M, EM> where
|
||||
C: MiningBlockChainClient,
|
||||
@ -60,7 +77,7 @@ pub struct EthClient<C, S: ?Sized, M, EM> where
|
||||
miner: Weak<M>,
|
||||
external_miner: Arc<EM>,
|
||||
seed_compute: Mutex<SeedHashCompute>,
|
||||
allow_pending_receipt_query: bool,
|
||||
options: EthClientOptions,
|
||||
}
|
||||
|
||||
impl<C, S: ?Sized, M, EM> EthClient<C, S, M, EM> where
|
||||
@ -70,7 +87,7 @@ impl<C, S: ?Sized, M, EM> EthClient<C, S, M, EM> where
|
||||
EM: ExternalMinerService {
|
||||
|
||||
/// Creates new EthClient.
|
||||
pub fn new(client: &Arc<C>, sync: &Arc<S>, accounts: &Arc<AccountProvider>, miner: &Arc<M>, em: &Arc<EM>, allow_pending_receipt_query: bool)
|
||||
pub fn new(client: &Arc<C>, sync: &Arc<S>, accounts: &Arc<AccountProvider>, miner: &Arc<M>, em: &Arc<EM>, options: EthClientOptions)
|
||||
-> EthClient<C, S, M, EM> {
|
||||
EthClient {
|
||||
client: Arc::downgrade(client),
|
||||
@ -79,7 +96,7 @@ impl<C, S: ?Sized, M, EM> EthClient<C, S, M, EM> where
|
||||
accounts: Arc::downgrade(accounts),
|
||||
external_miner: em.clone(),
|
||||
seed_compute: Mutex::new(SeedHashCompute::new()),
|
||||
allow_pending_receipt_query: allow_pending_receipt_query,
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
@ -316,7 +333,7 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
|
||||
fn is_mining(&self, params: Params) -> Result<Value, Error> {
|
||||
try!(self.active());
|
||||
match params {
|
||||
Params::None => to_value(&self.external_miner.is_mining()),
|
||||
Params::None => to_value(&(take_weak!(self.miner).is_sealing())),
|
||||
_ => Err(Error::invalid_params())
|
||||
}
|
||||
}
|
||||
@ -496,7 +513,7 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
|
||||
let miner = take_weak!(self.miner);
|
||||
let hash: H256 = hash.into();
|
||||
match miner.pending_receipts().get(&hash) {
|
||||
Some(receipt) if self.allow_pending_receipt_query => to_value(&Receipt::from(receipt.clone())),
|
||||
Some(receipt) if self.options.allow_pending_receipt_query => to_value(&Receipt::from(receipt.clone())),
|
||||
_ => {
|
||||
let client = take_weak!(self.client);
|
||||
let receipt = client.transaction_receipt(TransactionID::Hash(hash));
|
||||
@ -582,8 +599,13 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
|
||||
let pow_hash = b.hash();
|
||||
let target = Ethash::difficulty_to_boundary(b.block().header().difficulty());
|
||||
let seed_hash = self.seed_compute.lock().get_seedhash(b.block().header().number());
|
||||
|
||||
if self.options.send_block_number_in_get_work {
|
||||
let block_number = RpcU256::from(b.block().header().number());
|
||||
to_value(&(RpcH256::from(pow_hash), RpcH256::from(seed_hash), RpcH256::from(target), block_number))
|
||||
} else {
|
||||
to_value(&(RpcH256::from(pow_hash), RpcH256::from(seed_hash), RpcH256::from(target)))
|
||||
}
|
||||
}).unwrap_or(Err(Error::internal_error())) // no work found.
|
||||
},
|
||||
_ => Err(Error::invalid_params())
|
||||
|
@ -23,24 +23,21 @@ use ethcore::client::MiningBlockChainClient;
|
||||
use util::{U256, Address, H256, Mutex};
|
||||
use transient_hashmap::TransientHashMap;
|
||||
use ethcore::account_provider::AccountProvider;
|
||||
use v1::helpers::{SigningQueue, ConfirmationPromise, ConfirmationResult, ConfirmationsQueue, TransactionRequest as TRequest};
|
||||
use v1::helpers::{SigningQueue, ConfirmationPromise, ConfirmationResult, ConfirmationsQueue, ConfirmationPayload, TransactionRequest as TRequest, FilledTransactionRequest as FilledRequest};
|
||||
use v1::traits::EthSigning;
|
||||
use v1::types::{TransactionRequest, H160 as RpcH160, H256 as RpcH256, H520 as RpcH520, U256 as RpcU256};
|
||||
use v1::impls::{default_gas_price, sign_and_dispatch, transaction_rejected_error};
|
||||
use v1::impls::{default_gas_price, sign_and_dispatch, transaction_rejected_error, signer_disabled_error};
|
||||
|
||||
fn fill_optional_fields<C, M>(request: &mut TRequest, client: &C, miner: &M)
|
||||
fn fill_optional_fields<C, M>(request: TRequest, client: &C, miner: &M) -> FilledRequest
|
||||
where C: MiningBlockChainClient, M: MinerService {
|
||||
if request.value.is_none() {
|
||||
request.value = Some(U256::from(0));
|
||||
}
|
||||
if request.gas.is_none() {
|
||||
request.gas = Some(miner.sensible_gas_limit());
|
||||
}
|
||||
if request.gas_price.is_none() {
|
||||
request.gas_price = Some(default_gas_price(client, miner));
|
||||
}
|
||||
if request.data.is_none() {
|
||||
request.data = Some(Vec::new());
|
||||
FilledRequest {
|
||||
from: request.from,
|
||||
to: request.to,
|
||||
nonce: request.nonce,
|
||||
gas_price: request.gas_price.unwrap_or_else(|| default_gas_price(client, miner)),
|
||||
gas: request.gas.unwrap_or_else(|| miner.sensible_gas_limit()),
|
||||
value: request.value.unwrap_or_else(|| 0.into()),
|
||||
data: request.data.unwrap_or_else(Vec::new),
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,10 +71,26 @@ impl<C, M> EthSigningQueueClient<C, M> where C: MiningBlockChainClient, M: Miner
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn dispatch<F: FnOnce(ConfirmationPromise) -> Result<Value, Error>>(&self, params: Params, f: F) -> Result<Value, Error> {
|
||||
fn dispatch_sign<F: FnOnce(ConfirmationPromise) -> Result<Value, Error>>(&self, params: Params, f: F) -> Result<Value, Error> {
|
||||
from_params::<(RpcH160, RpcH256)>(params).and_then(|(address, msg)| {
|
||||
let address: Address = address.into();
|
||||
let msg: H256 = msg.into();
|
||||
|
||||
let accounts = take_weak!(self.accounts);
|
||||
if accounts.is_unlocked(address) {
|
||||
return to_value(&accounts.sign(address, msg).ok().map_or_else(RpcH520::default, Into::into));
|
||||
}
|
||||
|
||||
let queue = take_weak!(self.queue);
|
||||
let promise = queue.add_request(ConfirmationPayload::Sign(address, msg));
|
||||
f(promise)
|
||||
})
|
||||
}
|
||||
|
||||
fn dispatch_transaction<F: FnOnce(ConfirmationPromise) -> Result<Value, Error>>(&self, params: Params, f: F) -> Result<Value, Error> {
|
||||
from_params::<(TransactionRequest, )>(params)
|
||||
.and_then(|(request, )| {
|
||||
let mut request: TRequest = request.into();
|
||||
let request: TRequest = request.into();
|
||||
let accounts = take_weak!(self.accounts);
|
||||
let (client, miner) = (take_weak!(self.client), take_weak!(self.miner));
|
||||
|
||||
@ -87,8 +100,8 @@ impl<C, M> EthSigningQueueClient<C, M> where C: MiningBlockChainClient, M: Miner
|
||||
}
|
||||
|
||||
let queue = take_weak!(self.queue);
|
||||
fill_optional_fields(&mut request, &*client, &*miner);
|
||||
let promise = queue.add_request(request);
|
||||
let request = fill_optional_fields(request, &*client, &*miner);
|
||||
let promise = queue.add_request(ConfirmationPayload::Transaction(request));
|
||||
f(promise)
|
||||
})
|
||||
}
|
||||
@ -98,23 +111,32 @@ impl<C, M> EthSigning for EthSigningQueueClient<C, M>
|
||||
where C: MiningBlockChainClient + 'static, M: MinerService + 'static
|
||||
{
|
||||
|
||||
fn sign(&self, _params: Params) -> Result<Value, Error> {
|
||||
fn sign(&self, params: Params) -> Result<Value, Error> {
|
||||
try!(self.active());
|
||||
warn!("Invoking eth_sign is not yet supported with signer enabled.");
|
||||
// TODO [ToDr] Implement sign when rest of the signing queue is ready.
|
||||
rpc_unimplemented!()
|
||||
self.dispatch_sign(params, |promise| {
|
||||
promise.wait_with_timeout().unwrap_or_else(|| to_value(&RpcH520::default()))
|
||||
})
|
||||
}
|
||||
|
||||
fn post_sign(&self, params: Params) -> Result<Value, Error> {
|
||||
try!(self.active());
|
||||
self.dispatch_sign(params, |promise| {
|
||||
let id = promise.id();
|
||||
self.pending.lock().insert(id, promise);
|
||||
to_value(&RpcU256::from(id))
|
||||
})
|
||||
}
|
||||
|
||||
fn send_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||
try!(self.active());
|
||||
self.dispatch(params, |promise| {
|
||||
self.dispatch_transaction(params, |promise| {
|
||||
promise.wait_with_timeout().unwrap_or_else(|| to_value(&RpcH256::default()))
|
||||
})
|
||||
}
|
||||
|
||||
fn post_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||
try!(self.active());
|
||||
self.dispatch(params, |promise| {
|
||||
self.dispatch_transaction(params, |promise| {
|
||||
let id = promise.id();
|
||||
self.pending.lock().insert(id, promise);
|
||||
to_value(&RpcU256::from(id))
|
||||
@ -193,13 +215,18 @@ impl<C, M> EthSigning for EthSigningUnsafeClient<C, M> where
|
||||
})
|
||||
}
|
||||
|
||||
fn post_sign(&self, _: Params) -> Result<Value, Error> {
|
||||
// We don't support this in non-signer mode.
|
||||
Err(signer_disabled_error())
|
||||
}
|
||||
|
||||
fn post_transaction(&self, _: Params) -> Result<Value, Error> {
|
||||
// We don't support this in non-signer mode.
|
||||
Err(Error::invalid_params())
|
||||
Err(signer_disabled_error())
|
||||
}
|
||||
|
||||
fn check_transaction(&self, _: Params) -> Result<Value, Error> {
|
||||
// We don't support this in non-signer mode.
|
||||
Err(Error::invalid_params())
|
||||
Err(signer_disabled_error())
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ use ethcore::miner::MinerService;
|
||||
use v1::traits::Ethcore;
|
||||
use v1::types::{Bytes, U256};
|
||||
use v1::helpers::{SigningQueue, ConfirmationsQueue};
|
||||
use v1::impls::error_codes;
|
||||
use v1::impls::signer_disabled_error;
|
||||
|
||||
/// Ethcore implementation.
|
||||
pub struct EthcoreClient<C, M> where
|
||||
@ -152,11 +152,7 @@ impl<C, M> Ethcore for EthcoreClient<C, M> where M: MinerService + 'static, C: M
|
||||
fn unsigned_transactions_count(&self, _params: Params) -> Result<Value, Error> {
|
||||
try!(self.active());
|
||||
match self.confirmations_queue {
|
||||
None => Err(Error {
|
||||
code: ErrorCode::ServerError(error_codes::SIGNER_DISABLED),
|
||||
message: "Trusted Signer is disabled. This API is not available.".into(),
|
||||
data: None
|
||||
}),
|
||||
None => Err(signer_disabled_error()),
|
||||
Some(ref queue) => to_value(&queue.len()),
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ mod traces;
|
||||
mod rpc;
|
||||
|
||||
pub use self::web3::Web3Client;
|
||||
pub use self::eth::EthClient;
|
||||
pub use self::eth::{EthClient, EthClientOptions};
|
||||
pub use self::eth_filter::EthFilterClient;
|
||||
pub use self::eth_signing::{EthSigningUnsafeClient, EthSigningQueueClient};
|
||||
pub use self::net::NetClient;
|
||||
@ -54,7 +54,7 @@ pub use self::traces::TracesClient;
|
||||
pub use self::rpc::RpcClient;
|
||||
|
||||
use v1::helpers::TransactionRequest;
|
||||
use v1::types::H256 as NH256;
|
||||
use v1::types::{H256 as RpcH256, H520 as RpcH520};
|
||||
use ethcore::error::Error as EthcoreError;
|
||||
use ethcore::miner::MinerService;
|
||||
use ethcore::client::MiningBlockChainClient;
|
||||
@ -80,7 +80,7 @@ mod error_codes {
|
||||
|
||||
fn dispatch_transaction<C, M>(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result<Value, Error>
|
||||
where C: MiningBlockChainClient, M: MinerService {
|
||||
let hash = NH256::from(signed_transaction.hash());
|
||||
let hash = RpcH256::from(signed_transaction.hash());
|
||||
|
||||
let import = miner.import_own_transaction(client, signed_transaction);
|
||||
|
||||
@ -89,6 +89,12 @@ fn dispatch_transaction<C, M>(client: &C, miner: &M, signed_transaction: SignedT
|
||||
.and_then(|_| to_value(&hash))
|
||||
}
|
||||
|
||||
fn signature_with_password(accounts: &AccountProvider, address: Address, hash: H256, pass: String) -> Result<Value, Error> {
|
||||
accounts.sign_with_password(address, pass, hash)
|
||||
.map_err(password_error)
|
||||
.and_then(|hash| to_value(&RpcH520::from(hash)))
|
||||
}
|
||||
|
||||
fn prepare_transaction<C, M>(client: &C, miner: &M, request: TransactionRequest) -> Transaction where C: MiningBlockChainClient, M: MinerService {
|
||||
Transaction {
|
||||
nonce: request.nonce
|
||||
@ -105,9 +111,10 @@ fn prepare_transaction<C, M>(client: &C, miner: &M, request: TransactionRequest)
|
||||
}
|
||||
}
|
||||
|
||||
fn unlock_sign_and_dispatch<C, M>(client: &C, miner: &M, request: TransactionRequest, account_provider: &AccountProvider, address: Address, password: String) -> Result<Value, Error>
|
||||
fn unlock_sign_and_dispatch<C, M>(client: &C, miner: &M, request: TransactionRequest, account_provider: &AccountProvider, password: String) -> Result<Value, Error>
|
||||
where C: MiningBlockChainClient, M: MinerService {
|
||||
|
||||
let address = request.from;
|
||||
let signed_transaction = {
|
||||
let t = prepare_transaction(client, miner, request);
|
||||
let hash = t.hash();
|
||||
@ -140,6 +147,14 @@ fn default_gas_price<C, M>(client: &C, miner: &M) -> U256 where C: MiningBlockCh
|
||||
.unwrap_or_else(|_| miner.sensible_gas_price())
|
||||
}
|
||||
|
||||
fn signer_disabled_error() -> Error {
|
||||
Error {
|
||||
code: ErrorCode::ServerError(error_codes::SIGNER_DISABLED),
|
||||
message: "Trusted Signer is disabled. This API is not available.".into(),
|
||||
data: None
|
||||
}
|
||||
}
|
||||
|
||||
fn signing_error(error: AccountError) -> Error {
|
||||
Error {
|
||||
code: ErrorCode::ServerError(error_codes::ACCOUNT_LOCKED),
|
||||
|
@ -105,10 +105,9 @@ impl<C: 'static, M: 'static> Personal for PersonalClient<C, M> where C: MiningBl
|
||||
from_params::<(TransactionRequest, String)>(params)
|
||||
.and_then(|(request, password)| {
|
||||
let request: TRequest = request.into();
|
||||
let sender = request.from;
|
||||
let accounts = take_weak!(self.accounts);
|
||||
|
||||
unlock_sign_and_dispatch(&*take_weak!(self.client), &*take_weak!(self.miner), request, &*accounts, sender, password)
|
||||
unlock_sign_and_dispatch(&*take_weak!(self.client), &*take_weak!(self.miner), request, &*accounts, password)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -22,9 +22,9 @@ use ethcore::account_provider::AccountProvider;
|
||||
use ethcore::client::MiningBlockChainClient;
|
||||
use ethcore::miner::MinerService;
|
||||
use v1::traits::PersonalSigner;
|
||||
use v1::types::{TransactionModification, TransactionConfirmation, U256};
|
||||
use v1::impls::unlock_sign_and_dispatch;
|
||||
use v1::helpers::{SigningQueue, ConfirmationsQueue};
|
||||
use v1::types::{TransactionModification, ConfirmationRequest, U256};
|
||||
use v1::impls::{unlock_sign_and_dispatch, signature_with_password};
|
||||
use v1::helpers::{SigningQueue, ConfirmationsQueue, ConfirmationPayload};
|
||||
|
||||
/// Transactions confirmation (personal) rpc implementation.
|
||||
pub struct SignerClient<C, M> where C: MiningBlockChainClient, M: MinerService {
|
||||
@ -55,14 +55,16 @@ impl<C: 'static, M: 'static> SignerClient<C, M> where C: MiningBlockChainClient,
|
||||
|
||||
impl<C: 'static, M: 'static> PersonalSigner for SignerClient<C, M> where C: MiningBlockChainClient, M: MinerService {
|
||||
|
||||
fn transactions_to_confirm(&self, _params: Params) -> Result<Value, Error> {
|
||||
fn requests_to_confirm(&self, _params: Params) -> Result<Value, Error> {
|
||||
try!(self.active());
|
||||
let queue = take_weak!(self.queue);
|
||||
to_value(&queue.requests().into_iter().map(From::from).collect::<Vec<TransactionConfirmation>>())
|
||||
to_value(&queue.requests().into_iter().map(From::from).collect::<Vec<ConfirmationRequest>>())
|
||||
}
|
||||
|
||||
fn confirm_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||
fn confirm_request(&self, params: Params) -> Result<Value, Error> {
|
||||
try!(self.active());
|
||||
// TODO [ToDr] TransactionModification is redundant for some calls
|
||||
// might be better to replace it in future
|
||||
from_params::<(U256, TransactionModification, String)>(params).and_then(
|
||||
|(id, modification, pass)| {
|
||||
let id = id.into();
|
||||
@ -70,17 +72,23 @@ impl<C: 'static, M: 'static> PersonalSigner for SignerClient<C, M> where C: Mini
|
||||
let queue = take_weak!(self.queue);
|
||||
let client = take_weak!(self.client);
|
||||
let miner = take_weak!(self.miner);
|
||||
|
||||
queue.peek(&id).map(|confirmation| {
|
||||
let mut request = confirmation.transaction;
|
||||
let result = match confirmation.payload {
|
||||
ConfirmationPayload::Transaction(mut request) => {
|
||||
// apply modification
|
||||
if let Some(gas_price) = modification.gas_price {
|
||||
request.gas_price = Some(gas_price.into());
|
||||
request.gas_price = gas_price.into();
|
||||
}
|
||||
|
||||
let sender = request.from;
|
||||
let result = unlock_sign_and_dispatch(&*client, &*miner, request, &*accounts, sender, pass);
|
||||
if let Ok(ref hash) = result {
|
||||
queue.request_confirmed(id, Ok(hash.clone()));
|
||||
unlock_sign_and_dispatch(&*client, &*miner, request.into(), &*accounts, pass)
|
||||
},
|
||||
ConfirmationPayload::Sign(address, hash) => {
|
||||
signature_with_password(&*accounts, address, hash, pass)
|
||||
}
|
||||
};
|
||||
if let Ok(ref response) = result {
|
||||
queue.request_confirmed(id, Ok(response.clone()));
|
||||
}
|
||||
result
|
||||
}).unwrap_or_else(|| Err(Error::invalid_params()))
|
||||
@ -88,7 +96,7 @@ impl<C: 'static, M: 'static> PersonalSigner for SignerClient<C, M> where C: Mini
|
||||
)
|
||||
}
|
||||
|
||||
fn reject_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||
fn reject_request(&self, params: Params) -> Result<Value, Error> {
|
||||
try!(self.active());
|
||||
from_params::<(U256, )>(params).and_then(
|
||||
|(id, )| {
|
||||
|
@ -121,7 +121,7 @@ impl EthTester {
|
||||
&account_provider,
|
||||
&miner_service,
|
||||
&external_miner,
|
||||
true
|
||||
Default::default(),
|
||||
);
|
||||
let eth_sign = EthSigningUnsafeClient::new(
|
||||
&client,
|
||||
|
@ -181,8 +181,9 @@ impl MinerService for TestMinerService {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn map_sealing_work<F, T>(&self, _chain: &MiningBlockChainClient, _f: F) -> Option<T> where F: FnOnce(&ClosedBlock) -> T {
|
||||
None
|
||||
fn map_sealing_work<F, T>(&self, chain: &MiningBlockChainClient, f: F) -> Option<T> where F: FnOnce(&ClosedBlock) -> T {
|
||||
let open_block = chain.prepare_open_block(self.author(), *self.gas_range_target.write(), self.extra_data());
|
||||
Some(f(&open_block.close()))
|
||||
}
|
||||
|
||||
fn transaction(&self, hash: &H256) -> Option<SignedTransaction> {
|
||||
@ -205,6 +206,10 @@ impl MinerService for TestMinerService {
|
||||
self.last_nonces.read().get(address).cloned()
|
||||
}
|
||||
|
||||
fn is_sealing(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Submit `seal` as a valid solution for the header of `pow_hash`.
|
||||
/// Will check the seal, but not actually insert the block into the chain.
|
||||
fn submit_seal(&self, _chain: &MiningBlockChainClient, _pow_hash: H256, _seal: Vec<Bytes>) -> Result<(), Error> {
|
||||
|
@ -17,10 +17,11 @@
|
||||
use std::str::FromStr;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Instant, Duration};
|
||||
use jsonrpc_core::IoHandler;
|
||||
use util::hash::{Address, H256, FixedHash};
|
||||
use util::numbers::{Uint, U256};
|
||||
use util::RwLock;
|
||||
use util::Mutex;
|
||||
use ethcore::account_provider::AccountProvider;
|
||||
use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, TransactionID};
|
||||
use ethcore::log_entry::{LocalizedLogEntry, LogEntry};
|
||||
@ -28,7 +29,7 @@ use ethcore::receipt::LocalizedReceipt;
|
||||
use ethcore::transaction::{Transaction, Action};
|
||||
use ethcore::miner::{ExternalMiner, MinerService};
|
||||
use ethsync::SyncState;
|
||||
use v1::{Eth, EthClient, EthSigning, EthSigningUnsafeClient};
|
||||
use v1::{Eth, EthClient, EthClientOptions, EthSigning, EthSigningUnsafeClient};
|
||||
use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService};
|
||||
use rustc_serialize::hex::ToHex;
|
||||
|
||||
@ -57,19 +58,25 @@ struct EthTester {
|
||||
pub sync: Arc<TestSyncProvider>,
|
||||
pub accounts_provider: Arc<AccountProvider>,
|
||||
pub miner: Arc<TestMinerService>,
|
||||
hashrates: Arc<RwLock<HashMap<H256, U256>>>,
|
||||
hashrates: Arc<Mutex<HashMap<H256, (Instant, U256)>>>,
|
||||
pub io: IoHandler,
|
||||
}
|
||||
|
||||
impl Default for EthTester {
|
||||
fn default() -> Self {
|
||||
Self::new_with_options(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl EthTester {
|
||||
pub fn new_with_options(options: EthClientOptions) -> Self {
|
||||
let client = blockchain_client();
|
||||
let sync = sync_provider();
|
||||
let ap = accounts_provider();
|
||||
let miner = miner_service();
|
||||
let hashrates = Arc::new(RwLock::new(HashMap::new()));
|
||||
let hashrates = Arc::new(Mutex::new(HashMap::new()));
|
||||
let external_miner = Arc::new(ExternalMiner::new(hashrates.clone()));
|
||||
let eth = EthClient::new(&client, &sync, &ap, &miner, &external_miner, true).to_delegate();
|
||||
let eth = EthClient::new(&client, &sync, &ap, &miner, &external_miner, options).to_delegate();
|
||||
let sign = EthSigningUnsafeClient::new(&client, &ap, &miner).to_delegate();
|
||||
let io = IoHandler::new();
|
||||
io.add_delegate(eth);
|
||||
@ -133,9 +140,9 @@ fn rpc_eth_syncing() {
|
||||
#[test]
|
||||
fn rpc_eth_hashrate() {
|
||||
let tester = EthTester::default();
|
||||
tester.hashrates.write().insert(H256::from(0), U256::from(0xfffa));
|
||||
tester.hashrates.write().insert(H256::from(0), U256::from(0xfffb));
|
||||
tester.hashrates.write().insert(H256::from(1), U256::from(0x1));
|
||||
tester.hashrates.lock().insert(H256::from(0), (Instant::now() + Duration::from_secs(2), U256::from(0xfffa)));
|
||||
tester.hashrates.lock().insert(H256::from(0), (Instant::now() + Duration::from_secs(2), U256::from(0xfffb)));
|
||||
tester.hashrates.lock().insert(H256::from(1), (Instant::now() + Duration::from_secs(2), U256::from(0x1)));
|
||||
|
||||
let request = r#"{"jsonrpc": "2.0", "method": "eth_hashrate", "params": [], "id": 1}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":"0xfffc","id":1}"#;
|
||||
@ -158,8 +165,8 @@ fn rpc_eth_submit_hashrate() {
|
||||
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
|
||||
|
||||
assert_eq!(tester.io.handle_request(request), Some(response.to_owned()));
|
||||
assert_eq!(tester.hashrates.read().get(&H256::from("0x59daa26581d0acd1fce254fb7e85952f4c09d0915afd33d3886cd914bc7d283c")).cloned(),
|
||||
Some(U256::from(0x500_000)));
|
||||
assert_eq!(tester.hashrates.lock().get(&H256::from("0x59daa26581d0acd1fce254fb7e85952f4c09d0915afd33d3886cd914bc7d283c")).cloned().unwrap().1,
|
||||
U256::from(0x500_000));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -210,16 +217,11 @@ fn rpc_eth_author() {
|
||||
#[test]
|
||||
fn rpc_eth_mining() {
|
||||
let tester = EthTester::default();
|
||||
tester.miner.set_author(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap());
|
||||
|
||||
let request = r#"{"jsonrpc": "2.0", "method": "eth_mining", "params": [], "id": 1}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":false,"id":1}"#;
|
||||
assert_eq!(tester.io.handle_request(request), Some(response.to_owned()));
|
||||
|
||||
tester.hashrates.write().insert(H256::from(1), U256::from(0x1));
|
||||
|
||||
let request = r#"{"jsonrpc": "2.0", "method": "eth_mining", "params": [], "id": 1}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
|
||||
assert_eq!(tester.io.handle_request(request), Some(response.to_owned()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -794,15 +796,26 @@ fn returns_no_work_if_cant_mine() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn returns_error_if_can_mine_and_no_closed_block() {
|
||||
use ethsync::{SyncState};
|
||||
|
||||
fn returns_correct_work_package() {
|
||||
let eth_tester = EthTester::default();
|
||||
eth_tester.miner.set_author(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap());
|
||||
eth_tester.sync.status.write().state = SyncState::Idle;
|
||||
|
||||
let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [], "id": 1}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","error":{"code":-32603,"message":"Internal error","data":null},"id":1}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":["0x3bbe93f74e7b97ae00784aeff8819c5cb600dd87e8b282a5d3446f3f871f0347","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000800000000000000000000000000000000000000000000000000000000000","0x01"],"id":1}"#;
|
||||
|
||||
assert_eq!(eth_tester.io.handle_request(request), Some(response.to_owned()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_return_block_number() {
|
||||
let eth_tester = EthTester::new_with_options(EthClientOptions {
|
||||
allow_pending_receipt_query: true,
|
||||
send_block_number_in_get_work: false,
|
||||
});
|
||||
eth_tester.miner.set_author(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap());
|
||||
|
||||
let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [], "id": 1}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":["0x3bbe93f74e7b97ae00784aeff8819c5cb600dd87e8b282a5d3446f3f871f0347","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000800000000000000000000000000000000000000000000000000000000000"],"id":1}"#;
|
||||
|
||||
assert_eq!(eth_tester.io.handle_request(request), Some(response.to_owned()));
|
||||
}
|
||||
|
@ -16,13 +16,14 @@
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use jsonrpc_core::IoHandler;
|
||||
use v1::impls::EthSigningQueueClient;
|
||||
use v1::traits::EthSigning;
|
||||
use v1::helpers::{ConfirmationsQueue, SigningQueue};
|
||||
use v1::tests::helpers::TestMinerService;
|
||||
use util::{Address, FixedHash};
|
||||
use util::numbers::{Uint, U256};
|
||||
use util::numbers::{Uint, U256, H256};
|
||||
use ethcore::account_provider::AccountProvider;
|
||||
use ethcore::client::TestBlockChainClient;
|
||||
use ethcore::transaction::{Transaction, Action};
|
||||
@ -37,7 +38,7 @@ struct EthSigningTester {
|
||||
|
||||
impl Default for EthSigningTester {
|
||||
fn default() -> Self {
|
||||
let queue = Arc::new(ConfirmationsQueue::default());
|
||||
let queue = Arc::new(ConfirmationsQueue::with_timeout(Duration::from_millis(1)));
|
||||
let client = Arc::new(TestBlockChainClient::default());
|
||||
let miner = Arc::new(TestMinerService::default());
|
||||
let accounts = Arc::new(AccountProvider::transient_provider());
|
||||
@ -58,6 +59,78 @@ fn eth_signing() -> EthSigningTester {
|
||||
EthSigningTester::default()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_add_sign_to_queue() {
|
||||
// given
|
||||
let tester = eth_signing();
|
||||
let address = Address::random();
|
||||
assert_eq!(tester.queue.requests().len(), 0);
|
||||
|
||||
// when
|
||||
let request = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_sign",
|
||||
"params": [
|
||||
""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000005"
|
||||
],
|
||||
"id": 1
|
||||
}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","id":1}"#;
|
||||
|
||||
// then
|
||||
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
||||
assert_eq!(tester.queue.requests().len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_post_sign_to_queue() {
|
||||
// given
|
||||
let tester = eth_signing();
|
||||
let address = Address::random();
|
||||
assert_eq!(tester.queue.requests().len(), 0);
|
||||
|
||||
// when
|
||||
let request = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_postSign",
|
||||
"params": [
|
||||
""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000005"
|
||||
],
|
||||
"id": 1
|
||||
}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":"0x01","id":1}"#;
|
||||
|
||||
// then
|
||||
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
||||
assert_eq!(tester.queue.requests().len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_sign_if_account_is_unlocked() {
|
||||
// given
|
||||
let tester = eth_signing();
|
||||
let hash: H256 = 5.into();
|
||||
let acc = tester.accounts.new_account("test").unwrap();
|
||||
tester.accounts.unlock_account_permanently(acc, "test".into()).unwrap();
|
||||
|
||||
let signature = tester.accounts.sign(acc, hash).unwrap();
|
||||
|
||||
// when
|
||||
let request = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_sign",
|
||||
"params": [
|
||||
""#.to_owned() + format!("0x{:?}", acc).as_ref() + r#"",
|
||||
""# + format!("0x{:?}", hash).as_ref() + r#""
|
||||
],
|
||||
"id": 1
|
||||
}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", signature).as_ref() + r#"","id":1}"#;
|
||||
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
||||
assert_eq!(tester.queue.requests().len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_add_transaction_to_queue() {
|
||||
@ -81,7 +154,6 @@ fn should_add_transaction_to_queue() {
|
||||
}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000","id":1}"#;
|
||||
|
||||
|
||||
// then
|
||||
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
||||
assert_eq!(tester.queue.requests().len(), 1);
|
||||
|
@ -23,7 +23,7 @@ use ethcore::client::TestBlockChainClient;
|
||||
use ethcore::transaction::{Transaction, Action};
|
||||
use v1::{SignerClient, PersonalSigner};
|
||||
use v1::tests::helpers::TestMinerService;
|
||||
use v1::helpers::{SigningQueue, ConfirmationsQueue, TransactionRequest};
|
||||
use v1::helpers::{SigningQueue, ConfirmationsQueue, FilledTransactionRequest, ConfirmationPayload};
|
||||
|
||||
struct PersonalSignerTester {
|
||||
queue: Arc<ConfirmationsQueue>,
|
||||
@ -68,22 +68,28 @@ fn signer_tester() -> PersonalSignerTester {
|
||||
|
||||
|
||||
#[test]
|
||||
fn should_return_list_of_transactions_in_queue() {
|
||||
fn should_return_list_of_items_to_confirm() {
|
||||
// given
|
||||
let tester = signer_tester();
|
||||
tester.queue.add_request(TransactionRequest {
|
||||
tester.queue.add_request(ConfirmationPayload::Transaction(FilledTransactionRequest {
|
||||
from: Address::from(1),
|
||||
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
|
||||
gas_price: Some(U256::from(10_000)),
|
||||
gas: Some(U256::from(10_000_000)),
|
||||
value: Some(U256::from(1)),
|
||||
data: None,
|
||||
gas_price: U256::from(10_000),
|
||||
gas: U256::from(10_000_000),
|
||||
value: U256::from(1),
|
||||
data: vec![],
|
||||
nonce: None,
|
||||
});
|
||||
}));
|
||||
tester.queue.add_request(ConfirmationPayload::Sign(1.into(), 5.into()));
|
||||
|
||||
// when
|
||||
let request = r#"{"jsonrpc":"2.0","method":"personal_transactionsToConfirm","params":[],"id":1}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":[{"id":"0x01","transaction":{"data":null,"from":"0x0000000000000000000000000000000000000001","gas":"0x989680","gasPrice":"0x2710","nonce":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","value":"0x01"}}],"id":1}"#;
|
||||
let request = r#"{"jsonrpc":"2.0","method":"personal_requestsToConfirm","params":[],"id":1}"#;
|
||||
let response = concat!(
|
||||
r#"{"jsonrpc":"2.0","result":["#,
|
||||
r#"{"id":"0x01","payload":{"transaction":{"data":"0x","from":"0x0000000000000000000000000000000000000001","gas":"0x989680","gasPrice":"0x2710","nonce":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","value":"0x01"}}},"#,
|
||||
r#"{"id":"0x02","payload":{"sign":{"address":"0x0000000000000000000000000000000000000001","hash":"0x0000000000000000000000000000000000000000000000000000000000000005"}}}"#,
|
||||
r#"],"id":1}"#
|
||||
);
|
||||
|
||||
// then
|
||||
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
||||
@ -94,19 +100,19 @@ fn should_return_list_of_transactions_in_queue() {
|
||||
fn should_reject_transaction_from_queue_without_dispatching() {
|
||||
// given
|
||||
let tester = signer_tester();
|
||||
tester.queue.add_request(TransactionRequest {
|
||||
tester.queue.add_request(ConfirmationPayload::Transaction(FilledTransactionRequest {
|
||||
from: Address::from(1),
|
||||
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
|
||||
gas_price: Some(U256::from(10_000)),
|
||||
gas: Some(U256::from(10_000_000)),
|
||||
value: Some(U256::from(1)),
|
||||
data: None,
|
||||
gas_price: U256::from(10_000),
|
||||
gas: U256::from(10_000_000),
|
||||
value: U256::from(1),
|
||||
data: vec![],
|
||||
nonce: None,
|
||||
});
|
||||
}));
|
||||
assert_eq!(tester.queue.requests().len(), 1);
|
||||
|
||||
// when
|
||||
let request = r#"{"jsonrpc":"2.0","method":"personal_rejectTransaction","params":["0x01"],"id":1}"#;
|
||||
let request = r#"{"jsonrpc":"2.0","method":"personal_rejectRequest","params":["0x01"],"id":1}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
|
||||
|
||||
// then
|
||||
@ -119,19 +125,35 @@ fn should_reject_transaction_from_queue_without_dispatching() {
|
||||
fn should_not_remove_transaction_if_password_is_invalid() {
|
||||
// given
|
||||
let tester = signer_tester();
|
||||
tester.queue.add_request(TransactionRequest {
|
||||
tester.queue.add_request(ConfirmationPayload::Transaction(FilledTransactionRequest {
|
||||
from: Address::from(1),
|
||||
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
|
||||
gas_price: Some(U256::from(10_000)),
|
||||
gas: Some(U256::from(10_000_000)),
|
||||
value: Some(U256::from(1)),
|
||||
data: None,
|
||||
gas_price: U256::from(10_000),
|
||||
gas: U256::from(10_000_000),
|
||||
value: U256::from(1),
|
||||
data: vec![],
|
||||
nonce: None,
|
||||
});
|
||||
}));
|
||||
assert_eq!(tester.queue.requests().len(), 1);
|
||||
|
||||
// when
|
||||
let request = r#"{"jsonrpc":"2.0","method":"personal_confirmTransaction","params":["0x01",{},"xxx"],"id":1}"#;
|
||||
let request = r#"{"jsonrpc":"2.0","method":"personal_confirmRequest","params":["0x01",{},"xxx"],"id":1}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","error":{"code":-32021,"message":"Account password is invalid or account does not exist.","data":"SStore(InvalidAccount)"},"id":1}"#;
|
||||
|
||||
// then
|
||||
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
||||
assert_eq!(tester.queue.requests().len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_remove_sign_if_password_is_invalid() {
|
||||
// given
|
||||
let tester = signer_tester();
|
||||
tester.queue.add_request(ConfirmationPayload::Sign(0.into(), 5.into()));
|
||||
assert_eq!(tester.queue.requests().len(), 1);
|
||||
|
||||
// when
|
||||
let request = r#"{"jsonrpc":"2.0","method":"personal_confirmRequest","params":["0x01",{},"xxx"],"id":1}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","error":{"code":-32021,"message":"Account password is invalid or account does not exist.","data":"SStore(InvalidAccount)"},"id":1}"#;
|
||||
|
||||
// then
|
||||
@ -145,15 +167,15 @@ fn should_confirm_transaction_and_dispatch() {
|
||||
let tester = signer_tester();
|
||||
let address = tester.accounts.new_account("test").unwrap();
|
||||
let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap();
|
||||
tester.queue.add_request(TransactionRequest {
|
||||
tester.queue.add_request(ConfirmationPayload::Transaction(FilledTransactionRequest {
|
||||
from: address,
|
||||
to: Some(recipient),
|
||||
gas_price: Some(U256::from(10_000)),
|
||||
gas: Some(U256::from(10_000_000)),
|
||||
value: Some(U256::from(1)),
|
||||
data: None,
|
||||
gas_price: U256::from(10_000),
|
||||
gas: U256::from(10_000_000),
|
||||
value: U256::from(1),
|
||||
data: vec![],
|
||||
nonce: None,
|
||||
});
|
||||
}));
|
||||
|
||||
let t = Transaction {
|
||||
nonce: U256::zero(),
|
||||
@ -172,7 +194,7 @@ fn should_confirm_transaction_and_dispatch() {
|
||||
// when
|
||||
let request = r#"{
|
||||
"jsonrpc":"2.0",
|
||||
"method":"personal_confirmTransaction",
|
||||
"method":"personal_confirmRequest",
|
||||
"params":["0x01", {"gasPrice":"0x1000"}, "test"],
|
||||
"id":1
|
||||
}"#;
|
||||
|
@ -206,6 +206,10 @@ pub trait EthSigning: Sized + Send + Sync + 'static {
|
||||
/// Signs the data with given address signature.
|
||||
fn sign(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Posts sign request asynchronously.
|
||||
/// Will return a confirmation ID for later use with check_transaction.
|
||||
fn post_sign(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Sends transaction; will block for 20s to try to return the
|
||||
/// transaction hash.
|
||||
/// If it cannot yet be signed, it will return a transaction ID for
|
||||
@ -226,6 +230,7 @@ pub trait EthSigning: Sized + Send + Sync + 'static {
|
||||
fn to_delegate(self) -> IoDelegate<Self> {
|
||||
let mut delegate = IoDelegate::new(Arc::new(self));
|
||||
delegate.add_method("eth_sign", EthSigning::sign);
|
||||
delegate.add_method("eth_postSign", EthSigning::post_sign);
|
||||
delegate.add_method("eth_sendTransaction", EthSigning::send_transaction);
|
||||
delegate.add_method("eth_postTransaction", EthSigning::post_transaction);
|
||||
delegate.add_method("eth_checkTransaction", EthSigning::check_transaction);
|
||||
|
@ -61,24 +61,24 @@ pub trait Personal: Sized + Send + Sync + 'static {
|
||||
}
|
||||
}
|
||||
|
||||
/// Personal extension for transactions confirmations rpc interface.
|
||||
/// Personal extension for confirmations rpc interface.
|
||||
pub trait PersonalSigner: Sized + Send + Sync + 'static {
|
||||
|
||||
/// Returns a list of transactions to confirm.
|
||||
fn transactions_to_confirm(&self, _: Params) -> Result<Value, Error>;
|
||||
/// Returns a list of items to confirm.
|
||||
fn requests_to_confirm(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Confirm and send a specific transaction.
|
||||
fn confirm_transaction(&self, _: Params) -> Result<Value, Error>;
|
||||
/// Confirm specific request.
|
||||
fn confirm_request(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Reject the transaction request.
|
||||
fn reject_transaction(&self, _: Params) -> Result<Value, Error>;
|
||||
/// Reject the confirmation request.
|
||||
fn reject_request(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Should be used to convert object to io delegate.
|
||||
fn to_delegate(self) -> IoDelegate<Self> {
|
||||
let mut delegate = IoDelegate::new(Arc::new(self));
|
||||
delegate.add_method("personal_transactionsToConfirm", PersonalSigner::transactions_to_confirm);
|
||||
delegate.add_method("personal_confirmTransaction", PersonalSigner::confirm_transaction);
|
||||
delegate.add_method("personal_rejectTransaction", PersonalSigner::reject_transaction);
|
||||
delegate.add_method("personal_requestsToConfirm", PersonalSigner::requests_to_confirm);
|
||||
delegate.add_method("personal_confirmRequest", PersonalSigner::confirm_request);
|
||||
delegate.add_method("personal_rejectRequest", PersonalSigner::reject_request);
|
||||
delegate
|
||||
}
|
||||
}
|
||||
|
150
rpc/src/v1/types/confirmations.rs
Normal file
150
rpc/src/v1/types/confirmations.rs
Normal file
@ -0,0 +1,150 @@
|
||||
// Copyright 2015, 2016 Ethcore (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/>.
|
||||
|
||||
//! Types used in Confirmations queue (Trusted Signer)
|
||||
|
||||
use v1::types::{U256, TransactionRequest, H160, H256};
|
||||
use v1::helpers;
|
||||
|
||||
|
||||
/// Confirmation waiting in a queue
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)]
|
||||
pub struct ConfirmationRequest {
|
||||
/// Id of this confirmation
|
||||
pub id: U256,
|
||||
/// Payload
|
||||
pub payload: ConfirmationPayload,
|
||||
}
|
||||
|
||||
impl From<helpers::ConfirmationRequest> for ConfirmationRequest {
|
||||
fn from(c: helpers::ConfirmationRequest) -> Self {
|
||||
ConfirmationRequest {
|
||||
id: c.id.into(),
|
||||
payload: c.payload.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sign request
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)]
|
||||
pub struct SignRequest {
|
||||
/// Address
|
||||
pub address: H160,
|
||||
/// Hash to sign
|
||||
pub hash: H256,
|
||||
}
|
||||
|
||||
/// Confirmation payload, i.e. the thing to be confirmed
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)]
|
||||
pub enum ConfirmationPayload {
|
||||
/// Transaction
|
||||
#[serde(rename="transaction")]
|
||||
Transaction(TransactionRequest),
|
||||
/// Signature
|
||||
#[serde(rename="sign")]
|
||||
Sign(SignRequest),
|
||||
}
|
||||
|
||||
impl From<helpers::ConfirmationPayload> for ConfirmationPayload {
|
||||
fn from(c: helpers::ConfirmationPayload) -> Self {
|
||||
match c {
|
||||
helpers::ConfirmationPayload::Transaction(t) => ConfirmationPayload::Transaction(t.into()),
|
||||
helpers::ConfirmationPayload::Sign(address, hash) => ConfirmationPayload::Sign(SignRequest {
|
||||
address: address.into(),
|
||||
hash: hash.into(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Possible modifications to the confirmed transaction sent by `Trusted Signer`
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub struct TransactionModification {
|
||||
/// Modified gas price
|
||||
#[serde(rename="gasPrice")]
|
||||
pub gas_price: Option<U256>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
use serde_json;
|
||||
use v1::types::U256;
|
||||
use v1::helpers;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_serialize_sign_confirmation() {
|
||||
// given
|
||||
let request = helpers::ConfirmationRequest {
|
||||
id: 15.into(),
|
||||
payload: helpers::ConfirmationPayload::Sign(1.into(), 5.into()),
|
||||
};
|
||||
|
||||
// when
|
||||
let res = serde_json::to_string(&ConfirmationRequest::from(request));
|
||||
let expected = r#"{"id":"0x0f","payload":{"sign":{"address":"0x0000000000000000000000000000000000000001","hash":"0x0000000000000000000000000000000000000000000000000000000000000005"}}}"#;
|
||||
|
||||
// then
|
||||
assert_eq!(res.unwrap(), expected.to_owned());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_serialize_transaction_confirmation() {
|
||||
// given
|
||||
let request = helpers::ConfirmationRequest {
|
||||
id: 15.into(),
|
||||
payload: helpers::ConfirmationPayload::Transaction(helpers::FilledTransactionRequest {
|
||||
from: 0.into(),
|
||||
to: None,
|
||||
gas: 15_000.into(),
|
||||
gas_price: 10_000.into(),
|
||||
value: 100_000.into(),
|
||||
data: vec![1, 2, 3],
|
||||
nonce: Some(1.into()),
|
||||
}),
|
||||
};
|
||||
|
||||
// when
|
||||
let res = serde_json::to_string(&ConfirmationRequest::from(request));
|
||||
let expected = r#"{"id":"0x0f","payload":{"transaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x0186a0","data":"0x010203","nonce":"0x01"}}}"#;
|
||||
|
||||
// then
|
||||
assert_eq!(res.unwrap(), expected.to_owned());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_deserialize_modification() {
|
||||
// given
|
||||
let s1 = r#"{
|
||||
"gasPrice":"0x0ba43b7400"
|
||||
}"#;
|
||||
let s2 = r#"{}"#;
|
||||
|
||||
// when
|
||||
let res1: TransactionModification = serde_json::from_str(s1).unwrap();
|
||||
let res2: TransactionModification = serde_json::from_str(s2).unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(res1, TransactionModification {
|
||||
gas_price: Some(U256::from_str("0ba43b7400").unwrap()),
|
||||
});
|
||||
assert_eq!(res2, TransactionModification {
|
||||
gas_price: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,8 @@
|
||||
mod bytes;
|
||||
mod block;
|
||||
mod block_number;
|
||||
mod call_request;
|
||||
mod confirmations;
|
||||
mod filter;
|
||||
mod hash;
|
||||
mod index;
|
||||
@ -24,7 +26,6 @@ mod log;
|
||||
mod sync;
|
||||
mod transaction;
|
||||
mod transaction_request;
|
||||
mod call_request;
|
||||
mod receipt;
|
||||
mod trace;
|
||||
mod trace_filter;
|
||||
@ -33,14 +34,15 @@ mod uint;
|
||||
pub use self::bytes::Bytes;
|
||||
pub use self::block::{Block, BlockTransactions};
|
||||
pub use self::block_number::BlockNumber;
|
||||
pub use self::call_request::CallRequest;
|
||||
pub use self::confirmations::{ConfirmationPayload, ConfirmationRequest, TransactionModification};
|
||||
pub use self::filter::Filter;
|
||||
pub use self::hash::{H64, H160, H256, H520, H2048};
|
||||
pub use self::index::Index;
|
||||
pub use self::log::Log;
|
||||
pub use self::sync::{SyncStatus, SyncInfo};
|
||||
pub use self::transaction::Transaction;
|
||||
pub use self::transaction_request::{TransactionRequest, TransactionConfirmation, TransactionModification};
|
||||
pub use self::call_request::CallRequest;
|
||||
pub use self::transaction_request::TransactionRequest;
|
||||
pub use self::receipt::Receipt;
|
||||
pub use self::trace::{LocalizedTrace, TraceResults};
|
||||
pub use self::trace_filter::TraceFilter;
|
||||
|
@ -17,7 +17,7 @@
|
||||
//! `TransactionRequest` type
|
||||
|
||||
use v1::types::{Bytes, H160, U256};
|
||||
use v1::helpers::{TransactionRequest as Request, TransactionConfirmation as Confirmation};
|
||||
use v1::helpers;
|
||||
|
||||
/// Transaction request coming from RPC
|
||||
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||
@ -39,8 +39,8 @@ pub struct TransactionRequest {
|
||||
pub nonce: Option<U256>,
|
||||
}
|
||||
|
||||
impl From<Request> for TransactionRequest {
|
||||
fn from(r: Request) -> Self {
|
||||
impl From<helpers::TransactionRequest> for TransactionRequest {
|
||||
fn from(r: helpers::TransactionRequest) -> Self {
|
||||
TransactionRequest {
|
||||
from: r.from.into(),
|
||||
to: r.to.map(Into::into),
|
||||
@ -53,9 +53,23 @@ impl From<Request> for TransactionRequest {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Request> for TransactionRequest {
|
||||
fn into(self) -> Request {
|
||||
Request {
|
||||
impl From<helpers::FilledTransactionRequest> for TransactionRequest {
|
||||
fn from(r: helpers::FilledTransactionRequest) -> Self {
|
||||
TransactionRequest {
|
||||
from: r.from.into(),
|
||||
to: r.to.map(Into::into),
|
||||
gas_price: Some(r.gas_price.into()),
|
||||
gas: Some(r.gas.into()),
|
||||
value: Some(r.value.into()),
|
||||
data: Some(r.data.into()),
|
||||
nonce: r.nonce.map(Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<helpers::TransactionRequest> for TransactionRequest {
|
||||
fn into(self) -> helpers::TransactionRequest {
|
||||
helpers::TransactionRequest {
|
||||
from: self.from.into(),
|
||||
to: self.to.map(Into::into),
|
||||
gas_price: self.gas_price.map(Into::into),
|
||||
@ -67,32 +81,6 @@ impl Into<Request> for TransactionRequest {
|
||||
}
|
||||
}
|
||||
|
||||
/// Transaction confirmation waiting in a queue
|
||||
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Serialize)]
|
||||
pub struct TransactionConfirmation {
|
||||
/// Id of this confirmation
|
||||
pub id: U256,
|
||||
/// TransactionRequest
|
||||
pub transaction: TransactionRequest,
|
||||
}
|
||||
|
||||
impl From<Confirmation> for TransactionConfirmation {
|
||||
fn from(c: Confirmation) -> Self {
|
||||
TransactionConfirmation {
|
||||
id: c.id.into(),
|
||||
transaction: c.transaction.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Possible modifications to the confirmed transaction sent by `SignerUI`
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub struct TransactionModification {
|
||||
/// Modified gas price
|
||||
#[serde(rename="gasPrice")]
|
||||
pub gas_price: Option<U256>,
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
@ -188,7 +176,6 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn transaction_request_deserialize_error() {
|
||||
let s = r#"{
|
||||
@ -203,26 +190,5 @@ mod tests {
|
||||
|
||||
assert!(deserialized.is_err(), "Should be error because to is empty");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_deserialize_modification() {
|
||||
// given
|
||||
let s1 = r#"{
|
||||
"gasPrice":"0x0ba43b7400"
|
||||
}"#;
|
||||
let s2 = r#"{}"#;
|
||||
|
||||
// when
|
||||
let res1: TransactionModification = serde_json::from_str(s1).unwrap();
|
||||
let res2: TransactionModification = serde_json::from_str(s2).unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(res1, TransactionModification {
|
||||
gas_price: Some(U256::from_str("0ba43b7400").unwrap()),
|
||||
});
|
||||
assert_eq!(res2, TransactionModification {
|
||||
gas_price: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,6 +53,7 @@ pub struct ServerBuilder {
|
||||
queue: Arc<ConfirmationsQueue>,
|
||||
handler: Arc<IoHandler>,
|
||||
authcodes_path: PathBuf,
|
||||
skip_origin_validation: bool,
|
||||
}
|
||||
|
||||
impl Extendable for ServerBuilder {
|
||||
@ -68,13 +69,21 @@ impl ServerBuilder {
|
||||
queue: queue,
|
||||
handler: Arc::new(IoHandler::new()),
|
||||
authcodes_path: authcodes_path,
|
||||
skip_origin_validation: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// If set to `true` server will not verify Origin of incoming requests.
|
||||
/// Not recommended. Use only for development.
|
||||
pub fn skip_origin_validation(mut self, skip: bool) -> Self {
|
||||
self.skip_origin_validation = skip;
|
||||
self
|
||||
}
|
||||
|
||||
/// Starts a new `WebSocket` server in separate thread.
|
||||
/// Returns a `Server` handle which closes the server when droped.
|
||||
pub fn start(self, addr: SocketAddr) -> Result<Server, ServerError> {
|
||||
Server::start(addr, self.handler, self.queue, self.authcodes_path)
|
||||
Server::start(addr, self.handler, self.queue, self.authcodes_path, self.skip_origin_validation)
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,10 +98,10 @@ pub struct Server {
|
||||
impl Server {
|
||||
/// Starts a new `WebSocket` server in separate thread.
|
||||
/// Returns a `Server` handle which closes the server when droped.
|
||||
fn start(addr: SocketAddr, handler: Arc<IoHandler>, queue: Arc<ConfirmationsQueue>, authcodes_path: PathBuf) -> Result<Server, ServerError> {
|
||||
fn start(addr: SocketAddr, handler: Arc<IoHandler>, queue: Arc<ConfirmationsQueue>, authcodes_path: PathBuf, skip_origin_validation: bool) -> Result<Server, ServerError> {
|
||||
let config = {
|
||||
let mut config = ws::Settings::default();
|
||||
// It's also used for handling min-sysui requests (browser can make many of them in paralel)
|
||||
// accept only handshakes beginning with GET
|
||||
config.method_strict = true;
|
||||
// Was shutting down server when suspending on linux:
|
||||
config.shutdown_on_interrupt = false;
|
||||
@ -101,7 +110,9 @@ impl Server {
|
||||
|
||||
// Create WebSocket
|
||||
let origin = format!("{}", addr);
|
||||
let ws = try!(ws::Builder::new().with_settings(config).build(session::Factory::new(handler, origin, authcodes_path)));
|
||||
let ws = try!(ws::Builder::new().with_settings(config).build(
|
||||
session::Factory::new(handler, origin, authcodes_path, skip_origin_validation)
|
||||
));
|
||||
|
||||
let panic_handler = PanicHandler::new_in_arc();
|
||||
let ph = panic_handler.clone();
|
||||
|
@ -96,6 +96,7 @@ fn add_headers(mut response: ws::Response, mime: &str) -> ws::Response {
|
||||
|
||||
pub struct Session {
|
||||
out: ws::Sender,
|
||||
skip_origin_validation: bool,
|
||||
self_origin: String,
|
||||
authcodes_path: PathBuf,
|
||||
handler: Arc<IoHandler>,
|
||||
@ -107,10 +108,12 @@ impl ws::Handler for Session {
|
||||
let host = req.header("host").or_else(|| req.header("Host")).map(|x| &x[..]);
|
||||
|
||||
// Check request origin and host header.
|
||||
if !self.skip_origin_validation {
|
||||
if !origin_is_allowed(&self.self_origin, origin) && !(origin.is_none() && origin_is_allowed(&self.self_origin, host)) {
|
||||
warn!(target: "signer", "Blocked connection to Signer API from untrusted origin.");
|
||||
return Ok(ws::Response::forbidden(format!("You are not allowed to access system ui. Use: http://{}", self.self_origin)));
|
||||
}
|
||||
}
|
||||
|
||||
// Detect if it's a websocket request.
|
||||
if req.header("sec-websocket-key").is_some() {
|
||||
@ -150,14 +153,16 @@ impl ws::Handler for Session {
|
||||
|
||||
pub struct Factory {
|
||||
handler: Arc<IoHandler>,
|
||||
skip_origin_validation: bool,
|
||||
self_origin: String,
|
||||
authcodes_path: PathBuf,
|
||||
}
|
||||
|
||||
impl Factory {
|
||||
pub fn new(handler: Arc<IoHandler>, self_origin: String, authcodes_path: PathBuf) -> Self {
|
||||
pub fn new(handler: Arc<IoHandler>, self_origin: String, authcodes_path: PathBuf, skip_origin_validation: bool) -> Self {
|
||||
Factory {
|
||||
handler: handler,
|
||||
skip_origin_validation: skip_origin_validation,
|
||||
self_origin: self_origin,
|
||||
authcodes_path: authcodes_path,
|
||||
}
|
||||
@ -171,6 +176,7 @@ impl ws::Factory for Factory {
|
||||
Session {
|
||||
out: sender,
|
||||
handler: self.handler.clone(),
|
||||
skip_origin_validation: self.skip_origin_validation,
|
||||
self_origin: self.self_origin.clone(),
|
||||
authcodes_path: self.authcodes_path.clone(),
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ build = "build.rs"
|
||||
[lib]
|
||||
|
||||
[build-dependencies]
|
||||
syntex = "0.33"
|
||||
ethcore-ipc-codegen = { path = "../ipc/codegen" }
|
||||
|
||||
[dependencies]
|
||||
|
@ -14,26 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate syntex;
|
||||
extern crate ethcore_ipc_codegen as codegen;
|
||||
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
extern crate ethcore_ipc_codegen;
|
||||
|
||||
fn main() {
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
|
||||
// sync interface
|
||||
{
|
||||
let src = Path::new("src/api.rs");
|
||||
let intermediate = Path::new(&out_dir).join("api.intermediate.rs");
|
||||
let mut registry = syntex::Registry::new();
|
||||
codegen::register(&mut registry);
|
||||
registry.expand("", &src, &intermediate).unwrap();
|
||||
|
||||
let dst = Path::new(&out_dir).join("api.ipc.rs");
|
||||
let mut registry = syntex::Registry::new();
|
||||
codegen::register(&mut registry);
|
||||
registry.expand("", &intermediate, &dst).unwrap();
|
||||
}
|
||||
ethcore_ipc_codegen::derive_ipc("src/api.rs").unwrap();
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ mod tests;
|
||||
|
||||
mod api {
|
||||
#![allow(dead_code, unused_assignments, unused_variables, missing_docs)] // codegen issues
|
||||
include!(concat!(env!("OUT_DIR"), "/api.ipc.rs"));
|
||||
include!(concat!(env!("OUT_DIR"), "/api.rs"));
|
||||
}
|
||||
|
||||
pub use api::{EthSync, SyncProvider, SyncClient, NetworkManagerClient, ManageNetwork, SyncConfig,
|
||||
|
@ -36,7 +36,7 @@ use std::fmt;
|
||||
use std::slice;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
/// Slie pretty print helper
|
||||
/// Slice pretty print helper
|
||||
pub struct PrettySlice<'a> (&'a [u8]);
|
||||
|
||||
impl<'a> fmt::Debug for PrettySlice<'a> {
|
||||
|
@ -71,6 +71,9 @@ impl<T> UsingQueue<T> where T: Clone {
|
||||
self.pending = Some(b);
|
||||
}
|
||||
|
||||
/// Is there anything in the queue currently?
|
||||
pub fn is_in_use(&self) -> bool { self.in_use.len() > 0 }
|
||||
|
||||
/// Clears everything; the queue is entirely reset.
|
||||
pub fn reset(&mut self) {
|
||||
self.pending = None;
|
||||
|
Loading…
Reference in New Issue
Block a user