Support for decryption in Signer (#2421)
* Adding some tests * Implementing decrypt in queue * Removing code duplication. * Printing public key in ethstore * Bump UI * Normalizing dapps format for signer. * Fixing tests compilation * fix whitespace [ci:skip]
This commit is contained in:
parent
85eeb3ea6e
commit
03c1559ead
13
Cargo.lock
generated
13
Cargo.lock
generated
@ -505,6 +505,7 @@ dependencies = [
|
|||||||
"ethcore-util 1.4.0",
|
"ethcore-util 1.4.0",
|
||||||
"jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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]]
|
[[package]]
|
||||||
name = "parity-dapps"
|
name = "parity-dapps"
|
||||||
version = "1.4.0"
|
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 = [
|
dependencies = [
|
||||||
"aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -1181,7 +1182,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-dapps-home"
|
name = "parity-dapps-home"
|
||||||
version = "1.4.0"
|
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 = [
|
dependencies = [
|
||||||
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||||
]
|
]
|
||||||
@ -1189,7 +1190,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-dapps-signer"
|
name = "parity-dapps-signer"
|
||||||
version = "1.4.0"
|
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 = [
|
dependencies = [
|
||||||
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||||
]
|
]
|
||||||
@ -1197,7 +1198,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-dapps-status"
|
name = "parity-dapps-status"
|
||||||
version = "1.4.0"
|
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 = [
|
dependencies = [
|
||||||
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||||
]
|
]
|
||||||
@ -1205,7 +1206,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-dapps-wallet"
|
name = "parity-dapps-wallet"
|
||||||
version = "1.4.0"
|
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 = [
|
dependencies = [
|
||||||
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"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]]
|
[[package]]
|
||||||
name = "ws"
|
name = "ws"
|
||||||
version = "0.5.2"
|
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 = [
|
dependencies = [
|
||||||
"bytes 0.4.0-dev (git+https://github.com/carllerche/bytes)",
|
"bytes 0.4.0-dev (git+https://github.com/carllerche/bytes)",
|
||||||
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -23,7 +23,7 @@ use std::time::{Instant, Duration};
|
|||||||
use util::{Mutex, RwLock};
|
use util::{Mutex, RwLock};
|
||||||
use ethstore::{SecretStore, Error as SSError, SafeAccount, EthStore};
|
use ethstore::{SecretStore, Error as SSError, SafeAccount, EthStore};
|
||||||
use ethstore::dir::{KeyDirectory};
|
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;
|
use ethjson::misc::AccountMeta;
|
||||||
pub use ethstore::ethkey::Signature;
|
pub use ethstore::ethkey::Signature;
|
||||||
|
|
||||||
@ -182,9 +182,16 @@ impl AccountProvider {
|
|||||||
|
|
||||||
/// Creates new random account.
|
/// Creates new random account.
|
||||||
pub fn new_account(&self, password: &str) -> Result<Address, Error> {
|
pub fn new_account(&self, password: &str) -> Result<Address, Error> {
|
||||||
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));
|
let address = try!(self.sstore.insert_account(secret, password));
|
||||||
Ok(address)
|
Ok((address, public))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts new account into underlying store.
|
/// Inserts new account into underlying store.
|
||||||
@ -280,6 +287,21 @@ impl AccountProvider {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn password(&self, account: &Address) -> Result<String, Error> {
|
||||||
|
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.
|
/// Unlocks account permanently.
|
||||||
pub fn unlock_account_permanently(&self, account: Address, password: String) -> Result<(), Error> {
|
pub fn unlock_account_permanently(&self, account: Address, password: String) -> Result<(), Error> {
|
||||||
self.unlock_account(account, password, Unlock::Perm)
|
self.unlock_account(account, password, Unlock::Perm)
|
||||||
@ -301,51 +323,16 @@ impl AccountProvider {
|
|||||||
unlocked.get(&account).is_some()
|
unlocked.get(&account).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Signs the message. Account must be unlocked.
|
/// Signs the message. If password is not provided the account must be unlocked.
|
||||||
pub fn sign(&self, account: Address, message: Message) -> Result<Signature, Error> {
|
pub fn sign(&self, account: Address, password: Option<String>, message: Message) -> Result<Signature, Error> {
|
||||||
let data = {
|
let password = try!(password.map(Ok).unwrap_or_else(|| self.password(&account)));
|
||||||
let mut unlocked = self.unlocked.lock();
|
Ok(try!(self.sstore.sign(&account, &password, &message)))
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decrypts a message. Account must be unlocked.
|
/// Decrypts a message. If password is not provided the account must be unlocked.
|
||||||
pub fn decrypt(&self, account: Address, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
pub fn decrypt(&self, account: Address, password: Option<String>, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
||||||
let data = {
|
let password = try!(password.map(Ok).unwrap_or_else(|| self.password(&account)));
|
||||||
let mut unlocked = self.unlocked.lock();
|
Ok(try!(self.sstore.decrypt(&account, &password, shared_mac, message)))
|
||||||
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<Signature, Error> {
|
|
||||||
let signature = try!(self.sstore.sign(&account, &password, &message));
|
|
||||||
Ok(signature)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the underlying `SecretStore` reference if one exists.
|
/// 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.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(), "test1".into()).is_err());
|
||||||
assert!(ap.unlock_account_temporarily(kp.address(), "test".into()).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(), None, Default::default()).is_ok());
|
||||||
assert!(ap.sign(kp.address(), Default::default()).is_err());
|
assert!(ap.sign(kp.address(), None, Default::default()).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -397,11 +384,11 @@ mod tests {
|
|||||||
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
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(), "test1".into()).is_err());
|
||||||
assert!(ap.unlock_account_permanently(kp.address(), "test".into()).is_ok());
|
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(), None, 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.unlock_account_temporarily(kp.address(), "test".into()).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(), None, Default::default()).is_ok());
|
||||||
assert!(ap.sign(kp.address(), Default::default()).is_ok());
|
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -411,8 +398,8 @@ mod tests {
|
|||||||
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
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(), "test1".into(), 2000).is_err());
|
||||||
assert!(ap.unlock_account_timed(kp.address(), "test".into(), 2000).is_ok());
|
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));
|
::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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,7 @@ impl Engine for BasicAuthority {
|
|||||||
let header = block.header();
|
let header = block.header();
|
||||||
let message = header.bare_hash();
|
let message = header.bare_hash();
|
||||||
// account should be pernamently unlocked, otherwise sealing will fail
|
// 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()]);
|
return Some(vec![::rlp::encode(&(&*signature as &[u8])).to_vec()]);
|
||||||
} else {
|
} else {
|
||||||
trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable");
|
trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable");
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use ethkey::{KeyPair, sign, Address, Secret, Signature, Message};
|
use ethkey::{KeyPair, sign, Address, Secret, Signature, Message, Public};
|
||||||
use {json, Error, crypto};
|
use {json, Error, crypto};
|
||||||
use crypto::Keccak256;
|
use crypto::Keccak256;
|
||||||
use random::Random;
|
use random::Random;
|
||||||
@ -180,6 +180,11 @@ impl SafeAccount {
|
|||||||
crypto::ecies::decrypt(&secret, shared_mac, message).map_err(From::from)
|
crypto::ecies::decrypt(&secret, shared_mac, message).map_err(From::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn public(&self, password: &str) -> Result<Public, Error> {
|
||||||
|
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<Self, Error> {
|
pub fn change_password(&self, old_password: &str, new_password: &str, iterations: u32) -> Result<Self, Error> {
|
||||||
let secret = try!(self.crypto.secret(old_password));
|
let secret = try!(self.crypto.secret(old_password));
|
||||||
let result = SafeAccount {
|
let result = SafeAccount {
|
||||||
|
@ -37,6 +37,7 @@ Usage:
|
|||||||
ethstore import-wallet <path> <password> [--dir DIR]
|
ethstore import-wallet <path> <password> [--dir DIR]
|
||||||
ethstore remove <address> <password> [--dir DIR]
|
ethstore remove <address> <password> [--dir DIR]
|
||||||
ethstore sign <address> <password> <message> [--dir DIR]
|
ethstore sign <address> <password> <message> [--dir DIR]
|
||||||
|
ethstore public <address> <password>
|
||||||
ethstore [-h | --help]
|
ethstore [-h | --help]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
@ -56,6 +57,7 @@ Commands:
|
|||||||
import-wallet Import presale wallet.
|
import-wallet Import presale wallet.
|
||||||
remove Remove account.
|
remove Remove account.
|
||||||
sign Sign message.
|
sign Sign message.
|
||||||
|
public Displays public key for an address.
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
#[derive(Debug, RustcDecodable)]
|
#[derive(Debug, RustcDecodable)]
|
||||||
@ -67,6 +69,7 @@ struct Args {
|
|||||||
cmd_import_wallet: bool,
|
cmd_import_wallet: bool,
|
||||||
cmd_remove: bool,
|
cmd_remove: bool,
|
||||||
cmd_sign: bool,
|
cmd_sign: bool,
|
||||||
|
cmd_public: bool,
|
||||||
arg_secret: String,
|
arg_secret: String,
|
||||||
arg_password: String,
|
arg_password: String,
|
||||||
arg_old_pwd: String,
|
arg_old_pwd: String,
|
||||||
@ -103,7 +106,7 @@ fn key_dir(location: &str) -> Result<Box<KeyDirectory>, Error> {
|
|||||||
fn format_accounts(accounts: &[Address]) -> String {
|
fn format_accounts(accounts: &[Address]) -> String {
|
||||||
accounts.iter()
|
accounts.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, a)| format!("{:2}: {}", i, a))
|
.map(|(i, a)| format!("{:2}: 0x{:?}", i, a))
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join("\n")
|
.join("\n")
|
||||||
}
|
}
|
||||||
@ -128,7 +131,7 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
|
|||||||
let secret = try!(args.arg_secret.parse().map_err(|_| Error::InvalidSecret));
|
let secret = try!(args.arg_secret.parse().map_err(|_| Error::InvalidSecret));
|
||||||
let password = try!(load_password(&args.arg_password));
|
let password = try!(load_password(&args.arg_password));
|
||||||
let address = try!(store.insert_account(secret, &password));
|
let address = try!(store.insert_account(secret, &password));
|
||||||
Ok(format!("{}", address))
|
Ok(format!("0x{:?}", address))
|
||||||
} else if args.cmd_change_pwd {
|
} else if args.cmd_change_pwd {
|
||||||
let address = try!(args.arg_address.parse().map_err(|_| Error::InvalidAccount));
|
let address = try!(args.arg_address.parse().map_err(|_| Error::InvalidAccount));
|
||||||
let old_pwd = try!(load_password(&args.arg_old_pwd));
|
let old_pwd = try!(load_password(&args.arg_old_pwd));
|
||||||
@ -148,7 +151,7 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
|
|||||||
let password = try!(load_password(&args.arg_password));
|
let password = try!(load_password(&args.arg_password));
|
||||||
let kp = try!(wallet.decrypt(&password));
|
let kp = try!(wallet.decrypt(&password));
|
||||||
let address = try!(store.insert_account(kp.secret().clone(), &password));
|
let address = try!(store.insert_account(kp.secret().clone(), &password));
|
||||||
Ok(format!("{}", address))
|
Ok(format!("0x{:?}", address))
|
||||||
} else if args.cmd_remove {
|
} else if args.cmd_remove {
|
||||||
let address = try!(args.arg_address.parse().map_err(|_| Error::InvalidAccount));
|
let address = try!(args.arg_address.parse().map_err(|_| Error::InvalidAccount));
|
||||||
let password = try!(load_password(&args.arg_password));
|
let password = try!(load_password(&args.arg_password));
|
||||||
@ -159,7 +162,12 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
|
|||||||
let message = try!(args.arg_message.parse().map_err(|_| Error::InvalidMessage));
|
let message = try!(args.arg_message.parse().map_err(|_| Error::InvalidMessage));
|
||||||
let password = try!(load_password(&args.arg_password));
|
let password = try!(load_password(&args.arg_password));
|
||||||
let signature = try!(store.sign(&address, &password, &message));
|
let signature = try!(store.sign(&address, &password, &message));
|
||||||
Ok(format!("{}", signature))
|
Ok(format!("0x{:?}", signature))
|
||||||
|
} else if args.cmd_public {
|
||||||
|
let address = try!(args.arg_address.parse().map_err(|_| Error::InvalidAccount));
|
||||||
|
let password = try!(load_password(&args.arg_password));
|
||||||
|
let public = try!(store.public(&address, &password));
|
||||||
|
Ok(format!("0x{:?}", public))
|
||||||
} else {
|
} else {
|
||||||
Ok(format!("{}", USAGE))
|
Ok(format!("{}", USAGE))
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ use std::mem;
|
|||||||
use ethkey::KeyPair;
|
use ethkey::KeyPair;
|
||||||
use crypto::KEY_ITERATIONS;
|
use crypto::KEY_ITERATIONS;
|
||||||
use random::Random;
|
use random::Random;
|
||||||
use ethkey::{Signature, Address, Message, Secret};
|
use ethkey::{Signature, Address, Message, Secret, Public};
|
||||||
use dir::KeyDirectory;
|
use dir::KeyDirectory;
|
||||||
use account::SafeAccount;
|
use account::SafeAccount;
|
||||||
use {Error, SecretStore};
|
use {Error, SecretStore};
|
||||||
@ -149,6 +149,11 @@ impl SecretStore for EthStore {
|
|||||||
account.decrypt(password, shared_mac, message)
|
account.decrypt(password, shared_mac, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn public(&self, account: &Address, password: &str) -> Result<Public, Error> {
|
||||||
|
let account = try!(self.get(account));
|
||||||
|
account.public(password)
|
||||||
|
}
|
||||||
|
|
||||||
fn uuid(&self, address: &Address) -> Result<UUID, Error> {
|
fn uuid(&self, address: &Address) -> Result<UUID, Error> {
|
||||||
let account = try!(self.get(address));
|
let account = try!(self.get(address));
|
||||||
Ok(account.id.into())
|
Ok(account.id.into())
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use ethkey::{Address, Message, Signature, Secret};
|
use ethkey::{Address, Message, Signature, Secret, Public};
|
||||||
use Error;
|
use Error;
|
||||||
use json::UUID;
|
use json::UUID;
|
||||||
|
|
||||||
@ -27,6 +27,7 @@ pub trait SecretStore: Send + Sync {
|
|||||||
|
|
||||||
fn sign(&self, account: &Address, password: &str, message: &Message) -> Result<Signature, Error>;
|
fn sign(&self, account: &Address, password: &str, message: &Message) -> Result<Signature, Error>;
|
||||||
fn decrypt(&self, account: &Address, password: &str, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error>;
|
fn decrypt(&self, account: &Address, password: &str, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error>;
|
||||||
|
fn public(&self, account: &Address, password: &str) -> Result<Public, Error>;
|
||||||
|
|
||||||
fn accounts(&self) -> Result<Vec<Address>, Error>;
|
fn accounts(&self) -> Result<Vec<Address>, Error>;
|
||||||
fn uuid(&self, account: &Address) -> Result<UUID, Error>;
|
fn uuid(&self, account: &Address) -> Result<UUID, Error>;
|
||||||
|
@ -14,23 +14,73 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use util::{Address, H256, U256, Uint};
|
use util::{Address, H256, U256, Uint, Bytes};
|
||||||
use util::bytes::ToPretty;
|
use util::bytes::ToPretty;
|
||||||
|
use ethkey::Signature;
|
||||||
use ethcore::miner::MinerService;
|
use ethcore::miner::MinerService;
|
||||||
use ethcore::client::MiningBlockChainClient;
|
use ethcore::client::MiningBlockChainClient;
|
||||||
use ethcore::transaction::{Action, SignedTransaction, Transaction};
|
use ethcore::transaction::{Action, SignedTransaction, Transaction};
|
||||||
use ethcore::account_provider::AccountProvider;
|
use ethcore::account_provider::AccountProvider;
|
||||||
use jsonrpc_core::{Error, Value, to_value};
|
use jsonrpc_core::{Error, Value, to_value};
|
||||||
use v1::helpers::TransactionRequest;
|
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;
|
use v1::helpers::errors;
|
||||||
|
|
||||||
|
pub const DEFAULT_MAC: [u8; 2] = [0, 0];
|
||||||
|
|
||||||
|
pub fn dispatch_transaction<C, M>(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result<RpcH256, Error>
|
||||||
|
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<String>, hash: H256) -> Result<Signature, Error> {
|
||||||
|
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<String>, hash: H256) -> Result<Value, Error> {
|
||||||
|
signature(accounts, address, password, hash)
|
||||||
|
.map(RpcH520::from)
|
||||||
|
.map(to_value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decrypt(accounts: &AccountProvider, address: Address, password: Option<String>, msg: Bytes) -> Result<Value, Error> {
|
||||||
|
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<C, M>(client: &C, miner: &M, accounts: &AccountProvider, request: TransactionRequest, password: Option<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();
|
||||||
|
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<C, M>(client: &C, miner: &M, request: TransactionRequest) -> Transaction where C: MiningBlockChainClient, M: MinerService {
|
fn prepare_transaction<C, M>(client: &C, miner: &M, request: TransactionRequest) -> Transaction where C: MiningBlockChainClient, M: MinerService {
|
||||||
Transaction {
|
Transaction {
|
||||||
nonce: request.nonce
|
nonce: request.nonce
|
||||||
.or_else(|| miner
|
.or_else(|| miner
|
||||||
.last_nonce(&request.from)
|
.last_nonce(&request.from)
|
||||||
.map(|nonce| nonce + U256::one()))
|
.map(|nonce| nonce + U256::one()))
|
||||||
.unwrap_or_else(|| client.latest_nonce(&request.from)),
|
.unwrap_or_else(|| client.latest_nonce(&request.from)),
|
||||||
|
|
||||||
action: request.to.map_or(Action::Create, Action::Call),
|
action: request.to.map_or(Action::Create, Action::Call),
|
||||||
@ -41,52 +91,6 @@ fn prepare_transaction<C, M>(client: &C, miner: &M, request: TransactionRequest)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dispatch_transaction<C, M>(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result<RpcH256, Error>
|
|
||||||
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<Value, Error> {
|
|
||||||
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<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();
|
|
||||||
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<C, M>(client: &C, miner: &M, request: TransactionRequest, account_provider: &AccountProvider, address: Address) -> Result<Value, Error>
|
|
||||||
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<C, M>(client: &C, miner: &M) -> U256 where C: MiningBlockChainClient, M: MinerService {
|
pub fn default_gas_price<C, M>(client: &C, miner: &M) -> U256 where C: MiningBlockChainClient, M: MinerService {
|
||||||
client
|
client
|
||||||
.gas_price_statistics(100, 8)
|
.gas_price_statistics(100, 8)
|
||||||
|
@ -43,6 +43,7 @@ mod codes {
|
|||||||
pub const REQUEST_REJECTED_LIMIT: i64 = -32041;
|
pub const REQUEST_REJECTED_LIMIT: i64 = -32041;
|
||||||
pub const REQUEST_NOT_FOUND: i64 = -32042;
|
pub const REQUEST_NOT_FOUND: i64 = -32042;
|
||||||
pub const COMPILATION_ERROR: i64 = -32050;
|
pub const COMPILATION_ERROR: i64 = -32050;
|
||||||
|
pub const ENCRYPTION_ERROR: i64 = -32055;
|
||||||
pub const FETCH_ERROR: i64 = -32060;
|
pub const FETCH_ERROR: i64 = -32060;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,6 +167,14 @@ pub fn signer_disabled() -> Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn encryption_error<T: fmt::Debug>(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 {
|
pub fn from_fetch_error(error: FetchError) -> Error {
|
||||||
Error {
|
Error {
|
||||||
code: ErrorCode::ServerError(codes::FETCH_ERROR),
|
code: ErrorCode::ServerError(codes::FETCH_ERROR),
|
||||||
|
@ -103,4 +103,6 @@ pub enum ConfirmationPayload {
|
|||||||
Transaction(FilledTransactionRequest),
|
Transaction(FilledTransactionRequest),
|
||||||
/// Sign request
|
/// Sign request
|
||||||
Sign(Address, H256),
|
Sign(Address, H256),
|
||||||
|
/// Decrypt request
|
||||||
|
Decrypt(Address, Bytes),
|
||||||
}
|
}
|
||||||
|
@ -24,9 +24,9 @@ use util::{U256, Address, H256, Mutex};
|
|||||||
use transient_hashmap::TransientHashMap;
|
use transient_hashmap::TransientHashMap;
|
||||||
use ethcore::account_provider::AccountProvider;
|
use ethcore::account_provider::AccountProvider;
|
||||||
use v1::helpers::{errors, SigningQueue, ConfirmationPromise, ConfirmationResult, ConfirmationPayload, TransactionRequest as TRequest, FilledTransactionRequest as FilledRequest, SignerService};
|
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::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<C, M>(request: TRequest, client: &C, miner: &M) -> FilledRequest
|
fn fill_optional_fields<C, M>(request: TRequest, client: &C, miner: &M) -> FilledRequest
|
||||||
where C: MiningBlockChainClient, M: MinerService {
|
where C: MiningBlockChainClient, M: MinerService {
|
||||||
@ -76,41 +76,59 @@ impl<C, M> EthSigningQueueClient<C, M> where C: MiningBlockChainClient, M: Miner
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_to_queue<WhenUnlocked, Payload>(&self, sender: Address, when_unlocked: WhenUnlocked, payload: Payload)
|
||||||
|
-> Result<DispatchResult, Error> where
|
||||||
|
WhenUnlocked: Fn(&AccountProvider) -> Result<Value, Error>,
|
||||||
|
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<DispatchResult, Error>, 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<DispatchResult, Error> {
|
fn dispatch_sign(&self, params: Params) -> Result<DispatchResult, Error> {
|
||||||
from_params::<(RpcH160, RpcH256)>(params).and_then(|(address, msg)| {
|
from_params::<(RpcH160, RpcH256)>(params).and_then(|(address, msg)| {
|
||||||
let address: Address = address.into();
|
let address: Address = address.into();
|
||||||
let msg: H256 = msg.into();
|
let msg: H256 = msg.into();
|
||||||
|
|
||||||
let accounts = take_weak!(self.accounts);
|
self.add_to_queue(
|
||||||
if accounts.is_unlocked(address) {
|
address,
|
||||||
return Ok(DispatchResult::Value(to_value(&accounts.sign(address, msg).ok().map_or_else(RpcH520::default, Into::into))))
|
|accounts| sign(accounts, address, None, msg.clone()),
|
||||||
}
|
|| ConfirmationPayload::Sign(address, msg.clone()),
|
||||||
|
)
|
||||||
let signer = take_weak!(self.signer);
|
|
||||||
signer.add_request(ConfirmationPayload::Sign(address, msg))
|
|
||||||
.map(DispatchResult::Promise)
|
|
||||||
.map_err(|_| errors::request_rejected_limit())
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dispatch_transaction(&self, params: Params) -> Result<DispatchResult, Error> {
|
fn dispatch_transaction(&self, params: Params) -> Result<DispatchResult, Error> {
|
||||||
from_params::<(TransactionRequest, )>(params)
|
from_params::<(TransactionRequest, )>(params).and_then(|(request, )| {
|
||||||
.and_then(|(request, )| {
|
let request: TRequest = request.into();
|
||||||
let request: TRequest = request.into();
|
let (client, miner) = (take_weak!(self.client), take_weak!(self.miner));
|
||||||
let accounts = take_weak!(self.accounts);
|
self.add_to_queue(
|
||||||
let (client, miner) = (take_weak!(self.client), take_weak!(self.miner));
|
request.from,
|
||||||
|
|accounts| sign_and_dispatch(&*client, &*miner, accounts, request.clone(), None),
|
||||||
if accounts.is_unlocked(request.from) {
|
|| {
|
||||||
let sender = request.from;
|
let request = fill_optional_fields(request.clone(), &*client, &*miner);
|
||||||
return sign_and_dispatch(&*client, &*miner, request, &*accounts, sender).map(DispatchResult::Value);
|
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<C, M> EthSigning for EthSigningQueueClient<C, M>
|
|||||||
where C: MiningBlockChainClient + 'static, M: MinerService + 'static
|
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<Value, Error> {
|
fn post_sign(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
self.dispatch_sign(params).map(|result| match result {
|
self.dispatch_sign(params).map(|result| match result {
|
||||||
@ -143,19 +148,6 @@ impl<C, M> EthSigning for EthSigningQueueClient<C, M>
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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<Value, Error> {
|
fn post_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
self.dispatch_transaction(params).map(|result| match result {
|
self.dispatch_transaction(params).map(|result| match result {
|
||||||
@ -168,13 +160,6 @@ impl<C, M> EthSigning for EthSigningQueueClient<C, M>
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrypt_message(&self, params: Params) -> Result<Value, Error> {
|
|
||||||
try!(self.active());
|
|
||||||
from_params::<(RpcH160, RpcBytes)>(params).and_then(|(_account, _ciphertext)| {
|
|
||||||
Err(errors::unimplemented())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_request(&self, params: Params) -> Result<Value, Error> {
|
fn check_request(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
let mut pending = self.pending.lock();
|
let mut pending = self.pending.lock();
|
||||||
@ -192,6 +177,32 @@ impl<C, M> EthSigning for EthSigningQueueClient<C, M>
|
|||||||
res
|
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.
|
/// Implementation of functions that require signing when no trusted signer is used.
|
||||||
@ -232,9 +243,7 @@ impl<C, M> EthSigning for EthSigningUnsafeClient<C, M> where
|
|||||||
ready.ready(self.active()
|
ready.ready(self.active()
|
||||||
.and_then(|_| from_params::<(RpcH160, RpcH256)>(params))
|
.and_then(|_| from_params::<(RpcH160, RpcH256)>(params))
|
||||||
.and_then(|(address, msg)| {
|
.and_then(|(address, msg)| {
|
||||||
let address: Address = address.into();
|
sign(&*take_weak!(self.accounts), address.into(), None, msg.into())
|
||||||
let msg: H256 = msg.into();
|
|
||||||
Ok(to_value(&take_weak!(self.accounts).sign(address, msg).ok().map_or_else(RpcH520::default, Into::into)))
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,18 +251,16 @@ impl<C, M> EthSigning for EthSigningUnsafeClient<C, M> where
|
|||||||
ready.ready(self.active()
|
ready.ready(self.active()
|
||||||
.and_then(|_| from_params::<(TransactionRequest, )>(params))
|
.and_then(|_| from_params::<(TransactionRequest, )>(params))
|
||||||
.and_then(|(request, )| {
|
.and_then(|(request, )| {
|
||||||
let request: TRequest = request.into();
|
sign_and_dispatch(&*take_weak!(self.client), &*take_weak!(self.miner), &*take_weak!(self.accounts), request.into(), None)
|
||||||
let sender = request.from;
|
|
||||||
sign_and_dispatch(&*take_weak!(self.client), &*take_weak!(self.miner), request, &*take_weak!(self.accounts), sender)
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrypt_message(&self, params: Params) -> Result<Value, Error> {
|
fn decrypt_message(&self, params: Params, ready: Ready) {
|
||||||
try!(self.active());
|
ready.ready(self.active()
|
||||||
from_params::<(RpcH160, RpcBytes)>(params).and_then(|(address, ciphertext)| {
|
.and_then(|_| from_params::<(RpcH160, RpcBytes)>(params))
|
||||||
let s = try!(take_weak!(self.accounts).decrypt(address.into(), &[0; 0], &ciphertext.0).map_err(|_| Error::internal_error()));
|
.and_then(|(address, ciphertext)| {
|
||||||
Ok(to_value(RpcBytes::from(s)))
|
decrypt(&*take_weak!(self.accounts), address.into(), None, ciphertext.0)
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_sign(&self, _: Params) -> Result<Value, Error> {
|
fn post_sign(&self, _: Params) -> Result<Value, Error> {
|
||||||
|
@ -35,6 +35,8 @@ use jsonrpc_core::Error;
|
|||||||
use v1::traits::Ethcore;
|
use v1::traits::Ethcore;
|
||||||
use v1::types::{Bytes, U256, H160, H256, H512, Peers, Transaction, RpcSettings};
|
use v1::types::{Bytes, U256, H160, H256, H512, Peers, Transaction, RpcSettings};
|
||||||
use v1::helpers::{errors, SigningQueue, SignerService, NetworkSettings};
|
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;
|
use v1::helpers::auto_args::Ready;
|
||||||
|
|
||||||
/// Ethcore implementation.
|
/// Ethcore implementation.
|
||||||
@ -265,8 +267,8 @@ impl<C, M, S: ?Sized, F> Ethcore for EthcoreClient<C, M, S, F> where
|
|||||||
fn encrypt_message(&self, key: H512, phrase: Bytes) -> Result<Bytes, Error> {
|
fn encrypt_message(&self, key: H512, phrase: Bytes) -> Result<Bytes, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
|
|
||||||
ecies::encrypt(&key.into(), &[0; 0], &phrase.0)
|
ecies::encrypt(&key.into(), &DEFAULT_MAC, &phrase.0)
|
||||||
.map_err(|_| Error::internal_error())
|
.map_err(errors::encryption_error)
|
||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,9 +22,9 @@ use jsonrpc_core::*;
|
|||||||
use ethkey::{Brain, Generator};
|
use ethkey::{Brain, Generator};
|
||||||
use v1::traits::Personal;
|
use v1::traits::Personal;
|
||||||
use v1::types::{H160 as RpcH160, TransactionRequest};
|
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::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::account_provider::AccountProvider;
|
||||||
use ethcore::client::MiningBlockChainClient;
|
use ethcore::client::MiningBlockChainClient;
|
||||||
use ethcore::miner::MinerService;
|
use ethcore::miner::MinerService;
|
||||||
@ -139,10 +139,13 @@ impl<C: 'static, M: 'static> Personal for PersonalClient<C, M> where C: MiningBl
|
|||||||
try!(self.active());
|
try!(self.active());
|
||||||
from_params::<(TransactionRequest, String)>(params)
|
from_params::<(TransactionRequest, String)>(params)
|
||||||
.and_then(|(request, password)| {
|
.and_then(|(request, password)| {
|
||||||
let request: TRequest = request.into();
|
sign_and_dispatch(
|
||||||
let accounts = take_weak!(self.accounts);
|
&*take_weak!(self.client),
|
||||||
|
&*take_weak!(self.miner),
|
||||||
unlock_sign_and_dispatch(&*take_weak!(self.client), &*take_weak!(self.miner), request, &*accounts, password)
|
&*take_weak!(self.accounts),
|
||||||
|
request.into(),
|
||||||
|
Some(password)
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ use v1::traits::PersonalSigner;
|
|||||||
use v1::types::{TransactionModification, ConfirmationRequest, U256};
|
use v1::types::{TransactionModification, ConfirmationRequest, U256};
|
||||||
use v1::helpers::{errors, SignerService, SigningQueue, ConfirmationPayload};
|
use v1::helpers::{errors, SignerService, SigningQueue, ConfirmationPayload};
|
||||||
use v1::helpers::params::expect_no_params;
|
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.
|
/// Transactions confirmation (personal) rpc implementation.
|
||||||
pub struct SignerClient<C, M> where C: MiningBlockChainClient, M: MinerService {
|
pub struct SignerClient<C, M> where C: MiningBlockChainClient, M: MinerService {
|
||||||
@ -87,12 +87,14 @@ impl<C: 'static, M: 'static> PersonalSigner for SignerClient<C, M> where C: Mini
|
|||||||
if let Some(gas_price) = modification.gas_price {
|
if let Some(gas_price) = modification.gas_price {
|
||||||
request.gas_price = gas_price.into();
|
request.gas_price = gas_price.into();
|
||||||
}
|
}
|
||||||
|
sign_and_dispatch(&*client, &*miner, &*accounts, request.into(), Some(pass))
|
||||||
unlock_sign_and_dispatch(&*client, &*miner, request.into(), &*accounts, pass)
|
|
||||||
},
|
},
|
||||||
ConfirmationPayload::Sign(address, hash) => {
|
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 {
|
if let Ok(ref response) = result {
|
||||||
signer.request_confirmed(id, Ok(response.clone()));
|
signer.request_confirmed(id, Ok(response.clone()));
|
||||||
|
@ -33,9 +33,10 @@ use util::{U256, H256, Uint, Address};
|
|||||||
use jsonrpc_core::IoHandler;
|
use jsonrpc_core::IoHandler;
|
||||||
use ethjson::blockchain::BlockChain;
|
use ethjson::blockchain::BlockChain;
|
||||||
|
|
||||||
use v1::types::U256 as NU256;
|
|
||||||
use v1::traits::eth::{Eth, EthSigning};
|
|
||||||
use v1::impls::{EthClient, EthSigningUnsafeClient};
|
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};
|
use v1::tests::helpers::{TestSyncProvider, Config};
|
||||||
|
|
||||||
fn account_provider() -> Arc<AccountProvider> {
|
fn account_provider() -> Arc<AccountProvider> {
|
||||||
|
@ -264,7 +264,7 @@ fn rpc_eth_sign() {
|
|||||||
let account = tester.accounts_provider.new_account("abcd").unwrap();
|
let account = tester.accounts_provider.new_account("abcd").unwrap();
|
||||||
tester.accounts_provider.unlock_account_permanently(account, "abcd".into()).unwrap();
|
tester.accounts_provider.unlock_account_permanently(account, "abcd".into()).unwrap();
|
||||||
let message = H256::from("0x0cc175b9c0f1b6a831c399e26977266192eb5ffee6ae2fec3ad71c777531578f");
|
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#"{
|
let req = r#"{
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
@ -709,7 +709,7 @@ fn rpc_eth_send_transaction() {
|
|||||||
value: U256::from(0x9184e72au64),
|
value: U256::from(0x9184e72au64),
|
||||||
data: vec![]
|
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 t = t.with_signature(signature);
|
||||||
|
|
||||||
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#;
|
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),
|
value: U256::from(0x9184e72au64),
|
||||||
data: vec![]
|
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 t = t.with_signature(signature);
|
||||||
|
|
||||||
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#;
|
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),
|
value: U256::from(0x9184e72au64),
|
||||||
data: vec![]
|
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 t = t.with_signature(signature);
|
||||||
|
|
||||||
let rlp = ::rlp::encode(&t).to_vec().to_hex();
|
let rlp = ::rlp::encode(&t).to_vec().to_hex();
|
||||||
|
@ -16,16 +16,20 @@
|
|||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use jsonrpc_core::{IoHandler, to_value};
|
use jsonrpc_core::{IoHandler, to_value, Success};
|
||||||
use v1::impls::EthSigningQueueClient;
|
use v1::impls::EthSigningQueueClient;
|
||||||
use v1::traits::EthSigning;
|
use v1::traits::{EthSigning, Ethcore};
|
||||||
use v1::helpers::{SignerService, SigningQueue};
|
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::helpers::TestMinerService;
|
||||||
|
use v1::tests::mocked::ethcore;
|
||||||
|
|
||||||
use util::{Address, FixedHash, Uint, U256, H256, H520};
|
use util::{Address, FixedHash, Uint, U256, H256, H520};
|
||||||
use ethcore::account_provider::AccountProvider;
|
use ethcore::account_provider::AccountProvider;
|
||||||
use ethcore::client::TestBlockChainClient;
|
use ethcore::client::TestBlockChainClient;
|
||||||
use ethcore::transaction::{Transaction, Action};
|
use ethcore::transaction::{Transaction, Action};
|
||||||
|
use ethstore::ethkey::{Generator, Random};
|
||||||
|
use serde_json;
|
||||||
|
|
||||||
struct EthSigningTester {
|
struct EthSigningTester {
|
||||||
pub signer: Arc<SignerService>,
|
pub signer: Arc<SignerService>,
|
||||||
@ -178,7 +182,7 @@ fn should_sign_if_account_is_unlocked() {
|
|||||||
let acc = tester.accounts.new_account("test").unwrap();
|
let acc = tester.accounts.new_account("test").unwrap();
|
||||||
tester.accounts.unlock_account_permanently(acc, "test".into()).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
|
// when
|
||||||
let request = r#"{
|
let request = r#"{
|
||||||
@ -242,7 +246,7 @@ fn should_dispatch_transaction_if_account_is_unlock() {
|
|||||||
value: U256::from(0x9184e72au64),
|
value: U256::from(0x9184e72au64),
|
||||||
data: vec![]
|
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);
|
let t = t.with_signature(signature);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -263,3 +267,65 @@ fn should_dispatch_transaction_if_account_is_unlock() {
|
|||||||
// then
|
// then
|
||||||
assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned()));
|
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());
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
@ -19,6 +19,7 @@ use util::log::RotatingLogger;
|
|||||||
use util::U256;
|
use util::U256;
|
||||||
use ethsync::ManageNetwork;
|
use ethsync::ManageNetwork;
|
||||||
use ethcore::client::{TestBlockChainClient};
|
use ethcore::client::{TestBlockChainClient};
|
||||||
|
use ethstore::ethkey::{Generator, Random};
|
||||||
|
|
||||||
use jsonrpc_core::IoHandler;
|
use jsonrpc_core::IoHandler;
|
||||||
use v1::{Ethcore, EthcoreClient};
|
use v1::{Ethcore, EthcoreClient};
|
||||||
@ -34,7 +35,7 @@ fn client_service() -> Arc<TestBlockChainClient> {
|
|||||||
Arc::new(TestBlockChainClient::default())
|
Arc::new(TestBlockChainClient::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sync_provider() -> Arc<TestSyncProvider> {
|
pub fn sync_provider() -> Arc<TestSyncProvider> {
|
||||||
Arc::new(TestSyncProvider::new(Config {
|
Arc::new(TestSyncProvider::new(Config {
|
||||||
network_id: U256::from(3),
|
network_id: U256::from(3),
|
||||||
num_peers: 120,
|
num_peers: 120,
|
||||||
@ -56,13 +57,13 @@ fn settings() -> Arc<NetworkSettings> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn network_service() -> Arc<ManageNetwork> {
|
pub fn network_service() -> Arc<ManageNetwork> {
|
||||||
Arc::new(TestManageNetwork)
|
Arc::new(TestManageNetwork)
|
||||||
}
|
}
|
||||||
|
|
||||||
type TestEthcoreClient = EthcoreClient<TestBlockChainClient, TestMinerService, TestSyncProvider, TestFetch>;
|
pub type TestEthcoreClient = EthcoreClient<TestBlockChainClient, TestMinerService, TestSyncProvider, TestFetch>;
|
||||||
|
|
||||||
fn ethcore_client(
|
pub fn ethcore_client(
|
||||||
client: &Arc<TestBlockChainClient>,
|
client: &Arc<TestBlockChainClient>,
|
||||||
miner: &Arc<TestMinerService>,
|
miner: &Arc<TestMinerService>,
|
||||||
sync: &Arc<TestSyncProvider>,
|
sync: &Arc<TestSyncProvider>,
|
||||||
@ -324,3 +325,17 @@ fn rpc_ethcore_pending_transactions() {
|
|||||||
|
|
||||||
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
|
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.");
|
||||||
|
}
|
||||||
|
@ -227,7 +227,7 @@ fn sign_and_send_transaction() {
|
|||||||
data: vec![]
|
data: vec![]
|
||||||
};
|
};
|
||||||
tester.accounts.unlock_account_temporarily(address, "password123".into()).unwrap();
|
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 t = t.with_signature(signature);
|
||||||
|
|
||||||
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#;
|
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![]
|
data: vec![]
|
||||||
};
|
};
|
||||||
tester.accounts.unlock_account_temporarily(address, "password123".into()).unwrap();
|
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 t = t.with_signature(signature);
|
||||||
|
|
||||||
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#;
|
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#;
|
||||||
|
@ -186,7 +186,7 @@ fn should_confirm_transaction_and_dispatch() {
|
|||||||
data: vec![]
|
data: vec![]
|
||||||
};
|
};
|
||||||
tester.accounts.unlock_account_temporarily(address, "test".into()).unwrap();
|
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);
|
let t = t.with_signature(signature);
|
||||||
|
|
||||||
assert_eq!(tester.signer.requests().len(), 1);
|
assert_eq!(tester.signer.requests().len(), 1);
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Eth rpc interface.
|
//! Eth rpc interface.
|
||||||
use std::sync::Arc;
|
|
||||||
use jsonrpc_core::*;
|
use jsonrpc_core::*;
|
||||||
|
|
||||||
use v1::types::{Block, BlockNumber, Bytes, CallRequest, Filter, FilterChanges, Index};
|
use v1::types::{Block, BlockNumber, Bytes, CallRequest, Filter, FilterChanges, Index};
|
||||||
@ -198,46 +197,3 @@ build_rpc_trait! {
|
|||||||
fn uninstall_filter(&self, Index) -> Result<bool, Error>;
|
fn uninstall_filter(&self, Index) -> Result<bool, Error>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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<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
|
|
||||||
/// 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<Value, Error>;
|
|
||||||
|
|
||||||
/// 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<Value, Error>;
|
|
||||||
|
|
||||||
/// 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<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_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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
63
rpc/src/v1/traits/eth_signing.rs
Normal file
63
rpc/src/v1/traits/eth_signing.rs
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! 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<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
|
||||||
|
/// 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<Value, Error>;
|
||||||
|
|
||||||
|
/// 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<Value, Error>;
|
||||||
|
|
||||||
|
/// 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<Self> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
pub mod web3;
|
pub mod web3;
|
||||||
pub mod eth;
|
pub mod eth;
|
||||||
|
pub mod eth_signing;
|
||||||
pub mod net;
|
pub mod net;
|
||||||
pub mod personal;
|
pub mod personal;
|
||||||
pub mod ethcore;
|
pub mod ethcore;
|
||||||
@ -26,7 +27,8 @@ pub mod traces;
|
|||||||
pub mod rpc;
|
pub mod rpc;
|
||||||
|
|
||||||
pub use self::web3::Web3;
|
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::net::Net;
|
||||||
pub use self::personal::{Personal, PersonalSigner};
|
pub use self::personal::{Personal, PersonalSigner};
|
||||||
pub use self::ethcore::Ethcore;
|
pub use self::ethcore::Ethcore;
|
||||||
@ -34,4 +36,3 @@ pub use self::ethcore_set::EthcoreSet;
|
|||||||
pub use self::traces::Traces;
|
pub use self::traces::Traces;
|
||||||
pub use self::rpc::Rpc;
|
pub use self::rpc::Rpc;
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
//! Types used in Confirmations queue (Trusted Signer)
|
//! 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;
|
use v1::helpers;
|
||||||
|
|
||||||
|
|
||||||
@ -47,6 +47,15 @@ pub struct SignRequest {
|
|||||||
pub hash: H256,
|
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
|
/// Confirmation payload, i.e. the thing to be confirmed
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)]
|
||||||
pub enum ConfirmationPayload {
|
pub enum ConfirmationPayload {
|
||||||
@ -56,6 +65,9 @@ pub enum ConfirmationPayload {
|
|||||||
/// Signature
|
/// Signature
|
||||||
#[serde(rename="sign")]
|
#[serde(rename="sign")]
|
||||||
Sign(SignRequest),
|
Sign(SignRequest),
|
||||||
|
/// Decryption
|
||||||
|
#[serde(rename="decrypt")]
|
||||||
|
Decrypt(DecryptRequest),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<helpers::ConfirmationPayload> for ConfirmationPayload {
|
impl From<helpers::ConfirmationPayload> for ConfirmationPayload {
|
||||||
@ -66,6 +78,10 @@ impl From<helpers::ConfirmationPayload> for ConfirmationPayload {
|
|||||||
address: address.into(),
|
address: address.into(),
|
||||||
hash: hash.into(),
|
hash: hash.into(),
|
||||||
}),
|
}),
|
||||||
|
helpers::ConfirmationPayload::Decrypt(address, msg) => ConfirmationPayload::Decrypt(DecryptRequest {
|
||||||
|
address: address.into(),
|
||||||
|
msg: msg.into(),
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,11 +20,12 @@ ethcore-util = { path = "../util" }
|
|||||||
ethcore-io = { path = "../util/io" }
|
ethcore-io = { path = "../util/io" }
|
||||||
ethcore-rpc = { path = "../rpc" }
|
ethcore-rpc = { path = "../rpc" }
|
||||||
ethcore-devtools = { path = "../devtools" }
|
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}
|
parity-dapps-signer = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4", optional = true}
|
||||||
|
|
||||||
clippy = { version = "0.0.90", optional = true}
|
clippy = { version = "0.0.90", optional = true}
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
dev = ["clippy"]
|
dev = ["clippy"]
|
||||||
ui = ["parity-dapps-signer"]
|
ui = ["parity-dapps", "parity-dapps-signer"]
|
||||||
use-precompiled-js = ["parity-dapps-signer/use-precompiled-js"]
|
use-precompiled-js = ["parity-dapps-signer/use-precompiled-js"]
|
||||||
|
@ -54,6 +54,8 @@ extern crate jsonrpc_core;
|
|||||||
extern crate ws;
|
extern crate ws;
|
||||||
#[cfg(feature = "ui")]
|
#[cfg(feature = "ui")]
|
||||||
extern crate parity_dapps_signer as signer;
|
extern crate parity_dapps_signer as signer;
|
||||||
|
#[cfg(feature = "ui")]
|
||||||
|
extern crate parity_dapps as dapps;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
extern crate ethcore_devtools as devtools;
|
extern crate ethcore_devtools as devtools;
|
||||||
|
|
||||||
|
@ -26,21 +26,39 @@ use util::{H256, Mutex, version};
|
|||||||
|
|
||||||
#[cfg(feature = "ui")]
|
#[cfg(feature = "ui")]
|
||||||
mod signer {
|
mod signer {
|
||||||
use signer;
|
use signer::SignerApp;
|
||||||
|
use dapps::{self, WebApp};
|
||||||
|
|
||||||
pub fn handle(req: &str) -> Option<signer::File> {
|
#[derive(Default)]
|
||||||
signer::handle(req)
|
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"))]
|
#[cfg(not(feature = "ui"))]
|
||||||
mod signer {
|
mod signer {
|
||||||
pub struct File {
|
pub struct File {
|
||||||
pub content: String,
|
pub content: &'static str,
|
||||||
pub mime: String,
|
pub content_type: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle(_req: &str) -> Option<File> {
|
#[derive(Default)]
|
||||||
None
|
pub struct Handler {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler {
|
||||||
|
pub fn handle(&self, _req: &str) -> Option<&File> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,6 +125,7 @@ pub struct Session {
|
|||||||
self_origin: String,
|
self_origin: String,
|
||||||
authcodes_path: PathBuf,
|
authcodes_path: PathBuf,
|
||||||
handler: Arc<IoHandler>,
|
handler: Arc<IoHandler>,
|
||||||
|
file_handler: Arc<signer::Handler>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ws::Handler for Session {
|
impl ws::Handler for Session {
|
||||||
@ -152,12 +171,12 @@ impl ws::Handler for Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise try to serve a page.
|
// Otherwise try to serve a page.
|
||||||
Ok(signer::handle(req.resource())
|
Ok(self.file_handler.handle(req.resource())
|
||||||
.map_or_else(
|
.map_or_else(
|
||||||
// return 404 not found
|
// return 404 not found
|
||||||
|| error(ErrorType::NotFound, "Not found", "Requested file was not found.", None),
|
|| error(ErrorType::NotFound, "Not found", "Requested file was not found.", None),
|
||||||
// or serve the file
|
// 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,
|
skip_origin_validation: bool,
|
||||||
self_origin: String,
|
self_origin: String,
|
||||||
authcodes_path: PathBuf,
|
authcodes_path: PathBuf,
|
||||||
|
file_handler: Arc<signer::Handler>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Factory {
|
impl Factory {
|
||||||
@ -190,6 +210,7 @@ impl Factory {
|
|||||||
skip_origin_validation: skip_origin_validation,
|
skip_origin_validation: skip_origin_validation,
|
||||||
self_origin: self_origin,
|
self_origin: self_origin,
|
||||||
authcodes_path: authcodes_path,
|
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,
|
skip_origin_validation: self.skip_origin_validation,
|
||||||
self_origin: self.self_origin.clone(),
|
self_origin: self.self_origin.clone(),
|
||||||
authcodes_path: self.authcodes_path.clone(),
|
authcodes_path: self.authcodes_path.clone(),
|
||||||
|
file_handler: self.file_handler.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user