EIP-712 implementation (#9631)
* EIP-712 impl * added more tests * removed size parsing unwrap * corrected TYPE_REGEX to disallow zero sized fixed length arrays, replaced LinkedHashSet with IndexSet, added API spec to docs, fixed Type::Byte encoding branch * use Option<u64> instead of u64 for Type::Array::Length * replace `.iter()` with `.values()` Co-Authored-By: seunlanlege <seunlanlege@gmail.com> * tabify eip712.rs * use proper comments for docs * Cargo.lock: revert unrelated changes * tabify encode.rs
This commit is contained in:
parent
1b9396dcbb
commit
61c1646b43
136
Cargo.lock
generated
136
Cargo.lock
generated
@ -424,6 +424,29 @@ name = "edit-distance"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "eip712"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ethabi 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethereum-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"indexmap 1.0.2 (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.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lunarity-lexer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.0.5 (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.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.32 (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_derive 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.5.0"
|
||||
@ -1061,6 +1084,26 @@ dependencies = [
|
||||
"vm 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "failure"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "failure_derive"
|
||||
version = "0.1.3"
|
||||
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)",
|
||||
"syn 0.15.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fake-fetch"
|
||||
version = "0.0.1"
|
||||
@ -1173,6 +1216,14 @@ dependencies = [
|
||||
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fxhash"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gcc"
|
||||
version = "0.3.55"
|
||||
@ -1216,7 +1267,7 @@ dependencies = [
|
||||
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"http 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"indexmap 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"string 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1389,6 +1440,11 @@ dependencies = [
|
||||
"unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "if_chain"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "igd"
|
||||
version = "0.7.0"
|
||||
@ -1407,7 +1463,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@ -1442,6 +1498,14 @@ dependencies = [
|
||||
"either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.7.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.3"
|
||||
@ -1751,6 +1815,14 @@ dependencies = [
|
||||
"linked-hash-map 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lunarity-lexer"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"toolshed 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "macros"
|
||||
version = "0.1.0"
|
||||
@ -2261,6 +2333,7 @@ version = "1.12.0"
|
||||
dependencies = [
|
||||
"ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cid 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"eip712 0.1.0",
|
||||
"ethash 1.12.0",
|
||||
"ethcore 1.12.0",
|
||||
"ethcore-devtools 1.12.0",
|
||||
@ -3176,6 +3249,17 @@ dependencies = [
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.10.1"
|
||||
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)",
|
||||
"syn 0.15.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "target_info"
|
||||
version = "0.1.0"
|
||||
@ -3515,6 +3599,14 @@ dependencies = [
|
||||
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toolshed"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trace-time"
|
||||
version = "0.1.1"
|
||||
@ -3687,6 +3779,34 @@ name = "utf8-ranges"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "validator"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "validator_derive"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"if_chain 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"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 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"validator 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.1"
|
||||
@ -3972,6 +4092,8 @@ dependencies = [
|
||||
"checksum ethbloom 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a93a43ce2e9f09071449da36bfa7a1b20b950ee344b6904ff23de493b03b386"
|
||||
"checksum ethereum-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "35b3c5a18bc5e73a32a110ac743ec04b02bbbcd3b71d3118d40a6113d509378a"
|
||||
"checksum ethereum-types-serialize 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ac59a21a9ce98e188f3dace9eb67a6c4a3c67ec7fbc7218cb827852679dc002"
|
||||
"checksum failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6dd377bcc1b1b7ce911967e3ec24fa19c3224394ec05b54aa7b083d498341ac7"
|
||||
"checksum failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "64c2d913fe8ed3b6c6518eedf4538255b989945c14c2a7d5cbff62a5e2120596"
|
||||
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
||||
"checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa"
|
||||
"checksum fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d5ec8112f00ea8a483e04748a85522184418fd1cf02890b626d8fc28683f7de"
|
||||
@ -3982,6 +4104,7 @@ dependencies = [
|
||||
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
|
||||
"checksum futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "49e7653e374fe0d0c12de4250f0bdb60680b8c80eed558c5c7538eec9c89e21b"
|
||||
"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
|
||||
"checksum fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
|
||||
"checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"
|
||||
"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d"
|
||||
"checksum getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "0a7292d30132fb5424b354f5dc02512a86e4c516fe544bb7a25e7f266951b797"
|
||||
@ -4001,13 +4124,15 @@ dependencies = [
|
||||
"checksum hyper 0.12.11 (registry+https://github.com/rust-lang/crates.io-index)" = "78d50abbd1790e0f4c74cb1d4a2211b439bac661d54107ad5564c55e77906762"
|
||||
"checksum hyper-rustls 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "68f2aa6b1681795bf4da8063f718cd23145aa0c9a5143d9787b345aa60d38ee4"
|
||||
"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
|
||||
"checksum if_chain 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4bac95d9aa0624e7b78187d6fb8ab012b41d9f6f54b1bcb61e61c4845f8357ec"
|
||||
"checksum igd 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a254e265e8810deb357a9de757f784787ec415d056ededf410c0aa460afee9e"
|
||||
"checksum indexmap 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08173ba1e906efb6538785a8844dd496f5d34f0a2d88038e95195172fc667220"
|
||||
"checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d"
|
||||
"checksum integer-encoding 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "26746cbc2e680af687e88d717f20ff90079bd10fc984ad57d277cd0e37309fa5"
|
||||
"checksum interleaved-ordered 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "141340095b15ed7491bd3d4ced9d20cebfb826174b6bb03386381f62b01e3d77"
|
||||
"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08"
|
||||
"checksum ipnetwork 0.12.8 (registry+https://github.com/rust-lang/crates.io-index)" = "70783119ac90828aaba91eae39db32c6c1b8838deea3637e5238efa0130801ab"
|
||||
"checksum itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4833d6978da405305126af4ac88569b5d71ff758581ce5a987dbfa3755f694fc"
|
||||
"checksum itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)" = "f58856976b776fedd95533137617a02fb25719f40e7d9b01c7043cd65474f450"
|
||||
"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
|
||||
"checksum jni 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1ecfa3b81afc64d9a6539c4eece96ac9a93c551c713a313800dade8e33d7b5c1"
|
||||
"checksum jni-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
||||
@ -4038,6 +4163,7 @@ dependencies = [
|
||||
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
|
||||
"checksum log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f"
|
||||
"checksum lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4d06ff7ff06f729ce5f4e227876cb88d10bc59cd4ae1e09fbb2bde15c850dc21"
|
||||
"checksum lunarity-lexer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a1670671f305792567116d4660e6e5bd785d6fa973e817c3445c0a7a54cecb6"
|
||||
"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
|
||||
"checksum memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4b3629fe9fdbff6daa6c33b90f7c08355c1aca05a3d01fa8063b822fcf185f3b"
|
||||
"checksum memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2ffa2c986de11a9df78620c01eeaaf27d94d3ff02bf81bfcca953102dd0c6ff"
|
||||
@ -4157,6 +4283,7 @@ dependencies = [
|
||||
"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
|
||||
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
|
||||
"checksum syn 0.15.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b036b7b35e846707c0e55c2c9441fa47867c0f87fca416921db3261b1d8c741a"
|
||||
"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
|
||||
"checksum target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c63f48baada5c52e65a29eef93ab4f8982681b67f9e8d29c7b05abcfec2b9ffe"
|
||||
"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
|
||||
"checksum term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327"
|
||||
@ -4189,6 +4316,7 @@ dependencies = [
|
||||
"checksum tokio-udp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "da941144b816d0dcda4db3a1ba87596e4df5e860a72b70783fe435891f80601c"
|
||||
"checksum tokio-uds 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "22e3aa6d1fcc19e635418dc0a30ab5bd65d347973d6f43f1a37bf8d9d1335fc9"
|
||||
"checksum toml 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4a2ecc31b0351ea18b3fe11274b8db6e4d82bce861bbb22e6dbed40417902c65"
|
||||
"checksum toolshed 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "450441e131c7663af72e63a33c02a6a1fbaaa8601dc652ed6757813bb55aeec7"
|
||||
"checksum trace-time 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe82f2f0bf1991e163e757baf044282823155dd326e70f44ce2186c3c320cc9"
|
||||
"checksum transaction-pool 1.13.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e5866e5126b14358f1d7af4bf51a0be677a363799b90e655edcec8254edef1d2"
|
||||
"checksum transient-hashmap 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aeb4b191d033a35edfce392a38cdcf9790b6cebcb30fa690c312c29da4dc433e"
|
||||
@ -4211,6 +4339,8 @@ dependencies = [
|
||||
"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 utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd70f467df6810094968e2fce0ee1bd0e87157aceb026a8c083bcf5e25b9efe4"
|
||||
"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 vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
|
||||
"checksum vergen 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c3365f36c57e5df714a34be40902b27a992eeddb9996eca52d0584611cf885d"
|
||||
"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
|
||||
|
@ -59,6 +59,7 @@ parity-updater = { path = "../updater" }
|
||||
parity-version = { path = "../util/version" }
|
||||
patricia-trie = "0.3.0"
|
||||
rlp = { version = "0.3.0", features = ["ethereum"] }
|
||||
eip712 = { path = "../util/EIP-712" }
|
||||
stats = { path = "../util/stats" }
|
||||
vm = { path = "../ethcore/vm" }
|
||||
|
||||
|
@ -63,6 +63,7 @@ extern crate parity_runtime;
|
||||
extern crate parity_updater as updater;
|
||||
extern crate parity_version as version;
|
||||
extern crate patricia_trie as trie;
|
||||
extern crate eip712;
|
||||
extern crate rlp;
|
||||
extern crate stats;
|
||||
extern crate vm;
|
||||
|
@ -286,6 +286,14 @@ pub fn signing(error: AccountError) -> Error {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invalid_call_data<T: fmt::Display>(error: T) -> Error {
|
||||
Error {
|
||||
code: ErrorCode::ServerError(codes::ENCODING_ERROR),
|
||||
message: format!("{}", error),
|
||||
data: None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn password(error: AccountError) -> Error {
|
||||
Error {
|
||||
code: ErrorCode::ServerError(codes::PASSWORD_INVALID),
|
||||
|
@ -38,6 +38,7 @@ use v1::types::{
|
||||
RichRawTransaction as RpcRichRawTransaction,
|
||||
};
|
||||
use v1::metadata::Metadata;
|
||||
use eip712::{EIP712, hash_structured_data};
|
||||
|
||||
/// Account management (personal) rpc implementation.
|
||||
pub struct PersonalClient<D: Dispatcher> {
|
||||
@ -150,6 +151,29 @@ impl<D: Dispatcher + 'static> Personal for PersonalClient<D> {
|
||||
}))
|
||||
}
|
||||
|
||||
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())))),
|
||||
};
|
||||
let dispatcher = self.dispatcher.clone();
|
||||
let accounts = self.accounts.clone();
|
||||
|
||||
let payload = RpcConfirmationPayload::EthSignMessage((account.clone(), RpcBytes(data)).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 ec_recover(&self, data: RpcBytes, signature: RpcH520) -> BoxFuture<RpcH160> {
|
||||
let signature: H520 = signature.into();
|
||||
let signature = Signature::from_electrum(&signature);
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
//! Personal rpc interface.
|
||||
use jsonrpc_core::{BoxFuture, Result};
|
||||
|
||||
use eip712::EIP712;
|
||||
use v1::types::{Bytes, U128, H160, H256, H520, TransactionRequest, RichRawTransaction as RpcRichRawTransaction};
|
||||
|
||||
build_rpc_trait! {
|
||||
@ -42,6 +42,11 @@ build_rpc_trait! {
|
||||
#[rpc(name = "personal_sign")]
|
||||
fn sign(&self, Bytes, H160, String) -> BoxFuture<H520>;
|
||||
|
||||
/// Produces an EIP-712 compliant signature with given account using the given password to unlock the
|
||||
/// account during the request.
|
||||
#[rpc(name = "personal_signTypedData")]
|
||||
fn sign_typed_data(&self, EIP712, 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")]
|
||||
|
23
util/EIP-712/Cargo.toml
Normal file
23
util/EIP-712/Cargo.toml
Normal file
@ -0,0 +1,23 @@
|
||||
[package]
|
||||
name = "eip712"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
serde_derive = "1.0"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
ethabi = "6.0"
|
||||
keccak-hash = "0.1"
|
||||
ethereum-types = "0.4"
|
||||
failure = "0.1"
|
||||
itertools = "0.7"
|
||||
failure_derive = "0.1"
|
||||
lazy_static = "1.1"
|
||||
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"
|
177
util/EIP-712/src/eip712.rs
Normal file
177
util/EIP-712/src/eip712.rs
Normal file
@ -0,0 +1,177 @@
|
||||
// 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/>.
|
||||
|
||||
//! EIP712 structs
|
||||
use serde_json::{Value};
|
||||
use std::collections::HashMap;
|
||||
use ethereum_types::{U256, H256, Address};
|
||||
use regex::Regex;
|
||||
use validator::Validate;
|
||||
use validator::ValidationErrors;
|
||||
|
||||
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]*(\[([1-9]\d*)*\])*$").unwrap();
|
||||
static ref IDENT_REGEX: Regex = Regex::new(r"^[a-zA-Z_$][a-zA-Z_$0-9]*$").unwrap();
|
||||
}
|
||||
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[derive(Deserialize, Serialize, Validate, Debug, Clone)]
|
||||
pub(crate) struct EIP712Domain {
|
||||
pub(crate) name: String,
|
||||
pub(crate) version: String,
|
||||
pub(crate) chain_id: U256,
|
||||
pub(crate) verifying_contract: Address,
|
||||
#[serde(skip_serializing_if="Option::is_none")]
|
||||
pub(crate) salt: Option<H256>,
|
||||
}
|
||||
/// EIP-712 struct
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct EIP712 {
|
||||
pub(crate) types: MessageTypes,
|
||||
pub(crate) primary_type: String,
|
||||
pub(crate) message: Value,
|
||||
pub(crate) domain: EIP712Domain,
|
||||
}
|
||||
|
||||
impl Validate for EIP712 {
|
||||
fn validate(&self) -> Result<(), ValidationErrors> {
|
||||
for field_types in self.types.values() {
|
||||
for field_type in field_types {
|
||||
field_type.validate()?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Validate, Debug, Clone)]
|
||||
pub(crate) struct FieldType {
|
||||
#[validate(regex = "IDENT_REGEX")]
|
||||
pub name: String,
|
||||
#[serde(rename = "type")]
|
||||
#[validate(regex = "TYPE_REGEX")]
|
||||
pub type_: String,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::from_str;
|
||||
|
||||
#[test]
|
||||
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)
|
||||
}
|
||||
|
||||
let test_cases = vec!["bytes32", "Foo[]", "bytes1", "bytes32[][]", "byte[9]", "contents"];
|
||||
for case in test_cases {
|
||||
assert_eq!(TYPE_REGEX.is_match(case), true)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deserialization() {
|
||||
let string = r#"{
|
||||
"primaryType": "Mail",
|
||||
"domain": {
|
||||
"name": "Ether Mail",
|
||||
"version": "1",
|
||||
"chainId": "0x1",
|
||||
"verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
|
||||
},
|
||||
"message": {
|
||||
"from": {
|
||||
"name": "Cow",
|
||||
"wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
|
||||
},
|
||||
"to": {
|
||||
"name": "Bob",
|
||||
"wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
|
||||
},
|
||||
"contents": "Hello, Bob!"
|
||||
},
|
||||
"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" }
|
||||
]
|
||||
}
|
||||
}"#;
|
||||
let _ = from_str::<EIP712>(string).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_failing_deserialization() {
|
||||
let string = r#"{
|
||||
"primaryType": "Mail",
|
||||
"domain": {
|
||||
"name": "Ether Mail",
|
||||
"version": "1",
|
||||
"chainId": "0x1",
|
||||
"verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
|
||||
},
|
||||
"message": {
|
||||
"from": {
|
||||
"name": "Cow",
|
||||
"wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
|
||||
},
|
||||
"to": {
|
||||
"name": "Bob",
|
||||
"wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
|
||||
},
|
||||
"contents": "Hello, Bob!"
|
||||
},
|
||||
"types": {
|
||||
"EIP712Domain": [
|
||||
{ "name": "name", "type": "string" },
|
||||
{ "name": "version", "type": "string" },
|
||||
{ "name": "chainId", "type": "7uint256[x] Seun" },
|
||||
{ "name": "verifyingContract", "type": "address" }
|
||||
],
|
||||
"Person": [
|
||||
{ "name": "name", "type": "string" },
|
||||
{ "name": "wallet amen", "type": "address" }
|
||||
],
|
||||
"Mail": [
|
||||
{ "name": "from", "type": "Person" },
|
||||
{ "name": "to", "type": "Person" },
|
||||
{ "name": "contents", "type": "string" }
|
||||
]
|
||||
}
|
||||
}"#;
|
||||
let data = from_str::<EIP712>(string).unwrap();
|
||||
assert_eq!(data.validate().is_err(), true);
|
||||
}
|
||||
}
|
415
util/EIP-712/src/encode.rs
Normal file
415
util/EIP-712/src/encode.rs
Normal file
@ -0,0 +1,415 @@
|
||||
// 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/>.
|
||||
|
||||
//! EIP712 Encoder
|
||||
use ethabi::{encode, Token as EthAbiToken};
|
||||
use ethereum_types::{Address as EthAddress, U256, H256};
|
||||
use keccak_hash::keccak;
|
||||
use serde_json::Value;
|
||||
use std::str::FromStr;
|
||||
use itertools::Itertools;
|
||||
use indexmap::IndexSet;
|
||||
use serde_json::to_value;
|
||||
use parser::{Parser, Type};
|
||||
use error::{Result, ErrorKind, serde_error};
|
||||
use eip712::{EIP712, MessageTypes};
|
||||
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>)>
|
||||
{
|
||||
if message_types.get(message_type).is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut types = IndexSet::new();
|
||||
types.insert(message_type);
|
||||
let mut deps = HashSet::new();
|
||||
|
||||
while let Some(item) = types.pop() {
|
||||
if let Some(fields) = message_types.get(item) {
|
||||
deps.insert(item);
|
||||
|
||||
for field in fields {
|
||||
// seen this type before? or not a custom type skip
|
||||
if deps.contains(&*field.type_) || !message_types.contains_key(&*field.type_) {
|
||||
continue;
|
||||
}
|
||||
types.insert(&*field.type_);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return Some(deps)
|
||||
}
|
||||
|
||||
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)?;
|
||||
temp.remove(message_type);
|
||||
let mut temp = temp.into_iter().collect::<Vec<_>>();
|
||||
(&mut temp[..]).sort_unstable();
|
||||
temp.insert(0, message_type);
|
||||
temp
|
||||
};
|
||||
|
||||
let encoded = deps
|
||||
.into_iter()
|
||||
.filter_map(|dep| {
|
||||
message_types.get(dep).map(|field_types| {
|
||||
let types = field_types
|
||||
.iter()
|
||||
.map(|value| format!("{} {}", value.type_, value.name))
|
||||
.join(",");
|
||||
return format!("{}({})", dep, types);
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.concat();
|
||||
Ok(encoded)
|
||||
}
|
||||
|
||||
fn type_hash(message_type: &str, typed_data: &MessageTypes) -> Result<H256> {
|
||||
Ok(keccak(encode_type(message_type, typed_data)?))
|
||||
}
|
||||
|
||||
fn encode_data(
|
||||
parser: &Parser,
|
||||
message_type: &Type,
|
||||
message_types: &MessageTypes,
|
||||
value: &Value,
|
||||
field_name: Option<&str>
|
||||
) -> Result<Vec<u8>>
|
||||
{
|
||||
let encoded = match message_type {
|
||||
Type::Array {
|
||||
inner,
|
||||
length
|
||||
} => {
|
||||
let mut items = vec![];
|
||||
let values = value.as_array().ok_or_else(|| 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))?
|
||||
}
|
||||
|
||||
for item in values {
|
||||
let mut encoded = encode_data(parser, &*inner, &message_types, item, field_name)?;
|
||||
items.append(&mut encoded);
|
||||
}
|
||||
|
||||
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 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))?;
|
||||
tokens.append(&mut encoded);
|
||||
}
|
||||
|
||||
keccak(tokens).to_vec()
|
||||
}
|
||||
|
||||
Type::Bytes => {
|
||||
let string = value.as_str().ok_or_else(|| 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).to_vec();
|
||||
|
||||
encode(&[EthAbiToken::FixedBytes(bytes)])
|
||||
}
|
||||
|
||||
Type::Byte(_) => {
|
||||
let string = value.as_str().ok_or_else(|| serde_error("string", field_name))?;
|
||||
|
||||
check_hex(&string)?;
|
||||
|
||||
let mut bytes = (&string[2..])
|
||||
.from_hex::<Vec<u8>>()
|
||||
.map_err(|err| ErrorKind::HexParseError(format!("{}", err)))?;
|
||||
|
||||
encode(&[EthAbiToken::FixedBytes(bytes)])
|
||||
}
|
||||
|
||||
Type::String => {
|
||||
let value = value.as_str().ok_or_else(|| serde_error("string", field_name))?;
|
||||
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::Address => {
|
||||
let addr = value.as_str().ok_or_else(|| 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)))?;
|
||||
encode(&[EthAbiToken::Address(address)])
|
||||
}
|
||||
|
||||
Type::Uint | Type::Int => {
|
||||
let string = value.as_str().ok_or_else(|| serde_error("int/uint", field_name))?;
|
||||
|
||||
check_hex(&string)?;
|
||||
|
||||
let uint = U256::from_str(&string[2..]).map_err(|err| ErrorKind::HexParseError(format!("{}", err)))?;
|
||||
|
||||
let token = if *message_type == Type::Uint {
|
||||
EthAbiToken::Uint(uint)
|
||||
} else {
|
||||
EthAbiToken::Int(uint)
|
||||
};
|
||||
encode(&[token])
|
||||
}
|
||||
|
||||
_ => return Err(ErrorKind::UnknownType(format!("{}", field_name.unwrap_or("")), format!("{}", *message_type)))?
|
||||
};
|
||||
|
||||
Ok(encoded)
|
||||
}
|
||||
|
||||
/// encodes and hashes the given EIP712 struct
|
||||
pub fn hash_structured_data(typed_data: EIP712) -> Result<Vec<u8>> {
|
||||
// validate input
|
||||
typed_data.validate()?;
|
||||
// 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)?
|
||||
);
|
||||
let concat = [&prefix[..], &domain_hash[..], &data_hash[..]].concat();
|
||||
Ok(keccak(concat).to_vec())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::from_str;
|
||||
use rustc_hex::ToHex;
|
||||
|
||||
const JSON: &'static str = r#"{
|
||||
"primaryType": "Mail",
|
||||
"domain": {
|
||||
"name": "Ether Mail",
|
||||
"version": "1",
|
||||
"chainId": "0x1",
|
||||
"verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
|
||||
},
|
||||
"message": {
|
||||
"from": {
|
||||
"name": "Cow",
|
||||
"wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
|
||||
},
|
||||
"to": {
|
||||
"name": "Bob",
|
||||
"wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
|
||||
},
|
||||
"contents": "Hello, Bob!"
|
||||
},
|
||||
"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" }
|
||||
]
|
||||
}
|
||||
}"#;
|
||||
|
||||
#[test]
|
||||
fn test_build_dependencies() {
|
||||
let string = r#"{
|
||||
"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" }
|
||||
]
|
||||
}"#;
|
||||
|
||||
let value = from_str::<MessageTypes>(string).expect("alas error!");
|
||||
let mail = "Mail";
|
||||
let person = "Person";
|
||||
|
||||
let hashset = {
|
||||
let mut temp = HashSet::new();
|
||||
temp.insert(mail);
|
||||
temp.insert(person);
|
||||
temp
|
||||
};
|
||||
assert_eq!(build_dependencies(mail, &value), Some(hashset));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_type() {
|
||||
let string = r#"{
|
||||
"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" }
|
||||
]
|
||||
}"#;
|
||||
|
||||
let value = from_str::<MessageTypes>(string).expect("alas error!");
|
||||
let mail = &String::from("Mail");
|
||||
assert_eq!(
|
||||
"Mail(Person from,Person to,string contents)Person(string name,address wallet)",
|
||||
encode_type(&mail, &value).expect("alas error!")
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_type_hash() {
|
||||
let string = r#"{
|
||||
"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" }
|
||||
]
|
||||
}"#;
|
||||
|
||||
let value = from_str::<MessageTypes>(string).expect("alas error!");
|
||||
let mail = &String::from("Mail");
|
||||
let hash = (type_hash(&mail, &value).expect("alas error!").0).to_hex::<String>();
|
||||
assert_eq!(
|
||||
hash,
|
||||
"a0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash_data() {
|
||||
let typed_data = from_str::<EIP712>(JSON).expect("alas error!");
|
||||
assert_eq!(
|
||||
hash_structured_data(typed_data).expect("alas error!").to_hex::<String>(),
|
||||
"be609aee343fb3c4b28e1df9e632fca64fcfaede20f02e86244efddf30957bd2"
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unequal_array_lengths() {
|
||||
const TEST: &'static str = r#"{
|
||||
"primaryType": "Mail",
|
||||
"domain": {
|
||||
"name": "Ether Mail",
|
||||
"version": "1",
|
||||
"chainId": "0x1",
|
||||
"verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
|
||||
},
|
||||
"message": {
|
||||
"from": {
|
||||
"name": "Cow",
|
||||
"wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
|
||||
},
|
||||
"to": [{
|
||||
"name": "Bob",
|
||||
"wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
|
||||
}],
|
||||
"contents": "Hello, Bob!"
|
||||
},
|
||||
"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[2]" },
|
||||
{ "name": "contents", "type": "string" }
|
||||
]
|
||||
}
|
||||
}"#;
|
||||
|
||||
let typed_data = from_str::<EIP712>(TEST).expect("alas error!");
|
||||
assert_eq!(
|
||||
hash_structured_data(typed_data).unwrap_err().kind(),
|
||||
ErrorKind::UnequalArrayItems(2, "Person[2]".into(), 1)
|
||||
)
|
||||
}
|
||||
}
|
125
util/EIP-712/src/error.rs
Normal file
125
util/EIP-712/src/error.rs
Normal file
@ -0,0 +1,125 @@
|
||||
// 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/>.
|
||||
|
||||
use std::fmt::{self, Display};
|
||||
use failure::{Fail, Context, Backtrace};
|
||||
use validator::ValidationErrors;
|
||||
use validator::ValidationErrorsKind;
|
||||
|
||||
pub(crate) type Result<T> = ::std::result::Result<T, Error>;
|
||||
/// Error type
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
inner: Context<ErrorKind>,
|
||||
}
|
||||
/// Possible errors encountered while hashing/encoding an EIP-712 compliant data structure
|
||||
#[derive(Clone, Fail, Debug, PartialEq)]
|
||||
pub enum ErrorKind {
|
||||
/// if we fail to deserialize from a serde::Value as a type specified in message types
|
||||
/// fail with this error.
|
||||
#[fail(display = "Expected type '{}' for field '{}'", _0, _1)]
|
||||
UnexpectedType(String, String),
|
||||
/// the primary type supplied doesn't exist in the MessageTypes
|
||||
#[fail(display = "The given primaryType wasn't found in the types field")]
|
||||
NonExistentType,
|
||||
/// an invalid address was encountered during encoding
|
||||
#[fail(display = "Address string should be a 0x-prefixed 40 character string, got '{}'", _0)]
|
||||
InvalidAddressLength(usize),
|
||||
/// a hex parse error occured
|
||||
#[fail(display = "Failed to parse hex '{}'", _0)]
|
||||
HexParseError(String),
|
||||
/// the field was declared with a unknown type
|
||||
#[fail(display = "The field '{}' has an unknown type '{}'", _0, _1)]
|
||||
UnknownType(String, String),
|
||||
/// Unexpected token
|
||||
#[fail(display = "Unexpected token '{}' while parsing typename '{}'", _0, _1)]
|
||||
UnexpectedToken(String, String),
|
||||
/// the user has attempted to define a typed array with a depth > 10
|
||||
#[fail(display = "Maximum depth for nested arrays is 10")]
|
||||
UnsupportedArrayDepth,
|
||||
/// FieldType validation error
|
||||
#[fail(display = "{}", _0)]
|
||||
ValidationError(String),
|
||||
/// the typed array defined in message types was declared with a fixed length
|
||||
/// that is of unequal length with the items to be encoded
|
||||
#[fail(display = "Expected {} items for array type {}, got {} items", _0, _1, _2)]
|
||||
UnequalArrayItems(u64, String, u64),
|
||||
/// Typed array length doesn't fit into a u64
|
||||
#[fail(display = "Attempted to declare fixed size with length {}", _0)]
|
||||
InvalidArraySize(String)
|
||||
}
|
||||
|
||||
pub(crate) fn serde_error(expected: &str, field: Option<&str>) -> ErrorKind {
|
||||
ErrorKind::UnexpectedType(expected.to_owned(), field.unwrap_or("").to_owned())
|
||||
}
|
||||
|
||||
impl Fail for Error {
|
||||
fn cause(&self) -> Option<&Fail> {
|
||||
self.inner.cause()
|
||||
}
|
||||
|
||||
fn backtrace(&self) -> Option<&Backtrace> {
|
||||
self.inner.backtrace()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
Display::fmt(&self.inner, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// extract the error kind
|
||||
pub fn kind(&self) -> ErrorKind {
|
||||
self.inner.get_context().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ErrorKind> for Error {
|
||||
fn from(kind: ErrorKind) -> Error {
|
||||
Error { inner: Context::new(kind) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Context<ErrorKind>> for Error {
|
||||
fn from(inner: Context<ErrorKind>) -> Error {
|
||||
Error { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ValidationErrors> for Error {
|
||||
fn from(error: ValidationErrors) -> Self {
|
||||
let mut string: String = "".into();
|
||||
for (field_name, error_kind) in error.errors() {
|
||||
match error_kind {
|
||||
ValidationErrorsKind::Field(validation_errors) => {
|
||||
for error in validation_errors {
|
||||
let str_error = format!(
|
||||
"the field '{}', has an invalid value {}",
|
||||
field_name,
|
||||
error.params["value"]
|
||||
);
|
||||
string.push_str(&str_error);
|
||||
}
|
||||
},
|
||||
_ => unreachable!("#[validate] is only used on fields for regex;\
|
||||
its impossible to get any other ErrorKind; qed")
|
||||
}
|
||||
}
|
||||
ErrorKind::ValidationError(string).into()
|
||||
}
|
||||
}
|
192
util/EIP-712/src/lib.rs
Normal file
192
util/EIP-712/src/lib.rs
Normal file
@ -0,0 +1,192 @@
|
||||
// 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-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;
|
||||
extern crate ethabi;
|
||||
extern crate ethereum_types;
|
||||
extern crate keccak_hash;
|
||||
extern crate itertools;
|
||||
extern crate failure;
|
||||
extern crate indexmap;
|
||||
extern crate lunarity_lexer;
|
||||
extern crate toolshed;
|
||||
extern crate regex;
|
||||
extern crate validator;
|
||||
#[macro_use]
|
||||
extern crate validator_derive;
|
||||
#[macro_use]
|
||||
extern crate failure_derive;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
extern crate rustc_hex;
|
||||
|
||||
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;
|
172
util/EIP-712/src/parser.rs
Normal file
172
util/EIP-712/src/parser.rs
Normal file
@ -0,0 +1,172 @@
|
||||
// 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/>.
|
||||
|
||||
//! Solidity type-name parsing
|
||||
use lunarity_lexer::{Lexer, Token};
|
||||
use error::*;
|
||||
use toolshed::Arena;
|
||||
use std::{fmt, result};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Type {
|
||||
Address,
|
||||
Uint,
|
||||
Int,
|
||||
String,
|
||||
Bool,
|
||||
Bytes,
|
||||
Byte(u8),
|
||||
Custom(String),
|
||||
Array {
|
||||
length: Option<u64>,
|
||||
inner: Box<Type>
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Type> for String {
|
||||
fn from(field_type: Type) -> String {
|
||||
match field_type {
|
||||
Type::Address => "address".into(),
|
||||
Type::Uint => "uint".into(),
|
||||
Type::Int => "int".into(),
|
||||
Type::String => "string".into(),
|
||||
Type::Bool => "bool".into(),
|
||||
Type::Bytes => "bytes".into(),
|
||||
Type::Byte(len) => format!("bytes{}", len),
|
||||
Type::Custom(custom) => custom,
|
||||
Type::Array {
|
||||
inner,
|
||||
length
|
||||
} => {
|
||||
let inner: String = (*inner).into();
|
||||
match length {
|
||||
None => format!("{}[]", inner),
|
||||
Some(length) => format!("{}[{}]", inner, length)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Type {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
|
||||
let item: String = self.clone().into();
|
||||
write!(f, "{}", item)
|
||||
}
|
||||
}
|
||||
|
||||
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(&self, field_type: &str) -> Result<Type> {
|
||||
#[derive(PartialEq)]
|
||||
enum State { Open, Close }
|
||||
|
||||
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();
|
||||
continue;
|
||||
},
|
||||
Token::BracketOpen if token.is_some() && state == State::Close => {
|
||||
state = State::Open;
|
||||
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.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_);
|
||||
lexer.consume();
|
||||
}
|
||||
|
||||
Ok(token.ok_or_else(|| ErrorKind::NonExistentType)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parser() {
|
||||
let parser = Parser::new();
|
||||
let source = "byte[][][7][][][][][][][]";
|
||||
parser.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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_malformed_array_type() {
|
||||
let parser = Parser::new();
|
||||
let source = "byte[7[]uint][]";
|
||||
assert_eq!(parser.parse_type(source).is_err(), true)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user