Merge branch 'master' into ui-2
This commit is contained in:
commit
45c6cc6ac0
22
.dockerignore
Normal file
22
.dockerignore
Normal 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
|
@ -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
|
||||
|
28
CHANGELOG.md
28
CHANGELOG.md
@ -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
363
Cargo.lock
generated
@ -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"
|
||||
|
@ -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]
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -21,4 +21,3 @@ mod response;
|
||||
mod types;
|
||||
|
||||
pub use self::api::RestApi;
|
||||
pub use self::types::App;
|
||||
|
@ -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");
|
||||
|
@ -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
55
dapps/src/apps/app.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
@ -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(),
|
||||
|
@ -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,
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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
55
dapps/src/apps/ui.rs
Normal 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(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
139
dapps/src/lib.rs
139
dapps/src/lib.rs
@ -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(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)))
|
||||
}
|
||||
|
@ -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()],
|
||||
|
@ -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()),
|
||||
}
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
3
docker/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
Usage
|
||||
|
||||
```docker build -f docker/ubuntu/Dockerfile --tag ethcore/parity:branch_or_tag_name .```
|
@ -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
|
||||
|
||||
|
@ -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"]
|
@ -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 .```
|
@ -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'\
|
||||
|
@ -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'\
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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"]
|
@ -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
|
||||
|
@ -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]
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
279
ethcore/light/src/net/load_timer.rs
Normal file
279
ethcore/light/src/net/load_timer.rs
Normal 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);
|
||||
}
|
||||
}
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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 }
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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::*;
|
||||
|
@ -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.
|
||||
|
@ -16,3 +16,4 @@ native-contract-generator = { path = "generator" }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
test_contracts = []
|
||||
|
@ -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)]
|
||||
|
51
ethcore/res/ethereum/metropolis_test.json
Normal file
51
ethcore/res/ethereum/metropolis_test.json
Normal 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 } } } }
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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};
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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::*;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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")) }
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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)]
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 => {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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],
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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/"}
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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};
|
||||
|
@ -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};
|
||||
|
@ -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! {
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)]
|
||||
|
@ -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;
|
||||
|
@ -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" }
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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,
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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>",
|
||||
|
@ -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}`);
|
||||
});
|
||||
|
@ -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
@ -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 }
|
||||
/>
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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'
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user