Merge pull request #114 from paritytech/snd-issue-112
fix #112 - users were able to burn value during withdraw
This commit is contained in:
commit
396198781d
|
@ -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"
|
||||
|
|
18
README.md
18
README.md
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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` can’t 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];
|
||||
}
|
||||
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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": ""
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -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!([]);
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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()));
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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");
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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));
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue