only add transactions to signing-queue if it is enabled (#11272)
* only add transactions to signing-queue if it is enabled * Update rpc/src/v1/helpers/errors.rs Co-Authored-By: David <dvdplm@gmail.com> * use errors::codes::ACCOUNT_LOCKED * bail early if account isn't unlocked * use errors::signing * Update rpc/src/v1/helpers/errors.rs * Update rpc/src/v1/helpers/errors.rs * test * adds cli flag to enable signing queue. * use helper method siginig_queue_disabled instead of accounts::SignError * fix typo, use raw i64 * fixed tests
This commit is contained in:
parent
f6c3d4c695
commit
80a83c95d3
@ -348,6 +348,10 @@ usage! {
|
|||||||
"--unlock=[ACCOUNTS]",
|
"--unlock=[ACCOUNTS]",
|
||||||
"Unlock ACCOUNTS for the duration of the execution. ACCOUNTS is a comma-delimited list of addresses.",
|
"Unlock ACCOUNTS for the duration of the execution. ACCOUNTS is a comma-delimited list of addresses.",
|
||||||
|
|
||||||
|
ARG arg_enable_signing_queue: (bool) = false, or |c: &Config| c.account.as_ref()?.enable_signing_queue,
|
||||||
|
"--enable-signing-queue=[BOOLEAN]",
|
||||||
|
"Enables the signing queue for external transaction signing either via CLI or personal_unlockAccount, turned off by default.",
|
||||||
|
|
||||||
ARG arg_password: (Vec<String>) = Vec::new(), or |c: &Config| c.account.as_ref()?.password.clone(),
|
ARG arg_password: (Vec<String>) = Vec::new(), or |c: &Config| c.account.as_ref()?.password.clone(),
|
||||||
"--password=[FILE]...",
|
"--password=[FILE]...",
|
||||||
"Provide a file containing a password for unlocking an account. Leading and trailing whitespace is trimmed.",
|
"Provide a file containing a password for unlocking an account. Leading and trailing whitespace is trimmed.",
|
||||||
@ -1194,6 +1198,7 @@ struct Operating {
|
|||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
struct Account {
|
struct Account {
|
||||||
unlock: Option<Vec<String>>,
|
unlock: Option<Vec<String>>,
|
||||||
|
enable_signing_queue: Option<bool>,
|
||||||
password: Option<Vec<String>>,
|
password: Option<Vec<String>>,
|
||||||
keys_iterations: Option<u32>,
|
keys_iterations: Option<u32>,
|
||||||
refresh_time: Option<u64>,
|
refresh_time: Option<u64>,
|
||||||
@ -1728,6 +1733,7 @@ mod tests {
|
|||||||
arg_restore_file: None,
|
arg_restore_file: None,
|
||||||
arg_tools_hash_file: None,
|
arg_tools_hash_file: None,
|
||||||
|
|
||||||
|
arg_enable_signing_queue: false,
|
||||||
arg_signer_sign_id: None,
|
arg_signer_sign_id: None,
|
||||||
arg_signer_reject_id: None,
|
arg_signer_reject_id: None,
|
||||||
arg_dapp_path: None,
|
arg_dapp_path: None,
|
||||||
@ -2045,6 +2051,7 @@ mod tests {
|
|||||||
_legacy_public_node: None,
|
_legacy_public_node: None,
|
||||||
}),
|
}),
|
||||||
account: Some(Account {
|
account: Some(Account {
|
||||||
|
enable_signing_queue: None,
|
||||||
unlock: Some(vec!["0x1".into(), "0x2".into(), "0x3".into()]),
|
unlock: Some(vec!["0x1".into(), "0x2".into(), "0x3".into()]),
|
||||||
password: Some(vec!["passwdfile path".into()]),
|
password: Some(vec!["passwdfile path".into()]),
|
||||||
keys_iterations: None,
|
keys_iterations: None,
|
||||||
|
@ -898,7 +898,7 @@ impl Configuration {
|
|||||||
fn ws_config(&self) -> Result<WsConfiguration, String> {
|
fn ws_config(&self) -> Result<WsConfiguration, String> {
|
||||||
let support_token_api =
|
let support_token_api =
|
||||||
// enabled when not unlocking
|
// enabled when not unlocking
|
||||||
self.args.arg_unlock.is_none();
|
self.args.arg_unlock.is_none() && self.args.arg_enable_signing_queue;
|
||||||
|
|
||||||
let conf = WsConfiguration {
|
let conf = WsConfiguration {
|
||||||
enabled: self.ws_enabled(),
|
enabled: self.ws_enabled(),
|
||||||
@ -1388,7 +1388,7 @@ mod tests {
|
|||||||
origins: Some(vec!["parity://*".into(),"chrome-extension://*".into(), "moz-extension://*".into()]),
|
origins: Some(vec!["parity://*".into(),"chrome-extension://*".into(), "moz-extension://*".into()]),
|
||||||
hosts: Some(vec![]),
|
hosts: Some(vec![]),
|
||||||
signer_path: expected.into(),
|
signer_path: expected.into(),
|
||||||
support_token_api: true,
|
support_token_api: false,
|
||||||
max_connections: 100,
|
max_connections: 100,
|
||||||
}, LogConfig {
|
}, LogConfig {
|
||||||
color: !cfg!(windows),
|
color: !cfg!(windows),
|
||||||
|
@ -119,7 +119,7 @@ impl Default for WsConfiguration {
|
|||||||
origins: Some(vec!["parity://*".into(),"chrome-extension://*".into(), "moz-extension://*".into()]),
|
origins: Some(vec!["parity://*".into(),"chrome-extension://*".into(), "moz-extension://*".into()]),
|
||||||
hosts: Some(Vec::new()),
|
hosts: Some(Vec::new()),
|
||||||
signer_path: replace_home(&data_dir, "$BASE/signer").into(),
|
signer_path: replace_home(&data_dir, "$BASE/signer").into(),
|
||||||
support_token_api: true,
|
support_token_api: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -380,6 +380,16 @@ pub fn invalid_call_data<T: fmt::Display>(error: T) -> Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn signing_queue_disabled() -> Error {
|
||||||
|
Error {
|
||||||
|
code: ErrorCode::ServerError(-32020),
|
||||||
|
message: "Your account is locked and the signing queue is disabled. \
|
||||||
|
You can either Unlock the account via CLI, personal_unlockAccount or \
|
||||||
|
enable the signing queue to use Trusted Signer.".into(),
|
||||||
|
data: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(any(test, feature = "accounts"))]
|
#[cfg(any(test, feature = "accounts"))]
|
||||||
pub fn signing(error: ::accounts::SignError) -> Error {
|
pub fn signing(error: ::accounts::SignError) -> Error {
|
||||||
Error {
|
Error {
|
||||||
|
@ -114,6 +114,12 @@ impl<D: Dispatcher + 'static> SigningQueueClient<D> {
|
|||||||
|
|
||||||
fn dispatch(&self, payload: RpcConfirmationPayload, origin: Origin) -> BoxFuture<DispatchResult> {
|
fn dispatch(&self, payload: RpcConfirmationPayload, origin: Origin) -> BoxFuture<DispatchResult> {
|
||||||
let default_account = self.accounts.default_account();
|
let default_account = self.accounts.default_account();
|
||||||
|
let from = &payload.sender().unwrap_or(&default_account);
|
||||||
|
// bail early if the account isn't unlocked
|
||||||
|
if !self.accounts.is_unlocked(from) && !self.signer.is_enabled() {
|
||||||
|
return Box::new(future::done(Err(errors::signing_queue_disabled())))
|
||||||
|
}
|
||||||
|
|
||||||
let accounts = self.accounts.clone();
|
let accounts = self.accounts.clone();
|
||||||
let dispatcher = self.dispatcher.clone();
|
let dispatcher = self.dispatcher.clone();
|
||||||
let signer = self.signer.clone();
|
let signer = self.signer.clone();
|
||||||
@ -127,7 +133,9 @@ impl<D: Dispatcher + 'static> SigningQueueClient<D> {
|
|||||||
} else {
|
} else {
|
||||||
Either::B(future::done(
|
Either::B(future::done(
|
||||||
signer.add_request(payload, origin)
|
signer.add_request(payload, origin)
|
||||||
.map(|(id, future)| DispatchResult::Future(id, future))
|
.map(|(id, future)| {
|
||||||
|
DispatchResult::Future(id, future)
|
||||||
|
})
|
||||||
.map_err(|_| errors::request_rejected_limit())
|
.map_err(|_| errors::request_rejected_limit())
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -50,10 +50,10 @@ struct SigningTester {
|
|||||||
pub io: IoHandler<Metadata>,
|
pub io: IoHandler<Metadata>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SigningTester {
|
impl SigningTester {
|
||||||
fn default() -> Self {
|
fn new(signing_queue_enabled: bool) ->Self {
|
||||||
let runtime = Runtime::with_thread_count(1);
|
let runtime = Runtime::with_thread_count(1);
|
||||||
let signer = Arc::new(SignerService::new_test(false));
|
let signer = Arc::new(SignerService::new_test(signing_queue_enabled));
|
||||||
let client = Arc::new(TestBlockChainClient::default());
|
let client = Arc::new(TestBlockChainClient::default());
|
||||||
let miner = Arc::new(TestMinerService::default());
|
let miner = Arc::new(TestMinerService::default());
|
||||||
let accounts = Arc::new(AccountProvider::transient_provider());
|
let accounts = Arc::new(AccountProvider::transient_provider());
|
||||||
@ -81,15 +81,15 @@ impl Default for SigningTester {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eth_signing() -> SigningTester {
|
fn eth_signing(signing_queue_enabled: bool) -> SigningTester {
|
||||||
SigningTester::default()
|
SigningTester::new(signing_queue_enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rpc_eth_sign() {
|
fn rpc_eth_sign() {
|
||||||
use rustc_hex::FromHex;
|
use rustc_hex::FromHex;
|
||||||
|
|
||||||
let tester = eth_signing();
|
let tester = eth_signing(true);
|
||||||
|
|
||||||
let account = tester.accounts.insert_account(Secret::from([69u8; 32]), &"abcd".into()).unwrap();
|
let account = tester.accounts.insert_account(Secret::from([69u8; 32]), &"abcd".into()).unwrap();
|
||||||
tester.accounts.unlock_account_permanently(account, "abcd".into()).unwrap();
|
tester.accounts.unlock_account_permanently(account, "abcd".into()).unwrap();
|
||||||
@ -112,7 +112,7 @@ fn rpc_eth_sign() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_add_sign_to_queue() {
|
fn should_add_sign_to_queue() {
|
||||||
// given
|
// given
|
||||||
let tester = eth_signing();
|
let tester = eth_signing(true);
|
||||||
let address = Address::random();
|
let address = Address::random();
|
||||||
assert_eq!(tester.signer.requests().len(), 0);
|
assert_eq!(tester.signer.requests().len(), 0);
|
||||||
|
|
||||||
@ -150,7 +150,7 @@ fn should_add_sign_to_queue() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_post_sign_to_queue() {
|
fn should_post_sign_to_queue() {
|
||||||
// given
|
// given
|
||||||
let tester = eth_signing();
|
let tester = eth_signing(true);
|
||||||
let address = Address::random();
|
let address = Address::random();
|
||||||
assert_eq!(tester.signer.requests().len(), 0);
|
assert_eq!(tester.signer.requests().len(), 0);
|
||||||
|
|
||||||
@ -174,7 +174,7 @@ fn should_post_sign_to_queue() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_check_status_of_request() {
|
fn should_check_status_of_request() {
|
||||||
// given
|
// given
|
||||||
let tester = eth_signing();
|
let tester = eth_signing(true);
|
||||||
let address = Address::random();
|
let address = Address::random();
|
||||||
let request = r#"{
|
let request = r#"{
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
@ -203,7 +203,7 @@ fn should_check_status_of_request() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_check_status_of_request_when_its_resolved() {
|
fn should_check_status_of_request_when_its_resolved() {
|
||||||
// given
|
// given
|
||||||
let tester = eth_signing();
|
let tester = eth_signing(true);
|
||||||
let address = Address::random();
|
let address = Address::random();
|
||||||
let request = r#"{
|
let request = r#"{
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
@ -234,10 +234,56 @@ fn should_check_status_of_request_when_its_resolved() {
|
|||||||
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 eth_sign_locked_account() {
|
||||||
|
let secret = "8a283037bb19c4fed7b1c569e40c7dcff366165eb869110a1b11532963eb9cb2".parse().unwrap();
|
||||||
|
let tester = eth_signing(false);
|
||||||
|
let address = tester.accounts.insert_account(secret, &"".into()).unwrap();
|
||||||
|
|
||||||
|
let req_send_trans = r#"{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "eth_sendTransaction",
|
||||||
|
"params": [{
|
||||||
|
"from": ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"",
|
||||||
|
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
|
||||||
|
"gas": "0x30000",
|
||||||
|
"gasPrice": "0x1",
|
||||||
|
"value": "0x9184e72a"
|
||||||
|
}],
|
||||||
|
"id": 16
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
// expected error response
|
||||||
|
let error_res = r#"{
|
||||||
|
"jsonrpc":"2.0",
|
||||||
|
"error":
|
||||||
|
{
|
||||||
|
"code":-32020,
|
||||||
|
"message":"Your account is locked and the signing queue is disabled.
|
||||||
|
You can either Unlock the account via CLI, personal_unlockAccount or
|
||||||
|
enable the signing queue to use Trusted Signer."
|
||||||
|
},
|
||||||
|
"id":16
|
||||||
|
}"#;
|
||||||
|
// dispatch the transaction, without unlocking the account.
|
||||||
|
assert_eq!(
|
||||||
|
error_res.replace("\t", "").replace("\n", ""),
|
||||||
|
tester.io.handle_request_sync(&req_send_trans).unwrap()
|
||||||
|
);
|
||||||
|
// unlock the account
|
||||||
|
tester.accounts.unlock_account_permanently(address, "".into()).unwrap();
|
||||||
|
|
||||||
|
// try again, this time account is unlocked.
|
||||||
|
assert_eq!(
|
||||||
|
r#"{"jsonrpc":"2.0","result":"0x70df76392fba654351714803d99a90be1d58d165352e0008e376427284314c88","id":16}"#,
|
||||||
|
tester.io.handle_request_sync(&req_send_trans).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_sign_if_account_is_unlocked() {
|
fn should_sign_if_account_is_unlocked() {
|
||||||
// given
|
// given
|
||||||
let tester = eth_signing();
|
let tester = eth_signing(true);
|
||||||
let data = vec![5u8];
|
let data = vec![5u8];
|
||||||
let acc = tester.accounts.insert_account(Secret::from([69u8; 32]), &"test".into()).unwrap();
|
let acc = tester.accounts.insert_account(Secret::from([69u8; 32]), &"test".into()).unwrap();
|
||||||
tester.accounts.unlock_account_permanently(acc, "test".into()).unwrap();
|
tester.accounts.unlock_account_permanently(acc, "test".into()).unwrap();
|
||||||
@ -260,7 +306,7 @@ fn should_sign_if_account_is_unlocked() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_add_transaction_to_queue() {
|
fn should_add_transaction_to_queue() {
|
||||||
// given
|
// given
|
||||||
let tester = eth_signing();
|
let tester = eth_signing(true);
|
||||||
let address = Address::random();
|
let address = Address::random();
|
||||||
assert_eq!(tester.signer.requests().len(), 0);
|
assert_eq!(tester.signer.requests().len(), 0);
|
||||||
|
|
||||||
@ -301,7 +347,7 @@ fn should_add_transaction_to_queue() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_add_sign_transaction_to_the_queue() {
|
fn should_add_sign_transaction_to_the_queue() {
|
||||||
// given
|
// given
|
||||||
let tester = eth_signing();
|
let tester = eth_signing(true);
|
||||||
let address = tester.accounts.new_account(&"test".into()).unwrap();
|
let address = tester.accounts.new_account(&"test".into()).unwrap();
|
||||||
|
|
||||||
assert_eq!(tester.signer.requests().len(), 0);
|
assert_eq!(tester.signer.requests().len(), 0);
|
||||||
@ -380,7 +426,7 @@ fn should_add_sign_transaction_to_the_queue() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_dispatch_transaction_if_account_is_unlock() {
|
fn should_dispatch_transaction_if_account_is_unlock() {
|
||||||
// given
|
// given
|
||||||
let tester = eth_signing();
|
let tester = eth_signing(true);
|
||||||
let acc = tester.accounts.new_account(&"test".into()).unwrap();
|
let acc = tester.accounts.new_account(&"test".into()).unwrap();
|
||||||
tester.accounts.unlock_account_permanently(acc, "test".into()).unwrap();
|
tester.accounts.unlock_account_permanently(acc, "test".into()).unwrap();
|
||||||
|
|
||||||
@ -417,7 +463,7 @@ fn should_dispatch_transaction_if_account_is_unlock() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_decrypt_message_if_account_is_unlocked() {
|
fn should_decrypt_message_if_account_is_unlocked() {
|
||||||
// given
|
// given
|
||||||
let mut tester = eth_signing();
|
let mut tester = eth_signing(true);
|
||||||
let parity = parity::Dependencies::new();
|
let parity = parity::Dependencies::new();
|
||||||
tester.io.extend_with(parity.client(None).to_delegate());
|
tester.io.extend_with(parity.client(None).to_delegate());
|
||||||
let (address, public) = tester.accounts.new_account_and_public(&"test".into()).unwrap();
|
let (address, public) = tester.accounts.new_account_and_public(&"test".into()).unwrap();
|
||||||
@ -449,7 +495,7 @@ fn should_decrypt_message_if_account_is_unlocked() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_add_decryption_to_the_queue() {
|
fn should_add_decryption_to_the_queue() {
|
||||||
// given
|
// given
|
||||||
let tester = eth_signing();
|
let tester = eth_signing(true);
|
||||||
let acc = Random.generate().unwrap();
|
let acc = Random.generate().unwrap();
|
||||||
assert_eq!(tester.signer.requests().len(), 0);
|
assert_eq!(tester.signer.requests().len(), 0);
|
||||||
|
|
||||||
@ -486,7 +532,7 @@ fn should_add_decryption_to_the_queue() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_compose_transaction() {
|
fn should_compose_transaction() {
|
||||||
// given
|
// given
|
||||||
let tester = eth_signing();
|
let tester = eth_signing(true);
|
||||||
let acc = Random.generate().unwrap();
|
let acc = Random.generate().unwrap();
|
||||||
assert_eq!(tester.signer.requests().len(), 0);
|
assert_eq!(tester.signer.requests().len(), 0);
|
||||||
let from = format!("{:x}", acc.address());
|
let from = format!("{:x}", acc.address());
|
||||||
|
@ -229,6 +229,18 @@ impl From<helpers::ConfirmationPayload> for ConfirmationPayload {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ConfirmationPayload {
|
||||||
|
pub fn sender(&self) -> Option<&H160> {
|
||||||
|
match *self {
|
||||||
|
ConfirmationPayload::SendTransaction(ref request) => request.from.as_ref(),
|
||||||
|
ConfirmationPayload::SignTransaction(ref request) => request.from.as_ref(),
|
||||||
|
ConfirmationPayload::EthSignMessage(ref request) => Some(&request.address),
|
||||||
|
ConfirmationPayload::EIP191SignMessage(ref request) => Some(&request.address),
|
||||||
|
ConfirmationPayload::Decrypt(ref request) => Some(&request.address),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Possible modifications to the confirmed transaction sent by `Trusted Signer`
|
/// Possible modifications to the confirmed transaction sent by `Trusted Signer`
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
|
Loading…
Reference in New Issue
Block a user