v2.5.10 stable (#11239)
* ropsten #6631425 foundation #8798209 (#11201) * [stable] builtin, istanbul and mordor testnet backports (#11234) * ethcore-builtin (#10850) * [builtin]: support `multiple prices and activations` in chain spec (#11039) * [chain specs]: activate `Istanbul` on mainnet (#11228) * ethcore/res: add mordor testnet configuration (#11200) * Update list of bootnodes for xDai chain (#11236) * ethcore: remove `test-helper feat` from build (#11047) * Secret store: fix Instant::now() related race in net_keep_alive (#11155) (#11159) * [stable]: backport #10691 and #10683 (#11143) * Fix compiler warning (that will become an error) (#10683) * Refactor Clique stepping (#10691) * Add Constantinople eips to the dev (instant_seal) config (#10809) * Add cargo-remote dir to .gitignore (?) * Insert explicit warning into the panic hook (#11225) * Fix docker centos build (#11226) * Update MIX bootnodes. (#11203) * Use provided usd-per-eth value if an endpoint is specified (#11209) * Add new line after writing block to hex file. (#10984) * Type annotation for next_key() matching of json filter options (#11192) (but no `FilterOption` in 2.5 so…) * Upgrade jsonrpc to latest (#11206) * [CI] check evmbin build (#11096) * Correct EIP-712 encoding (#11092) * [client]: Fix for incorrectly dropped consensus messages (#11086) * Fix block detail updating (#11015) * Switching sccache from local to Redis (#10971) * Made ecrecover implementation trait public (#11188) * [dependencies]: jsonrpc `14.0.1` (#11183) * [receipt]: add `sender` & `receiver` to `RichReceipts` (#11179) * [ethcore/builtin]: do not panic in blake2pricer on short input (#11180) * util Host: fix a double Read Lock bug in fn Host::session_readable() (#11175) * ethcore client: fix a double Read Lock bug in fn Client::logs() (#11172) * Change how RPCs eth_call and eth_estimateGas handle "Pending" (#11127) * Cleanup stratum a bit (#11161) * Upgrade to jsonrpc v14 (#11151) * SecretStore: expose restore_key_public in HTTP API (#10241)
This commit is contained in:
@@ -23,7 +23,7 @@ use std::str::FromStr;
|
||||
use itertools::Itertools;
|
||||
use indexmap::IndexSet;
|
||||
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::eip712::{EIP712, MessageTypes};
|
||||
use rustc_hex::FromHex;
|
||||
@@ -56,11 +56,16 @@ fn build_dependencies<'a>(message_type: &'a str, message_types: &'a MessageTypes
|
||||
deps.insert(item);
|
||||
|
||||
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
|
||||
if deps.contains(&*field.type_) || !message_types.contains_key(&*field.type_) {
|
||||
continue;
|
||||
if !deps.contains(field_type) || message_types.contains_key(field_type) {
|
||||
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> {
|
||||
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);
|
||||
let mut temp = temp.into_iter().collect::<Vec<_>>();
|
||||
(&mut temp[..]).sort_unstable();
|
||||
@@ -99,7 +105,6 @@ fn type_hash(message_type: &str, typed_data: &MessageTypes) -> Result<H256> {
|
||||
}
|
||||
|
||||
fn encode_data(
|
||||
parser: &Parser,
|
||||
message_type: &Type,
|
||||
message_types: &MessageTypes,
|
||||
value: &Value,
|
||||
@@ -112,52 +117,70 @@ fn encode_data(
|
||||
length
|
||||
} => {
|
||||
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
|
||||
// the length of items to be encoded
|
||||
if length.is_some() && Some(values.len() as u64) != *length {
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
|
||||
// keccak(items).as_ref().to_vec()
|
||||
keccak(items).to_vec()
|
||||
}
|
||||
|
||||
Type::Custom(ref ident) if message_types.get(&*ident).is_some() => {
|
||||
let type_hash = (&type_hash(ident, &message_types)?).to_vec();
|
||||
let type_hash = (&type_hash(ident, &message_types)?).0.to_vec();
|
||||
let mut tokens = encode(&[EthAbiToken::FixedBytes(type_hash)]);
|
||||
|
||||
for field in message_types.get(ident).expect("Already checked in match guard; qed") {
|
||||
let value = &value[&field.name];
|
||||
let type_ = parser.parse_type(&*field.type_)?;
|
||||
let mut encoded = encode_data(parser, &type_, &message_types, &value, Some(&*field.name))?;
|
||||
let type_ = parse_type(&*field.type_)?;
|
||||
let mut encoded = encode_data(
|
||||
&type_,
|
||||
&message_types,
|
||||
&value,
|
||||
Some(&*field.name)
|
||||
)?;
|
||||
tokens.append(&mut encoded);
|
||||
}
|
||||
|
||||
// keccak(tokens).as_ref().to_vec()
|
||||
keccak(tokens).to_vec()
|
||||
}
|
||||
|
||||
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)?;
|
||||
|
||||
let bytes = (&string[2..])
|
||||
.from_hex::<Vec<u8>>()
|
||||
.map_err(|err| ErrorKind::HexParseError(format!("{}", err)))?;
|
||||
// let bytes = keccak(&bytes).as_ref().to_vec();
|
||||
let bytes = keccak(&bytes).to_vec();
|
||||
|
||||
encode(&[EthAbiToken::FixedBytes(bytes)])
|
||||
}
|
||||
|
||||
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)?;
|
||||
|
||||
@@ -169,28 +192,35 @@ fn encode_data(
|
||||
}
|
||||
|
||||
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).to_vec();
|
||||
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 => {
|
||||
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 {
|
||||
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)])
|
||||
}
|
||||
|
||||
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)?;
|
||||
|
||||
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 {
|
||||
EthAbiToken::Uint(uint)
|
||||
@@ -200,7 +230,12 @@ fn encode_data(
|
||||
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)
|
||||
@@ -213,10 +248,19 @@ pub fn hash_structured_data(typed_data: EIP712) -> Result<H256> {
|
||||
// EIP-191 compliant
|
||||
let prefix = (b"\x19\x01").to_vec();
|
||||
let domain = to_value(&typed_data.domain).unwrap();
|
||||
let parser = Parser::new();
|
||||
let (domain_hash, data_hash) = (
|
||||
encode_data(&parser, &Type::Custom("EIP712Domain".into()), &typed_data.types, &domain, None)?,
|
||||
encode_data(&parser, &Type::Custom(typed_data.primary_type), &typed_data.types, &typed_data.message, None)?
|
||||
encode_data(
|
||||
&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();
|
||||
Ok(keccak(concat))
|
||||
@@ -359,9 +403,10 @@ mod tests {
|
||||
#[test]
|
||||
fn test_hash_data() {
|
||||
let typed_data = from_str::<EIP712>(JSON).expect("alas error!");
|
||||
let hash = hash_structured_data(typed_data).expect("alas error!");
|
||||
assert_eq!(
|
||||
hash_structured_data(typed_data).expect("alas error!").to_hex::<String>(),
|
||||
"be609aee343fb3c4b28e1df9e632fca64fcfaede20f02e86244efddf30957bd2"
|
||||
&format!("{:x}", hash)[..],
|
||||
"be609aee343fb3c4b28e1df9e632fca64fcfaede20f02e86244efddf30957bd2",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -411,4 +456,196 @@ mod tests {
|
||||
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",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ pub(crate) fn serde_error(expected: &str, field: Option<&str>) -> ErrorKind {
|
||||
}
|
||||
|
||||
impl Fail for Error {
|
||||
fn cause(&self) -> Option<&Fail> {
|
||||
fn cause(&self) -> Option<&dyn Fail> {
|
||||
self.inner.cause()
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
//! Solidity type-name parsing
|
||||
use lunarity_lexer::{Lexer, Token};
|
||||
use crate::error::*;
|
||||
use toolshed::Arena;
|
||||
use std::{fmt, result};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@@ -68,81 +67,70 @@ impl fmt::Display for Type {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Parser {
|
||||
arena: Arena,
|
||||
}
|
||||
|
||||
impl Parser {
|
||||
pub fn new() -> Self {
|
||||
Parser {
|
||||
arena: Arena::new()
|
||||
}
|
||||
}
|
||||
/// the type string is being validated before it's parsed.
|
||||
pub fn parse_type(field_type: &str) -> Result<Type> {
|
||||
#[derive(PartialEq)]
|
||||
enum State { Open, Close }
|
||||
|
||||
/// the type string is being validated before it's parsed.
|
||||
pub fn parse_type(&self, field_type: &str) -> Result<Type> {
|
||||
#[derive(PartialEq)]
|
||||
enum State { Open, Close }
|
||||
let mut lexer = Lexer::new(field_type);
|
||||
let mut token = None;
|
||||
let mut state = State::Close;
|
||||
let mut array_depth = 0;
|
||||
let mut current_array_length: Option<u64> = None;
|
||||
|
||||
let mut lexer = Lexer::new(&self.arena, field_type);
|
||||
let mut token = None;
|
||||
let mut state = State::Close;
|
||||
let mut array_depth = 0;
|
||||
let mut current_array_length: Option<u64> = None;
|
||||
|
||||
while lexer.token != Token::EndOfProgram {
|
||||
let type_ = match lexer.token {
|
||||
Token::Identifier => Type::Custom(lexer.token_as_str().to_owned()),
|
||||
Token::TypeByte => Type::Byte(lexer.type_size.0),
|
||||
Token::TypeBytes => Type::Bytes,
|
||||
Token::TypeBool => Type::Bool,
|
||||
Token::TypeUint => Type::Uint,
|
||||
Token::TypeInt => Type::Int,
|
||||
Token::TypeString => Type::String,
|
||||
Token::TypeAddress => Type::Address,
|
||||
Token::LiteralInteger => {
|
||||
let length = lexer.token_as_str();
|
||||
current_array_length = Some(length
|
||||
.parse()
|
||||
.map_err(|_|
|
||||
ErrorKind::InvalidArraySize(length.into())
|
||||
)?
|
||||
);
|
||||
lexer.consume();
|
||||
while lexer.token != Token::EndOfProgram {
|
||||
let type_ = match lexer.token {
|
||||
Token::Identifier => Type::Custom(lexer.slice().to_owned()),
|
||||
Token::TypeByte => Type::Byte(lexer.extras.0),
|
||||
Token::TypeBytes => Type::Bytes,
|
||||
Token::TypeBool => Type::Bool,
|
||||
Token::TypeUint => Type::Uint,
|
||||
Token::TypeInt => Type::Int,
|
||||
Token::TypeString => Type::String,
|
||||
Token::TypeAddress => Type::Address,
|
||||
Token::LiteralInteger => {
|
||||
let length = lexer.slice();
|
||||
current_array_length = Some(length
|
||||
.parse()
|
||||
.map_err(|_|
|
||||
ErrorKind::InvalidArraySize(length.into())
|
||||
)?
|
||||
);
|
||||
lexer.advance();
|
||||
continue;
|
||||
}
|
||||
Token::BracketOpen if token.is_some() && state == State::Close => {
|
||||
state = State::Open;
|
||||
lexer.advance();
|
||||
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;
|
||||
},
|
||||
Token::BracketOpen if token.is_some() && state == State::Close => {
|
||||
state = State::Open;
|
||||
lexer.consume();
|
||||
continue
|
||||
} else {
|
||||
return Err(ErrorKind::UnexpectedToken(lexer.slice().to_owned(), field_type.to_owned()))?;
|
||||
}
|
||||
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.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::BracketClose if array_depth == 10 => {
|
||||
return Err(ErrorKind::UnsupportedArrayDepth)?;
|
||||
}
|
||||
_ => return Err(ErrorKind::UnexpectedToken(lexer.slice().to_owned(), field_type.to_owned()))?
|
||||
};
|
||||
|
||||
token = Some(type_);
|
||||
lexer.consume();
|
||||
}
|
||||
|
||||
Ok(token.ok_or_else(|| ErrorKind::NonExistentType)?)
|
||||
token = Some(type_);
|
||||
lexer.advance();
|
||||
}
|
||||
|
||||
Ok(token.ok_or(ErrorKind::NonExistentType)?)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -151,22 +139,19 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_parser() {
|
||||
let parser = Parser::new();
|
||||
let source = "byte[][][7][][][][][][][]";
|
||||
parser.parse_type(source).unwrap();
|
||||
parse_type(source).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_array() {
|
||||
let parser = Parser::new();
|
||||
let source = "byte[][][7][][][][][][][][]";
|
||||
assert_eq!(parser.parse_type(source).is_err(), true);
|
||||
assert_eq!(parse_type(source).is_err(), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_malformed_array_type() {
|
||||
let parser = Parser::new();
|
||||
let source = "byte[7[]uint][]";
|
||||
assert_eq!(parser.parse_type(source).is_err(), true)
|
||||
assert_eq!(parse_type(source).is_err(), true)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user