diff --git a/ethcore/src/account_provider.rs b/ethcore/src/account_provider.rs
index 6744c6bd2..a71251727 100644
--- a/ethcore/src/account_provider.rs
+++ b/ethcore/src/account_provider.rs
@@ -205,6 +205,13 @@ impl AccountProvider {
self.unlock_account(account, password, Unlock::Temp)
}
+ /// Checks if given account is unlocked
+ pub fn is_unlocked(&self, account: A) -> bool where Address: From {
+ let account = Address::from(account).into();
+ let unlocked = self.unlocked.read().unwrap();
+ unlocked.get(&account).is_some()
+ }
+
/// Signs the message. Account must be unlocked.
pub fn sign(&self, account: A, message: M) -> Result where Address: From, Message: From {
let account = Address::from(account).into();
diff --git a/parity/main.rs b/parity/main.rs
index c6960993a..17d941a5d 100644
--- a/parity/main.rs
+++ b/parity/main.rs
@@ -198,6 +198,12 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig)
});
}
+ // Display warning about using unlock with signer
+ if conf.args.flag_signer && conf.args.flag_unlock.is_some() {
+ warn!("Using Trusted Signer and --unlock is not recommended!");
+ warn!("NOTE that Signer will not ask you to confirm transactions from unlocked account.");
+ }
+
// Secret Store
let account_service = Arc::new(conf.account_service());
diff --git a/parity/rpc_apis.rs b/parity/rpc_apis.rs
index 22520f266..c0daaa926 100644
--- a/parity/rpc_apis.rs
+++ b/parity/rpc_apis.rs
@@ -150,7 +150,7 @@ pub fn setup_rpc(server: T, deps: Arc, apis: ApiSet
server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate());
if deps.signer_port.is_some() {
- server.add_delegate(EthSigningQueueClient::new(&deps.signer_queue, &deps.client, &deps.miner).to_delegate());
+ server.add_delegate(EthSigningQueueClient::new(&deps.signer_queue, &deps.client, &deps.miner, &deps.secret_store).to_delegate());
} else {
server.add_delegate(EthSigningUnsafeClient::new(&deps.client, &deps.secret_store, &deps.miner).to_delegate());
}
diff --git a/rpc/src/v1/impls/eth_signing.rs b/rpc/src/v1/impls/eth_signing.rs
index c5103fd2d..700c679d6 100644
--- a/rpc/src/v1/impls/eth_signing.rs
+++ b/rpc/src/v1/impls/eth_signing.rs
@@ -43,15 +43,17 @@ fn fill_optional_fields(request: &mut TransactionRequest, client: &C, mine
/// Implementation of functions that require signing when no trusted signer is used.
pub struct EthSigningQueueClient where C: MiningBlockChainClient, M: MinerService {
queue: Weak,
+ accounts: Weak,
client: Weak,
miner: Weak,
}
impl EthSigningQueueClient where C: MiningBlockChainClient, M: MinerService {
/// Creates a new signing queue client given shared signing queue.
- pub fn new(queue: &Arc, client: &Arc, miner: &Arc) -> Self {
+ pub fn new(queue: &Arc, client: &Arc, miner: &Arc, accounts: &Arc) -> Self {
EthSigningQueueClient {
queue: Arc::downgrade(queue),
+ accounts: Arc::downgrade(accounts),
client: Arc::downgrade(client),
miner: Arc::downgrade(miner),
}
@@ -71,9 +73,18 @@ impl EthSigning for EthSigningQueueClient
fn send_transaction(&self, params: Params) -> Result {
from_params::<(TransactionRequest, )>(params)
.and_then(|(mut request, )| {
- let queue = take_weak!(self.queue);
+ 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 match sign_and_dispatch(&*client, &*miner, request, &*accounts, sender) {
+ Ok(hash) => to_value(&hash),
+ _ => to_value(&H256::zero()),
+ }
+ }
+
+ let queue = take_weak!(self.queue);
fill_optional_fields(&mut request, &*client, &*miner);
let id = queue.add_request(request);
let result = id.wait_with_timeout();
diff --git a/rpc/src/v1/tests/mocked/eth_signing.rs b/rpc/src/v1/tests/mocked/eth_signing.rs
index a2755ce18..5f3e75d35 100644
--- a/rpc/src/v1/tests/mocked/eth_signing.rs
+++ b/rpc/src/v1/tests/mocked/eth_signing.rs
@@ -14,6 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
+use std::str::FromStr;
use std::sync::Arc;
use jsonrpc_core::IoHandler;
use v1::impls::EthSigningQueueClient;
@@ -21,12 +22,16 @@ use v1::traits::EthSigning;
use v1::helpers::{ConfirmationsQueue, SigningQueue};
use v1::tests::helpers::TestMinerService;
use util::{Address, FixedHash};
+use util::numbers::{Uint, U256};
+use ethcore::account_provider::AccountProvider;
use ethcore::client::TestBlockChainClient;
+use ethcore::transaction::{Transaction, Action};
struct EthSigningTester {
pub queue: Arc,
pub client: Arc,
pub miner: Arc,
+ pub accounts: Arc,
pub io: IoHandler,
}
@@ -35,13 +40,15 @@ impl Default for EthSigningTester {
let queue = Arc::new(ConfirmationsQueue::default());
let client = Arc::new(TestBlockChainClient::default());
let miner = Arc::new(TestMinerService::default());
+ let accounts = Arc::new(AccountProvider::transient_provider());
let io = IoHandler::new();
- io.add_delegate(EthSigningQueueClient::new(&queue, &client, &miner).to_delegate());
+ io.add_delegate(EthSigningQueueClient::new(&queue, &client, &miner, &accounts).to_delegate());
EthSigningTester {
queue: queue,
client: client,
miner: miner,
+ accounts: accounts,
io: io,
}
}
@@ -78,5 +85,41 @@ fn should_add_transaction_to_queue() {
// then
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
assert_eq!(tester.queue.requests().len(), 1);
-
+}
+
+#[test]
+fn should_dispatch_transaction_if_account_is_unlocked() {
+ // given
+ let tester = eth_signing();
+ let acc = tester.accounts.new_account("test").unwrap();
+ tester.accounts.unlock_account_permanently(acc, "test".into()).unwrap();
+
+ let t = Transaction {
+ nonce: U256::zero(),
+ gas_price: U256::from(0x9184e72a000u64),
+ gas: U256::from(0x76c0),
+ action: Action::Call(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
+ value: U256::from(0x9184e72au64),
+ data: vec![]
+ };
+ let signature = tester.accounts.sign(acc, t.hash()).unwrap();
+ let t = t.with_signature(signature);
+
+ // when
+ let request = r#"{
+ "jsonrpc": "2.0",
+ "method": "eth_sendTransaction",
+ "params": [{
+ "from": ""#.to_owned() + format!("0x{:?}", acc).as_ref() + r#"",
+ "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
+ "gas": "0x76c0",
+ "gasPrice": "0x9184e72a000",
+ "value": "0x9184e72a"
+ }],
+ "id": 1
+ }"#;
+ let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#;
+
+ // then
+ assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
}