Correct EIP-712 encoding (#11092)
* support encoding custom array types as fields * new line * removed expect * Update util/EIP-712/src/encode.rs Co-Authored-By: Andronik Ordian <write@reusable.software> * bump lunarity * update cargo lock * nits * nits
This commit is contained in:
parent
4979c62bb5
commit
8471b91002
42
Cargo.lock
generated
42
Cargo.lock
generated
@ -815,13 +815,12 @@ dependencies = [
|
|||||||
"itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"keccak-hash 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"keccak-hash 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lunarity-lexer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lunarity-lexer 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"toolshed 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"validator 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"validator 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"validator_derive 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"validator_derive 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
@ -2456,6 +2455,27 @@ dependencies = [
|
|||||||
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "logos"
|
||||||
|
version = "0.7.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"logos-derive 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"toolshed 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "logos-derive"
|
||||||
|
version = "0.7.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"utf8-ranges 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lru-cache"
|
name = "lru-cache"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@ -2466,10 +2486,10 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lunarity-lexer"
|
name = "lunarity-lexer"
|
||||||
version = "0.1.0"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"toolshed 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"logos 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4704,7 +4724,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toolshed"
|
name = "toolshed"
|
||||||
version = "0.4.0"
|
version = "0.6.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -4905,6 +4925,11 @@ dependencies = [
|
|||||||
name = "using_queue"
|
name = "using_queue"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8-ranges"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "validator"
|
name = "validator"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@ -5377,8 +5402,10 @@ dependencies = [
|
|||||||
"checksum lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed946d4529956a20f2d63ebe1b69996d5a2137c91913fe3ebbeff957f5bca7ff"
|
"checksum lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed946d4529956a20f2d63ebe1b69996d5a2137c91913fe3ebbeff957f5bca7ff"
|
||||||
"checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc"
|
"checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc"
|
||||||
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
|
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
|
||||||
|
"checksum logos 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)" = "60ca690691528b32832c7e8aaae8ae1edcdee4e9ffde55b2d31a4795bc7a12d0"
|
||||||
|
"checksum logos-derive 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)" = "917dccdd529d5681f3d28b26bcfdafd2ed67fe4f26d15b5ac679f67b55279f3d"
|
||||||
"checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
|
"checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
|
||||||
"checksum lunarity-lexer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a1670671f305792567116d4660e6e5bd785d6fa973e817c3445c0a7a54cecb6"
|
"checksum lunarity-lexer 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "28a5446c03ed5bd4ae2cca322c4c84d9bd9741b6788f75c404719474cb63d3b7"
|
||||||
"checksum malloc_size_of_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "35adee9ed962cf7d07d62cb58bc45029f3227f5b5b86246caa8632f06c187bc3"
|
"checksum malloc_size_of_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "35adee9ed962cf7d07d62cb58bc45029f3227f5b5b86246caa8632f06c187bc3"
|
||||||
"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
|
"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
|
||||||
"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
|
"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
|
||||||
@ -5559,7 +5586,7 @@ dependencies = [
|
|||||||
"checksum tokio-uds 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "22e3aa6d1fcc19e635418dc0a30ab5bd65d347973d6f43f1a37bf8d9d1335fc9"
|
"checksum tokio-uds 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "22e3aa6d1fcc19e635418dc0a30ab5bd65d347973d6f43f1a37bf8d9d1335fc9"
|
||||||
"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f"
|
"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f"
|
||||||
"checksum toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b8c96d7873fa7ef8bdeb3a9cda3ac48389b4154f32b9803b4bc26220b677b039"
|
"checksum toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b8c96d7873fa7ef8bdeb3a9cda3ac48389b4154f32b9803b4bc26220b677b039"
|
||||||
"checksum toolshed 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "450441e131c7663af72e63a33c02a6a1fbaaa8601dc652ed6757813bb55aeec7"
|
"checksum toolshed 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "54a272adbf14cfbb486774d09ee3e00c38d488cd390084a528f70e10e3a184a8"
|
||||||
"checksum trace-time 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe82f2f0bf1991e163e757baf044282823155dd326e70f44ce2186c3c320cc9"
|
"checksum trace-time 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe82f2f0bf1991e163e757baf044282823155dd326e70f44ce2186c3c320cc9"
|
||||||
"checksum transaction-pool 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "454adc482e32785c3beab9415dd0f3c689f29cc2d16717eb62f6a784d53544b4"
|
"checksum transaction-pool 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "454adc482e32785c3beab9415dd0f3c689f29cc2d16717eb62f6a784d53544b4"
|
||||||
"checksum transient-hashmap 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aeb4b191d033a35edfce392a38cdcf9790b6cebcb30fa690c312c29da4dc433e"
|
"checksum transient-hashmap 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aeb4b191d033a35edfce392a38cdcf9790b6cebcb30fa690c312c29da4dc433e"
|
||||||
@ -5580,6 +5607,7 @@ dependencies = [
|
|||||||
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
|
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
|
||||||
"checksum untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f"
|
"checksum untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f"
|
||||||
"checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6"
|
"checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6"
|
||||||
|
"checksum utf8-ranges 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba"
|
||||||
"checksum validator 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "236a5eda3df2c877872e98dbc55d497d943792e6405d8fc65bd4f8a5e3b53c99"
|
"checksum validator 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "236a5eda3df2c877872e98dbc55d497d943792e6405d8fc65bd4f8a5e3b53c99"
|
||||||
"checksum validator_derive 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d360d6f5754972c0c1da14fb3d5580daa31aee566e1e45e2f8d3bf5950ecd3e9"
|
"checksum validator_derive 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d360d6f5754972c0c1da14fb3d5580daa31aee566e1e45e2f8d3bf5950ecd3e9"
|
||||||
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
|
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
|
||||||
|
@ -20,10 +20,9 @@ ethereum-types = "0.6.0"
|
|||||||
failure = "0.1"
|
failure = "0.1"
|
||||||
itertools = "0.7"
|
itertools = "0.7"
|
||||||
lazy_static = "1.1"
|
lazy_static = "1.1"
|
||||||
toolshed = "0.4"
|
|
||||||
regex = "1.0"
|
regex = "1.0"
|
||||||
validator = "0.8"
|
validator = "0.8"
|
||||||
validator_derive = "0.8"
|
validator_derive = "0.8"
|
||||||
lunarity-lexer = "0.1"
|
lunarity-lexer = "0.2"
|
||||||
rustc-hex = "2.0"
|
rustc-hex = "2.0"
|
||||||
indexmap = "1.0.2"
|
indexmap = "1.0.2"
|
||||||
|
@ -23,7 +23,7 @@ use std::str::FromStr;
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use indexmap::IndexSet;
|
use indexmap::IndexSet;
|
||||||
use serde_json::to_value;
|
use serde_json::to_value;
|
||||||
use crate::parser::{Parser, Type};
|
use crate::parser::{parse_type, Type};
|
||||||
use crate::error::{Result, ErrorKind, serde_error};
|
use crate::error::{Result, ErrorKind, serde_error};
|
||||||
use crate::eip712::{EIP712, MessageTypes};
|
use crate::eip712::{EIP712, MessageTypes};
|
||||||
use rustc_hex::FromHex;
|
use rustc_hex::FromHex;
|
||||||
@ -56,11 +56,16 @@ fn build_dependencies<'a>(message_type: &'a str, message_types: &'a MessageTypes
|
|||||||
deps.insert(item);
|
deps.insert(item);
|
||||||
|
|
||||||
for field in fields {
|
for field in fields {
|
||||||
|
// check if this field is an array type
|
||||||
|
let field_type = if let Some(index) = field.type_.find('[') {
|
||||||
|
&field.type_[..index]
|
||||||
|
} else {
|
||||||
|
&field.type_
|
||||||
|
};
|
||||||
// seen this type before? or not a custom type skip
|
// seen this type before? or not a custom type skip
|
||||||
if deps.contains(&*field.type_) || !message_types.contains_key(&*field.type_) {
|
if !deps.contains(field_type) || message_types.contains_key(field_type) {
|
||||||
continue;
|
types.insert(field_type);
|
||||||
}
|
}
|
||||||
types.insert(&*field.type_);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -70,7 +75,8 @@ fn build_dependencies<'a>(message_type: &'a str, message_types: &'a MessageTypes
|
|||||||
|
|
||||||
fn encode_type(message_type: &str, message_types: &MessageTypes) -> Result<String> {
|
fn encode_type(message_type: &str, message_types: &MessageTypes) -> Result<String> {
|
||||||
let deps = {
|
let deps = {
|
||||||
let mut temp = build_dependencies(message_type, message_types).ok_or_else(|| ErrorKind::NonExistentType)?;
|
let mut temp = build_dependencies(message_type, message_types)
|
||||||
|
.ok_or(ErrorKind::NonExistentType)?;
|
||||||
temp.remove(message_type);
|
temp.remove(message_type);
|
||||||
let mut temp = temp.into_iter().collect::<Vec<_>>();
|
let mut temp = temp.into_iter().collect::<Vec<_>>();
|
||||||
(&mut temp[..]).sort_unstable();
|
(&mut temp[..]).sort_unstable();
|
||||||
@ -99,7 +105,6 @@ fn type_hash(message_type: &str, typed_data: &MessageTypes) -> Result<H256> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn encode_data(
|
fn encode_data(
|
||||||
parser: &Parser,
|
|
||||||
message_type: &Type,
|
message_type: &Type,
|
||||||
message_types: &MessageTypes,
|
message_types: &MessageTypes,
|
||||||
value: &Value,
|
value: &Value,
|
||||||
@ -112,17 +117,25 @@ fn encode_data(
|
|||||||
length
|
length
|
||||||
} => {
|
} => {
|
||||||
let mut items = vec![];
|
let mut items = vec![];
|
||||||
let values = value.as_array().ok_or_else(|| serde_error("array", field_name))?;
|
let values = value.as_array()
|
||||||
|
.ok_or(serde_error("array", field_name))?;
|
||||||
|
|
||||||
// check if the type definition actually matches
|
// check if the type definition actually matches
|
||||||
// the length of items to be encoded
|
// the length of items to be encoded
|
||||||
if length.is_some() && Some(values.len() as u64) != *length {
|
if length.is_some() && Some(values.len() as u64) != *length {
|
||||||
let array_type = format!("{}[{}]", *inner, length.unwrap());
|
let array_type = format!("{}[{}]", *inner, length.unwrap());
|
||||||
return Err(ErrorKind::UnequalArrayItems(length.unwrap(), array_type, values.len() as u64))?
|
return Err(
|
||||||
|
ErrorKind::UnequalArrayItems(length.unwrap(), array_type, values.len() as u64)
|
||||||
|
)?
|
||||||
}
|
}
|
||||||
|
|
||||||
for item in values {
|
for item in values {
|
||||||
let mut encoded = encode_data(parser, &*inner, &message_types, item, field_name)?;
|
let mut encoded = encode_data(
|
||||||
|
&*inner,
|
||||||
|
&message_types,
|
||||||
|
item,
|
||||||
|
field_name
|
||||||
|
)?;
|
||||||
items.append(&mut encoded);
|
items.append(&mut encoded);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,8 +148,13 @@ fn encode_data(
|
|||||||
|
|
||||||
for field in message_types.get(ident).expect("Already checked in match guard; qed") {
|
for field in message_types.get(ident).expect("Already checked in match guard; qed") {
|
||||||
let value = &value[&field.name];
|
let value = &value[&field.name];
|
||||||
let type_ = parser.parse_type(&*field.type_)?;
|
let type_ = parse_type(&*field.type_)?;
|
||||||
let mut encoded = encode_data(parser, &type_, &message_types, &value, Some(&*field.name))?;
|
let mut encoded = encode_data(
|
||||||
|
&type_,
|
||||||
|
&message_types,
|
||||||
|
&value,
|
||||||
|
Some(&*field.name)
|
||||||
|
)?;
|
||||||
tokens.append(&mut encoded);
|
tokens.append(&mut encoded);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +162,8 @@ fn encode_data(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Type::Bytes => {
|
Type::Bytes => {
|
||||||
let string = value.as_str().ok_or_else(|| serde_error("string", field_name))?;
|
let string = value.as_str()
|
||||||
|
.ok_or(serde_error("string", field_name))?;
|
||||||
|
|
||||||
check_hex(&string)?;
|
check_hex(&string)?;
|
||||||
|
|
||||||
@ -157,7 +176,8 @@ fn encode_data(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Type::Byte(_) => {
|
Type::Byte(_) => {
|
||||||
let string = value.as_str().ok_or_else(|| serde_error("string", field_name))?;
|
let string = value.as_str()
|
||||||
|
.ok_or(serde_error("string", field_name))?;
|
||||||
|
|
||||||
check_hex(&string)?;
|
check_hex(&string)?;
|
||||||
|
|
||||||
@ -169,28 +189,34 @@ fn encode_data(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Type::String => {
|
Type::String => {
|
||||||
let value = value.as_str().ok_or_else(|| serde_error("string", field_name))?;
|
let value = value.as_str()
|
||||||
|
.ok_or(serde_error("string", field_name))?;
|
||||||
let hash = keccak(value).as_ref().to_vec();
|
let hash = keccak(value).as_ref().to_vec();
|
||||||
encode(&[EthAbiToken::FixedBytes(hash)])
|
encode(&[EthAbiToken::FixedBytes(hash)])
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::Bool => encode(&[EthAbiToken::Bool(value.as_bool().ok_or_else(|| serde_error("bool", field_name))?)]),
|
Type::Bool => encode(&[EthAbiToken::Bool(value.as_bool()
|
||||||
|
.ok_or(serde_error("bool", field_name))?)]),
|
||||||
|
|
||||||
Type::Address => {
|
Type::Address => {
|
||||||
let addr = value.as_str().ok_or_else(|| serde_error("string", field_name))?;
|
let addr = value.as_str()
|
||||||
|
.ok_or(serde_error("string", field_name))?;
|
||||||
if addr.len() != 42 {
|
if addr.len() != 42 {
|
||||||
return Err(ErrorKind::InvalidAddressLength(addr.len()))?;
|
return Err(ErrorKind::InvalidAddressLength(addr.len()))?;
|
||||||
}
|
}
|
||||||
let address = EthAddress::from_str(&addr[2..]).map_err(|err| ErrorKind::HexParseError(format!("{}", err)))?;
|
let address = EthAddress::from_str(&addr[2..])
|
||||||
|
.map_err(|err| ErrorKind::HexParseError(format!("{}", err)))?;
|
||||||
encode(&[EthAbiToken::Address(address)])
|
encode(&[EthAbiToken::Address(address)])
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::Uint | Type::Int => {
|
Type::Uint | Type::Int => {
|
||||||
let string = value.as_str().ok_or_else(|| serde_error("int/uint", field_name))?;
|
let string = value.as_str()
|
||||||
|
.ok_or(serde_error("int/uint", field_name))?;
|
||||||
|
|
||||||
check_hex(&string)?;
|
check_hex(&string)?;
|
||||||
|
|
||||||
let uint = U256::from_str(&string[2..]).map_err(|err| ErrorKind::HexParseError(format!("{}", err)))?;
|
let uint = U256::from_str(&string[2..])
|
||||||
|
.map_err(|err| ErrorKind::HexParseError(format!("{}", err)))?;
|
||||||
|
|
||||||
let token = if *message_type == Type::Uint {
|
let token = if *message_type == Type::Uint {
|
||||||
EthAbiToken::Uint(uint)
|
EthAbiToken::Uint(uint)
|
||||||
@ -200,7 +226,12 @@ fn encode_data(
|
|||||||
encode(&[token])
|
encode(&[token])
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => return Err(ErrorKind::UnknownType(format!("{}", field_name.unwrap_or("")), format!("{}", *message_type)))?
|
_ => return Err(
|
||||||
|
ErrorKind::UnknownType(
|
||||||
|
format!("{}", field_name.unwrap_or("")),
|
||||||
|
format!("{}", *message_type)
|
||||||
|
).into()
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(encoded)
|
Ok(encoded)
|
||||||
@ -213,10 +244,19 @@ pub fn hash_structured_data(typed_data: EIP712) -> Result<H256> {
|
|||||||
// EIP-191 compliant
|
// EIP-191 compliant
|
||||||
let prefix = (b"\x19\x01").to_vec();
|
let prefix = (b"\x19\x01").to_vec();
|
||||||
let domain = to_value(&typed_data.domain).unwrap();
|
let domain = to_value(&typed_data.domain).unwrap();
|
||||||
let parser = Parser::new();
|
|
||||||
let (domain_hash, data_hash) = (
|
let (domain_hash, data_hash) = (
|
||||||
encode_data(&parser, &Type::Custom("EIP712Domain".into()), &typed_data.types, &domain, None)?,
|
encode_data(
|
||||||
encode_data(&parser, &Type::Custom(typed_data.primary_type), &typed_data.types, &typed_data.message, None)?
|
&Type::Custom("EIP712Domain".into()),
|
||||||
|
&typed_data.types,
|
||||||
|
&domain,
|
||||||
|
None
|
||||||
|
)?,
|
||||||
|
encode_data(
|
||||||
|
&Type::Custom(typed_data.primary_type),
|
||||||
|
&typed_data.types,
|
||||||
|
&typed_data.message,
|
||||||
|
None
|
||||||
|
)?
|
||||||
);
|
);
|
||||||
let concat = [&prefix[..], &domain_hash[..], &data_hash[..]].concat();
|
let concat = [&prefix[..], &domain_hash[..], &data_hash[..]].concat();
|
||||||
Ok(keccak(concat))
|
Ok(keccak(concat))
|
||||||
@ -412,4 +452,196 @@ mod tests {
|
|||||||
ErrorKind::UnequalArrayItems(2, "Person[2]".into(), 1)
|
ErrorKind::UnequalArrayItems(2, "Person[2]".into(), 1)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_typed_data_v4() {
|
||||||
|
let string = r#"{
|
||||||
|
"types": {
|
||||||
|
"EIP712Domain": [
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "version",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "chainId",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "verifyingContract",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Person": [
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "wallets",
|
||||||
|
"type": "address[]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Mail": [
|
||||||
|
{
|
||||||
|
"name": "from",
|
||||||
|
"type": "Person"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "to",
|
||||||
|
"type": "Person[]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "contents",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Group": [
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "members",
|
||||||
|
"type": "Person[]"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"domain": {
|
||||||
|
"name": "Ether Mail",
|
||||||
|
"version": "1",
|
||||||
|
"chainId": "0x1",
|
||||||
|
"verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
|
||||||
|
},
|
||||||
|
"primaryType": "Mail",
|
||||||
|
"message": {
|
||||||
|
"from": {
|
||||||
|
"name": "Cow",
|
||||||
|
"wallets": [
|
||||||
|
"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
|
||||||
|
"0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"to": [
|
||||||
|
{
|
||||||
|
"name": "Bob",
|
||||||
|
"wallets": [
|
||||||
|
"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB",
|
||||||
|
"0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57",
|
||||||
|
"0xB0B0b0b0b0b0B000000000000000000000000000"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"contents": "Hello, Bob!"
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let typed_data = from_str::<EIP712>(string).expect("alas error!");
|
||||||
|
let hash = hash_structured_data(typed_data.clone()).expect("alas error!");
|
||||||
|
assert_eq!(
|
||||||
|
&format!("{:x}", hash)[..],
|
||||||
|
|
||||||
|
"a85c2e2b118698e88db68a8105b794a8cc7cec074e89ef991cb4f5f533819cc2",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_typed_data_v4_custom_array() {
|
||||||
|
let string = r#"{
|
||||||
|
"types": {
|
||||||
|
"EIP712Domain": [
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "version",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "chainId",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "verifyingContract",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Person": [
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "wallets",
|
||||||
|
"type": "address[]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Mail": [
|
||||||
|
{
|
||||||
|
"name": "from",
|
||||||
|
"type": "Person"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "to",
|
||||||
|
"type": "Group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "contents",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Group": [
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "members",
|
||||||
|
"type": "Person[]"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"domain": {
|
||||||
|
"name": "Ether Mail",
|
||||||
|
"version": "1",
|
||||||
|
"chainId": "0x1",
|
||||||
|
"verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
|
||||||
|
},
|
||||||
|
"primaryType": "Mail",
|
||||||
|
"message": {
|
||||||
|
"from": {
|
||||||
|
"name": "Cow",
|
||||||
|
"wallets": [
|
||||||
|
"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
|
||||||
|
"0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"to": {
|
||||||
|
"name": "Farmers",
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"name": "Bob",
|
||||||
|
"wallets": [
|
||||||
|
"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB",
|
||||||
|
"0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57",
|
||||||
|
"0xB0B0b0b0b0b0B000000000000000000000000000"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"contents": "Hello, Bob!"
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
let typed_data = from_str::<EIP712>(string).expect("alas error!");
|
||||||
|
let hash = hash_structured_data(typed_data.clone()).expect("alas error!");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
&format!("{:x}", hash)[..],
|
||||||
|
"cd8b34cd09c541cfc0a2fcd147e47809b98b335649c2aa700db0b0c4501a02a0",
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
//! Solidity type-name parsing
|
//! Solidity type-name parsing
|
||||||
use lunarity_lexer::{Lexer, Token};
|
use lunarity_lexer::{Lexer, Token};
|
||||||
use crate::error::*;
|
use crate::error::*;
|
||||||
use toolshed::Arena;
|
|
||||||
use std::{fmt, result};
|
use std::{fmt, result};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
@ -68,81 +67,70 @@ impl fmt::Display for Type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Parser {
|
|
||||||
arena: Arena,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parser {
|
/// the type string is being validated before it's parsed.
|
||||||
pub fn new() -> Self {
|
pub fn parse_type(field_type: &str) -> Result<Type> {
|
||||||
Parser {
|
#[derive(PartialEq)]
|
||||||
arena: Arena::new()
|
enum State { Open, Close }
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// the type string is being validated before it's parsed.
|
let mut lexer = Lexer::new(field_type);
|
||||||
pub fn parse_type(&self, field_type: &str) -> Result<Type> {
|
let mut token = None;
|
||||||
#[derive(PartialEq)]
|
let mut state = State::Close;
|
||||||
enum State { Open, Close }
|
let mut array_depth = 0;
|
||||||
|
let mut current_array_length: Option<u64> = None;
|
||||||
|
|
||||||
let mut lexer = Lexer::new(&self.arena, field_type);
|
while lexer.token != Token::EndOfProgram {
|
||||||
let mut token = None;
|
let type_ = match lexer.token {
|
||||||
let mut state = State::Close;
|
Token::Identifier => Type::Custom(lexer.slice().to_owned()),
|
||||||
let mut array_depth = 0;
|
Token::TypeByte => Type::Byte(lexer.extras.0),
|
||||||
let mut current_array_length: Option<u64> = None;
|
Token::TypeBytes => Type::Bytes,
|
||||||
|
Token::TypeBool => Type::Bool,
|
||||||
while lexer.token != Token::EndOfProgram {
|
Token::TypeUint => Type::Uint,
|
||||||
let type_ = match lexer.token {
|
Token::TypeInt => Type::Int,
|
||||||
Token::Identifier => Type::Custom(lexer.token_as_str().to_owned()),
|
Token::TypeString => Type::String,
|
||||||
Token::TypeByte => Type::Byte(lexer.type_size.0),
|
Token::TypeAddress => Type::Address,
|
||||||
Token::TypeBytes => Type::Bytes,
|
Token::LiteralInteger => {
|
||||||
Token::TypeBool => Type::Bool,
|
let length = lexer.slice();
|
||||||
Token::TypeUint => Type::Uint,
|
current_array_length = Some(length
|
||||||
Token::TypeInt => Type::Int,
|
.parse()
|
||||||
Token::TypeString => Type::String,
|
.map_err(|_|
|
||||||
Token::TypeAddress => Type::Address,
|
ErrorKind::InvalidArraySize(length.into())
|
||||||
Token::LiteralInteger => {
|
)?
|
||||||
let length = lexer.token_as_str();
|
);
|
||||||
current_array_length = Some(length
|
lexer.advance();
|
||||||
.parse()
|
continue;
|
||||||
.map_err(|_|
|
}
|
||||||
ErrorKind::InvalidArraySize(length.into())
|
Token::BracketOpen if token.is_some() && state == State::Close => {
|
||||||
)?
|
state = State::Open;
|
||||||
);
|
lexer.advance();
|
||||||
lexer.consume();
|
continue;
|
||||||
|
}
|
||||||
|
Token::BracketClose if array_depth < 10 => {
|
||||||
|
if state == State::Open && token.is_some() {
|
||||||
|
let length = current_array_length.take();
|
||||||
|
state = State::Close;
|
||||||
|
token = Some(Type::Array {
|
||||||
|
inner: Box::new(token.expect("if statement checks for some; qed")),
|
||||||
|
length,
|
||||||
|
});
|
||||||
|
lexer.advance();
|
||||||
|
array_depth += 1;
|
||||||
continue;
|
continue;
|
||||||
},
|
} else {
|
||||||
Token::BracketOpen if token.is_some() && state == State::Close => {
|
return Err(ErrorKind::UnexpectedToken(lexer.slice().to_owned(), field_type.to_owned()))?;
|
||||||
state = State::Open;
|
|
||||||
lexer.consume();
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
Token::BracketClose if array_depth < 10 => {
|
}
|
||||||
if state == State::Open && token.is_some() {
|
Token::BracketClose if array_depth == 10 => {
|
||||||
let length = current_array_length.take();
|
return Err(ErrorKind::UnsupportedArrayDepth)?;
|
||||||
state = State::Close;
|
}
|
||||||
token = Some(Type::Array {
|
_ => return Err(ErrorKind::UnexpectedToken(lexer.slice().to_owned(), field_type.to_owned()))?
|
||||||
inner: Box::new(token.expect("if statement checks for some; qed")),
|
};
|
||||||
length
|
|
||||||
});
|
|
||||||
lexer.consume();
|
|
||||||
array_depth += 1;
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
return Err(ErrorKind::UnexpectedToken(lexer.token_as_str().to_owned(), field_type.to_owned()))?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Token::BracketClose if array_depth == 10 => {
|
|
||||||
return Err(ErrorKind::UnsupportedArrayDepth)?
|
|
||||||
}
|
|
||||||
_ => return Err(ErrorKind::UnexpectedToken(lexer.token_as_str().to_owned(), field_type.to_owned()))?
|
|
||||||
};
|
|
||||||
|
|
||||||
token = Some(type_);
|
token = Some(type_);
|
||||||
lexer.consume();
|
lexer.advance();
|
||||||
}
|
|
||||||
|
|
||||||
Ok(token.ok_or_else(|| ErrorKind::NonExistentType)?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(token.ok_or(ErrorKind::NonExistentType)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -151,22 +139,19 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parser() {
|
fn test_parser() {
|
||||||
let parser = Parser::new();
|
|
||||||
let source = "byte[][][7][][][][][][][]";
|
let source = "byte[][][7][][][][][][][]";
|
||||||
parser.parse_type(source).unwrap();
|
parse_type(source).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_nested_array() {
|
fn test_nested_array() {
|
||||||
let parser = Parser::new();
|
|
||||||
let source = "byte[][][7][][][][][][][][]";
|
let source = "byte[][][7][][][][][][][][]";
|
||||||
assert_eq!(parser.parse_type(source).is_err(), true);
|
assert_eq!(parse_type(source).is_err(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_malformed_array_type() {
|
fn test_malformed_array_type() {
|
||||||
let parser = Parser::new();
|
|
||||||
let source = "byte[7[]uint][]";
|
let source = "byte[7[]uint][]";
|
||||||
assert_eq!(parser.parse_type(source).is_err(), true)
|
assert_eq!(parse_type(source).is_err(), true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user