Add raw hash signing (#5423)
* add sign any * Add RPC signMessage call to JS API * Add signMessage to JSON RPC docs * PostSignTransaction -> EthSignMessage * fix doc typo * revert incorect naming
This commit is contained in:
parent
daf1495c4e
commit
52eae66c72
@ -522,6 +522,11 @@ export default class Parity {
|
|||||||
.then(outNumber);
|
.then(outNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signMessage (address, password, messageHash) {
|
||||||
|
return this._transport
|
||||||
|
.execute('parity_signMessage', inAddress(address), password, inHex(messageHash));
|
||||||
|
}
|
||||||
|
|
||||||
testPassword (account, password) {
|
testPassword (account, password) {
|
||||||
return this._transport
|
return this._transport
|
||||||
.execute('parity_testPassword', inAddress(account), password);
|
.execute('parity_testPassword', inAddress(account), password);
|
||||||
|
@ -1881,5 +1881,31 @@ export default {
|
|||||||
desc: 'Decrypted message.',
|
desc: 'Decrypted message.',
|
||||||
example: withComment('0x68656c6c6f20776f726c64', 'hello world')
|
example: withComment('0x68656c6c6f20776f726c64', 'hello world')
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
signMessage: {
|
||||||
|
desc: 'Sign the hashed message bytes with the given account.',
|
||||||
|
params: [
|
||||||
|
{
|
||||||
|
type: Address,
|
||||||
|
desc: 'Account which signs the message.',
|
||||||
|
example: '0xc171033d5cbff7175f29dfd3a63dda3d6f8f385e'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: String,
|
||||||
|
desc: 'Passphrase to unlock the account.',
|
||||||
|
example: 'password1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: Data,
|
||||||
|
desc: 'Hashed message.',
|
||||||
|
example: '0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: {
|
||||||
|
type: Data,
|
||||||
|
desc: 'Message signature.',
|
||||||
|
example: '0x1d9e33a8cf8bfc089a172bca01da462f9e359c6cb1b0f29398bc884e4d18df4f78588aee4fb5cc067ca62d2abab995e0bba29527be6ac98105b0320020a2efaf00'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -474,7 +474,7 @@ pub fn execute<D: Dispatcher + 'static>(
|
|||||||
.map(ConfirmationResponse::SignTransaction)
|
.map(ConfirmationResponse::SignTransaction)
|
||||||
).boxed()
|
).boxed()
|
||||||
},
|
},
|
||||||
ConfirmationPayload::Signature(address, mut data) => {
|
ConfirmationPayload::EthSignMessage(address, mut data) => {
|
||||||
let mut message_data =
|
let mut message_data =
|
||||||
format!("\x19Ethereum Signed Message:\n{}", data.len())
|
format!("\x19Ethereum Signed Message:\n{}", data.len())
|
||||||
.into_bytes();
|
.into_bytes();
|
||||||
@ -574,8 +574,8 @@ pub fn from_rpc<D>(payload: RpcConfirmationPayload, default_account: Address, di
|
|||||||
RpcConfirmationPayload::Decrypt(RpcDecryptRequest { address, msg }) => {
|
RpcConfirmationPayload::Decrypt(RpcDecryptRequest { address, msg }) => {
|
||||||
future::ok(ConfirmationPayload::Decrypt(address.into(), msg.into())).boxed()
|
future::ok(ConfirmationPayload::Decrypt(address.into(), msg.into())).boxed()
|
||||||
},
|
},
|
||||||
RpcConfirmationPayload::Signature(RpcSignRequest { address, data }) => {
|
RpcConfirmationPayload::EthSignMessage(RpcSignRequest { address, data }) => {
|
||||||
future::ok(ConfirmationPayload::Signature(address.into(), data.into())).boxed()
|
future::ok(ConfirmationPayload::EthSignMessage(address.into(), data.into())).boxed()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,8 +113,8 @@ pub enum ConfirmationPayload {
|
|||||||
SendTransaction(FilledTransactionRequest),
|
SendTransaction(FilledTransactionRequest),
|
||||||
/// Sign Transaction
|
/// Sign Transaction
|
||||||
SignTransaction(FilledTransactionRequest),
|
SignTransaction(FilledTransactionRequest),
|
||||||
/// Sign request
|
/// Sign a message with an Ethereum specific security prefix.
|
||||||
Signature(Address, Bytes),
|
EthSignMessage(Address, Bytes),
|
||||||
/// Decrypt request
|
/// Decrypt request
|
||||||
Decrypt(Address, Bytes),
|
Decrypt(Address, Bytes),
|
||||||
}
|
}
|
||||||
@ -124,7 +124,7 @@ impl ConfirmationPayload {
|
|||||||
match *self {
|
match *self {
|
||||||
ConfirmationPayload::SendTransaction(ref request) => request.from,
|
ConfirmationPayload::SendTransaction(ref request) => request.from,
|
||||||
ConfirmationPayload::SignTransaction(ref request) => request.from,
|
ConfirmationPayload::SignTransaction(ref request) => request.from,
|
||||||
ConfirmationPayload::Signature(ref address, _) => *address,
|
ConfirmationPayload::EthSignMessage(ref address, _) => *address,
|
||||||
ConfirmationPayload::Decrypt(ref address, _) => *address,
|
ConfirmationPayload::Decrypt(ref address, _) => *address,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
//! Account management (personal) rpc implementation
|
//! Account management (personal) rpc implementation
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use util::{Address};
|
use util::Address;
|
||||||
|
|
||||||
use ethkey::{Brain, Generator, Secret};
|
use ethkey::{Brain, Generator, Secret};
|
||||||
use ethstore::KeyFile;
|
use ethstore::KeyFile;
|
||||||
@ -27,7 +27,7 @@ use jsonrpc_core::Error;
|
|||||||
use v1::helpers::errors;
|
use v1::helpers::errors;
|
||||||
use v1::helpers::accounts::unwrap_provider;
|
use v1::helpers::accounts::unwrap_provider;
|
||||||
use v1::traits::ParityAccounts;
|
use v1::traits::ParityAccounts;
|
||||||
use v1::types::{H160 as RpcH160, H256 as RpcH256, DappId, Derive, DeriveHierarchical, DeriveHash};
|
use v1::types::{H160 as RpcH160, H256 as RpcH256, H520 as RpcH520, DappId, Derive, DeriveHierarchical, DeriveHash};
|
||||||
|
|
||||||
/// Account management (personal) rpc implementation.
|
/// Account management (personal) rpc implementation.
|
||||||
pub struct ParityAccountsClient {
|
pub struct ParityAccountsClient {
|
||||||
@ -334,6 +334,17 @@ impl ParityAccounts for ParityAccountsClient {
|
|||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
.map_err(|e| errors::account("Could not export account.", e))
|
.map_err(|e| errors::account("Could not export account.", e))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sign_message(&self, addr: RpcH160, password: String, message: RpcH256) -> Result<RpcH520, Error> {
|
||||||
|
self.account_provider()?
|
||||||
|
.sign(
|
||||||
|
addr.into(),
|
||||||
|
Some(password),
|
||||||
|
message.into()
|
||||||
|
)
|
||||||
|
.map(Into::into)
|
||||||
|
.map_err(|e| errors::account("Could not sign message.", e))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_vec<A, B>(a: Vec<A>) -> Vec<B> where
|
fn into_vec<A, B>(a: Vec<A>) -> Vec<B> where
|
||||||
|
@ -140,7 +140,7 @@ impl<D: Dispatcher + 'static> ParitySigning for SigningQueueClient<D> {
|
|||||||
fn post_sign(&self, meta: Metadata, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcEither<RpcU256, RpcConfirmationResponse>, Error> {
|
fn post_sign(&self, meta: Metadata, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcEither<RpcU256, RpcConfirmationResponse>, Error> {
|
||||||
let pending = self.pending.clone();
|
let pending = self.pending.clone();
|
||||||
self.dispatch(
|
self.dispatch(
|
||||||
RpcConfirmationPayload::Signature((address.clone(), data).into()),
|
RpcConfirmationPayload::EthSignMessage((address.clone(), data).into()),
|
||||||
DefaultAccount::Provided(address.into()),
|
DefaultAccount::Provided(address.into()),
|
||||||
meta.origin
|
meta.origin
|
||||||
).map(move |result| match result {
|
).map(move |result| match result {
|
||||||
@ -216,7 +216,7 @@ impl<D: Dispatcher + 'static> EthSigning for SigningQueueClient<D> {
|
|||||||
|
|
||||||
fn sign(&self, meta: Metadata, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcH520, Error> {
|
fn sign(&self, meta: Metadata, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcH520, Error> {
|
||||||
let res = self.dispatch(
|
let res = self.dispatch(
|
||||||
RpcConfirmationPayload::Signature((address.clone(), data).into()),
|
RpcConfirmationPayload::EthSignMessage((address.clone(), data).into()),
|
||||||
address.into(),
|
address.into(),
|
||||||
meta.origin,
|
meta.origin,
|
||||||
);
|
);
|
||||||
|
@ -78,7 +78,7 @@ impl<D: Dispatcher + 'static> EthSigning for SigningUnsafeClient<D>
|
|||||||
type Metadata = Metadata;
|
type Metadata = Metadata;
|
||||||
|
|
||||||
fn sign(&self, _: Metadata, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcH520, Error> {
|
fn sign(&self, _: Metadata, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcH520, Error> {
|
||||||
self.handle(RpcConfirmationPayload::Signature((address.clone(), data).into()), address.into())
|
self.handle(RpcConfirmationPayload::EthSignMessage((address.clone(), data).into()), address.into())
|
||||||
.then(|res| match res {
|
.then(|res| match res {
|
||||||
Ok(RpcConfirmationResponse::Signature(signature)) => Ok(signature),
|
Ok(RpcConfirmationResponse::Signature(signature)) => Ok(signature),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
|
@ -500,3 +500,20 @@ fn should_export_account() {
|
|||||||
println!("Response: {:?}", response);
|
println!("Response: {:?}", response);
|
||||||
assert_eq!(result, Some(response.into()));
|
assert_eq!(result, Some(response.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_sign_message() {
|
||||||
|
let tester = setup();
|
||||||
|
let hash = tester.accounts
|
||||||
|
.insert_account(
|
||||||
|
"0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a".parse().unwrap(),
|
||||||
|
"password1")
|
||||||
|
.expect("account should be inserted ok");
|
||||||
|
|
||||||
|
assert_eq!(hash, "c171033d5cbff7175f29dfd3a63dda3d6f8f385e".parse().unwrap());
|
||||||
|
|
||||||
|
let request = r#"{"jsonrpc": "2.0", "method": "parity_signMessage", "params": ["0xc171033d5cbff7175f29dfd3a63dda3d6f8f385e", "password1", "0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a"], "id": 3}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":"0x1d9e33a8cf8bfc089a172bca01da462f9e359c6cb1b0f29398bc884e4d18df4f78588aee4fb5cc067ca62d2abab995e0bba29527be6ac98105b0320020a2efaf00","id":3}"#;
|
||||||
|
let res = tester.io.handle_request_sync(&request);
|
||||||
|
assert_eq!(res, Some(response.into()));
|
||||||
|
}
|
||||||
|
@ -90,7 +90,7 @@ fn should_return_list_of_items_to_confirm() {
|
|||||||
nonce: None,
|
nonce: None,
|
||||||
condition: None,
|
condition: None,
|
||||||
}), Origin::Dapps("http://parity.io".into())).unwrap();
|
}), Origin::Dapps("http://parity.io".into())).unwrap();
|
||||||
tester.signer.add_request(ConfirmationPayload::Signature(1.into(), vec![5].into()), Origin::Unknown).unwrap();
|
tester.signer.add_request(ConfirmationPayload::EthSignMessage(1.into(), vec![5].into()), Origin::Unknown).unwrap();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let request = r#"{"jsonrpc":"2.0","method":"signer_requestsToConfirm","params":[],"id":1}"#;
|
let request = r#"{"jsonrpc":"2.0","method":"signer_requestsToConfirm","params":[],"id":1}"#;
|
||||||
@ -163,7 +163,7 @@ fn should_not_remove_transaction_if_password_is_invalid() {
|
|||||||
fn should_not_remove_sign_if_password_is_invalid() {
|
fn should_not_remove_sign_if_password_is_invalid() {
|
||||||
// given
|
// given
|
||||||
let tester = signer_tester();
|
let tester = signer_tester();
|
||||||
tester.signer.add_request(ConfirmationPayload::Signature(0.into(), vec![5].into()), Origin::Unknown).unwrap();
|
tester.signer.add_request(ConfirmationPayload::EthSignMessage(0.into(), vec![5].into()), Origin::Unknown).unwrap();
|
||||||
assert_eq!(tester.signer.requests().len(), 1);
|
assert_eq!(tester.signer.requests().len(), 1);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
|
@ -19,7 +19,7 @@ use std::collections::BTreeMap;
|
|||||||
|
|
||||||
use jsonrpc_core::Error;
|
use jsonrpc_core::Error;
|
||||||
use ethstore::KeyFile;
|
use ethstore::KeyFile;
|
||||||
use v1::types::{H160, H256, DappId, DeriveHash, DeriveHierarchical};
|
use v1::types::{H160, H256, H520, DappId, DeriveHash, DeriveHierarchical};
|
||||||
|
|
||||||
build_rpc_trait! {
|
build_rpc_trait! {
|
||||||
/// Personal Parity rpc interface.
|
/// Personal Parity rpc interface.
|
||||||
@ -180,5 +180,9 @@ build_rpc_trait! {
|
|||||||
/// Exports an account with given address if provided password matches.
|
/// Exports an account with given address if provided password matches.
|
||||||
#[rpc(name = "parity_exportAccount")]
|
#[rpc(name = "parity_exportAccount")]
|
||||||
fn export_account(&self, H160, String) -> Result<KeyFile, Error>;
|
fn export_account(&self, H160, String) -> Result<KeyFile, Error>;
|
||||||
|
|
||||||
|
/// Sign raw hash with the key corresponding to address and password.
|
||||||
|
#[rpc(name = "parity_signMessage")]
|
||||||
|
fn sign_message(&self, H160, String, H256) -> Result<H520, Error>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ impl fmt::Display for ConfirmationPayload {
|
|||||||
match *self {
|
match *self {
|
||||||
ConfirmationPayload::SendTransaction(ref transaction) => write!(f, "{}", transaction),
|
ConfirmationPayload::SendTransaction(ref transaction) => write!(f, "{}", transaction),
|
||||||
ConfirmationPayload::SignTransaction(ref transaction) => write!(f, "(Sign only) {}", transaction),
|
ConfirmationPayload::SignTransaction(ref transaction) => write!(f, "(Sign only) {}", transaction),
|
||||||
ConfirmationPayload::Signature(ref sign) => write!(f, "{}", sign),
|
ConfirmationPayload::EthSignMessage(ref sign) => write!(f, "{}", sign),
|
||||||
ConfirmationPayload::Decrypt(ref decrypt) => write!(f, "{}", decrypt),
|
ConfirmationPayload::Decrypt(ref decrypt) => write!(f, "{}", decrypt),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -169,7 +169,7 @@ pub enum ConfirmationPayload {
|
|||||||
SignTransaction(TransactionRequest),
|
SignTransaction(TransactionRequest),
|
||||||
/// Signature
|
/// Signature
|
||||||
#[serde(rename="sign")]
|
#[serde(rename="sign")]
|
||||||
Signature(SignRequest),
|
EthSignMessage(SignRequest),
|
||||||
/// Decryption
|
/// Decryption
|
||||||
#[serde(rename="decrypt")]
|
#[serde(rename="decrypt")]
|
||||||
Decrypt(DecryptRequest),
|
Decrypt(DecryptRequest),
|
||||||
@ -180,7 +180,7 @@ impl From<helpers::ConfirmationPayload> for ConfirmationPayload {
|
|||||||
match c {
|
match c {
|
||||||
helpers::ConfirmationPayload::SendTransaction(t) => ConfirmationPayload::SendTransaction(t.into()),
|
helpers::ConfirmationPayload::SendTransaction(t) => ConfirmationPayload::SendTransaction(t.into()),
|
||||||
helpers::ConfirmationPayload::SignTransaction(t) => ConfirmationPayload::SignTransaction(t.into()),
|
helpers::ConfirmationPayload::SignTransaction(t) => ConfirmationPayload::SignTransaction(t.into()),
|
||||||
helpers::ConfirmationPayload::Signature(address, data) => ConfirmationPayload::Signature(SignRequest {
|
helpers::ConfirmationPayload::EthSignMessage(address, data) => ConfirmationPayload::EthSignMessage(SignRequest {
|
||||||
address: address.into(),
|
address: address.into(),
|
||||||
data: data.into(),
|
data: data.into(),
|
||||||
}),
|
}),
|
||||||
@ -255,7 +255,7 @@ mod tests {
|
|||||||
// given
|
// given
|
||||||
let request = helpers::ConfirmationRequest {
|
let request = helpers::ConfirmationRequest {
|
||||||
id: 15.into(),
|
id: 15.into(),
|
||||||
payload: helpers::ConfirmationPayload::Signature(1.into(), vec![5].into()),
|
payload: helpers::ConfirmationPayload::EthSignMessage(1.into(), vec![5].into()),
|
||||||
origin: Origin::Rpc("test service".into()),
|
origin: Origin::Rpc("test service".into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user