Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

EIP 191 #9701

Merged
merged 6 commits into from
Nov 14, 2018
Merged

EIP 191 #9701

Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions rpc/src/v1/helpers/eip191.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2015-2018 Parity Technologies (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/>.

//! EIP-191 compliant decoding
use v1::types::{EIP191Version, Bytes, WithValidator};
use eip712::{hash_structured_data, EIP712};
use serde_json::{Value, from_value};
use v1::helpers::errors;
use jsonrpc_core::Error;
use v1::helpers::dispatch::eth_data_hash;
use hash::keccak;
use std::fmt::Display;
use ethereum_types::H256;

pub fn decode_message(version: EIP191Version, data: Value) -> Result<H256, Error> {
ordian marked this conversation as resolved.
Show resolved Hide resolved
let data = match version {
EIP191Version::StructuredData => {
let typed_data = from_value::<EIP712>(data)
.map_err(map_serde_err("StructuredData"))?;

hash_structured_data(typed_data)
.map_err(|err| errors::invalid_call_data(err.kind()))?
}

EIP191Version::WithValidator => {
let typed_data = from_value::<WithValidator>(data)
.map_err(map_serde_err("WithValidator"))?;
let prefix = b"\x19\x00";
let data = [&prefix[..], &typed_data.address.0[..], &typed_data.application_data.0[..]].concat();
keccak(data)
}

EIP191Version::PersonalMessage => {
let bytes = from_value::<Bytes>(data)
.map_err(map_serde_err("Bytes"))?;
eth_data_hash(bytes.0)
}
};

Ok(data)
}

fn map_serde_err<T: Display>(struct_name: &'static str) -> impl Fn(T) -> Error {
move |error: T| {
errors::invalid_call_data(format!("Error deserializing '{}': {}", struct_name, error))
}
}
1 change: 1 addition & 0 deletions rpc/src/v1/helpers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub mod light_fetch;
pub mod nonce;
pub mod oneshot;
pub mod secretstore;
pub mod eip191;

mod network_settings;
mod poll_filter;
Expand Down
32 changes: 29 additions & 3 deletions rpc/src/v1/impls/personal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use ethkey::{public_to_address, recover, Signature};

use jsonrpc_core::{BoxFuture, Result};
use jsonrpc_core::futures::{future, Future};
use v1::helpers::errors;
use v1::helpers::{errors, eip191};
use v1::helpers::dispatch::{self, eth_data_hash, Dispatcher, SignWith};
use v1::traits::Personal;
use v1::types::{
Expand All @@ -36,9 +36,11 @@ use v1::types::{
ConfirmationResponse as RpcConfirmationResponse,
TransactionRequest,
RichRawTransaction as RpcRichRawTransaction,
EIP191Version,
};
use v1::metadata::Metadata;
use eip712::{EIP712, hash_structured_data};
use jsonrpc_core::types::Value;

/// Account management (personal) rpc implementation.
pub struct PersonalClient<D: Dispatcher> {
Expand Down Expand Up @@ -151,15 +153,39 @@ impl<D: Dispatcher + 'static> Personal for PersonalClient<D> {
}))
}

fn sign_191(&self, version: EIP191Version, data: Value, account: RpcH160, password: String) -> BoxFuture<RpcH520> {
let data = match eip191::decode_message(version, data) {
Ok(d) => d,
Err(e) => return Box::new(future::err(e))
};
ordian marked this conversation as resolved.
Show resolved Hide resolved

let dispatcher = self.dispatcher.clone();
let accounts = self.accounts.clone();

let payload = RpcConfirmationPayload::EthSignMessage((account.clone(), RpcBytes(data.to_vec())).into());

Box::new(dispatch::from_rpc(payload, account.into(), &dispatcher)
.and_then(|payload| {
dispatch::execute(dispatcher, accounts, payload, dispatch::SignWith::Password(password.into()))
})
.map(|v| v.into_value())
.then(|res| match res {
Ok(RpcConfirmationResponse::Signature(signature)) => Ok(signature),
Err(e) => Err(e),
e => Err(errors::internal("Unexpected result", e)),
})
)
}

