Merge branch 'master' into ui-2

This commit is contained in:
Jaco Greeff 2017-05-24 14:09:51 +02:00
commit 45c6cc6ac0
217 changed files with 5200 additions and 3698 deletions

22
.dockerignore Normal file
View File

@ -0,0 +1,22 @@
# Generated by Cargo
# will have compiled files and executables
target
*.swp
*.swo
*.swn
*.DS_Store
# Visual Studio Code stuff
.vscode
# GitEye stuff
.project
# idea ide
.idea
# git stuff
.git
ethcore/res/ethereum/tests

View File

@ -549,7 +549,7 @@ test-darwin:
- triggers
before_script:
- git submodule update --init --recursive
- export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep -v -e ^js -e ^\\. -e ^LICENSE -e ^README.md -e ^appveyor.yml -e ^test.sh -e ^windows/ -e ^scripts/ -e^mac/ -e ^nsis/ | wc -l)
- export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep -v -e "^js/" -e ^\\. -e ^LICENSE -e ^README.md -e ^appveyor.yml -e ^test.sh -e ^windows/ -e ^scripts/ -e^mac/ -e ^nsis/ | wc -l)
script:
- export RUST_BACKTRACE=1
- if [ $RUST_FILES_MODIFIED -eq 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi
@ -564,7 +564,7 @@ test-windows:
- git submodule update --init --recursive
script:
- set RUST_BACKTRACE=1
- echo cargo test --features json-tests -p rlp -p ethash -p ethcore -p ethcore-bigint -p ethcore-dapps -p parity-rpc -p ethcore-signer -p ethcore-util -p ethcore-network -p ethcore-io -p ethkey -p ethstore -p ethsync -p ethcore-ipc -p ethcore-ipc-tests -p ethcore-ipc-nano -p parity %CARGOFLAGS% --verbose --release
- echo cargo test --features json-tests -p rlp -p ethash -p ethcore -p ethcore-bigint -p parity-dapps -p parity-rpc -p ethcore-util -p ethcore-network -p ethcore-io -p ethkey -p ethstore -p ethsync -p ethcore-ipc -p ethcore-ipc-tests -p ethcore-ipc-nano -p parity-rpc-client -p parity %CARGOFLAGS% --verbose --release
tags:
- rust-windows
allow_failure: true

View File

@ -1,3 +1,31 @@
## Parity [v1.6.7](https://github.com/paritytech/parity/releases/tag/v1.6.7) (2017-05-18)
This release addresses:
- potential usability issues with [import and recovery of existing accounts](https://blog.parity.io/restoring-blank-seed-phrase/).
- canceling scheduled transactions via RPC or UI.
- warp sync issues with the Kovan network.
Full changelog:
- Backporting to beta [#5657](https://github.com/paritytech/parity/pull/5657)
- Add CHANGELOG.md [#5513](https://github.com/paritytech/parity/pull/5513)
- Reorg into blocks before minimum history [#5558](https://github.com/paritytech/parity/pull/5558)
- Bump to v1.6.7
- Cancel Transaction [#5656](https://github.com/paritytech/parity/pull/5656)
- option to disable persistent txqueue [#5544](https://github.com/paritytech/parity/pull/5544)
- Remove transaction RPC [#4949](https://github.com/paritytech/parity/pull/4949)
- Cancel tx JS [#4958](https://github.com/paritytech/parity/pull/4958)
- Updating documentation for RPCs [#5392](https://github.com/paritytech/parity/pull/5392)
- Backport Recover button [#5654](https://github.com/paritytech/parity/pull/5654)
- Backport [#5645](https://github.com/paritytech/parity/pull/5645)
- Add monotonic step to Kovan [#5630](https://github.com/paritytech/parity/pull/5630)
- Add monotonic transition to kovan [#5587](https://github.com/paritytech/parity/pull/5587)
- Fix ethsign [#5600](https://github.com/paritytech/parity/pull/5600)
- Registry backports [#5445](https://github.com/paritytech/parity/pull/5445)
- Fixes to the Registry dapp [#4984](https://github.com/paritytech/parity/pull/4984)
- Fix references to api outside of `parity.js` [#4981](https://github.com/paritytech/parity/pull/4981)
## Parity [v1.6.6](https://github.com/paritytech/parity/releases/tag/v1.6.6) (2017-04-11)
This release brings warp sync support for kovan network.

363
Cargo.lock generated
View File

@ -1,61 +1,6 @@
[root]
name = "parity"
version = "1.7.0"
dependencies = [
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
"ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)",
"daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"docopt 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.7.0",
"ethcore-devtools 1.7.0",
"ethcore-io 1.7.0",
"ethcore-ipc 1.7.0",
"ethcore-ipc-hypervisor 1.2.0",
"ethcore-ipc-nano 1.7.0",
"ethcore-ipc-tests 0.1.0",
"ethcore-light 1.7.0",
"ethcore-logger 1.7.0",
"ethcore-secretstore 1.0.0",
"ethcore-signer 1.7.0",
"ethcore-stratum 1.7.0",
"ethcore-util 1.7.0",
"ethkey 0.2.0",
"ethsync 1.7.0",
"evmbin 0.1.0",
"fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-dapps 1.7.0",
"parity-hash-fetch 1.7.0",
"parity-ipfs-api 1.7.0",
"parity-local-store 0.1.0",
"parity-reactor 0.1.0",
"parity-rpc 1.7.0",
"parity-rpc-client 1.4.0",
"parity-updater 1.7.0",
"path 0.1.0",
"pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.2.0",
"rpassword 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rpc-cli 1.4.0",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.2.0 (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)",
]
name = "using_queue"
version = "0.1.0"
[[package]]
name = "advapi32-sys"
@ -124,16 +69,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bigint"
version = "1.0.5"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bincode"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bit-set"
version = "0.2.0"
@ -224,6 +179,14 @@ dependencies = [
"multihash 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "clippy"
version = "0.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"clippy_lints 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "clippy"
version = "0.0.103"
@ -232,6 +195,20 @@ dependencies = [
"clippy_lints 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "clippy_lints"
version = "0.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"quine-mc_cluskey 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "clippy_lints"
version = "0.0.103"
@ -340,10 +317,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "elastic-array"
version = "0.7.0"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -439,10 +416,9 @@ dependencies = [
[[package]]
name = "ethcore-bigint"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bigint 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"bigint 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
@ -488,8 +464,10 @@ name = "ethcore-ipc-codegen"
version = "1.7.0"
dependencies = [
"aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
"quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"quasi_macros 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -535,6 +513,7 @@ dependencies = [
name = "ethcore-light"
version = "1.7.0"
dependencies = [
"bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.7.0",
"ethcore-devtools 1.7.0",
"ethcore-io 1.7.0",
@ -547,7 +526,9 @@ dependencies = [
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.2.0",
"smallvec 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"stats 0.1.0",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -573,6 +554,7 @@ version = "1.7.0"
dependencies = [
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-devtools 1.7.0",
"ethcore-io 1.7.0",
"ethcore-logger 1.7.0",
@ -626,26 +608,6 @@ dependencies = [
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ethcore-signer"
version = "1.7.0"
dependencies = [
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-devtools 1.7.0",
"ethcore-io 1.7.0",
"ethcore-util 1.7.0",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-rpc 1.7.0",
"parity-ui 1.7.0",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"ws 0.5.3 (git+https://github.com/paritytech/ws-rs.git?branch=parity-1.7)",
]
[[package]]
name = "ethcore-stratum"
version = "1.7.0"
@ -673,14 +635,14 @@ version = "1.7.0"
dependencies = [
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
"elastic-array 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"elastic-array 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"eth-secp256k1 0.5.6 (git+https://github.com/paritytech/rust-secp256k1)",
"ethcore-bigint 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-bigint 0.1.3",
"ethcore-bloom-journal 0.1.0",
"ethcore-devtools 1.7.0",
"ethcore-logger 1.7.0",
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
@ -707,7 +669,7 @@ name = "ethcrypto"
version = "0.1.0"
dependencies = [
"eth-secp256k1 0.5.6 (git+https://github.com/paritytech/rust-secp256k1)",
"ethcore-bigint 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-bigint 0.1.3",
"ethkey 0.2.0",
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
@ -717,6 +679,7 @@ dependencies = [
name = "ethjson"
version = "0.1.0"
dependencies = [
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-util 1.7.0",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -731,7 +694,7 @@ dependencies = [
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"docopt 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"eth-secp256k1 0.5.6 (git+https://github.com/paritytech/rust-secp256k1)",
"ethcore-bigint 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-bigint 0.1.3",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
@ -744,7 +707,7 @@ name = "ethstore"
version = "0.1.0"
dependencies = [
"docopt 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-bigint 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-bigint 0.1.3",
"ethcrypto 0.1.0",
"ethkey 0.2.0",
"itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
@ -758,7 +721,7 @@ dependencies = [
"serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
@ -780,13 +743,13 @@ dependencies = [
"ethcore-network 1.7.0",
"ethcore-util 1.7.0",
"ethkey 0.2.0",
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.2.0",
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -819,6 +782,7 @@ dependencies = [
name = "fetch"
version = "0.1.0"
dependencies = [
"clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
@ -836,6 +800,11 @@ dependencies = [
"miniz-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fnv"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "futures"
version = "0.1.11"
@ -873,6 +842,18 @@ name = "glob"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "globset"
version = "0.1.4"
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)",
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "hamming"
version = "0.1.3"
@ -882,17 +863,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "hardware-wallet"
version = "1.7.0"
dependencies = [
"ethcore-bigint 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-bigint 0.1.3",
"ethkey 0.2.0",
"hidapi 0.3.1 (git+https://github.com/paritytech/hidapi-rs)",
"libusb 0.3.0 (git+https://github.com/paritytech/libusb-rs)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "heapsize"
version = "0.3.6"
version = "0.4.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)",
@ -1040,7 +1022,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "jsonrpc-core"
version = "7.0.0"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#8ed20d6e094e88f707045fca2d0959f46bfd23f9"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#900b528213ffd1aaaefd29e2b99dfab892b15ab4"
dependencies = [
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1052,7 +1034,7 @@ dependencies = [
[[package]]
name = "jsonrpc-http-server"
version = "7.0.0"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#8ed20d6e094e88f707045fca2d0959f46bfd23f9"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#900b528213ffd1aaaefd29e2b99dfab892b15ab4"
dependencies = [
"hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
@ -1065,7 +1047,7 @@ dependencies = [
[[package]]
name = "jsonrpc-ipc-server"
version = "7.0.0"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#8ed20d6e094e88f707045fca2d0959f46bfd23f9"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#900b528213ffd1aaaefd29e2b99dfab892b15ab4"
dependencies = [
"bytes 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
@ -1078,7 +1060,7 @@ dependencies = [
[[package]]
name = "jsonrpc-macros"
version = "7.0.0"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#8ed20d6e094e88f707045fca2d0959f46bfd23f9"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#900b528213ffd1aaaefd29e2b99dfab892b15ab4"
dependencies = [
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-pubsub 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
@ -1088,7 +1070,7 @@ dependencies = [
[[package]]
name = "jsonrpc-minihttp-server"
version = "7.0.0"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#8ed20d6e094e88f707045fca2d0959f46bfd23f9"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#900b528213ffd1aaaefd29e2b99dfab892b15ab4"
dependencies = [
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
@ -1102,7 +1084,7 @@ dependencies = [
[[package]]
name = "jsonrpc-pubsub"
version = "7.0.0"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#8ed20d6e094e88f707045fca2d0959f46bfd23f9"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#900b528213ffd1aaaefd29e2b99dfab892b15ab4"
dependencies = [
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1112,8 +1094,9 @@ dependencies = [
[[package]]
name = "jsonrpc-server-utils"
version = "7.0.0"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#8ed20d6e094e88f707045fca2d0959f46bfd23f9"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#900b528213ffd1aaaefd29e2b99dfab892b15ab4"
dependencies = [
"globset 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1123,7 +1106,7 @@ dependencies = [
[[package]]
name = "jsonrpc-tcp-server"
version = "7.0.0"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#8ed20d6e094e88f707045fca2d0959f46bfd23f9"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#900b528213ffd1aaaefd29e2b99dfab892b15ab4"
dependencies = [
"bytes 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
@ -1137,7 +1120,7 @@ dependencies = [
[[package]]
name = "jsonrpc-ws-server"
version = "7.0.0"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#8ed20d6e094e88f707045fca2d0959f46bfd23f9"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#900b528213ffd1aaaefd29e2b99dfab892b15ab4"
dependencies = [
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
@ -1583,6 +1566,64 @@ dependencies = [
"stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parity"
version = "1.7.0"
dependencies = [
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
"ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)",
"daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"docopt 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.7.0",
"ethcore-devtools 1.7.0",
"ethcore-io 1.7.0",
"ethcore-ipc 1.7.0",
"ethcore-ipc-hypervisor 1.2.0",
"ethcore-ipc-nano 1.7.0",
"ethcore-ipc-tests 0.1.0",
"ethcore-light 1.7.0",
"ethcore-logger 1.7.0",
"ethcore-secretstore 1.0.0",
"ethcore-stratum 1.7.0",
"ethcore-util 1.7.0",
"ethkey 0.2.0",
"ethsync 1.7.0",
"evmbin 0.1.0",
"fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-dapps 1.7.0",
"parity-hash-fetch 1.7.0",
"parity-ipfs-api 1.7.0",
"parity-local-store 0.1.0",
"parity-reactor 0.1.0",
"parity-rpc 1.7.0",
"parity-rpc-client 1.4.0",
"parity-updater 1.7.0",
"path 0.1.0",
"pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.2.0",
"rpassword 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rpc-cli 1.4.0",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.2.0 (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 = "parity-dapps"
version = "1.7.0"
@ -1734,18 +1775,18 @@ dependencies = [
name = "parity-rpc-client"
version = "1.4.0"
dependencies = [
"ethcore-signer 1.7.0",
"ethcore-util 1.7.0",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-ws-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-rpc 1.7.0",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
"tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ws 0.5.3 (git+https://github.com/paritytech/ws-rs.git?branch=parity-1.7)",
]
[[package]]
@ -1784,7 +1825,7 @@ dependencies = [
[[package]]
name = "parity-ui-precompiled"
version = "1.4.0"
source = "git+https://github.com/paritytech/js-precompiled.git#6597fc70499226546fdcb35e7c09f9347f4f3c07"
source = "git+https://github.com/paritytech/js-precompiled.git#d09c2b70b2e6c6e84d88999bfa6f3c8bb43b910e"
dependencies = [
"parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1957,6 +1998,14 @@ dependencies = [
"syntex_syntax 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quasi_macros"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quick-error"
version = "1.1.0"
@ -2042,8 +2091,8 @@ name = "rlp"
version = "0.2.0"
dependencies = [
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"elastic-array 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-bigint 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"elastic-array 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-bigint 0.1.3",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -2104,7 +2153,7 @@ dependencies = [
name = "rpc-cli"
version = "1.4.0"
dependencies = [
"ethcore-bigint 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"bigint 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-util 1.7.0",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-rpc 1.7.0",
@ -2224,12 +2273,17 @@ name = "serde"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde_codegen_internals"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"syn 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -2239,7 +2293,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_codegen_internals 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.11.4 (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"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive_internals 0.15.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.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)",
"synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -2290,11 +2363,6 @@ name = "siphasher"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "slab"
version = "0.2.0"
source = "git+https://github.com/carllerche/slab?rev=5476efcafb#5476efcafbc5ef4d7315b1bea3f756d8a1fe975e"
[[package]]
name = "slab"
version = "0.2.0"
@ -2317,10 +2385,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "smallvec"
version = "0.3.2"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -2347,10 +2415,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "0.11.4"
version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)",
"synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "synom"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -2681,10 +2758,6 @@ dependencies = [
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "using_queue"
version = "0.1.0"
[[package]]
name = "utf8-ranges"
version = "1.0.0"
@ -2723,25 +2796,10 @@ name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "ws"
version = "0.5.3"
source = "git+https://github.com/paritytech/ws-rs.git?branch=parity-1.7#30415c17f1bec53b2dcabae5b8b887df75dcbe34"
dependencies = [
"bytes 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.1 (git+https://github.com/paritytech/mio)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.2.0 (git+https://github.com/carllerche/slab?rev=5476efcafb)",
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ws"
version = "0.6.0"
source = "git+https://github.com/tomusdrw/ws-rs#3259e7ca906c848beae109eb32e492871f8f397d"
source = "git+https://github.com/tomusdrw/ws-rs#7f8e416b7f048880228005457e117128be38bf0f"
dependencies = [
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2804,7 +2862,8 @@ dependencies = [
"checksum aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfdf7355d9db158df68f976ed030ab0f6578af811f5a7bb6dcf221ec24e0e0"
"checksum base-x 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f59103b47307f76e03bef1633aec7fa9e29bfb5aa6daf5a334f94233c71f6c1"
"checksum base32 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1b9605ba46d61df0410d8ac686b0007add8172eba90e8e909c347856fe794d8c"
"checksum bigint 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "5d1b3ef6756498df0e2c6bb67c065f4154d0ecd721eb5b3c3f865c8012b9fd74"
"checksum bigint 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d493e6869ed11b135750f4a4f44d574a52bf8f67e656cdc15b4085316c2098b6"
"checksum bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e103c8b299b28a9c6990458b7013dc4a8356a9b854c51b9883241f5866fac36e"
"checksum bit-set 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e6e1e6fb1c9e3d6fcdec57216a74eaa03e41f52a22f13a16438251d8e88b89da"
"checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c"
"checksum bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5b97c2c8e8bbb4251754f559df8af22fb264853c7d009084a576cdf12565089d"
@ -2820,7 +2879,9 @@ dependencies = [
"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c"
"checksum cid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e53e6cdfa5ca294863e8c8a32a7cdb4dc0a442c8971d47a0e75b6c27ea268a6a"
"checksum clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "5b4fabf979ddf6419a313c1c0ada4a5b95cfd2049c56e8418d622d27b4b6ff32"
"checksum clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "d19bda68c3db98e3a780342f6101b44312fef20a5f13ce756d1202a35922b01b"
"checksum clippy_lints 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "ce96ec05bfe018a0d5d43da115e54850ea2217981ff0f2e462780ab9d594651a"
"checksum clippy_lints 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "3d4ed67c69b9bb35169be2538691d290a3aa0cbfd4b9f0bfb7c221fc1d399a96"
"checksum cookie 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d53b80dde876f47f03cda35303e368a79b91c70b0d65ecba5fd5280944a08591"
"checksum core-foundation 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "20a6d0448d3a99d977ae4a2aa5a98d886a923e863e81ad9ff814645b6feb3bbd"
"checksum core-foundation-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "05eed248dc504a5391c63794fe4fb64f46f071280afaa1b73308f3c0ce4574c5"
@ -2833,20 +2894,21 @@ dependencies = [
"checksum docopt 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab32ea6e284d87987066f21a9e809a73c14720571ef34516f0890b3d355ccfd8"
"checksum dtoa 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5edd69c67b2f8e0911629b7e6b8a34cb3956613cd7c6e6414966dee349c2db4f"
"checksum either 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3d2b503c86dad62aaf414ecf2b8c527439abedb3f8d812537f0b12bfd6f32a91"
"checksum elastic-array 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71a64decd4b8cd06654a4e643c45cb558ad554abbffd82a7e16e34f45f51b605"
"checksum elastic-array 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "561b1b1bb58e6d9212b75a28cca442f8a87cceb35cb1b6d6f39f5df5346a9160"
"checksum env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e3856f1697098606fc6cb97a93de88ca3f3bc35bb878c725920e6e82ecf05e83"
"checksum eth-secp256k1 0.5.6 (git+https://github.com/paritytech/rust-secp256k1)" = "<none>"
"checksum ethabi 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "63df67d0af5e3cb906b667ca1a6e00baffbed87d0d8f5f78468a1f5eb3a66345"
"checksum ethcore-bigint 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5d237300af825a8d78f4c0dc835b0eab76a207e9df4aa088d91e162a173e0ca0"
"checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa"
"checksum flate2 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "3eeb481e957304178d2e782f2da1257f1434dfecbae883bafb61ada2a9fea3bb"
"checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344"
"checksum futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8e51e7f9c150ba7fd4cee9df8bf6ea3dea5b63b68955ddad19ccd35b71dcfb4d"
"checksum futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bb982bb25cd8fa5da6a8eb3a460354c984ff1113da82bcb4f0b0862b5795db82"
"checksum gcc 0.3.43 (registry+https://github.com/rust-lang/crates.io-index)" = "c07c758b972368e703a562686adb39125707cc1ef3399da8c019fc6c2498a75d"
"checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518"
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
"checksum globset 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90d069fe6beb9be359ef505650b3f73228c5591a3c4b1f32be2f4f44459ffa3a"
"checksum hamming 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65043da274378d68241eb9a8f8f8aa54e349136f7b8e12f63e3ef44043cc30e1"
"checksum heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "abb306abb8d398e053cfb1b3e7b72c2f580be048b85745c52652954f8ad1439c"
"checksum heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c7593b1522161003928c959c20a2ca421c68e940d63d75573316a009e48a6d4"
"checksum heck 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6f807d2f64cc044a6bcf250ff23e59be0deec7a16612c014f962a06fa7e020f9"
"checksum hidapi 0.3.1 (git+https://github.com/paritytech/hidapi-rs)" = "<none>"
"checksum httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46534074dbb80b070d60a5cb8ecadd8963a00a438ae1a95268850a7ef73b67ae"
@ -2937,6 +2999,7 @@ dependencies = [
"checksum primal-sieve 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7aa73fd87e5984a00bdb4c1b14d3d5d6d0bad01b2caaaf924c16ab7260ac946c"
"checksum quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18c45c4854d6d1cf5d531db97c75880feb91c958b0720f4ec1057135fec358b3"
"checksum quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9e25fa23c044c1803f43ca59c98dac608976dd04ce799411edd58ece776d4"
"checksum quasi_macros 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29cec87bc2816766d7e4168302d505dd06b0a825aed41b00633d296e922e02dd"
"checksum quick-error 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0aad603e8d7fb67da22dbdf1f4b826ce8829e406124109e73cf1b2454b93a71c"
"checksum quine-mc_cluskey 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6683b0e23d80813b1a535841f0048c1537d3f86d63c999e8373b39a9b0eb74a"
"checksum quote 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)" = "6732e32663c9c271bfc7c1823486b471f18c47a2dbf87c066897b7b51afc83be"
@ -2966,23 +3029,26 @@ dependencies = [
"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 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0ae9a3c8b07c09dbe43022486d55a18c629a0618d2241e49829aaef9b6d862f9"
"checksum serde 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "991ef6be409a3b7a46cb9ee701d86156ce851825c65dbee7f16dbd5c4e7e2d47"
"checksum serde_codegen_internals 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c3172bf2940b975c0e4f6ab42a511c0a4407d4f46ccef87a9d3615db5c26fa96"
"checksum serde_derive 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ecc6e0379ca933ece58302d2d3034443f06fbf38fd535857c1dc516195cbc3bf"
"checksum serde_derive 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd81eef9f0b4ec341b11095335b6a4b28ed85581b12dd27585dee1529df35e0"
"checksum serde_derive_internals 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "021c338d22c7e30f957a6ab7e388cb6098499dda9fd4ba1661ee074ca7a180d1"
"checksum serde_json 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "cf37ce931677e98b4fa5e6469aaa3ab4b6228309ea33b1b22d3ec055adfc4515"
"checksum serde_urlencoded 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a81f15da4b9780e1524697f73b09076b6e42298ef673bead9ca8f848b334ef84"
"checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c"
"checksum shell32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72f20b8f3c060374edb8046591ba28f62448c369ccbdc7b02075103fb3a9e38d"
"checksum siphasher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c44e42fa187b5a8782489cf7740cc27c3125806be2bf33563cf5e02e9533fcd"
"checksum slab 0.2.0 (git+https://github.com/carllerche/slab?rev=5476efcafb)" = "<none>"
"checksum slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6dbdd334bd28d328dad1c41b0ea662517883d8880d8533895ef96c8003dec9c4"
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
"checksum smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fcc8d19212aacecf95e4a7a2179b26f7aeb9732a915cf01f05b0d3e044865410"
"checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013"
"checksum smallvec 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dca03f2f42500a9ef8ac0d16183dff8bed40e3dcf98f9d4147928548d5c4236e"
"checksum smallvec 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2e40af10aafe98b4d8294ae8388d8a5cd0707c65d364872efe72d063ec44bee0"
"checksum spmc 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "93bdab61c1a413e591c4d17388ffa859eaff2df27f1e13a5ec8b716700605adf"
"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.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f4f94368aae82bb29656c98443a7026ca931a659e8d19dcdc41d6e273054e820"
"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 syntex 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)" = "35f3cc9d446323ef8fefad933b65cd6de271d29fa14a2e9d036a084770c6d6d5"
"checksum syntex_errors 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3af03823ea45d420dd2c1a44bb074e13ea55f9b99afe960fd58eb4069b7f6cad"
"checksum syntex_pos 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1e502a4a904d9f37cf975dbdbb0b08f2d111322f6792bda6eb095b4112c9a24b"
@ -3025,7 +3091,6 @@ dependencies = [
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
"checksum ws 0.5.3 (git+https://github.com/paritytech/ws-rs.git?branch=parity-1.7)" = "<none>"
"checksum ws 0.6.0 (git+https://github.com/tomusdrw/ws-rs)" = "<none>"
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
"checksum xdg 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "77b831a5ba77110f438f0ac5583aafeb087f70432998ba6b7dcb1d32185db453"

View File

@ -33,7 +33,6 @@ ethcore = { path = "ethcore" }
ethcore-util = { path = "util" }
ethcore-io = { path = "util/io" }
ethcore-devtools = { path = "devtools" }
ethcore-signer = { path = "signer" }
ethcore-ipc = { path = "ipc/rpc" }
ethcore-ipc-nano = { path = "ipc/nano" }
ethcore-ipc-hypervisor = { path = "ipc/hypervisor" }
@ -75,17 +74,15 @@ default = ["ui-precompiled"]
ui = [
"dapps",
"parity-dapps/ui",
"ethcore-signer/ui",
]
ui-precompiled = [
"dapps",
"ethcore-signer/ui-precompiled",
"parity-dapps/ui-precompiled",
]
dapps = ["parity-dapps"]
ipc = ["ethcore/ipc", "ethsync/ipc"]
jit = ["ethcore/jit"]
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "parity-rpc/dev", "parity-dapps/dev", "ethcore-signer/dev"]
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "parity-rpc/dev", "parity-dapps/dev"]
json-tests = ["ethcore/json-tests"]
test-heavy = ["ethcore/test-heavy"]
ethkey-cli = ["ethcore/ethkey-cli"]
@ -104,3 +101,5 @@ name = "parity"
debug = false
lto = false
panic = "abort"
[workspace]

View File

@ -16,42 +16,27 @@
use std::sync::Arc;
use unicase::UniCase;
use hyper::{server, net, Decoder, Encoder, Next, Control};
use hyper::header;
use hyper::method::Method;
use api::types::{App, ApiError};
use api::types::ApiError;
use api::response;
use apps::fetcher::Fetcher;
use handlers::extract_url;
use endpoint::{Endpoint, Endpoints, Handler, EndpointPath};
use jsonrpc_http_server::{self, AccessControlAllowOrigin};
use endpoint::{Endpoint, Handler, EndpointPath};
#[derive(Clone)]
pub struct RestApi {
// TODO [ToDr] cors_domains should be handled by the server to avoid duplicated logic.
// RequestMiddleware should be able to tell that cors headers should be included.
cors_domains: Option<Vec<AccessControlAllowOrigin>>,
apps: Vec<App>,
fetcher: Arc<Fetcher>,
}
impl RestApi {
pub fn new(cors_domains: Vec<AccessControlAllowOrigin>, endpoints: &Endpoints, fetcher: Arc<Fetcher>) -> Box<Endpoint> {
pub fn new(fetcher: Arc<Fetcher>) -> Box<Endpoint> {
Box::new(RestApi {
cors_domains: Some(cors_domains),
apps: Self::list_apps(endpoints),
fetcher: fetcher,
})
}
fn list_apps(endpoints: &Endpoints) -> Vec<App> {
endpoints.iter().filter_map(|(ref k, ref e)| {
e.info().map(|ref info| App::from_info(k, info))
}).collect()
}
}
impl Endpoint for RestApi {
@ -62,7 +47,6 @@ impl Endpoint for RestApi {
struct RestApiRouter {
api: RestApi,
cors_header: Option<header::AccessControlAllowOrigin>,
path: Option<EndpointPath>,
control: Option<Control>,
handler: Box<Handler>,
@ -72,7 +56,6 @@ impl RestApiRouter {
fn new(api: RestApi, path: EndpointPath, control: Control) -> Self {
RestApiRouter {
path: Some(path),
cors_header: None,
control: Some(control),
api: api,
handler: response::as_json_error(&ApiError {
@ -92,35 +75,10 @@ impl RestApiRouter {
_ => None
}
}
/// Returns basic headers for a response (it may be overwritten by the handler)
fn response_headers(cors_header: Option<header::AccessControlAllowOrigin>) -> header::Headers {
let mut headers = header::Headers::new();
if let Some(cors_header) = cors_header {
headers.set(header::AccessControlAllowCredentials);
headers.set(header::AccessControlAllowMethods(vec![
Method::Options,
Method::Post,
Method::Get,
]));
headers.set(header::AccessControlAllowHeaders(vec![
UniCase("origin".to_owned()),
UniCase("content-type".to_owned()),
UniCase("accept".to_owned()),
]));
headers.set(cors_header);
}
headers
}
}
impl server::Handler<net::HttpStream> for RestApiRouter {
fn on_request(&mut self, request: server::Request<net::HttpStream>) -> Next {
self.cors_header = jsonrpc_http_server::cors_header(&request, &self.api.cors_domains).into();
if let Method::Options = *request.method() {
self.handler = response::empty();
return Next::write();
@ -144,7 +102,6 @@ impl server::Handler<net::HttpStream> for RestApiRouter {
if let Some(ref hash) = hash { path.app_id = hash.clone().to_owned() }
let handler = endpoint.and_then(|v| match v {
"apps" => Some(response::as_json(&self.api.apps)),
"ping" => Some(response::ping()),
"content" => self.resolve_content(hash, path, control),
_ => None
@ -163,7 +120,6 @@ impl server::Handler<net::HttpStream> for RestApiRouter {
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
*res.headers_mut() = Self::response_headers(self.cors_header.take());
self.handler.on_response(res)
}

View File

@ -21,4 +21,3 @@ mod response;
mod types;
pub use self::api::RestApi;
pub use self::types::App;

View File

@ -23,12 +23,6 @@ pub fn empty() -> Box<Handler> {
Box::new(ContentHandler::ok("".into(), mime!(Text/Plain)))
}
pub fn as_json<T: Serialize>(val: &T) -> Box<Handler> {
let json = serde_json::to_string(val)
.expect("serialization to string is infallible; qed");
Box::new(ContentHandler::ok(json, mime!(Application/Json)))
}
pub fn as_json_error<T: Serialize>(val: &T) -> Box<Handler> {
let json = serde_json::to_string(val)
.expect("serialization to string is infallible; qed");

View File

@ -14,46 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use endpoint::EndpointInfo;
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct App {
pub id: String,
pub name: String,
pub description: String,
pub version: String,
pub author: String,
#[serde(rename="iconUrl")]
pub icon_url: String,
}
impl App {
/// Creates `App` instance from `EndpointInfo` and `id`.
pub fn from_info(id: &str, info: &EndpointInfo) -> Self {
App {
id: id.to_owned(),
name: info.name.to_owned(),
description: info.description.to_owned(),
version: info.version.to_owned(),
author: info.author.to_owned(),
icon_url: info.icon_url.to_owned(),
}
}
}
impl Into<EndpointInfo> for App {
fn into(self) -> EndpointInfo {
EndpointInfo {
name: self.name,
description: self.description,
version: self.version,
author: self.author,
icon_url: self.icon_url,
}
}
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ApiError {

55
dapps/src/apps/app.rs Normal file
View File

@ -0,0 +1,55 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use endpoint::EndpointInfo;
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct App {
pub id: String,
pub name: String,
pub description: String,
pub version: String,
pub author: String,
#[serde(rename="iconUrl")]
pub icon_url: String,
}
impl App {
/// Creates `App` instance from `EndpointInfo` and `id`.
pub fn from_info(id: &str, info: &EndpointInfo) -> Self {
App {
id: id.to_owned(),
name: info.name.to_owned(),
description: info.description.to_owned(),
version: info.version.to_owned(),
author: info.author.to_owned(),
icon_url: info.icon_url.to_owned(),
}
}
}
impl Into<EndpointInfo> for App {
fn into(self) -> EndpointInfo {
EndpointInfo {
name: self.name,
description: self.description,
version: self.version,
author: self.author,
icon_url: self.icon_url,
}
}
}

View File

@ -55,6 +55,7 @@ pub struct ContentFetcher<F: Fetch = FetchClient, R: URLHint + 'static = URLHint
embeddable_on: Option<(String, u16)>,
remote: Remote,
fetch: F,
only_content: bool,
}
impl<R: URLHint + 'static, F: Fetch> Drop for ContentFetcher<F, R> {
@ -66,7 +67,12 @@ impl<R: URLHint + 'static, F: Fetch> Drop for ContentFetcher<F, R> {
impl<R: URLHint + 'static, F: Fetch> ContentFetcher<F, R> {
pub fn new(resolver: R, sync_status: Arc<SyncStatus>, embeddable_on: Option<(String, u16)>, remote: Remote, fetch: F) -> Self {
pub fn new(
resolver: R,
sync_status: Arc<SyncStatus>,
remote: Remote,
fetch: F,
) -> Self {
let mut dapps_path = env::temp_dir();
dapps_path.push(random_filename());
@ -75,12 +81,23 @@ impl<R: URLHint + 'static, F: Fetch> ContentFetcher<F, R> {
resolver: resolver,
sync: sync_status,
cache: Arc::new(Mutex::new(ContentCache::default())),
embeddable_on: embeddable_on,
embeddable_on: None,
remote: remote,
fetch: fetch,
only_content: true,
}
}
pub fn allow_dapps(mut self, dapps: bool) -> Self {
self.only_content = !dapps;
self
}
pub fn embeddable_on(mut self, embeddable_on: Option<(String, u16)>) -> Self {
self.embeddable_on = embeddable_on;
self
}
fn still_syncing(address: Option<(String, u16)>) -> Box<Handler> {
Box::new(ContentHandler::error(
StatusCode::ServiceUnavailable,
@ -91,6 +108,16 @@ impl<R: URLHint + 'static, F: Fetch> ContentFetcher<F, R> {
))
}
fn dapps_disabled(address: Option<(String, u16)>) -> Box<Handler> {
Box::new(ContentHandler::error(
StatusCode::ServiceUnavailable,
"Network Dapps Not Available",
"This interface doesn't support network dapps for security reasons.",
None,
address,
))
}
#[cfg(test)]
fn set_status(&self, content_id: &str, status: ContentStatus) {
self.cache.lock().insert(content_id.to_owned(), status);
@ -163,6 +190,9 @@ impl<R: URLHint + 'static, F: Fetch> Fetcher for ContentFetcher<F, R> {
Some(URLHintResult::Dapp(_)) if self.sync.is_major_importing() => {
(None, Self::still_syncing(self.embeddable_on.clone()))
},
Some(URLHintResult::Dapp(_)) if self.only_content => {
(None, Self::dapps_disabled(self.embeddable_on.clone()))
},
Some(URLHintResult::Dapp(dapp)) => {
let handler = ContentFetcherHandler::new(
dapp.url(),
@ -254,7 +284,8 @@ mod tests {
fn should_true_if_contains_the_app() {
// given
let path = env::temp_dir();
let fetcher = ContentFetcher::new(FakeResolver, Arc::new(|| false), None, Remote::new_sync(), Client::new().unwrap());
let fetcher = ContentFetcher::new(FakeResolver, Arc::new(|| false), Remote::new_sync(), Client::new().unwrap())
.allow_dapps(true);
let handler = LocalPageEndpoint::new(path, EndpointInfo {
name: "fake".into(),
description: "".into(),

View File

@ -14,12 +14,13 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::collections::BTreeMap;
use std::io;
use std::io::Read;
use std::fs;
use std::path::{Path, PathBuf};
use page::{LocalPageEndpoint, PageCache};
use endpoint::{Endpoints, EndpointInfo};
use endpoint::{Endpoint, EndpointInfo};
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest};
struct LocalDapp {
@ -85,8 +86,8 @@ fn local_dapp(name: String, path: PathBuf) -> LocalDapp {
/// Returns endpoints for Local Dapps found for given filesystem path.
/// Scans the directory and collects `LocalPageEndpoints`.
pub fn local_endpoints<P: AsRef<Path>>(dapps_path: P, signer_address: Option<(String, u16)>) -> Endpoints {
let mut pages = Endpoints::new();
pub fn local_endpoints<P: AsRef<Path>>(dapps_path: P, signer_address: Option<(String, u16)>) -> BTreeMap<String, Box<Endpoint>> {
let mut pages = BTreeMap::<String, Box<Endpoint>>::new();
for dapp in local_dapps(dapps_path.as_ref()) {
pages.insert(
dapp.id,

View File

@ -15,7 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use serde_json;
pub use api::App as Manifest;
pub use apps::App as Manifest;
pub const MANIFEST_FILENAME: &'static str = "manifest.json";

View File

@ -14,8 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::collections::BTreeMap;
use std::path::PathBuf;
use std::sync::Arc;
use endpoint::{Endpoints, Endpoint};
use page::PageEndpoint;
use proxypac::ProxyPac;
@ -23,17 +25,19 @@ use web::Web;
use fetch::Fetch;
use parity_dapps::WebApp;
use parity_reactor::Remote;
use parity_ui;
use {WebProxyTokens};
mod app;
mod cache;
mod fs;
mod ui;
pub mod fetcher;
pub mod manifest;
extern crate parity_ui;
pub use self::app::App;
pub const HOME_PAGE: &'static str = "parity";
pub const DAPPS_DOMAIN: &'static str = ".web3.site";
pub const HOME_PAGE: &'static str = "home";
pub const RPC_PATH: &'static str = "rpc";
pub const API_PATH: &'static str = "api";
pub const UTILS_PATH: &'static str = "parity-utils";
@ -44,18 +48,27 @@ pub fn utils() -> Box<Endpoint> {
Box::new(PageEndpoint::with_prefix(parity_ui::App::default(), UTILS_PATH.to_owned()))
}
pub fn ui() -> Box<Endpoint> {
Box::new(PageEndpoint::with_fallback_to_index(parity_ui::App::default()))
}
pub fn ui_redirection(ui_address: Option<(String, u16)>) -> Box<Endpoint> {
Box::new(ui::Redirection::new(ui_address))
}
pub fn all_endpoints<F: Fetch>(
dapps_path: PathBuf,
extra_dapps: Vec<PathBuf>,
signer_address: Option<(String, u16)>,
dapps_domain: String,
ui_address: Option<(String, u16)>,
web_proxy_tokens: Arc<WebProxyTokens>,
remote: Remote,
fetch: F,
) -> Endpoints {
// fetch fs dapps at first to avoid overwriting builtins
let mut pages = fs::local_endpoints(dapps_path, signer_address.clone());
let mut pages = fs::local_endpoints(dapps_path, ui_address.clone());
for path in extra_dapps {
if let Some((id, endpoint)) = fs::local_endpoint(path.clone(), signer_address.clone()) {
if let Some((id, endpoint)) = fs::local_endpoint(path.clone(), ui_address.clone()) {
pages.insert(id, endpoint);
} else {
warn!(target: "dapps", "Ignoring invalid dapp at {}", path.display());
@ -63,14 +76,14 @@ pub fn all_endpoints<F: Fetch>(
}
// NOTE [ToDr] Dapps will be currently embeded on 8180
insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(signer_address.clone()));
pages.insert("proxy".into(), ProxyPac::boxed(signer_address.clone()));
pages.insert(WEB_PATH.into(), Web::boxed(signer_address.clone(), web_proxy_tokens.clone(), remote.clone(), fetch.clone()));
insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(ui_address.clone()));
pages.insert("proxy".into(), ProxyPac::boxed(ui_address.clone(), dapps_domain));
pages.insert(WEB_PATH.into(), Web::boxed(ui_address.clone(), web_proxy_tokens.clone(), remote.clone(), fetch.clone()));
pages
Arc::new(pages)
}
fn insert<T : WebApp + Default + 'static>(pages: &mut Endpoints, id: &str, embed_at: Embeddable) {
fn insert<T : WebApp + Default + 'static>(pages: &mut BTreeMap<String, Box<Endpoint>>, id: &str, embed_at: Embeddable) {
pages.insert(id.to_owned(), Box::new(match embed_at {
Embeddable::Yes(address) => PageEndpoint::new_safe_to_embed(T::default(), address),
Embeddable::No => PageEndpoint::new(T::default()),

55
dapps/src/apps/ui.rs Normal file
View File

@ -0,0 +1,55 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! UI redirections
use hyper::{Control, StatusCode};
use endpoint::{Endpoint, Handler, EndpointPath};
use {address, handlers};
/// Redirection to UI server.
pub struct Redirection {
signer_address: Option<(String, u16)>,
}
impl Redirection {
pub fn new(
signer_address: Option<(String, u16)>,
) -> Self {
Redirection {
signer_address: signer_address,
}
}
}
impl Endpoint for Redirection {
fn to_async_handler(&self, _path: EndpointPath, _control: Control) -> Box<Handler> {
if let Some(ref signer_address) = self.signer_address {
trace!(target: "dapps", "Redirecting to signer interface.");
handlers::Redirection::boxed(&format!("http://{}", address(signer_address)))
} else {
trace!(target: "dapps", "Signer disabled, returning 404.");
Box::new(handlers::ContentHandler::error(
StatusCode::NotFound,
"404 Not Found",
"Your homepage is not available when Trusted Signer is disabled.",
Some("You can still access dapps by writing a correct address, though. Re-enable Signer to get your homepage back."),
self.signer_address.clone(),
))
}
}
}

View File

@ -16,6 +16,7 @@
//! URL Endpoint traits
use std::sync::Arc;
use std::collections::BTreeMap;
use hyper::{self, server, net};
@ -38,7 +39,7 @@ pub struct EndpointInfo {
pub icon_url: String,
}
pub type Endpoints = BTreeMap<String, Box<Endpoint>>;
pub type Endpoints = Arc<BTreeMap<String, Box<Endpoint>>>;
pub type Handler = server::Handler<net::HttpStream> + Send;
pub trait Endpoint : Send + Sync {

View File

@ -40,6 +40,7 @@ extern crate fetch;
extern crate parity_dapps_glue as parity_dapps;
extern crate parity_hash_fetch as hash_fetch;
extern crate parity_reactor;
extern crate parity_ui;
#[macro_use]
extern crate log;
@ -70,7 +71,7 @@ use std::path::PathBuf;
use std::sync::Arc;
use std::collections::HashMap;
use jsonrpc_http_server::{self as http, hyper, AccessControlAllowOrigin};
use jsonrpc_http_server::{self as http, hyper};
use fetch::Fetch;
use parity_reactor::Remote;
@ -97,18 +98,74 @@ impl<F> WebProxyTokens for F where F: Fn(String) -> bool + Send + Sync {
fn is_web_proxy_token_valid(&self, token: &str) -> bool { self(token.to_owned()) }
}
/// Current supported endpoints.
pub struct Endpoints {
endpoints: endpoint::Endpoints,
}
impl Endpoints {
/// Returns a current list of app endpoints.
pub fn list(&self) -> Vec<apps::App> {
self.endpoints.iter().filter_map(|(ref k, ref e)| {
e.info().map(|ref info| apps::App::from_info(k, info))
}).collect()
}
}
/// Dapps server as `jsonrpc-http-server` request middleware.
pub struct Middleware {
router: router::Router,
endpoints: endpoint::Endpoints,
}
impl Middleware {
/// Creates new Dapps server middleware.
pub fn new<F: Fetch + Clone>(
/// Get local endpoints handle.
pub fn endpoints(&self) -> Endpoints {
Endpoints {
endpoints: self.endpoints.clone(),
}
}
/// Creates new middleware for UI server.
pub fn ui<F: Fetch + Clone>(
remote: Remote,
signer_address: Option<(String, u16)>,
registrar: Arc<ContractClient>,
sync_status: Arc<SyncStatus>,
fetch: F,
dapps_domain: String,
) -> Self {
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
hash_fetch::urlhint::URLHintContract::new(registrar),
sync_status,
remote.clone(),
fetch.clone(),
).embeddable_on(None).allow_dapps(false));
let special = {
let mut special = special_endpoints(content_fetcher.clone());
special.insert(router::SpecialEndpoint::Home, Some(apps::ui()));
special
};
let router = router::Router::new(
content_fetcher,
None,
special,
None,
dapps_domain,
);
Middleware {
router: router,
endpoints: Default::default(),
}
}
/// Creates new Dapps server middleware.
pub fn dapps<F: Fetch + Clone>(
remote: Remote,
ui_address: Option<(String, u16)>,
dapps_path: PathBuf,
extra_dapps: Vec<PathBuf>,
dapps_domain: String,
registrar: Arc<ContractClient>,
sync_status: Arc<SyncStatus>,
web_proxy_tokens: Arc<WebProxyTokens>,
@ -117,45 +174,36 @@ impl Middleware {
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
hash_fetch::urlhint::URLHintContract::new(registrar),
sync_status,
signer_address.clone(),
remote.clone(),
fetch.clone(),
));
).embeddable_on(ui_address.clone()).allow_dapps(true));
let endpoints = apps::all_endpoints(
dapps_path,
extra_dapps,
signer_address.clone(),
dapps_domain.clone(),
ui_address.clone(),
web_proxy_tokens,
remote.clone(),
fetch.clone(),
);
let cors_domains = cors_domains(signer_address.clone());
let special = {
let mut special = HashMap::new();
special.insert(router::SpecialEndpoint::Rpc, None);
special.insert(router::SpecialEndpoint::Utils, Some(apps::utils()));
special.insert(
router::SpecialEndpoint::Api,
Some(api::RestApi::new(
cors_domains.clone(),
&endpoints,
content_fetcher.clone()
)),
);
let mut special = special_endpoints(content_fetcher.clone());
special.insert(router::SpecialEndpoint::Home, Some(apps::ui_redirection(ui_address.clone())));
special
};
let router = router::Router::new(
signer_address,
content_fetcher,
endpoints,
Some(endpoints.clone()),
special,
ui_address,
dapps_domain,
);
Middleware {
router: router,
endpoints: endpoints,
}
}
}
@ -166,21 +214,12 @@ impl http::RequestMiddleware for Middleware {
}
}
/// Returns a list of CORS domains for API endpoint.
fn cors_domains(signer_address: Option<(String, u16)>) -> Vec<AccessControlAllowOrigin> {
use self::apps::{HOME_PAGE, DAPPS_DOMAIN};
match signer_address {
Some(signer_address) => [
format!("http://{}{}", HOME_PAGE, DAPPS_DOMAIN),
format!("http://{}{}:{}", HOME_PAGE, DAPPS_DOMAIN, signer_address.1),
format!("http://{}", address(&signer_address)),
format!("https://{}{}", HOME_PAGE, DAPPS_DOMAIN),
format!("https://{}{}:{}", HOME_PAGE, DAPPS_DOMAIN, signer_address.1),
format!("https://{}", address(&signer_address)),
].into_iter().map(|val| AccessControlAllowOrigin::Value(val.into())).collect(),
None => vec![],
}
fn special_endpoints(content_fetcher: Arc<apps::fetcher::Fetcher>) -> HashMap<router::SpecialEndpoint, Option<Box<endpoint::Endpoint>>> {
let mut special = HashMap::new();
special.insert(router::SpecialEndpoint::Rpc, None);
special.insert(router::SpecialEndpoint::Utils, Some(apps::utils()));
special.insert(router::SpecialEndpoint::Api, Some(api::RestApi::new(content_fetcher)));
special
}
fn address(address: &(String, u16)) -> String {
@ -193,29 +232,3 @@ fn random_filename() -> String {
let mut rng = ::rand::OsRng::new().unwrap();
rng.gen_ascii_chars().take(12).collect()
}
#[cfg(test)]
mod util_tests {
use super::cors_domains;
use jsonrpc_http_server::AccessControlAllowOrigin;
#[test]
fn should_return_cors_domains() {
// given
// when
let none = cors_domains(None);
let some = cors_domains(Some(("127.0.0.1".into(), 18180)));
// then
assert_eq!(none, Vec::<AccessControlAllowOrigin>::new());
assert_eq!(some, vec![
"http://parity.web3.site".into(),
"http://parity.web3.site:18180".into(),
"http://127.0.0.1:18180".into(),
"https://parity.web3.site".into(),
"https://parity.web3.site:18180".into(),
"https://127.0.0.1:18180".into(),
]);
}
}

View File

@ -27,6 +27,7 @@ pub struct PageEndpoint<T : WebApp + 'static> {
/// Safe to be loaded in frame by other origin. (use wisely!)
safe_to_embed_on: Option<(String, u16)>,
info: EndpointInfo,
fallback_to_index_html: bool,
}
impl<T: WebApp + 'static> PageEndpoint<T> {
@ -38,6 +39,20 @@ impl<T: WebApp + 'static> PageEndpoint<T> {
prefix: None,
safe_to_embed_on: None,
info: EndpointInfo::from(info),
fallback_to_index_html: false,
}
}
/// Creates a new `PageEndpoint` for builtin (compile time) Dapp.
/// Instead of returning 404 this endpoint will always server index.html.
pub fn with_fallback_to_index(app: T) -> Self {
let info = app.info();
PageEndpoint {
app: Arc::new(app),
prefix: None,
safe_to_embed_on: None,
info: EndpointInfo::from(info),
fallback_to_index_html: true,
}
}
@ -51,6 +66,7 @@ impl<T: WebApp + 'static> PageEndpoint<T> {
prefix: Some(prefix),
safe_to_embed_on: None,
info: EndpointInfo::from(info),
fallback_to_index_html: false,
}
}
@ -64,6 +80,7 @@ impl<T: WebApp + 'static> PageEndpoint<T> {
prefix: None,
safe_to_embed_on: address,
info: EndpointInfo::from(info),
fallback_to_index_html: false,
}
}
}
@ -76,7 +93,7 @@ impl<T: WebApp> Endpoint for PageEndpoint<T> {
fn to_handler(&self, path: EndpointPath) -> Box<Handler> {
Box::new(handler::PageHandler {
app: BuiltinDapp::new(self.app.clone()),
app: BuiltinDapp::new(self.app.clone(), self.fallback_to_index_html),
prefix: self.prefix.clone(),
path: path,
file: handler::ServedFile::new(self.safe_to_embed_on.clone()),
@ -100,12 +117,14 @@ impl From<Info> for EndpointInfo {
struct BuiltinDapp<T: WebApp + 'static> {
app: Arc<T>,
fallback_to_index_html: bool,
}
impl<T: WebApp + 'static> BuiltinDapp<T> {
fn new(app: Arc<T>) -> Self {
fn new(app: Arc<T>, fallback_to_index_html: bool) -> Self {
BuiltinDapp {
app: app,
fallback_to_index_html: fallback_to_index_html,
}
}
}
@ -114,13 +133,19 @@ impl<T: WebApp + 'static> handler::Dapp for BuiltinDapp<T> {
type DappFile = BuiltinDappFile<T>;
fn file(&self, path: &str) -> Option<Self::DappFile> {
self.app.file(path).map(|_| {
let file = |path| self.app.file(path).map(|_| {
BuiltinDappFile {
app: self.app.clone(),
path: path.into(),
write_pos: 0,
}
})
});
let res = file(path);
if self.fallback_to_index_html {
res.or_else(|| file("index.html"))
} else {
res
}
}
}

View File

@ -18,17 +18,19 @@
use endpoint::{Endpoint, Handler, EndpointPath};
use handlers::ContentHandler;
use apps::{HOME_PAGE, DAPPS_DOMAIN};
use apps::HOME_PAGE;
use address;
pub struct ProxyPac {
signer_address: Option<(String, u16)>,
dapps_domain: String,
}
impl ProxyPac {
pub fn boxed(signer_address: Option<(String, u16)>) -> Box<Endpoint> {
pub fn boxed(signer_address: Option<(String, u16)>, dapps_domain: String) -> Box<Endpoint> {
Box::new(ProxyPac {
signer_address: signer_address
signer_address: signer_address,
dapps_domain: dapps_domain,
})
}
}
@ -43,12 +45,12 @@ impl Endpoint for ProxyPac {
let content = format!(
r#"
function FindProxyForURL(url, host) {{
if (shExpMatch(host, "{0}{1}"))
if (shExpMatch(host, "{0}.{1}"))
{{
return "PROXY {4}";
}}
if (shExpMatch(host, "*{1}"))
if (shExpMatch(host, "*.{1}"))
{{
return "PROXY {2}:{3}";
}}
@ -56,7 +58,7 @@ function FindProxyForURL(url, host) {{
return "DIRECT";
}}
"#,
HOME_PAGE, DAPPS_DOMAIN, path.host, path.port, signer);
HOME_PAGE, self.dapps_domain, path.host, path.port, signer);
Box::new(ContentHandler::ok(content, mime!(Application/Javascript)))
}

View File

@ -17,20 +17,19 @@
//! Router implementation
//! Dispatch requests to proper application.
use address;
use std::cmp;
use std::sync::Arc;
use std::collections::HashMap;
use url::{Url, Host};
use hyper::{self, server, header, Control, StatusCode};
use hyper::{self, server, header, Control};
use hyper::net::HttpStream;
use jsonrpc_http_server as http;
use apps::{self, DAPPS_DOMAIN};
use apps;
use apps::fetcher::Fetcher;
use endpoint::{Endpoint, Endpoints, EndpointPath, Handler};
use handlers::{self, Redirection, ContentHandler};
use handlers;
/// Special endpoints are accessible on every domain (every dapp)
#[derive(Debug, PartialEq, Hash, Eq)]
@ -38,26 +37,28 @@ pub enum SpecialEndpoint {
Rpc,
Api,
Utils,
Home,
None,
}
pub struct Router {
signer_address: Option<(String, u16)>,
endpoints: Endpoints,
endpoints: Option<Endpoints>,
fetch: Arc<Fetcher>,
special: HashMap<SpecialEndpoint, Option<Box<Endpoint>>>,
embeddable_on: Option<(String, u16)>,
dapps_domain: String,
}
impl http::RequestMiddleware for Router {
fn on_request(&self, req: &server::Request<HttpStream>, control: &Control) -> http::RequestMiddlewareAction {
// Choose proper handler depending on path / domain
let url = handlers::extract_url(req);
let endpoint = extract_endpoint(&url);
let referer = extract_referer_endpoint(req);
let endpoint = extract_endpoint(&url, &self.dapps_domain);
let referer = extract_referer_endpoint(req, &self.dapps_domain);
let is_utils = endpoint.1 == SpecialEndpoint::Utils;
let is_dapps_domain = endpoint.0.as_ref().map(|endpoint| endpoint.using_dapps_domains).unwrap_or(false);
let is_origin_set = req.headers().get::<http::hyper::header::Origin>().is_some();
let is_origin_set = req.headers().get::<header::Origin>().is_some();
let is_get_request = *req.method() == hyper::Method::Get;
let is_head_request = *req.method() == hyper::Method::Head;
trace!(target: "dapps", "Routing request to {:?}. Details: {:?}", url, req);
@ -67,7 +68,7 @@ impl http::RequestMiddleware for Router {
// Handle invalid web requests that we can recover from
(ref path, SpecialEndpoint::None, Some((ref referer, ref referer_url)))
if referer.app_id == apps::WEB_PATH
&& self.endpoints.contains_key(apps::WEB_PATH)
&& self.endpoints.as_ref().map(|ep| ep.contains_key(apps::WEB_PATH)).unwrap_or(false)
&& !is_web_endpoint(path)
=>
{
@ -75,7 +76,7 @@ impl http::RequestMiddleware for Router {
let len = cmp::min(referer_url.path.len(), 2); // /web/<encoded>/
let base = referer_url.path[..len].join("/");
let requested = url.map(|u| u.path.join("/")).unwrap_or_default();
Some(Redirection::boxed(&format!("/{}/{}", base, requested)))
Some(handlers::Redirection::boxed(&format!("/{}/{}", base, requested)))
},
// First check special endpoints
(ref path, ref endpoint, _) if self.special.contains_key(endpoint) => {
@ -86,9 +87,12 @@ impl http::RequestMiddleware for Router {
.map(|special| special.to_async_handler(path.clone().unwrap_or_default(), control))
},
// Then delegate to dapp
(Some(ref path), _, _) if self.endpoints.contains_key(&path.app_id) => {
(Some(ref path), _, _) if self.endpoints.as_ref().map(|ep| ep.contains_key(&path.app_id)).unwrap_or(false) => {
trace!(target: "dapps", "Resolving to local/builtin dapp.");
Some(self.endpoints.get(&path.app_id)
Some(self.endpoints
.as_ref()
.expect("endpoints known to be set; qed")
.get(&path.app_id)
.expect("endpoints known to contain key; qed")
.to_async_handler(path.clone(), control))
},
@ -97,36 +101,28 @@ impl http::RequestMiddleware for Router {
trace!(target: "dapps", "Resolving to fetchable content.");
Some(self.fetch.to_async_handler(path.clone(), control))
},
// NOTE [todr] /home is redirected to home page since some users may have the redirection cached
// (in the past we used 301 instead of 302)
// It should be safe to remove it in (near) future.
//
// 404 for non-existent content
(Some(ref path), _, _) if is_get_request && path.app_id != "home" => {
// 404 for non-existent content (only if serving endpoints and not homepage)
(Some(ref path), _, _)
if (is_get_request || is_head_request)
&& self.endpoints.is_some()
&& path.app_id != apps::HOME_PAGE
=>
{
trace!(target: "dapps", "Resolving to 404.");
Some(Box::new(ContentHandler::error(
StatusCode::NotFound,
Some(Box::new(handlers::ContentHandler::error(
hyper::StatusCode::NotFound,
"404 Not Found",
"Requested content was not found.",
None,
self.signer_address.clone(),
self.embeddable_on.clone(),
)))
},
// Redirect any other GET request to signer.
_ if is_get_request => {
if let Some(ref signer_address) = self.signer_address {
trace!(target: "dapps", "Redirecting to signer interface.");
Some(Redirection::boxed(&format!("http://{}", address(signer_address))))
} else {
trace!(target: "dapps", "Signer disabled, returning 404.");
Some(Box::new(ContentHandler::error(
StatusCode::NotFound,
"404 Not Found",
"Your homepage is not available when Trusted Signer is disabled.",
Some("You can still access dapps by writing a correct address, though. Re-enable Signer to get your homepage back."),
self.signer_address.clone(),
)))
}
// Any other GET|HEAD requests to home page.
_ if (is_get_request || is_head_request) && self.special.contains_key(&SpecialEndpoint::Home) => {
self.special.get(&SpecialEndpoint::Home)
.expect("special known to contain key; qed")
.as_ref()
.map(|special| special.to_async_handler(Default::default(), control))
},
// RPC by default
_ => {
@ -137,7 +133,7 @@ impl http::RequestMiddleware for Router {
match handler {
Some(handler) => http::RequestMiddlewareAction::Respond {
should_validate_hosts: !(is_utils || is_dapps_domain),
should_validate_hosts: !is_utils,
handler: handler,
},
None => http::RequestMiddlewareAction::Proceed {
@ -149,16 +145,18 @@ impl http::RequestMiddleware for Router {
impl Router {
pub fn new(
signer_address: Option<(String, u16)>,
content_fetcher: Arc<Fetcher>,
endpoints: Endpoints,
endpoints: Option<Endpoints>,
special: HashMap<SpecialEndpoint, Option<Box<Endpoint>>>,
embeddable_on: Option<(String, u16)>,
dapps_domain: String,
) -> Self {
Router {
signer_address: signer_address,
endpoints: endpoints,
fetch: content_fetcher,
special: special,
embeddable_on: embeddable_on,
dapps_domain: format!(".{}", dapps_domain),
}
}
}
@ -170,19 +168,19 @@ fn is_web_endpoint(path: &Option<EndpointPath>) -> bool {
}
}
fn extract_referer_endpoint(req: &server::Request<HttpStream>) -> Option<(EndpointPath, Url)> {
fn extract_referer_endpoint(req: &server::Request<HttpStream>, dapps_domain: &str) -> Option<(EndpointPath, Url)> {
let referer = req.headers().get::<header::Referer>();
let url = referer.and_then(|referer| Url::parse(&referer.0).ok());
url.and_then(|url| {
let option = Some(url);
extract_url_referer_endpoint(&option).or_else(|| {
extract_endpoint(&option).0.map(|endpoint| (endpoint, option.expect("Just wrapped; qed")))
extract_url_referer_endpoint(&option, dapps_domain).or_else(|| {
extract_endpoint(&option, dapps_domain).0.map(|endpoint| (endpoint, option.expect("Just wrapped; qed")))
})
})
}
fn extract_url_referer_endpoint(url: &Option<Url>) -> Option<(EndpointPath, Url)> {
fn extract_url_referer_endpoint(url: &Option<Url>, dapps_domain: &str) -> Option<(EndpointPath, Url)> {
let query = url.as_ref().and_then(|url| url.query.as_ref());
match (url, query) {
(&Some(ref url), Some(ref query)) if query.starts_with(apps::URL_REFERER) => {
@ -190,7 +188,7 @@ fn extract_url_referer_endpoint(url: &Option<Url>) -> Option<(EndpointPath, Url)
debug!(target: "dapps", "Recovering referer from query parameter: {}", referer_url);
let referer_url = Url::parse(&referer_url).ok();
extract_endpoint(&referer_url).0.map(|endpoint| {
extract_endpoint(&referer_url, dapps_domain).0.map(|endpoint| {
(endpoint, referer_url.expect("Endpoint returned only when url `is_some`").clone())
})
},
@ -198,7 +196,7 @@ fn extract_url_referer_endpoint(url: &Option<Url>) -> Option<(EndpointPath, Url)
}
}
fn extract_endpoint(url: &Option<Url>) -> (Option<EndpointPath>, SpecialEndpoint) {
fn extract_endpoint(url: &Option<Url>, dapps_domain: &str) -> (Option<EndpointPath>, SpecialEndpoint) {
fn special_endpoint(url: &Url) -> SpecialEndpoint {
if url.path.len() <= 1 {
return SpecialEndpoint::None;
@ -208,14 +206,15 @@ fn extract_endpoint(url: &Option<Url>) -> (Option<EndpointPath>, SpecialEndpoint
apps::RPC_PATH => SpecialEndpoint::Rpc,
apps::API_PATH => SpecialEndpoint::Api,
apps::UTILS_PATH => SpecialEndpoint::Utils,
apps::HOME_PAGE => SpecialEndpoint::Home,
_ => SpecialEndpoint::None,
}
}
match *url {
Some(ref url) => match url.host {
Host::Domain(ref domain) if domain.ends_with(DAPPS_DOMAIN) => {
let id = &domain[0..(domain.len() - DAPPS_DOMAIN.len())];
Host::Domain(ref domain) if domain.ends_with(dapps_domain) => {
let id = &domain[0..(domain.len() - dapps_domain.len())];
let (id, params) = if let Some(split) = id.rfind('.') {
let (params, id) = id.split_at(split);
(id[1..].to_owned(), [params.to_owned()].into_iter().chain(&url.path).cloned().collect())
@ -249,11 +248,12 @@ fn extract_endpoint(url: &Option<Url>) -> (Option<EndpointPath>, SpecialEndpoint
#[test]
fn should_extract_endpoint() {
assert_eq!(extract_endpoint(&None), (None, SpecialEndpoint::None));
let dapps_domain = ".web3.site";
assert_eq!(extract_endpoint(&None, dapps_domain), (None, SpecialEndpoint::None));
// With path prefix
assert_eq!(
extract_endpoint(&Url::parse("http://localhost:8080/status/index.html").ok()),
extract_endpoint(&Url::parse("http://localhost:8080/status/index.html").ok(), dapps_domain),
(Some(EndpointPath {
app_id: "status".to_owned(),
app_params: vec!["index.html".to_owned()],
@ -265,7 +265,7 @@ fn should_extract_endpoint() {
// With path prefix
assert_eq!(
extract_endpoint(&Url::parse("http://localhost:8080/rpc/").ok()),
extract_endpoint(&Url::parse("http://localhost:8080/rpc/").ok(), dapps_domain),
(Some(EndpointPath {
app_id: "rpc".to_owned(),
app_params: vec!["".to_owned()],
@ -276,7 +276,7 @@ fn should_extract_endpoint() {
);
assert_eq!(
extract_endpoint(&Url::parse("http://my.status.web3.site/parity-utils/inject.js").ok()),
extract_endpoint(&Url::parse("http://my.status.web3.site/parity-utils/inject.js").ok(), dapps_domain),
(Some(EndpointPath {
app_id: "status".to_owned(),
app_params: vec!["my".to_owned(), "parity-utils".into(), "inject.js".into()],
@ -288,7 +288,7 @@ fn should_extract_endpoint() {
// By Subdomain
assert_eq!(
extract_endpoint(&Url::parse("http://status.web3.site/test.html").ok()),
extract_endpoint(&Url::parse("http://status.web3.site/test.html").ok(), dapps_domain),
(Some(EndpointPath {
app_id: "status".to_owned(),
app_params: vec!["test.html".to_owned()],
@ -300,7 +300,7 @@ fn should_extract_endpoint() {
// RPC by subdomain
assert_eq!(
extract_endpoint(&Url::parse("http://my.status.web3.site/rpc/").ok()),
extract_endpoint(&Url::parse("http://my.status.web3.site/rpc/").ok(), dapps_domain),
(Some(EndpointPath {
app_id: "status".to_owned(),
app_params: vec!["my".to_owned(), "rpc".into(), "".into()],
@ -312,7 +312,7 @@ fn should_extract_endpoint() {
// API by subdomain
assert_eq!(
extract_endpoint(&Url::parse("http://my.status.web3.site/api/").ok()),
extract_endpoint(&Url::parse("http://my.status.web3.site/api/").ok(), dapps_domain),
(Some(EndpointPath {
app_id: "status".to_owned(),
app_params: vec!["my".to_owned(), "api".into(), "".into()],

View File

@ -1,97 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use hyper;
use parity_rpc::{Metadata, Origin};
use jsonrpc_core::{Middleware, MetaIoHandler};
use jsonrpc_http_server::{self as http, AccessControlAllowOrigin, HttpMetaExtractor};
use jsonrpc_http_server::tokio_core::reactor::Remote;
use endpoint::{Endpoint, EndpointPath, Handler};
pub fn rpc<T: Middleware<Metadata>>(
handler: MetaIoHandler<Metadata, T>,
remote: Remote,
cors_domains: Vec<AccessControlAllowOrigin>,
) -> Box<Endpoint> {
Box::new(RpcEndpoint {
handler: Arc::new(handler),
remote: remote,
meta_extractor: Arc::new(MetadataExtractor),
cors_domain: Some(cors_domains),
// NOTE [ToDr] We don't need to do any hosts validation here. It's already done in router.
allowed_hosts: None,
})
}
struct RpcEndpoint<T: Middleware<Metadata>> {
handler: Arc<MetaIoHandler<Metadata, T>>,
remote: Remote,
meta_extractor: Arc<HttpMetaExtractor<Metadata>>,
cors_domain: Option<Vec<AccessControlAllowOrigin>>,
allowed_hosts: Option<Vec<http::Host>>,
}
impl<T: Middleware<Metadata>> Endpoint for RpcEndpoint<T> {
fn to_async_handler(&self, _path: EndpointPath, control: hyper::Control) -> Box<Handler> {
Box::new(http::ServerHandler::new(
http::Rpc {
handler: self.handler.clone(),
remote: self.remote.clone(),
extractor: self.meta_extractor.clone(),
},
self.cors_domain.clone(),
self.allowed_hosts.clone(),
Arc::new(NoopMiddleware),
control,
))
}
}
#[derive(Default)]
struct NoopMiddleware;
impl http::RequestMiddleware for NoopMiddleware {
fn on_request(&self, request: &http::hyper::server::Request<http::hyper::net::HttpStream>, _control: &http::hyper::Control) -> http::RequestMiddlewareAction {
http::RequestMiddlewareAction::Proceed {
should_continue_on_invalid_cors: request.headers().get::<http::hyper::header::Origin>().is_none(),
}
}
}
pub struct MetadataExtractor;
impl HttpMetaExtractor<Metadata> for MetadataExtractor {
fn read_metadata(&self, request: &http::hyper::server::Request<http::hyper::net::HttpStream>) -> Metadata {
let dapp_id = request.headers().get::<http::hyper::header::Origin>()
.map(|origin| format!("{}://{}", origin.scheme, origin.host))
.or_else(|| {
// fallback to custom header, but only if origin is null
request.headers().get_raw("origin")
.and_then(|raw| raw.one())
.and_then(|raw| if raw == "null".as_bytes() {
request.headers().get_raw("x-parity-origin")
.and_then(|raw| raw.one())
.map(|raw| String::from_utf8_lossy(raw).into_owned())
} else {
None
})
});
Metadata {
origin: Origin::Dapps(dapp_id.map(Into::into).unwrap_or_default()),
}
}
}

View File

@ -39,29 +39,6 @@ fn should_return_error() {
assert_security_headers(&response.headers);
}
#[test]
fn should_serve_apps() {
// given
let server = serve();
// when
let response = request(server,
"\
GET /api/apps HTTP/1.1\r\n\
Host: 127.0.0.1:8080\r\n\
Connection: close\r\n\
\r\n\
{}
"
);
// then
response.assert_status("HTTP/1.1 200 OK");
response.assert_header("Content-Type", "application/json");
assert!(response.body.contains("Parity UI"), response.body);
assert_security_headers(&response.headers);
}
#[test]
fn should_handle_ping() {
// given
@ -106,92 +83,3 @@ fn should_try_to_resolve_dapp() {
assert_eq!(registrar.calls.lock().len(), 2);
assert_security_headers(&response.headers);
}
#[test]
fn should_return_signer_port_cors_headers() {
// given
let server = serve();
// when
let response = request(server,
"\
POST /api/ping HTTP/1.1\r\n\
Host: localhost:8080\r\n\
Origin: http://127.0.0.1:18180\r\n\
Connection: close\r\n\
\r\n\
{}
"
);
// then
response.assert_status("HTTP/1.1 200 OK");
response.assert_header("Access-Control-Allow-Origin", "http://127.0.0.1:18180");
}
#[test]
fn should_return_signer_port_cors_headers_for_home_parity() {
// given
let server = serve();
// when
let response = request(server,
"\
POST /api/ping HTTP/1.1\r\n\
Host: localhost:8080\r\n\
Origin: http://parity.web3.site\r\n\
Connection: close\r\n\
\r\n\
{}
"
);
// then
response.assert_status("HTTP/1.1 200 OK");
response.assert_header("Access-Control-Allow-Origin", "http://parity.web3.site");
}
#[test]
fn should_return_signer_port_cors_headers_for_home_parity_with_https() {
// given
let server = serve();
// when
let response = request(server,
"\
POST /api/ping HTTP/1.1\r\n\
Host: localhost:8080\r\n\
Origin: https://parity.web3.site\r\n\
Connection: close\r\n\
\r\n\
{}
"
);
// then
response.assert_status("HTTP/1.1 200 OK");
response.assert_header("Access-Control-Allow-Origin", "https://parity.web3.site");
}
#[test]
fn should_return_signer_port_cors_headers_for_home_parity_with_port() {
// given
let server = serve();
// when
let response = request(server,
"\
POST /api/ping HTTP/1.1\r\n\
Host: localhost:8080\r\n\
Origin: http://parity.web3.site:18180\r\n\
Connection: close\r\n\
\r\n\
{}
"
);
// then
response.assert_status("HTTP/1.1 200 OK");
response.assert_header("Access-Control-Allow-Origin", "http://parity.web3.site:18180");
}

View File

@ -26,7 +26,7 @@ use jsonrpc_http_server::{self as http, Host, DomainsValidation};
use devtools::http_client;
use hash_fetch::urlhint::ContractClient;
use fetch::{Fetch, Client as FetchClient};
use parity_reactor::{EventLoop, Remote};
use parity_reactor::Remote;
use {Middleware, SyncStatus, WebProxyTokens};
@ -47,20 +47,7 @@ fn init_logger() {
}
}
pub struct ServerLoop {
pub server: Server,
pub event_loop: EventLoop,
}
impl ::std::ops::Deref for ServerLoop {
type Target = Server;
fn deref(&self) -> &Self::Target {
&self.server
}
}
pub fn init_server<F, B>(process: F, io: IoHandler, remote: Remote) -> (ServerLoop, Arc<FakeRegistrar>) where
pub fn init_server<F, B>(process: F, io: IoHandler, remote: Remote) -> (Server, Arc<FakeRegistrar>) where
F: FnOnce(ServerBuilder) -> ServerBuilder<B>,
B: Fetch,
{
@ -69,44 +56,41 @@ pub fn init_server<F, B>(process: F, io: IoHandler, remote: Remote) -> (ServerLo
let mut dapps_path = env::temp_dir();
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
// TODO [ToDr] When https://github.com/paritytech/jsonrpc/issues/26 is resolved
// this additional EventLoop wouldn't be needed, we should be able to re-use remote.
let event_loop = EventLoop::spawn();
let server = process(ServerBuilder::new(
&dapps_path, registrar.clone(), remote,
))
.signer_address(Some(("127.0.0.1".into(), SIGNER_PORT)))
.start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), io).unwrap();
(
ServerLoop { server: server, event_loop: event_loop },
server,
registrar,
)
}
pub fn serve_with_rpc(io: IoHandler) -> ServerLoop {
pub fn serve_with_rpc(io: IoHandler) -> Server {
init_server(|builder| builder, io, Remote::new_sync()).0
}
pub fn serve_hosts(hosts: Option<Vec<String>>) -> ServerLoop {
pub fn serve_hosts(hosts: Option<Vec<String>>) -> Server {
let hosts = hosts.map(|hosts| hosts.into_iter().map(Into::into).collect());
init_server(|builder| builder.allowed_hosts(hosts.into()), Default::default(), Remote::new_sync()).0
}
pub fn serve_with_registrar() -> (ServerLoop, Arc<FakeRegistrar>) {
pub fn serve_with_registrar() -> (Server, Arc<FakeRegistrar>) {
init_server(|builder| builder, Default::default(), Remote::new_sync())
}
pub fn serve_with_registrar_and_sync() -> (ServerLoop, Arc<FakeRegistrar>) {
pub fn serve_with_registrar_and_sync() -> (Server, Arc<FakeRegistrar>) {
init_server(|builder| {
builder.sync_status(Arc::new(|| true))
}, Default::default(), Remote::new_sync())
}
pub fn serve_with_registrar_and_fetch() -> (ServerLoop, FakeFetch, Arc<FakeRegistrar>) {
pub fn serve_with_registrar_and_fetch() -> (Server, FakeFetch, Arc<FakeRegistrar>) {
serve_with_registrar_and_fetch_and_threads(false)
}
pub fn serve_with_registrar_and_fetch_and_threads(multi_threaded: bool) -> (ServerLoop, FakeFetch, Arc<FakeRegistrar>) {
pub fn serve_with_registrar_and_fetch_and_threads(multi_threaded: bool) -> (Server, FakeFetch, Arc<FakeRegistrar>) {
let fetch = FakeFetch::default();
let f = fetch.clone();
let (server, reg) = init_server(move |builder| {
@ -116,7 +100,7 @@ pub fn serve_with_registrar_and_fetch_and_threads(multi_threaded: bool) -> (Serv
(server, fetch, reg)
}
pub fn serve_with_fetch(web_token: &'static str) -> (ServerLoop, FakeFetch) {
pub fn serve_with_fetch(web_token: &'static str) -> (Server, FakeFetch) {
let fetch = FakeFetch::default();
let f = fetch.clone();
let (server, _) = init_server(move |builder| {
@ -128,11 +112,11 @@ pub fn serve_with_fetch(web_token: &'static str) -> (ServerLoop, FakeFetch) {
(server, fetch)
}
pub fn serve() -> ServerLoop {
pub fn serve() -> Server {
init_server(|builder| builder, Default::default(), Remote::new_sync()).0
}
pub fn request(server: ServerLoop, request: &str) -> http_client::Response {
pub fn request(server: Server, request: &str) -> http_client::Response {
http_client::request(server.addr(), request)
}
@ -240,6 +224,7 @@ impl<T: Fetch> ServerBuilder<T> {
}
}
const DAPPS_DOMAIN: &'static str = "web3.site";
/// Webapps HTTP server.
pub struct Server {
@ -260,19 +245,27 @@ impl Server {
remote: Remote,
fetch: F,
) -> Result<Server, http::Error> {
let middleware = Middleware::new(
let middleware = Middleware::dapps(
remote,
signer_address,
dapps_path,
extra_dapps,
DAPPS_DOMAIN.into(),
registrar,
sync_status,
web_proxy_tokens,
fetch,
);
let mut allowed_hosts: Option<Vec<Host>> = allowed_hosts.into();
allowed_hosts.as_mut().map(|mut hosts| {
hosts.push(format!("http://*.{}:*", DAPPS_DOMAIN).into());
hosts.push(format!("http://*.{}", DAPPS_DOMAIN).into());
});
http::ServerBuilder::new(io)
.request_middleware(middleware)
.allowed_hosts(allowed_hosts)
.allowed_hosts(allowed_hosts.into())
.cors(http::DomainsValidation::Disabled)
.start_http(addr)
.map(|server| Server {

View File

@ -37,15 +37,15 @@ fn should_redirect_to_home() {
}
#[test]
fn should_redirect_to_home_when_trailing_slash_is_missing() {
fn should_redirect_to_home_with_domain() {
// given
let server = serve();
// when
let response = request(server,
"\
GET /app HTTP/1.1\r\n\
Host: 127.0.0.1:8080\r\n\
GET / HTTP/1.1\r\n\
Host: home.web3.site\r\n\
Connection: close\r\n\
\r\n\
"
@ -57,14 +57,14 @@ fn should_redirect_to_home_when_trailing_slash_is_missing() {
}
#[test]
fn should_redirect_to_home_for_users_with_cached_redirection() {
fn should_redirect_to_home_when_trailing_slash_is_missing() {
// given
let server = serve();
// when
let response = request(server,
"\
GET /home/ HTTP/1.1\r\n\
GET /app HTTP/1.1\r\n\
Host: 127.0.0.1:8080\r\n\
Connection: close\r\n\
\r\n\
@ -179,7 +179,7 @@ fn should_serve_proxy_pac() {
// then
response.assert_status("HTTP/1.1 200 OK");
assert_eq!(response.body, "DD\n\nfunction FindProxyForURL(url, host) {\n\tif (shExpMatch(host, \"parity.web3.site\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:18180\";\n\t}\n\n\tif (shExpMatch(host, \"*.web3.site\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:8080\";\n\t}\n\n\treturn \"DIRECT\";\n}\n\n0\n\n".to_owned());
assert_eq!(response.body, "DB\n\nfunction FindProxyForURL(url, host) {\n\tif (shExpMatch(host, \"home.web3.site\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:18180\";\n\t}\n\n\tif (shExpMatch(host, \"*.web3.site\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:8080\";\n\t}\n\n\treturn \"DIRECT\";\n}\n\n0\n\n".to_owned());
assert_security_headers(&response.headers);
}

3
docker/README.md Normal file
View File

@ -0,0 +1,3 @@
Usage
```docker build -f docker/ubuntu/Dockerfile --tag ethcore/parity:branch_or_tag_name .```

View File

@ -1,29 +1,32 @@
FROM centos:latest
WORKDIR /build
# install tools and dependencies
RUN yum -y update&& \
yum install -y git make gcc-c++ gcc file binutils
# install rustup
RUN curl -sSf https://static.rust-lang.org/rustup.sh -o rustup.sh &&\
ls&&\
sh rustup.sh --disable-sudo
# show backtraces
ENV RUST_BACKTRACE 1
# set compiler
ENV CXX g++
ENV CC gcc
# show tools
RUN rustc -vV && \
cargo -V && \
gcc -v &&\
g++ -v
# build parity
RUN git clone https://github.com/paritytech/parity && \
cd parity&&\
git checkout beta && \
git pull && \
ls -a&&\
cargo build --release --verbose && \
ADD . /build/parity
RUN cd parity&&\
cargo build --release --verbose && \
ls /build/parity/target/release/parity && \
strip /build/parity/target/release/parity

View File

@ -1,83 +0,0 @@
FROM ubuntu:14.04
MAINTAINER Parity Technologies <devops@parity.io>
WORKDIR /build
#ENV for build TAG
ARG BUILD_TAG
ENV BUILD_TAG ${BUILD_TAG:-master}
RUN echo $BUILD_TAG
# install tools and dependencies
RUN apt-get update && \
apt-get install -y --force-yes --no-install-recommends \
# make
build-essential \
# add-apt-repository
software-properties-common \
make \
curl \
wget \
git \
g++ \
gcc \
libc6 \
libc6-dev \
binutils \
file \
openssl \
libssl-dev \
libudev-dev \
pkg-config \
dpkg-dev \
# evmjit dependencies
zlib1g-dev \
libedit-dev \
libudev-dev &&\
# cmake and llvm ppa's. then update ppa's
add-apt-repository -y "ppa:george-edison55/cmake-3.x" && \
add-apt-repository "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.7 main" && \
apt-get update && \
apt-get install -y --force-yes cmake llvm-3.7-dev && \
# install evmjit
git clone https://github.com/debris/evmjit && \
cd evmjit && \
mkdir build && cd build && \
cmake .. && make && make install && cd && \
# install rustup
curl https://sh.rustup.rs -sSf | sh -s -- -y && \
# rustup directory
PATH=/root/.cargo/bin:$PATH && \
# show backtraces
RUST_BACKTRACE=1 && \
# build parity
cd /build&&git clone https://github.com/paritytech/parity && \
cd parity && \
git pull&& \
git checkout $BUILD_TAG && \
cargo build --verbose --release --features final && \
#ls /build/parity/target/release/parity && \
strip /build/parity/target/release/parity && \
file /build/parity/target/release/parity&&mkdir -p /parity&& cp /build/parity/target/release/parity /parity&&\
#cleanup Docker image
rm -rf /root/.cargo&&rm -rf /root/.multirust&&rm -rf /root/.rustup&&rm -rf /build&&\
apt-get purge -y \
# make
build-essential \
# add-apt-repository
software-properties-common \
make \
curl \
wget \
git \
g++ \
gcc \
binutils \
file \
pkg-config \
dpkg-dev \
# evmjit dependencies
zlib1g-dev \
libedit-dev \
cmake llvm-3.7-dev&&\
rm -rf /var/lib/apt/lists/*
# setup ENTRYPOINT
EXPOSE 8080 8545 8180
ENTRYPOINT ["/parity/parity"]

View File

@ -1,3 +0,0 @@
Usage
```docker build --build-arg BUILD_TAG=branch_or_tag_name --no-cache=true --tag ethcore/parity:branch_or_tag_name .```

View File

@ -1,5 +1,6 @@
FROM ubuntu:14.04
WORKDIR /build
# install tools and dependencies
RUN apt-get -y update && \
apt-get install -y --force-yes --no-install-recommends \
@ -24,14 +25,11 @@ RUN rustup target add aarch64-unknown-linux-gnu
ENV RUST_BACKTRACE 1
# show tools
RUN rustc -vV && \
cargo -V
RUN rustc -vV && cargo -V
# build parity
RUN git clone https://github.com/paritytech/parity && \
cd parity && \
git checkout beta && \
git pull && \
ADD . /build/parity
RUN cd parity && \
mkdir -p .cargo && \
echo '[target.aarch64-unknown-linux-gnu]\n\
linker = "aarch64-linux-gnu-gcc"\n'\

View File

@ -1,5 +1,6 @@
FROM ubuntu:14.04
WORKDIR /build
# install tools and dependencies
RUN apt-get -y update && \
apt-get install -y --force-yes --no-install-recommends \
@ -23,16 +24,12 @@ RUN rustup target add armv7-unknown-linux-gnueabihf
# show backtraces
ENV RUST_BACKTRACE 1
# show tools
RUN rustc -vV && \
cargo -V
RUN rustc -vV && cargo -V
# build parity
RUN git clone https://github.com/paritytech/parity && \
cd parity && \
git checkout beta && \
git pull && \
ADD . /build/parity
RUN cd parity && \
mkdir -p .cargo && \
echo '[target.armv7-unknown-linux-gnueabihf]\n\
linker = "arm-linux-gnueabihf-gcc"\n'\

View File

@ -1,37 +0,0 @@
FROM ubuntu:14.04
# install tools and dependencies
RUN apt-get update && \
apt-get install -y \
# make
build-essential \
# add-apt-repository
software-properties-common \
curl \
g++ \
wget \
git \
# evmjit dependencies
zlib1g-dev \
libedit-dev
# cmake, llvm and rocksdb ppas. then update ppas
RUN add-apt-repository -y "ppa:george-edison55/cmake-3.x" && \
add-apt-repository "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.7 main" && \
apt-get update && \
apt-get install -y --force-yes cmake llvm-3.7-dev
# install evmjit
RUN git clone https://github.com/debris/evmjit && \
cd evmjit && \
mkdir build && cd build && \
cmake .. && make && make install && cd
# install rustup
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
# rustup directory
ENV PATH /root/.cargo/bin:$PATH
# show backtraces
ENV RUST_BACKTRACE 1

View File

@ -1,5 +1,6 @@
FROM ubuntu:14.04
WORKDIR /build
# install tools and dependencies
RUN apt-get update && \
apt-get install -y \
@ -45,10 +46,8 @@ gcc -v &&\
g++ -v
# build parity
RUN git clone https://github.com/paritytech/parity && \
cd parity && \
git checkout beta && \
git pull && \
ADD . /build/parity
RUN cd parity && \
cargo build --release --features ethcore/jit --verbose && \
ls /build/parity/target/release/parity && \
strip /build/parity/target/release/parity

View File

@ -1,40 +0,0 @@
FROM ubuntu:14.04
WORKDIR /build
# install tools and dependencies
RUN apt-get update && \
apt-get install -y \
build-essential \
g++ \
curl \
git \
file \
binutils
# install rustup
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
# rustup directory
ENV PATH /root/.cargo/bin:$PATH
# show backtraces
ENV RUST_BACKTRACE 1
# show tools
RUN rustc -vV && \
cargo -V && \
gcc -v &&\
g++ -v
# build parity
RUN git clone https://github.com/paritytech/parity && \
cd parity && \
git checkout stable && \
git pull && \
cargo build --release --verbose && \
ls /build/parity/target/release/parity && \
strip /build/parity/target/release/parity
RUN file /build/parity/target/release/parity
EXPOSE 8080 8545 8180
ENTRYPOINT ["/build/parity/target/release/parity"]

View File

@ -1,5 +1,6 @@
FROM ubuntu:14.04
WORKDIR /build
# install tools and dependencies
RUN apt-get update && \
apt-get install -y \
@ -29,10 +30,8 @@ gcc -v &&\
g++ -v
# build parity
RUN git clone https://github.com/paritytech/parity && \
cd parity && \
git checkout beta && \
git pull && \
ADD . /build/parity
RUN cd parity && \
cargo build --release --verbose && \
ls /build/parity/target/release/parity && \
strip /build/parity/target/release/parity

View File

@ -20,10 +20,13 @@ ethcore-ipc = { path = "../../ipc/rpc", optional = true }
ethcore-devtools = { path = "../../devtools" }
rlp = { path = "../../util/rlp" }
time = "0.1"
smallvec = "0.3.1"
smallvec = "0.4"
futures = "0.1"
rand = "0.3"
itertools = "0.5"
bincode = "0.8.0"
serde = "1.0"
serde_derive = "1.0"
stats = { path = "../../util/stats" }
[features]

View File

@ -16,7 +16,7 @@
//! Light client implementation. Stores data from light sync
use std::sync::Arc;
use std::sync::{Weak, Arc};
use ethcore::block_import_error::BlockImportError;
use ethcore::block_status::BlockStatus;
@ -111,6 +111,12 @@ pub trait LightChainClient: Send + Sync {
fn eip86_transition(&self) -> u64;
}
/// An actor listening to light chain events.
pub trait LightChainNotify: Send + Sync {
/// Notifies about imported headers.
fn new_headers(&self, good: &[H256]);
}
/// Something which can be treated as a `LightChainClient`.
pub trait AsLightClient {
/// The kind of light client this can be treated as.
@ -134,6 +140,7 @@ pub struct Client {
report: RwLock<ClientReport>,
import_lock: Mutex<()>,
db: Arc<KeyValueDB>,
listeners: RwLock<Vec<Weak<LightChainNotify>>>,
}
impl Client {
@ -148,9 +155,15 @@ impl Client {
report: RwLock::new(ClientReport::default()),
import_lock: Mutex::new(()),
db: db,
listeners: RwLock::new(vec![]),
})
}
/// Adds a new `LightChainNotify` listener.
pub fn add_listener(&self, listener: Weak<LightChainNotify>) {
self.listeners.write().push(listener);
}
/// Create a new `Client` backed purely in-memory.
/// This will ignore all database options in the configuration.
pub fn in_memory(config: Config, spec: &Spec, io_channel: IoChannel<ClientIoMessage>, cache: Arc<Mutex<Cache>>) -> Self {
@ -272,6 +285,8 @@ impl Client {
self.queue.mark_as_bad(&bad);
self.queue.mark_as_good(&good);
self.notify(|listener| listener.new_headers(&good));
}
/// Get a report about blocks imported.
@ -327,6 +342,14 @@ impl Client {
Arc::new(v)
}
fn notify<F: Fn(&LightChainNotify)>(&self, f: F) {
for listener in &*self.listeners.read() {
if let Some(listener) = listener.upgrade() {
f(&*listener)
}
}
}
}
impl LightChainClient for Client {

View File

@ -60,20 +60,25 @@ pub use self::provider::Provider;
pub use self::transaction_queue::TransactionQueue;
pub use types::request as request;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate log;
extern crate ethcore;
extern crate ethcore_util as util;
extern crate ethcore_network as network;
extern crate bincode;
extern crate ethcore_io as io;
extern crate rlp;
extern crate smallvec;
extern crate time;
extern crate ethcore_network as network;
extern crate ethcore_util as util;
extern crate ethcore;
extern crate futures;
extern crate rand;
extern crate itertools;
extern crate rand;
extern crate rlp;
extern crate serde;
extern crate smallvec;
extern crate stats;
extern crate time;
#[cfg(feature = "ipc")]
extern crate ethcore_ipc as ipc;

View File

@ -0,0 +1,279 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Request load timer and distribution manager.
//!
//! This uses empirical samples of the length of time taken to respond
//! to requests in order to inform request credit costs.
//!
//! The average request time is determined by an exponential moving average
//! of the mean request times during the last `MOVING_SAMPLE_SIZE` time periods of
//! length `TIME_PERIOD_MS`, with the exception that time periods where no data is
//! gathered are excluded.
use std::collections::{HashMap, VecDeque};
use std::fs::File;
use std::path::PathBuf;
use request::{CompleteRequest, Kind};
use bincode;
use time;
use util::{RwLock, Mutex};
/// Number of time periods samples should be kept for.
pub const MOVING_SAMPLE_SIZE: usize = 256;
/// Stores rolling load timer samples.
// TODO: switch to bigint if possible (FP casts aren't available)
pub trait SampleStore: Send + Sync {
/// Load samples.
fn load(&self) -> HashMap<Kind, VecDeque<u64>>;
/// Store all samples.
fn store(&self, samples: &HashMap<Kind, VecDeque<u64>>);
}
// get a hardcoded, arbitrarily determined (but intended overestimate)
// of the time in nanoseconds to serve a request of the given kind.
//
// TODO: seed this with empirical data.
fn hardcoded_serve_time(kind: Kind) -> u64 {
match kind {
Kind::Headers => 500_000,
Kind::HeaderProof => 500_000,
Kind::Receipts => 1_000_000,
Kind::Body => 1_000_000,
Kind::Account => 1_500_000,
Kind::Storage => 2_000_000,
Kind::Code => 1_500_000,
Kind::Execution => 250, // per gas.
}
}
/// A no-op store.
pub struct NullStore;
impl SampleStore for NullStore {
fn load(&self) -> HashMap<Kind, VecDeque<u64>> { HashMap::new() }
fn store(&self, _samples: &HashMap<Kind, VecDeque<u64>>) { }
}
/// Request load distributions.
pub struct LoadDistribution {
active_period: RwLock<HashMap<Kind, Mutex<(u64, u64)>>>,
samples: RwLock<HashMap<Kind, VecDeque<u64>>>,
}
impl LoadDistribution {
/// Load rolling samples from the given store.
pub fn load(store: &SampleStore) -> Self {
let mut samples = store.load();
for kind_samples in samples.values_mut() {
while kind_samples.len() > MOVING_SAMPLE_SIZE {
kind_samples.pop_front();
}
}
LoadDistribution {
active_period: RwLock::new(HashMap::new()),
samples: RwLock::new(samples),
}
}
/// Begin a timer.
pub fn begin_timer<'a>(&'a self, req: &CompleteRequest) -> LoadTimer<'a> {
let kind = req.kind();
let n = match *req {
CompleteRequest::Headers(ref req) => req.max,
CompleteRequest::Execution(ref req) => req.gas.low_u64(),
_ => 1,
};
LoadTimer {
start: time::precise_time_ns(),
n: n,
dist: self,
kind: kind,
}
}
/// Calculate EMA of load in nanoseconds for a specific request kind.
/// If there is no data for the given request kind, no EMA will be calculated,
/// but a hardcoded time will be returned.
pub fn expected_time_ns(&self, kind: Kind) -> u64 {
let samples = self.samples.read();
samples.get(&kind).and_then(|s| {
if s.len() == 0 { return None }
let alpha: f64 = 1f64 / s.len() as f64;
let start = s.front().expect("length known to be non-zero; qed").clone();
let ema = s.iter().skip(1).fold(start as f64, |a, &c| {
(alpha * c as f64) + ((1.0 - alpha) * a)
});
Some(ema as u64)
}).unwrap_or_else(move || hardcoded_serve_time(kind))
}
/// End the current time period. Provide a store to
pub fn end_period(&self, store: &SampleStore) {
let active_period = self.active_period.read();
let mut samples = self.samples.write();
for (&kind, set) in active_period.iter() {
let (elapsed, n) = ::std::mem::replace(&mut *set.lock(), (0, 0));
if n == 0 { continue }
let kind_samples = samples.entry(kind)
.or_insert_with(|| VecDeque::with_capacity(MOVING_SAMPLE_SIZE));
if kind_samples.len() == MOVING_SAMPLE_SIZE { kind_samples.pop_front(); }
kind_samples.push_back(elapsed / n);
}
store.store(&*samples);
}
fn update(&self, kind: Kind, elapsed: u64, n: u64) {
macro_rules! update_counters {
($counters: expr) => {
$counters.0 = $counters.0.saturating_add(elapsed);
$counters.1 = $counters.1.saturating_add(n);
}
};
{
let set = self.active_period.read();
if let Some(counters) = set.get(&kind) {
let mut counters = counters.lock();
update_counters!(counters);
return;
}
}
let mut set = self.active_period.write();
let counters = set
.entry(kind)
.or_insert_with(|| Mutex::new((0, 0)));
update_counters!(counters.get_mut());
}
}
/// A timer for a single request.
/// On drop, this will update the distribution.
pub struct LoadTimer<'a> {
start: u64,
n: u64,
dist: &'a LoadDistribution,
kind: Kind,
}
impl<'a> Drop for LoadTimer<'a> {
fn drop(&mut self) {
let elapsed = time::precise_time_ns() - self.start;
self.dist.update(self.kind, elapsed, self.n);
}
}
/// A store which writes directly to a file.
pub struct FileStore(pub PathBuf);
impl SampleStore for FileStore {
fn load(&self) -> HashMap<Kind, VecDeque<u64>> {
File::open(&self.0)
.map_err(|e| Box::new(bincode::ErrorKind::IoError(e)))
.and_then(|mut file| bincode::deserialize_from(&mut file, bincode::Infinite))
.unwrap_or_else(|_| HashMap::new())
}
fn store(&self, samples: &HashMap<Kind, VecDeque<u64>>) {
let res = File::create(&self.0)
.map_err(|e| Box::new(bincode::ErrorKind::IoError(e)))
.and_then(|mut file| bincode::serialize_into(&mut file, samples, bincode::Infinite));
if let Err(e) = res {
warn!(target: "pip", "Error writing light request timing samples to file: {}", e);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use request::Kind;
#[test]
fn hardcoded_before_data() {
let dist = LoadDistribution::load(&NullStore);
assert_eq!(dist.expected_time_ns(Kind::Headers), hardcoded_serve_time(Kind::Headers));
dist.update(Kind::Headers, 100_000, 100);
dist.end_period(&NullStore);
assert_eq!(dist.expected_time_ns(Kind::Headers), 1000);
}
#[test]
fn moving_average() {
let dist = LoadDistribution::load(&NullStore);
let mut sum = 0;
for (i, x) in (0..10).map(|x| x * 10_000).enumerate() {
dist.update(Kind::Headers, x, 1);
dist.end_period(&NullStore);
sum += x;
if i == 0 { continue }
let moving_average = dist.expected_time_ns(Kind::Headers);
// should be weighted below the maximum entry.
let arith_average = (sum as f64 / (i + 1) as f64) as u64;
assert!(moving_average < x);
// when there are only 2 entries, they should be equal due to choice of
// ALPHA = 1/N.
// otherwise, the weight should be below the arithmetic mean because the much
// smaller previous values are discounted less.
if i == 1 {
assert_eq!(moving_average, arith_average);
} else {
assert!(moving_average < arith_average)
}
}
}
#[test]
fn file_store() {
let path = ::devtools::RandomTempPath::new();
let store = FileStore(path.as_path().clone());
let mut samples = store.load();
assert!(samples.is_empty());
samples.insert(Kind::Headers, vec![5, 2, 7, 2, 2, 4].into());
samples.insert(Kind::Execution, vec![1, 1, 100, 250].into());
store.store(&samples);
let dup = store.load();
assert_eq!(samples, dup);
}
}

View File

@ -38,11 +38,13 @@ use request::{Request, NetworkRequests as Requests, Response};
use self::request_credits::{Credits, FlowParams};
use self::context::{Ctx, TickCtx};
use self::error::Punishment;
use self::load_timer::{LoadDistribution, NullStore};
use self::request_set::RequestSet;
use self::id_guard::IdGuard;
mod context;
mod error;
mod load_timer;
mod status;
mod request_set;
@ -51,8 +53,9 @@ mod tests;
pub mod request_credits;
pub use self::error::Error;
pub use self::context::{BasicContext, EventContext, IoContext};
pub use self::error::Error;
pub use self::load_timer::{SampleStore, FileStore};
pub use self::status::{Status, Capabilities, Announcement};
const TIMEOUT: TimerToken = 0;
@ -64,6 +67,9 @@ const TICK_TIMEOUT_INTERVAL_MS: u64 = 5000;
const PROPAGATE_TIMEOUT: TimerToken = 2;
const PROPAGATE_TIMEOUT_INTERVAL_MS: u64 = 5000;
const RECALCULATE_COSTS_TIMEOUT: TimerToken = 3;
const RECALCULATE_COSTS_INTERVAL_MS: u64 = 60 * 60 * 1000;
// minimum interval between updates.
const UPDATE_INTERVAL_MS: i64 = 5000;
@ -88,13 +94,18 @@ mod packet {
pub const REQUEST: u8 = 0x02;
pub const RESPONSE: u8 = 0x03;
// request credits update and acknowledgement.
pub const UPDATE_CREDITS: u8 = 0x04;
pub const ACKNOWLEDGE_UPDATE: u8 = 0x05;
// relay transactions to peers.
pub const SEND_TRANSACTIONS: u8 = 0x04;
pub const SEND_TRANSACTIONS: u8 = 0x06;
}
// timeouts for different kinds of requests. all values are in milliseconds.
mod timeout {
pub const HANDSHAKE: i64 = 2500;
pub const ACKNOWLEDGE_UPDATE: i64 = 5000;
pub const BASE: i64 = 1500; // base timeout for packet.
// timeouts per request within packet.
@ -141,6 +152,9 @@ pub struct Peer {
pending_requests: RequestSet,
failed_requests: Vec<ReqId>,
propagated_transactions: HashSet<H256>,
skip_update: bool,
local_flow: Arc<FlowParams>,
awaiting_acknowledge: Option<(SteadyTime, Arc<FlowParams>)>,
}
/// A light protocol event handler.
@ -176,14 +190,36 @@ pub trait Handler: Send + Sync {
fn on_abort(&self) { }
}
/// Protocol parameters.
/// Configuration.
pub struct Config {
/// How many stored seconds of credits peers should be able to accumulate.
pub max_stored_seconds: u64,
/// How much of the total load capacity each peer should be allowed to take.
pub load_share: f64,
}
impl Default for Config {
fn default() -> Self {
const LOAD_SHARE: f64 = 1.0 / 25.0;
const MAX_ACCUMULATED: u64 = 60 * 5; // only charge for 5 minutes.
Config {
max_stored_seconds: MAX_ACCUMULATED,
load_share: LOAD_SHARE,
}
}
}
/// Protocol initialization parameters.
pub struct Params {
/// Network id.
pub network_id: u64,
/// Request credits parameters.
pub flow_params: FlowParams,
/// Config.
pub config: Config,
/// Initial capabilities.
pub capabilities: Capabilities,
/// The sample store (`None` if data shouldn't persist between runs).
pub sample_store: Option<Box<SampleStore>>,
}
/// Type alias for convenience.
@ -249,14 +285,17 @@ mod id_guard {
// on the peers, only one peer may be held at a time.
pub struct LightProtocol {
provider: Arc<Provider>,
config: Config,
genesis_hash: H256,
network_id: u64,
pending_peers: RwLock<HashMap<PeerId, PendingPeer>>,
peers: RwLock<PeerMap>,
capabilities: RwLock<Capabilities>,
flow_params: FlowParams, // assumed static and same for every peer.
flow_params: RwLock<Arc<FlowParams>>,
handlers: Vec<Arc<Handler>>,
req_id: AtomicUsize,
sample_store: Box<SampleStore>,
load_distribution: LoadDistribution,
}
impl LightProtocol {
@ -265,16 +304,27 @@ impl LightProtocol {
debug!(target: "pip", "Initializing light protocol handler");
let genesis_hash = provider.chain_info().genesis_hash;
let sample_store = params.sample_store.unwrap_or_else(|| Box::new(NullStore));
let load_distribution = LoadDistribution::load(&*sample_store);
let flow_params = FlowParams::from_request_times(
|kind| load_distribution.expected_time_ns(kind),
params.config.load_share,
params.config.max_stored_seconds,
);
LightProtocol {
provider: provider,
config: params.config,
genesis_hash: genesis_hash,
network_id: params.network_id,
pending_peers: RwLock::new(HashMap::new()),
peers: RwLock::new(HashMap::new()),
capabilities: RwLock::new(params.capabilities),
flow_params: params.flow_params,
flow_params: RwLock::new(Arc::new(flow_params)),
handlers: Vec::new(),
req_id: AtomicUsize::new(0),
sample_store: sample_store,
load_distribution: load_distribution,
}
}
@ -422,8 +472,9 @@ impl LightProtocol {
let res = match peers.get(peer) {
Some(peer_info) => {
let mut peer_info = peer_info.lock();
let peer_info: &mut Peer = &mut *peer_info;
let req_info = peer_info.pending_requests.remove(&req_id, SteadyTime::now());
let cumulative_cost = peer_info.pending_requests.cumulative_cost();
let last_batched = peer_info.pending_requests.is_empty();
let flow_info = peer_info.remote_flow.as_mut();
match (req_info, flow_info) {
@ -431,11 +482,14 @@ impl LightProtocol {
let &mut (ref mut c, ref mut flow) = flow_info;
// only update if the cumulative cost of the request set is zero.
if cumulative_cost == 0.into() {
// and this response wasn't from before request costs were updated.
if !peer_info.skip_update && last_batched {
let actual_credits = ::std::cmp::min(cur_credits, *flow.limit());
c.update_to(actual_credits);
}
if last_batched { peer_info.skip_update = false }
Ok(())
}
(None, _) => Err(Error::UnsolicitedResponse),
@ -464,6 +518,9 @@ impl LightProtocol {
packet::REQUEST => self.request(peer, io, rlp),
packet::RESPONSE => self.response(peer, io, rlp),
packet::UPDATE_CREDITS => self.update_credits(peer, io, rlp),
packet::ACKNOWLEDGE_UPDATE => self.acknowledge_update(peer, io, rlp),
packet::SEND_TRANSACTIONS => self.relay_transactions(peer, io, rlp),
other => {
@ -497,13 +554,22 @@ impl LightProtocol {
}
}
// request timeouts
// request and update ack timeouts
let ack_duration = Duration::milliseconds(timeout::ACKNOWLEDGE_UPDATE);
{
for (peer_id, peer) in self.peers.read().iter() {
if peer.lock().pending_requests.check_timeout(now) {
let peer = peer.lock();
if peer.pending_requests.check_timeout(now) {
debug!(target: "pip", "Peer {} request timeout", peer_id);
io.disconnect_peer(*peer_id);
}
if let Some((ref start, _)) = peer.awaiting_acknowledge {
if *start + ack_duration <= now {
debug!(target: "pip", "Peer {} update acknowledgement timeout", peer_id);
io.disconnect_peer(*peer_id);
}
}
}
}
}
@ -574,7 +640,8 @@ impl LightProtocol {
};
let capabilities = self.capabilities.read().clone();
let status_packet = status::write_handshake(&status, &capabilities, Some(&self.flow_params));
let local_flow = self.flow_params.read();
let status_packet = status::write_handshake(&status, &capabilities, Some(&**local_flow));
self.pending_peers.write().insert(*peer, PendingPeer {
sent_head: chain_info.best_block_hash,
@ -628,6 +695,35 @@ impl LightProtocol {
})
}
}
fn begin_new_cost_period(&self, io: &IoContext) {
self.load_distribution.end_period(&*self.sample_store);
let new_params = Arc::new(FlowParams::from_request_times(
|kind| self.load_distribution.expected_time_ns(kind),
self.config.load_share,
self.config.max_stored_seconds,
));
*self.flow_params.write() = new_params.clone();
let peers = self.peers.read();
let now = SteadyTime::now();
let packet_body = {
let mut stream = RlpStream::new_list(3);
stream.append(new_params.limit())
.append(new_params.recharge_rate())
.append(new_params.cost_table());
stream.out()
};
for (peer_id, peer_info) in peers.iter() {
let mut peer_info = peer_info.lock();
io.send(*peer_id, packet::UPDATE_CREDITS, packet_body.clone());
peer_info.awaiting_acknowledge = Some((now.clone(), new_params.clone()));
}
}
}
impl LightProtocol {
@ -653,9 +749,10 @@ impl LightProtocol {
}
let remote_flow = flow_params.map(|params| (params.create_credits(), params));
let local_flow = self.flow_params.read().clone();
self.peers.write().insert(*peer, Mutex::new(Peer {
local_credits: self.flow_params.create_credits(),
local_credits: local_flow.create_credits(),
status: status.clone(),
capabilities: capabilities.clone(),
remote_flow: remote_flow,
@ -664,6 +761,9 @@ impl LightProtocol {
pending_requests: RequestSet::default(),
failed_requests: Vec::new(),
propagated_transactions: HashSet::new(),
skip_update: false,
local_flow: local_flow,
awaiting_acknowledge: None,
}));
for handler in &self.handlers {
@ -739,6 +839,7 @@ impl LightProtocol {
}
};
let mut peer = peer.lock();
let peer: &mut Peer = &mut *peer;
let req_id: u64 = raw.val_at(0)?;
let mut request_builder = RequestBuilder::default();
@ -746,12 +847,13 @@ impl LightProtocol {
trace!(target: "pip", "Received requests (id: {}) from peer {}", req_id, peer_id);
// deserialize requests, check costs and request validity.
self.flow_params.recharge(&mut peer.local_credits);
peer.local_flow.recharge(&mut peer.local_credits);
peer.local_credits.deduct_cost(self.flow_params.base_cost())?;
peer.local_credits.deduct_cost(peer.local_flow.base_cost())?;
for request_rlp in raw.at(1)?.iter().take(MAX_REQUESTS) {
let request: Request = request_rlp.as_val()?;
peer.local_credits.deduct_cost(self.flow_params.compute_cost(&request))?;
let cost = peer.local_flow.compute_cost(&request);
peer.local_credits.deduct_cost(cost)?;
request_builder.push(request).map_err(|_| Error::BadBackReference)?;
}
@ -761,6 +863,7 @@ impl LightProtocol {
// respond to all requests until one fails.
let responses = requests.respond_to_all(|complete_req| {
let _timer = self.load_distribution.begin_timer(&complete_req);
match complete_req {
CompleteRequest::Headers(req) => self.provider.block_headers(req).map(Response::Headers),
CompleteRequest::HeaderProof(req) => self.provider.header_proof(req).map(Response::HeaderProof),
@ -804,6 +907,60 @@ impl LightProtocol {
Ok(())
}
// handle an update of request credits parameters.
fn update_credits(&self, peer_id: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> {
let peers = self.peers.read();
let peer = peers.get(peer_id).ok_or(Error::UnknownPeer)?;
let mut peer = peer.lock();
trace!(target: "pip", "Received an update to request credit params from peer {}", peer_id);
{
let &mut (ref mut credits, ref mut old_params) = peer.remote_flow.as_mut().ok_or(Error::NotServer)?;
old_params.recharge(credits);
let new_params = FlowParams::new(
raw.val_at(0)?, // limit
raw.val_at(2)?, // cost table
raw.val_at(1)?, // recharge.
);
// preserve ratio of current : limit when updating params.
credits.maintain_ratio(*old_params.limit(), *new_params.limit());
*old_params = new_params;
}
// set flag to true when there is an in-flight request
// corresponding to old flow params.
if !peer.pending_requests.is_empty() {
peer.skip_update = true;
}
// let peer know we've acknowledged the update.
io.respond(packet::ACKNOWLEDGE_UPDATE, Vec::new());
Ok(())
}
// handle an acknowledgement of request credits update.
fn acknowledge_update(&self, peer_id: &PeerId, _io: &IoContext, _raw: UntrustedRlp) -> Result<(), Error> {
let peers = self.peers.read();
let peer = peers.get(peer_id).ok_or(Error::UnknownPeer)?;
let mut peer = peer.lock();
trace!(target: "pip", "Received an acknowledgement for new request credit params from peer {}", peer_id);
let (_, new_params) = match peer.awaiting_acknowledge.take() {
Some(x) => x,
None => return Err(Error::UnsolicitedResponse),
};
let old_limit = *peer.local_flow.limit();
peer.local_credits.maintain_ratio(old_limit, *new_params.limit());
peer.local_flow = new_params;
Ok(())
}
// Receive a set of transactions to relay.
fn relay_transactions(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> {
const MAX_TRANSACTIONS: usize = 256;
@ -850,6 +1007,8 @@ impl NetworkProtocolHandler for LightProtocol {
.expect("Error registering sync timer.");
io.register_timer(PROPAGATE_TIMEOUT, PROPAGATE_TIMEOUT_INTERVAL_MS)
.expect("Error registering sync timer.");
io.register_timer(RECALCULATE_COSTS_TIMEOUT, RECALCULATE_COSTS_INTERVAL_MS)
.expect("Error registering request timer interval token.");
}
fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) {
@ -869,6 +1028,7 @@ impl NetworkProtocolHandler for LightProtocol {
TIMEOUT => self.timeout_check(io),
TICK_TIMEOUT => self.tick_handlers(io),
PROPAGATE_TIMEOUT => self.propagate_transactions(io),
RECALCULATE_COSTS_TIMEOUT => self.begin_new_cost_period(io),
_ => warn!(target: "pip", "received timeout on unknown token {}", timer),
}
}

View File

@ -56,6 +56,11 @@ impl Credits {
self.recharge_point = SteadyTime::now();
}
/// Maintain ratio to current limit against an old limit.
pub fn maintain_ratio(&mut self, old_limit: U256, new_limit: U256) {
self.estimate = (new_limit * self.estimate) / old_limit;
}
/// Attempt to apply the given cost to the amount of credits.
///
/// If successful, the cost will be deducted successfully.
@ -188,6 +193,53 @@ impl FlowParams {
}
}
/// Create new flow parameters from ,
/// proportion of total capacity which should be given to a peer,
/// and number of seconds of stored capacity a peer can accumulate.
pub fn from_request_times<F: Fn(::request::Kind) -> u64>(
request_time_ns: F,
load_share: f64,
max_stored_seconds: u64
) -> Self {
use request::Kind;
let load_share = load_share.abs();
let recharge: u64 = 100_000_000;
let max = recharge.saturating_mul(max_stored_seconds);
let cost_for_kind = |kind| {
// how many requests we can handle per second
let ns = request_time_ns(kind);
let second_duration = 1_000_000_000f64 / ns as f64;
// scale by share of the load given to this peer.
let serve_per_second = second_duration * load_share;
let serve_per_second = serve_per_second.max(1.0 / 10_000.0);
// as a percentage of the recharge per second.
U256::from((recharge as f64 / serve_per_second) as u64)
};
let costs = CostTable {
base: 0.into(),
headers: cost_for_kind(Kind::Headers),
body: cost_for_kind(Kind::Body),
receipts: cost_for_kind(Kind::Receipts),
account: cost_for_kind(Kind::Account),
storage: cost_for_kind(Kind::Storage),
code: cost_for_kind(Kind::Code),
header_proof: cost_for_kind(Kind::HeaderProof),
transaction_proof: cost_for_kind(Kind::Execution),
};
FlowParams {
costs: costs,
limit: max.into(),
recharge: recharge.into(),
}
}
/// Create effectively infinite flow params.
pub fn free() -> Self {
let free_cost: U256 = 0.into();
@ -316,4 +368,28 @@ mod tests {
assert_eq!(credits.estimate, 100.into());
}
#[test]
fn scale_by_load_share_and_time() {
let flow_params = FlowParams::from_request_times(
|_| 10_000,
0.05,
60,
);
let flow_params2 = FlowParams::from_request_times(
|_| 10_000,
0.1,
60,
);
let flow_params3 = FlowParams::from_request_times(
|_| 5_000,
0.05,
60,
);
assert_eq!(flow_params2.costs, flow_params3.costs);
assert_eq!(flow_params.costs.headers, flow_params2.costs.headers * 2.into());
}
}

View File

@ -120,6 +120,8 @@ impl RequestSet {
pub fn is_empty(&self) -> bool { self.len() == 0 }
/// The cumulative cost of all requests in the set.
// this may be useful later for load balancing.
#[allow(dead_code)]
pub fn cumulative_cost(&self) -> U256 { self.cumulative_cost }
}

View File

@ -24,9 +24,8 @@ use ethcore::transaction::{Action, PendingTransaction};
use ethcore::encoded;
use network::{PeerId, NodeId};
use net::request_credits::FlowParams;
use net::context::IoContext;
use net::status::{Capabilities, Status, write_handshake};
use net::status::{Capabilities, Status};
use net::{LightProtocol, Params, packet, Peer};
use provider::Provider;
use request;
@ -162,10 +161,6 @@ impl Provider for TestProvider {
}
}
fn make_flow_params() -> FlowParams {
FlowParams::new(5_000_000.into(), Default::default(), 100_000.into())
}
fn capabilities() -> Capabilities {
Capabilities {
serve_headers: true,
@ -175,16 +170,22 @@ fn capabilities() -> Capabilities {
}
}
fn write_handshake(status: &Status, capabilities: &Capabilities, proto: &LightProtocol) -> Vec<u8> {
let flow_params = proto.flow_params.read().clone();
::net::status::write_handshake(status, capabilities, Some(&*flow_params))
}
// helper for setting up the protocol handler and provider.
fn setup(flow_params: FlowParams, capabilities: Capabilities) -> (Arc<TestProviderInner>, LightProtocol) {
fn setup(capabilities: Capabilities) -> (Arc<TestProviderInner>, LightProtocol) {
let provider = Arc::new(TestProviderInner {
client: TestBlockChainClient::new(),
});
let proto = LightProtocol::new(Arc::new(TestProvider(provider.clone())), Params {
network_id: 2,
flow_params: flow_params,
config: Default::default(),
capabilities: capabilities,
sample_store: None,
});
(provider, proto)
@ -204,14 +205,13 @@ fn status(chain_info: BlockChainInfo) -> Status {
#[test]
fn handshake_expected() {
let flow_params = make_flow_params();
let capabilities = capabilities();
let (provider, proto) = setup(flow_params.clone(), capabilities.clone());
let (provider, proto) = setup(capabilities.clone());
let status = status(provider.client.chain_info());
let packet_body = write_handshake(&status, &capabilities, Some(&flow_params));
let packet_body = write_handshake(&status, &capabilities, &proto);
proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body));
}
@ -219,42 +219,40 @@ fn handshake_expected() {
#[test]
#[should_panic]
fn genesis_mismatch() {
let flow_params = make_flow_params();
let capabilities = capabilities();
let (provider, proto) = setup(flow_params.clone(), capabilities.clone());
let (provider, proto) = setup(capabilities.clone());
let mut status = status(provider.client.chain_info());
status.genesis_hash = H256::default();
let packet_body = write_handshake(&status, &capabilities, Some(&flow_params));
let packet_body = write_handshake(&status, &capabilities, &proto);
proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body));
}
#[test]
fn credit_overflow() {
let flow_params = make_flow_params();
let capabilities = capabilities();
let (provider, proto) = setup(flow_params.clone(), capabilities.clone());
let (provider, proto) = setup(capabilities.clone());
let status = status(provider.client.chain_info());
{
let packet_body = write_handshake(&status, &capabilities, Some(&flow_params));
let packet_body = write_handshake(&status, &capabilities, &proto);
proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body));
}
{
let my_status = write_handshake(&status, &capabilities, Some(&flow_params));
let my_status = write_handshake(&status, &capabilities, &proto);
proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status);
}
// 1000 requests is far too many for the default flow params.
// 1 billion requests is far too many for the default flow params.
let requests = encode_single(Request::Headers(IncompleteHeadersRequest {
start: HashOrNumber::Number(1).into(),
max: 1000,
max: 1_000_000_000,
skip: 0,
reverse: false,
}));
@ -268,20 +266,20 @@ fn credit_overflow() {
#[test]
fn get_block_headers() {
let flow_params = FlowParams::new(5_000_000.into(), Default::default(), 0.into());
let capabilities = capabilities();
let (provider, proto) = setup(flow_params.clone(), capabilities.clone());
let (provider, proto) = setup(capabilities.clone());
let flow_params = proto.flow_params.read().clone();
let cur_status = status(provider.client.chain_info());
let my_status = write_handshake(&cur_status, &capabilities, Some(&flow_params));
let my_status = write_handshake(&cur_status, &capabilities, &proto);
provider.client.add_blocks(100, EachBlockWith::Nothing);
let cur_status = status(provider.client.chain_info());
{
let packet_body = write_handshake(&cur_status, &capabilities, Some(&flow_params));
let packet_body = write_handshake(&cur_status, &capabilities, &proto);
proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body));
proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status);
}
@ -320,20 +318,20 @@ fn get_block_headers() {
#[test]
fn get_block_bodies() {
let flow_params = FlowParams::new(5_000_000.into(), Default::default(), 0.into());
let capabilities = capabilities();
let (provider, proto) = setup(flow_params.clone(), capabilities.clone());
let (provider, proto) = setup(capabilities.clone());
let flow_params = proto.flow_params.read().clone();
let cur_status = status(provider.client.chain_info());
let my_status = write_handshake(&cur_status, &capabilities, Some(&flow_params));
let my_status = write_handshake(&cur_status, &capabilities, &proto);
provider.client.add_blocks(100, EachBlockWith::Nothing);
let cur_status = status(provider.client.chain_info());
{
let packet_body = write_handshake(&cur_status, &capabilities, Some(&flow_params));
let packet_body = write_handshake(&cur_status, &capabilities, &proto);
proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body));
proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status);
}
@ -368,20 +366,20 @@ fn get_block_bodies() {
#[test]
fn get_block_receipts() {
let flow_params = FlowParams::new(5_000_000.into(), Default::default(), 0.into());
let capabilities = capabilities();
let (provider, proto) = setup(flow_params.clone(), capabilities.clone());
let (provider, proto) = setup(capabilities.clone());
let flow_params = proto.flow_params.read().clone();
let cur_status = status(provider.client.chain_info());
let my_status = write_handshake(&cur_status, &capabilities, Some(&flow_params));
let my_status = write_handshake(&cur_status, &capabilities, &proto);
provider.client.add_blocks(1000, EachBlockWith::Nothing);
let cur_status = status(provider.client.chain_info());
{
let packet_body = write_handshake(&cur_status, &capabilities, Some(&flow_params));
let packet_body = write_handshake(&cur_status, &capabilities, &proto);
proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body));
proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status);
}
@ -423,16 +421,17 @@ fn get_block_receipts() {
#[test]
fn get_state_proofs() {
let flow_params = FlowParams::new(5_000_000.into(), Default::default(), 0.into());
let capabilities = capabilities();
let (provider, proto) = setup(flow_params.clone(), capabilities.clone());
let (provider, proto) = setup(capabilities.clone());
let flow_params = proto.flow_params.read().clone();
let provider = TestProvider(provider);
let cur_status = status(provider.0.client.chain_info());
{
let packet_body = write_handshake(&cur_status, &capabilities, Some(&flow_params));
let packet_body = write_handshake(&cur_status, &capabilities, &proto);
proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body.clone()));
proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &packet_body);
}
@ -481,15 +480,15 @@ fn get_state_proofs() {
#[test]
fn get_contract_code() {
let flow_params = FlowParams::new(5_000_000.into(), Default::default(), 0.into());
let capabilities = capabilities();
let (provider, proto) = setup(flow_params.clone(), capabilities.clone());
let (provider, proto) = setup(capabilities.clone());
let flow_params = proto.flow_params.read().clone();
let cur_status = status(provider.client.chain_info());
{
let packet_body = write_handshake(&cur_status, &capabilities, Some(&flow_params));
let packet_body = write_handshake(&cur_status, &capabilities, &proto);
proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body.clone()));
proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &packet_body);
}
@ -524,15 +523,15 @@ fn get_contract_code() {
#[test]
fn proof_of_execution() {
let flow_params = FlowParams::new(5_000_000.into(), Default::default(), 0.into());
let capabilities = capabilities();
let (provider, proto) = setup(flow_params.clone(), capabilities.clone());
let (provider, proto) = setup(capabilities.clone());
let flow_params = proto.flow_params.read().clone();
let cur_status = status(provider.client.chain_info());
{
let packet_body = write_handshake(&cur_status, &capabilities, Some(&flow_params));
let packet_body = write_handshake(&cur_status, &capabilities, &proto);
proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body.clone()));
proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &packet_body);
}
@ -553,7 +552,11 @@ fn proof_of_execution() {
let request_body = make_packet(req_id, &requests);
let response = {
let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests());
let limit = *flow_params.limit();
let cost = flow_params.compute_cost_multi(requests.requests());
println!("limit = {}, cost = {}", limit, cost);
let new_creds = limit - cost;
let mut response_stream = RlpStream::new_list(3);
response_stream.append(&req_id).append(&new_creds).begin_list(0);
@ -581,10 +584,10 @@ fn id_guard() {
use super::request_set::RequestSet;
use super::ReqId;
let flow_params = FlowParams::new(5_000_000.into(), Default::default(), 0.into());
let capabilities = capabilities();
let (provider, proto) = setup(flow_params.clone(), capabilities.clone());
let (provider, proto) = setup(capabilities.clone());
let flow_params = proto.flow_params.read().clone();
let req_id_1 = ReqId(5143);
let req_id_2 = ReqId(1111);
@ -607,12 +610,15 @@ fn id_guard() {
local_credits: flow_params.create_credits(),
status: status(provider.client.chain_info()),
capabilities: capabilities.clone(),
remote_flow: Some((flow_params.create_credits(), flow_params)),
remote_flow: Some((flow_params.create_credits(), (&*flow_params).clone())),
sent_head: provider.client.chain_info().best_block_hash,
last_update: ::time::SteadyTime::now(),
pending_requests: pending_requests,
failed_requests: Vec::new(),
propagated_transactions: Default::default(),
skip_update: false,
local_flow: flow_params,
awaiting_acknowledge: None,
}));
// first, malformed responses.

View File

@ -22,24 +22,19 @@ use std::collections::HashMap;
use std::marker::PhantomData;
use std::sync::Arc;
use ethcore::basic_account::BasicAccount;
use ethcore::encoded;
use ethcore::receipt::Receipt;
use ethcore::executed::{Executed, ExecutionError};
use futures::{future, Async, Poll, Future, BoxFuture};
use futures::{Async, Poll, Future};
use futures::sync::oneshot::{self, Sender, Receiver, Canceled};
use network::PeerId;
use rlp::RlpStream;
use util::{Bytes, RwLock, Mutex, U256, H256};
use util::sha3::{SHA3_NULL_RLP, SHA3_EMPTY, SHA3_EMPTY_LIST_RLP};
use util::{RwLock, Mutex};
use net::{self, Handler, Status, Capabilities, Announcement, EventContext, BasicContext, ReqId};
use cache::Cache;
use request::{self as basic_request, Request as NetworkRequest};
use self::request::CheckedRequest;
pub use self::request::{Request, Response};
pub use self::request::{Request, Response, HeaderRef};
#[cfg(test)]
mod tests;
@ -75,6 +70,98 @@ struct Pending {
sender: oneshot::Sender<Vec<Response>>,
}
impl Pending {
// answer as many of the given requests from the supplied cache as possible.
// TODO: support re-shuffling.
fn answer_from_cache(&mut self, cache: &Mutex<Cache>) {
while !self.requests.is_complete() {
let idx = self.requests.num_answered();
match self.requests[idx].respond_local(cache) {
Some(response) => {
self.requests.supply_response_unchecked(&response);
self.update_header_refs(idx, &response);
self.responses.push(response);
}
None => break,
}
}
}
// update header refs if the given response contains a header future requests require for
// verification.
// `idx` is the index of the request the response corresponds to.
fn update_header_refs(&mut self, idx: usize, response: &Response) {
match *response {
Response::HeaderByHash(ref hdr) => {
// fill the header for all requests waiting on this one.
// TODO: could be faster if we stored a map usize => Vec<usize>
// but typical use just has one header request that others
// depend on.
for r in self.requests.iter_mut().skip(idx + 1) {
if r.needs_header().map_or(false, |(i, _)| i == idx) {
r.provide_header(hdr.clone())
}
}
}
_ => {}, // no other responses produce headers.
}
}
// supply a response.
fn supply_response(&mut self, cache: &Mutex<Cache>, response: &basic_request::Response)
-> Result<(), basic_request::ResponseError<self::request::Error>>
{
match self.requests.supply_response(&cache, response) {
Ok(response) => {
let idx = self.responses.len();
self.update_header_refs(idx, &response);
self.responses.push(response);
Ok(())
}
Err(e) => Err(e),
}
}
// if the requests are complete, send the result and consume self.
fn try_complete(self) -> Option<Self> {
if self.requests.is_complete() {
let _ = self.sender.send(self.responses);
None
} else {
Some(self)
}
}
fn fill_unanswered(&mut self) {
self.requests.fill_unanswered();
}
// update the cached network requests.
fn update_net_requests(&mut self) {
use request::IncompleteRequest;
let mut builder = basic_request::RequestBuilder::default();
let num_answered = self.requests.num_answered();
let mut mapping = move |idx| idx - num_answered;
for request in self.requests.iter().skip(num_answered) {
let mut net_req = request.clone().into_net_request();
// all back-references with request index less than `num_answered` have
// been filled by now. all remaining requests point to nothing earlier
// than the next unanswered request.
net_req.adjust_refs(&mut mapping);
builder.push(net_req)
.expect("all back-references to answered requests have been filled; qed");
}
// update pending fields.
let capabilities = guess_capabilities(&self.requests[num_answered..]);
self.net_requests = builder.build();
self.required_capabilities = capabilities;
}
}
// helper to guess capabilities required for a given batch of network requests.
fn guess_capabilities(requests: &[CheckedRequest]) -> Capabilities {
let mut caps = Capabilities {
@ -97,16 +184,21 @@ fn guess_capabilities(requests: &[CheckedRequest]) -> Capabilities {
caps.serve_headers = true,
CheckedRequest::HeaderByHash(_, _) =>
caps.serve_headers = true,
CheckedRequest::Body(ref req, _) =>
update_since(&mut caps.serve_chain_since, req.header.number()),
CheckedRequest::Receipts(ref req, _) =>
update_since(&mut caps.serve_chain_since, req.0.number()),
CheckedRequest::Account(ref req, _) =>
update_since(&mut caps.serve_state_since, req.header.number()),
CheckedRequest::Code(ref req, _) =>
update_since(&mut caps.serve_state_since, req.block_id.1),
CheckedRequest::Execution(ref req, _) =>
update_since(&mut caps.serve_state_since, req.header.number()),
CheckedRequest::Body(ref req, _) => if let Ok(ref hdr) = req.0.as_ref() {
update_since(&mut caps.serve_chain_since, hdr.number());
},
CheckedRequest::Receipts(ref req, _) => if let Ok(ref hdr) = req.0.as_ref() {
update_since(&mut caps.serve_chain_since, hdr.number());
},
CheckedRequest::Account(ref req, _) => if let Ok(ref hdr) = req.header.as_ref() {
update_since(&mut caps.serve_state_since, hdr.number());
},
CheckedRequest::Code(ref req, _) => if let Ok(ref hdr) = req.header.as_ref() {
update_since(&mut caps.serve_state_since, hdr.number());
},
CheckedRequest::Execution(ref req, _) => if let Ok(ref hdr) = req.header.as_ref() {
update_since(&mut caps.serve_state_since, hdr.number());
},
}
}
@ -163,158 +255,6 @@ impl OnDemand {
me
}
/// Request a header's hash by block number and CHT root hash.
/// Returns the hash.
pub fn hash_by_number(&self, ctx: &BasicContext, req: request::HeaderProof) -> BoxFuture<H256, Canceled> {
let cached = {
let mut cache = self.cache.lock();
cache.block_hash(&req.num())
};
match cached {
Some(hash) => future::ok(hash).boxed(),
None => {
self.request(ctx, req)
.expect("request given fully fleshed out; qed")
.map(|(h, _)| h)
.boxed()
},
}
}
/// Request a canonical block's chain score.
/// Returns the chain score.
pub fn chain_score_by_number(&self, ctx: &BasicContext, req: request::HeaderProof) -> BoxFuture<U256, Canceled> {
let cached = {
let mut cache = self.cache.lock();
cache.block_hash(&req.num()).and_then(|hash| cache.chain_score(&hash))
};
match cached {
Some(score) => future::ok(score).boxed(),
None => {
self.request(ctx, req)
.expect("request given fully fleshed out; qed")
.map(|(_, s)| s)
.boxed()
},
}
}
/// Request a canonical block's hash and chain score by number.
/// Returns the hash and chain score.
pub fn hash_and_score_by_number(&self, ctx: &BasicContext, req: request::HeaderProof) -> BoxFuture<(H256, U256), Canceled> {
let cached = {
let mut cache = self.cache.lock();
let hash = cache.block_hash(&req.num());
(
hash.clone(),
hash.and_then(|hash| cache.chain_score(&hash)),
)
};
match cached {
(Some(hash), Some(score)) => future::ok((hash, score)).boxed(),
_ => {
self.request(ctx, req)
.expect("request given fully fleshed out; qed")
.boxed()
},
}
}
/// Request a header by hash. This is less accurate than by-number because we don't know
/// where in the chain this header lies, and therefore can't find a peer who is supposed to have
/// it as easily.
pub fn header_by_hash(&self, ctx: &BasicContext, req: request::HeaderByHash) -> BoxFuture<encoded::Header, Canceled> {
match { self.cache.lock().block_header(&req.0) } {
Some(hdr) => future::ok(hdr).boxed(),
None => {
self.request(ctx, req)
.expect("request given fully fleshed out; qed")
.boxed()
},
}
}
/// Request a block, given its header. Block bodies are requestable by hash only,
/// and the header is required anyway to verify and complete the block body
/// -- this just doesn't obscure the network query.
pub fn block(&self, ctx: &BasicContext, req: request::Body) -> BoxFuture<encoded::Block, Canceled> {
// fast path for empty body.
if req.header.transactions_root() == SHA3_NULL_RLP && req.header.uncles_hash() == SHA3_EMPTY_LIST_RLP {
let mut stream = RlpStream::new_list(3);
stream.append_raw(&req.header.into_inner(), 1);
stream.begin_list(0);
stream.begin_list(0);
future::ok(encoded::Block::new(stream.out())).boxed()
} else {
match { self.cache.lock().block_body(&req.hash) } {
Some(body) => {
let mut stream = RlpStream::new_list(3);
let body = body.rlp();
stream.append_raw(&req.header.into_inner(), 1);
stream.append_raw(&body.at(0).as_raw(), 1);
stream.append_raw(&body.at(1).as_raw(), 1);
future::ok(encoded::Block::new(stream.out())).boxed()
}
None => {
self.request(ctx, req)
.expect("request given fully fleshed out; qed")
.boxed()
}
}
}
}
/// Request the receipts for a block. The header serves two purposes:
/// provide the block hash to fetch receipts for, and for verification of the receipts root.
pub fn block_receipts(&self, ctx: &BasicContext, req: request::BlockReceipts) -> BoxFuture<Vec<Receipt>, Canceled> {
// fast path for empty receipts.
if req.0.receipts_root() == SHA3_NULL_RLP {
return future::ok(Vec::new()).boxed()
}
match { self.cache.lock().block_receipts(&req.0.hash()) } {
Some(receipts) => future::ok(receipts).boxed(),
None => {
self.request(ctx, req)
.expect("request given fully fleshed out; qed")
.boxed()
},
}
}
/// Request an account by address and block header -- which gives a hash to query and a state root
/// to verify against.
/// `None` here means that no account by the queried key exists in the queried state.
pub fn account(&self, ctx: &BasicContext, req: request::Account) -> BoxFuture<Option<BasicAccount>, Canceled> {
self.request(ctx, req)
.expect("request given fully fleshed out; qed")
.boxed()
}
/// Request code by address, known code hash, and block header.
pub fn code(&self, ctx: &BasicContext, req: request::Code) -> BoxFuture<Bytes, Canceled> {
// fast path for no code.
if req.code_hash == SHA3_EMPTY {
future::ok(Vec::new()).boxed()
} else {
self.request(ctx, req)
.expect("request given fully fleshed out; qed")
.boxed()
}
}
/// Request proof-of-execution for a transaction.
pub fn transaction_proof(&self, ctx: &BasicContext, req: request::TransactionProof) -> BoxFuture<ExecutionResult, Canceled> {
self.request(ctx, req)
.expect("request given fully fleshed out; qed")
.boxed()
}
/// Submit a vector of requests to be processed together.
///
/// Fails if back-references are not coherent.
@ -332,15 +272,33 @@ impl OnDemand {
let mut builder = basic_request::RequestBuilder::default();
let responses = Vec::with_capacity(requests.len());
for request in requests {
builder.push(CheckedRequest::from(request))?;
let mut header_producers = HashMap::new();
for (i, request) in requests.into_iter().enumerate() {
let request = CheckedRequest::from(request);
// ensure that all requests needing headers will get them.
if let Some((idx, field)) = request.needs_header() {
// a request chain with a header back-reference is valid only if it both
// points to a request that returns a header and has the same back-reference
// for the block hash.
match header_producers.get(&idx) {
Some(ref f) if &field == *f => {}
_ => return Err(basic_request::NoSuchOutput),
}
}
if let CheckedRequest::HeaderByHash(ref req, _) = request {
header_producers.insert(i, req.0.clone());
}
builder.push(request)?;
}
let requests = builder.build();
let net_requests = requests.clone().map_requests(|req| req.into_net_request());
let capabilities = guess_capabilities(requests.requests());
self.pending.write().push(Pending {
self.submit_pending(ctx, Pending {
requests: requests,
net_requests: net_requests,
required_capabilities: capabilities,
@ -348,8 +306,6 @@ impl OnDemand {
sender: sender,
});
self.attempt_dispatch(ctx);
Ok(receiver)
}
@ -430,6 +386,19 @@ impl OnDemand {
})
.collect(); // `pending` now contains all requests we couldn't dispatch.
}
// submit a pending request set. attempts to answer from cache before
// going to the network. if complete, sends response and consumes the struct.
fn submit_pending(&self, ctx: &BasicContext, mut pending: Pending) {
// answer as many requests from cache as we can, and schedule for dispatch
// if incomplete.
pending.answer_from_cache(&*self.cache);
if let Some(mut pending) = pending.try_complete() {
pending.update_net_requests();
self.pending.write().push(pending);
self.attempt_dispatch(ctx);
}
}
}
impl Handler for OnDemand {
@ -468,63 +437,27 @@ impl Handler for OnDemand {
}
fn on_responses(&self, ctx: &EventContext, req_id: ReqId, responses: &[basic_request::Response]) {
use request::IncompleteRequest;
let mut pending = match self.in_transit.write().remove(&req_id) {
Some(req) => req,
None => return,
};
// for each incoming response
// 1. ensure verification data filled. (still TODO since on_demand doesn't use back-references yet)
// 1. ensure verification data filled.
// 2. pending.requests.supply_response
// 3. if extracted on-demand response, keep it for later.
for response in responses {
match pending.requests.supply_response(&*self.cache, response) {
Ok(response) => {
pending.responses.push(response)
}
Err(e) => {
let peer = ctx.peer();
debug!(target: "on_demand", "Peer {} gave bad response: {:?}", peer, e);
ctx.disable_peer(peer);
if let Err(e) = pending.supply_response(&*self.cache, response) {
let peer = ctx.peer();
debug!(target: "on_demand", "Peer {} gave bad response: {:?}", peer, e);
ctx.disable_peer(peer);
break;
}
break;
}
}
pending.requests.fill_unanswered();
if pending.requests.is_complete() {
let _ = pending.sender.send(pending.responses);
return;
}
// update network requests (unless we're done, in which case fulfill the future.)
let mut builder = basic_request::RequestBuilder::default();
let num_answered = pending.requests.num_answered();
let mut mapping = move |idx| idx - num_answered;
for request in pending.requests.requests().iter().skip(num_answered) {
let mut net_req = request.clone().into_net_request();
// all back-references with request index less than `num_answered` have
// been filled by now. all remaining requests point to nothing earlier
// than the next unanswered request.
net_req.adjust_refs(&mut mapping);
builder.push(net_req)
.expect("all back-references to answered requests have been filled; qed");
}
// update pending fields and re-queue.
let capabilities = guess_capabilities(&pending.requests.requests()[num_answered..]);
pending.net_requests = builder.build();
pending.required_capabilities = capabilities;
self.pending.write().push(pending);
self.attempt_dispatch(ctx.as_basic());
pending.fill_unanswered();
self.submit_pending(ctx.as_basic(), pending);
}
fn tick(&self, ctx: &BasicContext) {

View File

@ -26,12 +26,12 @@ use ethcore::receipt::Receipt;
use ethcore::state::{self, ProvedExecution};
use ethcore::transaction::SignedTransaction;
use request::{self as net_request, IncompleteRequest, Output, OutputKind};
use request::{self as net_request, IncompleteRequest, CompleteRequest, Output, OutputKind, Field};
use rlp::{RlpStream, UntrustedRlp};
use util::{Address, Bytes, DBValue, HashDB, Mutex, H256, U256};
use util::memorydb::MemoryDB;
use util::sha3::Hashable;
use util::sha3::{Hashable, SHA3_NULL_RLP, SHA3_EMPTY, SHA3_EMPTY_LIST_RLP};
use util::trie::{Trie, TrieDB, TrieError};
const SUPPLIED_MATCHES: &'static str = "supplied responses always match produced requests; enforced by `check_response`; qed";
@ -87,6 +87,18 @@ pub trait RequestAdapter {
fn extract_from(Vec<Response>) -> Self::Out;
}
impl<T: RequestArg> RequestAdapter for Vec<T> {
type Out = Vec<T::Out>;
fn make_requests(self) -> Vec<Request> {
self.into_iter().map(RequestArg::make).collect()
}
fn extract_from(r: Vec<Response>) -> Self::Out {
r.into_iter().map(T::extract).collect()
}
}
// helper to implement `RequestArg` and `From` for a single request kind.
macro_rules! impl_single {
($variant: ident, $me: ty, $out: ty) => {
@ -173,6 +185,50 @@ mod impls {
impl_args!(A, B, C, D, E, F, G, H, I, J, K, L,);
}
/// A block header to be used for verification.
/// May be stored or an unresolved output of a prior request.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HeaderRef {
/// A stored header.
Stored(encoded::Header),
/// An unresolved header. The first item here is the index of the request which
/// will return the header. The second is a back-reference pointing to a block hash
/// which can be used to make requests until that header is resolved.
Unresolved(usize, Field<H256>),
}
impl HeaderRef {
/// Attempt to inspect the header.
pub fn as_ref(&self) -> Result<&encoded::Header, Error> {
match *self {
HeaderRef::Stored(ref hdr) => Ok(hdr),
HeaderRef::Unresolved(idx, _) => Err(Error::UnresolvedHeader(idx)),
}
}
// get the blockhash field to be used in requests.
fn field(&self) -> Field<H256> {
match *self {
HeaderRef::Stored(ref hdr) => Field::Scalar(hdr.hash()),
HeaderRef::Unresolved(_, ref field) => field.clone(),
}
}
// yield the index of the request which will produce the header.
fn needs_header(&self) -> Option<(usize, Field<H256>)> {
match *self {
HeaderRef::Stored(_) => None,
HeaderRef::Unresolved(idx, ref field) => Some((idx, field.clone())),
}
}
}
impl From<encoded::Header> for HeaderRef {
fn from(header: encoded::Header) -> Self {
HeaderRef::Stored(header)
}
}
/// Requests coupled with their required data for verification.
/// This is used internally but not part of the public API.
#[derive(Clone)]
@ -192,7 +248,7 @@ impl From<Request> for CheckedRequest {
match req {
Request::HeaderByHash(req) => {
let net_req = net_request::IncompleteHeadersRequest {
start: net_request::HashOrNumber::Hash(req.0).into(),
start: req.0.map(Into::into),
skip: 0,
max: 1,
reverse: false,
@ -207,33 +263,33 @@ impl From<Request> for CheckedRequest {
}
Request::Body(req) => {
let net_req = net_request::IncompleteBodyRequest {
hash: req.hash.into(),
hash: req.0.field(),
};
CheckedRequest::Body(req, net_req)
}
Request::Receipts(req) => {
let net_req = net_request::IncompleteReceiptsRequest {
hash: req.0.hash().into(),
hash: req.0.field(),
};
CheckedRequest::Receipts(req, net_req)
}
Request::Account(req) => {
Request::Account(req) => {
let net_req = net_request::IncompleteAccountRequest {
block_hash: req.header.hash().into(),
block_hash: req.header.field(),
address_hash: ::util::Hashable::sha3(&req.address).into(),
};
CheckedRequest::Account(req, net_req)
}
Request::Code(req) => {
let net_req = net_request::IncompleteCodeRequest {
block_hash: req.block_id.0.into(),
block_hash: req.header.field(),
code_hash: req.code_hash.into(),
};
CheckedRequest::Code(req, net_req)
}
Request::Execution(req) => {
let net_req = net_request::IncompleteExecutionRequest {
block_hash: req.header.hash().into(),
block_hash: req.header.field(),
from: req.tx.sender(),
gas: req.tx.gas,
gas_price: req.tx.gas_price,
@ -262,6 +318,119 @@ impl CheckedRequest {
CheckedRequest::Execution(_, req) => NetRequest::Execution(req),
}
}
/// Whether this needs a header from a prior request.
/// Returns `Some` with the index of the request returning the header
/// and the field giving the hash
/// if so, `None` otherwise.
pub fn needs_header(&self) -> Option<(usize, Field<H256>)> {
match *self {
CheckedRequest::Receipts(ref x, _) => x.0.needs_header(),
CheckedRequest::Body(ref x, _) => x.0.needs_header(),
CheckedRequest::Account(ref x, _) => x.header.needs_header(),
CheckedRequest::Code(ref x, _) => x.header.needs_header(),
CheckedRequest::Execution(ref x, _) => x.header.needs_header(),
_ => None,
}
}
/// Provide a header where one was needed. Should only be called if `needs_header`
/// returns `Some`, and for correctness, only use the header yielded by the correct
/// request.
pub fn provide_header(&mut self, header: encoded::Header) {
match *self {
CheckedRequest::Receipts(ref mut x, _) => x.0 = HeaderRef::Stored(header),
CheckedRequest::Body(ref mut x, _) => x.0 = HeaderRef::Stored(header),
CheckedRequest::Account(ref mut x, _) => x.header = HeaderRef::Stored(header),
CheckedRequest::Code(ref mut x, _) => x.header = HeaderRef::Stored(header),
CheckedRequest::Execution(ref mut x, _) => x.header = HeaderRef::Stored(header),
_ => {},
}
}
/// Attempt to complete the request based on data in the cache.
pub fn respond_local(&self, cache: &Mutex<::cache::Cache>) -> Option<Response> {
match *self {
CheckedRequest::HeaderProof(ref check, _) => {
let mut cache = cache.lock();
cache.block_hash(&check.num)
.and_then(|h| cache.chain_score(&h).map(|s| (h, s)))
.map(|(h, s)| Response::HeaderProof((h, s)))
}
CheckedRequest::HeaderByHash(_, ref req) => {
if let Some(&net_request::HashOrNumber::Hash(ref h)) = req.start.as_ref() {
return cache.lock().block_header(h).map(Response::HeaderByHash);
}
None
}
CheckedRequest::Receipts(ref check, ref req) => {
// empty transactions -> no receipts
if check.0.as_ref().ok().map_or(false, |hdr| hdr.receipts_root() == SHA3_NULL_RLP) {
return Some(Response::Receipts(Vec::new()));
}
req.hash.as_ref()
.and_then(|hash| cache.lock().block_receipts(hash))
.map(Response::Receipts)
}
CheckedRequest::Body(ref check, ref req) => {
// check for empty body.
if let Some(hdr) = check.0.as_ref().ok() {
if hdr.transactions_root() == SHA3_NULL_RLP && hdr.uncles_hash() == SHA3_EMPTY_LIST_RLP {
let mut stream = RlpStream::new_list(3);
stream.append_raw(hdr.rlp().as_raw(), 1);
stream.begin_list(0);
stream.begin_list(0);
return Some(Response::Body(encoded::Block::new(stream.out())));
}
}
// otherwise, check for cached body and header.
let block_hash = req.hash.as_ref()
.cloned()
.or_else(|| check.0.as_ref().ok().map(|hdr| hdr.hash()));
let block_hash = match block_hash {
Some(hash) => hash,
None => return None,
};
let mut cache = cache.lock();
let cached_header;
// can't use as_ref here although it seems like you would be able to:
// it complains about uninitialized `cached_header`.
let block_header = match check.0.as_ref().ok() {
Some(hdr) => Some(hdr),
None => {
cached_header = cache.block_header(&block_hash);
cached_header.as_ref()
}
};
block_header
.and_then(|hdr| cache.block_body(&block_hash).map(|b| (hdr, b)))
.map(|(hdr, body)| {
let mut stream = RlpStream::new_list(3);
let body = body.rlp();
stream.append_raw(&hdr.rlp().as_raw(), 1);
stream.append_raw(&body.at(0).as_raw(), 1);
stream.append_raw(&body.at(1).as_raw(), 1);
Response::Body(encoded::Block::new(stream.out()))
})
}
CheckedRequest::Code(_, ref req) => {
if req.code_hash.as_ref().map_or(false, |&h| h == SHA3_EMPTY) {
Some(Response::Code(Vec::new()))
} else {
None
}
}
_ => None,
}
}
}
macro_rules! match_me {
@ -279,37 +448,40 @@ macro_rules! match_me {
}
impl IncompleteRequest for CheckedRequest {
type Complete = net_request::CompleteRequest;
type Complete = CompleteRequest;
type Response = net_request::Response;
/// Check prior outputs against the needed inputs.
///
/// This is called to ensure consistency of this request with
/// others in the same packet.
fn check_outputs<F>(&self, f: F) -> Result<(), net_request::NoSuchOutput>
fn check_outputs<F>(&self, mut f: F) -> Result<(), net_request::NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), net_request::NoSuchOutput>
{
match_me!(*self, (_, ref req) => req.check_outputs(f))
match *self {
CheckedRequest::HeaderProof(_, ref req) => req.check_outputs(f),
CheckedRequest::HeaderByHash(ref check, ref req) => {
req.check_outputs(&mut f)?;
// make sure the output given is definitively a hash.
match check.0 {
Field::BackReference(r, idx) => f(r, idx, OutputKind::Hash),
_ => Ok(()),
}
}
CheckedRequest::Receipts(_, ref req) => req.check_outputs(f),
CheckedRequest::Body(_, ref req) => req.check_outputs(f),
CheckedRequest::Account(_, ref req) => req.check_outputs(f),
CheckedRequest::Code(_, ref req) => req.check_outputs(f),
CheckedRequest::Execution(_, ref req) => req.check_outputs(f),
}
}
/// Note that this request will produce the following outputs.
fn note_outputs<F>(&self, f: F) where F: FnMut(usize, OutputKind) {
match_me!(*self, (_, ref req) => req.note_outputs(f))
}
/// Fill fields of the request.
///
/// This function is provided an "output oracle" which allows fetching of
/// prior request outputs.
/// Only outputs previously checked with `check_outputs` may be available.
fn fill<F>(&mut self, f: F) where F: Fn(usize, usize) -> Result<Output, net_request::NoSuchOutput> {
match_me!(*self, (_, ref mut req) => req.fill(f))
}
/// Will succeed if all fields have been filled, will fail otherwise.
fn complete(self) -> Result<Self::Complete, net_request::NoSuchOutput> {
use ::request::CompleteRequest;
match self {
CheckedRequest::HeaderProof(_, req) => req.complete().map(CompleteRequest::HeaderProof),
CheckedRequest::HeaderByHash(_, req) => req.complete().map(CompleteRequest::Headers),
@ -333,35 +505,42 @@ impl net_request::CheckedRequest for CheckedRequest {
type Environment = Mutex<::cache::Cache>;
/// Check whether the response matches (beyond the type).
fn check_response(&self, cache: &Mutex<::cache::Cache>, response: &Self::Response) -> Result<Response, Error> {
fn check_response(&self, complete: &Self::Complete, cache: &Mutex<::cache::Cache>, response: &Self::Response) -> Result<Response, Error> {
use ::request::Response as NetResponse;
// helper for expecting a specific response for a given request.
macro_rules! expect {
($res: pat => $e: expr) => {
match *response {
($res: pat => $e: expr) => {{
match (response, complete) {
$res => $e,
_ => Err(Error::WrongKind),
}
}
}}
}
// check response against contained prover.
match *self {
CheckedRequest::HeaderProof(ref prover, _) => expect!(NetResponse::HeaderProof(ref res) =>
prover.check_response(cache, &res.proof).map(Response::HeaderProof)),
CheckedRequest::HeaderByHash(ref prover, _) => expect!(NetResponse::Headers(ref res) =>
prover.check_response(cache, &res.headers).map(Response::HeaderByHash)),
CheckedRequest::Receipts(ref prover, _) => expect!(NetResponse::Receipts(ref res) =>
prover.check_response(cache, &res.receipts).map(Response::Receipts)),
CheckedRequest::Body(ref prover, _) => expect!(NetResponse::Body(ref res) =>
prover.check_response(cache, &res.body).map(Response::Body)),
CheckedRequest::Account(ref prover, _) => expect!(NetResponse::Account(ref res) =>
prover.check_response(cache, &res.proof).map(Response::Account)),
CheckedRequest::Code(ref prover, _) => expect!(NetResponse::Code(ref res) =>
prover.check_response(cache, &res.code).map(Response::Code)),
CheckedRequest::Execution(ref prover, _) => expect!(NetResponse::Execution(ref res) =>
prover.check_response(cache, &res.items).map(Response::Execution)),
CheckedRequest::HeaderProof(ref prover, _) =>
expect!((&NetResponse::HeaderProof(ref res), _) =>
prover.check_response(cache, &res.proof).map(Response::HeaderProof)),
CheckedRequest::HeaderByHash(ref prover, _) =>
expect!((&NetResponse::Headers(ref res), &CompleteRequest::Headers(ref req)) =>
prover.check_response(cache, &req.start, &res.headers).map(Response::HeaderByHash)),
CheckedRequest::Receipts(ref prover, _) =>
expect!((&NetResponse::Receipts(ref res), _) =>
prover.check_response(cache, &res.receipts).map(Response::Receipts)),
CheckedRequest::Body(ref prover, _) =>
expect!((&NetResponse::Body(ref res), _) =>
prover.check_response(cache, &res.body).map(Response::Body)),
CheckedRequest::Account(ref prover, _) =>
expect!((&NetResponse::Account(ref res), _) =>
prover.check_response(cache, &res.proof).map(Response::Account)),
CheckedRequest::Code(ref prover, _) =>
expect!((&NetResponse::Code(ref res), &CompleteRequest::Code(ref req)) =>
prover.check_response(cache, &req.code_hash, &res.code).map(Response::Code)),
CheckedRequest::Execution(ref prover, _) =>
expect!((&NetResponse::Execution(ref res), _) =>
prover.check_response(cache, &res.items).map(Response::Execution)),
}
}
}
@ -387,6 +566,23 @@ pub enum Response {
Execution(super::ExecutionResult),
}
impl net_request::ResponseLike for Response {
fn fill_outputs<F>(&self, mut f: F) where F: FnMut(usize, Output) {
match *self {
Response::HeaderProof((ref hash, _)) => f(0, Output::Hash(*hash)),
Response::Account(None) => {
f(0, Output::Hash(SHA3_EMPTY)); // code hash
f(1, Output::Hash(SHA3_NULL_RLP)); // storage root.
}
Response::Account(Some(ref acc)) => {
f(0, Output::Hash(acc.code_hash));
f(1, Output::Hash(acc.storage_root));
}
_ => {}
}
}
}
/// Errors in verification.
#[derive(Debug, PartialEq)]
pub enum Error {
@ -398,6 +594,10 @@ pub enum Error {
Trie(TrieError),
/// Bad inclusion proof
BadProof,
/// Header by number instead of hash.
HeaderByNumber,
/// Unresolved header reference.
UnresolvedHeader(usize),
/// Wrong header number.
WrongNumber(u64, u64),
/// Wrong hash.
@ -468,62 +668,63 @@ impl HeaderProof {
/// Request for a header by hash.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HeaderByHash(pub H256);
pub struct HeaderByHash(pub Field<H256>);
impl HeaderByHash {
/// Check a response for the header.
pub fn check_response(&self, cache: &Mutex<::cache::Cache>, headers: &[encoded::Header]) -> Result<encoded::Header, Error> {
pub fn check_response(
&self,
cache: &Mutex<::cache::Cache>,
start: &net_request::HashOrNumber,
headers: &[encoded::Header]
) -> Result<encoded::Header, Error> {
let expected_hash = match (self.0, start) {
(Field::Scalar(ref h), &net_request::HashOrNumber::Hash(ref h2)) => {
if h != h2 { return Err(Error::WrongHash(*h, *h2)) }
*h
}
(_, &net_request::HashOrNumber::Hash(h2)) => h2,
_ => return Err(Error::HeaderByNumber),
};
let header = headers.get(0).ok_or(Error::Empty)?;
let hash = header.sha3();
match hash == self.0 {
match hash == expected_hash {
true => {
cache.lock().insert_block_header(hash, header.clone());
Ok(header.clone())
}
false => Err(Error::WrongHash(self.0, hash)),
false => Err(Error::WrongHash(expected_hash, hash)),
}
}
}
/// Request for a block, with header and precomputed hash.
/// Request for a block, with header for verification.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Body {
/// The block's header.
pub header: encoded::Header,
/// The block's hash.
pub hash: H256,
}
pub struct Body(pub HeaderRef);
impl Body {
/// Create a request for a block body from a given header.
pub fn new(header: encoded::Header) -> Self {
let hash = header.hash();
Body {
header: header,
hash: hash,
}
}
/// Check a response for this block body.
pub fn check_response(&self, cache: &Mutex<::cache::Cache>, body: &encoded::Body) -> Result<encoded::Block, Error> {
// check the integrity of the the body against the header
let header = self.0.as_ref()?;
let tx_root = ::util::triehash::ordered_trie_root(body.rlp().at(0).iter().map(|r| r.as_raw().to_vec()));
if tx_root != self.header.transactions_root() {
return Err(Error::WrongTrieRoot(self.header.transactions_root(), tx_root));
if tx_root != header.transactions_root() {
return Err(Error::WrongTrieRoot(header.transactions_root(), tx_root));
}
let uncles_hash = body.rlp().at(1).as_raw().sha3();
if uncles_hash != self.header.uncles_hash() {
return Err(Error::WrongHash(self.header.uncles_hash(), uncles_hash));
if uncles_hash != header.uncles_hash() {
return Err(Error::WrongHash(header.uncles_hash(), uncles_hash));
}
// concatenate the header and the body.
let mut stream = RlpStream::new_list(3);
stream.append_raw(self.header.rlp().as_raw(), 1);
stream.append_raw(header.rlp().as_raw(), 1);
stream.append_raw(body.rlp().at(0).as_raw(), 1);
stream.append_raw(body.rlp().at(1).as_raw(), 1);
cache.lock().insert_block_body(self.hash, body.clone());
cache.lock().insert_block_body(header.hash(), body.clone());
Ok(encoded::Block::new(stream.out()))
}
@ -531,12 +732,12 @@ impl Body {
/// Request for a block's receipts with header for verification.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BlockReceipts(pub encoded::Header);
pub struct BlockReceipts(pub HeaderRef);
impl BlockReceipts {
/// Check a response with receipts against the stored header.
pub fn check_response(&self, cache: &Mutex<::cache::Cache>, receipts: &[Receipt]) -> Result<Vec<Receipt>, Error> {
let receipts_root = self.0.receipts_root();
let receipts_root = self.0.as_ref()?.receipts_root();
let found_root = ::util::triehash::ordered_trie_root(receipts.iter().map(|r| ::rlp::encode(r).to_vec()));
match receipts_root == found_root {
@ -553,7 +754,7 @@ impl BlockReceipts {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Account {
/// Header for verification.
pub header: encoded::Header,
pub header: HeaderRef,
/// Address requested.
pub address: Address,
}
@ -561,7 +762,8 @@ pub struct Account {
impl Account {
/// Check a response with an account against the stored header.
pub fn check_response(&self, _: &Mutex<::cache::Cache>, proof: &[Bytes]) -> Result<Option<BasicAccount>, Error> {
let state_root = self.header.state_root();
let header = self.header.as_ref()?;
let state_root = header.state_root();
let mut db = MemoryDB::new();
for node in proof { db.insert(&node[..]); }
@ -584,20 +786,25 @@ impl Account {
/// Request for account code.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Code {
/// Block hash, number pair.
pub block_id: (H256, u64),
/// Header reference.
pub header: HeaderRef,
/// Account's code hash.
pub code_hash: H256,
pub code_hash: Field<H256>,
}
impl Code {
/// Check a response with code against the code hash.
pub fn check_response(&self, _: &Mutex<::cache::Cache>, code: &[u8]) -> Result<Vec<u8>, Error> {
pub fn check_response(
&self,
_: &Mutex<::cache::Cache>,
code_hash: &H256,
code: &[u8]
) -> Result<Vec<u8>, Error> {
let found_hash = code.sha3();
if found_hash == self.code_hash {
if &found_hash == code_hash {
Ok(code.to_vec())
} else {
Err(Error::WrongHash(self.code_hash, found_hash))
Err(Error::WrongHash(*code_hash, found_hash))
}
}
}
@ -608,8 +815,9 @@ pub struct TransactionProof {
/// The transaction to request proof of.
pub tx: SignedTransaction,
/// Block header.
pub header: encoded::Header,
pub header: HeaderRef,
/// Transaction environment info.
// TODO: it's not really possible to provide this if the header is unknown.
pub env_info: EnvInfo,
/// Consensus engine.
pub engine: Arc<Engine>,
@ -618,7 +826,7 @@ pub struct TransactionProof {
impl TransactionProof {
/// Check the proof, returning the proved execution or indicate that the proof was bad.
pub fn check_response(&self, _: &Mutex<::cache::Cache>, state_items: &[DBValue]) -> Result<super::ExecutionResult, Error> {
let root = self.header.state_root();
let root = self.header.as_ref()?.state_root();
let mut env_info = self.env_info.clone();
env_info.gas_limit = self.tx.gas.clone();
@ -697,7 +905,7 @@ mod tests {
let raw_header = encoded::Header::new(::rlp::encode(&header).to_vec());
let cache = Mutex::new(make_cache());
assert!(HeaderByHash(hash).check_response(&cache, &[raw_header]).is_ok())
assert!(HeaderByHash(hash.into()).check_response(&cache, &hash.into(), &[raw_header]).is_ok())
}
#[test]
@ -708,10 +916,7 @@ mod tests {
let mut body_stream = RlpStream::new_list(2);
body_stream.begin_list(0).begin_list(0);
let req = Body {
header: encoded::Header::new(::rlp::encode(&header).to_vec()),
hash: header.hash(),
};
let req = Body(encoded::Header::new(::rlp::encode(&header).to_vec()).into());
let cache = Mutex::new(make_cache());
let response = encoded::Body::new(body_stream.drain().to_vec());
@ -734,7 +939,7 @@ mod tests {
header.set_receipts_root(receipts_root);
let req = BlockReceipts(encoded::Header::new(::rlp::encode(&header).to_vec()));
let req = BlockReceipts(encoded::Header::new(::rlp::encode(&header).to_vec()).into());
let cache = Mutex::new(make_cache());
assert!(req.check_response(&cache, &receipts).is_ok())
@ -782,7 +987,7 @@ mod tests {
header.set_state_root(root.clone());
let req = Account {
header: encoded::Header::new(::rlp::encode(&header).to_vec()),
header: encoded::Header::new(::rlp::encode(&header).to_vec()).into(),
address: addr,
};
@ -793,13 +998,15 @@ mod tests {
#[test]
fn check_code() {
let code = vec![1u8; 256];
let code_hash = ::util::Hashable::sha3(&code);
let header = Header::new();
let req = Code {
block_id: (Default::default(), 2),
code_hash: ::util::Hashable::sha3(&code),
header: encoded::Header::new(::rlp::encode(&header).to_vec()).into(),
code_hash: code_hash.into(),
};
let cache = Mutex::new(make_cache());
assert!(req.check_response(&cache, &code).is_ok());
assert!(req.check_response(&cache, &[]).is_err());
assert!(req.check_response(&cache, &code_hash, &code).is_ok());
assert!(req.check_response(&cache, &code_hash, &[]).is_err());
}
}

View File

@ -28,7 +28,7 @@ use ::request::{self as basic_request, Response};
use std::sync::Arc;
use super::{request, OnDemand, Peer};
use super::{request, OnDemand, Peer, HeaderRef};
// useful contexts to give the service.
enum Context {
@ -122,7 +122,10 @@ fn dummy_capabilities() -> Capabilities {
#[test]
fn detects_hangup() {
let on_demand = Harness::create().service;
let result = on_demand.header_by_hash(&Context::NoOp, request::HeaderByHash(H256::default()));
let result = on_demand.request_raw(
&Context::NoOp,
vec![request::HeaderByHash(H256::default().into()).into()],
);
assert_eq!(on_demand.pending.read().len(), 1);
drop(result);
@ -148,7 +151,7 @@ fn single_request() {
let recv = harness.service.request_raw(
&Context::NoOp,
vec![request::HeaderByHash(header.hash()).into()]
vec![request::HeaderByHash(header.hash().into()).into()]
).unwrap();
assert_eq!(harness.service.pending.read().len(), 1);
@ -182,7 +185,7 @@ fn no_capabilities() {
let _recv = harness.service.request_raw(
&Context::NoOp,
vec![request::HeaderByHash(Default::default()).into()]
vec![request::HeaderByHash(H256::default().into()).into()]
).unwrap();
assert_eq!(harness.service.pending.read().len(), 1);
@ -209,7 +212,7 @@ fn reassign() {
let recv = harness.service.request_raw(
&Context::NoOp,
vec![request::HeaderByHash(header.hash()).into()]
vec![request::HeaderByHash(header.hash().into()).into()]
).unwrap();
assert_eq!(harness.service.pending.read().len(), 1);
@ -264,8 +267,8 @@ fn partial_response() {
let recv = harness.service.request_raw(
&Context::NoOp,
vec![
request::HeaderByHash(header1.hash()).into(),
request::HeaderByHash(header2.hash()).into(),
request::HeaderByHash(header1.hash().into()).into(),
request::HeaderByHash(header2.hash().into()).into(),
],
).unwrap();
@ -323,8 +326,8 @@ fn part_bad_part_good() {
let recv = harness.service.request_raw(
&Context::NoOp,
vec![
request::HeaderByHash(header1.hash()).into(),
request::HeaderByHash(header2.hash()).into(),
request::HeaderByHash(header1.hash().into()).into(),
request::HeaderByHash(header2.hash().into()).into(),
],
).unwrap();
@ -378,7 +381,7 @@ fn wrong_kind() {
let _recv = harness.service.request_raw(
&Context::NoOp,
vec![request::HeaderByHash(Default::default()).into()]
vec![request::HeaderByHash(H256::default().into()).into()]
).unwrap();
assert_eq!(harness.service.pending.read().len(), 1);
@ -395,3 +398,100 @@ fn wrong_kind() {
assert_eq!(harness.service.pending.read().len(), 1);
}
#[test]
fn back_references() {
let harness = Harness::create();
let peer_id = 10101;
let req_id = ReqId(14426);
harness.inject_peer(peer_id, Peer {
status: dummy_status(),
capabilities: dummy_capabilities(),
});
let header = Header::default();
let encoded = encoded::Header::new(header.rlp(Seal::With));
let recv = harness.service.request_raw(
&Context::NoOp,
vec![
request::HeaderByHash(header.hash().into()).into(),
request::BlockReceipts(HeaderRef::Unresolved(0, header.hash().into())).into(),
]
).unwrap();
assert_eq!(harness.service.pending.read().len(), 1);
harness.service.dispatch_pending(&Context::RequestFrom(peer_id, req_id));
assert_eq!(harness.service.pending.read().len(), 0);
harness.service.on_responses(
&Context::WithPeer(peer_id),
req_id,
&[
Response::Headers(basic_request::HeadersResponse { headers: vec![encoded] }),
Response::Receipts(basic_request::ReceiptsResponse { receipts: vec![] }),
]
);
assert!(recv.wait().is_ok());
}
#[test]
#[should_panic]
fn bad_back_reference() {
let harness = Harness::create();
let header = Header::default();
let _ = harness.service.request_raw(
&Context::NoOp,
vec![
request::HeaderByHash(header.hash().into()).into(),
request::BlockReceipts(HeaderRef::Unresolved(1, header.hash().into())).into(),
]
).unwrap();
}
#[test]
fn fill_from_cache() {
let harness = Harness::create();
let peer_id = 10101;
let req_id = ReqId(14426);
harness.inject_peer(peer_id, Peer {
status: dummy_status(),
capabilities: dummy_capabilities(),
});
let header = Header::default();
let encoded = encoded::Header::new(header.rlp(Seal::With));
let recv = harness.service.request_raw(
&Context::NoOp,
vec![
request::HeaderByHash(header.hash().into()).into(),
request::BlockReceipts(HeaderRef::Unresolved(0, header.hash().into())).into(),
]
).unwrap();
assert_eq!(harness.service.pending.read().len(), 1);
harness.service.dispatch_pending(&Context::RequestFrom(peer_id, req_id));
assert_eq!(harness.service.pending.read().len(), 0);
harness.service.on_responses(
&Context::WithPeer(peer_id),
req_id,
&[
Response::Headers(basic_request::HeadersResponse { headers: vec![encoded] }),
]
);
assert!(recv.wait().is_ok());
}

View File

@ -19,6 +19,7 @@
//! supplied as well.
use std::collections::HashMap;
use std::ops::{Deref, DerefMut};
use request::{
IncompleteRequest, OutputKind, Output, NoSuchOutput, ResponseError, ResponseLike,
};
@ -124,23 +125,14 @@ impl<T: IncompleteRequest + Clone> Requests<T> {
req.fill(|req_idx, out_idx| outputs.get(&(req_idx, out_idx)).cloned().ok_or(NoSuchOutput))
}
}
}
impl<T: super::CheckedRequest> Requests<T> {
/// Supply a response for the next request.
/// Fails on: wrong request kind, all requests answered already.
pub fn supply_response(&mut self, env: &T::Environment, response: &T::Response)
-> Result<T::Extract, ResponseError<T::Error>>
{
let idx = self.answered;
// check validity.
if self.is_complete() { return Err(ResponseError::Unexpected) }
let extracted = self.requests[idx]
.check_response(env, response).map_err(ResponseError::Validity)?;
/// Supply a response, asserting its correctness.
/// Fill outputs based upon it.
pub fn supply_response_unchecked<R: ResponseLike>(&mut self, response: &R) {
if self.is_complete() { return }
let outputs = &mut self.outputs;
let idx = self.answered;
response.fill_outputs(|out_idx, output| {
// we don't need to check output kinds here because all back-references
// are validated in the builder.
@ -154,7 +146,26 @@ impl<T: super::CheckedRequest> Requests<T> {
if let Some(ref mut req) = self.requests.get_mut(self.answered) {
req.fill(|req_idx, out_idx| outputs.get(&(req_idx, out_idx)).cloned().ok_or(NoSuchOutput))
}
}
}
impl<T: super::CheckedRequest + Clone> Requests<T> {
/// Supply a response for the next request.
/// Fails on: wrong request kind, all requests answered already.
pub fn supply_response(&mut self, env: &T::Environment, response: &T::Response)
-> Result<T::Extract, ResponseError<T::Error>>
{
let idx = self.answered;
// check validity.
if idx == self.requests.len() { return Err(ResponseError::Unexpected) }
let completed = self.next_complete()
.expect("only fails when all requests have been answered; this just checked against; qed");
let extracted = self.requests[idx]
.check_response(&completed, env, response).map_err(ResponseError::Validity)?;
self.supply_response_unchecked(response);
Ok(extracted)
}
}
@ -182,6 +193,20 @@ impl Requests<super::Request> {
}
}
impl<T: IncompleteRequest> Deref for Requests<T> {
type Target = [T];
fn deref(&self) -> &[T] {
&self.requests[..]
}
}
impl<T: IncompleteRequest> DerefMut for Requests<T> {
fn deref_mut(&mut self) -> &mut [T] {
&mut self.requests[..]
}
}
#[cfg(test)]
mod tests {
use request::*;

View File

@ -83,7 +83,7 @@ pub enum ResponseError<T> {
}
/// An input to a request.
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Field<T> {
/// A pre-specified input.
Scalar(T),
@ -93,6 +93,29 @@ pub enum Field<T> {
}
impl<T> Field<T> {
/// Helper for creating a new back-reference field.
pub fn back_ref(idx: usize, req: usize) -> Self {
Field::BackReference(idx, req)
}
/// map a scalar into some other item.
pub fn map<F, U>(self, f: F) -> Field<U> where F: FnOnce(T) -> U {
match self {
Field::Scalar(x) => Field::Scalar(f(x)),
Field::BackReference(req, idx) => Field::BackReference(req, idx),
}
}
/// Attempt to get a reference to the inner scalar.
pub fn as_ref(&self) -> Option<&T> {
match *self {
Field::Scalar(ref x) => Some(x),
Field::BackReference(_, _) => None,
}
}
// attempt conversion into scalar value.
fn into_scalar(self) -> Result<T, NoSuchOutput> {
match self {
@ -256,6 +279,22 @@ pub enum CompleteRequest {
Execution(CompleteExecutionRequest),
}
impl CompleteRequest {
/// Inspect the kind of this response.
pub fn kind(&self) -> Kind {
match *self {
CompleteRequest::Headers(_) => Kind::Headers,
CompleteRequest::HeaderProof(_) => Kind::HeaderProof,
CompleteRequest::Receipts(_) => Kind::Receipts,
CompleteRequest::Body(_) => Kind::Body,
CompleteRequest::Account(_) => Kind::Account,
CompleteRequest::Storage(_) => Kind::Storage,
CompleteRequest::Code(_) => Kind::Code,
CompleteRequest::Execution(_) => Kind::Execution,
}
}
}
impl Request {
/// Get the request kind.
pub fn kind(&self) -> Kind {
@ -384,7 +423,7 @@ impl CheckedRequest for Request {
type Error = WrongKind;
type Environment = ();
fn check_response(&self, _: &(), response: &Response) -> Result<(), WrongKind> {
fn check_response(&self, _: &Self::Complete, _: &(), response: &Response) -> Result<(), WrongKind> {
if self.kind() == response.kind() {
Ok(())
} else {
@ -396,7 +435,7 @@ impl CheckedRequest for Request {
/// Kinds of requests.
/// Doubles as the "ID" field of the request.
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub enum Kind {
/// A request for headers.
Headers = 0,
@ -571,7 +610,7 @@ pub trait CheckedRequest: IncompleteRequest {
type Environment;
/// Check whether the response matches (beyond the type).
fn check_response(&self, &Self::Environment, &Self::Response) -> Result<Self::Extract, Self::Error>;
fn check_response(&self, &Self::Complete, &Self::Environment, &Self::Response) -> Result<Self::Extract, Self::Error>;
}
/// A response-like object.

View File

@ -16,3 +16,4 @@ native-contract-generator = { path = "generator" }
[features]
default = []
test_contracts = []

View File

@ -48,7 +48,7 @@ pub fn generate_module(struct_name: &str, abi: &str) -> Result<String, Error> {
use byteorder::{{BigEndian, ByteOrder}};
use futures::{{future, Future, IntoFuture, BoxFuture}};
use ethabi::{{Contract, Interface, Token, Event}};
use util::{{self, Uint}};
use util;
/// Generated Rust bindings to an Ethereum contract.
#[derive(Clone, Debug)]

View File

@ -0,0 +1,51 @@
{
"name": "Metropolis (Test)",
"engine": {
"Ethash": {
"params": {
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"homesteadTransition": "0x0",
"eip150Transition": "0x0",
"eip155Transition": "0x7fffffffffffffff",
"eip160Transition": "0x0",
"eip161abcTransition": "0x0",
"eip161dTransition": "0x0",
"maxCodeSize": 24576
}
}
},
"params": {
"accountStartNonce": "0x00",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1",
"eip98Transition": "0x7fffffffffffffff",
"eip86Transition": "0x0",
"eip140Transition": "0x0"
},
"genesis": {
"seal": {
"ethereum": {
"nonce": "0x0000000000000042",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
},
"difficulty": "0x400000000",
"author": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
"gasLimit": "0x1388"
},
"accounts": {
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
"0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
"0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
"0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }
}
}

View File

@ -15,7 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Evm input params.
use util::{Address, Bytes, Uint, U256};
use util::{Address, Bytes, U256};
use util::hash::{H256};
use util::sha3::{Hashable, SHA3_EMPTY};
use ethjson;

View File

@ -21,7 +21,7 @@ use std::sync::Arc;
use std::collections::HashSet;
use rlp::{UntrustedRlp, RlpStream, Encodable, Decodable, DecoderError};
use util::{Bytes, Address, Uint, Hashable, U256, H256, ordered_trie_root, SHA3_NULL_RLP};
use util::{Bytes, Address, Hashable, U256, H256, ordered_trie_root, SHA3_NULL_RLP};
use util::error::{Mismatch, OutOfBounds};
use basic_types::{LogBloom, Seal};

View File

@ -23,7 +23,7 @@ use crypto::ripemd160::Ripemd160 as Ripemd160Digest;
use crypto::digest::Digest;
use num::{BigUint, Zero, One};
use util::{U256, H256, Uint, Hashable, BytesRef};
use util::{U256, H256, Hashable, BytesRef};
use ethkey::{Signature, recover as ec_recover};
use ethjson;

View File

@ -21,7 +21,8 @@ use util::{H256, Bytes};
#[ipc]
pub trait ChainNotify : Send + Sync {
/// fires when chain has new blocks.
fn new_blocks(&self,
fn new_blocks(
&self,
_imported: Vec<H256>,
_invalid: Vec<H256>,
_enacted: Vec<H256>,
@ -29,7 +30,8 @@ pub trait ChainNotify : Send + Sync {
_sealed: Vec<H256>,
// Block bytes.
_proposed: Vec<Bytes>,
_duration: u64) {
_duration: u64,
) {
// does nothing by default
}

View File

@ -25,7 +25,7 @@ use time::precise_time_ns;
// util
use util::{Bytes, PerfTimer, Itertools, Mutex, RwLock, MutexGuard, Hashable};
use util::{journaldb, DBValue, TrieFactory, Trie};
use util::{U256, H256, Address, H2048, Uint};
use util::{U256, H256, Address, H2048};
use util::trie::TrieSpec;
use util::kvdb::*;

View File

@ -17,7 +17,7 @@
//! Tendermint specific parameters.
use ethjson;
use util::{U256, Uint, Address};
use util::{U256, Address};
use time::Duration;
use super::super::validator_set::{ValidatorSet, new_validator_set};
use super::super::transition::Timeouts;

View File

@ -75,6 +75,9 @@ pub fn new_transition_test() -> Spec { load(include_bytes!("../../res/ethereum/t
/// Create a new Foundation Mainnet chain spec without genesis accounts.
pub fn new_mainnet_like() -> Spec { load(include_bytes!("../../res/ethereum/frontier_like_test.json")) }
/// Create a new Foundation Metropolis era spec.
pub fn new_metropolis_test() -> Spec { load(include_bytes!("../../res/ethereum/metropolis_test.json")) }
/// Create a new Foundation Ropsten chain spec.
pub fn new_ropsten() -> Spec { load(include_bytes!("../../res/ethereum/ropsten.json")) }

View File

@ -17,7 +17,7 @@
//! Evm interface.
use std::{ops, cmp, fmt};
use util::{U128, U256, U512, Uint, trie};
use util::{U128, U256, U512, trie};
use action_params::ActionParams;
use evm::Ext;
use builtin;
@ -104,8 +104,25 @@ pub type Result<T> = ::std::result::Result<T, Error>;
pub enum GasLeft<'a> {
/// Known gas left
Known(U256),
/// Return instruction must be processed.
NeedsReturn(U256, &'a [u8]),
/// Return or Revert instruction must be processed.
NeedsReturn {
/// Amount of gas left.
gas_left: U256,
/// Return data buffer.
data: &'a [u8],
/// Apply or revert state changes on revert.
apply_state: bool
},
}
/// Finalization result. Gas Left: either it is a known value, or it needs to be computed by processing
/// a return instruction.
#[derive(Debug)]
pub struct FinalizationResult {
/// Final amount of gas left.
pub gas_left: U256,
/// Apply execution state changes or revert them.
pub apply_state: bool,
}
/// Types that can be "finalized" using an EVM.
@ -113,15 +130,18 @@ pub enum GasLeft<'a> {
/// In practice, this is just used to define an inherent impl on
/// `Reult<GasLeft<'a>>`.
pub trait Finalize {
/// Consume the externalities, call return if necessary, and produce a final amount of gas left.
fn finalize<E: Ext>(self, ext: E) -> Result<U256>;
/// Consume the externalities, call return if necessary, and produce call result.
fn finalize<E: Ext>(self, ext: E) -> Result<FinalizationResult>;
}
impl<'a> Finalize for Result<GasLeft<'a>> {
fn finalize<E: Ext>(self, ext: E) -> Result<U256> {
fn finalize<E: Ext>(self, ext: E) -> Result<FinalizationResult> {
match self {
Ok(GasLeft::Known(gas)) => Ok(gas),
Ok(GasLeft::NeedsReturn(gas, ret_code)) => ext.ret(&gas, ret_code),
Ok(GasLeft::Known(gas_left)) => Ok(FinalizationResult { gas_left: gas_left, apply_state: true }),
Ok(GasLeft::NeedsReturn {gas_left, data, apply_state}) => ext.ret(&gas_left, data).map(|gas_left| FinalizationResult {
gas_left: gas_left,
apply_state: apply_state,
}),
Err(err) => Err(err),
}
}
@ -161,11 +181,11 @@ impl CostType for U256 {
}
fn overflow_add(self, other: Self) -> (Self, bool) {
Uint::overflowing_add(self, other)
self.overflowing_add(other)
}
fn overflow_mul(self, other: Self) -> (Self, bool) {
Uint::overflowing_mul(self, other)
self.overflowing_mul(other)
}
fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool) {
@ -230,7 +250,7 @@ pub trait Evm {
#[cfg(test)]
mod tests {
use util::{U256, Uint};
use util::U256;
use super::CostType;
#[test]

View File

@ -20,7 +20,7 @@
use std::fmt;
use std::sync::Arc;
use evm::Evm;
use util::{U256, Uint};
use util::U256;
use super::interpreter::SharedCache;
#[derive(Debug, PartialEq, Clone)]

View File

@ -279,6 +279,7 @@ lazy_static! {
arr[DELEGATECALL as usize] = InstructionInfo::new("DELEGATECALL", 0, 6, 1, true, GasPriceTier::Special);
arr[SUICIDE as usize] = InstructionInfo::new("SUICIDE", 0, 1, 0, true, GasPriceTier::Special);
arr[CREATE2 as usize] = InstructionInfo::new("CREATE2", 0, 3, 1, true, GasPriceTier::Special);
arr[REVERT as usize] = InstructionInfo::new("REVERT", 0, 2, 0, true, GasPriceTier::Zero);
arr
};
}
@ -556,6 +557,8 @@ pub const RETURN: Instruction = 0xf3;
pub const DELEGATECALL: Instruction = 0xf4;
/// create a new account and set creation address to sha3(sender + sha3(init code)) % 2**160
pub const CREATE2: Instruction = 0xfb;
/// stop execution and revert state changes. Return output data.
pub const REVERT: Instruction = 0xfd;
/// halt execution and register account for later deletion
pub const SUICIDE: Instruction = 0xff;

View File

@ -164,7 +164,7 @@ impl<Gas: CostType> Gasometer<Gas> {
instructions::MSTORE8 => {
Request::GasMem(default_gas, mem_needed_const(stack.peek(0), 1)?)
},
instructions::RETURN => {
instructions::RETURN | instructions::REVERT => {
Request::GasMem(default_gas, mem_needed(stack.peek(0), stack.peek(1))?)
},
instructions::SHA3 => {

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use util::{U256, Uint};
use util::U256;
pub trait Memory {
/// Retrieve current size of the memory

View File

@ -84,8 +84,16 @@ enum InstructionResult<Gas> {
Ok,
UnusedGas(Gas),
JumpToPosition(U256),
// gas left, init_orf, init_size
StopExecutionNeedsReturn(Gas, U256, U256),
StopExecutionNeedsReturn {
/// Gas left.
gas: Gas,
/// Return data offset.
init_off: U256,
/// Return data size.
init_size: U256,
/// Apply or revert state changes.
apply: bool,
},
StopExecution,
}
@ -156,9 +164,13 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
let pos = self.verify_jump(position, &valid_jump_destinations)?;
reader.position = pos;
},
InstructionResult::StopExecutionNeedsReturn(gas, off, size) => {
InstructionResult::StopExecutionNeedsReturn {gas, init_off, init_size, apply} => {
informant.done();
return Ok(GasLeft::NeedsReturn(gas.as_u256(), self.mem.read_slice(off, size)));
return Ok(GasLeft::NeedsReturn {
gas_left: gas.as_u256(),
data: self.mem.read_slice(init_off, init_size),
apply_state: apply
});
},
InstructionResult::StopExecution => break,
_ => {},
@ -183,7 +195,8 @@ impl<Cost: CostType> Interpreter<Cost> {
let schedule = ext.schedule();
if (instruction == instructions::DELEGATECALL && !schedule.have_delegate_call) ||
(instruction == instructions::CREATE2 && !schedule.have_create2) {
(instruction == instructions::CREATE2 && !schedule.have_create2) ||
(instruction == instructions::REVERT && !schedule.have_revert) {
return Err(evm::Error::BadInstruction {
instruction: instruction
@ -363,7 +376,13 @@ impl<Cost: CostType> Interpreter<Cost> {
let init_off = stack.pop_back();
let init_size = stack.pop_back();
return Ok(InstructionResult::StopExecutionNeedsReturn(gas, init_off, init_size))
return Ok(InstructionResult::StopExecutionNeedsReturn {gas: gas, init_off: init_off, init_size: init_size, apply: true})
},
instructions::REVERT => {
let init_off = stack.pop_back();
let init_size = stack.pop_back();
return Ok(InstructionResult::StopExecutionNeedsReturn {gas: gas, init_off: init_off, init_size: init_size, apply: false})
},
instructions::STOP => {
return Ok(InstructionResult::StopExecution);

View File

@ -31,7 +31,7 @@ mod tests;
#[cfg(all(feature="benches", test))]
mod benches;
pub use self::evm::{Evm, Error, Finalize, GasLeft, Result, CostType};
pub use self::evm::{Evm, Error, Finalize, FinalizationResult, GasLeft, Result, CostType};
pub use self::ext::{Ext, ContractCreateResult, MessageCallResult, CreateContractAddress};
pub use self::factory::{Factory, VMType};
pub use self::schedule::Schedule;

View File

@ -24,6 +24,8 @@ pub struct Schedule {
pub have_delegate_call: bool,
/// Does it have a CREATE_P2SH instruction
pub have_create2: bool,
/// Does it have a REVERT instruction
pub have_revert: bool,
/// VM stack limit
pub stack_limit: usize,
/// Max number of nested calls/creates
@ -120,6 +122,7 @@ impl Schedule {
exceptional_failed_code_deposit: true,
have_delegate_call: true,
have_create2: have_metropolis_instructions,
have_revert: have_metropolis_instructions,
stack_limit: 1024,
max_depth: 1024,
tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0],
@ -171,6 +174,7 @@ impl Schedule {
exceptional_failed_code_deposit: efcd,
have_delegate_call: hdc,
have_create2: false,
have_revert: false,
stack_limit: 1024,
max_depth: 1024,
tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0],

View File

@ -64,7 +64,7 @@ pub struct FakeExt {
fn test_finalize(res: Result<GasLeft, evm::Error>) -> Result<U256, evm::Error> {
match res {
Ok(GasLeft::Known(gas)) => Ok(gas),
Ok(GasLeft::NeedsReturn(_, _)) => unimplemented!(), // since ret is unimplemented.
Ok(GasLeft::NeedsReturn{..}) => unimplemented!(), // since ret is unimplemented.
Err(e) => Err(e),
}
}

View File

@ -22,7 +22,7 @@ use engines::Engine;
use types::executed::CallType;
use env_info::EnvInfo;
use error::ExecutionError;
use evm::{self, Ext, Factory, Finalize, CreateContractAddress};
use evm::{self, Ext, Factory, Finalize, CreateContractAddress, FinalizationResult};
use externalities::*;
use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer};
use transaction::{Action, SignedTransaction};
@ -246,7 +246,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
output_policy: OutputPolicy,
tracer: &mut T,
vm_tracer: &mut V
) -> evm::Result<U256> where T: Tracer, V: VMTracer {
) -> evm::Result<FinalizationResult> where T: Tracer, V: VMTracer {
let depth_threshold = ::io::LOCAL_STACK_SIZE.with(|sz| sz.get() / STACK_SIZE_PER_DEPTH);
@ -366,9 +366,9 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
let traces = subtracer.traces();
match res {
Ok(ref gas_left) => tracer.trace_call(
Ok(ref res) => tracer.trace_call(
trace_info,
gas - *gas_left,
gas - res.gas_left,
trace_output,
traces
),
@ -379,7 +379,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
self.enact_result(&res, substate, unconfirmed_substate);
trace!(target: "executive", "enacted: substate={:?}\n", substate);
res
res.map(|r| r.gas_left)
} else {
// otherwise it's just a basic transaction, only do tracing, if necessary.
self.state.discard_checkpoint();
@ -438,9 +438,9 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
vm_tracer.done_subtrace(subvmtracer);
match res {
Ok(ref gas_left) => tracer.trace_create(
Ok(ref res) => tracer.trace_create(
trace_info,
gas - *gas_left,
gas - res.gas_left,
trace_output,
created,
subtracer.traces()
@ -449,7 +449,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
};
self.enact_result(&res, substate, unconfirmed_substate);
res
res.map(|r| r.gas_left)
}
/// Finalizes the transaction (does refunds and suicides).
@ -536,14 +536,15 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
}
}
fn enact_result(&mut self, result: &evm::Result<U256>, substate: &mut Substate, un_substate: Substate) {
fn enact_result(&mut self, result: &evm::Result<FinalizationResult>, substate: &mut Substate, un_substate: Substate) {
match *result {
Err(evm::Error::OutOfGas)
| Err(evm::Error::BadJumpDestination {..})
| Err(evm::Error::BadInstruction {.. })
| Err(evm::Error::StackUnderflow {..})
| Err(evm::Error::BuiltIn {..})
| Err(evm::Error::OutOfStack {..}) => {
| Err(evm::Error::OutOfStack {..})
| Ok(FinalizationResult { apply_state: false, .. }) => {
self.state.revert_to_checkpoint();
},
Ok(_) | Err(evm::Error::Internal(_)) => {
@ -560,7 +561,7 @@ mod tests {
use std::sync::Arc;
use ethkey::{Generator, Random};
use super::*;
use util::{H256, U256, U512, Address, Uint, FromHex, FromStr};
use util::{H256, U256, U512, Address, FromHex, FromStr};
use util::bytes::BytesRef;
use action_params::{ActionParams, ActionValue};
use env_info::EnvInfo;
@ -1242,11 +1243,43 @@ mod tests {
};
match result {
Err(_) => {
},
_ => {
panic!("Expected OutOfGas");
}
Err(_) => {},
_ => panic!("Expected OutOfGas"),
}
}
evm_test!{test_revert: test_revert_jit, test_revert_int}
fn test_revert(factory: Factory) {
let contract_address = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
// EIP-140 test case
let code = "6c726576657274656420646174616000557f726576657274206d657373616765000000000000000000000000000000000000600052600e6000fd".from_hex().unwrap();
let returns = "726576657274206d657373616765".from_hex().unwrap();
let mut state = get_temp_state();
state.add_balance(&sender, &U256::from_str("152d02c7e14af68000000").unwrap(), CleanupMode::NoEmpty).unwrap();
state.commit().unwrap();
let mut params = ActionParams::default();
params.address = contract_address.clone();
params.sender = sender.clone();
params.origin = sender.clone();
params.gas = U256::from(20025);
params.code = Some(Arc::new(code));
params.value = ActionValue::Transfer(U256::zero());
let mut state = get_temp_state();
state.add_balance(&sender, &U256::from_str("152d02c7e14af68000000").unwrap(), CleanupMode::NoEmpty).unwrap();
let info = EnvInfo::default();
let engine = TestEngine::new_metropolis();
let mut substate = Substate::new();
let mut output = [0u8; 14];
let result = {
let mut ex = Executive::new(&mut state, &info, &engine, &factory);
ex.call(params, &mut substate, BytesRef::Fixed(&mut output), &mut NoopTracer, &mut NoopVMTracer).unwrap()
};
assert_eq!(result, U256::from(1));
assert_eq!(output[..], returns[..]);
assert_eq!(state.storage_at(&contract_address, &H256::from(&U256::zero())).unwrap(), H256::from(&U256::from(0)));
}
}

View File

@ -254,9 +254,9 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec<String> {
match res {
Err(_) => fail_unless(out_of_gas, "didn't expect to run out of gas."),
Ok(gas_left) => {
Ok(res) => {
fail_unless(!out_of_gas, "expected to run out of gas.");
fail_unless(Some(gas_left) == vm.gas_left.map(Into::into), "gas_left is incorrect");
fail_unless(Some(res.gas_left) == vm.gas_left.map(Into::into), "gas_left is incorrect");
let vm_output: Option<Vec<u8>> = vm.output.map(Into::into);
fail_unless(Some(output) == vm_output, "output is incorrect");

View File

@ -29,6 +29,7 @@ lazy_static! {
pub static ref HOMESTEAD: Spec = ethereum::new_homestead_test();
pub static ref EIP150: Spec = ethereum::new_eip150_test();
pub static ref EIP161: Spec = ethereum::new_eip161_test();
pub static ref _METROPOLIS: Spec = ethereum::new_metropolis_test();
}
pub fn json_chain_test(json_data: &[u8]) -> Vec<String> {
@ -92,7 +93,6 @@ mod state_tests {
}
declare_test!{GeneralStateTest_stAttackTest, "GeneralStateTests/stAttackTest/"}
declare_test!{GeneralStateTest_stBlockHashTest, "GeneralStateTests/stBlockHashTest/"}
declare_test!{GeneralStateTest_stBoundsTest, "GeneralStateTests/stBoundsTest/"}
declare_test!{GeneralStateTest_stCallCodes, "GeneralStateTests/stCallCodes/"}
declare_test!{skip => [ "createJS_ExampleContract" ], GeneralStateTest_stCallCreateCallCodeTest, "GeneralStateTests/stCallCreateCallCodeTest/"}

View File

@ -24,7 +24,7 @@ use transient_hashmap::TransientHashMap;
use miner::{TransactionQueue, TransactionQueueDetailsProvider, TransactionImportResult, TransactionOrigin};
use miner::transaction_queue::QueuingInstant;
use error::{Error, TransactionError};
use util::{Uint, U256, H256, Address, Hashable};
use util::{U256, H256, Address, Hashable};
type Count = u16;
@ -215,7 +215,7 @@ mod tests {
use error::{Error, TransactionError};
use client::TransactionImportResult;
use miner::{TransactionQueue, TransactionOrigin};
use util::{Uint, U256, Address, FromHex, Hashable};
use util::{U256, Address, FromHex, Hashable};
use miner::transaction_queue::test::DummyTransactionDetailsProvider;
fn queue() -> BanningTransactionQueue {

View File

@ -1261,7 +1261,7 @@ mod tests {
use super::super::{MinerService, PrioritizationStrategy};
use super::*;
use block::IsBlock;
use util::{U256, Uint, FromHex};
use util::{U256, FromHex};
use ethkey::{Generator, Random};
use client::{BlockChainClient, TestBlockChainClient, EachBlockWith, TransactionImportResult};
use header::BlockNumber;

View File

@ -20,7 +20,7 @@ use types::ids::BlockId;
use futures::{future, Future};
use native_contracts::ServiceTransactionChecker as Contract;
use util::{U256, Uint, Mutex};
use util::{U256, Mutex};
const SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME: &'static str = "service_transaction_checker";

View File

@ -29,7 +29,7 @@
//! extern crate ethkey;
//! extern crate rustc_serialize;
//!
//! use util::{Uint, U256, Address};
//! use util::{U256, Address};
//! use ethkey::{Random, Generator};
//! use ethcore::miner::{TransactionQueue, RemovalReason, TransactionQueueDetailsProvider, AccountDetails, TransactionOrigin};
//! use ethcore::transaction::*;
@ -105,7 +105,7 @@ use std::cmp::Ordering;
use std::cmp;
use std::collections::{HashSet, HashMap, BTreeSet, BTreeMap};
use linked_hash_map::LinkedHashMap;
use util::{Address, H256, Uint, U256};
use util::{Address, H256, U256};
use util::table::Table;
use transaction::*;
use error::{Error, TransactionError};

View File

@ -29,7 +29,7 @@ use engines::Engine;
use header::Header;
use ids::BlockId;
use util::{Bytes, Hashable, HashDB, DBValue, snappy, U256, Uint};
use util::{Bytes, Hashable, HashDB, DBValue, snappy, U256};
use util::Mutex;
use util::hash::{H256};
use util::journaldb::{self, Algorithm, JournalDB};

View File

@ -40,7 +40,7 @@ const TRANSITION_BLOCK_1: usize = 2; // block at which the contract becomes acti
const TRANSITION_BLOCK_2: usize = 6; // block at which the second contract activates.
macro_rules! secret {
($e: expr) => { Secret::from_slice(&$e.sha3()).expect(format!("sha3({}) not valid secret.", $e).as_str()) }
($e: expr) => { Secret::from_slice(&$e.sha3()) }
}
lazy_static! {

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use util::{Address, H256, Uint, U256};
use util::{Address, H256, U256};
use util::sha3::SHA3_NULL_RLP;
use ethjson;
use super::seal::Seal;

View File

@ -61,6 +61,8 @@ pub struct CommonParams {
pub validate_receipts_transition: u64,
/// Number of first block where EIP-86 (Metropolis) rules begin.
pub eip86_transition: BlockNumber,
/// Number of first block where EIP-140 (Metropolis: REVERT opcode) rules begin.
pub eip140_transition: BlockNumber,
}
impl From<ethjson::spec::Params> for CommonParams {
@ -76,6 +78,7 @@ impl From<ethjson::spec::Params> for CommonParams {
eip98_transition: p.eip98_transition.map_or(0, Into::into),
validate_receipts_transition: p.validate_receipts_transition.map_or(0, Into::into),
eip86_transition: p.eip86_transition.map_or(BlockNumber::max_value(), Into::into),
eip140_transition: p.eip140_transition.map_or(BlockNumber::max_value(), Into::into),
}
}
}

View File

@ -56,6 +56,13 @@ impl TestEngine {
max_depth: max_depth,
}
}
pub fn new_metropolis() -> TestEngine {
TestEngine {
engine: ethereum::new_metropolis_test().engine,
max_depth: 0,
}
}
}
impl Engine for TestEngine {
@ -72,7 +79,7 @@ impl Engine for TestEngine {
}
fn schedule(&self, _block_number: u64) -> Schedule {
let mut schedule = Schedule::new_frontier();
let mut schedule = self.engine.schedule(0);
schedule.max_depth = self.max_depth;
schedule
}

View File

@ -19,7 +19,7 @@
use std::cmp::*;
use std::fmt;
use std::collections::BTreeMap;
use util::{U256, H256, Uint, Bytes};
use util::{U256, H256, Bytes};
use ipc::binary::BinaryConvertable;
#[derive(Debug, PartialEq, Eq, Clone)]

View File

@ -19,7 +19,7 @@
use std::ops::Deref;
use rlp::*;
use util::sha3::Hashable;
use util::{H256, Address, U256, Bytes, HeapSizeOf, Uint};
use util::{H256, Address, U256, Bytes, HeapSizeOf};
use ethkey::{Signature, Secret, Public, recover, public_to_address, Error as EthkeyError};
use error::*;
use evm::Schedule;

View File

@ -8,5 +8,5 @@ rust-crypto = "0.2.36"
tiny-keccak = "1.0"
eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1" }
ethkey = { path = "../ethkey" }
ethcore-bigint = "0.1.2"
ethcore-bigint = { path = "../util/bigint" }

View File

@ -10,7 +10,7 @@ tiny-keccak = "1.0"
eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1" }
rustc-serialize = "0.3"
docopt = { version = "0.7", optional = true }
ethcore-bigint = "0.1.2"
ethcore-bigint = { path = "../util/bigint" }
rust-crypto = "0.2"
byteorder = "1.0"

View File

@ -212,7 +212,7 @@ mod derivation {
use rcrypto::mac::Mac;
use rcrypto::sha2::Sha512;
use bigint::hash::{H512, H256};
use bigint::prelude::{U256, U512, Uint};
use bigint::prelude::{U256, U512};
use secp256k1::key::{SecretKey, PublicKey};
use SECP256K1;
use keccak;

View File

@ -54,7 +54,7 @@ impl Signature {
/// Parse bytes as a signature encoded as RSV (V in "Electrum" notation).
/// May return empty (invalid) signature if given data has invalid length.
pub fn from_electrum(data: &[u8]) -> Self {
if data.len() != 65 || data[0] < 27 {
if data.len() != 65 || data[64] < 27 {
// fallback to empty (invalid) signature
return Signature::default();
}

View File

@ -19,8 +19,8 @@ time = "0.1.34"
itertools = "0.5"
parking_lot = "0.4"
ethcrypto = { path = "../ethcrypto" }
ethcore-bigint = "0.1.2"
smallvec = "0.3.1"
ethcore-bigint = { path = "../util/bigint" }
smallvec = "0.4"
parity-wordlist = "1.0"
tempdir = "0.3"

View File

@ -32,7 +32,7 @@ use self::test::{Bencher, black_box};
use evm::run_vm;
use ethcore::action_params::ActionParams;
use ethcore_util::{U256, Uint};
use ethcore_util::U256;
use rustc_serialize::hex::FromHex;
#[bench]

View File

@ -30,7 +30,7 @@ use std::time::{Instant, Duration};
use std::fmt;
use std::str::FromStr;
use docopt::Docopt;
use util::{U256, FromHex, Uint, Bytes};
use util::{U256, FromHex, Bytes};
use ethcore::evm::{self, Factory, VMType, Finalize};
use ethcore::action_params::ActionParams;
@ -75,12 +75,12 @@ pub fn run_vm(params: ActionParams) -> Result<Success, Failure> {
let mut ext = ext::FakeExt::default();
let start = Instant::now();
let gas_left = vm.exec(params, &mut ext).finalize(ext);
let res = vm.exec(params, &mut ext).finalize(ext);
let duration = start.elapsed();
match gas_left {
Ok(gas_left) => Ok(Success {
gas_used: initial_gas - gas_left,
match res {
Ok(res) => Ok(Success {
gas_used: initial_gas - res.gas_left,
// TODO [ToDr] get output from ext
output: Vec::new(),
time: duration,

View File

@ -12,7 +12,7 @@ parking_lot = "0.4"
hidapi = { git = "https://github.com/paritytech/hidapi-rs" }
libusb = { git = "https://github.com/paritytech/libusb-rs" }
ethkey = { path = "../ethkey" }
ethcore-bigint = "0.1.2"
ethcore-bigint = { path = "../util/bigint" }
[dev-dependencies]
rustc-serialize = "0.3"

View File

@ -24,7 +24,7 @@ use std::str::FromStr;
use std::time::Duration;
use super::WalletInfo;
use ethkey::{Address, Signature};
use ethcore_bigint::hash::H256;
use bigint::hash::H256;
const LEDGER_VID: u16 = 0x2c97;
const LEDGER_PIDS: [u16; 2] = [0x0000, 0x0001]; // Nano S and Blue

View File

@ -20,7 +20,7 @@ extern crate parking_lot;
extern crate hidapi;
extern crate libusb;
extern crate ethkey;
extern crate ethcore_bigint;
extern crate ethcore_bigint as bigint;
#[macro_use] extern crate log;
#[cfg(test)] extern crate rustc_serialize;

View File

@ -13,7 +13,3 @@ ethcore-ipc-codegen = { path = "../ipc/codegen" }
semver = "0.6"
ethcore-ipc = { path = "../ipc/rpc" }
ethcore-util = { path = "../util" }
[profile.release]
debug = true
lto = false

View File

@ -1,6 +1,6 @@
{
"name": "parity.js",
"version": "1.7.82",
"version": "1.7.84",
"main": "release/index.js",
"jsnext:main": "src/index.js",
"author": "Parity Team <admin@parity.io>",

View File

@ -109,9 +109,9 @@ contract.at('0xa9280...7347b');
find & call a function
```javascript
contract.named
.callMe
.call({ gas: 21000 }, [true, 'someString']) // or estimateGas or sendTransaction
contract.instance
.myContractMethodName
.call({}, [myContractMethodParameter]) // or estimateGas or sendTransaction
.then((result) => {
console.log(`the result was ${result}`);
});

View File

@ -90,15 +90,14 @@ export default class Parity {
.execute('parity_consensusCapability');
}
dappsPort () {
dappsList () {
return this._transport
.execute('parity_dappsPort')
.then(outNumber);
.execute('parity_dappsList');
}
dappsInterface () {
dappsUrl () {
return this._transport
.execute('parity_dappsInterface');
.execute('parity_dappsUrl');
}
decryptMessage (address, data) {
@ -530,12 +529,6 @@ export default class Parity {
.execute('parity_setVaultMeta', vaultName, JSON.stringify(meta));
}
signerPort () {
return this._transport
.execute('parity_signerPort')
.then(outNumber);
}
signMessage (address, password, messageHash) {
return this._transport
.execute('parity_signMessage', inAddress(address), password, inHex(messageHash));
@ -567,4 +560,9 @@ export default class Parity {
return this._transport
.execute('parity_versionInfo');
}
wsUrl () {
return this._transport
.execute('parity_wsUrl');
}
}

File diff suppressed because one or more lines are too long

View File

@ -16,8 +16,6 @@
import React from 'react';
import { parityNode } from '@parity/shared/environment';
const styles = {
padding: '.5em',
border: '1px solid #777'
@ -34,7 +32,7 @@ export default (address) => {
return (
<img
src={ `${parityNode}/api/content/${address.replace(/^0x/, '')}` }
src={ `/api/content/${address.replace(/^0x/, '')}` }
alt={ address }
style={ styles }
/>

View File

@ -30,7 +30,6 @@ import styles from './token.css';
import { metaDataKeys } from '../../constants';
import { api } from '../../parity';
import { parityNode } from '@parity/shared/environment';
export default class Token extends Component {
static propTypes = {
@ -312,7 +311,7 @@ export default class Token extends Component {
</span> meta-data:
</p>
<div className={ styles['meta-image'] }>
<img src={ `${parityNode}/api/content/${imageHash}/` } />
<img src={ `/api/content/${imageHash}/` } />
</div>
</div>
);

View File

@ -143,25 +143,34 @@ export default {
}
},
dappsPort: {
section: SECTION_NODE,
desc: 'Returns the port the dapps are running on, error if not enabled.',
dappsList: {
subdoc: SUBDOC_SET,
desc: 'Returns a list of available local dapps.',
params: [],
returns: {
type: Quantity,
desc: 'The port number',
example: 8080
type: Array,
desc: 'The list of dapps',
example: [
{
author: 'Parity Technologies Ltd',
description: 'A skeleton dapp',
iconUrl: 'title.png',
id: 'skeleton',
name: 'Skeleton',
version: '0.1'
}
]
}
},
dappsInterface: {
dappsUrl: {
section: SECTION_NODE,
desc: 'Returns the interface the dapps are running on, error if not enabled.',
desc: 'Returns the hostname and the port of dapps/rpc server, error if not enabled.',
params: [],
returns: {
type: String,
desc: 'The interface',
example: '127.0.0.1'
desc: 'The hostname and port number',
example: 'localhost:8545'
}
},
@ -788,17 +797,6 @@ export default {
}
},
signerPort: {
section: SECTION_NODE,
desc: 'Returns the port the signer is running on, error if not enabled',
params: [],
returns: {
type: Quantity,
desc: 'The port number',
example: 8180
}
},
transactionsLimit: {
section: SECTION_MINING,
desc: 'Changes limit for transactions in queue.',
@ -1916,6 +1914,17 @@ export default {
}
},
wsUrl: {
section: SECTION_NODE,
desc: 'Returns the hostname and the port of WebSockets/Signer server, error if not enabled.',
params: [],
returns: {
type: String,
desc: 'The hostname and port number',
example: 'localhost:8546'
}
},
composeTransaction: {
desc: 'Given partial transaction request produces transaction with all fields filled in. Such transaction can be then signed externally.',
params: [
@ -1997,4 +2006,5 @@ export default {
example: 'QmSbFjqjd6nFwNHqsBCC7SK8GShGcayLUEtysJjNGhZAnC'
}
}
};

View File

@ -27,21 +27,28 @@ export default class SecureApi extends Api {
_needsToken = false;
_tokens = [];
_dappsInterface = null;
_dappsPort = 8545;
_signerPort = 8180;
_dappsUrl = null;
_wsUrl = null;
static getTransport (url, sysuiToken) {
return new Api.Transport.Ws(url, sysuiToken, false);
static getTransport (url, sysuiToken, protocol) {
const proto = protocol() === 'https:' ? 'wss:' : 'ws:';
return new Api.Transport.Ws(`${proto}//${url}`, sysuiToken, false);
}
constructor (url, nextToken, getTransport = SecureApi.getTransport) {
// Returns a protocol with `:` at the end.
static protocol () {
return window.location.protocol;
}
constructor (url, nextToken, getTransport = SecureApi.getTransport, protocol = SecureApi.protocol) {
const sysuiToken = store.get('sysuiToken');
const transport = getTransport(url, sysuiToken);
const transport = getTransport(url, sysuiToken, protocol);
super(transport);
this._url = url;
this._wsUrl = url;
this.protocol = protocol;
// Try tokens from localStorage, from hash and 'initial'
this._tokens = uniq([sysuiToken, nextToken, 'initial'])
.filter((token) => token)
@ -53,12 +60,30 @@ export default class SecureApi extends Api {
this.connect();
}
get _dappsAddress () {
if (!this._dappsUrl) {
return {
host: null,
port: 8545
};
}
const [host, port] = this._dappsUrl.split(':');
return {
host,
port: parseInt(port, 10)
};
}
get dappsPort () {
return this._dappsPort;
return this._dappsAddress.port;
}
get dappsUrl () {
return `http://${this.hostname}:${this.dappsPort}`;
const { port } = this._dappsAddress;
return `${this.protocol()}//${this.hostname}:${port}`;
}
get hostname () {
@ -66,15 +91,13 @@ export default class SecureApi extends Api {
return 'dapps.parity';
}
if (!this._dappsInterface || this._dappsInterface === '0.0.0.0') {
const { host } = this._dappsAddress;
if (!host || host === '0.0.0.0') {
return window.location.hostname;
}
return this._dappsInterface;
}
get signerPort () {
return this._signerPort;
return host;
}
get isConnecting () {
@ -98,18 +121,18 @@ export default class SecureApi extends Api {
* (`signerPort`, `dappsInterface`, `dappsPort`, ...)
*/
configure (configuration) {
const { dappsInterface, dappsPort, signerPort } = configuration;
const { dappsInterface, dappsPort, signerPort, wsPort } = configuration;
if (dappsInterface) {
this._dappsInterface = dappsInterface;
this._dappsUrl = `${dappsInterface}:${this._dappsAddress.port}`;
}
if (dappsPort) {
this._dappsPort = dappsPort;
this._dappsUrl = `${this.hostname}:${dappsPort}`;
}
if (signerPort) {
this._signerPort = signerPort;
if (signerPort || wsPort) {
this._wsUrl = `${this.hostname}:${signerPort || wsPort}`;
}
}
@ -166,9 +189,7 @@ export default class SecureApi extends Api {
* otherwise (HEAD request to the Node)
*/
isNodeUp () {
const url = this._url.replace(/wss?/, 'http');
return fetch(url, { method: 'HEAD' })
return fetch(`${this.protocol()}//${this._wsUrl}`, { method: 'HEAD', mode: 'no-cors' })
.then(
(r) => r.status === 200,
() => false
@ -297,14 +318,12 @@ export default class SecureApi extends Api {
_fetchSettings () {
return Promise
.all([
this.parity.dappsPort(),
this.parity.dappsInterface(),
this.parity.signerPort()
this.parity.dappsUrl(),
this.parity.wsUrl()
])
.then(([dappsPort, dappsInterface, signerPort]) => {
this._dappsPort = dappsPort.toNumber();
this._dappsInterface = dappsInterface;
this._signerPort = signerPort.toNumber();
.then(([dappsUrl, wsUrl]) => {
this._dappsUrl = dappsUrl;
this._wsUrl = dappsUrl;
});
}

View File

@ -19,14 +19,4 @@
import './tests';
const parityNode = (
process.env.PARITY_URL && `http://${process.env.PARITY_URL}`
) || (
process.env.NODE_ENV === 'production'
? 'http://127.0.0.1:8545'
: ''
);
export {
parityNode
};
export {};

Some files were not shown because too many files have changed in this diff Show More