diff --git a/Cargo.lock b/Cargo.lock
index cee712727..2cfb39db8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -505,6 +505,7 @@ dependencies = [
"ethcore-util 1.4.0",
"jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
"parity-dapps-signer 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1167,7 +1168,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "parity-dapps"
version = "1.4.0"
-source = "git+https://github.com/ethcore/parity-ui.git#926b09b66c4940b09dc82c52adb4afd9e31155bc"
+source = "git+https://github.com/ethcore/parity-ui.git#8b1c31319228ad4cf9bd4ae740a0b933aa9e19c7"
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)",
@@ -1181,7 +1182,7 @@ dependencies = [
[[package]]
name = "parity-dapps-home"
version = "1.4.0"
-source = "git+https://github.com/ethcore/parity-ui.git#926b09b66c4940b09dc82c52adb4afd9e31155bc"
+source = "git+https://github.com/ethcore/parity-ui.git#8b1c31319228ad4cf9bd4ae740a0b933aa9e19c7"
dependencies = [
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
]
@@ -1189,7 +1190,7 @@ dependencies = [
[[package]]
name = "parity-dapps-signer"
version = "1.4.0"
-source = "git+https://github.com/ethcore/parity-ui.git#926b09b66c4940b09dc82c52adb4afd9e31155bc"
+source = "git+https://github.com/ethcore/parity-ui.git#8b1c31319228ad4cf9bd4ae740a0b933aa9e19c7"
dependencies = [
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
]
@@ -1197,7 +1198,7 @@ dependencies = [
[[package]]
name = "parity-dapps-status"
version = "1.4.0"
-source = "git+https://github.com/ethcore/parity-ui.git#926b09b66c4940b09dc82c52adb4afd9e31155bc"
+source = "git+https://github.com/ethcore/parity-ui.git#8b1c31319228ad4cf9bd4ae740a0b933aa9e19c7"
dependencies = [
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
]
@@ -1205,7 +1206,7 @@ dependencies = [
[[package]]
name = "parity-dapps-wallet"
version = "1.4.0"
-source = "git+https://github.com/ethcore/parity-ui.git#926b09b66c4940b09dc82c52adb4afd9e31155bc"
+source = "git+https://github.com/ethcore/parity-ui.git#8b1c31319228ad4cf9bd4ae740a0b933aa9e19c7"
dependencies = [
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
]
@@ -1851,7 +1852,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "ws"
version = "0.5.2"
-source = "git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable#afbff59776ce16ccec5ee9e218b8891830ee6fdf"
+source = "git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable#609b21fdab96c8fffedec8699755ce3bea9454cb"
dependencies = [
"bytes 0.4.0-dev (git+https://github.com/carllerche/bytes)",
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/ethcore/src/account_provider.rs b/ethcore/src/account_provider.rs
index 851d015ba..3f4511f4b 100644
--- a/ethcore/src/account_provider.rs
+++ b/ethcore/src/account_provider.rs
@@ -23,7 +23,7 @@ use std::time::{Instant, Duration};
use util::{Mutex, RwLock};
use ethstore::{SecretStore, Error as SSError, SafeAccount, EthStore};
use ethstore::dir::{KeyDirectory};
-use ethstore::ethkey::{Address, Message, Secret, Random, Generator};
+use ethstore::ethkey::{Address, Message, Public, Secret, Random, Generator};
use ethjson::misc::AccountMeta;
pub use ethstore::ethkey::Signature;
@@ -182,9 +182,16 @@ impl AccountProvider {
/// Creates new random account.
pub fn new_account(&self, password: &str) -> Result
{
- let secret = Random.generate().unwrap().secret().clone();
+ self.new_account_and_public(password).map(|d| d.0)
+ }
+
+ /// Creates new random account and returns address and public key
+ pub fn new_account_and_public(&self, password: &str) -> Result<(Address, Public), Error> {
+ let acc = Random.generate().unwrap();
+ let public = acc.public().clone();
+ let secret = acc.secret().clone();
let address = try!(self.sstore.insert_account(secret, password));
- Ok(address)
+ Ok((address, public))
}
/// Inserts new account into underlying store.
@@ -280,6 +287,21 @@ impl AccountProvider {
Ok(())
}
+ fn password(&self, account: &Address) -> Result {
+ let mut unlocked = self.unlocked.lock();
+ let data = try!(unlocked.get(account).ok_or(Error::NotUnlocked)).clone();
+ if let Unlock::Temp = data.unlock {
+ unlocked.remove(account).expect("data exists: so key must exist: qed");
+ }
+ if let Unlock::Timed((ref start, ref duration)) = data.unlock {
+ if start.elapsed() > Duration::from_millis(*duration as u64) {
+ unlocked.remove(account).expect("data exists: so key must exist: qed");
+ return Err(Error::NotUnlocked);
+ }
+ }
+ Ok(data.password.clone())
+ }
+
/// Unlocks account permanently.
pub fn unlock_account_permanently(&self, account: Address, password: String) -> Result<(), Error> {
self.unlock_account(account, password, Unlock::Perm)
@@ -301,51 +323,16 @@ impl AccountProvider {
unlocked.get(&account).is_some()
}
- /// Signs the message. Account must be unlocked.
- pub fn sign(&self, account: Address, message: Message) -> Result {
- let data = {
- let mut unlocked = self.unlocked.lock();
- let data = try!(unlocked.get(&account).ok_or(Error::NotUnlocked)).clone();
- if let Unlock::Temp = data.unlock {
- unlocked.remove(&account).expect("data exists: so key must exist: qed");
- }
- if let Unlock::Timed((ref start, ref duration)) = data.unlock {
- if start.elapsed() > Duration::from_millis(*duration as u64) {
- unlocked.remove(&account).expect("data exists: so key must exist: qed");
- return Err(Error::NotUnlocked);
- }
- }
- data
- };
-
- let signature = try!(self.sstore.sign(&account, &data.password, &message));
- Ok(signature)
+ /// Signs the message. If password is not provided the account must be unlocked.
+ pub fn sign(&self, account: Address, password: Option, message: Message) -> Result {
+ let password = try!(password.map(Ok).unwrap_or_else(|| self.password(&account)));
+ Ok(try!(self.sstore.sign(&account, &password, &message)))
}
- /// Decrypts a message. Account must be unlocked.
- pub fn decrypt(&self, account: Address, shared_mac: &[u8], message: &[u8]) -> Result, Error> {
- let data = {
- let mut unlocked = self.unlocked.lock();
- let data = try!(unlocked.get(&account).ok_or(Error::NotUnlocked)).clone();
- if let Unlock::Temp = data.unlock {
- unlocked.remove(&account).expect("data exists: so key must exist: qed");
- }
- if let Unlock::Timed((ref start, ref duration)) = data.unlock {
- if start.elapsed() > Duration::from_millis(*duration as u64) {
- unlocked.remove(&account).expect("data exists: so key must exist: qed");
- return Err(Error::NotUnlocked);
- }
- }
- data
- };
-
- Ok(try!(self.sstore.decrypt(&account, &data.password, shared_mac, message)))
- }
-
- /// Unlocks an account, signs the message, and locks it again.
- pub fn sign_with_password(&self, account: Address, password: String, message: Message) -> Result {
- let signature = try!(self.sstore.sign(&account, &password, &message));
- Ok(signature)
+ /// Decrypts a message. If password is not provided the account must be unlocked.
+ pub fn decrypt(&self, account: Address, password: Option, shared_mac: &[u8], message: &[u8]) -> Result, Error> {
+ let password = try!(password.map(Ok).unwrap_or_else(|| self.password(&account)));
+ Ok(try!(self.sstore.decrypt(&account, &password, shared_mac, message)))
}
/// Returns the underlying `SecretStore` reference if one exists.
@@ -386,8 +373,8 @@ mod tests {
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
assert!(ap.unlock_account_temporarily(kp.address(), "test1".into()).is_err());
assert!(ap.unlock_account_temporarily(kp.address(), "test".into()).is_ok());
- assert!(ap.sign(kp.address(), Default::default()).is_ok());
- assert!(ap.sign(kp.address(), Default::default()).is_err());
+ assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
+ assert!(ap.sign(kp.address(), None, Default::default()).is_err());
}
#[test]
@@ -397,11 +384,11 @@ mod tests {
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
assert!(ap.unlock_account_permanently(kp.address(), "test1".into()).is_err());
assert!(ap.unlock_account_permanently(kp.address(), "test".into()).is_ok());
- assert!(ap.sign(kp.address(), Default::default()).is_ok());
- assert!(ap.sign(kp.address(), Default::default()).is_ok());
+ assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
+ assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
assert!(ap.unlock_account_temporarily(kp.address(), "test".into()).is_ok());
- assert!(ap.sign(kp.address(), Default::default()).is_ok());
- assert!(ap.sign(kp.address(), Default::default()).is_ok());
+ assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
+ assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
}
#[test]
@@ -411,8 +398,8 @@ mod tests {
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
assert!(ap.unlock_account_timed(kp.address(), "test1".into(), 2000).is_err());
assert!(ap.unlock_account_timed(kp.address(), "test".into(), 2000).is_ok());
- assert!(ap.sign(kp.address(), Default::default()).is_ok());
+ assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
::std::thread::sleep(Duration::from_millis(2000));
- assert!(ap.sign(kp.address(), Default::default()).is_err());
+ assert!(ap.sign(kp.address(), None, Default::default()).is_err());
}
}
diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs
index 6e3c2f1dd..bd3eb5bc6 100644
--- a/ethcore/src/engines/basic_authority.rs
+++ b/ethcore/src/engines/basic_authority.rs
@@ -112,7 +112,7 @@ impl Engine for BasicAuthority {
let header = block.header();
let message = header.bare_hash();
// account should be pernamently unlocked, otherwise sealing will fail
- if let Ok(signature) = ap.sign(*block.header().author(), message) {
+ if let Ok(signature) = ap.sign(*block.header().author(), None, message) {
return Some(vec![::rlp::encode(&(&*signature as &[u8])).to_vec()]);
} else {
trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable");
diff --git a/ethstore/src/account/safe_account.rs b/ethstore/src/account/safe_account.rs
index 315fd283b..5dab35251 100644
--- a/ethstore/src/account/safe_account.rs
+++ b/ethstore/src/account/safe_account.rs
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
-use ethkey::{KeyPair, sign, Address, Secret, Signature, Message};
+use ethkey::{KeyPair, sign, Address, Secret, Signature, Message, Public};
use {json, Error, crypto};
use crypto::Keccak256;
use random::Random;
@@ -180,6 +180,11 @@ impl SafeAccount {
crypto::ecies::decrypt(&secret, shared_mac, message).map_err(From::from)
}
+ pub fn public(&self, password: &str) -> Result {
+ let secret = try!(self.crypto.secret(password));
+ Ok(try!(KeyPair::from_secret(secret)).public().clone())
+ }
+
pub fn change_password(&self, old_password: &str, new_password: &str, iterations: u32) -> Result {
let secret = try!(self.crypto.secret(old_password));
let result = SafeAccount {
diff --git a/ethstore/src/bin/ethstore.rs b/ethstore/src/bin/ethstore.rs
index 94823dc06..9d499723b 100644
--- a/ethstore/src/bin/ethstore.rs
+++ b/ethstore/src/bin/ethstore.rs
@@ -37,6 +37,7 @@ Usage:
ethstore import-wallet [--dir DIR]
ethstore remove [--dir DIR]
ethstore sign [--dir DIR]
+ ethstore public
ethstore [-h | --help]
Options:
@@ -56,6 +57,7 @@ Commands:
import-wallet Import presale wallet.
remove Remove account.
sign Sign message.
+ public Displays public key for an address.
"#;
#[derive(Debug, RustcDecodable)]
@@ -67,6 +69,7 @@ struct Args {
cmd_import_wallet: bool,
cmd_remove: bool,
cmd_sign: bool,
+ cmd_public: bool,
arg_secret: String,
arg_password: String,
arg_old_pwd: String,
@@ -103,7 +106,7 @@ fn key_dir(location: &str) -> Result, Error> {
fn format_accounts(accounts: &[Address]) -> String {
accounts.iter()
.enumerate()
- .map(|(i, a)| format!("{:2}: {}", i, a))
+ .map(|(i, a)| format!("{:2}: 0x{:?}", i, a))
.collect::>()
.join("\n")
}
@@ -128,7 +131,7 @@ fn execute(command: I) -> Result where I: IntoIterator- (command: I) -> Result where I: IntoIterator
- (command: I) -> Result where I: IntoIterator
- Result {
+ let account = try!(self.get(account));
+ account.public(password)
+ }
+
fn uuid(&self, address: &Address) -> Result {
let account = try!(self.get(address));
Ok(account.id.into())
diff --git a/ethstore/src/secret_store.rs b/ethstore/src/secret_store.rs
index aa79cb8b6..06f38922b 100644
--- a/ethstore/src/secret_store.rs
+++ b/ethstore/src/secret_store.rs
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
-use ethkey::{Address, Message, Signature, Secret};
+use ethkey::{Address, Message, Signature, Secret, Public};
use Error;
use json::UUID;
@@ -27,6 +27,7 @@ pub trait SecretStore: Send + Sync {
fn sign(&self, account: &Address, password: &str, message: &Message) -> Result;
fn decrypt(&self, account: &Address, password: &str, shared_mac: &[u8], message: &[u8]) -> Result, Error>;
+ fn public(&self, account: &Address, password: &str) -> Result;
fn accounts(&self) -> Result, Error>;
fn uuid(&self, account: &Address) -> Result;
diff --git a/rpc/src/v1/helpers/dispatch.rs b/rpc/src/v1/helpers/dispatch.rs
index df2d8cbd3..56124108a 100644
--- a/rpc/src/v1/helpers/dispatch.rs
+++ b/rpc/src/v1/helpers/dispatch.rs
@@ -14,23 +14,73 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
-use util::{Address, H256, U256, Uint};
+use util::{Address, H256, U256, Uint, Bytes};
use util::bytes::ToPretty;
+use ethkey::Signature;
use ethcore::miner::MinerService;
use ethcore::client::MiningBlockChainClient;
use ethcore::transaction::{Action, SignedTransaction, Transaction};
use ethcore::account_provider::AccountProvider;
use jsonrpc_core::{Error, Value, to_value};
use v1::helpers::TransactionRequest;
-use v1::types::{H256 as RpcH256, H520 as RpcH520};
+use v1::types::{H256 as RpcH256, H520 as RpcH520, Bytes as RpcBytes};
use v1::helpers::errors;
+pub const DEFAULT_MAC: [u8; 2] = [0, 0];
+
+pub fn dispatch_transaction(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result
+ where C: MiningBlockChainClient, M: MinerService {
+ let hash = RpcH256::from(signed_transaction.hash());
+
+ miner.import_own_transaction(client, signed_transaction)
+ .map_err(errors::from_transaction_error)
+ .map(|_| hash)
+}
+
+fn signature(accounts: &AccountProvider, address: Address, password: Option, hash: H256) -> Result {
+ accounts.sign(address, password.clone(), hash).map_err(|e| match password {
+ Some(_) => errors::from_password_error(e),
+ None => errors::from_signing_error(e),
+ })
+}
+
+pub fn sign(accounts: &AccountProvider, address: Address, password: Option, hash: H256) -> Result {
+ signature(accounts, address, password, hash)
+ .map(RpcH520::from)
+ .map(to_value)
+}
+
+pub fn decrypt(accounts: &AccountProvider, address: Address, password: Option, msg: Bytes) -> Result {
+ accounts.decrypt(address, password.clone(), &DEFAULT_MAC, &msg)
+ .map_err(|e| match password {
+ Some(_) => errors::from_password_error(e),
+ None => errors::from_signing_error(e),
+ })
+ .map(RpcBytes::from)
+ .map(to_value)
+}
+
+pub fn sign_and_dispatch(client: &C, miner: &M, accounts: &AccountProvider, request: TransactionRequest, password: Option) -> Result
+ where C: MiningBlockChainClient, M: MinerService {
+
+ let address = request.from;
+ let signed_transaction = {
+ let t = prepare_transaction(client, miner, request);
+ let hash = t.hash();
+ let signature = try!(signature(accounts, address, password, hash));
+ t.with_signature(signature)
+ };
+
+ trace!(target: "miner", "send_transaction: dispatching tx: {}", ::rlp::encode(&signed_transaction).to_vec().pretty());
+ dispatch_transaction(&*client, &*miner, signed_transaction).map(to_value)
+}
+
fn prepare_transaction(client: &C, miner: &M, request: TransactionRequest) -> Transaction where C: MiningBlockChainClient, M: MinerService {
Transaction {
nonce: request.nonce
.or_else(|| miner
- .last_nonce(&request.from)
- .map(|nonce| nonce + U256::one()))
+ .last_nonce(&request.from)
+ .map(|nonce| nonce + U256::one()))
.unwrap_or_else(|| client.latest_nonce(&request.from)),
action: request.to.map_or(Action::Create, Action::Call),
@@ -41,52 +91,6 @@ fn prepare_transaction(client: &C, miner: &M, request: TransactionRequest)
}
}
-pub fn dispatch_transaction(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result
- where C: MiningBlockChainClient, M: MinerService {
- let hash = RpcH256::from(signed_transaction.hash());
-
- let import = miner.import_own_transaction(client, signed_transaction);
-
- import
- .map_err(errors::from_transaction_error)
- .map(|_| hash)
-}
-
-pub fn signature_with_password(accounts: &AccountProvider, address: Address, hash: H256, pass: String) -> Result {
- accounts.sign_with_password(address, pass, hash)
- .map_err(errors::from_password_error)
- .map(|hash| to_value(&RpcH520::from(hash)))
-}
-
-pub fn unlock_sign_and_dispatch(client: &C, miner: &M, request: TransactionRequest, account_provider: &AccountProvider, password: String) -> Result
- where C: MiningBlockChainClient, M: MinerService {
-
- let address = request.from;
- let signed_transaction = {
- let t = prepare_transaction(client, miner, request);
- let hash = t.hash();
- let signature = try!(account_provider.sign_with_password(address, password, hash).map_err(errors::from_password_error));
- t.with_signature(signature)
- };
-
- trace!(target: "miner", "send_transaction: dispatching tx: {}", ::rlp::encode(&signed_transaction).to_vec().pretty());
- dispatch_transaction(&*client, &*miner, signed_transaction).map(to_value)
-}
-
-pub fn sign_and_dispatch(client: &C, miner: &M, request: TransactionRequest, account_provider: &AccountProvider, address: Address) -> Result
- where C: MiningBlockChainClient, M: MinerService {
-
- let signed_transaction = {
- let t = prepare_transaction(client, miner, request);
- let hash = t.hash();
- let signature = try!(account_provider.sign(address, hash).map_err(errors::from_signing_error));
- t.with_signature(signature)
- };
-
- trace!(target: "miner", "send_transaction: dispatching tx: {}", ::rlp::encode(&signed_transaction).to_vec().pretty());
- dispatch_transaction(&*client, &*miner, signed_transaction).map(to_value)
-}
-
pub fn default_gas_price(client: &C, miner: &M) -> U256 where C: MiningBlockChainClient, M: MinerService {
client
.gas_price_statistics(100, 8)
diff --git a/rpc/src/v1/helpers/errors.rs b/rpc/src/v1/helpers/errors.rs
index 0d7902897..c54cd9c34 100644
--- a/rpc/src/v1/helpers/errors.rs
+++ b/rpc/src/v1/helpers/errors.rs
@@ -43,6 +43,7 @@ mod codes {
pub const REQUEST_REJECTED_LIMIT: i64 = -32041;
pub const REQUEST_NOT_FOUND: i64 = -32042;
pub const COMPILATION_ERROR: i64 = -32050;
+ pub const ENCRYPTION_ERROR: i64 = -32055;
pub const FETCH_ERROR: i64 = -32060;
}
@@ -166,6 +167,14 @@ pub fn signer_disabled() -> Error {
}
}
+pub fn encryption_error(error: T) -> Error {
+ Error {
+ code: ErrorCode::ServerError(codes::ENCRYPTION_ERROR),
+ message: "Encryption error.".into(),
+ data: Some(Value::String(format!("{:?}", error))),
+ }
+}
+
pub fn from_fetch_error(error: FetchError) -> Error {
Error {
code: ErrorCode::ServerError(codes::FETCH_ERROR),
diff --git a/rpc/src/v1/helpers/requests.rs b/rpc/src/v1/helpers/requests.rs
index 6a30d80bb..5cb6108c1 100644
--- a/rpc/src/v1/helpers/requests.rs
+++ b/rpc/src/v1/helpers/requests.rs
@@ -103,4 +103,6 @@ pub enum ConfirmationPayload {
Transaction(FilledTransactionRequest),
/// Sign request
Sign(Address, H256),
+ /// Decrypt request
+ Decrypt(Address, Bytes),
}
diff --git a/rpc/src/v1/impls/eth_signing.rs b/rpc/src/v1/impls/eth_signing.rs
index 9290a9425..e8b81d0c8 100644
--- a/rpc/src/v1/impls/eth_signing.rs
+++ b/rpc/src/v1/impls/eth_signing.rs
@@ -24,9 +24,9 @@ use util::{U256, Address, H256, Mutex};
use transient_hashmap::TransientHashMap;
use ethcore::account_provider::AccountProvider;
use v1::helpers::{errors, SigningQueue, ConfirmationPromise, ConfirmationResult, ConfirmationPayload, TransactionRequest as TRequest, FilledTransactionRequest as FilledRequest, SignerService};
-use v1::helpers::dispatch::{default_gas_price, sign_and_dispatch};
+use v1::helpers::dispatch::{default_gas_price, sign_and_dispatch, sign, decrypt};
use v1::traits::EthSigning;
-use v1::types::{TransactionRequest, H160 as RpcH160, H256 as RpcH256, H520 as RpcH520, U256 as RpcU256, Bytes as RpcBytes};
+use v1::types::{TransactionRequest, H160 as RpcH160, H256 as RpcH256, U256 as RpcU256, Bytes as RpcBytes};
fn fill_optional_fields(request: TRequest, client: &C, miner: &M) -> FilledRequest
where C: MiningBlockChainClient, M: MinerService {
@@ -76,41 +76,59 @@ impl EthSigningQueueClient where C: MiningBlockChainClient, M: Miner
Ok(())
}
+ fn add_to_queue(&self, sender: Address, when_unlocked: WhenUnlocked, payload: Payload)
+ -> Result where
+ WhenUnlocked: Fn(&AccountProvider) -> Result,
+ Payload: Fn() -> ConfirmationPayload, {
+
+ let accounts = take_weak!(self.accounts);
+ if accounts.is_unlocked(sender) {
+ return when_unlocked(&accounts).map(DispatchResult::Value);
+ }
+
+ take_weak!(self.signer).add_request(payload())
+ .map(DispatchResult::Promise)
+ .map_err(|_| errors::request_rejected_limit())
+ }
+
+ fn handle_dispatch(&self, res: Result, ready: Ready) {
+ match res {
+ Ok(DispatchResult::Value(v)) => ready.ready(Ok(v)),
+ Ok(DispatchResult::Promise(promise)) => {
+ promise.wait_for_result(move |result| {
+ ready.ready(result.unwrap_or_else(|| Err(errors::request_rejected())))
+ })
+ },
+ Err(e) => ready.ready(Err(e)),
+ }
+ }
+
fn dispatch_sign(&self, params: Params) -> Result {
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 Ok(DispatchResult::Value(to_value(&accounts.sign(address, msg).ok().map_or_else(RpcH520::default, Into::into))))
- }
-
- let signer = take_weak!(self.signer);
- signer.add_request(ConfirmationPayload::Sign(address, msg))
- .map(DispatchResult::Promise)
- .map_err(|_| errors::request_rejected_limit())
+ self.add_to_queue(
+ address,
+ |accounts| sign(accounts, address, None, msg.clone()),
+ || ConfirmationPayload::Sign(address, msg.clone()),
+ )
})
}
fn dispatch_transaction(&self, params: Params) -> Result {
- from_params::<(TransactionRequest, )>(params)
- .and_then(|(request, )| {
- let request: TRequest = request.into();
- let accounts = take_weak!(self.accounts);
- let (client, miner) = (take_weak!(self.client), take_weak!(self.miner));
-
- if accounts.is_unlocked(request.from) {
- let sender = request.from;
- return sign_and_dispatch(&*client, &*miner, request, &*accounts, sender).map(DispatchResult::Value);
+ from_params::<(TransactionRequest, )>(params).and_then(|(request, )| {
+ let request: TRequest = request.into();
+ let (client, miner) = (take_weak!(self.client), take_weak!(self.miner));
+ self.add_to_queue(
+ request.from,
+ |accounts| sign_and_dispatch(&*client, &*miner, accounts, request.clone(), None),
+ || {
+ let request = fill_optional_fields(request.clone(), &*client, &*miner);
+ ConfirmationPayload::Transaction(request)
}
-
- let signer = take_weak!(self.signer);
- let request = fill_optional_fields(request, &*client, &*miner);
- signer.add_request(ConfirmationPayload::Transaction(request))
- .map(DispatchResult::Promise)
- .map_err(|_| errors::request_rejected_limit())
- })
+ )
+ })
}
}
@@ -118,19 +136,6 @@ impl EthSigning for EthSigningQueueClient
where C: MiningBlockChainClient + 'static, M: MinerService + 'static
{
- fn sign(&self, params: Params, ready: Ready) {
- let res = self.active().and_then(|_| self.dispatch_sign(params));
- match res {
- Ok(DispatchResult::Promise(promise)) => {
- promise.wait_for_result(move |result| {
- ready.ready(result.unwrap_or_else(|| Err(errors::request_rejected())))
- })
- },
- Ok(DispatchResult::Value(v)) => ready.ready(Ok(v)),
- Err(e) => ready.ready(Err(e)),
- }
- }
-
fn post_sign(&self, params: Params) -> Result {
try!(self.active());
self.dispatch_sign(params).map(|result| match result {
@@ -143,19 +148,6 @@ impl EthSigning for EthSigningQueueClient
})
}
- fn send_transaction(&self, params: Params, ready: Ready) {
- let res = self.active().and_then(|_| self.dispatch_transaction(params));
- match res {
- Ok(DispatchResult::Promise(promise)) => {
- promise.wait_for_result(move |result| {
- ready.ready(result.unwrap_or_else(|| Err(errors::request_rejected())))
- })
- },
- Ok(DispatchResult::Value(v)) => ready.ready(Ok(v)),
- Err(e) => ready.ready(Err(e)),
- }
- }
-
fn post_transaction(&self, params: Params) -> Result {
try!(self.active());
self.dispatch_transaction(params).map(|result| match result {
@@ -168,13 +160,6 @@ impl EthSigning for EthSigningQueueClient
})
}
- fn decrypt_message(&self, params: Params) -> Result {
- try!(self.active());
- from_params::<(RpcH160, RpcBytes)>(params).and_then(|(_account, _ciphertext)| {
- Err(errors::unimplemented())
- })
- }
-
fn check_request(&self, params: Params) -> Result {
try!(self.active());
let mut pending = self.pending.lock();
@@ -192,6 +177,32 @@ impl EthSigning for EthSigningQueueClient
res
})
}
+
+ fn sign(&self, params: Params, ready: Ready) {
+ let res = self.active().and_then(|_| self.dispatch_sign(params));
+ self.handle_dispatch(res, ready);
+ }
+
+ fn send_transaction(&self, params: Params, ready: Ready) {
+ let res = self.active().and_then(|_| self.dispatch_transaction(params));
+ self.handle_dispatch(res, ready);
+ }
+
+ fn decrypt_message(&self, params: Params, ready: Ready) {
+ let res = self.active()
+ .and_then(|_| from_params::<(RpcH160, RpcBytes)>(params))
+ .and_then(|(address, msg)| {
+ let address: Address = address.into();
+
+ self.add_to_queue(
+ address,
+ |accounts| decrypt(accounts, address, None, msg.clone().into()),
+ || ConfirmationPayload::Decrypt(address, msg.clone().into())
+ )
+ });
+
+ self.handle_dispatch(res, ready);
+ }
}
/// Implementation of functions that require signing when no trusted signer is used.
@@ -232,9 +243,7 @@ impl EthSigning for EthSigningUnsafeClient where
ready.ready(self.active()
.and_then(|_| from_params::<(RpcH160, RpcH256)>(params))
.and_then(|(address, msg)| {
- let address: Address = address.into();
- let msg: H256 = msg.into();
- Ok(to_value(&take_weak!(self.accounts).sign(address, msg).ok().map_or_else(RpcH520::default, Into::into)))
+ sign(&*take_weak!(self.accounts), address.into(), None, msg.into())
}))
}
@@ -242,18 +251,16 @@ impl EthSigning for EthSigningUnsafeClient where
ready.ready(self.active()
.and_then(|_| from_params::<(TransactionRequest, )>(params))
.and_then(|(request, )| {
- let request: TRequest = request.into();
- let sender = request.from;
- sign_and_dispatch(&*take_weak!(self.client), &*take_weak!(self.miner), request, &*take_weak!(self.accounts), sender)
+ sign_and_dispatch(&*take_weak!(self.client), &*take_weak!(self.miner), &*take_weak!(self.accounts), request.into(), None)
}))
}
- fn decrypt_message(&self, params: Params) -> Result {
- try!(self.active());
- from_params::<(RpcH160, RpcBytes)>(params).and_then(|(address, ciphertext)| {
- let s = try!(take_weak!(self.accounts).decrypt(address.into(), &[0; 0], &ciphertext.0).map_err(|_| Error::internal_error()));
- Ok(to_value(RpcBytes::from(s)))
- })
+ fn decrypt_message(&self, params: Params, ready: Ready) {
+ ready.ready(self.active()
+ .and_then(|_| from_params::<(RpcH160, RpcBytes)>(params))
+ .and_then(|(address, ciphertext)| {
+ decrypt(&*take_weak!(self.accounts), address.into(), None, ciphertext.0)
+ }))
}
fn post_sign(&self, _: Params) -> Result {
diff --git a/rpc/src/v1/impls/ethcore.rs b/rpc/src/v1/impls/ethcore.rs
index 3b756342d..33123ddd5 100644
--- a/rpc/src/v1/impls/ethcore.rs
+++ b/rpc/src/v1/impls/ethcore.rs
@@ -35,6 +35,8 @@ use jsonrpc_core::Error;
use v1::traits::Ethcore;
use v1::types::{Bytes, U256, H160, H256, H512, Peers, Transaction, RpcSettings};
use v1::helpers::{errors, SigningQueue, SignerService, NetworkSettings};
+use v1::helpers::dispatch::DEFAULT_MAC;
+use v1::helpers::params::expect_no_params;
use v1::helpers::auto_args::Ready;
/// Ethcore implementation.
@@ -265,8 +267,8 @@ impl Ethcore for EthcoreClient where
fn encrypt_message(&self, key: H512, phrase: Bytes) -> Result {
try!(self.active());
- ecies::encrypt(&key.into(), &[0; 0], &phrase.0)
- .map_err(|_| Error::internal_error())
+ ecies::encrypt(&key.into(), &DEFAULT_MAC, &phrase.0)
+ .map_err(errors::encryption_error)
.map(Into::into)
}
diff --git a/rpc/src/v1/impls/personal.rs b/rpc/src/v1/impls/personal.rs
index 009f173b3..0830effd5 100644
--- a/rpc/src/v1/impls/personal.rs
+++ b/rpc/src/v1/impls/personal.rs
@@ -22,9 +22,9 @@ use jsonrpc_core::*;
use ethkey::{Brain, Generator};
use v1::traits::Personal;
use v1::types::{H160 as RpcH160, TransactionRequest};
-use v1::helpers::{errors, TransactionRequest as TRequest};
+use v1::helpers::errors;
use v1::helpers::params::expect_no_params;
-use v1::helpers::dispatch::unlock_sign_and_dispatch;
+use v1::helpers::dispatch::sign_and_dispatch;
use ethcore::account_provider::AccountProvider;
use ethcore::client::MiningBlockChainClient;
use ethcore::miner::MinerService;
@@ -139,10 +139,13 @@ impl Personal for PersonalClient where C: MiningBl
try!(self.active());
from_params::<(TransactionRequest, String)>(params)
.and_then(|(request, password)| {
- let request: TRequest = request.into();
- let accounts = take_weak!(self.accounts);
-
- unlock_sign_and_dispatch(&*take_weak!(self.client), &*take_weak!(self.miner), request, &*accounts, password)
+ sign_and_dispatch(
+ &*take_weak!(self.client),
+ &*take_weak!(self.miner),
+ &*take_weak!(self.accounts),
+ request.into(),
+ Some(password)
+ )
})
}
diff --git a/rpc/src/v1/impls/personal_signer.rs b/rpc/src/v1/impls/personal_signer.rs
index 441ed679b..b3a93736a 100644
--- a/rpc/src/v1/impls/personal_signer.rs
+++ b/rpc/src/v1/impls/personal_signer.rs
@@ -25,7 +25,7 @@ use v1::traits::PersonalSigner;
use v1::types::{TransactionModification, ConfirmationRequest, U256};
use v1::helpers::{errors, SignerService, SigningQueue, ConfirmationPayload};
use v1::helpers::params::expect_no_params;
-use v1::helpers::dispatch::{unlock_sign_and_dispatch, signature_with_password};
+use v1::helpers::dispatch::{sign_and_dispatch, sign, decrypt};
/// Transactions confirmation (personal) rpc implementation.
pub struct SignerClient where C: MiningBlockChainClient, M: MinerService {
@@ -87,12 +87,14 @@ impl PersonalSigner for SignerClient where C: Mini
if let Some(gas_price) = modification.gas_price {
request.gas_price = gas_price.into();
}
-
- unlock_sign_and_dispatch(&*client, &*miner, request.into(), &*accounts, pass)
+ sign_and_dispatch(&*client, &*miner, &*accounts, request.into(), Some(pass))
},
ConfirmationPayload::Sign(address, hash) => {
- signature_with_password(&*accounts, address, hash, pass)
- }
+ sign(&*accounts, address, Some(pass), hash)
+ },
+ ConfirmationPayload::Decrypt(address, msg) => {
+ decrypt(&*accounts, address, Some(pass), msg)
+ },
};
if let Ok(ref response) = result {
signer.request_confirmed(id, Ok(response.clone()));
diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs
index ff531d57a..1dfe97968 100644
--- a/rpc/src/v1/tests/eth.rs
+++ b/rpc/src/v1/tests/eth.rs
@@ -33,9 +33,10 @@ use util::{U256, H256, Uint, Address};
use jsonrpc_core::IoHandler;
use ethjson::blockchain::BlockChain;
-use v1::types::U256 as NU256;
-use v1::traits::eth::{Eth, EthSigning};
use v1::impls::{EthClient, EthSigningUnsafeClient};
+use v1::types::U256 as NU256;
+use v1::traits::eth::Eth;
+use v1::traits::eth_signing::EthSigning;
use v1::tests::helpers::{TestSyncProvider, Config};
fn account_provider() -> Arc {
diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs
index eb3fbaf6e..e41ca3231 100644
--- a/rpc/src/v1/tests/mocked/eth.rs
+++ b/rpc/src/v1/tests/mocked/eth.rs
@@ -264,7 +264,7 @@ fn rpc_eth_sign() {
let account = tester.accounts_provider.new_account("abcd").unwrap();
tester.accounts_provider.unlock_account_permanently(account, "abcd".into()).unwrap();
let message = H256::from("0x0cc175b9c0f1b6a831c399e26977266192eb5ffee6ae2fec3ad71c777531578f");
- let signed = tester.accounts_provider.sign(account, message).unwrap();
+ let signed = tester.accounts_provider.sign(account, None, message).unwrap();
let req = r#"{
"jsonrpc": "2.0",
@@ -709,7 +709,7 @@ fn rpc_eth_send_transaction() {
value: U256::from(0x9184e72au64),
data: vec![]
};
- let signature = tester.accounts_provider.sign(address, t.hash()).unwrap();
+ let signature = tester.accounts_provider.sign(address, None, t.hash()).unwrap();
let t = t.with_signature(signature);
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#;
@@ -726,7 +726,7 @@ fn rpc_eth_send_transaction() {
value: U256::from(0x9184e72au64),
data: vec![]
};
- let signature = tester.accounts_provider.sign(address, t.hash()).unwrap();
+ let signature = tester.accounts_provider.sign(address, None, t.hash()).unwrap();
let t = t.with_signature(signature);
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#;
@@ -791,7 +791,7 @@ fn rpc_eth_send_raw_transaction() {
value: U256::from(0x9184e72au64),
data: vec![]
};
- let signature = tester.accounts_provider.sign(address, t.hash()).unwrap();
+ let signature = tester.accounts_provider.sign(address, None, t.hash()).unwrap();
let t = t.with_signature(signature);
let rlp = ::rlp::encode(&t).to_vec().to_hex();
diff --git a/rpc/src/v1/tests/mocked/eth_signing.rs b/rpc/src/v1/tests/mocked/eth_signing.rs
index 1bf901e5f..fc5800cd7 100644
--- a/rpc/src/v1/tests/mocked/eth_signing.rs
+++ b/rpc/src/v1/tests/mocked/eth_signing.rs
@@ -16,16 +16,20 @@
use std::str::FromStr;
use std::sync::Arc;
-use jsonrpc_core::{IoHandler, to_value};
+use jsonrpc_core::{IoHandler, to_value, Success};
use v1::impls::EthSigningQueueClient;
-use v1::traits::EthSigning;
+use v1::traits::{EthSigning, Ethcore};
use v1::helpers::{SignerService, SigningQueue};
-use v1::types::{H256 as RpcH256, H520 as RpcH520};
+use v1::types::{H256 as RpcH256, H520 as RpcH520, Bytes};
use v1::tests::helpers::TestMinerService;
+use v1::tests::mocked::ethcore;
+
use util::{Address, FixedHash, Uint, U256, H256, H520};
use ethcore::account_provider::AccountProvider;
use ethcore::client::TestBlockChainClient;
use ethcore::transaction::{Transaction, Action};
+use ethstore::ethkey::{Generator, Random};
+use serde_json;
struct EthSigningTester {
pub signer: Arc,
@@ -178,7 +182,7 @@ fn should_sign_if_account_is_unlocked() {
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();
+ let signature = tester.accounts.sign(acc, None, hash).unwrap();
// when
let request = r#"{
@@ -242,7 +246,7 @@ fn should_dispatch_transaction_if_account_is_unlock() {
value: U256::from(0x9184e72au64),
data: vec![]
};
- let signature = tester.accounts.sign(acc, t.hash()).unwrap();
+ let signature = tester.accounts.sign(acc, None, t.hash()).unwrap();
let t = t.with_signature(signature);
// when
@@ -263,3 +267,65 @@ fn should_dispatch_transaction_if_account_is_unlock() {
// then
assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned()));
}
+
+#[test]
+fn should_decrypt_message_if_account_is_unlocked() {
+ // given
+ let tester = eth_signing();
+ let sync = ethcore::sync_provider();
+ let net = ethcore::network_service();
+ let ethcore_client = ethcore::ethcore_client(&tester.client, &tester.miner, &sync, &net);
+ tester.io.add_delegate(ethcore_client.to_delegate());
+ let (address, public) = tester.accounts.new_account_and_public("test").unwrap();
+ tester.accounts.unlock_account_permanently(address, "test".into()).unwrap();
+
+
+ // First encrypt message
+ let request = format!("{}0x{:?}{}",
+ r#"{"jsonrpc": "2.0", "method": "ethcore_encryptMessage", "params":[""#,
+ public,
+ r#"", "0x01020304"], "id": 1}"#
+ );
+ let encrypted: Success = serde_json::from_str(&tester.io.handle_request_sync(&request).unwrap()).unwrap();
+
+ // then call decrypt
+ let request = format!("{}{:?}{}{:?}{}",
+ r#"{"jsonrpc": "2.0", "method": "ethcore_decryptMessage", "params":["0x"#,
+ address,
+ r#"","#,
+ encrypted.result,
+ r#"], "id": 1}"#
+ );
+ println!("Request: {:?}", request);
+ let response = r#"{"jsonrpc":"2.0","result":"0x01020304","id":1}"#;
+
+ // then
+ assert_eq!(tester.io.handle_request_sync(&request), Some(response.into()));
+}
+
+#[test]
+fn should_add_decryption_to_the_queue() {
+ // given
+ let tester = eth_signing();
+ let acc = Random.generate().unwrap();
+ assert_eq!(tester.signer.requests().len(), 0);
+
+ // when
+ let request = r#"{
+ "jsonrpc": "2.0",
+ "method": "ethcore_decryptMessage",
+ "params": ["0x"#.to_owned() + &format!("{:?}", acc.address()) + r#"",
+ "0x012345"],
+ "id": 1
+ }"#;
+ let response = r#"{"jsonrpc":"2.0","result":"0x0102","id":1}"#;
+
+ // then
+ let async_result = tester.io.handle_request(&request).unwrap();
+ assert_eq!(tester.signer.requests().len(), 1);
+ // respond
+ tester.signer.request_confirmed(U256::from(1), Ok(to_value(Bytes(vec![0x1, 0x2]))));
+ assert!(async_result.on_result(move |res| {
+ assert_eq!(res, response.to_owned());
+ }));
+}
diff --git a/rpc/src/v1/tests/mocked/ethcore.rs b/rpc/src/v1/tests/mocked/ethcore.rs
index f09d84d5b..4cf23d46e 100644
--- a/rpc/src/v1/tests/mocked/ethcore.rs
+++ b/rpc/src/v1/tests/mocked/ethcore.rs
@@ -19,6 +19,7 @@ use util::log::RotatingLogger;
use util::U256;
use ethsync::ManageNetwork;
use ethcore::client::{TestBlockChainClient};
+use ethstore::ethkey::{Generator, Random};
use jsonrpc_core::IoHandler;
use v1::{Ethcore, EthcoreClient};
@@ -34,7 +35,7 @@ fn client_service() -> Arc {
Arc::new(TestBlockChainClient::default())
}
-fn sync_provider() -> Arc {
+pub fn sync_provider() -> Arc {
Arc::new(TestSyncProvider::new(Config {
network_id: U256::from(3),
num_peers: 120,
@@ -56,13 +57,13 @@ fn settings() -> Arc {
})
}
-fn network_service() -> Arc {
+pub fn network_service() -> Arc {
Arc::new(TestManageNetwork)
}
-type TestEthcoreClient = EthcoreClient;
+pub type TestEthcoreClient = EthcoreClient;
-fn ethcore_client(
+pub fn ethcore_client(
client: &Arc,
miner: &Arc,
sync: &Arc,
@@ -324,3 +325,17 @@ fn rpc_ethcore_pending_transactions() {
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
+
+#[test]
+fn rpc_ethcore_encrypt() {
+ let miner = miner_service();
+ let client = client_service();
+ let sync = sync_provider();
+ let net = network_service();
+ let io = IoHandler::new();
+ io.add_delegate(ethcore_client(&client, &miner, &sync, &net).to_delegate());
+ let key = format!("{:?}", Random.generate().unwrap().public());
+
+ let request = r#"{"jsonrpc": "2.0", "method": "ethcore_encryptMessage", "params":["0x"#.to_owned() + &key + r#"", "0x01"], "id": 1}"#;
+ assert!(io.handle_request_sync(&request).unwrap().contains("result"), "Should return success.");
+}
diff --git a/rpc/src/v1/tests/mocked/personal.rs b/rpc/src/v1/tests/mocked/personal.rs
index aa4bf29c1..c3c3d2954 100644
--- a/rpc/src/v1/tests/mocked/personal.rs
+++ b/rpc/src/v1/tests/mocked/personal.rs
@@ -227,7 +227,7 @@ fn sign_and_send_transaction() {
data: vec![]
};
tester.accounts.unlock_account_temporarily(address, "password123".into()).unwrap();
- let signature = tester.accounts.sign(address, t.hash()).unwrap();
+ let signature = tester.accounts.sign(address, None, t.hash()).unwrap();
let t = t.with_signature(signature);
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#;
@@ -245,7 +245,7 @@ fn sign_and_send_transaction() {
data: vec![]
};
tester.accounts.unlock_account_temporarily(address, "password123".into()).unwrap();
- let signature = tester.accounts.sign(address, t.hash()).unwrap();
+ let signature = tester.accounts.sign(address, None, t.hash()).unwrap();
let t = t.with_signature(signature);
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#;
diff --git a/rpc/src/v1/tests/mocked/personal_signer.rs b/rpc/src/v1/tests/mocked/personal_signer.rs
index 04ae829ee..650f16553 100644
--- a/rpc/src/v1/tests/mocked/personal_signer.rs
+++ b/rpc/src/v1/tests/mocked/personal_signer.rs
@@ -186,7 +186,7 @@ fn should_confirm_transaction_and_dispatch() {
data: vec![]
};
tester.accounts.unlock_account_temporarily(address, "test".into()).unwrap();
- let signature = tester.accounts.sign(address, t.hash()).unwrap();
+ let signature = tester.accounts.sign(address, None, t.hash()).unwrap();
let t = t.with_signature(signature);
assert_eq!(tester.signer.requests().len(), 1);
diff --git a/rpc/src/v1/traits/eth.rs b/rpc/src/v1/traits/eth.rs
index 62301e21f..7d9aa47f3 100644
--- a/rpc/src/v1/traits/eth.rs
+++ b/rpc/src/v1/traits/eth.rs
@@ -15,7 +15,6 @@
// along with Parity. If not, see .
//! Eth rpc interface.
-use std::sync::Arc;
use jsonrpc_core::*;
use v1::types::{Block, BlockNumber, Bytes, CallRequest, Filter, FilterChanges, Index};
@@ -198,46 +197,3 @@ build_rpc_trait! {
fn uninstall_filter(&self, Index) -> Result;
}
}
-
-/// Signing methods implementation relying on unlocked accounts.
-pub trait EthSigning: Sized + Send + Sync + 'static {
- /// Signs the data with given address signature.
- fn sign(&self, _: Params, _: Ready);
-
- /// Posts sign request asynchronously.
- /// Will return a confirmation ID for later use with check_transaction.
- fn post_sign(&self, _: Params) -> Result;
-
- /// 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
- /// later use with check_transaction.
- fn send_transaction(&self, _: Params, _: Ready);
-
- /// Posts transaction asynchronously.
- /// Will return a transaction ID for later use with check_transaction.
- fn post_transaction(&self, _: Params) -> Result;
-
- /// Checks the progress of a previously posted request (transaction/sign).
- /// Should be given a valid send_transaction ID.
- /// Returns the transaction hash, the zero hash (not yet available),
- /// or the signature,
- /// or an error.
- fn check_request(&self, _: Params) -> Result;
-
- /// Decrypt some ECIES-encrypted message.
- /// First parameter is the address with which it is encrypted, second is the ciphertext.
- fn decrypt_message(&self, _: Params) -> Result;
-
- /// Should be used to convert object to io delegate.
- fn to_delegate(self) -> IoDelegate {
- let mut delegate = IoDelegate::new(Arc::new(self));
- delegate.add_async_method("eth_sign", EthSigning::sign);
- delegate.add_async_method("eth_sendTransaction", EthSigning::send_transaction);
- delegate.add_method("eth_postSign", EthSigning::post_sign);
- delegate.add_method("eth_postTransaction", EthSigning::post_transaction);
- delegate.add_method("eth_checkRequest", EthSigning::check_request);
- delegate.add_method("ethcore_decryptMessage", EthSigning::decrypt_message);
- delegate
- }
-}
diff --git a/rpc/src/v1/traits/eth_signing.rs b/rpc/src/v1/traits/eth_signing.rs
new file mode 100644
index 000000000..1d6f6e501
--- /dev/null
+++ b/rpc/src/v1/traits/eth_signing.rs
@@ -0,0 +1,63 @@
+// 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 .
+
+//! Eth rpc interface.
+use std::sync::Arc;
+use jsonrpc_core::*;
+
+/// Signing methods implementation relying on unlocked accounts.
+pub trait EthSigning: Sized + Send + Sync + 'static {
+ /// Signs the data with given address signature.
+ fn sign(&self, _: Params, _: Ready);
+
+ /// Posts sign request asynchronously.
+ /// Will return a confirmation ID for later use with check_transaction.
+ fn post_sign(&self, _: Params) -> Result;
+
+ /// 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
+ /// later use with check_transaction.
+ fn send_transaction(&self, _: Params, _: Ready);
+
+ /// Posts transaction asynchronously.
+ /// Will return a transaction ID for later use with check_transaction.
+ fn post_transaction(&self, _: Params) -> Result;
+
+ /// Checks the progress of a previously posted request (transaction/sign).
+ /// Should be given a valid send_transaction ID.
+ /// Returns the transaction hash, the zero hash (not yet available),
+ /// or the signature,
+ /// or an error.
+ fn check_request(&self, _: Params) -> Result;
+
+ /// Decrypt some ECIES-encrypted message.
+ /// First parameter is the address with which it is encrypted, second is the ciphertext.
+ fn decrypt_message(&self, _: Params, _: Ready);
+
+ /// Should be used to convert object to io delegate.
+ fn to_delegate(self) -> IoDelegate {
+ let mut delegate = IoDelegate::new(Arc::new(self));
+ delegate.add_async_method("eth_sign", EthSigning::sign);
+ delegate.add_async_method("eth_sendTransaction", EthSigning::send_transaction);
+ delegate.add_async_method("ethcore_decryptMessage", EthSigning::decrypt_message);
+
+ delegate.add_method("eth_postSign", EthSigning::post_sign);
+ delegate.add_method("eth_postTransaction", EthSigning::post_transaction);
+ delegate.add_method("eth_checkRequest", EthSigning::check_request);
+ delegate
+ }
+}
diff --git a/rpc/src/v1/traits/mod.rs b/rpc/src/v1/traits/mod.rs
index 3ca11b654..e804c5553 100644
--- a/rpc/src/v1/traits/mod.rs
+++ b/rpc/src/v1/traits/mod.rs
@@ -18,6 +18,7 @@
pub mod web3;
pub mod eth;
+pub mod eth_signing;
pub mod net;
pub mod personal;
pub mod ethcore;
@@ -26,7 +27,8 @@ pub mod traces;
pub mod rpc;
pub use self::web3::Web3;
-pub use self::eth::{Eth, EthFilter, EthSigning};
+pub use self::eth::{Eth, EthFilter};
+pub use self::eth_signing::EthSigning;
pub use self::net::Net;
pub use self::personal::{Personal, PersonalSigner};
pub use self::ethcore::Ethcore;
@@ -34,4 +36,3 @@ pub use self::ethcore_set::EthcoreSet;
pub use self::traces::Traces;
pub use self::rpc::Rpc;
-
diff --git a/rpc/src/v1/types/confirmations.rs b/rpc/src/v1/types/confirmations.rs
index c074df443..c5ef4efa9 100644
--- a/rpc/src/v1/types/confirmations.rs
+++ b/rpc/src/v1/types/confirmations.rs
@@ -16,7 +16,7 @@
//! Types used in Confirmations queue (Trusted Signer)
-use v1::types::{U256, TransactionRequest, H160, H256};
+use v1::types::{U256, TransactionRequest, H160, H256, Bytes};
use v1::helpers;
@@ -47,6 +47,15 @@ pub struct SignRequest {
pub hash: H256,
}
+/// Decrypt request
+#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)]
+pub struct DecryptRequest {
+ /// Address
+ pub address: H160,
+ /// Message to decrypt
+ pub msg: Bytes,
+}
+
/// Confirmation payload, i.e. the thing to be confirmed
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)]
pub enum ConfirmationPayload {
@@ -56,6 +65,9 @@ pub enum ConfirmationPayload {
/// Signature
#[serde(rename="sign")]
Sign(SignRequest),
+ /// Decryption
+ #[serde(rename="decrypt")]
+ Decrypt(DecryptRequest),
}
impl From for ConfirmationPayload {
@@ -66,6 +78,10 @@ impl From for ConfirmationPayload {
address: address.into(),
hash: hash.into(),
}),
+ helpers::ConfirmationPayload::Decrypt(address, msg) => ConfirmationPayload::Decrypt(DecryptRequest {
+ address: address.into(),
+ msg: msg.into(),
+ }),
}
}
}
diff --git a/signer/Cargo.toml b/signer/Cargo.toml
index 3d0e76896..43b6bd84a 100644
--- a/signer/Cargo.toml
+++ b/signer/Cargo.toml
@@ -20,11 +20,12 @@ ethcore-util = { path = "../util" }
ethcore-io = { path = "../util/io" }
ethcore-rpc = { path = "../rpc" }
ethcore-devtools = { path = "../devtools" }
+parity-dapps = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4", optional = true}
parity-dapps-signer = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4", optional = true}
clippy = { version = "0.0.90", optional = true}
[features]
dev = ["clippy"]
-ui = ["parity-dapps-signer"]
+ui = ["parity-dapps", "parity-dapps-signer"]
use-precompiled-js = ["parity-dapps-signer/use-precompiled-js"]
diff --git a/signer/src/lib.rs b/signer/src/lib.rs
index abe84a03e..630cefb82 100644
--- a/signer/src/lib.rs
+++ b/signer/src/lib.rs
@@ -54,6 +54,8 @@ extern crate jsonrpc_core;
extern crate ws;
#[cfg(feature = "ui")]
extern crate parity_dapps_signer as signer;
+#[cfg(feature = "ui")]
+extern crate parity_dapps as dapps;
#[cfg(test)]
extern crate ethcore_devtools as devtools;
diff --git a/signer/src/ws_server/session.rs b/signer/src/ws_server/session.rs
index afc6606d7..1e3a9a7d2 100644
--- a/signer/src/ws_server/session.rs
+++ b/signer/src/ws_server/session.rs
@@ -26,21 +26,39 @@ use util::{H256, Mutex, version};
#[cfg(feature = "ui")]
mod signer {
- use signer;
+ use signer::SignerApp;
+ use dapps::{self, WebApp};
- pub fn handle(req: &str) -> Option {
- signer::handle(req)
+ #[derive(Default)]
+ pub struct Handler {
+ signer: SignerApp,
+ }
+
+ impl Handler {
+ pub fn handle(&self, req: &str) -> Option<&dapps::File> {
+ let file = match req {
+ "" | "/" => "index.html",
+ path => &path[1..],
+ };
+ self.signer.file(file)
+ }
}
}
#[cfg(not(feature = "ui"))]
mod signer {
pub struct File {
- pub content: String,
- pub mime: String,
+ pub content: &'static str,
+ pub content_type: &'static str,
}
- pub fn handle(_req: &str) -> Option {
- None
+ #[derive(Default)]
+ pub struct Handler {
+ }
+
+ impl Handler {
+ pub fn handle(&self, _req: &str) -> Option<&File> {
+ None
+ }
}
}
@@ -107,6 +125,7 @@ pub struct Session {
self_origin: String,
authcodes_path: PathBuf,
handler: Arc,
+ file_handler: Arc,
}
impl ws::Handler for Session {
@@ -152,12 +171,12 @@ impl ws::Handler for Session {
}
// Otherwise try to serve a page.
- Ok(signer::handle(req.resource())
+ Ok(self.file_handler.handle(req.resource())
.map_or_else(
// return 404 not found
|| error(ErrorType::NotFound, "Not found", "Requested file was not found.", None),
// or serve the file
- |f| add_headers(ws::Response::ok(f.content.into()), &f.mime)
+ |f| add_headers(ws::Response::ok_raw(f.content.to_vec()), f.content_type)
))
}
@@ -181,6 +200,7 @@ pub struct Factory {
skip_origin_validation: bool,
self_origin: String,
authcodes_path: PathBuf,
+ file_handler: Arc,
}
impl Factory {
@@ -190,6 +210,7 @@ impl Factory {
skip_origin_validation: skip_origin_validation,
self_origin: self_origin,
authcodes_path: authcodes_path,
+ file_handler: Arc::new(signer::Handler::default()),
}
}
}
@@ -204,6 +225,7 @@ impl ws::Factory for Factory {
skip_origin_validation: self.skip_origin_validation,
self_origin: self.self_origin.clone(),
authcodes_path: self.authcodes_path.clone(),
+ file_handler: self.file_handler.clone(),
}
}
}