Merge pull request #114 from paritytech/snd-issue-112

fix #112 - users were able to burn value during withdraw
This commit is contained in:
snd 2018-02-15 14:48:00 +01:00 committed by GitHub
commit 396198781d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 1777 additions and 1022 deletions

123
Cargo.lock generated
View File

@ -1,11 +1,3 @@
[[package]]
name = "aho-corasick"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "aho-corasick"
version = "0.6.4"
@ -65,8 +57,11 @@ dependencies = [
"ethabi 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethabi-contract 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethabi-derive 5.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"ethereum-types 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"pretty_assertions 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"quickcheck 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
@ -84,7 +79,7 @@ version = "0.1.0"
dependencies = [
"bridge 0.1.0",
"docopt 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
@ -132,7 +127,7 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -145,11 +140,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "env_logger"
version = "0.3.5"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -286,7 +281,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "hyper"
version = "0.11.17"
version = "0.11.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"base64 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -302,7 +297,7 @@ dependencies = [
"relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -394,14 +389,6 @@ dependencies = [
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "memchr"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "memchr"
version = "2.0.1"
@ -508,16 +495,16 @@ version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot_core 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot_core 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parking_lot_core"
version = "0.2.10"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -535,6 +522,16 @@ dependencies = [
"difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quickcheck"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "0.3.15"
@ -567,19 +564,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "regex"
version = "0.1.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex"
version = "0.2.5"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
@ -589,11 +574,6 @@ dependencies = [
"utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex-syntax"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "regex-syntax"
version = "0.4.2"
@ -759,30 +739,16 @@ name = "tests"
version = "0.1.0"
dependencies = [
"bridge 0.1.0",
"ethabi 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethereum-types 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 8.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"pretty_assertions 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"web3 0.2.0 (git+https://github.com/tomusdrw/rust-web3?branch=bridge)",
]
[[package]]
name = "thread-id"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thread_local"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thread_local"
version = "0.3.5"
@ -819,17 +785,17 @@ dependencies = [
"mio 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-io"
version = "0.1.4"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -845,7 +811,7 @@ dependencies = [
"smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -879,7 +845,7 @@ dependencies = [
"mio 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"mio-uds 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -927,11 +893,6 @@ dependencies = [
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "utf8-ranges"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "utf8-ranges"
version = "1.0.0"
@ -957,7 +918,7 @@ dependencies = [
"ethabi 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethereum-types 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.11.17 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.11.18 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 8.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -966,7 +927,7 @@ dependencies = [
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-uds 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1010,7 +971,6 @@ dependencies = [
]
[metadata]
"checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66"
"checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4"
"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef"
"checksum backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbbf59b1c43eefa8c3ede390fcc36820b4999f7914104015be25025e0d62af2"
@ -1025,7 +985,7 @@ dependencies = [
"checksum difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3304d19798a8e067e48d8e69b2c37f0b5e9b4e462504ad9e27e9f3fce02bba8"
"checksum docopt 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d8acd393692c503b168471874953a2531df0e9ab77d0b6bbc582395743300a4a"
"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
"checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f"
"checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b"
"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
"checksum ethabi 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d8385a48c8ed984778dcca27efc0de162a191a14ed733a41a07d9b0cfaa999e"
"checksum ethabi-contract 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca2263c24359e827348ac99aa1f2e28ba5bab0d6c0b83941fa252de8a9e9c073"
@ -1041,7 +1001,7 @@ dependencies = [
"checksum heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1679e6ea370dee694f91f1dc469bf94cf8f52051d147aec3e1f9497c6fc22461"
"checksum heck 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e0db42a2924a5d7d628685e7a8cf9a2edd628650a9d01efc3dde35d3cdd22451"
"checksum httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2f407128745b78abc95c0ffbe4e5d37427fdc0d45470710cfef8c44522a2e37"
"checksum hyper 0.11.17 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4de6edd503089841ebfa88341e1c00fb19b6bf93d820d908db15960fd31226"
"checksum hyper 0.11.18 (registry+https://github.com/rust-lang/crates.io-index)" = "c4f9b276c87e3fc1902a8bdfcce264c3f7c8a1c35e5e0c946062739f55026664"
"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08"
"checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c"
"checksum jsonrpc-core 8.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ddf83704f4e79979a424d1082dd2c1e52683058056c9280efa19ac5f6bc9033c"
@ -1052,7 +1012,6 @@ dependencies = [
"checksum libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1e5d97d6708edaa407429faa671b942dc0f2727222fb6b6539bf1db936e4b121"
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
"checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2"
"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
"checksum mime 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e2e00e17be181010a91dbfefb01660b17311059dc8c7f48b9017677721e732bd"
"checksum mio 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "7da01a5e23070d92d99b1ecd1cd0af36447c6fd44b0fe283c2db199fa136724f"
@ -1065,16 +1024,15 @@ dependencies = [
"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30"
"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
"checksum parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3e7f7c9857874e54afeb950eebeae662b1e51a2493666d2ea4c0a5d91dcf0412"
"checksum parking_lot_core 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "9f35048d735bb93dd115a0030498785971aab3234d311fbe273d020084d26bd8"
"checksum parking_lot_core 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8c47785371ae3ca397fe9eb2350e5a3ac5cfd7d329f3d9ea8375e39f1a55f377"
"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
"checksum pretty_assertions 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1d510007841e87c7a6d829a36f7f0acb72aef12e38cc89073fe39810c1d976ac"
"checksum quickcheck 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "15cda2c09a11b72e8563c57760dd5cf8b1fb3bb595df5759b5653048ab04e030"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1"
"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
"checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd"
"checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f"
"checksum regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "744554e01ccbd98fff8c457c3b092cd67af62a555a43bfe97ae8a0451f7799fa"
"checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957"
"checksum regex 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "5be5347bde0c48cfd8c3fdc0766cdfe9d8a755ef84d620d6794c778c91de8b2b"
"checksum regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8e931c58b93d86f080c734bfd2bce7dd0079ae2331235818133c8be7f422e20e"
"checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a"
"checksum remove_dir_all 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b5d2f806b0fcdabd98acd380dc8daef485e22bcb7cddc811d1337967f2528cf5"
@ -1099,13 +1057,11 @@ dependencies = [
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
"checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5"
"checksum tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f73eebdb68c14bcb24aef74ea96079830e7fa7b31a6106e42ea7ee887c1e134e"
"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
"checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5"
"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963"
"checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098"
"checksum tiny-keccak 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e9241752647ca572f12c9b520a5d360d9099360c527770647e694001646a1d0"
"checksum tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "52b4e32d8edbf29501aabb3570f027c6ceb00ccef6538f4bddba0200503e74e8"
"checksum tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "514aae203178929dbf03318ad7c683126672d4d96eccb77b29603d33c9e25743"
"checksum tokio-io 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b9532748772222bf70297ec0e2ad0f17213b4a7dd0e6afb68e0a0768f69f4e4f"
"checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389"
"checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162"
"checksum tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6131e780037787ff1b3f8aad9da83bca02438b72277850dd6ad0d455e0e20efc"
@ -1116,7 +1072,6 @@ dependencies = [
"checksum unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8083c594e02b8ae1654ae26f0ade5158b119bd88ad0e8227a5d8fcd72407946"
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
"checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f"
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
"checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"

View File

@ -47,7 +47,7 @@ they get the same amount of ERC20 tokens on `foreign`.
[they can use `ForeignBridge` as they would use any ERC20 token.](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md)
to convert their `foreign` ERC20 into ether on `home`
users can always call `ForeignBridge.transferHomeViaRelay(homeRecipientAddress, value)`.
users can always call `ForeignBridge.transferHomeViaRelay(homeRecipientAddress, value, homeGasPrice)`.
`foreign` is assumed to use PoA (proof of authority) consensus.
relays between the chains happen in a byzantine fault tolerant way using the authorities of `foreign`.
@ -66,8 +66,8 @@ with identical arguments and from distinct authorities then
### high level explanation of foreign ERC20 -> home ether relay
`sender` executes `ForeignBridge.transferHomeViaRelay(recipient, value)`
which checks and reduces `ForeignBridge.balances(sender)` by `value` and emits `ForeignBridge.Withdraw(recipient, value)`.
`sender` executes `ForeignBridge.transferHomeViaRelay(recipient, value, homeGasPrice)`
which checks and reduces `ForeignBridge.balances(sender)` by `value` and emits `ForeignBridge.Withdraw(recipient, value, homeGasPrice)`.
for every `ForeignBridge.Withdraw`, every bridge authority creates a message containing
`value`, `recipient` and the `transactionHash` of the transaction referenced by the `ForeignBridge.Withdraw` event;
@ -273,7 +273,7 @@ pay for the gas.
this opened up an attack where a malicious user could
deposit a very small amount of wei on `HomeBridge`, get it relayed to `ForeignBridge`,
then spam `ForeignBridge.transfer` with `1` wei withdraws.
then spam `ForeignBridge.transferHomeViaRelay` with `1` wei withdraws.
it would cost the attacker very little `home` chain wei and essentially
free `foreign` testnet wei to cause the authorities to spend orders of magnitude more wei
to relay the withdraw to `home` by executing `HomeBridge.withdraw`.
@ -284,9 +284,13 @@ to shut down this attack `HomeBridge.withdraw` was modified so
doing the relay.
this way the `recipient` pays the relaying `authority` for the execution of the `withdraw` transaction.
if the value withdrawn is too low to pay for the relay at current gas prices then
bridge authorities will ignore it. one can think of it as value getting
spent entirely on paying the relay with no value left to pay out the recipient.
relayers can set the gas price for `HomeBridge.withdraw`.
they could set a very high gas price resulting in a very high `cost` through which they could burn large portions of `value`.
to shut down this attack the `homeGasPrice` param was added to `ForeignBridge.transferHomeViaRelay`.
end users have control over the cost/latency tradeoff of their relay transaction through the `homeGasPrice`.
relayers have to set gas price to `homeGasPrice` when calling `HomeBridge.withdraw`.
the `recipient` for `value` is the exception and can freely choose any gas price.
see https://github.com/paritytech/parity-bridge/issues/112 for more details.
`HomeBridge.withdraw` is currently the only transaction bridge authorities execute on `home`.
care must be taken to secure future functions that bridge authorities will execute

View File

@ -18,6 +18,9 @@ ethabi-derive = "5.0"
ethabi-contract = "5.0"
rustc-hex = "1.0"
log = "0.3"
ethereum-types = "0.2"
pretty_assertions = "0.2.1"
[dev-dependencies]
tempdir = "0.3"
quickcheck = "0.6.1"

View File

@ -52,6 +52,7 @@ impl<T: Transport + Clone> Future for Deploy<T> {
self.app.config.foreign.contract.bin.clone().0,
self.app.config.authorities.required_signatures,
self.app.config.authorities.accounts.clone(),
self.app.config.estimated_gas_cost_of_withdraw
);
let main_tx_request = TransactionRequest {

View File

@ -3,39 +3,24 @@ use std::ops;
use futures::{Future, Stream, Poll};
use futures::future::{JoinAll, join_all};
use tokio_timer::Timeout;
use ethabi::RawLog;
use web3::Transport;
use web3::types::{H256, H520, Address, TransactionRequest, Log, Bytes, FilterBuilder};
use web3::types::{H256, H520, Address, TransactionRequest, Bytes, FilterBuilder};
use api::{self, LogStream, ApiCall};
use app::App;
use contracts::foreign;
use util::web3_filter;
use database::Database;
use error::Error;
use message_to_mainnet::{MessageToMainnet, MESSAGE_LENGTH};
fn withdraws_filter(foreign: &foreign::ForeignBridge, address: Address) -> FilterBuilder {
let filter = foreign.events().withdraw().create_filter();
web3_filter(filter, address)
}
fn withdraw_confirm_sign_payload(foreign: &foreign::ForeignBridge, log: Log) -> Result<Bytes, Error> {
let raw_log = RawLog {
topics: log.topics,
data: log.data.0,
};
let withdraw_log = foreign.events().withdraw().parse_log(raw_log)?;
let hash = log.transaction_hash.expect("log to be mined and contain `transaction_hash`");
let mut result = vec![0u8; 84];
result[0..20].copy_from_slice(&withdraw_log.recipient);
result[20..52].copy_from_slice(&H256::from(withdraw_log.value));
result[52..84].copy_from_slice(&hash);
Ok(result.into())
}
fn withdraw_submit_signature_payload(foreign: &foreign::ForeignBridge, withdraw_message: Bytes, signature: H520) -> Bytes {
assert_eq!(signature.0.len(), 65);
assert_eq!(withdraw_message.0.len(), 84, "ForeignBridge never accepts messages with len != 84 bytes; qed");
foreign.functions().submit_signature().input(signature.0.to_vec(), withdraw_message.0).into()
fn withdraw_submit_signature_payload(foreign: &foreign::ForeignBridge, withdraw_message: Vec<u8>, signature: H520) -> Bytes {
assert_eq!(withdraw_message.len(), MESSAGE_LENGTH, "ForeignBridge never accepts messages with len != {} bytes; qed", MESSAGE_LENGTH);
foreign.functions().submit_signature().input(signature.0.to_vec(), withdraw_message).into()
}
/// State of withdraw confirmation.
@ -44,7 +29,7 @@ enum WithdrawConfirmState<T: Transport> {
Wait,
/// Signing withdraws.
SignWithdraws {
withdraws: Vec<Bytes>,
messages: Vec<Vec<u8>>,
future: JoinAll<Vec<Timeout<ApiCall<H520, T::Out>>>>,
block: u64,
},
@ -91,19 +76,19 @@ impl<T: Transport> Stream for WithdrawConfirm<T> {
WithdrawConfirmState::Wait => {
let item = try_stream!(self.logs.poll());
info!("got {} new withdraws to sign", item.logs.len());
let withdraws = item.logs
let withdraw_messages = item.logs
.into_iter()
.map(|log| {
info!("withdraw is ready for signature submission. tx hash {}", log.transaction_hash.unwrap());
withdraw_confirm_sign_payload(&self.app.foreign_bridge, log)
Ok(MessageToMainnet::from_log(log)?.to_bytes())
})
.collect::<Result<Vec<_>, _>>()?;
.collect::<Result<Vec<_>, Error>>()?;
let requests = withdraws.clone()
let requests = withdraw_messages.clone()
.into_iter()
.map(|bytes| {
.map(|message| {
self.app.timer.timeout(
api::sign(&self.app.connections.foreign, self.app.config.foreign.account, bytes),
api::sign(&self.app.connections.foreign, self.app.config.foreign.account, Bytes(message)),
self.app.config.foreign.request_timeout)
})
.collect::<Vec<_>>();
@ -111,20 +96,22 @@ impl<T: Transport> Stream for WithdrawConfirm<T> {
info!("signing");
WithdrawConfirmState::SignWithdraws {
future: join_all(requests),
withdraws: withdraws,
messages: withdraw_messages,
block: item.to,
}
},
WithdrawConfirmState::SignWithdraws { ref mut future, ref mut withdraws, block } => {
WithdrawConfirmState::SignWithdraws { ref mut future, ref mut messages, block } => {
let signatures = try_ready!(future.poll());
info!("signing complete");
// borrow checker...
let app = &self.app;
let foreign_contract = &self.foreign_contract;
let confirmations = withdraws
let confirmations = messages
.drain(ops::RangeFull)
.zip(signatures.into_iter())
.map(|(withdraw_message, signature)| withdraw_submit_signature_payload(&app.foreign_bridge, withdraw_message, signature))
.map(|(withdraw_message, signature)| {
withdraw_submit_signature_payload(&app.foreign_bridge, withdraw_message, signature)
})
.map(|payload| TransactionRequest {
from: app.config.foreign.account,
to: Some(foreign_contract.clone()),
@ -166,40 +153,3 @@ impl<T: Transport> Stream for WithdrawConfirm<T> {
}
}
}
#[cfg(test)]
mod tests {
use rustc_hex::FromHex;
use web3::types::{Log, Bytes};
use contracts::foreign;
use super::{withdraw_confirm_sign_payload, withdraw_submit_signature_payload};
#[test]
fn test_withdraw_confirm_sign_payload() {
let foreign = foreign::ForeignBridge::default();
let data = "000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0".from_hex().unwrap();
let log = Log {
data: data.into(),
topics: vec!["884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364".into()],
transaction_hash: Some("884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364".into()),
..Default::default()
};
let payload = withdraw_confirm_sign_payload(&foreign, log).unwrap();
let expected: Bytes = "aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364".from_hex().unwrap().into();
assert_eq!(expected, payload);
}
#[test]
fn test_withdraw_submit_signature_payload() {
let foreign = foreign::ForeignBridge::default();
let message: Bytes = "aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364".from_hex().unwrap().into();
let signature = "8697c15331677e6ebccccaff3454fce5edbc8cca8697c15331677aff3454fce5edbc8cca8697c15331677e6ebccccaff3454fce5edbc8cca8697c15331677e6ebc".into();
let payload = withdraw_submit_signature_payload(&foreign, message, signature);
let expected: Bytes = "630cea8e000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000418697c15331677e6ebccccaff3454fce5edbc8cca8697c15331677aff3454fce5edbc8cca8697c15331677e6ebccccaff3454fce5edbc8cca8697c15331677e6ebc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364000000000000000000000000".from_hex().unwrap().into();
assert_eq!(expected, payload);
}
}

View File

@ -3,15 +3,16 @@ use futures::{Future, Stream, Poll};
use futures::future::{JoinAll, join_all, Join};
use tokio_timer::Timeout;
use web3::Transport;
use web3::types::{U256, H256, Address, FilterBuilder, Log, Bytes, TransactionRequest};
use web3::types::{H256, Address, FilterBuilder, Log, Bytes, TransactionRequest};
use ethabi::{RawLog, self};
use app::App;
use api::{self, LogStream, ApiCall};
use contracts::{home, foreign};
use contracts::foreign;
use util::web3_filter;
use database::Database;
use error::{self, Error};
use rustc_hex::ToHex;
use message_to_mainnet::MessageToMainnet;
use signature::Signature;
/// returns a filter for `ForeignBridge.CollectedSignatures` events
fn collected_signatures_filter(foreign: &foreign::ForeignBridge, address: Address) -> FilterBuilder {
@ -55,38 +56,6 @@ fn signatures_payload(foreign: &foreign::ForeignBridge, required_signatures: u32
}))
}
/// returns the payload for a call to `HomeBridge.isMessageValueSufficientToCoverRelay(message)`
/// for the given `message`
fn message_value_sufficient_payload(home: &home::HomeBridge, message: &Bytes) -> Bytes {
assert_eq!(message.0.len(), 84, "ForeignBridge never accepts messages with len != 84 bytes; qed");
home
.functions()
.is_message_value_sufficient_to_cover_relay()
.input(message.0.clone()).into()
}
/// returns the payload for a transaction to `HomeBridge.withdraw(r, s, v, message)`
/// for the given `signatures` (r, s, v) and `message`
fn withdraw_relay_payload(home: &home::HomeBridge, signatures: &[Bytes], message: &Bytes) -> Bytes {
assert_eq!(message.0.len(), 84, "ForeignBridge never accepts messages with len != 84 bytes; qed");
let mut v_vec = Vec::new();
let mut r_vec = Vec::new();
let mut s_vec = Vec::new();
for signature in signatures {
assert_eq!(signature.0.len(), 65, "ForeignBridge never accepts signatures with len != 65 bytes; qed");
let mut r = [0u8; 32];
let mut s= [0u8; 32];
let mut v = [0u8; 32];
r.copy_from_slice(&signature.0[0..32]);
s.copy_from_slice(&signature.0[32..64]);
v[31] = signature.0[64];
v_vec.push(v);
s_vec.push(s);
r_vec.push(r);
}
home.functions().withdraw().input(v_vec, r_vec, s_vec, message.0.clone()).into()
}
/// state of the withdraw relay state machine
pub enum WithdrawRelayState<T: Transport> {
Wait,
@ -97,12 +66,6 @@ pub enum WithdrawRelayState<T: Transport> {
>,
block: u64,
},
FetchMessageValueSufficient {
future: JoinAll<Vec<Timeout<ApiCall<Bytes, T::Out>>>>,
messages: Vec<Bytes>,
signatures: Vec<Vec<Bytes>>,
block: u64,
},
RelayWithdraws {
future: JoinAll<Vec<Timeout<ApiCall<H256, T::Out>>>>,
block: u64,
@ -211,82 +174,43 @@ impl<T: Transport> Stream for WithdrawRelay<T> {
.map(|signatures|
signatures.iter().map(
|signature| {
app.foreign_bridge.functions().signature().output(signature.0.as_slice()).map(Bytes)
Signature::from_bytes(
app.foreign_bridge
.functions()
.signature()
.output(signature.0.as_slice())?
.as_slice())
}
)
.collect::<ethabi::Result<Vec<_>>>()
.collect::<Result<Vec<_>, Error>>()
.map_err(error::Error::from)
)
.collect::<error::Result<Vec<_>>>()?;
let message_value_sufficient_payloads = messages
.iter()
.map(|message| {
message_value_sufficient_payload(
&app.home_bridge,
message
)
})
.map(|payload| {
app.timer.timeout(
api::call(&app.connections.home, home_contract.clone(), payload),
app.config.home.request_timeout)
})
.collect::<Vec<_>>();
info!("fetching whether message values are sufficent");
WithdrawRelayState::FetchMessageValueSufficient {
future: join_all(message_value_sufficient_payloads),
messages,
signatures,
block,
}
},
WithdrawRelayState::FetchMessageValueSufficient {
ref mut future,
ref messages,
ref signatures,
block
} => {
let message_value_sufficient = try_ready!(future.poll());
info!("fetching whether message values are sufficent complete");
let app = &self.app;
let home_contract = &self.home_contract;
let relays = messages.into_iter()
.zip(signatures.into_iter())
.zip(message_value_sufficient.into_iter())
// ignore those messages that don't have sufficient
// value to pay for the relay gas cost
.filter(|&((ref message, _), ref is_message_value_sufficient)| {
// TODO [snd] this is ugly.
// in the future ethabi should return a bool
// for `is_message_value_sufficient`
// since the contract function returns a bool
let is_sufficient = U256::from(is_message_value_sufficient.0.as_slice()) == U256::from(1);
if !is_sufficient {
info!("value in message is not sufficient to cover relay costs. ignoring. message: {}", message.0.to_hex());
}
is_sufficient
})
.map(|((message, signatures), _)| withdraw_relay_payload(&app.home_bridge, &signatures, message))
.map(|payload| TransactionRequest {
from: app.config.home.account,
to: Some(home_contract.clone()),
gas: Some(app.config.txs.withdraw_relay.gas.into()),
gas_price: Some(app.config.txs.withdraw_relay.gas_price.into()),
value: None,
data: Some(payload),
nonce: None,
condition: None,
})
.map(|request| {
.map(|(message, signatures)| {
let payload: Bytes = app.home_bridge.functions().withdraw().input(
signatures.iter().map(|x| x.v),
signatures.iter().map(|x| x.r),
signatures.iter().map(|x| x.s),
message.clone().0).into();
let request = TransactionRequest {
from: app.config.home.account,
to: Some(home_contract.clone()),
gas: Some(app.config.txs.withdraw_relay.gas.into()),
gas_price: Some(MessageToMainnet::from_bytes(message.0.as_slice()).mainnet_gas_price),
value: None,
data: Some(payload),
nonce: None,
condition: None,
};
app.timer.timeout(
api::send_transaction(&app.connections.home, request),
app.config.home.request_timeout)
})
.collect::<Vec<_>>();
info!("relaying {} withdraws", relays.len());
WithdrawRelayState::RelayWithdraws {
future: join_all(relays),
@ -315,8 +239,8 @@ impl<T: Transport> Stream for WithdrawRelay<T> {
mod tests {
use rustc_hex::FromHex;
use web3::types::{Log, Bytes};
use contracts::{home, foreign};
use super::{signatures_payload, withdraw_relay_payload};
use contracts::foreign;
use super::signatures_payload;
#[test]
fn test_signatures_payload() {
@ -359,18 +283,4 @@ mod tests {
let assignment = signatures_payload(&foreign, 2, my_address, log).unwrap();
assert_eq!(None, assignment);
}
#[test]
fn test_withdraw_relay_payload() {
let home = home::HomeBridge::default();
let signatures: Vec<Bytes> = vec![
vec![0x11; 65].into(),
vec![0x22; 65].into(),
];
let message: Bytes = vec![0x33; 84].into();
let payload = withdraw_relay_payload(&home, &signatures, &message);
let expected: Bytes = "9ce318f6000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000002111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222220000000000000000000000000000000000000000000000000000000000000002111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222220000000000000000000000000000000000000000000000000000000000000054333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333000000000000000000000000".from_hex().unwrap().into();
assert_eq!(expected, payload);
}
}

View File

@ -18,6 +18,12 @@ extern crate ethabi_contract;
extern crate rustc_hex;
#[macro_use]
extern crate log;
extern crate ethereum_types;
#[macro_use]
extern crate pretty_assertions;
#[cfg(test)]
#[macro_use]
extern crate quickcheck;
#[macro_use]
mod macros;
@ -30,4 +36,5 @@ pub mod contracts;
pub mod database;
pub mod error;
pub mod util;
pub mod message_to_mainnet;
pub mod signature;

View File

@ -0,0 +1,108 @@
use ethereum_types::{Address, U256, H256};
use contracts::foreign::events::Withdraw;
use web3::types::Log;
use ethabi;
use error::Error;
/// the message that is relayed from side to main.
/// contains all the information required for the relay.
/// validators sign off on this message.
#[derive(PartialEq, Debug)]
pub struct MessageToMainnet {
pub recipient: Address,
pub value: U256,
pub sidenet_transaction_hash: H256,
pub mainnet_gas_price: U256,
}
/// length of a `MessageToMainnet.to_bytes()` in bytes
pub const MESSAGE_LENGTH: usize = 116;
impl MessageToMainnet {
/// parses message from a byte slice
pub fn from_bytes(bytes: &[u8]) -> Self {
assert_eq!(bytes.len(), MESSAGE_LENGTH);
Self {
recipient: bytes[0..20].into(),
value: U256::from_little_endian(&bytes[20..52]),
sidenet_transaction_hash: bytes[52..84].into(),
mainnet_gas_price: U256::from_little_endian(&bytes[84..MESSAGE_LENGTH]),
}
}
/// construct a message from a `Withdraw` event that was logged on `foreign`
pub fn from_log(web3_log: Log) -> Result<Self, Error> {
let ethabi_raw_log = ethabi::RawLog {
topics: web3_log.topics,
data: web3_log.data.0,
};
let withdraw_log = Withdraw::default().parse_log(ethabi_raw_log)?;
let hash = web3_log.transaction_hash.ok_or_else(|| "`log` must be mined and contain `transaction_hash`")?;
Ok(Self {
recipient: withdraw_log.recipient,
value: withdraw_log.value,
sidenet_transaction_hash: hash,
mainnet_gas_price: withdraw_log.home_gas_price,
})
}
/// serializes message to a byte vector.
/// mainly used to construct the message byte vector that is then signed
/// and passed to `ForeignBridge.submitSignature`
pub fn to_bytes(&self) -> Vec<u8> {
let mut result = vec![0u8; MESSAGE_LENGTH];
result[0..20].copy_from_slice(&self.recipient.0[..]);
self.value.to_little_endian(&mut result[20..52]);
result[52..84].copy_from_slice(&self.sidenet_transaction_hash.0[..]);
self.mainnet_gas_price.to_little_endian(&mut result[84..MESSAGE_LENGTH]);
return result;
}
/// serializes message to an ethabi payload
pub fn to_payload(&self) -> Vec<u8> {
ethabi::encode(&[ethabi::Token::Bytes(self.to_bytes())])
}
}
#[cfg(test)]
mod test {
use quickcheck::TestResult;
use super::*;
quickcheck! {
fn quickcheck_message_to_mainnet_roundtrips_to_bytes(
recipient_raw: Vec<u8>,
value_raw: u64,
sidenet_transaction_hash_raw: Vec<u8>,
mainnet_gas_price_raw: u64
) -> TestResult {
if recipient_raw.len() != 20 || sidenet_transaction_hash_raw.len() != 32 {
return TestResult::discard();
}
let recipient: Address = recipient_raw.as_slice().into();
let value: U256 = value_raw.into();
let sidenet_transaction_hash: H256 = sidenet_transaction_hash_raw.as_slice().into();
let mainnet_gas_price: U256 = mainnet_gas_price_raw.into();
let message = MessageToMainnet {
recipient,
value,
sidenet_transaction_hash,
mainnet_gas_price
};
let bytes = message.to_bytes();
assert_eq!(message, MessageToMainnet::from_bytes(bytes.as_slice()));
let payload = message.to_payload();
let mut tokens = ethabi::decode(&[ethabi::ParamType::Bytes], payload.as_slice())
.unwrap();
let decoded = tokens.pop().unwrap().to_bytes().unwrap();
assert_eq!(message, MessageToMainnet::from_bytes(decoded.as_slice()));
TestResult::passed()
}
}
}

77
bridge/src/signature.rs Normal file
View File

@ -0,0 +1,77 @@
/// ECDSA signatures:
/// conversion from/to byte vectors.
/// from/to v, r, s components.
use ethereum_types::H256;
use ethabi;
use error::Error;
pub const SIGNATURE_LENGTH: usize = 65;
/// an ECDSA signature consisting of `v`, `r` and `s`
#[derive(PartialEq, Debug)]
pub struct Signature {
pub v: u8,
pub r: H256,
pub s: H256,
}
impl Signature {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
if bytes.len() != SIGNATURE_LENGTH {
bail!("`bytes`.len() must be {}", SIGNATURE_LENGTH);
}
Ok(Self {
v: bytes[64],
r: bytes[0..32].into(),
s: bytes[32..64].into(),
})
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut result = vec![0u8; SIGNATURE_LENGTH];
result[0..32].copy_from_slice(&self.r.0[..]);
result[32..64].copy_from_slice(&self.s.0[..]);
result[64] = self.v;
return result;
}
pub fn to_payload(&self) -> Vec<u8> {
ethabi::encode(&[ethabi::Token::Bytes(self.to_bytes())])
}
}
#[cfg(test)]
mod test {
use quickcheck::TestResult;
use super::*;
quickcheck! {
fn quickcheck_signature_roundtrips(v: u8, r_raw: Vec<u8>, s_raw: Vec<u8>) -> TestResult {
if r_raw.len() != 32 || s_raw.len() != 32 {
return TestResult::discard();
}
let r: H256 = r_raw.as_slice().into();
let s: H256 = s_raw.as_slice().into();
let signature = Signature { v, r, s };
assert_eq!(v, signature.v);
assert_eq!(r, signature.r);
assert_eq!(s, signature.s);
let bytes = signature.to_bytes();
assert_eq!(signature, Signature::from_bytes(bytes.as_slice()).unwrap());
let payload = signature.to_payload();
let mut tokens = ethabi::decode(&[ethabi::ParamType::Bytes], payload.as_slice())
.unwrap();
let decoded = tokens.pop().unwrap().to_bytes().unwrap();
assert_eq!(signature, Signature::from_bytes(decoded.as_slice()).unwrap());
TestResult::passed()
}
}
}

View File

@ -14,5 +14,5 @@ serde_derive = "1.0"
tokio-core = "0.1.8"
docopt = "0.8.1"
log = "0.3"
env_logger = "0.3"
env_logger = "0.4"
futures = "0.1.14"

View File

@ -6,7 +6,7 @@ pragma solidity ^0.4.17;
library Helpers {
/// returns whether `array` contains `value`.
function addressArrayContains(address[] array, address value) internal pure returns (bool) {
for (uint i = 0; i < array.length; i++) {
for (uint256 i = 0; i < array.length; i++) {
if (array[i] == value) {
return true;
}
@ -16,10 +16,10 @@ library Helpers {
// returns the digits of `inputValue` as a string.
// example: `uintToString(12345678)` returns `"12345678"`
function uintToString(uint inputValue) internal pure returns (string) {
function uintToString(uint256 inputValue) internal pure returns (string) {
// figure out the length of the resulting string
uint length = 0;
uint currentValue = inputValue;
uint256 length = 0;
uint256 currentValue = inputValue;
do {
length++;
currentValue /= 10;
@ -27,7 +27,7 @@ library Helpers {
// allocate enough memory
bytes memory result = new bytes(length);
// construct the string backwards
uint i = length - 1;
uint256 i = length - 1;
currentValue = inputValue;
do {
result[i--] = byte(48 + currentValue % 10);
@ -35,6 +35,35 @@ library Helpers {
} while (currentValue != 0);
return string(result);
}
/// returns whether signatures (whose components are in `vs`, `rs`, `ss`)
/// contain `requiredSignatures` distinct correct signatures
/// where signer is in `allowed_signers`
/// that signed `message`
function hasEnoughValidSignatures(bytes message, uint8[] vs, bytes32[] rs, bytes32[] ss, address[] allowed_signers, uint256 requiredSignatures) internal pure returns (bool) {
// not enough signatures
if (vs.length < requiredSignatures) {
return false;
}
var hash = MessageSigning.hashMessage(message);
var encountered_addresses = new address[](allowed_signers.length);
for (uint256 i = 0; i < requiredSignatures; i++) {
var recovered_address = ecrecover(hash, vs[i], rs[i], ss[i]);
// only signatures by addresses in `addresses` are allowed
if (!addressArrayContains(allowed_signers, recovered_address)) {
return false;
}
// duplicate signatures are not allowed
if (addressArrayContains(encountered_addresses, recovered_address)) {
return false;
}
encountered_addresses[i] = recovered_address;
}
return true;
}
}
@ -47,6 +76,10 @@ library HelpersTest {
function uintToString(uint256 inputValue) public pure returns (string str) {
return Helpers.uintToString(inputValue);
}
function hasEnoughValidSignatures(bytes message, uint8[] vs, bytes32[] rs, bytes32[] ss, address[] addresses, uint256 requiredSignatures) public pure returns (bool) {
return Helpers.hasEnoughValidSignatures(message, vs, rs, ss, addresses, requiredSignatures);
}
}
@ -84,10 +117,11 @@ library MessageSigningTest {
library Message {
// layout of message :: bytes:
// offset 0: 32 bytes :: uint (little endian) - message length
// offset 0: 32 bytes :: uint256 (little endian) - message length
// offset 32: 20 bytes :: address - recipient address
// offset 52: 32 bytes :: uint (little endian) - value
// offset 52: 32 bytes :: uint256 (little endian) - value
// offset 84: 32 bytes :: bytes32 - transaction hash
// offset 116: 32 bytes :: uint256 (little endian) - home gas price
// bytes 1 to 32 are 0 because message length is stored as little endian.
// mload always reads 32 bytes.
@ -109,8 +143,8 @@ library Message {
return recipient;
}
function getValue(bytes message) internal pure returns (uint) {
uint value;
function getValue(bytes message) internal pure returns (uint256) {
uint256 value;
// solium-disable-next-line security/no-inline-assembly
assembly {
value := mload(add(message, 52))
@ -126,6 +160,15 @@ library Message {
}
return hash;
}
function getHomeGasPrice(bytes message) internal pure returns (uint256) {
uint256 gasPrice;
// solium-disable-next-line security/no-inline-assembly
assembly {
gasPrice := mload(add(message, 116))
}
return gasPrice;
}
}
@ -135,13 +178,17 @@ library MessageTest {
return Message.getRecipient(message);
}
function getValue(bytes message) public pure returns (uint) {
function getValue(bytes message) public pure returns (uint256) {
return Message.getValue(message);
}
function getTransactionHash(bytes message) public pure returns (bytes32) {
return Message.getTransactionHash(message);
}
function getHomeGasPrice(bytes message) public pure returns (uint256) {
return Message.getHomeGasPrice(message);
}
}
@ -149,14 +196,14 @@ contract HomeBridge {
/// Number of authorities signatures required to withdraw the money.
///
/// Must be lesser than number of authorities.
uint public requiredSignatures;
uint256 public requiredSignatures;
/// The gas cost of calling `HomeBridge.withdraw`.
///
/// Is subtracted from `value` on withdraw.
/// recipient pays the relaying authority for withdraw.
/// this shuts down attacks that exhaust authorities funds on home chain.
uint public estimatedGasCostOfWithdraw;
uint256 public estimatedGasCostOfWithdraw;
/// Contract authorities.
address[] public authorities;
@ -165,32 +212,16 @@ contract HomeBridge {
mapping (bytes32 => bool) withdraws;
/// Event created on money deposit.
event Deposit (address recipient, uint value);
event Deposit (address recipient, uint256 value);
/// Event created on money withdraw.
event Withdraw (address recipient, uint value);
/// Multisig authority validation
modifier allAuthorities(uint8[] v, bytes32[] r, bytes32[] s, bytes message) {
var hash = MessageSigning.hashMessage(message);
var used = new address[](requiredSignatures);
require(requiredSignatures <= v.length);
for (uint i = 0; i < requiredSignatures; i++) {
var a = ecrecover(hash, v[i], r[i], s[i]);
require(Helpers.addressArrayContains(authorities, a));
require(!Helpers.addressArrayContains(used, a));
used[i] = a;
}
_;
}
event Withdraw (address recipient, uint256 value);
/// Constructor.
function HomeBridge(
uint requiredSignaturesParam,
uint256 requiredSignaturesParam,
address[] authoritiesParam,
uint estimatedGasCostOfWithdrawParam
uint256 estimatedGasCostOfWithdrawParam
) public
{
require(requiredSignaturesParam != 0);
@ -205,31 +236,37 @@ contract HomeBridge {
Deposit(msg.sender, msg.value);
}
/// to be called by authorities to check
/// whether they withdraw message should be relayed or whether it
/// is too low to cover the cost of calling withdraw and can be ignored
function isMessageValueSufficientToCoverRelay(bytes message) public view returns (bool) {
return Message.getValue(message) > getWithdrawRelayCost();
}
/// final step of a withdraw.
/// checks that `requiredSignatures` `authorities` have signed of on the `message`.
/// then transfers `value` to `recipient` (both extracted from `message`).
/// see message library above for a breakdown of the `message` contents.
/// `vs`, `rs`, `ss` are the components of the signatures.
/// an upper bound to the cost of relaying a withdraw by calling HomeBridge.withdraw
function getWithdrawRelayCost() public view returns (uint) {
return estimatedGasCostOfWithdraw * tx.gasprice;
}
/// anyone can call this, provided they have the message and required signatures!
/// only the `authorities` can create these signatures.
/// `requiredSignatures` authorities can sign arbitrary `message`s
/// transfering any ether `value` out of this contract to `recipient`.
/// bridge users must trust a majority of `requiredSignatures` of the `authorities`.
function withdraw(uint8[] vs, bytes32[] rs, bytes32[] ss, bytes message) public {
require(message.length == 116);
// check that at least `requiredSignatures` `authorities` have signed `message`
require(Helpers.hasEnoughValidSignatures(message, vs, rs, ss, authorities, requiredSignatures));
/// Used to withdraw money from the contract.
///
/// message contains:
/// withdrawal recipient (bytes20)
/// withdrawal value (uint)
/// foreign transaction hash (bytes32) // to avoid transaction duplication
///
/// NOTE that anyone can call withdraw provided they have the message and required signatures!
function withdraw(uint8[] v, bytes32[] r, bytes32[] s, bytes message) public allAuthorities(v, r, s, message) {
require(message.length == 84);
address recipient = Message.getRecipient(message);
uint value = Message.getValue(message);
uint256 value = Message.getValue(message);
bytes32 hash = Message.getTransactionHash(message);
uint256 homeGasPrice = Message.getHomeGasPrice(message);
// if the recipient calls `withdraw` they can choose the gas price freely.
// if anyone else calls `withdraw` they have to use the gas price
// `homeGasPrice` specified by the user initiating the withdraw.
// this is a security mechanism designed to shut down
// malicious senders setting extremely high gas prices
// and effectively burning recipients withdrawn value.
// see https://github.com/paritytech/parity-bridge/issues/112
// for further explanation.
require((recipient == msg.sender) || (tx.gasprice == homeGasPrice));
// The following two statements guard against reentry into this function.
// Duplicated withdraw or reentry.
@ -237,15 +274,10 @@ contract HomeBridge {
// Order of operations below is critical to avoid TheDAO-like re-entry bug
withdraws[hash] = true;
// this fails if `value` is not even enough to cover the relay cost.
// Authorities simply IGNORE withdraws where `value` cant relay cost.
// Think of it as `value` getting burned entirely on the relay with no value left to pay out the recipient.
require(isMessageValueSufficientToCoverRelay(message));
uint estimatedWeiCostOfWithdraw = getWithdrawRelayCost();
uint256 estimatedWeiCostOfWithdraw = estimatedGasCostOfWithdraw * homeGasPrice;
// charge recipient for relay cost
uint valueRemainingAfterSubtractingCost = value - estimatedWeiCostOfWithdraw;
uint256 valueRemainingAfterSubtractingCost = value - estimatedWeiCostOfWithdraw;
// pay out recipient
recipient.transfer(valueRemainingAfterSubtractingCost);
@ -262,21 +294,21 @@ contract ForeignBridge {
// following is the part of ForeignBridge that implements an ERC20 token.
// ERC20 spec: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md
uint public totalSupply;
uint256 public totalSupply;
string public name = "ForeignBridge";
/// maps addresses to their token balances
mapping (address => uint) public balances;
mapping (address => uint256) public balances;
// owner of account approves the transfer of an amount by another account
mapping(address => mapping (address => uint)) allowed;
mapping(address => mapping (address => uint256)) allowed;
/// Event created on money transfer
event Transfer(address indexed from, address indexed to, uint tokens);
event Transfer(address indexed from, address indexed to, uint256 tokens);
// returns the ERC20 token balance of the given address
function balanceOf(address tokenOwner) public view returns (uint) {
function balanceOf(address tokenOwner) public view returns (uint256) {
return balances[tokenOwner];
}
@ -284,7 +316,7 @@ contract ForeignBridge {
///
/// does not affect `home` chain. does not do a relay.
/// as specificed in ERC20 this doesn't fail if tokens == 0.
function transfer(address recipient, uint tokens) public returns (bool) {
function transfer(address recipient, uint256 tokens) public returns (bool) {
require(balances[msg.sender] >= tokens);
// fails if there is an overflow
require(balances[recipient] + tokens >= balances[recipient]);
@ -301,11 +333,11 @@ contract ForeignBridge {
// created when `approve` is executed to mark that
// `tokenOwner` has approved `spender` to spend `tokens` of his tokens
event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
event Approval(address indexed tokenOwner, address indexed spender, uint256 tokens);
// allow `spender` to withdraw from your account, multiple times, up to the `tokens` amount.
// calling this function repeatedly overwrites the current allowance.
function approve(address spender, uint tokens) public returns (bool) {
function approve(address spender, uint256 tokens) public returns (bool) {
allowed[msg.sender][spender] = tokens;
Approval(msg.sender, spender, tokens);
return true;
@ -348,7 +380,9 @@ contract ForeignBridge {
/// Number of authorities signatures required to withdraw the money.
///
/// Must be less than number of authorities.
uint public requiredSignatures;
uint256 public requiredSignatures;
uint256 public estimatedGasCostOfWithdraw;
/// Contract authorities.
address[] public authorities;
@ -360,23 +394,25 @@ contract ForeignBridge {
mapping (bytes32 => SignaturesCollection) signatures;
/// triggered when relay of deposit from HomeBridge is complete
event Deposit(address recipient, uint value);
event Deposit(address recipient, uint256 value);
/// Event created on money withdraw.
event Withdraw(address recipient, uint value);
event Withdraw(address recipient, uint256 value, uint256 homeGasPrice);
/// Collected signatures which should be relayed to home chain.
event CollectedSignatures(address authorityResponsibleForRelay, bytes32 messageHash);
function ForeignBridge(
uint _requiredSignatures,
address[] _authorities
uint256 _requiredSignatures,
address[] _authorities,
uint256 _estimatedGasCostOfWithdraw
) public
{
require(_requiredSignatures != 0);
require(_requiredSignatures <= _authorities.length);
requiredSignatures = _requiredSignatures;
authorities = _authorities;
estimatedGasCostOfWithdraw = _estimatedGasCostOfWithdraw;
}
/// require that sender is an authority
@ -388,9 +424,9 @@ contract ForeignBridge {
/// Used to deposit money to the contract.
///
/// deposit recipient (bytes20)
/// deposit value (uint)
/// deposit value (uint256)
/// mainnet transaction hash (bytes32) // to avoid transaction duplication
function deposit(address recipient, uint value, bytes32 transactionHash) public onlyAuthority() {
function deposit(address recipient, uint256 value, bytes32 transactionHash) public onlyAuthority() {
// Protection from misbehaving authority
var hash = keccak256(recipient, value, transactionHash);
@ -420,11 +456,14 @@ contract ForeignBridge {
/// once `requiredSignatures` are collected a `CollectedSignatures` event will be emitted.
/// an authority will pick up `CollectedSignatures` an call `HomeBridge.withdraw`
/// which transfers `value - relayCost` to `recipient` completing the transfer.
function transferHomeViaRelay(address recipient, uint value) public {
function transferHomeViaRelay(address recipient, uint256 value, uint256 homeGasPrice) public {
require(balances[msg.sender] >= value);
// don't allow 0 value transfers to home
require(value > 0);
uint256 estimatedWeiCostOfWithdraw = estimatedGasCostOfWithdraw * homeGasPrice;
require(value > estimatedWeiCostOfWithdraw);
balances[msg.sender] -= value;
// burns tokens
totalSupply -= value;
@ -432,7 +471,7 @@ contract ForeignBridge {
// recommended by ERC20 (see implementation of `deposit` above)
// we trigger a Transfer event to `0x0` on token destruction
Transfer(msg.sender, 0x0, value);
Withdraw(recipient, value);
Withdraw(recipient, value, homeGasPrice);
}
/// Should be used as sync tool
@ -441,17 +480,16 @@ contract ForeignBridge {
///
/// for withdraw message contains:
/// withdrawal recipient (bytes20)
/// withdrawal value (uint)
/// withdrawal value (uint256)
/// foreign transaction hash (bytes32) // to avoid transaction duplication
function submitSignature(bytes signature, bytes message) public onlyAuthority() {
// Validate submited signatures
require(MessageSigning.recoverAddressFromSignedMessage(signature, message) == msg.sender);
// ensure that `signature` is really `message` signed by `msg.sender`
require(msg.sender == MessageSigning.recoverAddressFromSignedMessage(signature, message));
// Valid withdraw message must have 84 bytes
require(message.length == 84);
require(message.length == 116);
var hash = keccak256(message);
// Duplicated signatures
// each authority can only provide one signature per message
require(!Helpers.addressArrayContains(signatures[hash].signed, msg.sender));
signatures[hash].message = message;
signatures[hash].signed.push(msg.sender);
@ -464,7 +502,7 @@ contract ForeignBridge {
}
/// Get signature
function signature(bytes32 hash, uint index) public view returns (bytes) {
function signature(bytes32 hash, uint256 index) public view returns (bytes) {
return signatures[hash].signatures[index];
}

View File

@ -238,7 +238,8 @@ fn test_basic_deposit_then_withdraw() {
.transfer_home_via_relay()
.input(
Address::from(user_address),
U256::from(100000));
U256::from(100000),
U256::from(0));
let future = foreign_eth.send_transaction(web3::types::TransactionRequest{
from: address_from_str(user_address),
to: Some(address_from_str(foreign_contract_address)),

View File

@ -10,3 +10,6 @@ jsonrpc-core = "8.0"
web3 = { git = "https://github.com/tomusdrw/rust-web3", branch = "bridge" }
serde_json = "1.0"
pretty_assertions = "0.2.1"
ethabi = "5.0"
ethereum-types = "0.2"
rustc-hex = "1.0"

View File

@ -15,11 +15,11 @@ pub struct MockedRequest {
pub params: Vec<rpc::Value>,
}
impl From<(&'static str, &'static str)> for MockedRequest {
fn from(a: (&'static str, &'static str)) -> Self {
impl From<(&'static str, serde_json::Value)> for MockedRequest {
fn from(a: (&'static str, serde_json::Value)) -> Self {
MockedRequest {
method: a.0.to_owned(),
params: serde_json::from_str(a.1).unwrap(),
params: a.1.as_array().unwrap().clone()
}
}
}
@ -28,7 +28,7 @@ impl From<(&'static str, &'static str)> for MockedRequest {
pub struct MockedTransport {
pub requests: Cell<usize>,
pub expected_requests: Vec<MockedRequest>,
pub mocked_responses: Vec<&'static str>,
pub mocked_responses: Vec<serde_json::Value>,
}
impl Transport for MockedTransport {
@ -46,7 +46,7 @@ impl Transport for MockedTransport {
fn send(&self, _id: usize, _request: rpc::Call) -> web3::Result<rpc::Value> {
let response = self.mocked_responses.iter().nth(self.requests.get() - 1).expect("missing response");
let f = futures::finished(serde_json::from_str(response).expect("invalid response"));
let f = futures::finished(response.clone());
Box::new(f)
}
}
@ -157,6 +157,8 @@ macro_rules! test_app_stream {
let stream = $init_stream(app, &$db);
let res = stream.collect().wait();
assert_eq!($expected, res.unwrap());
assert_eq!(
home.expected_requests.len(),
home.requests.get(),
@ -172,8 +174,6 @@ macro_rules! test_app_stream {
foreign.expected_requests.len(),
foreign.requests.get()
);
assert_eq!($expected, res.unwrap());
}
}
}

View File

@ -1,10 +1,14 @@
extern crate futures;
#[macro_use]
extern crate serde_json;
extern crate bridge;
#[macro_use]
extern crate tests;
use bridge::bridge::create_deposit_relay;
const DEPOSIT_TOPIC: &str = "0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c";
test_app_stream! {
name => deposit_relay_basic,
database => Database::default(),
@ -25,17 +29,29 @@ test_app_stream! {
expected => vec![0x1005, 0x1006],
home_transport => [
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1011""#;
req => json!([]),
res => json!("0x1011");
"eth_getLogs" =>
req => r#"[{"address":["0x0000000000000000000000000000000000000000"],"fromBlock":"0x1","limit":null,"toBlock":"0x1005","topics":[["0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"],null,null,null]}]"#,
res => r#"[]"#;
req => json!([{
"address": ["0x0000000000000000000000000000000000000000"],
"fromBlock": "0x1",
"limit": null,
"toBlock": "0x1005",
"topics": [[DEPOSIT_TOPIC], null, null, null]
}]),
res => json!([]);
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1012""#;
req => json!([]),
res => json!("0x1012");
"eth_getLogs" =>
req => r#"[{"address":["0x0000000000000000000000000000000000000000"],"fromBlock":"0x1006","limit":null,"toBlock":"0x1006","topics":[["0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"],null,null,null]}]"#,
res => r#"[]"#;
req => json!([{
"address": ["0x0000000000000000000000000000000000000000"],
"fromBlock": "0x1006",
"limit": null,
"toBlock": "0x1006",
"topics": [[DEPOSIT_TOPIC], null, null, null]
}]),
res => json!([]);
],
foreign_transport => []
}
@ -63,22 +79,46 @@ test_app_stream! {
expected => vec![0x1005, 0x1006],
home_transport => [
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1011""#;
req => json!([]),
res => json!("0x1011");
"eth_getLogs" =>
req => r#"[{"address":["0x0000000000000000000000000000000000000000"],"fromBlock":"0x6","limit":null,"toBlock":"0x1005","topics":[["0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"],null,null,null]}]"#,
res => r#"[{"address":"0x0000000000000000000000000000000000000000","topics":["0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"],"data":"0x000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0","type":"","transactionHash":"0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"}]"#;
req => json!([{
"address": ["0x0000000000000000000000000000000000000000"],
"fromBlock": "0x6",
"limit": null,
"toBlock":"0x1005",
"topics": [[DEPOSIT_TOPIC], null, null, null]
}]),
res => json!([{
"address": "0x0000000000000000000000000000000000000000",
"topics": [DEPOSIT_TOPIC],
"data": "0x000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0",
"type": "",
"transactionHash": "0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"
}]);
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1012""#;
req => json!([]),
res => json!("0x1012");
"eth_getLogs" =>
req => r#"[{"address":["0x0000000000000000000000000000000000000000"],"fromBlock":"0x1006","limit":null,"toBlock":"0x1006","topics":[["0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"],null,null,null]}]"#,
res => r#"[]"#;
req => json!([{
"address": ["0x0000000000000000000000000000000000000000"],
"fromBlock": "0x1006",
"limit": null,
"toBlock": "0x1006",
"topics":[[DEPOSIT_TOPIC], null, null, null]
}]),
res => json!([]);
],
foreign_transport => [
"eth_sendTransaction" =>
req => r#"[{"data":"0x26b3293f000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364","from":"0x0000000000000000000000000000000000000001","gas":"0x0","gasPrice":"0x0","to":"0x0000000000000000000000000000000000000000"}]"#,
res => r#""0x1db8f385535c0d178b8f40016048f3a3cffee8f94e68978ea4b277f57b638f0b""#;
req => json!([{
"data": "0x26b3293f000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364",
"from": "0x0000000000000000000000000000000000000001",
"gas": "0x0",
"gasPrice": "0x0",
"to": "0x0000000000000000000000000000000000000000"
}]),
res => json!("0x1db8f385535c0d178b8f40016048f3a3cffee8f94e68978ea4b277f57b638f0b");
]
}
@ -111,16 +151,32 @@ test_app_stream! {
expected => vec![0x1005],
home_transport => [
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1011""#;
req => json!([]),
res => json!("0x1011");
"eth_getLogs" =>
req => r#"[{"address":["0x0000000000000000000000000000000000000000"],"fromBlock":"0x6","limit":null,"toBlock":"0x1005","topics":[["0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"],null,null,null]}]"#,
res => r#"[{"address":"0x0000000000000000000000000000000000000000","topics":["0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"],"data":"0x000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0","type":"","transactionHash":"0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"}]"#;
req => json!([{
"address": ["0x0000000000000000000000000000000000000000"],
"fromBlock": "0x6",
"limit": null,
"toBlock": "0x1005",
"topics": [[DEPOSIT_TOPIC], null, null, null]
}]),
res => json!([{
"address": "0x0000000000000000000000000000000000000000",
"topics": [DEPOSIT_TOPIC],
"data": "0x000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0","type":"","transactionHash":"0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"
}]);
],
foreign_transport => [
"eth_sendTransaction" =>
req => r#"[{"data":"0x26b3293f000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364","from":"0x0000000000000000000000000000000000000001","gas":"0xfd","gasPrice":"0xa0","to":"0x0000000000000000000000000000000000000000"}]"#,
res => r#""0x1db8f385535c0d178b8f40016048f3a3cffee8f94e68978ea4b277f57b638f0b""#;
req => json!([{
"data": "0x26b3293f000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364",
"from": "0x0000000000000000000000000000000000000001",
"gas": "0xfd",
"gasPrice": "0xa0",
"to": "0x0000000000000000000000000000000000000000"
}]),
res => json!("0x1db8f385535c0d178b8f40016048f3a3cffee8f94e68978ea4b277f57b638f0b");
]
}
@ -148,16 +204,34 @@ test_app_stream! {
expected => vec![0x1005],
home_transport => [
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1011""#;
req => json!([]),
res => json!("0x1011");
"eth_getLogs" =>
req => r#"[{"address":["0x0000000000000000000000000000000000000cc1"],"fromBlock":"0x1","limit":null,"toBlock":"0x1005","topics":[["0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"],null,null,null]}]"#,
res => r#"[{"address":"0x0000000000000000000000000000000000000cc1","topics":["0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"],"data":"0x000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0","type":"","transactionHash":"0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"}]"#;
req => json!([{
"address": ["0x0000000000000000000000000000000000000cc1"],
"fromBlock": "0x1",
"limit": null,
"toBlock": "0x1005",
"topics": [[DEPOSIT_TOPIC], null, null, null]
}]),
res => json!([{
"address": "0x0000000000000000000000000000000000000cc1",
"topics": ["0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"],
"data": "0x000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0",
"type": "",
"transactionHash": "0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"
}]);
],
foreign_transport => [
"eth_sendTransaction" =>
req => r#"[{"data":"0x26b3293f000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364","from":"0x0000000000000000000000000000000000000001","gas":"0x0","gasPrice":"0x0","to":"0x0000000000000000000000000000000000000dd1"}]"#,
res => r#""0x1db8f385535c0d178b8f40016048f3a3cffee8f94e68978ea4b277f57b638f0b""#;
req => json!([{
"data": "0x26b3293f000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364",
"from": "0x0000000000000000000000000000000000000001",
"gas": "0x0",
"gasPrice": "0x0",
"to": "0x0000000000000000000000000000000000000dd1"
}]),
res => json!("0x1db8f385535c0d178b8f40016048f3a3cffee8f94e68978ea4b277f57b638f0b");
]
}
@ -185,16 +259,34 @@ test_app_stream! {
expected => vec![0x1005],
home_transport => [
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1011""#;
req => json!([]),
res => json!("0x1011");
"eth_getLogs" =>
req => r#"[{"address":["0x0000000000000000000000000000000000000cc1"],"fromBlock":"0x1","limit":null,"toBlock":"0x1005","topics":[["0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"],null,null,null]}]"#,
res => r#"[{"address":"0x0000000000000000000000000000000000000cc1","topics":["0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"],"data":"0x000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0","type":"","transactionHash":"0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"}]"#;
req => json!([{
"address": ["0x0000000000000000000000000000000000000cc1"],
"fromBlock": "0x1",
"limit": null,
"toBlock": "0x1005",
"topics": [[DEPOSIT_TOPIC], null, null, null]
}]),
res => json!([{
"address": "0x0000000000000000000000000000000000000cc1",
"topics": [DEPOSIT_TOPIC],
"data": "0x000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0",
"type": "",
"transactionHash": "0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"
}]);
],
foreign_transport => [
"eth_sendTransaction" =>
req => r#"[{"data":"0x26b3293f000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364","from":"0x00000000000000000000000000000000000000ee","gas":"0x0","gasPrice":"0x0","to":"0x0000000000000000000000000000000000000dd1"}]"#,
res => r#""0x1db8f385535c0d178b8f40016048f3a3cffee8f94e68978ea4b277f57b638f0b""#;
req => json!([{
"data": "0x26b3293f000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364",
"from": "0x00000000000000000000000000000000000000ee",
"gas": "0x0",
"gasPrice": "0x0",
"to":"0x0000000000000000000000000000000000000dd1"
}]),
res => json!("0x1db8f385535c0d178b8f40016048f3a3cffee8f94e68978ea4b277f57b638f0b");
]
}
@ -218,18 +310,51 @@ test_app_stream! {
expected => vec![0x1005],
home_transport => [
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1011""#;
req => json!([]),
res => json!("0x1011");
"eth_getLogs" =>
req => r#"[{"address":["0x0000000000000000000000000000000000000000"],"fromBlock":"0x1","limit":null,"toBlock":"0x1005","topics":[["0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"],null,null,null]}]"#,
res => r#"[{"address":"0x0000000000000000000000000000000000000000","topics":["0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"],"data":"0x000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0","type":"","transactionHash":"0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"},{"address":"0x0000000000000000000000000000000000000000","topics":["0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"],"data":"0x000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0","type":"","transactionHash":"0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a942436f"}]"#;
req => json!([{
"address": ["0x0000000000000000000000000000000000000000"],
"fromBlock": "0x1",
"limit": null,
"toBlock": "0x1005",
"topics": [[DEPOSIT_TOPIC], null, null, null]
}]),
res => json!([
{
"address": "0x0000000000000000000000000000000000000000",
"topics": ["0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"],
"data": "0x000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0",
"type": "",
"transactionHash": "0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"
},
{
"address":"0x0000000000000000000000000000000000000000",
"topics": ["0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"],
"data": "0x000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0",
"type": "",
"transactionHash": "0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a942436f"
}
]);
],
foreign_transport => [
"eth_sendTransaction" =>
req => r#"[{"data":"0x26b3293f000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364","from":"0x0000000000000000000000000000000000000001","gas":"0x0","gasPrice":"0x0","to":"0x0000000000000000000000000000000000000000"}]"#,
res => r#""0x1db8f385535c0d178b8f40016048f3a3cffee8f94e68978ea4b277f57b638f0b""#;
req => json!([{
"data": "0x26b3293f000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364",
"from": "0x0000000000000000000000000000000000000001",
"gas": "0x0",
"gasPrice": "0x0",
"to": "0x0000000000000000000000000000000000000000"
}]),
res => json!("0x1db8f385535c0d178b8f40016048f3a3cffee8f94e68978ea4b277f57b638f0b");
"eth_sendTransaction" =>
req => r#"[{"data":"0x26b3293f000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a942436f","from":"0x0000000000000000000000000000000000000001","gas":"0x0","gasPrice":"0x0","to":"0x0000000000000000000000000000000000000000"}]"#,
res => r#""0x1db8f385535c0d178b8f40016048f3a3cffee8f94e68978ea4b277f57b638f0b""#;
req => json!([{
"data": "0x26b3293f000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a942436f",
"from": "0x0000000000000000000000000000000000000001",
"gas": "0x0",
"gasPrice": "0x0",
"to": "0x0000000000000000000000000000000000000000"
}]),
res => json!("0x1db8f385535c0d178b8f40016048f3a3cffee8f94e68978ea4b277f57b638f0b");
]
}

View File

@ -1,4 +1,6 @@
extern crate futures;
#[macro_use]
extern crate serde_json;
extern crate web3;
extern crate bridge;
#[macro_use]
@ -31,20 +33,32 @@ test_transport_stream! {
logs: vec![],
}],
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1010""#;
req => json!([]),
res => json!("0x1010");
"eth_getLogs" =>
req => r#"[{"address":null,"fromBlock":"0xb","limit":null,"toBlock":"0x1006","topics":null}]"#,
res => r#"[]"#;
req => json!([{
"address": null,
"fromBlock": "0xb",
"limit": null,
"toBlock": "0x1006",
"topics": null
}]),
res => json!([]);
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1010""#;
req => json!([]),
res => json!("0x1010");
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1011""#;
req => json!([]),
res => json!("0x1011");
"eth_getLogs" =>
req => r#"[{"address":null,"fromBlock":"0x1007","limit":null,"toBlock":"0x1007","topics":null}]"#,
res => r#"[]"#;
req => json!([{
"address": null,
"fromBlock": "0x1007",
"limit": null,
"toBlock": "0x1007",
"topics": null
}]),
res => json!([]);
}
test_transport_stream! {
@ -70,23 +84,35 @@ test_transport_stream! {
logs: vec![],
}],
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x17""#;
req => json!([]),
res => json!("0x17");
"eth_getLogs" =>
req => r#"[{"address":null,"fromBlock":"0xb","limit":null,"toBlock":"0xd","topics":null}]"#,
res => r#"[]"#;
req => json!([{
"address": null,
"fromBlock": "0xb",
"limit": null,
"toBlock": "0xd",
"topics": null
}]),
res => json!([]);
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x16""#;
req => json!([]),
res => json!("0x16");
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x17""#;
req => json!([]),
res => json!("0x17");
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x19""#;
req => json!([]),
res => json!("0x19");
"eth_getLogs" =>
req => r#"[{"address":null,"fromBlock":"0xe","limit":null,"toBlock":"0xf","topics":null}]"#,
res => r#"[]"#;
req => json!([{
"address": null,
"fromBlock": "0xe",
"limit": null,
"toBlock": "0xf",
"topics": null
}]),
res => json!([]);
}
test_transport_stream! {
@ -108,17 +134,23 @@ test_transport_stream! {
logs: vec![],
}],
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x13""#;
req => json!([]),
res => json!("0x13");
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x14""#;
req => json!([]),
res => json!("0x14");
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x17""#;
req => json!([]),
res => json!("0x17");
"eth_getLogs" =>
req => r#"[{"address":null,"fromBlock":"0xb","limit":null,"toBlock":"0xd","topics":null}]"#,
res => r#"[]"#;
req => json!([{
"address": null,
"fromBlock": "0xb",
"limit": null,
"toBlock": "0xd",
"topics": null
}]),
res => json!([]);
}
test_transport_stream! {
@ -148,26 +180,44 @@ test_transport_stream! {
logs: vec![],
}],
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x13""#;
req => json!([]),
res => json!("0x13");
"eth_getLogs" =>
req => r#"[{"address":null,"fromBlock":"0xb","limit":null,"toBlock":"0x13","topics":null}]"#,
res => r#"[]"#;
req => json!([{
"address": null,
"fromBlock": "0xb",
"limit": null,
"toBlock": "0x13",
"topics": null
}]),
res => json!([]);
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x14""#;
req => json!([]),
res => json!("0x14");
"eth_getLogs" =>
req => r#"[{"address":null,"fromBlock":"0x14","limit":null,"toBlock":"0x14","topics":null}]"#,
res => r#"[]"#;
req => json!([{
"address": null,
"fromBlock": "0x14",
"limit": null,
"toBlock": "0x14",
"topics": null
}]),
res => json!([]);
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x14""#;
req => json!([]),
res => json!("0x14");
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x17""#;
req => json!([]),
res => json!("0x17");
"eth_getLogs" =>
req => r#"[{"address":null,"fromBlock":"0x15","limit":null,"toBlock":"0x17","topics":null}]"#,
res => r#"[]"#;
req => json!([{
"address": null,
"fromBlock": "0x15",
"limit": null,
"toBlock": "0x17",
"topics": null
}]),
res => json!([]);
}
test_transport_stream! {
@ -193,17 +243,29 @@ test_transport_stream! {
logs: vec![],
}],
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x13""#;
req => json!([]),
res => json!("0x13");
"eth_getLogs" =>
req => r#"[{"address":["0x1111111111111111111111111111111111111111"],"fromBlock":"0xc","limit":null,"toBlock":"0x13","topics":null}]"#,
res => r#"[]"#;
req => json!([{
"address": ["0x1111111111111111111111111111111111111111"],
"fromBlock": "0xc",
"limit": null,
"toBlock": "0x13",
"topics": null
}]),
res => json!([]);
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x14""#;
req => json!([]),
res => json!("0x14");
"eth_getLogs" =>
req => r#"[{"address":["0x1111111111111111111111111111111111111111"],"fromBlock":"0x14","limit":null,"toBlock":"0x14","topics":null}]"#,
res => r#"[]"#;
req => json!([{
"address":["0x1111111111111111111111111111111111111111"],
"fromBlock": "0x14",
"limit": null,
"toBlock": "0x14",
"topics": null
}]),
res => json!([]);
}
test_transport_stream! {
@ -229,17 +291,29 @@ test_transport_stream! {
logs: vec![],
}],
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x13""#;
req => json!([]),
res => json!("0x13");
"eth_getLogs" =>
req => r#"[{"address":null,"fromBlock":"0xc","limit":null,"toBlock":"0x13","topics":[["0x2222222222222222222222222222222222222222222222222222222222222222"],null,null,null]}]"#,
res => r#"[]"#;
req => json!([{
"address": null,
"fromBlock": "0xc",
"limit": null,
"toBlock": "0x13",
"topics":[["0x2222222222222222222222222222222222222222222222222222222222222222"], null, null, null]
}]),
res => json!([]);
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x14""#;
req => json!([]),
res => json!("0x14");
"eth_getLogs" =>
req => r#"[{"address":null,"fromBlock":"0x14","limit":null,"toBlock":"0x14","topics":[["0x2222222222222222222222222222222222222222222222222222222222222222"],null,null,null]}]"#,
res => r#"[]"#;
req => json!([{
"address": null,
"fromBlock": "0x14",
"limit": null,
"toBlock": "0x14",
"topics": [["0x2222222222222222222222222222222222222222222222222222222222222222"], null, null, null]
}]),
res => json!([]);
}
test_transport_stream! {
@ -267,12 +341,22 @@ test_transport_stream! {
}],
}],
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1010""#;
req => json!([]),
res => json!("0x1010");
"eth_getLogs" =>
req => r#"[{"address":null,"fromBlock":"0xb","limit":null,"toBlock":"0x1006","topics":null}]"#,
res =>
r#"[{"address":"0x0000000000000000000000000000000000000001","topics":[],"data":"0x10","type":""}]"#;
req => json!([{
"address": null,
"fromBlock": "0xb",
"limit": null,
"toBlock": "0x1006",
"topics": null
}]),
res => json!([{
"address": "0x0000000000000000000000000000000000000001",
"topics": [],
"data": "0x10",
"type": ""
}]);
}
test_transport_stream! {
@ -320,21 +404,57 @@ test_transport_stream! {
}],
}],
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1010""#;
req => json!([]),
res => json!("0x1010");
"eth_getLogs" =>
req => r#"[{"address":null,"fromBlock":"0xb","limit":null,"toBlock":"0x1006","topics":null}]"#,
res => r#"[{"address":"0x0000000000000000000000000000000000000001","topics":[],"data":"0x10","type":""}]"#;
req => json!([{
"address": null,
"fromBlock": "0xb",
"limit": null,
"toBlock": "0x1006",
"topics": null
}]),
res => json!([{
"address": "0x0000000000000000000000000000000000000001",
"topics": [],
"data": "0x10",
"type": ""
}]);
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1011""#;
req => json!([]),
res => json!("0x1011");
"eth_getLogs" =>
req => r#"[{"address":null,"fromBlock":"0x1007","limit":null,"toBlock":"0x1007","topics":null}]"#,
res => r#"[]"#;
req => json!([{
"address": null,
"fromBlock": "0x1007",
"limit": null,
"toBlock": "0x1007",
"topics": null
}]),
res => json!([]);
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1012""#;
req => json!([]),
res => json!("0x1012");
"eth_getLogs" =>
req => r#"[{"address":null,"fromBlock":"0x1008","limit":null,"toBlock":"0x1008","topics":null}]"#,
res => r#"[{"address":"0x0000000000000000000000000000000000000002","topics":[],"data":"0x20","type":""},{"address":"0x0000000000000000000000000000000000000002","topics":[],"data":"0x30","type":""}]"#;
req => json!([{
"address": null,
"fromBlock": "0x1008",
"limit": null,
"toBlock": "0x1008",
"topics": null
}]),
res => json!([
{
"address": "0x0000000000000000000000000000000000000002",
"topics": [],
"data": "0x20",
"type":""
},
{
"address":"0x0000000000000000000000000000000000000002",
"topics": [],
"data": "0x30",
"type": ""
}
]);
}

View File

@ -1,9 +1,22 @@
/// test interactions of withdraw_confirm state machine with RPC
extern crate futures;
#[macro_use]
extern crate serde_json;
extern crate bridge;
#[macro_use]
extern crate tests;
extern crate ethabi;
extern crate rustc_hex;
extern crate ethereum_types;
use rustc_hex::{ToHex, FromHex};
use bridge::bridge::create_withdraw_confirm;
use bridge::message_to_mainnet::MessageToMainnet;
use bridge::contracts;
use ethabi::{encode, Token};
const WITHDRAW_TOPIC: &str = "0xf279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568";
test_app_stream! {
name => withdraw_confirm_basic,
@ -26,17 +39,29 @@ test_app_stream! {
home_transport => [],
foreign_transport => [
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1011""#;
req => json!([]),
res => json!("0x1011");
"eth_getLogs" =>
req => r#"[{"address":["0x0000000000000000000000000000000000000000"],"fromBlock":"0x1","limit":null,"toBlock":"0x1005","topics":[["0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"],null,null,null]}]"#,
res => r#"[]"#;
req => json!([{
"address": ["0x0000000000000000000000000000000000000000"],
"fromBlock": "0x1",
"limit": null,
"toBlock": "0x1005",
"topics": [[WITHDRAW_TOPIC], null, null, null]
}]),
res => json!([]);
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1012""#;
req => json!([]),
res => json!("0x1012");
"eth_getLogs" =>
req => r#"[{"address":["0x0000000000000000000000000000000000000000"],"fromBlock":"0x1006","limit":null,"toBlock":"0x1006","topics":[["0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"],null,null,null]}]"#,
res => r#"[]"#;
req => json!([{
"address": ["0x0000000000000000000000000000000000000000"],
"fromBlock": "0x1006",
"limit": null,
"toBlock": "0x1006",
"topics":[[WITHDRAW_TOPIC], null, null, null]
}]),
res => json!([]);
]
}
@ -64,20 +89,31 @@ test_app_stream! {
home_transport => [],
foreign_transport => [
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x0100""#;
req => json!([]),
res => json!("0x0100");
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1011""#;
req => json!([]),
res => json!("0x1011");
"eth_getLogs" =>
req => r#"[{"address":["0x0000000000000000000000000000000000000000"],"fromBlock":"0xf6","limit":null,"toBlock":"0x1005","topics":[["0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"],null,null,null]}]"#,
res => r#"[]"#;
req => json!([{
"address":["0x0000000000000000000000000000000000000000"],
"fromBlock": "0xf6",
"limit": null, "toBlock": "0x1005",
"topics": [[WITHDRAW_TOPIC], null, null, null]
}]),
res => json!([]);
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1012""#;
req => json!([]),
res => json!("0x1012");
"eth_getLogs" =>
req => r#"[{"address":["0x0000000000000000000000000000000000000000"],"fromBlock":"0x1006","limit":null,"toBlock":"0x1006","topics":[["0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"],null,null,null]}]"#,
res => r#"[]"#;
req => json!([{
"address":["0x0000000000000000000000000000000000000000"],
"fromBlock": "0x1006",
"limit": null,
"toBlock": "0x1006",
"topics":[[WITHDRAW_TOPIC], null, null, null]
}]),
res => json!([]);
]
}
@ -107,20 +143,32 @@ test_app_stream! {
home_transport => [],
foreign_transport => [
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x0100""#;
req => json!([]),
res => json!("0x0100");
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1011""#;
req => json!([]),
res => json!("0x1011");
"eth_getLogs" =>
req => r#"[{"address":["0x49edf201c1e139282643d5e7c6fb0c7219ad1db8"],"fromBlock":"0xf6","limit":null,"toBlock":"0x1005","topics":[["0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"],null,null,null]}]"#,
res => r#"[]"#;
req => json!([{
"address":["0x49edf201c1e139282643d5e7c6fb0c7219ad1db8"],
"fromBlock": "0xf6",
"limit": null,
"toBlock": "0x1005",
"topics":[[WITHDRAW_TOPIC],null,null,null]
}]),
res => json!([]);
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1012""#;
req => json!([]),
res => json!("0x1012");
"eth_getLogs" =>
req => r#"[{"address":["0x49edf201c1e139282643d5e7c6fb0c7219ad1db8"],"fromBlock":"0x1006","limit":null,"toBlock":"0x1006","topics":[["0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"],null,null,null]}]"#,
res => r#"[]"#;
req => json!([{
"address": ["0x49edf201c1e139282643d5e7c6fb0c7219ad1db8"],
"fromBlock": "0x1006",
"limit": null,
"toBlock": "0x1006",
"topics":[[WITHDRAW_TOPIC],null,null,null]
}]),
res => json!([]);
]
}
@ -156,26 +204,77 @@ test_app_stream! {
home_transport => [],
foreign_transport => [
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x0100""#;
req => json!([]),
res => json!("0x0100");
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1011""#;
req => json!([]),
res => json!("0x1011");
"eth_getLogs" =>
req => r#"[{"address":["0x49edf201c1e139282643d5e7c6fb0c7219ad1db8"],"fromBlock":"0xf6","limit":null,"toBlock":"0x1005","topics":[["0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"],null,null,null]}]"#,
res => r#"[{"address":"0x49edf201c1e139282643d5e7c6fb0c7219ad1db8","topics":["0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"],"data":"0x000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0","type":"","transactionHash":"0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"}]"#;
req => json!([{
"address": ["0x49edf201c1e139282643d5e7c6fb0c7219ad1db8"],
"fromBlock": "0xf6",
"limit": null,
"toBlock": "0x1005",
"topics": [[WITHDRAW_TOPIC], null, null, null]
}]),
res => json!([{
"address": "0x49edf201c1e139282643d5e7c6fb0c7219ad1db8",
"topics": [WITHDRAW_TOPIC],
"data": format!("0x{}", encode(&[
Token::Address([1u8; 20].into()),
Token::Uint(10000.into()),
Token::Uint(1000.into()),
]).to_hex()),
"type": "",
"transactionHash": "0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"
}]);
"eth_sign" =>
req => r#"["0x0000000000000000000000000000000000000001","0xaff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"]"#,
res => r#""0x8697c15331677e6ebccccaff3454fce5edbc8cca8697c15331677aff3454fce5edbc8cca8697c15331677e6ebccccaff3454fce5edbc8cca8697c15331677e6ebc""#;
req => json!([
"0x0000000000000000000000000000000000000001",
format!("0x{}", MessageToMainnet {
recipient: [1u8; 20].into(),
value: 10000.into(),
sidenet_transaction_hash: "0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364".into(),
mainnet_gas_price: 1000.into(),
}
.to_bytes()
.to_hex())
]),
res => json!("0x8697c15331677e6ebccccaff3454fce5edbc8cca8697c15331677aff3454fce5edbc8cca8697c15331677e6ebccccaff3454fce5edbc8cca8697c15331677e6ebc");
// `submitSignature`
"eth_sendTransaction" =>
req => r#"[{"data":"0x630cea8e000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000418697c15331677e6ebccccaff3454fce5edbc8cca8697c15331677aff3454fce5edbc8cca8697c15331677e6ebccccaff3454fce5edbc8cca8697c15331677e6ebc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364000000000000000000000000","from":"0x0000000000000000000000000000000000000001","gas":"0xfe","gasPrice":"0xa1","to":"0x49edf201c1e139282643d5e7c6fb0c7219ad1db8"}]"#,
res => r#""0x1db8f385535c0d178b8f40016048f3a3cffee8f94e68978ea4b277f57b638f0b""#;
req => json!([{
"data": format!("0x{}", contracts::foreign::ForeignBridge::default()
.functions()
.submit_signature()
.input(
"8697c15331677e6ebccccaff3454fce5edbc8cca8697c15331677aff3454fce5edbc8cca8697c15331677e6ebccccaff3454fce5edbc8cca8697c15331677e6ebc".from_hex().unwrap(),
MessageToMainnet {
recipient: [1u8; 20].into(),
value: 10000.into(),
sidenet_transaction_hash: "0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364".into(),
mainnet_gas_price: 1000.into(),
}.to_bytes()
)
.to_hex()),
"from": "0x0000000000000000000000000000000000000001",
"gas": "0xfe",
"gasPrice": "0xa1",
"to":"0x49edf201c1e139282643d5e7c6fb0c7219ad1db8"
}]),
res => json!("0x1db8f385535c0d178b8f40016048f3a3cffee8f94e68978ea4b277f57b638f0b");
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1012""#;
req => json!([]),
res => json!("0x1012");
"eth_getLogs" =>
req => r#"[{"address":["0x49edf201c1e139282643d5e7c6fb0c7219ad1db8"],"fromBlock":"0x1006","limit":null,"toBlock":"0x1006","topics":[["0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"],null,null,null]}]"#,
res => r#"[]"#;
req => json!([{
"address": ["0x49edf201c1e139282643d5e7c6fb0c7219ad1db8"],
"fromBlock": "0x1006",
"limit": null,
"toBlock": "0x1006",
"topics":[[WITHDRAW_TOPIC], null, null, null]
}]),
res => json!([]);
]
}
@ -210,28 +309,118 @@ test_app_stream! {
home_transport => [],
foreign_transport => [
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0xe""#;
req => json!([]),
res => json!("0xe");
"eth_getLogs" =>
req => r#"[{"address":["0x49edf201c1e139282643d5e7c6fb0c7219ad1db8"],"fromBlock":"0x1","limit":null,"toBlock":"0x2","topics":[["0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"],null,null,null]}]"#,
res => r#"[{"address":"0x49edf201c1e139282643d5e7c6fb0c7219ad1db8","topics":["0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"],"data":"0x000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0","type":"","transactionHash":"0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"},{"address":"0x49edf201c1e139282643d5e7c6fb0c7219ad1db8","topics":["0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"],"data":"0x000000000000000000000000001da5bcab735024168f00b43abcc9ef522392e90000000000000000000000000000000000000000000000000000000000000099","type":"","transactionHash":"0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424399"}]"#;
req => json!([{
"address": ["0x49edf201c1e139282643d5e7c6fb0c7219ad1db8"],
"fromBlock": "0x1",
"limit": null,
"toBlock": "0x2",
"topics": [[WITHDRAW_TOPIC], null, null, null]
}]),
res => json!([{
"address": "0x49edf201c1e139282643d5e7c6fb0c7219ad1db8",
"topics": [WITHDRAW_TOPIC],
"data": format!("0x{}", encode(&[
Token::Address([1u8; 20].into()),
Token::Uint(10000.into()),
Token::Uint(1000.into()),
]).to_hex()),
"type": "",
"transactionHash": "0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"},
{
"address":"0x49edf201c1e139282643d5e7c6fb0c7219ad1db8",
"topics": [WITHDRAW_TOPIC],
"data": format!("0x{}", encode(&[
Token::Address([2u8; 20].into()),
Token::Uint(42.into()),
Token::Uint(100.into()),
]).to_hex()),
"type":"",
"transactionHash":"0xfffedad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"
}]);
"eth_sign" =>
req => r#"["0x0000000000000000000000000000000000000001","0xaff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"]"#,
res => r#""0x8697c15331677e6ebccccaff3454fce5edbc8cca8697c15331677aff3454fce5edbc8cca8697c15331677e6ebccccaff3454fce5edbc8cca8697c15331677e6ebc""#;
req => json!([
"0x0000000000000000000000000000000000000001",
format!("0x{}", MessageToMainnet {
recipient: [1u8; 20].into(),
value: 10000.into(),
sidenet_transaction_hash: "0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364".into(),
mainnet_gas_price: 1000.into(),
}
.to_bytes()
.to_hex())
]),
res => json!("0x8697c15331677e6ebccccaff3454fce5edbc8cca8697c15331677aff3454fce5edbc8cca8697c15331677e6ebccccaff3454fce5edbc8cca8697c15331677e6ebc");
"eth_sign" =>
req => r#"["0x0000000000000000000000000000000000000001","0x001da5bcab735024168f00b43abcc9ef522392e90000000000000000000000000000000000000000000000000000000000000099884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424399"]"#,
res => r#""0x8a3b24c56e46f6fc9fa7ed14795745348059b8ac84d6ee93323e83a429e760ae6e89510834ee4d65eefacd74cddca53df61b5eba1c3007ed88d2eebff2e0e2151b""#;
req => json!([
"0x0000000000000000000000000000000000000001",
format!("0x{}", MessageToMainnet {
recipient: [2u8; 20].into(),
value: 42.into(),
sidenet_transaction_hash: "0xfffedad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364".into(),
mainnet_gas_price: 100.into(),
}
.to_bytes()
.to_hex())
]),
res => json!("0x8a3b24c56e46f6fc9fa7ed14795745348059b8ac84d6ee93323e83a429e760ae6e89510834ee4d65eefacd74cddca53df61b5eba1c3007ed88d2eebff2e0e2151b");
// `submitSignature`
"eth_sendTransaction" =>
req => r#"[{"data":"0x630cea8e000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000418697c15331677e6ebccccaff3454fce5edbc8cca8697c15331677aff3454fce5edbc8cca8697c15331677e6ebccccaff3454fce5edbc8cca8697c15331677e6ebc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364000000000000000000000000","from":"0x0000000000000000000000000000000000000001","gas":"0xff","gasPrice":"0xaa","to":"0x49edf201c1e139282643d5e7c6fb0c7219ad1db8"}]"#,
res => r#""0x1db8f385535c0d178b8f40016048f3a3cffee8f94e68978ea4b277f57b638f0b""#;
req => json!([{
"data": format!("0x{}", contracts::foreign::ForeignBridge::default()
.functions()
.submit_signature()
.input(
"8697c15331677e6ebccccaff3454fce5edbc8cca8697c15331677aff3454fce5edbc8cca8697c15331677e6ebccccaff3454fce5edbc8cca8697c15331677e6ebc".from_hex().unwrap(),
MessageToMainnet {
recipient: [1u8; 20].into(),
value: 10000.into(),
sidenet_transaction_hash: "0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364".into(),
mainnet_gas_price: 1000.into(),
}.to_bytes()
)
.to_hex()),
"from": "0x0000000000000000000000000000000000000001",
"gas": "0xff",
"gasPrice": "0xaa",
"to":"0x49edf201c1e139282643d5e7c6fb0c7219ad1db8"
}]),
res => json!("0x1db8f385535c0d178b8f40016048f3a3cffee8f94e68978ea4b277f57b638f0b");
// `submitSignature`
"eth_sendTransaction" =>
req => r#"[{"data":"0x630cea8e000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000418a3b24c56e46f6fc9fa7ed14795745348059b8ac84d6ee93323e83a429e760ae6e89510834ee4d65eefacd74cddca53df61b5eba1c3007ed88d2eebff2e0e2151b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054001da5bcab735024168f00b43abcc9ef522392e90000000000000000000000000000000000000000000000000000000000000099884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424399000000000000000000000000","from":"0x0000000000000000000000000000000000000001","gas":"0xff","gasPrice":"0xaa","to":"0x49edf201c1e139282643d5e7c6fb0c7219ad1db8"}]"#,
res => r#""0x1db8f385535c0d178b8f40016048f3a3cffee8f94e68978ea4b277f57b638f0c""#;
req => json!([{
"data": format!("0x{}", contracts::foreign::ForeignBridge::default()
.functions()
.submit_signature()
.input(
"8a3b24c56e46f6fc9fa7ed14795745348059b8ac84d6ee93323e83a429e760ae6e89510834ee4d65eefacd74cddca53df61b5eba1c3007ed88d2eebff2e0e2151b".from_hex().unwrap(),
MessageToMainnet {
recipient: [2u8; 20].into(),
value: 42.into(),
sidenet_transaction_hash: "0xfffedad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364".into(),
mainnet_gas_price: 100.into(),
}.to_bytes()
)
.to_hex()),
"from": "0x0000000000000000000000000000000000000001",
"gas": "0xff",
"gasPrice": "0xaa",
"to":"0x49edf201c1e139282643d5e7c6fb0c7219ad1db8"
}]),
res => json!("0x1db8f385535c0d178b8f40016048f3a3cffee8f94e68978ea4b277f57b638f0c");
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1012""#;
req => json!([]),
res => json!("0x1012");
"eth_getLogs" =>
req => r#"[{"address":["0x49edf201c1e139282643d5e7c6fb0c7219ad1db8"],"fromBlock":"0x3","limit":null,"toBlock":"0x1006","topics":[["0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"],null,null,null]}]"#,
res => r#"[]"#;
req => json!([{
"address": ["0x49edf201c1e139282643d5e7c6fb0c7219ad1db8"],
"fromBlock": "0x3",
"limit": null,
"toBlock": "0x1006",
"topics":[[WITHDRAW_TOPIC], null, null, null]
}]),
res => json!([]);
]
}

View File

@ -1,9 +1,24 @@
/// test interactions of withdraw_relay state machine with RPC
extern crate futures;
#[macro_use]
extern crate serde_json;
extern crate bridge;
#[macro_use]
extern crate tests;
extern crate ethabi;
extern crate ethereum_types;
extern crate rustc_hex;
use ethereum_types::{U256, H256};
use rustc_hex::ToHex;
use bridge::bridge::create_withdraw_relay;
use bridge::message_to_mainnet::MessageToMainnet;
use bridge::signature::Signature;
use bridge::contracts;
const COLLECTED_SIGNATURES_TOPIC: &str = "0xeb043d149eedb81369bec43d4c3a3a53087debc88d2525f13bfaa3eecda28b5c";
// 1 signature required. relay polled twice.
// no CollectedSignatures on ForeignBridge.
@ -29,17 +44,29 @@ test_app_stream! {
home_transport => [],
foreign_transport => [
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1011""#;
req => json!([]),
res => json!("0x1011");
"eth_getLogs" =>
req => r#"[{"address":["0x0000000000000000000000000000000000000000"],"fromBlock":"0x1","limit":null,"toBlock":"0x1005","topics":[["0xeb043d149eedb81369bec43d4c3a3a53087debc88d2525f13bfaa3eecda28b5c"],null,null,null]}]"#,
res => r#"[]"#;
req => json!([{
"address": ["0x0000000000000000000000000000000000000000"],
"fromBlock": "0x1",
"limit": null,
"toBlock": "0x1005",
"topics": [[COLLECTED_SIGNATURES_TOPIC], null, null, null]
}]),
res => json!([]);
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1012""#;
req => json!([]),
res => json!("0x1012");
"eth_getLogs" =>
req => r#"[{"address":["0x0000000000000000000000000000000000000000"],"fromBlock":"0x1006","limit":null,"toBlock":"0x1006","topics":[["0xeb043d149eedb81369bec43d4c3a3a53087debc88d2525f13bfaa3eecda28b5c"],null,null,null]}]"#,
res => r#"[]"#;
req => json!([{
"address": ["0x0000000000000000000000000000000000000000"],
"fromBlock": "0x1006",
"limit": null,
"toBlock": "0x1006",
"topics": [[COLLECTED_SIGNATURES_TOPIC], null, null, null]
}]),
res => json!([]);
]
}
@ -68,179 +95,31 @@ test_app_stream! {
home_transport => [],
foreign_transport => [
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1011""#;
req => json!([]),
res => json!("0x1011");
"eth_getLogs" =>
req => r#"[{"address":["0x0000000000000000000000000000000000000000"],"fromBlock":"0x1","limit":null,"toBlock":"0x1005","topics":[["0xeb043d149eedb81369bec43d4c3a3a53087debc88d2525f13bfaa3eecda28b5c"],null,null,null]}]"#,
res => r#"[{"address":"0x0000000000000000000000000000000000000000","topics":["0xeb043d149eedb81369bec43d4c3a3a53087debc88d2525f13bfaa3eecda28b5c"],"data":"0x000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0","type":"","transactionHash":"0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"}]"#;
req => json!([{
"address": ["0x0000000000000000000000000000000000000000"],
"fromBlock": "0x1",
"limit": null,
"toBlock": "0x1005",
"topics": [[COLLECTED_SIGNATURES_TOPIC], null, null, null]
}]),
res => json!([{
"address": "0x0000000000000000000000000000000000000000",
"topics": [COLLECTED_SIGNATURES_TOPIC],
"data": "0x000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0",
"type": "",
"transactionHash": "0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"
}]);
]
}
// 2 signatures required. relay polled twice.
// single CollectedSignatures log present. message value covers relay cost.
// single CollectedSignatures log present.
// message gets relayed.
test_app_stream! {
name => withdraw_relay_single_log_sufficient_value_relay,
database => Database::default(),
home =>
account => "0000000000000000000000000000000000000001",
confirmations => 12;
foreign =>
account => "aff3454fce5edbc8cca8697c15331677e6ebcccc",
confirmations => 12;
authorities =>
accounts => [
"0000000000000000000000000000000000000001",
"0000000000000000000000000000000000000002",
],
signatures => 2;
txs => Transactions::default(),
init => |app, db| create_withdraw_relay(app, db).take(1),
expected => vec![0x1005],
home_transport => [
// call to `isValueInMessageLargeEnoughToCoverWithdrawCost`
"eth_call" =>
req => r#"[{"data":"0x6498d59000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000054333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333000000000000000000000000","to":"0x0000000000000000000000000000000000000000"},"latest"]"#,
// respond with `true`
res => r#""0x0000000000000000000000000000000000000000000000000000000000000001""#;
// `withdraw`
"eth_sendTransaction" =>
req => r#"[{"data":"0x9ce318f6000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000002111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222220000000000000000000000000000000000000000000000000000000000000002111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222220000000000000000000000000000000000000000000000000000000000000054333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333000000000000000000000000","from":"0x0000000000000000000000000000000000000001","gas":"0x0","gasPrice":"0x0","to":"0x0000000000000000000000000000000000000000"}]"#,
res => r#""0x1db8f385535c0d178b8f40016048f3a3cffee8f94e68978ea4b277f57b638f0b""#;
],
foreign_transport => [
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1011""#;
"eth_getLogs" =>
req => r#"[{"address":["0x0000000000000000000000000000000000000000"],"fromBlock":"0x1","limit":null,"toBlock":"0x1005","topics":[["0xeb043d149eedb81369bec43d4c3a3a53087debc88d2525f13bfaa3eecda28b5c"],null,null,null]}]"#,
res => r#"[{"address":"0x0000000000000000000000000000000000000000","topics":["0xeb043d149eedb81369bec43d4c3a3a53087debc88d2525f13bfaa3eecda28b5c"],"data":"0x000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0","type":"","transactionHash":"0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"}]"#;
// call to `message`
"eth_call" =>
req => r#"[{"data":"0x490a32c600000000000000000000000000000000000000000000000000000000000000f0","to":"0x0000000000000000000000000000000000000000"},"latest"]"#,
res => r#""0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000054333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333000000000000000000000000""#;
// calls to `signature`
"eth_call" =>
req => r#"[{"data":"0x1812d99600000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000000","to":"0x0000000000000000000000000000000000000000"},"latest"]"#,
res => r#""0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000041111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000000""#;
"eth_call" =>
req => r#"[{"data":"0x1812d99600000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000001","to":"0x0000000000000000000000000000000000000000"},"latest"]"#,
res => r#""0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000041222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222200000000000000000000000000000000000000000000000000000000000000""#;
]
}
// 2 signatures required. relay polled twice.
// single CollectedSignatures log present. message value doesn't cover cost.
// message is ignored.
test_app_stream! {
name => withdraw_relay_single_log_insufficient_value_no_relay,
database => Database::default(),
home =>
account => "0000000000000000000000000000000000000001",
confirmations => 12;
foreign =>
account => "aff3454fce5edbc8cca8697c15331677e6ebcccc",
confirmations => 12;
authorities =>
accounts => [
"0000000000000000000000000000000000000001",
"0000000000000000000000000000000000000002",
],
signatures => 2;
txs => Transactions::default(),
init => |app, db| create_withdraw_relay(app, db).take(1),
expected => vec![0x1005],
home_transport => [
// call to `isValueInMessageLargeEnoughToCoverWithdrawCost`
"eth_call" =>
req => r#"[{"data":"0x6498d59000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000054333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333000000000000000000000000","to":"0x0000000000000000000000000000000000000000"},"latest"]"#,
// respond with `false`
res => r#""0x0000000000000000000000000000000000000000000000000000000000000000""#;
// no `withdraw`
],
foreign_transport => [
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1011""#;
"eth_getLogs" =>
req => r#"[{"address":["0x0000000000000000000000000000000000000000"],"fromBlock":"0x1","limit":null,"toBlock":"0x1005","topics":[["0xeb043d149eedb81369bec43d4c3a3a53087debc88d2525f13bfaa3eecda28b5c"],null,null,null]}]"#,
res => r#"[{"address":"0x0000000000000000000000000000000000000000","topics":["0xeb043d149eedb81369bec43d4c3a3a53087debc88d2525f13bfaa3eecda28b5c"],"data":"0x000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0","type":"","transactionHash":"0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"}]"#;
// call to `message`
"eth_call" =>
req => r#"[{"data":"0x490a32c600000000000000000000000000000000000000000000000000000000000000f0","to":"0x0000000000000000000000000000000000000000"},"latest"]"#,
res => r#""0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000054333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333000000000000000000000000""#;
// calls to `signature`
"eth_call" =>
req => r#"[{"data":"0x1812d99600000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000000","to":"0x0000000000000000000000000000000000000000"},"latest"]"#,
res => r#""0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000041111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000000""#;
"eth_call" =>
req => r#"[{"data":"0x1812d99600000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000001","to":"0x0000000000000000000000000000000000000000"},"latest"]"#,
res => r#""0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000041222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222200000000000000000000000000000000000000000000000000000000000000""#;
]
}
// like `withdraw_relay_single_log_sufficient_value_relay`
// but with explicit gas
test_app_stream! {
name => withdraw_relay_explicit_gas,
database => Database::default(),
home =>
account => "0000000000000000000000000000000000000001",
confirmations => 12;
foreign =>
account => "aff3454fce5edbc8cca8697c15331677e6ebcccc",
confirmations => 12;
authorities =>
accounts => [
"0000000000000000000000000000000000000001",
"0000000000000000000000000000000000000002",
],
signatures => 2;
txs => Transactions {
withdraw_relay: TransactionConfig {
gas: 0x10,
gas_price: 0x20,
},
..Default::default()
},
init => |app, db| create_withdraw_relay(app, db).take(1),
expected => vec![0x1005],
home_transport => [
// call to `isValueInMessageLargeEnoughToCoverWithdrawCost`
"eth_call" =>
req => r#"[{"data":"0x6498d59000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000054333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333000000000000000000000000","to":"0x0000000000000000000000000000000000000000"},"latest"]"#,
// true
res => r#""0x0000000000000000000000000000000000000000000000000000000000000001""#;
// `withdraw`
"eth_sendTransaction" =>
req => r#"[{"data":"0x9ce318f6000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000002111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222220000000000000000000000000000000000000000000000000000000000000002111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222220000000000000000000000000000000000000000000000000000000000000054333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333000000000000000000000000","from":"0x0000000000000000000000000000000000000001","gas":"0x10","gasPrice":"0x20","to":"0x0000000000000000000000000000000000000000"}]"#,
res => r#""0x1db8f385535c0d178b8f40016048f3a3cffee8f94e68978ea4b277f57b638f0b""#;
],
foreign_transport => [
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1011""#;
"eth_getLogs" =>
req => r#"[{"address":["0x0000000000000000000000000000000000000000"],"fromBlock":"0x1","limit":null,"toBlock":"0x1005","topics":[["0xeb043d149eedb81369bec43d4c3a3a53087debc88d2525f13bfaa3eecda28b5c"],null,null,null]}]"#,
res => r#"[{"address":"0x0000000000000000000000000000000000000000","topics":["0xeb043d149eedb81369bec43d4c3a3a53087debc88d2525f13bfaa3eecda28b5c"],"data":"0x000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0","type":"","transactionHash":"0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"}]"#;
// call to `message`
"eth_call" =>
req => r#"[{"data":"0x490a32c600000000000000000000000000000000000000000000000000000000000000f0","to":"0x0000000000000000000000000000000000000000"},"latest"]"#,
res => r#""0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000054333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333000000000000000000000000""#;
// calls to `signature`
"eth_call" =>
req => r#"[{"data":"0x1812d99600000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000000","to":"0x0000000000000000000000000000000000000000"},"latest"]"#,
res => r#""0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000041111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000000""#;
"eth_call" =>
req => r#"[{"data":"0x1812d99600000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000001","to":"0x0000000000000000000000000000000000000000"},"latest"]"#,
res => r#""0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000041222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222200000000000000000000000000000000000000000000000000000000000000""#;
]
}
// like `withdraw_relay_single_log_sufficient_value_relay`
// but with explicit contract addresses
test_app_stream! {
name => withdraw_relay_single_explicit_contract_addresses,
database => Database {
home_contract_address: "00000000000000000000000000000000000000dd".into(),
foreign_contract_address: "00000000000000000000000000000000000000ee".into(),
@ -262,33 +141,73 @@ test_app_stream! {
init => |app, db| create_withdraw_relay(app, db).take(1),
expected => vec![0x1005],
home_transport => [
// call to `isValueInMessageLargeEnoughToCoverWithdrawCost`
"eth_call" =>
req => r#"[{"data":"0x6498d59000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000054333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333000000000000000000000000","to":"0x00000000000000000000000000000000000000dd"},"latest"]"#,
// true
res => r#""0x0000000000000000000000000000000000000000000000000000000000000001""#;
// `withdraw`
// `HomeBridge.withdraw`
"eth_sendTransaction" =>
req => r#"[{"data":"0x9ce318f6000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000002111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222220000000000000000000000000000000000000000000000000000000000000002111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222220000000000000000000000000000000000000000000000000000000000000054333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333000000000000000000000000","from":"0x0000000000000000000000000000000000000001","gas":"0x0","gasPrice":"0x0","to":"0x00000000000000000000000000000000000000dd"}]"#,
res => r#""0x1db8f385535c0d178b8f40016048f3a3cffee8f94e68978ea4b277f57b638f0b""#;
req => json!([{
"data": format!("0x{}", contracts::home::HomeBridge::default()
.functions()
.withdraw()
.input(
vec![U256::from(1), U256::from(4)],
vec![H256::from(2), H256::from(5)],
vec![H256::from(3), H256::from(6)],
MessageToMainnet {
recipient: [1u8; 20].into(),
value: 10000.into(),
sidenet_transaction_hash: "0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364".into(),
mainnet_gas_price: 1000.into(),
}.to_bytes()
).to_hex()),
"from": "0x0000000000000000000000000000000000000001",
"gas": "0x0",
"gasPrice": "0x3e8",
"to": "0x00000000000000000000000000000000000000dd"
}]),
res => json!("0x1db8f385535c0d178b8f40016048f3a3cffee8f94e68978ea4b277f57b638f0b");
],
foreign_transport => [
"eth_blockNumber" =>
req => r#"[]"#,
res => r#""0x1011""#;
req => json!([]),
res => json!("0x1011");
"eth_getLogs" =>
req => r#"[{"address":["0x00000000000000000000000000000000000000ee"],"fromBlock":"0x1","limit":null,"toBlock":"0x1005","topics":[["0xeb043d149eedb81369bec43d4c3a3a53087debc88d2525f13bfaa3eecda28b5c"],null,null,null]}]"#,
res => r#"[{"address":"0x00000000000000000000000000000000000000ee","topics":["0xeb043d149eedb81369bec43d4c3a3a53087debc88d2525f13bfaa3eecda28b5c"],"data":"0x000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0","type":"","transactionHash":"0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"}]"#;
req => json!([{
"address": ["0x00000000000000000000000000000000000000ee"],
"fromBlock": "0x1",
"limit": null,
"toBlock": "0x1005",
"topics": [[COLLECTED_SIGNATURES_TOPIC], null, null, null]
}]),
res => json!([{
"address": "0x00000000000000000000000000000000000000ee",
"topics": [COLLECTED_SIGNATURES_TOPIC],
"data": "0x000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0",
"type": "",
"transactionHash": "0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364"
}]);
// call to `message`
"eth_call" =>
req => r#"[{"data":"0x490a32c600000000000000000000000000000000000000000000000000000000000000f0","to":"0x00000000000000000000000000000000000000ee"},"latest"]"#,
res => r#""0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000054333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333000000000000000000000000""#;
req => json!([{
"data": "0x490a32c600000000000000000000000000000000000000000000000000000000000000f0",
"to": "0x00000000000000000000000000000000000000ee"
}, "latest"]),
res => json!(format!("0x{}", MessageToMainnet {
recipient: [1u8; 20].into(),
value: 10000.into(),
sidenet_transaction_hash: "0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364".into(),
mainnet_gas_price: 1000.into(),
}.to_payload().to_hex()));
// calls to `signature`
"eth_call" =>
req => r#"[{"data":"0x1812d99600000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000000","to":"0x00000000000000000000000000000000000000ee"},"latest"]"#,
res => r#""0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000041111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000000""#;
req => json!([{
"data": "0x1812d99600000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000000",
"to": "0x00000000000000000000000000000000000000ee"
},"latest"]),
res => json!(format!("0x{}", Signature { v: 1, r: 2.into(), s: 3.into() }.to_payload().to_hex()));
"eth_call" =>
req => r#"[{"data":"0x1812d99600000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000001","to":"0x00000000000000000000000000000000000000ee"},"latest"]"#,
res => r#""0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000041222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222200000000000000000000000000000000000000000000000000000000000000""#;
req => json!([{
"data": "0x1812d99600000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000001",
"to": "0x00000000000000000000000000000000000000ee"
},"latest"]),
res => json!(format!("0x{}", Signature { v: 4, r: 5.into(), s: 6.into() }.to_payload().to_hex()));
]
}

View File

@ -3,73 +3,78 @@ var helpers = require("./helpers/helpers");
contract('ForeignBridge', function(accounts) {
it("totalSupply", function() {
var contract;
var contract;
var requiredSignatures = 1;
var estimatedGasCostOfWithdraw = 0;
var authorities = [accounts[0], accounts[1]];
var owner = accounts[2];
var owner = accounts[2];
var hash = "0xe55bb43c36cdf79e23b4adc149cdded921f0d482e613c50c6540977c213bc408";
var value = web3.toWei(3, "ether");
return ForeignBridge.new(requiredSignatures, authorities).then(function(instance) {
contract = instance;
return ForeignBridge.new(requiredSignatures, authorities, estimatedGasCostOfWithdraw).then(function(instance) {
contract = instance;
return contract.totalSupply();
}).then(function(result) {
assert.equal(0, result, "initial supply should be 0");
assert.equal(0, result, "initial supply should be 0");
return contract.deposit(owner, value, hash, {from: authorities[0]});
}).then(function(result) {
return contract.deposit(owner, value, hash, {from: authorities[0]});
}).then(function(result) {
return contract.totalSupply();
}).then(function(result) {
console.log(result);
assert(result.equals(value), "deposit should increase supply");
console.log(result);
assert(result.equals(value), "deposit should increase supply");
return contract.transferHomeViaRelay(owner, value, {from: owner});
}).then(function() {
var homeGasPrice = 1000;
return contract.transferHomeViaRelay(owner, value, homeGasPrice, {from: owner});
}).then(function() {
return contract.totalSupply();
}).then(function(result) {
assert.equal(0, result, "home transfer should decrease supply");
})
assert.equal(0, result, "home transfer should decrease supply");
})
})
it("should be able to approve others to spend tokens in their name", function() {
var contract;
var contract;
var requiredSignatures = 1;
var estimatedGasCostOfWithdraw = 0;
var authorities = [accounts[0], accounts[1]];
var owner = accounts[2];
var spender = accounts[3];
var receiver = accounts[4];
var owner = accounts[2];
var spender = accounts[3];
var receiver = accounts[4];
var hash = "0xe55bb43c36cdf79e23b4adc149cdded921f0d482e613c50c6540977c213bc408";
return ForeignBridge.new(requiredSignatures, authorities).then(function(instance) {
contract = instance;
return ForeignBridge.new(requiredSignatures, authorities, estimatedGasCostOfWithdraw).then(function(instance) {
contract = instance;
// deposit something so we can transfer it
return contract.deposit(owner, web3.toWei(3, "ether"), hash, {from: authorities[0]});
}).then(function(result) {
// deposit something so we can transfer it
return contract.deposit(owner, web3.toWei(3, "ether"), hash, {from: authorities[0]});
}).then(function(result) {
return contract.allowance(owner, spender);
}).then(function(result) {
assert.equal(0, result, "initial allowance should be 0");
assert.equal(0, result, "initial allowance should be 0");
return contract.transferFrom(owner, receiver, web3.toWei(1, "ether"), {from: spender});
}).then(function(result) {
assert(false, "transfer without allowance should fail");
return contract.transferFrom(owner, receiver, web3.toWei(1, "ether"), {from: spender})
.then(function() {
assert(false, "transfer without allowance should fail");
}, helpers.ignoreExpectedError)
}).then(function() {
// transfer 0 without allowance should work
// transfer 0 without allowance should work
return contract.transferFrom(owner, receiver, 0, {from: spender});
}).then(function(result) {
}).then(function(result) {
assert.equal(1, result.logs.length, "Exactly one event should be created");
assert.equal("Transfer", result.logs[0].event, "Event name should be Transfer");
assert.equal(owner, result.logs[0].args.from);
assert.equal(receiver, result.logs[0].args.to);
assert.equal(0, result.logs[0].args.tokens);
}, function(err) {
return contract.approve(spender, web3.toWei(4, "ether"), {from: owner});
}).then(function(result) {
// transfer should work
return contract.approve(spender, web3.toWei(4, "ether"), {from: owner});
}).then(function(result) {
assert.equal(1, result.logs.length, "Exactly one event should be created");
assert.equal("Approval", result.logs[0].event, "Event name should be Approval");
assert.equal(owner, result.logs[0].args.tokenOwner);
@ -78,14 +83,16 @@ contract('ForeignBridge', function(accounts) {
return contract.allowance(owner, spender);
}).then(function(result) {
assert.equal(web3.toWei(4, "ether"), result, "approval should set allowance");
assert.equal(web3.toWei(4, "ether"), result, "approval should set allowance");
return contract.transferFrom(owner, receiver, web3.toWei(4, "ether"), {from: spender});
return contract.transferFrom(owner, receiver, web3.toWei(4, "ether"), {from: spender})
.then(function() {
assert(false, "transferring more than balance should fail");
}, helpers.ignoreExpectedError)
}).then(function() {
return contract.approve(spender, web3.toWei(2, "ether"), {from: owner});
}).then(function(result) {
assert(false, "transferring more than balance should fail");
}, function(err) {
return contract.approve(spender, web3.toWei(2, "ether"), {from: owner});
}).then(function(result) {
assert.equal(1, result.logs.length, "Exactly one event should be created");
assert.equal("Approval", result.logs[0].event, "Event name should be Approval");
assert.equal(owner, result.logs[0].args.tokenOwner);
@ -94,12 +101,14 @@ contract('ForeignBridge', function(accounts) {
return contract.allowance(owner, spender);
}).then(function(result) {
assert.equal(web3.toWei(2, "ether"), result, "approval should update allowance");
assert.equal(web3.toWei(2, "ether"), result, "approval should update allowance");
return contract.transferFrom(owner, receiver, web3.toWei(2, "ether") + 2, {from: spender})
.then(function() {
assert(false, "transferring more than allowance should fail");
}, helpers.ignoreExpectedError)
}).then(function() {
return contract.transferFrom(owner, receiver, web3.toWei(2, "ether") + 2, {from: spender});
}).then(function(result) {
assert(false, "transferring more than allowance should fail");
}, function(err) {
return contract.transferFrom(owner, receiver, web3.toWei(2, "ether"), {from: spender});
}).then(function(result) {
assert.equal(1, result.logs.length, "Exactly one event should be created");
@ -110,32 +119,33 @@ contract('ForeignBridge', function(accounts) {
return contract.balanceOf(owner);
}).then(function(result) {
assert.equal(web3.toWei(1, "ether"), result, "transferring should reduce owners balance");
assert.equal(web3.toWei(1, "ether"), result, "transferring should reduce owners balance");
return contract.balanceOf(receiver);
}).then(function(result) {
assert.equal(web3.toWei(2, "ether"), result, "transferring should increase receivers balance");
assert.equal(web3.toWei(2, "ether"), result, "transferring should increase receivers balance");
return contract.balanceOf(spender);
}).then(function(result) {
assert.equal(0, result, "transferring should not modify spenders balance");
assert.equal(0, result, "transferring should not modify spenders balance");
return contract.allowance(owner, spender);
}).then(function(result) {
assert.equal(0, result, "transferring whole allowance should set allowance to 0");
})
assert.equal(0, result, "transferring whole allowance should set allowance to 0");
})
})
it("should allow user to transfer value locally", function() {
var meta;
var requiredSignatures = 1;
var estimatedGasCostOfWithdraw = 0;
var authorities = [accounts[0], accounts[1]];
var userAccount = accounts[2];
var userAccount2 = accounts[3];
var user1InitialValue = web3.toWei(3, "ether");
var transferedValue = web3.toWei(1, "ether");
var hash = "0xe55bb43c36cdf79e23b4adc149cdded921f0d482e613c50c6540977c213bc408";
return ForeignBridge.new(requiredSignatures, authorities).then(function(instance) {
return ForeignBridge.new(requiredSignatures, authorities, estimatedGasCostOfWithdraw).then(function(instance) {
meta = instance;
// top up balance so we can transfer
return meta.deposit(userAccount, user1InitialValue, hash, { from: authorities[0] });
@ -160,32 +170,34 @@ contract('ForeignBridge', function(accounts) {
it("should not allow user to transfer value they don't have", function() {
var meta;
var requiredSignatures = 1;
var estimatedGasCostOfWithdraw = 0;
var authorities = [accounts[0], accounts[1]];
var userAccount = accounts[2];
var recipientAccount = accounts[3];
var userValue = web3.toWei(3, "ether");
var transferedValue = web3.toWei(4, "ether");
var hash = "0xe55bb43c36cdf79e23b4adc149cdded921f0d482e613c50c6540977c213bc408";
return ForeignBridge.new(requiredSignatures, authorities).then(function(instance) {
return ForeignBridge.new(requiredSignatures, authorities, estimatedGasCostOfWithdraw).then(function(instance) {
meta = instance;
return meta.deposit(userAccount, userValue, hash, { from: authorities[0] });
}).then(function(result) {
return meta.transfer(recipientAccount, transferedValue, { from: userAccount });
}).then(function(result) {
assert(false, "transfer should fail");
}, function(err) {
})
return meta.transfer(recipientAccount, transferedValue, { from: userAccount })
.then(function() {
assert(false, "transfer should fail");
}, helpers.ignoreExpectedError)
})
})
it("should allow transfer of 0 value according to ERC20", function() {
var meta;
var requiredSignatures = 1;
var estimatedGasCostOfWithdraw = 0;
var authorities = [accounts[0], accounts[1]];
var userAccount = accounts[2];
var recipientAccount = accounts[3];
var userValue = web3.toWei(3, "ether");
var hash = "0xe55bb43c36cdf79e23b4adc149cdded921f0d482e613c50c6540977c213bc408";
return ForeignBridge.new(requiredSignatures, authorities).then(function(instance) {
return ForeignBridge.new(requiredSignatures, authorities, estimatedGasCostOfWithdraw).then(function(instance) {
meta = instance;
return meta.deposit(userAccount, userValue, hash, { from: authorities[0] });
}).then(function(result) {
@ -209,45 +221,47 @@ contract('ForeignBridge', function(accounts) {
it("transfer that results in overflow should fail", function() {
var meta;
var requiredSignatures = 1;
var estimatedGasCostOfWithdraw = 0;
var authorities = [accounts[0], accounts[1]];
var userAccount = accounts[2];
var recipientAccount = accounts[3];
var maxValue = web3.toWei("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "wei");
var hash = "0xe55bb43c36cdf79e23b4adc149cdded921f0d482e613c50c6540977c213bc408";
return ForeignBridge.new(requiredSignatures, authorities).then(function(instance) {
return ForeignBridge.new(requiredSignatures, authorities, estimatedGasCostOfWithdraw).then(function(instance) {
meta = instance;
return meta.deposit(recipientAccount, maxValue, hash, { from: authorities[0] });
}).then(function(result) {
return meta.deposit(userAccount, 1, hash, { from: authorities[0] });
}).then(function(result) {
return meta.transfer(recipientAccount, 1, { from: userAccount });
}).then(function(result) {
assert(false, "transfer should fail");
}, function(err) {
return meta.transfer(recipientAccount, 1, { from: userAccount })
.then(function() {
assert(false, "transfer should fail");
}, helpers.ignoreExpectedError)
})
})
it("transferFrom that results in overflow should fail", function() {
var meta;
var requiredSignatures = 1;
var estimatedGasCostOfWithdraw = 0;
var authorities = [accounts[0], accounts[1]];
var userAccount = accounts[2];
var spenderAccount = accounts[3];
var spenderAccount = accounts[3];
var recipientAccount = accounts[4];
var maxValue = web3.toWei("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "wei");
var hash = "0xe55bb43c36cdf79e23b4adc149cdded921f0d482e613c50c6540977c213bc408";
return ForeignBridge.new(requiredSignatures, authorities).then(function(instance) {
return ForeignBridge.new(requiredSignatures, authorities, estimatedGasCostOfWithdraw).then(function(instance) {
meta = instance;
return meta.deposit(recipientAccount, maxValue, hash, { from: authorities[0] });
}).then(function(result) {
return meta.deposit(userAccount, 1, hash, { from: authorities[0] });
}).then(function(result) {
return meta.approve(spenderAccount, 1, {from: userAccount});
return meta.approve(spenderAccount, 1, {from: userAccount});
}).then(function(result) {
return meta.transferFrom(userAccount, recipientAccount, 1, { from: spenderAccount });
}).then(function(result) {
assert(false, "transfer should fail");
}, function(err) {
return meta.transferFrom(userAccount, recipientAccount, 1, { from: spenderAccount })
.then(function() {
assert(false, "transfer should fail");
}, helpers.ignoreExpectedError)
})
})
})

View File

@ -5,9 +5,10 @@ contract('ForeignBridge', function(accounts) {
it("should deploy contract", function() {
var meta;
var requiredSignatures = 1;
var estimatedGasCostOfWithdraw = 0;
var authorities = [accounts[0], accounts[1]];
return ForeignBridge.new(requiredSignatures, authorities).then(function(instance) {
return ForeignBridge.new(requiredSignatures, authorities, estimatedGasCostOfWithdraw).then(function(instance) {
meta = instance;
return meta.requiredSignatures.call();
}).then(function(result) {
@ -20,31 +21,30 @@ contract('ForeignBridge', function(accounts) {
it("should fail to deploy contract with not enough required signatures", function() {
var authorities = [accounts[0], accounts[1]];
return ForeignBridge.new(0, authorities).then(function(_) {
assert(false, "Contract should fail to deploy");
}, function(err) {
// do nothing
})
return ForeignBridge.new(0, authorities, 0)
.then(function() {
assert(false, "Contract should fail to deploy");
}, helpers.ignoreExpectedError)
})
it("should fail to deploy contract with to many signatures", function() {
var authorities = [accounts[0], accounts[1]];
return ForeignBridge.new(3, authorities).then(function(_) {
assert(false, "Contract should fail to deploy");
}, function(err) {
// do nothing
})
return ForeignBridge.new(3, authorities, 0)
.then(function() {
assert(false, "Contract should fail to deploy");
}, helpers.ignoreExpectedError)
})
it("should allow a single authority to confirm a deposit", function() {
var meta;
var requiredSignatures = 1;
var estimatedGasCostOfWithdraw = 0;
var authorities = [accounts[0], accounts[1]];
var userAccount = accounts[2];
var value = web3.toWei(1, "ether");
var hash = "0xe55bb43c36cdf79e23b4adc149cdded921f0d482e613c50c6540977c213bc408";
return ForeignBridge.new(requiredSignatures, authorities).then(function(instance) {
return ForeignBridge.new(requiredSignatures, authorities, estimatedGasCostOfWithdraw).then(function(instance) {
meta = instance;
return meta.deposit(userAccount, value, hash, { from: authorities[0] });
}).then(function(result) {
@ -68,12 +68,13 @@ contract('ForeignBridge', function(accounts) {
it("should require 2 authorities to confirm deposit", function() {
var meta;
var requiredSignatures = 2;
var estimatedGasCostOfWithdraw = 0;
var authorities = [accounts[0], accounts[1]];
var userAccount = accounts[2];
var value = web3.toWei(1, "ether");
var hash = "0xe55bb43c36cdf79e23b4adc149cdded921f0d482e613c50c6540977c213bc408";
return ForeignBridge.new(requiredSignatures, authorities).then(function(instance) {
return ForeignBridge.new(requiredSignatures, authorities, estimatedGasCostOfWithdraw).then(function(instance) {
meta = instance;
return meta.deposit(userAccount, value, hash, { from: authorities[0] });
}).then(function(result) {
@ -102,49 +103,52 @@ contract('ForeignBridge', function(accounts) {
it("should not be possible to do same deposit twice for same authority", function() {
var meta;
var requiredSignatures = 1;
var estimatedGasCostOfWithdraw = 0;
var authorities = [accounts[0], accounts[1]];
var userAccount = accounts[2];
var value = web3.toWei(1, "ether");
var hash = "0xe55bb43c36cdf79e23b4adc149cdded921f0d482e613c50c6540977c213bc408";
return ForeignBridge.new(requiredSignatures, authorities).then(function(instance) {
return ForeignBridge.new(requiredSignatures, authorities, estimatedGasCostOfWithdraw).then(function(instance) {
meta = instance;
return meta.deposit(userAccount, value, hash, { from: authorities[0] });
}).then(function(_) {
return meta.deposit(userAccount, value, hash, { from: authorities[0] });
}).then(function(result) {
assert(false, "doing same deposit twice from same authority should fail");
}, function(err) {
return meta.deposit(userAccount, value, hash, { from: authorities[0] })
.then(function() {
assert(false, "doing same deposit twice from same authority should fail");
}, helpers.ignoreExpectedError)
})
})
it("should not allow non-authorities to execute deposit", function() {
var meta;
var requiredSignatures = 1;
var estimatedGasCostOfWithdraw = 0;
var authorities = [accounts[0], accounts[1]];
var userAccount = accounts[2];
var value = web3.toWei(1, "ether");
var hash = "0xe55bb43c36cdf79e23b4adc149cdded921f0d482e613c50c6540977c213bc408";
return ForeignBridge.new(requiredSignatures, authorities).then(function(instance) {
return ForeignBridge.new(requiredSignatures, authorities, estimatedGasCostOfWithdraw).then(function(instance) {
meta = instance;
return meta.deposit(userAccount, value, hash, { from: userAccount });
}).then(function(result) {
assert(false, "should fail");
}, function(err) {
return meta.deposit(userAccount, value, hash, { from: userAccount })
.then(function() {
assert(false, "should fail");
}, helpers.ignoreExpectedError)
})
})
it("should ignore misbehaving authority when confirming deposit", function() {
var meta;
var requiredSignatures = 2;
var estimatedGasCostOfWithdraw = 0;
var authorities = [accounts[0], accounts[1], accounts[2]];
var userAccount = accounts[3];
var invalidValue = web3.toWei(2, "ether");
var value = web3.toWei(1, "ether");
var hash = "0xe55bb43c36cdf79e23b4adc149cdded921f0d482e613c50c6540977c213bc408";
return ForeignBridge.new(requiredSignatures, authorities).then(function(instance) {
return ForeignBridge.new(requiredSignatures, authorities, estimatedGasCostOfWithdraw).then(function(instance) {
meta = instance;
return meta.deposit(userAccount, value, hash, { from: authorities[0] });
}).then(function(result) {
@ -174,77 +178,128 @@ contract('ForeignBridge', function(accounts) {
it("should not allow user to transfer value they don't have to home", function() {
var meta;
var requiredSignatures = 1;
var estimatedGasCostOfWithdraw = 0;
var authorities = [accounts[0], accounts[1]];
var userAccount = accounts[2];
var homeGasPrice = web3.toBigNumber(10000);
var recipientAccount = accounts[3];
var userValue = web3.toWei(3, "ether");
var transferedValue = web3.toWei(4, "ether");
var hash = "0xe55bb43c36cdf79e23b4adc149cdded921f0d482e613c50c6540977c213bc408";
return ForeignBridge.new(requiredSignatures, authorities).then(function(instance) {
return ForeignBridge.new(requiredSignatures, authorities, estimatedGasCostOfWithdraw).then(function(instance) {
meta = instance;
return meta.deposit(userAccount, userValue, hash, { from: authorities[0] });
}).then(function(result) {
return meta.transferHomeViaRelay(recipientAccount, transferedValue, { from: userAccount });
}).then(function(result) {
assert(false, "transferHomeViaRelay should fail");
}, function(err) {
return meta.transferHomeViaRelay(recipientAccount, transferedValue, homeGasPrice, { from: userAccount })
.then(function() {
assert(false, "transferHomeViaRelay should fail");
}, helpers.ignoreExpectedError)
})
})
it("should fail to transfer 0 value to home", function() {
var meta;
var requiredSignatures = 1;
var estimatedGasCostOfWithdraw = 0;
var authorities = [accounts[0], accounts[1]];
var userAccount = accounts[2];
var homeGasPrice = web3.toBigNumber(10000);
var recipientAccount = accounts[3];
var userValue = web3.toWei(3, "ether");
var transferedValue = web3.toWei(0, "ether");
var hash = "0xe55bb43c36cdf79e23b4adc149cdded921f0d482e613c50c6540977c213bc408";
return ForeignBridge.new(requiredSignatures, authorities).then(function(instance) {
return ForeignBridge.new(requiredSignatures, authorities, estimatedGasCostOfWithdraw).then(function(instance) {
meta = instance;
return meta.deposit(userAccount, userValue, hash, { from: authorities[0] });
}).then(function(result) {
return meta.transferHomeViaRelay(recipientAccount, transferedValue, { from: userAccount });
}).then(function(result) {
assert(false, "transferHomeViaRelay should fail");
}, function(err) {
return meta.transferHomeViaRelay(recipientAccount, transferedValue, homeGasPrice, { from: userAccount })
.then(function() {
assert(false, "transferHomeViaRelay should fail");
}, helpers.ignoreExpectedError)
})
})
it("should allow user to trigger withdraw", function() {
it("should fail to transfer more than balance home", function() {
var meta;
var requiredSignatures = 1;
var estimatedGasCostOfWithdraw = 0;
var authorities = [accounts[0], accounts[1]];
var userAccount = accounts[2];
var homeGasPrice = web3.toBigNumber(10000);
var recipientAccount = accounts[3];
var userValue = web3.toWei(3, "ether");
var transferedValue = web3.toWei(4, "ether");
var hash = "0xe55bb43c36cdf79e23b4adc149cdded921f0d482e613c50c6540977c213bc408";
return ForeignBridge.new(requiredSignatures, authorities, estimatedGasCostOfWithdraw).then(function(instance) {
meta = instance;
return meta.deposit(userAccount, userValue, hash, { from: authorities[0] });
}).then(function(result) {
return meta.transferHomeViaRelay(recipientAccount, transferedValue, homeGasPrice, { from: userAccount })
.then(function() {
assert(false, "transferHomeViaRelay should fail");
}, helpers.ignoreExpectedError)
})
})
it("should fail to transfer home with value that gets entirely burned on gas", function() {
var meta;
var requiredSignatures = 1;
var estimatedGasCostOfWithdraw = web3.toBigNumber(10000);
var authorities = [accounts[0], accounts[1]];
var userAccount = accounts[2];
var homeGasPrice = web3.toBigNumber(10000);
var recipientAccount = accounts[3];
var userValue = web3.toWei(3, "ether");
var transferedValue = estimatedGasCostOfWithdraw.times(homeGasPrice);
var hash = "0xe55bb43c36cdf79e23b4adc149cdded921f0d482e613c50c6540977c213bc408";
return ForeignBridge.new(requiredSignatures, authorities, estimatedGasCostOfWithdraw).then(function(instance) {
meta = instance;
return meta.deposit(userAccount, userValue, hash, { from: authorities[0] });
}).then(function(result) {
return meta.transferHomeViaRelay(recipientAccount, transferedValue, homeGasPrice, { from: userAccount })
.then(function() {
assert(false, "transferHomeViaRelay should fail");
}, helpers.ignoreExpectedError)
})
})
it("should allow user to transfer home", function() {
var meta;
var requiredSignatures = 1;
var estimatedGasCostOfWithdraw = web3.toBigNumber(10000);
var authorities = [accounts[0], accounts[1]];
var userAccount = accounts[2];
var userAccount2 = accounts[3];
var value = web3.toWei(3, "ether");
var value2 = web3.toWei(1, "ether");
var homeGasPrice = web3.toBigNumber(web3.toWei(3, "gwei"));
var transferedValue = estimatedGasCostOfWithdraw.times(homeGasPrice).plus(1);
var hash = "0xe55bb43c36cdf79e23b4adc149cdded921f0d482e613c50c6540977c213bc408";
return ForeignBridge.new(requiredSignatures, authorities).then(function(instance) {
return ForeignBridge.new(requiredSignatures, authorities, estimatedGasCostOfWithdraw).then(function(instance) {
meta = instance;
// top up balance so we can transfer
return meta.deposit(userAccount, value, hash, { from: authorities[0] });
}).then(function(result) {
return meta.transferHomeViaRelay(userAccount2, value2, { from: userAccount });
return meta.transferHomeViaRelay(userAccount2, transferedValue, homeGasPrice, { from: userAccount });
}).then(function(result) {
assert.equal(2, result.logs.length)
assert.equal("Transfer", result.logs[0].event);
assert.equal(userAccount, result.logs[0].args.from);
assert.equal("0x0000000000000000000000000000000000000000", result.logs[0].args.to);
assert.equal(value2, result.logs[0].args.tokens);
assert(transferedValue.equals(result.logs[0].args.tokens));
assert.equal("Withdraw", result.logs[1].event, "Event name should be Withdraw");
assert.equal(userAccount2, result.logs[1].args.recipient, "Event recipient should be equal to transaction recipient");
assert.equal(value2, result.logs[1].args.value, "Event value should match transaction value");
assert(transferedValue.equals(result.logs[1].args.value));
assert(homeGasPrice.equals(result.logs[1].args.homeGasPrice));
return Promise.all([
meta.balances.call(userAccount),
meta.balances.call(userAccount2)
])
}).then(function(result) {
assert.equal(web3.toWei(2, "ether"), result[0]);
assert.equal(web3.toWei(0, "ether"), result[1]);
assert(web3.toBigNumber(value).minus(transferedValue).equals(result[0]));
assert(web3.toBigNumber(web3.toWei(0, "ether")).equals(result[1]));
})
})
@ -252,9 +307,13 @@ contract('ForeignBridge', function(accounts) {
var meta;
var signature;
var requiredSignatures = 1;
var estimatedGasCostOfWithdraw = 0;
var authorities = [accounts[0], accounts[1]];
var message = "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111";
return ForeignBridge.new(requiredSignatures, authorities).then(function(instance) {
var recipientAccount = accounts[2];
var transactionHash = "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80";
var homeGasPrice = web3.toBigNumber(web3.toWei(3, "gwei"));
var message = helpers.createMessage(recipientAccount, web3.toBigNumber(1000), transactionHash, homeGasPrice);
return ForeignBridge.new(requiredSignatures, authorities, estimatedGasCostOfWithdraw).then(function(instance) {
meta = instance;
return helpers.sign(authorities[0], message);
}).then(function(result) {
@ -277,9 +336,13 @@ contract('ForeignBridge', function(accounts) {
it("should successfully submit signature but not trigger CollectedSignatures event", function() {
var meta;
var requiredSignatures = 2;
var estimatedGasCostOfWithdraw = 0;
var authorities = [accounts[0], accounts[1]];
var message = "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111";
return ForeignBridge.new(requiredSignatures, authorities).then(function(instance) {
var recipientAccount = accounts[2];
var transactionHash = "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80";
var homeGasPrice = web3.toBigNumber(web3.toWei(3, "gwei"));
var message = helpers.createMessage(recipientAccount, web3.toBigNumber(1000), transactionHash, homeGasPrice);
return ForeignBridge.new(requiredSignatures, authorities, estimatedGasCostOfWithdraw).then(function(instance) {
meta = instance;
return helpers.sign(authorities[0], message);
}).then(function(result) {
@ -294,10 +357,14 @@ contract('ForeignBridge', function(accounts) {
var signatures_for_message = [];
var signatures_for_message2 = [];
var requiredSignatures = 2;
var estimatedGasCostOfWithdraw = 0;
var authorities = [accounts[0], accounts[1]];
var message = "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111";
var message2 = "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111112";
return ForeignBridge.new(requiredSignatures, authorities).then(function(instance) {
var recipientAccount = accounts[2];
var transactionHash = "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80";
var homeGasPrice = web3.toBigNumber(web3.toWei(3, "gwei"));
var message = helpers.createMessage(recipientAccount, web3.toBigNumber(1000), transactionHash, homeGasPrice);
var message2 = helpers.createMessage(recipientAccount, web3.toBigNumber(2000), transactionHash, homeGasPrice);
return ForeignBridge.new(requiredSignatures, authorities, estimatedGasCostOfWithdraw).then(function(instance) {
meta = instance;
return Promise.all([
helpers.sign(authorities[0], message),
@ -347,76 +414,92 @@ contract('ForeignBridge', function(accounts) {
})
})
it("should not be possible to submit to short message", function() {
it("should not be possible to submit message that is too short", function() {
var meta;
var requiredSignatures = 1;
var estimatedGasCostOfWithdraw = 0;
var authorities = [accounts[0], accounts[1]];
var message = "0x1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111";
return ForeignBridge.new(requiredSignatures, authorities).then(function(instance) {
var recipientAccount = accounts[2];
var transactionHash = "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80";
var homeGasPrice = web3.toBigNumber(web3.toWei(3, "gwei"));
var message = helpers.createMessage(recipientAccount, web3.toBigNumber(1000), transactionHash, homeGasPrice);
var truncatedMessage = message.substr(0, 84);
return ForeignBridge.new(requiredSignatures, authorities, estimatedGasCostOfWithdraw).then(function(instance) {
meta = instance;
return helpers.sign(authorities[0], message);
}).then(function(result) {
return meta.submitSignature(result, message, { from: authorities[0] });
}).then(function(result) {
assert(false, "submitSignature should fail");
}, function (err) {
// nothing
return helpers.sign(authorities[0], truncatedMessage);
}).then(function(signature) {
return meta.submitSignature(signature, truncatedMessage, { from: authorities[0] })
.then(function() {
assert(false, "submitSignature should fail for message that is too short");
}, helpers.ignoreExpectedError)
})
})
it("should not be possible to submit different message then the signed one", function() {
var meta;
var requiredSignatures = 1;
var estimatedGasCostOfWithdraw = 0;
var authorities = [accounts[0], accounts[1]];
var message = "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111";
var message2 = "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111112";
return ForeignBridge.new(requiredSignatures, authorities).then(function(instance) {
var recipientAccount = accounts[2];
var transactionHash = "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80";
var homeGasPrice = web3.toBigNumber(web3.toWei(3, "gwei"));
var homeGasPrice2 = web3.toBigNumber(web3.toWei(2, "gwei"));
var message = helpers.createMessage(recipientAccount, web3.toBigNumber(1000), transactionHash, homeGasPrice);
var message2 = helpers.createMessage(recipientAccount, web3.toBigNumber(1000), transactionHash, homeGasPrice2);
return ForeignBridge.new(requiredSignatures, authorities, estimatedGasCostOfWithdraw).then(function(instance) {
meta = instance;
return helpers.sign(authorities[0], message);
}).then(function(result) {
return meta.submitSignature(result, message2, { from: authorities[0] });
}).then(function(result) {
assert(false, "submitSignature should fail");
}, function (err) {
// nothing
return meta.submitSignature(result, message2, { from: authorities[0] })
.then(function() {
assert(false, "submitSignature should fail");
}, helpers.ignoreExpectedError)
})
})
it("should not be possible to submit signature signed by different authority", function() {
var meta;
var requiredSignatures = 1;
var estimatedGasCostOfWithdraw = 0;
var authorities = [accounts[0], accounts[1]];
var message = "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111";
return ForeignBridge.new(requiredSignatures, authorities).then(function(instance) {
var recipientAccount = accounts[2];
var transactionHash = "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80";
var homeGasPrice = web3.toBigNumber(web3.toWei(3, "gwei"));
var homeGasPrice2 = web3.toBigNumber(web3.toWei(2, "gwei"));
var message = helpers.createMessage(recipientAccount, web3.toBigNumber(1000), transactionHash, homeGasPrice);
var message2 = helpers.createMessage(recipientAccount, web3.toBigNumber(1000), transactionHash, homeGasPrice2);
return ForeignBridge.new(requiredSignatures, authorities, estimatedGasCostOfWithdraw).then(function(instance) {
meta = instance;
return helpers.sign(authorities[0], message);
}).then(function(result) {
return meta.submitSignature(result, message, { from: authorities[1] });
}).then(function(result) {
assert(false, "submitSignature should fail");
}, function (err) {
// nothing
return meta.submitSignature(result, message, { from: authorities[1] })
.then(function() {
assert(false, "submitSignature should fail");
}, helpers.ignoreExpectedError)
})
})
it("should not be possible to submit signature twice", function() {
var meta;
var requiredSignatures = 1;
var estimatedGasCostOfWithdraw = 0;
var authorities = [accounts[0], accounts[1]];
var message = "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111";
var recipientAccount = accounts[2];
var transactionHash = "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80";
var homeGasPrice = web3.toBigNumber(web3.toWei(3, "gwei"));
var message = helpers.createMessage(recipientAccount, web3.toBigNumber(1000), transactionHash, homeGasPrice);
var signature;
return ForeignBridge.new(requiredSignatures, authorities).then(function(instance) {
return ForeignBridge.new(requiredSignatures, authorities, estimatedGasCostOfWithdraw).then(function(instance) {
meta = instance;
return helpers.sign(authorities[0], message);
}).then(function(result) {
signature = result;
return meta.submitSignature(signature, message, { from: authorities[0] });
}).then(function(_) {
return meta.submitSignature(signature, message, { from: authorities[0] });
}).then(function(_) {
assert(false, "submitSignature should fail");
}, function (_) {
// nothing
return meta.submitSignature(signature, message, { from: authorities[0] })
.then(function() {
assert(false, "submitSignature should fail");
}, helpers.ignoreExpectedError)
})
})
})

View File

@ -3,14 +3,10 @@ var Helpers = artifacts.require("HelpersTest");
// testing helpers
var helpers = require("./helpers/helpers");
contract("Helpers", function() {
contract("Helpers", function(accounts) {
it("`addressArrayContains` should function correctly", function() {
var addresses = [
"0xd4f04f18d253f831e5b9bcfde7f20450562e03da",
"0x46ee1abbcd7215364174f84c3cbc4770d45966e9",
"0x5ef98710ff315ded660fe757bf7a861114287c1e",
];
var otherAddress = "0x006e27b6a72e1f34c626762f3c4761547aff1421";
var addresses = accounts.slice(0, 3);
var otherAddress = accounts[3];
var library;
return Helpers.new().then(function(instance) {
library = instance;
@ -84,4 +80,205 @@ contract("Helpers", function() {
assert.equal(result, "131242344353464564564574574567456");
})
})
it("`hasEnoughValidSignatures` should pass for 1 required signature", function() {
var library;
var signature;
var requiredSignatures = 1;
var authorities = [accounts[0], accounts[1]];
var recipientAccount = accounts[2];
var value = web3.toBigNumber(web3.toWei(1, "ether"));
var homeGasPrice = web3.toBigNumber(10000);
var message = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80", homeGasPrice);
return Helpers.new().then(function(instance) {
library = instance;
}).then(function(result) {
return helpers.sign(authorities[0], message);
}).then(function(result) {
signature = result;
var vrs = helpers.signatureToVRS(signature);
return library.hasEnoughValidSignatures.call(
message,
[vrs.v],
[vrs.r],
[vrs.s],
authorities,
requiredSignatures
).then(function(result) {
assert(result, "should return true");
})
})
})
it("`verifySignatures` should pass for multiple signatures", function() {
var library;
var signatures = [];
var requiredSignatures = 3;
var authorities = [accounts[0], accounts[1], accounts[2]];
var recipientAccount = accounts[3];
var value = web3.toBigNumber(web3.toWei(1, "ether"));
var homeGasPrice = web3.toBigNumber(10000);
var message = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80", homeGasPrice);
return Helpers.new().then(function(instance) {
library = instance;
}).then(function(result) {
return helpers.sign(authorities[0], message);
}).then(function(result) {
signatures[0] = result;
return helpers.sign(authorities[1], message);
}).then(function(result) {
signatures[1] = result;
return helpers.sign(authorities[2], message);
}).then(function(result) {
signatures[2] = result;
var vrs = [];
vrs[0] = helpers.signatureToVRS(signatures[0]);
vrs[1] = helpers.signatureToVRS(signatures[1]);
vrs[2] = helpers.signatureToVRS(signatures[2]);
return library.hasEnoughValidSignatures.call(
message,
[vrs[0].v, vrs[1].v, vrs[2].v],
[vrs[0].r, vrs[1].r, vrs[2].r],
[vrs[0].s, vrs[1].s, vrs[2].s],
authorities,
requiredSignatures
).then(function(result) {
assert(result, "should return true");
})
})
})
it("`verifySignatures` should fail for signature for other message", function() {
var library;
var signature;
var requiredSignatures = 1;
var authorities = [accounts[0], accounts[1]];
var recipientAccount = accounts[2];
var value = web3.toBigNumber(web3.toWei(1, "ether"));
var homeGasPrice = web3.toBigNumber(10000);
var homeGasPrice2 = web3.toBigNumber(100);
var message = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80", homeGasPrice);
var message2 = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80", homeGasPrice2);
return Helpers.new().then(function(instance) {
library = instance;
}).then(function(result) {
return helpers.sign(authorities[0], message);
}).then(function(result) {
signature = result;
var vrs = helpers.signatureToVRS(signature);
return library.hasEnoughValidSignatures.call(
message2,
[vrs.v],
[vrs.r],
[vrs.s],
authorities,
requiredSignatures
).then(function(result) {
assert.equal(result, false, "should return false");
})
})
})
it("`verifySignatures` should fail if signer not in addresses", function() {
var library;
var signature;
var requiredSignatures = 1;
var authorities = [accounts[0], accounts[1]];
var recipientAccount = accounts[2];
var value = web3.toBigNumber(web3.toWei(1, "ether"));
var homeGasPrice = web3.toBigNumber(10000);
var message = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80", homeGasPrice);
return Helpers.new().then(function(instance) {
library = instance;
}).then(function(result) {
return helpers.sign(accounts[3], message);
}).then(function(result) {
signature = result;
var vrs = helpers.signatureToVRS(signature);
return library.hasEnoughValidSignatures.call(
message,
[vrs.v],
[vrs.r],
[vrs.s],
authorities,
requiredSignatures
).then(function(result) {
assert.equal(result, false, "should return false");
})
})
})
it("`verifySignatures` should fail for not enough signatures", function() {
var library;
var signature;
var requiredSignatures = 2;
var authorities = [accounts[0], accounts[1]];
var recipientAccount = accounts[2];
var value = web3.toBigNumber(web3.toWei(1, "ether"));
var homeGasPrice = web3.toBigNumber(10000);
var message = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80", homeGasPrice);
return Helpers.new().then(function(instance) {
library = instance;
}).then(function(result) {
return helpers.sign(authorities[0], message);
}).then(function(result) {
signature = result;
var vrs = helpers.signatureToVRS(signature);
return library.hasEnoughValidSignatures.call(
message,
[vrs.v],
[vrs.r],
[vrs.s],
authorities,
requiredSignatures
).then(function(result) {
assert.equal(result, false, "should return false");
})
})
})
it("`verifySignatures` should fail for duplicated signature", function() {
var library;
var signature;
var requiredSignatures = 2;
var authorities = [accounts[0], accounts[1]];
var recipientAccount = accounts[2];
var value = web3.toBigNumber(web3.toWei(1, "ether"));
var homeGasPrice = web3.toBigNumber(10000);
var message = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80", homeGasPrice);
return Helpers.new().then(function(instance) {
library = instance;
}).then(function(result) {
return helpers.sign(authorities[0], message);
}).then(function(result) {
signature = result;
var vrs = helpers.signatureToVRS(signature);
return library.hasEnoughValidSignatures.call(
message,
[vrs.v, vrs.v],
[vrs.r, vrs.r],
[vrs.s, vrs.r],
authorities,
requiredSignatures
).then(function(result) {
assert.equal(result, false, "should return false");
})
})
})
})

View File

@ -76,18 +76,23 @@ module.exports.getBalances = getBalances;
// returns hex string of the bytes of the message
// composed from `recipient`, `value` and `transactionHash`
// that is relayed from `foreign` to `home` on withdraw
function createMessage(recipient, value, transactionHash) {
function createMessage(recipient, value, transactionHash, homeGasPrice) {
web3._extend.utils.isBigNumber(value);
recipient = strip0x(recipient);
assert.equal(recipient.length, 20 * 2);
var value = strip0x(bigNumberToPaddedBytes32(value));
assert.equal(value.length, 64);
transactionHash = strip0x(transactionHash);
assert.equal(transactionHash.length, 32 * 2);
var value = strip0x(bigNumberToPaddedBytes32(value));
assert.equal(value.length, 64);
var message = "0x" + recipient + value + transactionHash;
var expectedMessageLength = (20 + 32 + 32) * 2 + 2;
web3._extend.utils.isBigNumber(homeGasPrice);
homeGasPrice = strip0x(bigNumberToPaddedBytes32(homeGasPrice));
assert.equal(homeGasPrice.length, 64);
var message = "0x" + recipient + value + transactionHash + homeGasPrice;
var expectedMessageLength = (20 + 32 + 32 + 32) * 2 + 2;
assert.equal(message.length, expectedMessageLength);
return message;
}
@ -102,3 +107,8 @@ function range(start, end) {
return result;
}
module.exports.range = range;
// just used to signal/document that we're explicitely ignoring/expecting an error
function ignoreExpectedError() {
}
module.exports.ignoreExpectedError = ignoreExpectedError;

View File

@ -25,20 +25,18 @@ contract('HomeBridge', function(accounts) {
it("should fail to deploy contract with not enough required signatures", function() {
var authorities = [accounts[0], accounts[1]];
return HomeBridge.new(0, authorities).then(function(_) {
assert(false, "Contract should fail to deploy");
}, function(err) {
// do nothing
})
return HomeBridge.new(0, authorities)
.then(function() {
assert(false, "Contract should fail to deploy");
}, helpers.ignoreExpectedError)
})
it("should fail to deploy contract with too many signatures", function() {
var authorities = [accounts[0], accounts[1]];
return HomeBridge.new(3, authorities, 0).then(function(_) {
assert(false, "Contract should fail to deploy");
}, function(err) {
// do nothing
})
return HomeBridge.new(3, authorities, 0)
.then(function() {
assert(false, "Contract should fail to deploy");
}, helpers.ignoreExpectedError)
})
it("should create deposit event", function() {
@ -67,54 +65,6 @@ contract('HomeBridge', function(accounts) {
})
})
it("isMessageValueSufficientToCoverRelay should work correctly", function() {
var homeBridge;
var gasPrice;
var requiredSignatures = 1;
var authorities = [accounts[0], accounts[1]];
var estimatedGasCostOfWithdraw = web3.toBigNumber(100000);
var userAccount = accounts[2];
var recipientAccount = accounts[3];
var estimatedWeiCostOfWithdraw;
var transactionHash = "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80";
return HomeBridge.new(
requiredSignatures,
authorities,
estimatedGasCostOfWithdraw
).then(function(instance) {
homeBridge = instance;
// do a test transaction so we can figure out the gasPrice
return homeBridge.sendTransaction({
value: 1,
from: userAccount
})
}).then(function(result) {
return web3.eth.getTransaction(result.tx);
}).then(function(tx) {
// getting the gas price dynamically instead of hardcoding it
// (which would require less code)
// is required because solidity-coverage sets it to 1
// and the usual default is 100000000000
gasPrice = tx.gasPrice;
estimatedWeiCostOfWithdraw = gasPrice.times(estimatedGasCostOfWithdraw);
return homeBridge.getWithdrawRelayCost();
}).then(function(result) {
assert(result.equals(estimatedWeiCostOfWithdraw), "getWithdrawRelayCost should return correct value");
var message = helpers.createMessage(recipientAccount, estimatedWeiCostOfWithdraw, transactionHash);
return homeBridge.isMessageValueSufficientToCoverRelay(message);
}).then(function(result) {
assert.equal(result, false, "exactly estimatedWeiCostOfWithdraw is not sufficient value");
var message = helpers.createMessage(recipientAccount, estimatedWeiCostOfWithdraw.plus(1), transactionHash);
return homeBridge.isMessageValueSufficientToCoverRelay(message);
}).then(function(result) {
assert.equal(result, true, "estimatedWeiCostOfWithdraw + 1 is sufficient value");
})
})
it("should allow correct withdraw without recipient paying for gas", function() {
var homeBridge;
var signature;
@ -124,7 +74,8 @@ contract('HomeBridge', function(accounts) {
var userAccount = accounts[2];
var recipientAccount = accounts[3];
var value = web3.toBigNumber(web3.toWei(1, "ether"));
var message = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80");
var homeGasPrice = web3.toBigNumber(0);
var message = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80", homeGasPrice);
return HomeBridge.new(
requiredSignatures,
@ -149,7 +100,7 @@ contract('HomeBridge', function(accounts) {
[vrs.r],
[vrs.s],
message,
{from: authorities[0]}
{from: userAccount, gasPrice: homeGasPrice}
);
}).then(function(result) {
console.log("estimated gas cost of HomeBridge.withdraw =", result);
@ -160,8 +111,7 @@ contract('HomeBridge', function(accounts) {
[vrs.r],
[vrs.s],
message,
// anyone can call withdraw (provided they have the message and required signatures)
{from: userAccount}
{from: userAccount, gasPrice: homeGasPrice}
);
}).then(function(result) {
assert.equal(1, result.logs.length, "Exactly one event should be created");
@ -186,7 +136,8 @@ contract('HomeBridge', function(accounts) {
var recipientAccount = accounts[3];
var chargerAccount = accounts[4];
var value = web3.toBigNumber(web3.toWei(1, "ether"));
var message = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80");
var homeGasPrice = web3.toBigNumber(10000);
var message = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80", homeGasPrice);
return HomeBridge.new(
requiredSignatures,
@ -215,8 +166,7 @@ contract('HomeBridge', function(accounts) {
[vrs.r],
[vrs.s],
message,
// anyone can call withdraw (provided they have the message and required signatures)
{ from: relayerAccount }
{ from: relayerAccount, gasPrice: homeGasPrice }
);
}).then(function(result) {
transactionResult = result;
@ -251,6 +201,90 @@ contract('HomeBridge', function(accounts) {
})
})
it("withdraw should fail if gas price != requested gas price", function() {
var homeBridge;
var signature;
var requiredSignatures = 1;
var authorities = [accounts[0], accounts[1]];
var estimatedGasCostOfWithdraw = 0;
var userAccount = accounts[2];
var recipientAccount = accounts[3];
var value = web3.toBigNumber(web3.toWei(1, "ether"));
var requestedGasPrice = web3.toBigNumber(100);
var usedGasPrice = web3.toBigNumber(1000);
var message = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80", requestedGasPrice);
return HomeBridge.new(
requiredSignatures,
authorities,
estimatedGasCostOfWithdraw
).then(function(instance) {
homeBridge = instance;
// "charge" HomeBridge so we can withdraw later
return homeBridge.sendTransaction({
value: value,
from: userAccount
})
}).then(function(result) {
return helpers.sign(authorities[0], message);
}).then(function(result) {
signature = result;
var vrs = helpers.signatureToVRS(signature);
return homeBridge.withdraw(
[vrs.v],
[vrs.r],
[vrs.s],
message,
{from: userAccount, gasPrice: usedGasPrice}
).then(function() {
assert(false, "withdraw should fail if used gas price != requested gas price");
}, helpers.ignoreExpectedError)
})
})
it("withdraw should succeed if gas price != requested gas price but sender is receiver", function() {
var homeBridge;
var signature;
var requiredSignatures = 1;
var authorities = [accounts[0], accounts[1]];
var estimatedGasCostOfWithdraw = 0;
var userAccount = accounts[2];
var recipientAccount = accounts[3];
var value = web3.toBigNumber(web3.toWei(1, "ether"));
var requestedGasPrice = web3.toBigNumber(100);
var usedGasPrice = web3.toBigNumber(1000);
var message = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80", requestedGasPrice);
return HomeBridge.new(
requiredSignatures,
authorities,
estimatedGasCostOfWithdraw
).then(function(instance) {
homeBridge = instance;
// "charge" HomeBridge so we can withdraw later
return homeBridge.sendTransaction({
value: value,
from: userAccount
})
}).then(function(result) {
return helpers.sign(authorities[0], message);
}).then(function(result) {
signature = result;
var vrs = helpers.signatureToVRS(signature);
return homeBridge.withdraw(
[vrs.v],
[vrs.r],
[vrs.s],
message,
{from: recipientAccount, gasPrice: usedGasPrice}
)
})
})
it("should revert withdraw if value is insufficient to cover costs", function() {
var homeBridge;
var initialBalances;
@ -262,7 +296,8 @@ contract('HomeBridge', function(accounts) {
var recipientAccount = accounts[3];
var chargerAccount = accounts[4];
var value = estimatedGasCostOfWithdraw;
var message = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80");
var homeGasPrice = web3.toBigNumber(10000);
var message = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80", homeGasPrice);
return HomeBridge.new(
requiredSignatures,
@ -291,13 +326,10 @@ contract('HomeBridge', function(accounts) {
[vrs.r],
[vrs.s],
message,
// anyone can call withdraw (provided they have the message and required signatures)
{ from: relayerAccount }
);
}).then(function(result) {
assert(false, "withdraw if value <= estimatedGasCostOfWithdraw should fail");
}, function (err) {
// nothing
{ from: relayerAccount, gasPrice: homeGasPrice }
).then(function() {
assert(false, "withdraw if value <= estimatedGasCostOfWithdraw should fail");
}, helpers.ignoreExpectedError)
})
})
@ -309,8 +341,9 @@ contract('HomeBridge', function(accounts) {
var userAccount = accounts[2];
var recipientAccount = accounts[3];
var value = web3.toBigNumber(web3.toWei(1, "ether"));
var message1 = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80");
var message2 = helpers.createMessage(recipientAccount, value, "0x038c79eb958a13aa71996bac27c628f33f227288bd27d5e157b97e55e08fd2b3");
var homeGasPrice = web3.toBigNumber(10000);
var message1 = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80", homeGasPrice);
var message2 = helpers.createMessage(recipientAccount, value, "0x038c79eb958a13aa71996bac27c628f33f227288bd27d5e157b97e55e08fd2b3", homeGasPrice);
return HomeBridge.new(
requiredSignatures,
@ -332,7 +365,7 @@ contract('HomeBridge', function(accounts) {
[vrs.r],
[vrs.s],
message1,
{from: authorities[0]}
{from: authorities[0], gasPrice: homeGasPrice}
);
}).then(function(result) {
assert.equal(1, result.logs.length, "Exactly one event should be created");
@ -348,7 +381,7 @@ contract('HomeBridge', function(accounts) {
[vrs.r],
[vrs.s],
message2,
{from: authorities[0]}
{from: authorities[0], gasPrice: homeGasPrice}
);
}).then(function(result) {
assert.equal(1, result.logs.length, "Exactly one event should be created");
@ -366,8 +399,9 @@ contract('HomeBridge', function(accounts) {
var userAccount = accounts[2];
var recipientAccount = accounts[3];
var value = web3.toBigNumber(web3.toWei(1, "ether"));
var message1 = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80");
var message2 = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80");
var homeGasPrice = web3.toBigNumber(10000);
var message1 = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80", homeGasPrice);
var message2 = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80", homeGasPrice);
return HomeBridge.new(
requiredSignatures,
@ -389,7 +423,7 @@ contract('HomeBridge', function(accounts) {
[vrs.r],
[vrs.s],
message1,
{from: authorities[0]}
{from: authorities[0], gasPrice: homeGasPrice}
);
}).then(function(result) {
assert.equal(1, result.logs.length, "Exactly one event should be created");
@ -405,12 +439,10 @@ contract('HomeBridge', function(accounts) {
[vrs.r],
[vrs.s],
message2,
{from: authorities[0]}
);
}).then(function(result) {
assert(false, "should fail");
}, function (err) {
// nothing
{from: authorities[0], gasPrice: homeGasPrice}
).then(function() {
assert(false, "should fail");
}, helpers.ignoreExpectedError)
})
})
@ -423,7 +455,8 @@ contract('HomeBridge', function(accounts) {
var userAccount = accounts[2];
var recipientAccount = accounts[3];
var value = web3.toBigNumber(web3.toWei(1, "ether"));
var message = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80");
var homeGasPrice = web3.toBigNumber(10000);
var message = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80", homeGasPrice);
return HomeBridge.new(
requiredSignatures,
@ -440,12 +473,10 @@ contract('HomeBridge', function(accounts) {
[vrs.r],
[vrs.s],
message.substr(0, 83),
{from: authorities[0]}
);
}).then(function(result) {
assert(false, "should fail");
}, function (err) {
// nothing
{from: authorities[0], gasPrice: homeGasPrice}
).then(function() {
assert(false, "should fail");
}, helpers.ignoreExpectedError)
})
})
@ -458,7 +489,8 @@ contract('HomeBridge', function(accounts) {
var userAccount = accounts[2];
var recipientAccount = accounts[3];
var value = web3.toBigNumber(web3.toWei(1, "ether"));
var message = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80");
var homeGasPrice = web3.toBigNumber(10000);
var message = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80", homeGasPrice);
// make message too short
message = message.substr(0, 83);
@ -486,11 +518,10 @@ contract('HomeBridge', function(accounts) {
[vrs.s],
message,
// anyone can call withdraw (provided they have the message and required signatures)
{from: userAccount}
);
}).then(function(result) {
assert(false, "withdraw should fail");
}, function(err) {
{from: userAccount, gasPrice: homeGasPrice}
).then(function() {
assert(false, "withdraw should fail");
}, helpers.ignoreExpectedError)
})
})
@ -503,7 +534,8 @@ contract('HomeBridge', function(accounts) {
var userAccount = accounts[2];
var recipientAccount = accounts[3];
var value = web3.toBigNumber(web3.toWei(1, "ether"));
var message = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80");
var homeGasPrice = web3.toBigNumber(10000);
var message = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80", homeGasPrice);
return HomeBridge.new(
requiredSignatures,
@ -529,11 +561,10 @@ contract('HomeBridge', function(accounts) {
[vrs.s],
message,
// anyone can call withdraw (provided they have the message and required signatures)
{from: userAccount}
);
}).then(function(result) {
assert(false, "should fail");
}, function(err) {
{from: userAccount, gasPrice: homeGasPrice}
).then(function() {
assert(false, "should fail");
}, helpers.ignoreExpectedError)
})
})
@ -546,7 +577,8 @@ contract('HomeBridge', function(accounts) {
var userAccount = accounts[2];
var recipientAccount = accounts[3];
var value = web3.toBigNumber(web3.toWei(1, "ether"));
var message = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80");
var homeGasPrice = web3.toBigNumber(10000);
var message = helpers.createMessage(recipientAccount, value, "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80", homeGasPrice);
return HomeBridge.new(
requiredSignatures,
@ -572,11 +604,10 @@ contract('HomeBridge', function(accounts) {
[vrs.s, vrs.s],
message,
// anyone can call withdraw (provided they have the message and required signatures)
{from: userAccount}
);
}).then(function(result) {
assert(false, "should fail");
}, function(err) {
{from: userAccount, gasPrice: homeGasPrice}
).then(function() {
assert(false, "should fail");
}, helpers.ignoreExpectedError)
})
})
})

View File

@ -1,11 +1,12 @@
var Message = artifacts.require("MessageTest");
var helpers = require("./helpers/helpers");
contract("Message", function() {
var recipientAccount = "0x006e27b6a72e1f34c626762f3c4761547aff1421";
contract("Message", function(accounts) {
var recipientAccount = accounts[0];
var value = web3.toBigNumber(web3.toWei(1, "ether"));
var transactionHash = "0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80";
var message = helpers.createMessage(recipientAccount, value, transactionHash);
var homeGasPrice = web3.toBigNumber(web3.toWei(3, "gwei"));
var message = helpers.createMessage(recipientAccount, value, transactionHash, homeGasPrice);
it("should extract value", function() {
return Message.new().then(function(instance) {
@ -30,4 +31,12 @@ contract("Message", function() {
assert.equal(result, transactionHash);
})
})
it("should extract homeGasPrice", function() {
return Message.new().then(function(instance) {
return instance.getHomeGasPrice.call(message)
}).then(function(result) {
assert(result.equals(homeGasPrice));
})
})
})

View File

@ -1,6 +1,7 @@
var MessageSigning = artifacts.require("MessageSigningTest");
var helpers = require("./helpers/helpers");
contract("MessageSigning", function() {
contract("MessageSigning", function(accounts) {
it("should recover address from signed message", function() {
var signature = "0xb585c41f3cceb2ff9b5c033f2edbefe93415bde365489c989bad8cef3b18e38148a13e100608a29735d709fe708926d37adcecfffb32b1d598727028a16df5db1b";
var message = "0xdeadbeaf";
@ -32,9 +33,9 @@ contract("MessageSigning", function() {
return MessageSigning.new().then(function(instance) {
return instance.recoverAddressFromSignedMessage.call(signature, message)
}).then(function(result) {
assert(false, "should fail because signature is too short");
}, function(err) {
})
.then(function() {
assert(false, "should fail because signature is too short");
}, helpers.ignoreExpectedError)
})
})
})