diff --git a/.gitignore b/.gitignore index 7181d54..e1ae25f 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,5 @@ examples/db.toml jsTests/package-lock.json node_modules compiled_contracts + +integration-tests/tmp diff --git a/.travis.yml b/.travis.yml index 484b204..86c26ed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,8 +13,13 @@ matrix: - sudo add-apt-repository ppa:ethereum/ethereum -y - sudo apt-get update -y - sudo apt-get install solc -y + - wget https://parity-downloads-mirror.parity.io/v1.8.6/x86_64-unknown-linux-gnu/parity + - chmod +x parity + - cp parity ${HOME}/bin + - export PATH=${HOME}/bin:${PATH} + - cd integration-tests script: - - cargo test --all + - env BACKTRACE=1 cargo test --all -- --nocapture - language: rust rust: beta cache: cargo @@ -23,8 +28,13 @@ matrix: - sudo add-apt-repository ppa:ethereum/ethereum -y - sudo apt-get update -y - sudo apt-get install solc -y + - wget https://parity-downloads-mirror.parity.io/v1.8.6/x86_64-unknown-linux-gnu/parity + - chmod +x parity + - cp parity ${HOME}/bin + - export PATH=${HOME}/bin:${PATH} + - cd integration-tests script: - - cargo test --all + - env BACKTRACE=1 cargo test --all -- --nocapture - language: rust rust: nightly cache: cargo @@ -33,8 +43,13 @@ matrix: - sudo add-apt-repository ppa:ethereum/ethereum -y - sudo apt-get update -y - sudo apt-get install solc -y + - wget https://parity-downloads-mirror.parity.io/v1.8.6/x86_64-unknown-linux-gnu/parity + - chmod +x parity + - cp parity ${HOME}/bin + - export PATH=${HOME}/bin:${PATH} + - cd integration-tests script: - - cargo test --all + - env BACKTRACE=1 cargo test --all -- --nocapture - language: node_js node_js: node cache: yarn diff --git a/Cargo.lock b/Cargo.lock index 393a672..d999f33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,96 +1,76 @@ -[root] -name = "tests" -version = "0.1.0" -dependencies = [ - "bridge 0.1.0", - "futures 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 7.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pretty_assertions 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "web3 0.0.5 (git+https://github.com/tomusdrw/rust-web3?branch=bridge)", -] - [[package]] name = "aho-corasick" -version = "0.5.3" +version = "0.6.4" 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.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "arrayvec" -version = "0.3.23" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "nodrop 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "odds 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)", + "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "backtrace" -version = "0.3.2" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "backtrace-sys" -version = "0.1.12" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "gcc 0.3.53 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "base64" -version = "0.6.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bitflags" -version = "0.7.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bridge" version = "0.1.0" dependencies = [ - "error-chain 0.11.0-rc.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-contract 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-derive 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "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.11 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.9 (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)", + "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "web3 0.0.5 (git+https://github.com/tomusdrw/rust-web3?branch=bridge)", + "web3 0.2.0 (git+https://github.com/tomusdrw/rust-web3?branch=bridge)", ] [[package]] @@ -98,55 +78,43 @@ name = "bridge-cli" version = "0.1.0" dependencies = [ "bridge 0.1.0", - "docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "docopt 0.8.3 (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)", + "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "byteorder" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bytes" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "cc" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "cfg-if" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "conv" -version = "0.3.3" +name = "crunchy" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "custom_derive" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "dbghelp-sys" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "difference" @@ -155,13 +123,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "docopt" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (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)", ] @@ -172,68 +140,131 @@ 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.8 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "error-chain" -version = "0.11.0-rc.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ethabi" -version = "4.0.0" +version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "error-chain 0.11.0-rc.2 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types 0.2.3 (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.11 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.3.1 (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)", + "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny-keccak 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ethabi-contract" -version = "4.0.0" +version = "5.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "ethabi-derive" -version = "4.0.0" +version = "5.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "ethabi 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "heck 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ethbloom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types-serialize 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fixed-hash 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny-keccak 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ethereum-types" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "ethbloom 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types-serialize 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fixed-hash 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "uint 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ethereum-types-serialize" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fixed-hash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "futures" -version = "0.1.15" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "futures-cpupool" -version = "0.1.5" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "gcc" -version = "0.3.53" +name = "heapsize" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "heck" @@ -245,55 +276,72 @@ dependencies = [ [[package]] name = "httparse" -version = "1.2.3" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "hyper" -version = "0.11.2" +version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-cpupool 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "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)", + "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mime 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "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.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.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "integration-tests" +version = "0.1.0" +dependencies = [ + "bridge 0.1.0", + "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)", + "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "web3 0.2.0 (git+https://github.com/tomusdrw/rust-web3?branch=bridge)", ] [[package]] name = "iovec" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "itoa" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "jsonrpc-core" -version = "7.1.0" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.2 (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)", + "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)", ] [[package]] @@ -312,77 +360,63 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "lazy_static" -version = "0.2.8" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "lazycell" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.30" +version = "0.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "log" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "magenta" -version = "0.1.1" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "conv 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "magenta-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "magenta-sys" -version = "0.1.1" +name = "log" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "memchr" -version = "0.1.11" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "memchr" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "mime" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "unicase 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "mio" -version = "0.6.10" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazycell 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "magenta 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "magenta-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -394,8 +428,8 @@ name = "mio-uds" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -416,37 +450,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "nodrop" -version = "0.1.9" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num-traits" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "odds 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-traits" -version = "0.1.40" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "num_cpus" -version = "1.6.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "odds" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "owning_ref" version = "0.3.3" @@ -457,28 +491,27 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.4.5" +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.4 (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.4" +version = "0.2.11" 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.30 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (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)", ] [[package]] name = "percent-encoding" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -489,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" @@ -496,51 +539,62 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "rand" -version = "0.3.16" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", - "magenta 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "redox_syscall" -version = "0.1.31" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "regex" -version = "0.1.80" +version = "0.2.6" 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.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex-syntax" -version = "0.3.9" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "regex-syntax" -version = "0.4.1" +name = "relay" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "remove_dir_all" +version = "0.3.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)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "rustc-demangle" @@ -552,17 +606,12 @@ name = "rustc-hex" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "rustc-serialize" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "rustc_version" -version = "0.1.7" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -577,27 +626,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "semver" -version = "0.1.20" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.11" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde_derive" -version = "1.0.11" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive_internals 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive_internals" -version = "0.15.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -606,13 +663,13 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.2" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -620,6 +677,11 @@ name = "slab" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "slab" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "smallvec" version = "0.2.1" @@ -627,7 +689,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "smallvec" -version = "0.4.3" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -665,77 +727,75 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "tempdir" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "remove_dir_all 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +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_local" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[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.30 (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.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "time" -version = "0.1.38" +version = "0.1.39" 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.30 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tiny-keccak" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "tokio-core" -version = "0.1.9" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "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)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "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.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.3 (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.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-io" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "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.4.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -743,15 +803,15 @@ name = "tokio-proto" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (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)", "net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "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.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.3 (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.5 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -760,7 +820,7 @@ name = "tokio-service" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -768,24 +828,24 @@ name = "tokio-timer" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-uds" -version = "0.1.5" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "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)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "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.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.3 (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.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -793,15 +853,26 @@ name = "toml" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "uint" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "unicase" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -824,12 +895,12 @@ dependencies = [ [[package]] name = "utf8-ranges" -version = "0.1.3" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "utf8-ranges" -version = "1.0.0" +name = "version_check" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -839,24 +910,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "web3" -version = "0.0.5" -source = "git+https://github.com/tomusdrw/rust-web3?branch=bridge#0205d57b5e9734049cdbd570c431c3ad35b106fb" +version = "0.2.0" +source = "git+https://github.com/tomusdrw/rust-web3?branch=bridge#aad14c09b38453933c9059b601c77e8763618674" dependencies = [ - "arrayvec 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 7.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "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.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)", + "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)", + "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.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.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-uds 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -864,11 +937,30 @@ name = "winapi" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "winapi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "winapi-build" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "ws2_32-sys" version = "0.2.1" @@ -879,107 +971,114 @@ dependencies = [ ] [metadata] -"checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66" -"checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699" -"checksum arrayvec 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)" = "699e63a93b79d717e8c3b5eb1b28b7780d0d6d9e59a72eb769291c83b0c8dc67" -"checksum backtrace 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "72f9b4182546f4b04ebc4ab7f84948953a118bd6021a1b6a6c909e3e94f6be76" -"checksum backtrace-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "afccc5772ba333abccdf60d55200fa3406f8c59dcf54d5f7998c9107d3799c7c" -"checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9" -"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" -"checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d" -"checksum bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d828f97b58cc5de3e40c421d0cf2132d6b2da4ee0e11b8632fa838f0f9333ad6" +"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" +"checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661" +"checksum base64 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "229d032f1a99302697f10b27167ae6d03d49d032e6a8e2550e8d3fc13356d2b4" +"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" +"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23" +"checksum bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1b7db437d718977f6dc9b2e3fd6fc343c02ac6b899b73fdd2179163447bd9ce9" +"checksum cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "deaf9ec656256bb25b404c51ef50097207b9cbb29c933d31f92cae5a8a0ffee0" "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" -"checksum conv 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299" -"checksum custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9" -"checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850" +"checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" "checksum difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3304d19798a8e067e48d8e69b2c37f0b5e9b4e462504ad9e27e9f3fce02bba8" -"checksum docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3b5b93718f8b3e5544fcc914c43de828ca6c6ace23e0332c6080a2977b49787a" +"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 error-chain 0.11.0-rc.2 (registry+https://github.com/rust-lang/crates.io-index)" = "38d3a55d9a7a456748f2a3912c0941a5d9a68006eb15b3c3c9836b8420dc102d" -"checksum ethabi 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "56418d314deb23ce7b7b261ea53c50c98844989604a84bf410ff743c9fe08afd" -"checksum ethabi-contract 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98f8b3887766d303f17e9bcdf45ccce9588f428f0028d83a73b7a03da7c22757" -"checksum ethabi-derive 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7161ab469518bf5f90d8c3227526ab38d88f4d871923c62026b8b23565e23ad8" -"checksum futures 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a82bdc62350ca9d7974c760e9665102fc9d740992a528c2254aa930e53b783c4" -"checksum futures-cpupool 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a283c84501e92cade5ea673a2a7ca44f71f209ccdd302a3e0896f50083d2c5ff" -"checksum gcc 0.3.53 (registry+https://github.com/rust-lang/crates.io-index)" = "e8310f7e9c890398b0e80e301c4f474e9918d2b27fca8f48486ca775fa9ffc5a" +"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" +"checksum ethabi-derive 5.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9a357d3f4f249408af8d8b241b53b39a419328606825d3bb3fef841bb9d8ba13" +"checksum ethbloom 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f240172b976e2421fa5485e45cd45287bbdb56d742aa3a1d77005c49071a8518" +"checksum ethereum-types 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5cff74129deda8a155b729cad1a22dc3cdd08115abd1165079c519d0cab6917a" +"checksum ethereum-types-serialize 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ac59a21a9ce98e188f3dace9eb67a6c4a3c67ec7fbc7218cb827852679dc002" +"checksum fixed-hash 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "21c520ebc46522d519aec9cba2b7115d49cea707d771b772c46bec61aa0daeb8" +"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +"checksum futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "0bab5b5e94f5c31fc764ba5dd9ad16568aae5d4825538c01d6bca680c9bf94a7" +"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" +"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.3 (registry+https://github.com/rust-lang/crates.io-index)" = "af2f2dd97457e8fb1ae7c5a420db346af389926e36f43768b96f101546b04a07" -"checksum hyper 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "641abc3e3fcf0de41165595f801376e01106bca1fd876dda937730e477ca004c" -"checksum iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29d062ee61fccdf25be172e70f34c9f6efc597e1fb8f6526e8437b2046ab26be" -"checksum itoa 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ac17257442c2ed77dbc9fd555cf83c58b0c7f7d0e8f2ae08c0ac05c72842e1f6" -"checksum jsonrpc-core 7.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "903e5eee845f3d83c1436d12848d97b1247cf850ff06a8e1db2f1ce3543af2cf" +"checksum httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2f407128745b78abc95c0ffbe4e5d37427fdc0d45470710cfef8c44522a2e37" +"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" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" -"checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf" -"checksum lazycell 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3b585b7a6811fb03aa10e74b278a0f00f8dd9b45dc681f148bb29fa5cb61859b" -"checksum libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)" = "2370ca07ec338939e356443dac2296f581453c35fe1e3a3ed06023c49435f915" -"checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b" -"checksum magenta 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf0336886480e671965f794bc9b6fce88503563013d1bfb7a502c81fe3ac527" -"checksum magenta-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40d014c7011ac470ae28e2f76a02bfea4a8480f73e701353b49ad7a8d75f4699" -"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20" -"checksum memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbccc0e46f1ea47b9f17e6d67c5a96bd27030519c519c9c91327e31275a47b4" -"checksum mime 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "153f98dde2b135dece079e5478ee400ae1bab13afa52d66590eacfc40e912435" -"checksum mio 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "dbd91d3bfbceb13897065e97b2ef177a09a438cb33612b2d371bf568819a9313" +"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" +"checksum lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a6f08839bc70ef4a3fe1d566d5350f519c5912ea86be0df1740a7d247c7fc0ef" +"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 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" "checksum mio-uds 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1731a873077147b626d89cc6c2a0db6288d607496c5d10c0cfcf3adc697ec673" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" "checksum net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)" = "3a80f842784ef6c9a958b68b7516bc7e35883c614004dd94959a4dca1b716c09" -"checksum nodrop 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "52cd74cd09beba596430cc6e3091b74007169a56246e1262f0ba451ea95117b2" -"checksum num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "99843c856d68d8b4313b03a17e33c4bb42ae8f6610ea81b28abe076ac721b9b0" -"checksum num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aec53c34f2d0247c5ca5d32cca1478762f301740468ee9ee6dcb7a0dd7a0c584" -"checksum odds 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "c3df9b730298cea3a1c3faa90b7e2f9df3a9c400d0936d6015e6165734eefcba" +"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" +"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" +"checksum num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e7de20f146db9d920c45ee8ed8f71681fd9ade71909b48c3acbd766aa504cf10" +"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.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2f213159e512f8376b31539ff28708164963357c59394747aa24c82a12633dca" -"checksum parking_lot_core 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a25dd36576d01cca115881dc920b1f0dd0037303ed8cfa0c5d0a4966151757f7" -"checksum percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de154f638187706bde41d9b4738748933d64e6b37bdbffc0b47a97d16a6ae356" +"checksum parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3e7f7c9857874e54afeb950eebeae662b1e51a2493666d2ea4c0a5d91dcf0412" +"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.16 (registry+https://github.com/rust-lang/crates.io-index)" = "eb250fd207a4729c976794d03db689c9be1d634ab5a1c9da9492a13d8fecbcdf" -"checksum redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "8dde11f18c108289bef24469638a04dce49da56084f2d50618b226e47eb04509" -"checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f" -"checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b" -"checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957" -"checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db" +"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.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" "checksum rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "aee45432acc62f7b9a108cc054142dac51f979e69e71ddce7d6fc7adf29e817e" "checksum rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0ceb8ce7a5e520de349e1fa172baeba4a9e8d5ef06c47471863530bc4972ee1e" -"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" -"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" +"checksum rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9743a7670d88d5d52950408ecdb7c71d8986251ab604d4689dd2ca25c9bca69" "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" "checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d" -"checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac" -"checksum serde 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f7726f29ddf9731b17ff113c461e362c381d9d69433f79de4f3dd572488823e9" -"checksum serde_derive 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cf823e706be268e73e7747b147aa31c8f633ab4ba31f115efb57e5047c3a76dd" -"checksum serde_derive_internals 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37aee4e0da52d801acfbc0cc219eb1eda7142112339726e427926a6f6ee65d3a" -"checksum serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "48b04779552e92037212c3615370f6bd57a40ebba7f20e554ff9f55e41a69a7b" +"checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "db99f3919e20faa51bb2996057f5031d8685019b5a06139b1ce761da671b8526" +"checksum serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "f4ba7591cfe93755e89eeecdbcc668885624829b020050e6aec99c2a03bd3fd0" +"checksum serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6e03f1c9530c3fb0a0a5c9b826bdd9246a5921ae995d75f512ac917fc4dd55b5" +"checksum serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c9db7266c7d63a4c4b7fe8719656ccdd51acf1bed6124b174f933b009fb10bcb" "checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" +"checksum slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdeff4cd9ecff59ec7e3744cbca73dfe5ac35c2aedb2cfba8a1c715a18912e9d" "checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013" -"checksum smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8fcd03faf178110ab0334d74ca9631d77f94c8c11cc77fcb59538abf0025695d" +"checksum smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44db0ecb22921ef790d17ae13a3f6d15784183ff5f2a01aa32098c7498d2b4b9" "checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b" "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" "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.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6" -"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.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1697c4b57aeeb7a536b647165a2825faddffb1d3bad386d507709bd51a90bb14" -"checksum time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "d5d788d3aa77bc0ef3e9621256885555368b47bd495c13dd2e7413c89f845520" -"checksum tiny-keccak 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d52d12ad79e4063e0cb0ca5efa202ed7244b6ce4d25f4d3abe410b2a66128292" -"checksum tokio-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e85d419699ec4b71bfe35bbc25bb8771e52eff0471a7f75c853ad06e200b4f86" -"checksum tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4ab83e7adb5677e42e405fa4ceff75659d93c4d7d7dd22f52fcec59ee9f02af" +"checksum tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f73eebdb68c14bcb24aef74ea96079830e7fa7b31a6106e42ea7ee887c1e134e" +"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.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" -"checksum tokio-uds 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6116c71be48f8f1656551fd16458247fdd6c03201d7893ad81189055fcde03e8" +"checksum tokio-uds 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "65ae5d255ce739e8537221ed2942e0445f4b3b813daebac1c0050ddaaa3587f9" "checksum toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7540f4ffc193e0d3c94121edb19b055670d369f77d5804db11ae053a45b6e7e" -"checksum unicase 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2e01da42520092d0cd2d6ac3ae69eb21a22ad43ff195676b86f8c37f487d6b80" +"checksum uint 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53a4340c35703f926ec365c6797bb4a7a10bb6b9affe29ca385c9d804401f5e3" +"checksum unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284b6d3db520d67fbe88fd778c21510d1b0ba4a551e5d0fbb023d33405f6de8a" "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" -"checksum web3 0.0.5 (git+https://github.com/tomusdrw/rust-web3?branch=bridge)" = "" +"checksum web3 0.2.0 (git+https://github.com/tomusdrw/rust-web3?branch=bridge)" = "" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" diff --git a/Cargo.toml b/Cargo.toml index bf900f8..df522cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,2 @@ [workspace] -members = ["tests", "cli", "bridge"] +members = ["tests", "cli", "bridge", "integration-tests"] diff --git a/README.md b/README.md index 68b4055..fca0f16 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # bridge +[![Join the chat at https://gitter.im/paritytech/parity-bridge](https://badges.gitter.im/paritytech/parity-bridge.svg)](https://gitter.im/paritytech/parity-bridge?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + [![Build Status][travis-image]][travis-url] [![Solidity Coverage Status][coveralls-image]][coveralls-url] (contracts only) @@ -8,82 +10,118 @@ [coveralls-image]: https://coveralls.io/repos/github/paritytech/parity-bridge/badge.svg?branch=master [coveralls-url]: https://coveralls.io/github/paritytech/parity-bridge?branch=master -bridge between two ethereum blockchains, `home` and `foreign`. +the bridge is an +[ERC20 token](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md) +contract on one ethereum-based blockchain that is backed by ether on **another** ethereum-based blockchain. + +users can convert ether +on one chain into the same amount of ERC20 tokens on the other and back. +the bridge securely relays these conversions. + +**the bridge can mitigate scaling issues:** +by deploying a [proof-of-authority](https://paritytech.github.io/wiki/Proof-of-Authority-Chains.html) +network and bridging it to the Ethereum Foundation network ('mainnet') users can convert their mainnet ether +into ERC20 tokens on the PoA chain +and there transfer them with much lower transaction fees, +faster block times and unaffected by mainnet congestion. + +the users can withdraw their tokens worth of ether on the mainnet at any point. + +parity is using the bridge project to prototype +the system that will eventually connect ethereum and other non-parachains to +[polkadot](https://polkadot.io/). + +### next steps + +1. deploy to bridge **ethereum** and **kovan** (bridge authorities TBD) +2. make the bridge work with contract-based dynamic validator sets +3. after kovan hardfork 2: deploy to kovan again with dynamic validator set ### current functionality -the bridge allows users to deposit ether into a smart contract on `home` and get it on `foreign` in form of a token balance. -it also allows users to withdraw their tokens on `foreign` and get the equivalent ether on `home`. -on `foreign` users can freely transfer tokens between each other. +the bridge connects two chains `home` and `foreign`. + +when users deposit ether into the `HomeBridge` contract on `home` +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, 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`. -### next steps - -1. deploy to bridge **ethereum** and **kovan** with the kovan authorities being the immutable set of bridge authorities -2. make bridge work with contract-based dynamic validator set -3. after kovan hardfork 2: deploy to kovan again with dynamic validator set - -### eventual goals - -connect ethereum to polkadot - -### deposit ether into `HomeBridge` and get it in form of a token balance on `ForeignBridge` +### high level explanation of home ether -> foreign ERC20 relay `sender` deposits `value` into `HomeBridge`. the `HomeBridge` fallback function emits `Deposit(sender, value)`. -for each `Deposit` event on `HomeBridge` every authority makes a transaction + +for each `Deposit` event on `HomeBridge` every authority executes `ForeignBridge.deposit(sender, value, transactionHash)`. + once there are `ForeignBridge.requiredSignatures` such transactions with identical arguments and from distinct authorities then -`ForeignBridge.balances(sender)` is increased by `value` and -`ForeignBridge.Deposit(sender, value)` is emitted. +`ForeignBridge.balanceOf(sender)` is increased by `value`. -### withdraw balance on `ForeignBridge` and get it as ether on `home` chain +### high level explanation of foreign ERC20 -> home ether relay + +`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; +signs that message and executes `ForeignBridge.submitSignature(signature, message)`. +this collection of signatures is on `foreign` because transactions are free for the authorities on `foreign`, +but not free on `home`. -`sender` executes `ForeignBridge.transferHomeViaRelay(recipient, value)` -which checks and reduces `ForeignBridge.balances(sender)` by `value` and emits `ForeignBridge.Withdraw(recipient, value)`. -for each `ForeignBridge.Withdraw` every bridge authority creates a message containg -`value`, `recipient` and the `transactionHash` of the transaction containing the `ForeignBridge.Withdraw` event, -signs the message and makes a transaction `ForeignBridge.submitSignature(signature, message)`. -this collection of signatures on `foreign` is necessary because transactions are free -for authorities on `foreign`, since they are the authorities of `foreign`, but not free on `home`. once `ForeignBridge.requiredSignatures` signatures by distinct authorities are collected a `ForeignBridge.CollectedSignatures(authorityThatSubmittedLastSignature, messageHash)` event is emitted. + everyone (usually `authorityThatSubmittedLastSignature`) can then call `ForeignBridge.message(messageHash)` and `ForeignBridge.signature(messageHash, 0..requiredSignatures)` to look up the message and signatures and execute `HomeBridge.withdraw(vs, rs, ss, message)` and complete the withdraw. -### transfer on `foreign` +`HomeBridge.withdraw(vs, rs, ss, message)` recovers the addresses from the signatures, +checks that enough authorities in its authority list have signed and +finally transfers `value` ether ([minus the relay gas costs](#recipient-pays-relay-cost-to-relaying-authority)) +to `recipient`. -`sender` executes `ForeignBridge.transferLocal(recipient, value)` -which checks and reduces `ForeignBridge.balances(sender)` and increases `ForeignBridge.balances(recipient)` -by `value`. +### run truffle smart contract tests + +requires `yarn` to be `$PATH`. [installation instructions](https://yarnpkg.com/lang/en/docs/install/) + +``` +cd truffle +yarn test +``` ### build +requires `rust` and `cargo`: [installation instructions.](https://www.rust-lang.org/en-US/install.html) + +requires `solc` to be in `$PATH`: [installation instructions.](https://solidity.readthedocs.io/en/develop/installing-solidity.html) + +assuming you've cloned the bridge (`git clone git@github.com:paritytech/parity-bridge.git`) +and are in the project directory (`cd parity-bridge`) run: + ``` cargo build -p bridge-cli --release ``` -### cli options +to install copy `../target/release/bridge` into a folder that's in your `$PATH`. + +### run ``` -Ethereum-Kovan bridge. - Copyright 2017 Parity Technologies (UK) Limited - -Usage: - bridge --config --database - bridge -h | --help - -Options: - -h, --help Display help message and exit. +bridge --config config.toml --database db.toml ``` - `--config` - location of the configuration file. configuration file must exist -- `--database` - location of the database file. if there is no file at specified location, new bridge contracts will be deployed and new database will be created +- `--database` - location of the database file. + if there is no file at specified location, new bridge contracts will be deployed + and new database will be created ### configuration [file example](./examples/config.toml) @@ -108,9 +146,9 @@ bin = "contracts/KovanBridge.bin" [authorities] accounts = [ - "0x006e27b6a72e1f34c626762f3c4761547aff1421", - "0x006e27b6a72e1f34c626762f3c4761547aff1421", - "0x006e27b6a72e1f34c626762f3c4761547aff1421" + "0x006e27b6a72e1f34c626762f3c4761547aff1421", + "0x006e27b6a72e1f34c626762f3c4761547aff1421", + "0x006e27b6a72e1f34c626762f3c4761547aff1421" ] required_signatures = 2 @@ -183,7 +221,7 @@ checked_withdraw_confirm = 121 - `foreign_deploy` - block number at which foreign contract has been deployed - `checked_deposit_relay` - number of the last block for which an authority has relayed deposits to the foreign - `checked_withdraw_relay` - number of the last block for which an authority has relayed withdraws to the home -- `checked_withdraw_confirm` - number of the last block for which an authirty has confirmed withdraw +- `checked_withdraw_confirm` - number of the last block for which an authority has confirmed withdraw ### example run @@ -205,15 +243,6 @@ checked_withdraw_confirm = 121 ![withdraw](./res/withdraw.png) -### truffle tests - -[requires yarn to be installed](https://yarnpkg.com/lang/en/docs/install/) - -``` -cd truffle -yarn test -``` - ### recipient pays relay cost to relaying authority a bridge `authority` has to pay for gas (`cost`) to execute `HomeBridge.withdraw` when @@ -226,8 +255,8 @@ that shuts down an attack that enabled exhaustion of authorities funds on `home` read on for a more thorough explanation. parity-bridge connects a value-bearing ethereum blockchain `home` -(initally the ethereum foundation chain) -to a non-value-bearing PoA ethereum blockchain `foreign` (initally the kovan testnet). +(initially the ethereum foundation chain) +to a non-value-bearing PoA ethereum blockchain `foreign` (initially the kovan testnet). value-bearing means that the ether on that chain has usable value in the sense that in order to obtain it one has to either mine it (trade in electricity) @@ -244,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`. @@ -255,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 diff --git a/bridge/Cargo.toml b/bridge/Cargo.toml index 6eb4245..5e9120a 100644 --- a/bridge/Cargo.toml +++ b/bridge/Cargo.toml @@ -13,11 +13,14 @@ tokio-timer = "0.1.2" toml = "0.4.2" web3 = { git = "https://github.com/tomusdrw/rust-web3", branch = "bridge" } error-chain = "0.11.0-rc.2" -ethabi = "4.0" -ethabi-derive = "4.0" -ethabi-contract = "4.0" +ethabi = "5.1" +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" diff --git a/bridge/build.rs b/bridge/build.rs index 3d5b83a..30168db 100644 --- a/bridge/build.rs +++ b/bridge/build.rs @@ -1,18 +1,35 @@ use std::process::Command; fn main() { - // rerun build script if bridge contract has changed. - // without this cargo doesn't since the bridge contract - // is outside the crate directories - println!("cargo:rerun-if-changed=../contracts/bridge.sol"); - let exit_status = Command::new("solc") - .arg("--abi") - .arg("--bin") - .arg("--optimize") - .arg("--output-dir").arg("../compiled_contracts") - .arg("--overwrite") - .arg("../contracts/bridge.sol") - .status() - .unwrap_or_else(|e| panic!("Error compiling solidity contracts: {}", e)); - assert!(exit_status.success(), "There was an error while compiling contracts code."); + // rerun build script if bridge contract has changed. + // without this cargo doesn't since the bridge contract + // is outside the crate directories + println!("cargo:rerun-if-changed=../contracts/bridge.sol"); + + match Command::new("solc") + .arg("--abi") + .arg("--bin") + .arg("--optimize") + .arg("--output-dir").arg("../compiled_contracts") + .arg("--overwrite") + .arg("../contracts/bridge.sol") + .status() + { + Ok(exit_status) => { + if !exit_status.success() { + if let Some(code) = exit_status.code() { + panic!("`solc` exited with error exit status code `{}`", code); + } else { + panic!("`solc` exited because it was terminated by a signal"); + } + } + }, + Err(err) => { + if let std::io::ErrorKind::NotFound = err.kind() { + panic!("`solc` executable not found in `$PATH`. `solc` is required to compile the bridge contracts. please install it: https://solidity.readthedocs.io/en/develop/installing-solidity.html"); + } else { + panic!("an error occurred when trying to spawn `solc`: {}", err); + } + } + } } diff --git a/bridge/src/api.rs b/bridge/src/api.rs index c91d039..4699ef5 100644 --- a/bridge/src/api.rs +++ b/bridge/src/api.rs @@ -88,7 +88,7 @@ pub struct LogStreamInit { pub filter: FilterBuilder, pub request_timeout: Duration, pub poll_interval: Duration, - pub confirmations: u64, + pub confirmations: usize, } /// Contains all logs matching `LogStream` filter in inclusive range `[from, to]`. @@ -137,7 +137,7 @@ pub struct LogStream { state: LogStreamState, after: u64, filter: FilterBuilder, - confirmations: u64, + confirmations: usize, request_timeout: Duration, } @@ -154,7 +154,7 @@ impl Stream for LogStream { }, LogStreamState::FetchBlockNumber(ref mut future) => { let last_block = try_ready!(future.poll()).low_u64(); - let last_confirmed_block = last_block.saturating_sub(self.confirmations); + let last_confirmed_block = last_block.saturating_sub(self.confirmations as u64); if last_confirmed_block > self.after { let from = self.after + 1; let filter = self.filter.clone() diff --git a/bridge/src/bridge/deploy.rs b/bridge/src/bridge/deploy.rs index 840b9b5..8f47437 100644 --- a/bridge/src/bridge/deploy.rs +++ b/bridge/src/bridge/deploy.rs @@ -6,7 +6,7 @@ use web3::types::{TransactionRequest}; use app::App; use database::Database; use error::{Error, ErrorKind}; -use {api, ethabi}; +use api; pub enum Deployed { /// No existing database found. Deployed new contracts. @@ -44,14 +44,15 @@ impl Future for Deploy { Err(ErrorKind::MissingFile(_)) => { let main_data = self.app.home_bridge.constructor( self.app.config.home.contract.bin.clone().0, - ethabi::util::pad_u32(self.app.config.authorities.required_signatures), - self.app.config.authorities.accounts.iter().map(|a| a.0.clone()).collect::>(), - ethabi::util::pad_u32(self.app.config.estimated_gas_cost_of_withdraw) + self.app.config.authorities.required_signatures, + self.app.config.authorities.accounts.clone(), + self.app.config.estimated_gas_cost_of_withdraw ); let test_data = self.app.foreign_bridge.constructor( self.app.config.foreign.contract.bin.clone().0, - ethabi::util::pad_u32(self.app.config.authorities.required_signatures), - self.app.config.authorities.accounts.iter().map(|a| a.0.clone()).collect::>() + 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 { diff --git a/bridge/src/bridge/deposit_relay.rs b/bridge/src/bridge/deposit_relay.rs index 1a83cf2..b2f2bf6 100644 --- a/bridge/src/bridge/deposit_relay.rs +++ b/bridge/src/bridge/deposit_relay.rs @@ -19,7 +19,7 @@ fn deposits_filter(home: &home::HomeBridge, address: Address) -> FilterBuilder { fn deposit_relay_payload(home: &home::HomeBridge, foreign: &foreign::ForeignBridge, log: Log) -> Result { let raw_log = RawLog { - topics: log.topics.into_iter().map(|t| t.0).collect(), + topics: log.topics, data: log.data.0, }; let deposit_log = home.events().deposit().parse_log(raw_log)?; @@ -47,11 +47,11 @@ pub fn create_deposit_relay(app: Arc>, init: &Datab request_timeout: app.config.home.request_timeout, poll_interval: app.config.home.poll_interval, confirmations: app.config.home.required_confirmations, - filter: deposits_filter(&app.home_bridge, init.home_contract_address.clone()), + filter: deposits_filter(&app.home_bridge, init.home_contract_address), }; DepositRelay { logs: api::log_stream(app.connections.home.clone(), app.timer.clone(), logs_init), - foreign_contract: init.foreign_contract_address.clone(), + foreign_contract: init.foreign_contract_address, state: DepositRelayState::Wait, app, } @@ -73,13 +73,14 @@ impl Stream for DepositRelay { let next_state = match self.state { DepositRelayState::Wait => { let item = try_stream!(self.logs.poll()); + info!("got {} new deposits to relay", item.logs.len()); let deposits = item.logs .into_iter() .map(|log| deposit_relay_payload(&self.app.home_bridge, &self.app.foreign_bridge, log)) .collect::>>()? .into_iter() .map(|payload| TransactionRequest { - from: self.app.config.foreign.account.clone(), + from: self.app.config.foreign.account, to: Some(self.foreign_contract.clone()), gas: Some(self.app.config.txs.deposit_relay.gas.into()), gas_price: Some(self.app.config.txs.deposit_relay.gas_price.into()), @@ -95,6 +96,7 @@ impl Stream for DepositRelay { }) .collect::>(); + info!("relaying {} deposits", deposits.len()); DepositRelayState::RelayDeposits { future: join_all(deposits), block: item.to, @@ -102,6 +104,7 @@ impl Stream for DepositRelay { }, DepositRelayState::RelayDeposits { ref mut future, block } => { let _ = try_ready!(future.poll()); + info!("deposit relay completed"); DepositRelayState::Yield(Some(block)) }, DepositRelayState::Yield(ref mut block) => match block.take() { @@ -129,8 +132,8 @@ mod tests { let data = "000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0".from_hex().unwrap(); let log = Log { data: data.into(), - topics: vec!["0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c".parse().unwrap()], - transaction_hash: Some("0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364".parse().unwrap()), + topics: vec!["e1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c".into()], + transaction_hash: Some("884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364".into()), ..Default::default() }; diff --git a/bridge/src/bridge/withdraw_confirm.rs b/bridge/src/bridge/withdraw_confirm.rs index ccf9bdf..bf1669d 100644 --- a/bridge/src/bridge/withdraw_confirm.rs +++ b/bridge/src/bridge/withdraw_confirm.rs @@ -3,37 +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 { - let raw_log = RawLog { - topics: log.topics.into_iter().map(|t| t.0).collect(), - 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(&withdraw_log.value); - result[52..84].copy_from_slice(&hash); - Ok(result.into()) -} - -fn withdraw_submit_signature_payload(foreign: &foreign::ForeignBridge, withdraw_payload: Bytes, signature: H520) -> Bytes { - foreign.functions().submit_signature().input(signature.to_vec(), withdraw_payload.0).into() +fn withdraw_submit_signature_payload(foreign: &foreign::ForeignBridge, withdraw_message: Vec, 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. @@ -42,7 +29,7 @@ enum WithdrawConfirmState { Wait, /// Signing withdraws. SignWithdraws { - withdraws: Vec, + messages: Vec>, future: JoinAll>>>, block: u64, }, @@ -66,7 +53,7 @@ pub fn create_withdraw_confirm(app: Arc>, init: &Da WithdrawConfirm { logs: api::log_stream(app.connections.foreign.clone(), app.timer.clone(), logs_init), - foreign_contract: init.foreign_contract_address.clone(), + foreign_contract: init.foreign_contract_address, state: WithdrawConfirmState::Wait, app, } @@ -88,37 +75,45 @@ impl Stream for WithdrawConfirm { let next_state = match self.state { WithdrawConfirmState::Wait => { let item = try_stream!(self.logs.poll()); - let withdraws = item.logs + info!("got {} new withdraws to sign", item.logs.len()); + let withdraw_messages = item.logs .into_iter() - .map(|log| withdraw_confirm_sign_payload(&self.app.foreign_bridge, log)) - .collect::, _>>()?; + .map(|log| { + info!("withdraw is ready for signature submission. tx hash {}", log.transaction_hash.unwrap()); + Ok(MessageToMainnet::from_log(log)?.to_bytes()) + }) + .collect::, 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.clone(), bytes), + api::sign(&self.app.connections.foreign, self.app.config.foreign.account, Bytes(message)), self.app.config.foreign.request_timeout) }) .collect::>(); + 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, signature)| withdraw_submit_signature_payload(&app.foreign_bridge, withdraw, signature)) + .map(|(withdraw_message, signature)| { + withdraw_submit_signature_payload(&app.foreign_bridge, withdraw_message, signature) + }) .map(|payload| TransactionRequest { - from: app.config.foreign.account.clone(), + from: app.config.foreign.account, to: Some(foreign_contract.clone()), gas: Some(app.config.txs.withdraw_confirm.gas.into()), gas_price: Some(app.config.txs.withdraw_confirm.gas_price.into()), @@ -128,12 +123,14 @@ impl Stream for WithdrawConfirm { condition: None, }) .map(|request| { + info!("submitting signature"); app.timer.timeout( api::send_transaction(&app.connections.foreign, request), app.config.foreign.request_timeout) }) .collect::>(); + info!("submitting {} signatures", confirmations.len()); WithdrawConfirmState::ConfirmWithdraws { future: join_all(confirmations), block, @@ -141,10 +138,14 @@ impl Stream for WithdrawConfirm { }, WithdrawConfirmState::ConfirmWithdraws { ref mut future, block } => { let _ = try_ready!(future.poll()); + info!("submitting signatures complete"); WithdrawConfirmState::Yield(Some(block)) }, WithdrawConfirmState::Yield(ref mut block) => match block.take() { - None => WithdrawConfirmState::Wait, + None => { + info!("waiting for new withdraws that should get signed"); + WithdrawConfirmState::Wait + }, some => return Ok(some.into()), } }; @@ -152,40 +153,3 @@ impl Stream for WithdrawConfirm { } } } - -#[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!["0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364".parse().unwrap()], - transaction_hash: Some("0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364".parse().unwrap()), - ..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 = "0x8697c15331677e6ebccccaff3454fce5edbc8cca8697c15331677aff3454fce5edbc8cca8697c15331677e6ebccccaff3454fce5edbc8cca8697c15331677e6ebc".parse().unwrap(); - - let payload = withdraw_submit_signature_payload(&foreign, message, signature); - let expected: Bytes = "630cea8e000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000418697c15331677e6ebccccaff3454fce5edbc8cca8697c15331677aff3454fce5edbc8cca8697c15331677e6ebccccaff3454fce5edbc8cca8697c15331677e6ebc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364000000000000000000000000".from_hex().unwrap().into(); - assert_eq!(expected, payload); - } -} diff --git a/bridge/src/bridge/withdraw_relay.rs b/bridge/src/bridge/withdraw_relay.rs index bfce4a5..2077fbc 100644 --- a/bridge/src/bridge/withdraw_relay.rs +++ b/bridge/src/bridge/withdraw_relay.rs @@ -3,14 +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 message_to_mainnet::MessageToMainnet; +use signature::Signature; /// returns a filter for `ForeignBridge.CollectedSignatures` events fn collected_signatures_filter(foreign: &foreign::ForeignBridge, address: Address) -> FilterBuilder { @@ -32,17 +34,17 @@ fn signatures_payload(foreign: &foreign::ForeignBridge, required_signatures: u32 // convert web3::Log to ethabi::RawLog since ethabi events can // only be parsed from the latter let raw_log = RawLog { - topics: log.topics.into_iter().map(|t| t.0).collect(), + topics: log.topics.into_iter().map(|t| t.0.into()).collect(), data: log.data.0, }; let collected_signatures = foreign.events().collected_signatures().parse_log(raw_log)?; - if collected_signatures.authority != my_address.0 { + if collected_signatures.authority_responsible_for_relay != my_address.0.into() { + info!("bridge not responsible for relaying transaction to home. tx hash: {}", log.transaction_hash.unwrap()); // this authority is not responsible for relaying this transaction. // someone else will relay this transaction to home. return Ok(None); } let signature_payloads = (0..required_signatures).into_iter() - .map(|index| ethabi::util::pad_u32(index)) .map(|index| foreign.functions().signature().input(collected_signatures.message_hash, index)) .map(Into::into) .collect(); @@ -54,49 +56,14 @@ 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 { Wait, FetchMessagesSignatures { - future: Join>>>, JoinAll>>>>>>, - block: u64, - }, - FetchMessageValueSufficient { - future: JoinAll>>>, - messages: Vec, - signatures: Vec>, + future: Join< + JoinAll>>>, + JoinAll>>>>> + >, block: u64, }, RelayWithdraws { @@ -112,13 +79,13 @@ pub fn create_withdraw_relay(app: Arc>, init: &Data request_timeout: app.config.foreign.request_timeout, poll_interval: app.config.foreign.poll_interval, confirmations: app.config.foreign.required_confirmations, - filter: collected_signatures_filter(&app.foreign_bridge, init.foreign_contract_address.clone()), + filter: collected_signatures_filter(&app.foreign_bridge, init.foreign_contract_address), }; WithdrawRelay { logs: api::log_stream(app.connections.foreign.clone(), app.timer.clone(), logs_init), - home_contract: init.home_contract_address.clone(), - foreign_contract: init.foreign_contract_address.clone(), + home_contract: init.home_contract_address, + foreign_contract: init.foreign_contract_address, state: WithdrawRelayState::Wait, app, } @@ -141,13 +108,17 @@ impl Stream for WithdrawRelay { let next_state = match self.state { WithdrawRelayState::Wait => { let item = try_stream!(self.logs.poll()); + info!("got {} new signed withdraws to relay", item.logs.len()); let assignments = item.logs .into_iter() - .map(|log| signatures_payload( + .map(|log| { + info!("collected signature is ready for relay: tx hash: {}", log.transaction_hash.unwrap()); + signatures_payload( &self.app.foreign_bridge, self.app.config.authorities.required_signatures, - self.app.config.foreign.account.clone(), - log)) + self.app.config.foreign.account, + log) + }) .collect::>>()?; let (signatures, messages): (Vec<_>, Vec<_>) = assignments.into_iter() @@ -176,82 +147,71 @@ impl Stream for WithdrawRelay { .map(|calls| join_all(calls)) .collect::>(); - // wait for fetching of messages and signatures to complete + info!("fetching messages and signatures"); WithdrawRelayState::FetchMessagesSignatures { future: join_all(message_calls).join(join_all(signature_calls)), block: item.to, } }, WithdrawRelayState::FetchMessagesSignatures { ref mut future, block } => { - let (messages, signatures) = try_ready!(future.poll()); - assert_eq!(messages.len(), signatures.len()); + let (messages_raw, signatures_raw) = try_ready!(future.poll()); + info!("fetching messages and signatures complete"); + assert_eq!(messages_raw.len(), signatures_raw.len()); let app = &self.app; let home_contract = &self.home_contract; - let message_value_sufficient_payloads = messages + let messages = messages_raw .iter() .map(|message| { - message_value_sufficient_payload( - &app.home_bridge, - message + app.foreign_bridge.functions().message().output(message.0.as_slice()).map(Bytes) + }) + .collect::>>() + .map_err(error::Error::from)?; + + let signatures = signatures_raw + .iter() + .map(|signatures| + signatures.iter().map( + |signature| { + Signature::from_bytes( + app.foreign_bridge + .functions() + .signature() + .output(signature.0.as_slice())? + .as_slice()) + } ) - }) - .map(|payload| { - app.timer.timeout( - api::call(&app.connections.home, home_contract.clone(), payload), - app.config.home.request_timeout) - }) - .collect::>(); - - 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()); - - let app = &self.app; - let home_contract = &self.home_contract; + .collect::, Error>>() + .map_err(error::Error::from) + ) + .collect::>>()?; 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 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 - U256::from(is_message_value_sufficient.0.as_slice()) == U256::from(1) - }) - .map(|((message, signatures), _)| withdraw_relay_payload(&app.home_bridge, &signatures, message)) - .map(|payload| TransactionRequest { - from: app.config.home.account.clone(), - 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::>(); - // wait for relays to complete + + info!("relaying {} withdraws", relays.len()); WithdrawRelayState::RelayWithdraws { future: join_all(relays), block, @@ -259,10 +219,14 @@ impl Stream for WithdrawRelay { }, WithdrawRelayState::RelayWithdraws { ref mut future, block } => { let _ = try_ready!(future.poll()); + info!("relaying withdraws complete"); WithdrawRelayState::Yield(Some(block)) }, WithdrawRelayState::Yield(ref mut block) => match block.take() { - None => WithdrawRelayState::Wait, + None => { + info!("waiting for signed withdraws to relay"); + WithdrawRelayState::Wait + }, some => return Ok(some.into()), } }; @@ -275,20 +239,20 @@ impl Stream for WithdrawRelay { 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() { let foreign = foreign::ForeignBridge::default(); - let my_address = "0xaff3454fce5edbc8cca8697c15331677e6ebcccc".parse().unwrap(); + let my_address = "aff3454fce5edbc8cca8697c15331677e6ebcccc".into(); let data = "000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0".from_hex().unwrap(); let log = Log { data: data.into(), - topics: vec!["0xeb043d149eedb81369bec43d4c3a3a53087debc88d2525f13bfaa3eecda28b5c".parse().unwrap()], - transaction_hash: Some("0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364".parse().unwrap()), + topics: vec!["eb043d149eedb81369bec43d4c3a3a53087debc88d2525f13bfaa3eecda28b5c".into()], + transaction_hash: Some("884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364".into()), ..Default::default() }; @@ -305,32 +269,18 @@ mod tests { #[test] fn test_signatures_payload_not_ours() { let foreign = foreign::ForeignBridge::default(); - let my_address = "0xaff3454fce5edbc8cca8697c15331677e6ebcccd".parse().unwrap(); + let my_address = "aff3454fce5edbc8cca8697c15331677e6ebcccd".into(); let data = "000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebcccc00000000000000000000000000000000000000000000000000000000000000f0".from_hex().unwrap(); let log = Log { data: data.into(), - topics: vec!["0xeb043d149eedb81369bec43d4c3a3a53087debc88d2525f13bfaa3eecda28b5c".parse().unwrap()], - transaction_hash: Some("0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364".parse().unwrap()), + topics: vec!["eb043d149eedb81369bec43d4c3a3a53087debc88d2525f13bfaa3eecda28b5c".into()], + transaction_hash: Some("884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364".into()), ..Default::default() }; 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 = 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); - } } diff --git a/bridge/src/config.rs b/bridge/src/config.rs index eeec8b1..712cde4 100644 --- a/bridge/src/config.rs +++ b/bridge/src/config.rs @@ -8,7 +8,7 @@ use error::{ResultExt, Error}; use {toml}; const DEFAULT_POLL_INTERVAL: u64 = 1; -const DEFAULT_CONFIRMATIONS: u64 = 12; +const DEFAULT_CONFIRMATIONS: usize = 12; const DEFAULT_TIMEOUT: u64 = 5; /// Application config. @@ -57,7 +57,7 @@ pub struct Node { pub ipc: PathBuf, pub request_timeout: Duration, pub poll_interval: Duration, - pub required_confirmations: u64, + pub required_confirmations: usize, } impl Node { @@ -154,7 +154,7 @@ mod load { pub ipc: PathBuf, pub request_timeout: Option, pub poll_interval: Option, - pub required_confirmations: Option, + pub required_confirmations: Option, } #[derive(Deserialize)] @@ -230,7 +230,7 @@ home_deploy = { gas = 20 } let mut expected = Config { txs: Transactions::default(), home: Node { - account: "0x1B68Cb0B50181FC4006Ce572cF346e596E51818b".parse().unwrap(), + account: "1B68Cb0B50181FC4006Ce572cF346e596E51818b".into(), ipc: "/home.ipc".into(), contract: ContractConfig { bin: include_str!("../../compiled_contracts/HomeBridge.bin").from_hex().unwrap().into(), @@ -240,7 +240,7 @@ home_deploy = { gas = 20 } required_confirmations: 100, }, foreign: Node { - account: "0x0000000000000000000000000000000000000001".parse().unwrap(), + account: "0000000000000000000000000000000000000001".into(), contract: ContractConfig { bin: include_str!("../../compiled_contracts/ForeignBridge.bin").from_hex().unwrap().into(), }, @@ -251,9 +251,9 @@ home_deploy = { gas = 20 } }, authorities: Authorities { accounts: vec![ - "0x0000000000000000000000000000000000000001".parse().unwrap(), - "0x0000000000000000000000000000000000000002".parse().unwrap(), - "0x0000000000000000000000000000000000000003".parse().unwrap(), + "0000000000000000000000000000000000000001".into(), + "0000000000000000000000000000000000000002".into(), + "0000000000000000000000000000000000000003".into(), ], required_signatures: 2, }, @@ -299,7 +299,7 @@ required_signatures = 2 let expected = Config { txs: Transactions::default(), home: Node { - account: "0x1B68Cb0B50181FC4006Ce572cF346e596E51818b".parse().unwrap(), + account: "1B68Cb0B50181FC4006Ce572cF346e596E51818b".into(), ipc: "".into(), contract: ContractConfig { bin: include_str!("../../compiled_contracts/HomeBridge.bin").from_hex().unwrap().into(), @@ -309,7 +309,7 @@ required_signatures = 2 required_confirmations: 12, }, foreign: Node { - account: "0x0000000000000000000000000000000000000001".parse().unwrap(), + account: "0000000000000000000000000000000000000001".into(), ipc: "".into(), contract: ContractConfig { bin: include_str!("../../compiled_contracts/ForeignBridge.bin").from_hex().unwrap().into(), @@ -320,9 +320,9 @@ required_signatures = 2 }, authorities: Authorities { accounts: vec![ - "0x0000000000000000000000000000000000000001".parse().unwrap(), - "0x0000000000000000000000000000000000000002".parse().unwrap(), - "0x0000000000000000000000000000000000000003".parse().unwrap(), + "0000000000000000000000000000000000000001".into(), + "0000000000000000000000000000000000000002".into(), + "0000000000000000000000000000000000000003".into(), ], required_signatures: 2, }, diff --git a/bridge/src/database.rs b/bridge/src/database.rs index 836e03d..a53e832 100644 --- a/bridge/src/database.rs +++ b/bridge/src/database.rs @@ -74,8 +74,8 @@ checked_withdraw_confirm = 121 "#; let expected = Database { - home_contract_address: "0x49edf201c1e139282643d5e7c6fb0c7219ad1db7".parse().unwrap(), - foreign_contract_address: "0x49edf201c1e139282643d5e7c6fb0c7219ad1db8".parse().unwrap(), + home_contract_address: "49edf201c1e139282643d5e7c6fb0c7219ad1db7".into(), + foreign_contract_address: "49edf201c1e139282643d5e7c6fb0c7219ad1db8".into(), home_deploy: 100, foreign_deploy: 101, checked_deposit_relay: 120, diff --git a/bridge/src/lib.rs b/bridge/src/lib.rs index 3b8a7cf..aa1b0df 100644 --- a/bridge/src/lib.rs +++ b/bridge/src/lib.rs @@ -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; diff --git a/bridge/src/message_to_mainnet.rs b/bridge/src/message_to_mainnet.rs new file mode 100644 index 0000000..749850a --- /dev/null +++ b/bridge/src/message_to_mainnet.rs @@ -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 { + 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 { + 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 { + 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, + value_raw: u64, + sidenet_transaction_hash_raw: Vec, + 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() + } + } +} diff --git a/bridge/src/signature.rs b/bridge/src/signature.rs new file mode 100644 index 0000000..9be316c --- /dev/null +++ b/bridge/src/signature.rs @@ -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 { + 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 { + 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 { + 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, s_raw: Vec) -> 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() + } + } +} diff --git a/bridge/src/util.rs b/bridge/src/util.rs index 0004a7b..dcdb2a4 100644 --- a/bridge/src/util.rs +++ b/bridge/src/util.rs @@ -7,7 +7,7 @@ fn web3_topic(topic: ethabi::Topic) -> Option> { if t.is_empty() { None } else { - Some(t.into_iter().map(|x| H256(x)).collect()) + Some(t) } } diff --git a/cli/Cargo.toml b/cli/Cargo.toml index f97091a..f0bcbac 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -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" diff --git a/contracts/bridge.sol b/contracts/bridge.sol index 57745e0..c9d758d 100644 --- a/contracts/bridge.sol +++ b/contracts/bridge.sol @@ -1,12 +1,11 @@ -pragma solidity ^0.4.17; - +pragma solidity ^0.4.19; /// general helpers. /// `internal` so they get compiled into contracts using them. 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 +15,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 +26,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 +34,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 +75,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 +116,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 +142,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 +159,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 +177,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 +195,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 +211,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 +235,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 +273,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); @@ -264,165 +295,209 @@ contract ERC20 { } contract ForeignBridge { - struct SignaturesCollection { - /// Signed message. - bytes message; - /// Authorities who signed the message. - address[] signed; - /// Signaturs - bytes[] signatures; - } - /// Number of authorities signatures required to withdraw the money. /// - /// Must be lesser than number of authorities. - uint public requiredSignatures; + /// Must be less than number of authorities. + uint256 public requiredSignatures; + + uint256 public estimatedGasCostOfWithdraw; /// Contract authorities. - address[] public authorities; + mapping (address => bool) authorities; + + /// Pending mesages + mapping (bytes32 => bytes) messages; + /// ??? + mapping (bytes32 => bytes) signatures; + + /// Pending deposits and authorities who confirmed them + mapping (bytes32 => bool) messages_signed; + mapping (bytes32 => uint) num_messages_signed; /// Pending deposits and authorities who confirmed them - mapping (bytes32 => address[]) deposits; + mapping (bytes32 => bool) deposits_signed; + mapping (bytes32 => uint) num_deposits_signed; - /// List of authorities confirmed to set up ERC-20 token address - mapping (address => address[]) public token_address; - /// Token to work with ERC20 public erc20token; - - /// Event created on money deposit. - event TokenAddress(address token); - - /// Pending signatures and authorities who confirmed them - mapping (bytes32 => SignaturesCollection) signatures; - /// Event created on money deposit. - event Deposit(address recipient, uint value); + /// List of authorities confirmed to set up ERC-20 token address + mapping (bytes32 => bool) tokenAddressAprroval_signs; + mapping (address => uint256) num_tokenAddressAprroval_signs; + + /// triggered when relay of deposit from HomeBridge is complete + 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 authority, bytes32 messageHash); + event CollectedSignatures(address authorityResponsibleForRelay, bytes32 messageHash); + + /// Event created when new token address is set up. + event TokenAddress(address token); /// Constructor. function ForeignBridge( - uint requiredSignaturesParam, - address[] authoritiesParam + uint256 _requiredSignatures, + address[] _authorities, + uint256 _estimatedGasCostOfWithdraw ) public { - require(requiredSignaturesParam != 0); - require(requiredSignaturesParam <= authoritiesParam.length); - requiredSignatures = requiredSignaturesParam; - authorities = authoritiesParam; + require(_requiredSignatures != 0); + require(_requiredSignatures <= _authorities.length); + requiredSignatures = _requiredSignatures; + + for (uint i = 0; i < _authorities.length; i++) { + authorities[_authorities[i]] = true; + } + + estimatedGasCostOfWithdraw = _estimatedGasCostOfWithdraw; } - /// Multisig authority validation + /// require that sender is an authority modifier onlyAuthority() { - require(Helpers.addressArrayContains(authorities, msg.sender)); + require(authorities[msg.sender]); _; } - /// Set up the token address. + /// Set up the token address. It allows to set up or change + /// the ERC20 token address only if authorities confirmed this. + /// + /// Usage maps instead of arrey allows to reduce gas consumption /// /// token address (address) function setTokenAddress (ERC20 token) public onlyAuthority() { - // Protect duplicated request - require(!token_address[token].contains(msg.sender)); + // Duplicated deposits + bytes32 token_sender = keccak256(msg.sender, token); + require(!tokenAddressAprroval_signs[token_sender]); + tokenAddressAprroval_signs[token_sender]= true; + + uint signed = num_tokenAddressAprroval_signs[address(token)] + 1; + num_tokenAddressAprroval_signs[address(token)] = signed; - token_address[token].push(msg.sender); // TODO: this may cause troubles if requriedSignatures len is changed - if (token_address[token].length == requiredSignatures) { + if (signed == requiredSignatures) { erc20token = ERC20(token); TokenAddress(token); } } - /// Used to deposit money to the contract. + /// Used to transfer tokens to the `recipient`. + /// The bridge contract must own enough tokens to release them for + /// recipients. Tokens must be transfered to the bridge contract BEFORE + /// the first deposit will be performed. + /// + /// Usage maps instead of array allows to reduce gas consumption + /// from 91169 to 89348 (solc 0.4.19). /// /// 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() { + require(erc20token != address(0x0)); + // Protection from misbehaing authority - var hash = keccak256(recipient, value, transactionHash); + bytes32 hash_msg = keccak256(recipient, value, transactionHash); + bytes32 hash_sender = keccak256(msg.sender, hash_msg); // Duplicated deposits - require(!Helpers.addressArrayContains(deposits[hash], msg.sender)); + require(!deposits_signed[hash_sender]); + deposits_signed[hash_sender]= true; + + uint signed = num_deposits_signed[hash_msg] + 1; + num_deposits_signed[hash_msg] = signed; - deposits[hash].push(msg.sender); // TODO: this may cause troubles if requriedSignatures len is changed - if (deposits[hash].length == requiredSignatures) { + if (signed == requiredSignatures) { + // If the bridge contract does not own enough tokens to transfer + // it will couse funds lock on the home side of the bridge erc20token.transfer(recipient, value); Deposit(recipient, value); } } - /// Transfer `value` from `msg.sender`s local balance (on `foreign` chain) to `recipient` on `home` chain. - /// - /// immediately decreases `msg.sender`s local balance. - /// emits a `Withdraw` event which will be picked up by the bridge authorities. - /// bridge authorities will then sign off (by calling `submitSignature`) on a message containing `value`, - /// `recipient` and the `hash` of the transaction on `foreign` containing the `Withdraw` event. - /// 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 { - require(erc20token.allowance(msg.sender, this) >= value); - erc20token.transferFrom(msg.sender, this, value); + /// Used to transfer `value` of tokens from `_from`s balance on local + /// (`foreign`) chain to the same address (`_from`) on `home` chain. + /// Transfer of tokens within local (`foreign`) chain performed by usual + /// way through transfer method of the token contract. + /// In order to swap tokens to coins the owner (`_from`) must allow this + /// explicitly in the token contract by calling approveAndCall with address + /// of the bridge account. + /// The method locks tokens and emits a `Withdraw` event which will be + /// picked up by the bridge authorities. + /// Bridge authorities will then sign off (by calling `submitSignature`) on + /// a message containing `value`, the recipient (`_from`) and the `hash` of + /// the transaction on `foreign` containing the `Withdraw` event. + /// 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 the + /// recipient completing the transfer. + function receiveApproval(address _from, uint256 _value, ERC20 _tokenContract, bytes _msg) external returns(bool) { + require(erc20token != address(0x0)); + require(msg.sender == address(erc20token)); + require(erc20token.allowance(_from, this) >= _value); + erc20token.transferFrom(_from, this, _value); - balances[msg.sender] -= value; - Withdraw(recipient, value); - } + // Need to decide what to do with homeGasPrice from the original parity-bridge contract + Withdraw(_from, _value, 18000000000 wei); - /// Transfer `value` to `recipient` on this `foreign` chain. - /// - /// does not affect `home` chain. does not do a relay. - function transferLocal(address recipient, uint value) public { - require(balances[msg.sender] >= value); - // fails if value == 0, or if there is an overflow - require(balances[recipient] + value > balances[recipient]); - - balances[msg.sender] -= value; - balances[recipient] += value; - Transfer(msg.sender, recipient, value); + return true; } /// Should be used as sync tool /// /// Message is a message that should be relayed to main chain once authorities sign it. /// + /// Usage several maps instead of structure allows to reduce gas consumption + /// from 265102 to 242334 (solc 0.4.19). + /// /// 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); - var hash = keccak256(message); + require(message.length == 116); + bytes32 hash = keccak256(message); + bytes32 hash_sender = keccak256(msg.sender, hash); - // Duplicated signatures - require(!Helpers.addressArrayContains(signatures[hash].signed, msg.sender)); - signatures[hash].message = message; - signatures[hash].signed.push(msg.sender); - signatures[hash].signatures.push(signature); + uint signed = num_messages_signed[hash_sender] + 1; + + if (signed > 1) { + // Duplicated signatures + require(!messages_signed[hash_sender]); + } + else { + // check if it will really reduce gas usage in case of the second transaction + // with the same hash + messages[hash] = message; + } + messages_signed[hash_sender] = true; + + bytes32 sign_idx = keccak256(hash, (signed-1)); + signatures[sign_idx]= signature; + + num_messages_signed[hash_sender] = signed; // TODO: this may cause troubles if requiredSignatures len is changed - if (signatures[hash].signed.length == requiredSignatures) { + if (signed == requiredSignatures) { CollectedSignatures(msg.sender, hash); } } /// Get signature function signature(bytes32 hash, uint index) public view returns (bytes) { - return signatures[hash].signatures[index]; + bytes32 sign_idx = keccak256(hash, index); + return signatures[sign_idx]; } /// Get message function message(bytes32 hash) public view returns (bytes) { - return signatures[hash].message; + return messages[hash]; } -} \ No newline at end of file +} diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml new file mode 100644 index 0000000..b07b3fb --- /dev/null +++ b/integration-tests/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "integration-tests" +version = "0.1.0" +authors = ["snd "] + +[dependencies] +bridge = { path = "../bridge" } +futures = "0.1" +jsonrpc-core = "8.0" +web3 = { git = "https://github.com/tomusdrw/rust-web3", branch = "bridge" } +serde_json = "1.0" +pretty_assertions = "0.2.1" +tempdir = "0.3.5" +ethereum-types = "0.2" +tokio-core = "0.1.8" diff --git a/integration-tests/bridge_config.toml b/integration-tests/bridge_config.toml new file mode 100644 index 0000000..511aa8a --- /dev/null +++ b/integration-tests/bridge_config.toml @@ -0,0 +1,30 @@ +estimated_gas_cost_of_withdraw = 0 + +[home] +account = "0x00bd138abd70e2f00903268f3db08f2d25677c9e" +ipc = "./home.ipc" +required_confirmations = 0 + +[home.contract] +bin = "../compiled_contracts/HomeBridge.bin" + +[foreign] +account = "0x00bd138abd70e2f00903268f3db08f2d25677c9e" +ipc = "./foreign.ipc" +required_confirmations = 0 + +[foreign.contract] +bin = "../compiled_contracts/ForeignBridge.bin" + +[authorities] +accounts = [ + "0x00bd138abd70e2f00903268f3db08f2d25677c9e", +] +required_signatures = 1 + +[transactions] +home_deploy = { gas = 3000000 } +foreign_deploy = { gas = 3000000 } +deposit_relay = { gas = 3000000 } +withdraw_relay = { gas = 3000000 } +withdraw_confirm = { gas = 3000000 } diff --git a/integration-tests/password.txt b/integration-tests/password.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/integration-tests/password.txt @@ -0,0 +1 @@ + diff --git a/integration-tests/tests/basic_deposit_then_withdraw.rs b/integration-tests/tests/basic_deposit_then_withdraw.rs new file mode 100644 index 0000000..13f3752 --- /dev/null +++ b/integration-tests/tests/basic_deposit_then_withdraw.rs @@ -0,0 +1,273 @@ +/// spins up two parity nodes with the dev chain. +/// starts one bridge authority that connects the two. +/// does a deposit by sending ether to the HomeBridge. +/// asserts that the deposit got relayed to foreign chain. +/// does a withdraw by executing ForeignBridge.transferToHomeBridge. +/// asserts that the withdraw got relayed to home chain. + +extern crate tempdir; +extern crate ethereum_types; +extern crate web3; +extern crate tokio_core; +extern crate bridge; + +use std::process::Command; +use std::time::Duration; +use std::thread; +use std::path::Path; + +use tokio_core::reactor::Core; + +use web3::transports::ipc::Ipc; +use web3::api::Namespace; +use ethereum_types::{Address, U256}; + +const TMP_PATH: &str = "tmp"; + +fn parity_home_command() -> Command { + let mut command = Command::new("parity"); + command + .arg("--base-path").arg(format!("{}/home", TMP_PATH)) + .arg("--chain").arg("dev") + .arg("--ipc-path").arg("home.ipc") + .arg("--logging").arg("rpc=trace") + .arg("--jsonrpc-port").arg("8550") + .arg("--jsonrpc-apis").arg("all") + .arg("--port").arg("30310") + .arg("--gasprice").arg("0") + .arg("--reseal-min-period").arg("0") + .arg("--no-ws") + .arg("--no-dapps") + .arg("--no-ui"); + command +} + +fn parity_foreign_command() -> Command { + let mut command = Command::new("parity"); + command + .arg("--base-path").arg(format!("{}/foreign", TMP_PATH)) + .arg("--chain").arg("dev") + .arg("--ipc-path").arg("foreign.ipc") + .arg("--logging").arg("rpc=trace") + .arg("--jsonrpc-port").arg("8551") + .arg("--jsonrpc-apis").arg("all") + .arg("--port").arg("30311") + .arg("--gasprice").arg("0") + .arg("--reseal-min-period").arg("0") + .arg("--no-ws") + .arg("--no-dapps") + .arg("--no-ui"); + command +} + +fn address_from_str(string: &'static str) -> web3::types::Address { + web3::types::Address::from(&Address::from(string).0[..]) +} + +#[test] +fn test_basic_deposit_then_withdraw() { + if Path::new(TMP_PATH).exists() { + std::fs::remove_dir_all(TMP_PATH).expect("failed to remove tmp dir"); + } + let _tmp_dir = tempdir::TempDir::new(TMP_PATH).expect("failed to create tmp dir"); + + println!("\nbuild the bridge cli executable so we can run it later\n"); + assert!(Command::new("cargo") + .env("RUST_BACKTRACE", "1") + .current_dir("../cli") + .arg("build") + .status() + .expect("failed to build bridge cli") + .success()); + + // start a parity node that represents the home chain + let mut parity_home = parity_home_command() + .spawn() + .expect("failed to spawn parity home node"); + + // start a parity node that represents the foreign chain + let mut parity_foreign = parity_foreign_command() + .spawn() + .expect("failed to spawn parity foreign node"); + + // give the clients time to start up + thread::sleep(Duration::from_millis(3000)); + + // A address containing a lot of tokens (0x00a329c0648769a73afac7f9381e08fb43dbea72) should be + // automatically added with a password being an empty string. + // source: https://paritytech.github.io/wiki/Private-development-chain.html + let user_address = "0x00a329c0648769a73afac7f9381e08fb43dbea72"; + + let authority_address = "0x00bd138abd70e2f00903268f3db08f2d25677c9e"; + + // create authority account on home + let exit_status = Command::new("curl") + .arg("--data").arg(r#"{"jsonrpc":"2.0","method":"parity_newAccountFromPhrase","params":["node0", ""],"id":0}"#) + .arg("-H").arg("Content-Type: application/json") + .arg("-X").arg("POST") + .arg("localhost:8550") + .status() + .expect("failed to create authority account on home"); + assert!(exit_status.success()); + // TODO [snd] assert that created address matches authority_address + + // create authority account on foreign + let exit_status = Command::new("curl") + .arg("--data").arg(r#"{"jsonrpc":"2.0","method":"parity_newAccountFromPhrase","params":["node0", ""],"id":0}"#) + .arg("-H").arg("Content-Type: application/json") + .arg("-X").arg("POST") + .arg("localhost:8551") + .status() + .expect("failed to create/unlock authority account on foreign"); + assert!(exit_status.success()); + // TODO [snd] assert that created address matches authority_address + + // give the operations time to complete + thread::sleep(Duration::from_millis(5000)); + + // kill the clients so we can restart them with the accounts unlocked + parity_home.kill().unwrap(); + parity_foreign.kill().unwrap(); + + // wait for clients to shut down + thread::sleep(Duration::from_millis(5000)); + + // start a parity node that represents the home chain with accounts unlocked + let mut parity_home = parity_home_command() + .arg("--unlock").arg(format!("{},{}", user_address, authority_address)) + .arg("--password").arg("password.txt") + .spawn() + .expect("failed to spawn parity home node"); + + // start a parity node that represents the foreign chain with accounts unlocked + let mut parity_foreign = parity_foreign_command() + .arg("--unlock").arg(format!("{},{}", user_address, authority_address)) + .arg("--password").arg("password.txt") + .spawn() + .expect("failed to spawn parity foreign node"); + + // give nodes time to start up + thread::sleep(Duration::from_millis(10000)); + + // start bridge authority 1 + let mut bridge1 = Command::new("env") + .arg("RUST_BACKTRACE=1") + .arg("../target/debug/bridge") + .env("RUST_LOG", "info") + .arg("--config").arg("bridge_config.toml") + .arg("--database").arg("tmp/bridge1_db.txt") + .spawn() + .expect("failed to spawn bridge process"); + + // give the bridge time to start up and deploy the contracts + thread::sleep(Duration::from_millis(10000)); + + let home_contract_address = "0xebd3944af37ccc6b67ff61239ac4fef229c8f69f"; + let foreign_contract_address = "0xebd3944af37ccc6b67ff61239ac4fef229c8f69f"; + + println!("\nuser deposits ether into HomeBridge\n"); + + // TODO [snd] use rpc client here instead of curl + let exit_status = Command::new("curl") + .arg("--data").arg(format!(r#"{{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{{ + "from": "{}", + "to": "{}", + "value": "0x186a0" + }}],"id":0}}"#, user_address, home_contract_address)) + .arg("-H").arg("Content-Type: application/json") + .arg("-X").arg("POST") + .arg("localhost:8550") + .status() + .expect("failed to deposit into HomeBridge"); + assert!(exit_status.success()); + + println!("\ndeposit into home sent. give it plenty of time to get mined and relayed\n"); + thread::sleep(Duration::from_millis(10000)); + + // connect to foreign and home via IPC + let mut event_loop = Core::new().unwrap(); + let foreign_transport = Ipc::with_event_loop("foreign.ipc", &event_loop.handle()) + .expect("failed to connect to foreign.ipc"); + let foreign = bridge::contracts::foreign::ForeignBridge::default(); + let foreign_eth = web3::api::Eth::new(foreign_transport); + let home_transport = Ipc::with_event_loop("home.ipc", &event_loop.handle()) + .expect("failed to connect to home.ipc"); + let home_eth = web3::api::Eth::new(home_transport); + + // totalSupply on ForeignBridge should have increased + let total_supply_payload = foreign.functions().total_supply().input(); + + let future = foreign_eth.call(web3::types::CallRequest{ + from: None, + to: address_from_str(foreign_contract_address), + gas: None, + gas_price: None, + value: None, + data: Some(web3::types::Bytes(total_supply_payload)), + }, None); + + let response = event_loop.run(future).unwrap(); + assert_eq!( + U256::from(response.0.as_slice()), + U256::from(100000), + "totalSupply on ForeignBridge should have increased"); + + // balance on ForeignBridge should have increased + let balance_payload = foreign.functions().balance_of().input(Address::from(user_address)); + + let future = foreign_eth.call(web3::types::CallRequest{ + from: None, + to: address_from_str(foreign_contract_address), + gas: None, + gas_price: None, + value: None, + data: Some(web3::types::Bytes(balance_payload)), + }, None); + + let response = event_loop.run(future).unwrap(); + let balance = U256::from(response.0.as_slice()); + assert_eq!( + balance, + U256::from(100000), + "balance on ForeignBridge should have increased"); + + println!("\nconfirmed that deposit reached foreign\n"); + + println!("\nuser executes ForeignBridge.transferHomeViaRelay\n"); + let transfer_payload = foreign.functions() + .transfer_home_via_relay() + .input( + Address::from(user_address), + 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)), + gas: None, + gas_price: None, + value: None, + data: Some(web3::types::Bytes(transfer_payload)), + condition: None, + nonce: None, + }); + event_loop.run(future).unwrap(); + + println!("\nForeignBridge.transferHomeViaRelay transaction sent. give it plenty of time to get mined and relayed\n"); + thread::sleep(Duration::from_millis(10000)); + + // test that withdraw completed + let future = home_eth.balance(address_from_str(user_address), None); + println!("waiting for future"); + let balance = event_loop.run(future).unwrap(); + assert!(balance > web3::types::U256::from(0)); + + println!("\nconfirmed that withdraw reached home\n"); + + bridge1.kill().unwrap(); + + // wait for bridge to shut down + thread::sleep(Duration::from_millis(1000)); + + parity_home.kill().unwrap(); + parity_foreign.kill().unwrap(); +} diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 8b754c3..616d644 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -6,7 +6,10 @@ authors = ["debris "] [dependencies] bridge = { path = "../bridge" } futures = "0.1" -jsonrpc-core = "7.0" +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" diff --git a/tests/src/lib.rs b/tests/src/lib.rs index edf3e0c..3b0ca18 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -9,24 +9,26 @@ extern crate pretty_assertions; use std::cell::Cell; use web3::Transport; +#[derive(Debug, Clone)] pub struct MockedRequest { pub method: String, pub params: Vec, } -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() } } } +#[derive(Debug, Clone)] pub struct MockedTransport { pub requests: Cell, pub expected_requests: Vec, - pub mocked_responses: Vec<&'static str>, + pub mocked_responses: Vec, } impl Transport for MockedTransport { @@ -44,7 +46,7 @@ impl Transport for MockedTransport { fn send(&self, _id: usize, _request: rpc::Call) -> web3::Result { 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) } } @@ -155,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(), @@ -170,8 +174,6 @@ macro_rules! test_app_stream { foreign.expected_requests.len(), foreign.requests.get() ); - - assert_eq!($expected, res.unwrap()); } } } diff --git a/tests/tests/deposit_relay.rs b/tests/tests/deposit_relay.rs index cba6bf3..32dffd9 100644 --- a/tests/tests/deposit_relay.rs +++ b/tests/tests/deposit_relay.rs @@ -1,23 +1,27 @@ 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(), home => - account => "0x0000000000000000000000000000000000000001", + account => "0000000000000000000000000000000000000001", confirmations => 12; foreign => - account => "0x0000000000000000000000000000000000000001", + account => "0000000000000000000000000000000000000001", confirmations => 12; authorities => accounts => [ - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", + "0000000000000000000000000000000000000001", + "0000000000000000000000000000000000000002", ], signatures => 1; txs => Transactions::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 => [] } @@ -47,15 +63,15 @@ test_app_stream! { ..Default::default() }, home => - account => "0x0000000000000000000000000000000000000001", + account => "0000000000000000000000000000000000000001", confirmations => 12; foreign => - account => "0x0000000000000000000000000000000000000001", + account => "0000000000000000000000000000000000000001", confirmations => 12; authorities => accounts => [ - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", + "0000000000000000000000000000000000000001", + "0000000000000000000000000000000000000002", ], signatures => 1; txs => Transactions::default(), @@ -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"); ] } @@ -89,15 +129,15 @@ test_app_stream! { ..Default::default() }, home => - account => "0x0000000000000000000000000000000000000001", + account => "0000000000000000000000000000000000000001", confirmations => 12; foreign => - account => "0x0000000000000000000000000000000000000001", + account => "0000000000000000000000000000000000000001", confirmations => 12; authorities => accounts => [ - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", + "0000000000000000000000000000000000000001", + "0000000000000000000000000000000000000002", ], signatures => 1; txs => Transactions { @@ -111,36 +151,52 @@ 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"); ] } test_app_stream! { name => deposit_relay_contract_address, database => Database { - home_contract_address: "0x0000000000000000000000000000000000000cc1".parse().unwrap(), - foreign_contract_address: "0x0000000000000000000000000000000000000dd1".parse().unwrap(), + home_contract_address: "0000000000000000000000000000000000000cc1".into(), + foreign_contract_address: "0000000000000000000000000000000000000dd1".into(), ..Default::default() }, home => - account => "0x0000000000000000000000000000000000000001", + account => "0000000000000000000000000000000000000001", confirmations => 12; foreign => - account => "0x0000000000000000000000000000000000000001", + account => "0000000000000000000000000000000000000001", confirmations => 12; authorities => accounts => [ - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", + "0000000000000000000000000000000000000001", + "0000000000000000000000000000000000000002", ], signatures => 1; txs => Transactions::default(), @@ -148,36 +204,54 @@ 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"); ] } test_app_stream! { name => deposit_relay_accounts, database => Database { - home_contract_address: "0x0000000000000000000000000000000000000cc1".parse().unwrap(), - foreign_contract_address: "0x0000000000000000000000000000000000000dd1".parse().unwrap(), + home_contract_address: "0000000000000000000000000000000000000cc1".into(), + foreign_contract_address: "0000000000000000000000000000000000000dd1".into(), ..Default::default() }, home => - account => "0x00000000000000000000000000000000000000ff", + account => "00000000000000000000000000000000000000ff", confirmations => 12; foreign => - account => "0x00000000000000000000000000000000000000ee", + account => "00000000000000000000000000000000000000ee", confirmations => 12; authorities => accounts => [ - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", + "0000000000000000000000000000000000000001", + "0000000000000000000000000000000000000002", ], signatures => 1; txs => Transactions::default(), @@ -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"); ] } @@ -202,15 +294,15 @@ test_app_stream! { name => deposit_relay_multiple_logs, database => Database::default(), home => - account => "0x0000000000000000000000000000000000000001", + account => "0000000000000000000000000000000000000001", confirmations => 12; foreign => - account => "0x0000000000000000000000000000000000000001", + account => "0000000000000000000000000000000000000001", confirmations => 12; authorities => accounts => [ - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", + "0000000000000000000000000000000000000001", + "0000000000000000000000000000000000000002", ], signatures => 1; txs => Transactions::default(), @@ -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"); ] } diff --git a/tests/tests/log_stream.rs b/tests/tests/log_stream.rs index ac8b7f2..0e79b53 100644 --- a/tests/tests/log_stream.rs +++ b/tests/tests/log_stream.rs @@ -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! { @@ -259,7 +333,7 @@ test_transport_stream! { from: 0xb, to: 0x1006, logs: vec![Log { - address: "0x0000000000000000000000000000000000000001".parse().unwrap(), + address: "0000000000000000000000000000000000000001".into(), topics: vec![], data: vec![0x10].into(), log_type: "".into(), @@ -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! { @@ -292,7 +376,7 @@ test_transport_stream! { from: 0xb, to: 0x1006, logs: vec![Log { - address: "0x0000000000000000000000000000000000000001".parse().unwrap(), + address: "0000000000000000000000000000000000000001".into(), topics: vec![], data: vec![0x10].into(), log_type: "".into(), @@ -306,13 +390,13 @@ test_transport_stream! { from: 0x1008, to: 0x1008, logs: vec![Log { - address: "0x0000000000000000000000000000000000000002".parse().unwrap(), + address: "0000000000000000000000000000000000000002".into(), topics: vec![], data: vec![0x20].into(), log_type: "".into(), ..Default::default() }, Log { - address: "0x0000000000000000000000000000000000000002".parse().unwrap(), + address: "0000000000000000000000000000000000000002".into(), topics: vec![], data: vec![0x30].into(), log_type: "".into(), @@ -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": "" + } + ]); } diff --git a/tests/tests/withdraw_confirm.rs b/tests/tests/withdraw_confirm.rs index eeaf57f..d48ae67 100644 --- a/tests/tests/withdraw_confirm.rs +++ b/tests/tests/withdraw_confirm.rs @@ -1,23 +1,36 @@ +/// 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, database => Database::default(), home => - account => "0x0000000000000000000000000000000000000001", + account => "0000000000000000000000000000000000000001", confirmations => 12; foreign => - account => "0x0000000000000000000000000000000000000001", + account => "0000000000000000000000000000000000000001", confirmations => 12; authorities => accounts => [ - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", + "0000000000000000000000000000000000000001", + "0000000000000000000000000000000000000002", ], signatures => 1; txs => Transactions::default(), @@ -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!([]); ] } @@ -47,15 +72,15 @@ test_app_stream! { ..Database::default() }, home => - account => "0x0000000000000000000000000000000000000001", + account => "0000000000000000000000000000000000000001", confirmations => 1; foreign => - account => "0x0000000000000000000000000000000000000001", + account => "0000000000000000000000000000000000000001", confirmations => 12; authorities => accounts => [ - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", + "0000000000000000000000000000000000000001", + "0000000000000000000000000000000000000002", ], signatures => 1; txs => Transactions::default(), @@ -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!([]); ] } @@ -85,20 +121,20 @@ test_app_stream! { name => withdraw_confirm_contract_address, database => Database { checked_withdraw_confirm: 0x00F5, - home_contract_address: "0x49edf201c1e139282643d5e7c6fb0c7219ad1db7".parse().unwrap(), - foreign_contract_address: "0x49edf201c1e139282643d5e7c6fb0c7219ad1db8".parse().unwrap(), + home_contract_address: "49edf201c1e139282643d5e7c6fb0c7219ad1db7".into(), + foreign_contract_address: "49edf201c1e139282643d5e7c6fb0c7219ad1db8".into(), ..Database::default() }, home => - account => "0x0000000000000000000000000000000000000001", + account => "0000000000000000000000000000000000000001", confirmations => 12; foreign => - account => "0x0000000000000000000000000000000000000001", + account => "0000000000000000000000000000000000000001", confirmations => 12; authorities => accounts => [ - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", + "0000000000000000000000000000000000000001", + "0000000000000000000000000000000000000002", ], signatures => 1; txs => Transactions::default(), @@ -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!([]); ] } @@ -128,20 +176,20 @@ test_app_stream! { name => withdraw_confirm_payload_gas, database => Database { checked_withdraw_confirm: 0x00F5, - home_contract_address: "0x49edf201c1e139282643d5e7c6fb0c7219ad1db7".parse().unwrap(), - foreign_contract_address: "0x49edf201c1e139282643d5e7c6fb0c7219ad1db8".parse().unwrap(), + home_contract_address: "49edf201c1e139282643d5e7c6fb0c7219ad1db7".into(), + foreign_contract_address: "49edf201c1e139282643d5e7c6fb0c7219ad1db8".into(), ..Database::default() }, home => - account => "0x0000000000000000000000000000000000000001", + account => "0000000000000000000000000000000000000001", confirmations => 12; foreign => - account => "0x0000000000000000000000000000000000000001", + account => "0000000000000000000000000000000000000001", confirmations => 12; authorities => accounts => [ - "0x00000000000000000000000000000000000000F1", - "0x00000000000000000000000000000000000000F2", + "00000000000000000000000000000000000000F1", + "00000000000000000000000000000000000000F2", ], signatures => 1; txs => Transactions { @@ -156,46 +204,97 @@ 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!([]); ] } test_app_stream! { name => withdraw_confirm_payload_multiple, database => Database { - home_contract_address: "0x49edf201c1e139282643d5e7c6fb0c7219ad1db7".parse().unwrap(), - foreign_contract_address: "0x49edf201c1e139282643d5e7c6fb0c7219ad1db8".parse().unwrap(), + home_contract_address: "49edf201c1e139282643d5e7c6fb0c7219ad1db7".into(), + foreign_contract_address: "49edf201c1e139282643d5e7c6fb0c7219ad1db8".into(), ..Database::default() }, home => - account => "0x0000000000000000000000000000000000000001", + account => "0000000000000000000000000000000000000001", confirmations => 12; foreign => - account => "0x0000000000000000000000000000000000000001", + account => "0000000000000000000000000000000000000001", confirmations => 12; authorities => accounts => [ - "0x00000000000000000000000000000000000000F1", - "0x00000000000000000000000000000000000000F2", + "00000000000000000000000000000000000000F1", + "00000000000000000000000000000000000000F2", ], signatures => 1; txs => Transactions { @@ -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!([]); ] } diff --git a/tests/tests/withdraw_relay.rs b/tests/tests/withdraw_relay.rs index 46caa90..b7661e7 100644 --- a/tests/tests/withdraw_relay.rs +++ b/tests/tests/withdraw_relay.rs @@ -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. @@ -12,15 +27,15 @@ test_app_stream! { name => withdraw_relay_no_log_no_relay, database => Database::default(), home => - account => "0x0000000000000000000000000000000000000001", + account => "0000000000000000000000000000000000000001", confirmations => 12; foreign => - account => "0x0000000000000000000000000000000000000001", + account => "0000000000000000000000000000000000000001", confirmations => 12; authorities => accounts => [ - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", + "0000000000000000000000000000000000000001", + "0000000000000000000000000000000000000002", ], signatures => 1; txs => Transactions::default(), @@ -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!([]); ] } @@ -51,15 +78,15 @@ test_app_stream! { name => withdraw_relay_single_log_authority_not_responsible_no_relay, database => Database::default(), home => - account => "0x0000000000000000000000000000000000000001", + account => "0000000000000000000000000000000000000001", confirmations => 12; foreign => - account => "0x0000000000000000000000000000000000000001", + account => "0000000000000000000000000000000000000001", confirmations => 12; authorities => accounts => [ - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", + "0000000000000000000000000000000000000001", + "0000000000000000000000000000000000000002", ], signatures => 1; txs => Transactions::default(), @@ -68,227 +95,119 @@ 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 => "0x0000000000000000000000000000000000000001", - confirmations => 12; - foreign => - account => "0xaff3454fce5edbc8cca8697c15331677e6ebcccc", - confirmations => 12; - authorities => - accounts => [ - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", - ], - 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#""0x333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333""#; - // calls to `signature` - "eth_call" => - req => r#"[{"data":"0x1812d99600000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000000","to":"0x0000000000000000000000000000000000000000"},"latest"]"#, - res => r#""0x1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111""#; - "eth_call" => - req => r#"[{"data":"0x1812d99600000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000001","to":"0x0000000000000000000000000000000000000000"},"latest"]"#, - res => r#""0x2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222""#; - ] -} - -// 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 => "0x0000000000000000000000000000000000000001", - confirmations => 12; - foreign => - account => "0xaff3454fce5edbc8cca8697c15331677e6ebcccc", - confirmations => 12; - authorities => - accounts => [ - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", - ], - 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#""0x333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333""#; - // calls to `signature` - "eth_call" => - req => r#"[{"data":"0x1812d99600000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000000","to":"0x0000000000000000000000000000000000000000"},"latest"]"#, - res => r#""0x1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111""#; - "eth_call" => - req => r#"[{"data":"0x1812d99600000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000001","to":"0x0000000000000000000000000000000000000000"},"latest"]"#, - res => r#""0x2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222""#; - ] -} - -// 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 => "0x0000000000000000000000000000000000000001", - confirmations => 12; - foreign => - account => "0xaff3454fce5edbc8cca8697c15331677e6ebcccc", - confirmations => 12; - authorities => - accounts => [ - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", - ], - 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#""0x333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333""#; - // calls to `signature` - "eth_call" => - req => r#"[{"data":"0x1812d99600000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000000","to":"0x0000000000000000000000000000000000000000"},"latest"]"#, - res => r#""0x1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111""#; - "eth_call" => - req => r#"[{"data":"0x1812d99600000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000001","to":"0x0000000000000000000000000000000000000000"},"latest"]"#, - res => r#""0x2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222""#; - ] -} - -// 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: "0x00000000000000000000000000000000000000dd".parse().unwrap(), - foreign_contract_address: "0x00000000000000000000000000000000000000ee".parse().unwrap(), + home_contract_address: "00000000000000000000000000000000000000dd".into(), + foreign_contract_address: "00000000000000000000000000000000000000ee".into(), ..Default::default() }, home => - account => "0x0000000000000000000000000000000000000001", + account => "0000000000000000000000000000000000000001", confirmations => 12; foreign => - account => "0xaff3454fce5edbc8cca8697c15331677e6ebcccc", + account => "aff3454fce5edbc8cca8697c15331677e6ebcccc", confirmations => 12; authorities => accounts => [ - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", + "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":"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#""0x333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333""#; + 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#""0x1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111""#; + 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#""0x2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222""#; + req => json!([{ + "data": "0x1812d99600000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000001", + "to": "0x00000000000000000000000000000000000000ee" + },"latest"]), + res => json!(format!("0x{}", Signature { v: 4, r: 5.into(), s: 6.into() }.to_payload().to_hex())); ] } diff --git a/truffle/.node-xmlhttprequest-content-37792 b/truffle/.node-xmlhttprequest-content-37792 new file mode 100644 index 0000000..6551190 --- /dev/null +++ b/truffle/.node-xmlhttprequest-content-37792 @@ -0,0 +1 @@ +{"err":null,"data":{"statusCode":200,"headers":{"access-control-allow-headers":"Origin, X-Requested-With, Content-Type, Accept","access-control-allow-origin":"*","access-control-allow-methods":"*","content-type":"application/json","date":"Fri, 26 Jan 2018 09:06:17 GMT","connection":"close","transfer-encoding":"chunked"},"text":"{\"id\":723,\"jsonrpc\":\"2.0\",\"result\":\"0x00000000000000056bc75e2d63100000\"}"}} \ No newline at end of file diff --git a/truffle/.node-xmlhttprequest-sync-37792 b/truffle/.node-xmlhttprequest-sync-37792 new file mode 100644 index 0000000..e69de29 diff --git a/truffle/test/foreign-erc20.js b/truffle/test/foreign-erc20.js new file mode 100644 index 0000000..de75675 --- /dev/null +++ b/truffle/test/foreign-erc20.js @@ -0,0 +1,267 @@ +var ForeignBridge = artifacts.require("ForeignBridge"); +var helpers = require("./helpers/helpers"); + +contract('ForeignBridge', function(accounts) { + it("totalSupply", function() { + var contract; + var requiredSignatures = 1; + var estimatedGasCostOfWithdraw = 0; + var authorities = [accounts[0], accounts[1]]; + var owner = accounts[2]; + var hash = "0xe55bb43c36cdf79e23b4adc149cdded921f0d482e613c50c6540977c213bc408"; + var value = web3.toWei(3, "ether"); + + 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"); + + 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"); + + 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"); + }) + }) + + it("should be able to approve others to spend tokens in their name", function() { + 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 hash = "0xe55bb43c36cdf79e23b4adc149cdded921f0d482e613c50c6540977c213bc408"; + + 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) { + + return contract.allowance(owner, spender); + }).then(function(result) { + assert.equal(0, result, "initial allowance should be 0"); + + 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 + return contract.transferFrom(owner, receiver, 0, {from: spender}); + }).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); + + // 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); + assert.equal(spender, result.logs[0].args.spender); + assert.equal(web3.toWei(4, "ether"), result.logs[0].args.tokens); + + return contract.allowance(owner, spender); + }).then(function(result) { + assert.equal(web3.toWei(4, "ether"), result, "approval should set allowance"); + + 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.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); + assert.equal(spender, result.logs[0].args.spender); + assert.equal(web3.toWei(2, "ether"), result.logs[0].args.tokens); + + return contract.allowance(owner, spender); + }).then(function(result) { + 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"), {from: spender}); + }).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(web3.toWei(2, "ether"), result.logs[0].args.tokens); + + return contract.balanceOf(owner); + }).then(function(result) { + 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"); + + return contract.balanceOf(spender); + }).then(function(result) { + 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"); + }) + }) + + 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, estimatedGasCostOfWithdraw).then(function(instance) { + meta = instance; + // top up balance so we can transfer + return meta.deposit(userAccount, user1InitialValue, hash, { from: authorities[0] }); + }).then(function(result) { + return meta.transfer(userAccount2, transferedValue, { from: userAccount }); + }).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(userAccount, result.logs[0].args.from, "Event from should be transaction sender"); + assert.equal(userAccount2, result.logs[0].args.to, "Event from should be transaction recipient"); + assert.equal(transferedValue, result.logs[0].args.tokens, "Event tokens should match transaction value"); + return Promise.all([ + meta.balances.call(userAccount), + meta.balances.call(userAccount2) + ]) + }).then(function(result) { + assert.equal(web3.toWei(2, "ether"), result[0]); + assert.equal(transferedValue, result[1]); + }) + }) + + 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, 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() { + 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, estimatedGasCostOfWithdraw).then(function(instance) { + meta = instance; + return meta.deposit(userAccount, userValue, hash, { from: authorities[0] }); + }).then(function(result) { + return meta.transfer(recipientAccount, 0, { from: userAccount }); + }).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(userAccount, result.logs[0].args.from, "Event from should be transaction sender"); + assert.equal(recipientAccount, result.logs[0].args.to, "Event from should be transaction recipient"); + assert.equal(0, result.logs[0].args.tokens, "Event tokens should match transaction value"); + return Promise.all([ + meta.balances.call(userAccount), + meta.balances.call(recipientAccount) + ]) + }).then(function(result) { + assert.equal(userValue, result[0]); + assert.equal(0, result[1]); + }) + }) + + 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, 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() { + 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 recipientAccount = accounts[4]; + var maxValue = web3.toWei("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "wei"); + var hash = "0xe55bb43c36cdf79e23b4adc149cdded921f0d482e613c50c6540977c213bc408"; + 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}); + }).then(function(result) { + return meta.transferFrom(userAccount, recipientAccount, 1, { from: spenderAccount }) + .then(function() { + assert(false, "transfer should fail"); + }, helpers.ignoreExpectedError) + }) + }) +}) diff --git a/truffle/test/foreign.js b/truffle/test/foreign.js index 2e51207..0daea92 100644 --- a/truffle/test/foreign.js +++ b/truffle/test/foreign.js @@ -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,38 +21,44 @@ 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) { - assert.equal(1, result.logs.length, "Exactly one event should be created"); - assert.equal("Deposit", result.logs[0].event, "Event name should be Deposit"); - assert.equal(userAccount, result.logs[0].args.recipient, "Event recipient should be transaction sender"); - assert.equal(value, result.logs[0].args.value, "Event value should match deposited ether"); + assert.equal(2, result.logs.length) + + assert.equal("Transfer", result.logs[0].event); + assert.equal("0x0000000000000000000000000000000000000000", result.logs[0].args.from); + assert.equal(userAccount, result.logs[0].args.to); + assert.equal(value, result.logs[0].args.tokens); + + assert.equal("Deposit", result.logs[1].event); + assert.equal(userAccount, result.logs[1].args.recipient); + assert.equal(value, result.logs[1].args.value); + return meta.balances.call(userAccount); }).then(function(result) { assert.equal(value, result, "Contract balance should change"); @@ -61,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) { @@ -76,10 +84,16 @@ contract('ForeignBridge', function(accounts) { assert.equal(web3.toWei(0, "ether"), result, "Contract balance should not change yet"); return meta.deposit(userAccount, value, hash, { from: authorities[1] }); }).then(function(result) { - assert.equal(1, result.logs.length, "Exactly one event should be created"); - assert.equal("Deposit", result.logs[0].event, "Event name should be Deposit"); - assert.equal(userAccount, result.logs[0].args.recipient, "Event recipient should be transaction sender"); - assert.equal(value, result.logs[0].args.value, "Event value should match deposited ether"); + assert.equal(2, result.logs.length) + + assert.equal("Transfer", result.logs[0].event); + assert.equal("0x0000000000000000000000000000000000000000", result.logs[0].args.from); + assert.equal(userAccount, result.logs[0].args.to); + assert.equal(value, result.logs[0].args.tokens); + + assert.equal("Deposit", result.logs[1].event, "Event name should be Deposit"); + assert.equal(userAccount, result.logs[1].args.recipient, "Event recipient should be transaction sender"); + assert.equal(value, result.logs[1].args.value, "Event value should match deposited ether"); return meta.balances.call(userAccount); }).then(function(result) { assert.equal(value, result, "Contract balance should change"); @@ -89,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) { @@ -141,146 +158,148 @@ contract('ForeignBridge', function(accounts) { assert.equal(0, result.logs.length, "Misbehaving authority should be ignored"); return meta.deposit(userAccount, value, hash, { from: authorities[2] }) }).then(function(result) { - assert.equal(1, result.logs.length, "Exactly one event should be created"); - assert.equal("Deposit", result.logs[0].event, "Event name should be Deposit"); - assert.equal(userAccount, result.logs[0].args.recipient, "Event recipient should be transaction sender"); - assert.equal(value, result.logs[0].args.value, "Event value should match transaction value"); + assert.equal(2, result.logs.length) + + assert.equal("Transfer", result.logs[0].event); + assert.equal("0x0000000000000000000000000000000000000000", result.logs[0].args.from); + assert.equal(userAccount, result.logs[0].args.to); + assert.equal(value, result.logs[0].args.tokens); + + assert.equal("Deposit", result.logs[1].event, "Event name should be Deposit"); + assert.equal(userAccount, result.logs[1].args.recipient, "Event recipient should be transaction sender"); + assert.equal(value, result.logs[1].args.value, "Event value should match transaction value"); + return meta.balances.call(userAccount); }).then(function(result) { assert.equal(value, result, "Contract balance should change"); }) }) - it("should allow user to transfer value locally", function() { - var meta; - var requiredSignatures = 1; - 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) { - meta = instance; - // top up balance so we can transfer - return meta.deposit(userAccount, user1InitialValue, hash, { from: authorities[0] }); - }).then(function(result) { - return meta.transferLocal(userAccount2, transferedValue, { from: userAccount }); - }).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(userAccount, result.logs[0].args.from, "Event from should be transaction sender"); - assert.equal(userAccount2, result.logs[0].args.to, "Event from should be transaction recipient"); - assert.equal(transferedValue, result.logs[0].args.value, "Event value should match transaction value"); - return Promise.all([ - meta.balances.call(userAccount), - meta.balances.call(userAccount2) - ]) - }).then(function(result) { - assert.equal(web3.toWei(2, "ether"), result[0]); - assert.equal(transferedValue, result[1]); - }) - }) - - it("should not allow user to transfer value they don't have either locally or to home", function() { + 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.transferLocal(recipientAccount, transferedValue, { from: userAccount }); - }).then(function(result) { - assert(false, "transferLocal should fail"); - }, function(err) { - 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 both locally and to home", function() { + 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.transferLocal(recipientAccount, transferedValue, { from: userAccount }); - }).then(function(result) { - assert(false, "transferLocal should fail"); - }, function(err) { - 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 with value overflow both locally and to home", 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("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "wei"); + 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.transferLocal(recipientAccount, transferedValue, { from: userAccount }); - }).then(function(result) { - assert(false, "transferLocal should fail"); - }, function(err) { - 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 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(1, result.logs.length, "Exactly one event should be created"); - assert.equal("Withdraw", result.logs[0].event, "Event name should be Withdraw"); - assert.equal(userAccount2, result.logs[0].args.recipient, "Event recipient should be equal to transaction recipient"); - assert.equal(value2, result.logs[0].args.value, "Event value should match transaction value"); + 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(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(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])); }) }) @@ -288,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) { @@ -299,7 +322,7 @@ contract('ForeignBridge', function(accounts) { }).then(function(result) { assert.equal(1, result.logs.length, "Exactly one event should be created"); assert.equal("CollectedSignatures", result.logs[0].event, "Event name should be CollectedSignatures"); - assert.equal(authorities[0], result.logs[0].args.authority, "Event authority should be equal to transaction sender"); + assert.equal(authorities[0], result.logs[0].args.authorityResponsibleForRelay, "Event authority should be equal to transaction sender"); return Promise.all([ meta.signature.call(result.logs[0].args.messageHash, 0), meta.message(result.logs[0].args.messageHash), @@ -313,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) { @@ -330,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), @@ -356,7 +387,7 @@ contract('ForeignBridge', function(accounts) { }).then(function(result) { assert.equal(1, result.logs.length, "Exactly one event should be created"); assert.equal("CollectedSignatures", result.logs[0].event, "Event name should be CollectedSignatures"); - assert.equal(authorities[0], result.logs[0].args.authority, "Event authority should be equal to transaction sender"); + assert.equal(authorities[0], result.logs[0].args.authorityResponsibleForRelay, "Event authority should be equal to transaction sender"); return Promise.all([ meta.signature.call(result.logs[0].args.messageHash, 0), meta.signature.call(result.logs[0].args.messageHash, 1), @@ -370,7 +401,7 @@ contract('ForeignBridge', function(accounts) { }).then(function(result) { assert.equal(1, result.logs.length, "Exactly one event should be created"); assert.equal("CollectedSignatures", result.logs[0].event, "Event name should be CollectedSignatures"); - assert.equal(authorities[1], result.logs[0].args.authority, "Event authority should be equal to transaction sender"); + assert.equal(authorities[1], result.logs[0].args.authorityResponsibleForRelay, "Event authority should be equal to transaction sender"); return Promise.all([ meta.signature.call(result.logs[0].args.messageHash, 0), meta.signature.call(result.logs[0].args.messageHash, 1), @@ -383,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) }) }) }) diff --git a/truffle/test/helpers.js b/truffle/test/helpers.js index 04c37be..ddd7390 100644 --- a/truffle/test/helpers.js +++ b/truffle/test/helpers.js @@ -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"); + }) + }) + }) }) diff --git a/truffle/test/helpers/helpers.js b/truffle/test/helpers/helpers.js index 3ad8ee0..0c6bc50 100644 --- a/truffle/test/helpers/helpers.js +++ b/truffle/test/helpers/helpers.js @@ -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; diff --git a/truffle/test/home.js b/truffle/test/home.js index 19f635d..5fc6b3d 100644 --- a/truffle/test/home.js +++ b/truffle/test/home.js @@ -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) }) }) }) diff --git a/truffle/test/message.js b/truffle/test/message.js index bd631db..6ad994c 100644 --- a/truffle/test/message.js +++ b/truffle/test/message.js @@ -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)); + }) + }) }) diff --git a/truffle/test/message_signing.js b/truffle/test/message_signing.js index 0c255fa..8570b89 100644 --- a/truffle/test/message_signing.js +++ b/truffle/test/message_signing.js @@ -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) + }) }) })