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

Commit

Permalink
corrected TYPE_REGEX to disallow zero sized fixed length arrays, repl…
Browse files Browse the repository at this point in the history
…aced LinkedHashSet with IndexSet, added API spec to docs, fixed Type::Byte encoding branch
  • Loading branch information
seunlanlege committed Oct 29, 2018
1 parent 17dad47 commit 9fe6fee
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 30 deletions.
2 changes: 1 addition & 1 deletion util/EIP-712/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ failure = "0.1"
itertools = "0.7"
failure_derive = "0.1"
lazy_static = "1.1"
linked_hash_set = "0.1.3"
toolshed = "0.4"
regex = "1.0"
validator = "0.8"
validator_derive = "0.8"
lunarity-lexer = "0.1"
rustc-hex = "2.0"
indexmap = "1.0.2"
6 changes: 3 additions & 3 deletions util/EIP-712/src/eip712.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub(crate) type MessageTypes = HashMap<String, Vec<FieldType>>;

lazy_static! {
// match solidity identifier with the addition of '[(\d)*]*'
static ref TYPE_REGEX: Regex = Regex::new(r"^[a-zA-Z_$][a-zA-Z_$0-9]*(\[[0-9]*\])*$").unwrap();
static ref TYPE_REGEX: Regex = Regex::new(r"^[a-zA-Z_$][a-zA-Z_$0-9]*(\[([1-9]\d*)*\])*$").unwrap();
static ref IDENT_REGEX: Regex = Regex::new(r"^[a-zA-Z_$][a-zA-Z_$0-9]*$").unwrap();
}