fn sign_typed_data(&self, typed_data: EIP712, account: RpcH160, password: String) -> BoxFuture<RpcH520> {
let data = match hash_structured_data(typed_data) {
Ok(d) => d,
Err(err) => return Box::new(future::done(Err(errors::invalid_call_data(err.kind())))),
Err(err) => return Box::new(future::err(errors::invalid_call_data(err.kind()))),
};
let dispatcher = self.dispatcher.clone();
let accounts = self.accounts.clone();

let payload = RpcConfirmationPayload::EthSignMessage((account.clone(), RpcBytes(data)).into());
let payload = RpcConfirmationPayload::EthSignMessage((account.clone(), RpcBytes(data.to_vec())).into());

Box::new(dispatch::from_rpc(payload, account.into(), &dispatcher)
.and_then(|payload| {
Expand Down
9 changes: 7 additions & 2 deletions rpc/src/v1/traits/personal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.

//! Personal rpc interface.
use jsonrpc_core::{BoxFuture, Result};
use eip712::EIP712;
use v1::types::{Bytes, U128, H160, H256, H520, TransactionRequest, RichRawTransaction as RpcRichRawTransaction};
use jsonrpc_core::types::Value;
use jsonrpc_core::{BoxFuture, Result};
use v1::types::{Bytes, U128, H160, H256, H520, TransactionRequest, RichRawTransaction as RpcRichRawTransaction, EIP191Version};

build_rpc_trait! {
/// Personal rpc interface. Safe (read-only) functions.
Expand Down Expand Up @@ -47,6 +48,10 @@ build_rpc_trait! {
#[rpc(name = "personal_signTypedData")]
fn sign_typed_data(&self, EIP712, H160, String) -> BoxFuture<H520>;

/// Signs an arbitrary message based on the version specified
#[rpc(name = "personal_sign191")]
fn sign_191(&self, EIP191Version, Value, H160, String) -> BoxFuture<H520>;

/// Returns the account associated with the private key that was used to calculate the signature in
/// `personal_sign`.
#[rpc(name = "personal_ecRecover")]
Expand Down
50 changes: 50 additions & 0 deletions rpc/src/v1/types/eip191.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2015-2018 Parity Technologies (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/>.

//! EIP-191 specific types
use serde::{Deserialize, Deserializer};
use serde::de;
use v1::types::{H160, Bytes};

pub enum EIP191Version {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please document all public types/fields/functions

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing docs for variants

StructuredData,
PersonalMessage,
WithValidator
}

#[derive(Deserialize)]
pub struct WithValidator {
// address of intended validator
pub address: H160,
// application specific data
pub application_data: Bytes
}

impl<'de> Deserialize<'de> for EIP191Version {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
let byte_version = match s.as_str() {
"0x00" | "0x0" | "0x" => EIP191Version::WithValidator,
"0x01" | "0x1" => EIP191Version::StructuredData,
"0x45" => EIP191Version::PersonalMessage,
other => return Err(de::Error::custom(format!("Invalid byte version '{}'", other))),
};
Ok(byte_version)
}
}
3 changes: 2 additions & 1 deletion rpc/src/v1/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,10 @@ mod transaction_condition;
mod uint;
mod work;
mod private_receipt;
mod eip191;

pub mod pubsub;

pub use self::eip191::{EIP191Version, WithValidator};
pub use self::account_info::{AccountInfo, ExtAccountInfo, HwAccountInfo};
pub use self::bytes::Bytes;
pub use self::block::{RichBlock, Block, BlockTransactions, Header, RichHeader, Rich};
Expand Down
4 changes: 2 additions & 2 deletions util/EIP-712/src/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ fn encode_data(
}

/// encodes and hashes the given EIP712 struct
pub fn hash_structured_data(typed_data: EIP712) -> Result<Vec<u8>> {
pub fn hash_structured_data(typed_data: EIP712) -> Result<H256> {
// validate input
typed_data.validate()?;
// EIP-191 compliant
Expand All @@ -220,7 +220,7 @@ pub fn hash_structured_data(typed_data: EIP712) -> Result<Vec<u8>> {
encode_data(&parser, &Type::Custom(typed_data.primary_type), &typed_data.types, &typed_data.message, None)?
);
let concat = [&prefix[..], &domain_hash[..], &data_hash[..]].concat();
Ok(keccak(concat).to_vec())
Ok(keccak(concat))
}

#[cfg(test)]
Expand Down