Expand Down Expand Up @@ -78,8 +78,8 @@ mod tests {
use serde_json::from_str;

#[test]
fn test_ident_regex() {
let test_cases = vec!["unint bytes32", "Seun\\[]", "byte[]uint", "byte[7[]uint][]"];
fn test_regex() {
let test_cases = vec!["unint bytes32", "Seun\\[]", "byte[]uint", "byte[7[]uint][]", "Person[0]"];
for case in test_cases {
assert_eq!(TYPE_REGEX.is_match(case), false)
}
Expand Down
43 changes: 22 additions & 21 deletions util/EIP-712/src/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use keccak_hash::keccak;
use serde_json::Value;
use std::str::FromStr;
use itertools::Itertools;
use linked_hash_set::LinkedHashSet;
use indexmap::IndexSet;
use serde_json::to_value;
use parser::{Parser, Type};
use error::{Result, ErrorKind, serde_error};
Expand All @@ -30,6 +30,16 @@ use rustc_hex::FromHex;
use validator::Validate;
use std::collections::HashSet;


fn check_hex(string: &str) -> Result<()> {
if string.len() >= 2 && &string[..2] == "0x" {
return Ok(())
}

return Err(ErrorKind::HexParseError(
format!("Expected a 0x-prefixed string of even length, found {} length string", string.len()))
)?
}
/// given a type and HashMap<String, Vec<FieldType>>
/// returns a HashSet of dependent types of the given type
fn build_dependencies<'a>(message_type: &'a str, message_types: &'a MessageTypes) -> Option<(HashSet<&'a str>)>
Expand All @@ -38,11 +48,11 @@ fn build_dependencies<'a>(message_type: &'a str, message_types: &'a MessageTypes
return None;
}

let mut types = LinkedHashSet::new();
let mut types = IndexSet::new();
types.insert(message_type);
let mut deps = HashSet::new();

while let Some(item) = types.pop_back() {
while let Some(item) = types.pop() {
if let Some(fields) = message_types.get(item) {
deps.insert(item);

Expand Down Expand Up @@ -136,11 +146,9 @@ fn encode_data(

Type::Bytes => {
let string = value.as_str().ok_or_else(|| serde_error("string", field_name))?;
if string.len() < 2 {
return Err(ErrorKind::HexParseError(
format!("Expected a 0x-prefixed string of even length, found {} length string", string.len()))
)?
}

check_hex(&string)?;

let bytes = (&string[2..])
.from_hex::<Vec<u8>>()
.map_err(|err| ErrorKind::HexParseError(format!("{}", err)))?;
Expand All @@ -151,18 +159,13 @@ fn encode_data(

Type::Byte(_) => {
let string = value.as_str().ok_or_else(|| serde_error("string", field_name))?;
if string.len() < 2 {
return Err(ErrorKind::HexParseError(
format!("Expected a 0x-prefixed string of even length, found {} length string", string.len()))
)?
}

check_hex(&string)?;

let mut bytes = (&string[2..])
.from_hex::<Vec<u8>>()
.map_err(|err| ErrorKind::HexParseError(format!("{}", err)))?;

if *message_type == Type::Bytes {
bytes = keccak(&bytes).to_vec();
}
encode(&[EthAbiToken::FixedBytes(bytes)])
}

Expand All @@ -185,11 +188,9 @@ fn encode_data(

Type::Uint | Type::Int => {
let string = value.as_str().ok_or_else(|| serde_error("int/uint", field_name))?;
if string.len() < 2 {
return Err(ErrorKind::HexParseError(
format!("Expected a 0x-prefixed string of even length, found {} length string", string.len()))
)?
}

check_hex(&string)?;

let uint = U256::from_str(&string[2..]).map_err(|err| ErrorKind::HexParseError(format!("{}", err)))?;

let token = if *message_type == Type::Uint {
Expand Down
5 changes: 2 additions & 3 deletions util/EIP-712/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,8 @@ impl From<ValidationErrors> for Error {
string.push_str(&str_error);
}
},
// #[validate] is only used on fields for regex
// its impossible to get any other errorkind
_ => unreachable!()
_ => unreachable!("#[validate] is only used on fields for regex;\
its impossible to get any other ErrorKind; qed")
}
}
ErrorKind::ValidationError(string).into()
Expand Down
139 changes: 137 additions & 2 deletions util/EIP-712/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,140 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.

//! EIP-712 encoding utilities
//! # Specification
//`encode(domainSeparator : 𝔹²⁵⁶, message : 𝕊) = "\x19\x01" ‖ domainSeparator ‖ hashStruct(message)`
//- data adheres to 𝕊, a structure defined in the rigorous eip-712
//- `\x01` is needed to comply with EIP-191
//- `domainSeparator` and `hashStruct` are defined below
//
//## A) domainSeparator
//`domainSeparator = hashStruct(eip712Domain)`
//<br/>
//<br/>
//Struct named `EIP712Domain` with the following fields
//
//- `name: String`
//- `version: String`
//- `chain_id: U256`,
//- `verifying_contract: H160`
//- `salt: Option<H256>`
//
//## C) hashStruct
//`hashStruct(s : 𝕊) = keccak256(typeHash ‖ encodeData(s))`
//<br/>
//`typeHash = keccak256(encodeType(typeOf(s)))`
//
//### i) encodeType
//- `name ‖ "(" ‖ member₁ ‖ "," ‖ member₂ ‖ "," ‖ … ‖ memberₙ ")"`
//- each member is written as `type ‖ " " ‖ name`
//- encodings cascade down and are sorted by name
//
//### ii) encodeData
//- `enc(value₁) ‖ enc(value₂) ‖ … ‖ enc(valueₙ)`
//- each encoded member is 32-byte long
//
// #### a) atomic
//
// - `boolean` => `U256`
// - `address` => `H160`
// - `uint` => sign-extended `U256` in big endian order
// - `bytes1:31` => `H@256`
//
// #### b) dynamic
//
// - `bytes` => `keccak256(bytes)`
// - `string` => `keccak256(string)`
//
// #### c) referenced
//
// - `array` => `keccak256(encodeData(array))`
// - `struct` => `rec(keccak256(hashStruct(struct)))`
//
//## D) Example
//### Query
//```json
//{
// "jsonrpc": "2.0",
// "method": "eth_signTypedData",
// "params": [
// "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
// {
// "types": {
// "EIP712Domain": [
// {
// "name": "name",
// "type": "string"
// },
// {
// "name": "version",
// "type": "string"
// },
// {
// "name": "chainId",
// "type": "uint256"
// },
// {
// "name": "verifyingContract",
// "type": "address"
// }
// ],
// "Person": [
// {
// "name": "name",
// "type": "string"
// },
// {
// "name": "wallet",
// "type": "address"
// }
// ],
// "Mail": [
// {
// "name": "from",
// "type": "Person"
// },
// {
// "name": "to",
// "type": "Person"
// },
// {
// "name": "contents",
// "type": "string"
// }
// ]
// },
// "primaryType": "Mail",
// "domain": {
// "name": "Ether Mail",
// "version": "1",
// "chainId": 1,
// "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
// },
// "message": {
// "from": {
// "name": "Cow",
// "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
// },
// "to": {
// "name": "Bob",
// "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
// },
// "contents": "Hello, Bob!"
// }
// }
// ],
// "id": 1
//}
//```
//
//### Response
//```json
//{
// "id":1,
// "jsonrpc": "2.0",
// "result": "0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c"
//}
//```
#![warn(missing_docs, unused_extern_crates)]

extern crate serde_json;
Expand All @@ -23,7 +157,7 @@ extern crate ethereum_types;
extern crate keccak_hash;
extern crate itertools;
extern crate failure;
extern crate linked_hash_set;
extern crate indexmap;
extern crate lunarity_lexer;
extern crate toolshed;
extern crate regex;
Expand All @@ -42,9 +176,10 @@ mod eip712;
mod error;
mod parser;
mod encode;

/// the EIP-712 encoding function
pub use encode::hash_structured_data;
/// encoding Error types
pub use error::{ErrorKind, Error};
/// EIP712 struct
pub use eip712::{EIP712};
pub use eip712::EIP712;

0 comments on commit 9fe6fee

Please sign in to comment.