Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0feb0bb6e7 | ||
|
|
3b5a8d5d69 | ||
|
|
aca9f13d45 | ||
|
|
e09bef98fb | ||
|
|
ceb590a360 | ||
|
|
75c0db2b15 | ||
|
|
70b42345c5 | ||
|
|
a42d780d02 | ||
|
|
582fa8ce45 | ||
|
|
73be0fb096 | ||
|
|
627d1a4971 | ||
|
|
a7807106f5 | ||
|
|
33b39f0725 | ||
|
|
53ec1141cf | ||
|
|
145229d46d | ||
|
|
568dc33a02 | ||
|
|
cf10450108 | ||
|
|
fe779686ca | ||
|
|
58c1dbe322 | ||
|
|
14b578832d | ||
|
|
e961398393 | ||
|
|
0fad2a6d8c | ||
|
|
f3bcada7b9 | ||
|
|
b814f1ccbf | ||
|
|
cad91df2b8 | ||
|
|
50a58e1ae8 | ||
|
|
1e36fc5d0f | ||
|
|
fa6a0a6b60 | ||
|
|
a8fc42d282 | ||
|
|
c6685a7f57 | ||
|
|
736a8c40f0 | ||
|
|
5f74f8c265 | ||
|
|
97ed569588 | ||
|
|
6766ef988d | ||
|
|
8a87cfb893 | ||
|
|
54aebdcb45 | ||
|
|
86a6145d76 | ||
|
|
718020b64b | ||
|
|
8c36a56365 | ||
|
|
7bccaa5c15 | ||
|
|
98ec46fff6 | ||
|
|
8dc584ece9 | ||
|
|
63d154dad3 | ||
|
|
0030bb4f1d |
@@ -1,3 +0,0 @@
|
||||
[target.x86_64-pc-windows-msvc]
|
||||
# Link the C runtime statically ; https://github.com/paritytech/parity-ethereum/issues/6643
|
||||
rustflags = ["-Ctarget-feature=+crt-static"]
|
||||
@@ -9,7 +9,7 @@ trim_trailing_whitespace=true
|
||||
max_line_length=120
|
||||
insert_final_newline=true
|
||||
|
||||
[*.{yml,sh}]
|
||||
[.travis.yml]
|
||||
indent_style=space
|
||||
indent_size=2
|
||||
tab_width=8
|
||||
|
||||
14
.github/CONTRIBUTING.md
vendored
14
.github/CONTRIBUTING.md
vendored
@@ -2,7 +2,7 @@
|
||||
|
||||
## Do you have a question?
|
||||
|
||||
Check out our [Basic Usage](https://wiki.parity.io/Basic-Usage), [Configuration](https://wiki.parity.io/Configuring-Parity-Ethereum), and [FAQ](https://wiki.parity.io/FAQ) articles on our [wiki](https://wiki.parity.io/)!
|
||||
Check out our [Basic Usage](https://github.com/paritytech/parity/wiki/Basic-Usage), [Configuration](https://github.com/paritytech/parity/wiki/Configuring-Parity), and [FAQ](https://github.com/paritytech/parity/wiki/FAQ) articles on our [wiki](https://github.com/paritytech/parity/wiki)!
|
||||
|
||||
See also frequently asked questions [tagged with `parity`](https://ethereum.stackexchange.com/questions/tagged/parity?sort=votes&pageSize=50) on Stack Exchange.
|
||||
|
||||
@@ -10,11 +10,11 @@ See also frequently asked questions [tagged with `parity`](https://ethereum.stac
|
||||
|
||||
Do **not** open an issue on Github if you think your discovered bug could be a **security-relevant vulnerability**. Please, read our [security policy](../SECURITY.md) instead.
|
||||
|
||||
Otherwise, just create a [new issue](https://github.com/paritytech/parity-ethereum/issues/new) in our repository and state:
|
||||
Otherwise, just create a [new issue](https://github.com/paritytech/parity/issues/new) in our repository and state:
|
||||
|
||||
- What's your Parity Ethereum version?
|
||||
- What's your Parity version?
|
||||
- What's your operating system and version?
|
||||
- How did you install Parity Ethereum?
|
||||
- How did you install parity?
|
||||
- Is your node fully synchronized?
|
||||
- Did you try turning it off and on again?
|
||||
|
||||
@@ -22,12 +22,12 @@ Also, try to include **steps to reproduce** the issue and expand on the **actual
|
||||
|
||||
## Contribute!
|
||||
|
||||
If you would like to contribute to Parity Ethereum, please **fork it**, fix bugs or implement features, and [propose a pull request](https://github.com/paritytech/parity-ethereum/compare).
|
||||
If you would like to contribute to Parity, please **fork it**, fix bugs or implement features, and [propose a pull request](https://github.com/paritytech/parity/compare).
|
||||
|
||||
Please, refer to the [Coding Guide](https://wiki.parity.io/Coding-guide) in our wiki for more details about hacking on Parity.
|
||||
Please, refer to the [Coding Guide](https://github.com/paritytech/parity/wiki/Coding-guide) in our wiki for more details about hacking on Parity.
|
||||
|
||||
## License.
|
||||
|
||||
By contributing to Parity Ethereum, you agree that your contributions will be licensed under the [GPLv3 License](../LICENSE).
|
||||
By contributing to Parity, you agree that your contributions will be licensed under the [GPLv3 License](../LICENSE).
|
||||
|
||||
Each contributor has to sign our Contributor License Agreement. The purpose of the CLA is to ensure that the guardian of a project's outputs has the necessary ownership or grants of rights over all contributions to allow them to distribute under the chosen license. You can read and sign our full Contributor License Agreement at [cla.parity.io](https://cla.parity.io) before submitting a pull request.
|
||||
|
||||
14
.github/ISSUE_TEMPLATE.md
vendored
14
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,11 +1,13 @@
|
||||
_Before filing a new issue, please **provide the following information**._
|
||||
|
||||
- **Parity Ethereum version**: 0.0.0
|
||||
- **Operating system**: Windows / MacOS / Linux
|
||||
- **Installation**: homebrew / one-line installer / built from source
|
||||
- **Fully synchronized**: no / yes
|
||||
- **Network**: ethereum / ropsten / kovan / ...
|
||||
- **Restarted**: no / yes
|
||||
> I'm running:
|
||||
>
|
||||
> - **Which Parity version?**: 0.0.0
|
||||
> - **Which operating system?**: Windows / MacOS / Linux
|
||||
> - **How installed?**: via installer / homebrew / binaries / from source
|
||||
> - **Are you fully synchronized?**: no / yes
|
||||
> - **Which network are you connected to?**: ethereum / ropsten / kovan / ...
|
||||
> - **Did you try to restart the node?**: no / yes
|
||||
|
||||
_Your issue description goes here below. Try to include **actual** vs. **expected behavior** and **steps to reproduce** the issue._
|
||||
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -38,8 +38,7 @@ node_modules
|
||||
|
||||
# Build artifacts
|
||||
out/
|
||||
parity-clib-examples/cpp/build/
|
||||
|
||||
.vscode
|
||||
rls/
|
||||
|
||||
/parity.*
|
||||
|
||||
427
.gitlab-ci.yml
427
.gitlab-ci.yml
@@ -1,218 +1,269 @@
|
||||
stages:
|
||||
- test
|
||||
- js-build
|
||||
- push-release
|
||||
- build
|
||||
- publish
|
||||
- optional
|
||||
|
||||
image: parity/rust-parity-ethereum-build:xenial
|
||||
variables:
|
||||
GIT_STRATEGY: fetch
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
CI_SERVER_NAME: "GitLab CI"
|
||||
CARGO_HOME: "${CI_PROJECT_DIR}/.cargo"
|
||||
CARGO_TARGET: x86_64-unknown-linux-gnu
|
||||
|
||||
.no_git: &no_git
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
GIT_SUBMODULE_STRATEGY: none
|
||||
|
||||
|
||||
.releaseable_branches: # list of git refs for building GitLab artifacts (think "pre-release binaries")
|
||||
only: &releaseable_branches
|
||||
RUST_BACKTRACE: "1"
|
||||
RUSTFLAGS: ""
|
||||
CARGOFLAGS: ""
|
||||
CI_SERVER_NAME: "GitLab CI"
|
||||
LIBSSL: "libssl1.0.0 (>=1.0.0)"
|
||||
cache:
|
||||
key: "$CI_BUILD_STAGE-$CI_BUILD_REF_NAME"
|
||||
paths:
|
||||
- target
|
||||
untracked: true
|
||||
linux-stable:
|
||||
stage: build
|
||||
image: parity/rust:gitlab-ci
|
||||
only:
|
||||
- stable
|
||||
- beta
|
||||
- tags
|
||||
- schedules
|
||||
|
||||
|
||||
.collect_artifacts: &collect_artifacts
|
||||
- triggers
|
||||
script:
|
||||
- rustup default stable
|
||||
# ARGUMENTS: 1. BUILD_PLATFORM (target for binaries) 2. PLATFORM (target for cargo) 3. ARC (architecture) 4. & 5. CC & CXX flags
|
||||
- scripts/gitlab-build.sh x86_64-unknown-linux-gnu x86_64-unknown-linux-gnu amd64 gcc g++
|
||||
tags:
|
||||
- rust-stable
|
||||
artifacts:
|
||||
name: "${CI_JOB_NAME}_${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}"
|
||||
when: on_success
|
||||
expire_in: 1 mos
|
||||
paths:
|
||||
- artifacts/
|
||||
|
||||
test-linux:
|
||||
stage: test
|
||||
variables:
|
||||
RUN_TESTS: all
|
||||
- parity.zip
|
||||
name: "stable-x86_64-unknown-linux-gnu_parity"
|
||||
linux-stable-debian:
|
||||
stage: build
|
||||
image: parity/rust-debian:gitlab-ci
|
||||
only:
|
||||
- stable
|
||||
- beta
|
||||
- tags
|
||||
- triggers
|
||||
script:
|
||||
- scripts/gitlab/test-all.sh
|
||||
- sccache -s
|
||||
- export LIBSSL="libssl1.1 (>=1.1.0)"
|
||||
- scripts/gitlab-build.sh x86_64-unknown-debian-gnu x86_64-unknown-linux-gnu amd64 gcc g++
|
||||
tags:
|
||||
- linux-docker
|
||||
|
||||
test-audit:
|
||||
stage: test
|
||||
- rust-debian
|
||||
artifacts:
|
||||
paths:
|
||||
- parity.zip
|
||||
name: "stable-x86_64-unknown-debian-gnu_parity"
|
||||
linux-centos:
|
||||
stage: build
|
||||
image: parity/rust-centos:gitlab-ci
|
||||
only:
|
||||
- stable
|
||||
- beta
|
||||
- tags
|
||||
- triggers
|
||||
script:
|
||||
- set -e
|
||||
- set -u
|
||||
- cargo audit
|
||||
- scripts/gitlab-build.sh x86_64-unknown-centos-gnu x86_64-unknown-linux-gnu x86_64 gcc g++
|
||||
tags:
|
||||
- linux-docker
|
||||
|
||||
build-linux: &build-linux
|
||||
stage: build
|
||||
only: *releaseable_branches
|
||||
- rust-centos
|
||||
artifacts:
|
||||
paths:
|
||||
- parity.zip
|
||||
name: "x86_64-unknown-centos-gnu_parity"
|
||||
linux-i686:
|
||||
stage: build
|
||||
image: parity/rust-i686:gitlab-ci
|
||||
only:
|
||||
- stable
|
||||
- beta
|
||||
- tags
|
||||
- triggers
|
||||
script:
|
||||
- scripts/gitlab/build-unix.sh
|
||||
- sccache -s
|
||||
<<: *collect_artifacts
|
||||
- scripts/gitlab-build.sh i686-unknown-linux-gnu i686-unknown-linux-gnu i386 gcc g++
|
||||
tags:
|
||||
- linux-docker
|
||||
|
||||
build-linux-i386:
|
||||
<<: *build-linux
|
||||
image: parity/rust-parity-ethereum-build:i386
|
||||
variables:
|
||||
CARGO_TARGET: i686-unknown-linux-gnu
|
||||
|
||||
build-linux-arm64:
|
||||
<<: *build-linux
|
||||
image: parity/rust-parity-ethereum-build:arm64
|
||||
variables:
|
||||
CARGO_TARGET: aarch64-unknown-linux-gnu
|
||||
|
||||
build-linux-armhf:
|
||||
<<: *build-linux
|
||||
image: parity/rust-parity-ethereum-build:armhf
|
||||
variables:
|
||||
CARGO_TARGET: armv7-unknown-linux-gnueabihf
|
||||
|
||||
build-darwin:
|
||||
stage: build
|
||||
only: *releaseable_branches
|
||||
variables:
|
||||
CARGO_TARGET: x86_64-apple-darwin
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
- rust-i686
|
||||
artifacts:
|
||||
paths:
|
||||
- parity.zip
|
||||
name: "i686-unknown-linux-gnu"
|
||||
linux-armv7:
|
||||
stage: build
|
||||
image: parity/rust-armv7:gitlab-ci
|
||||
only:
|
||||
- stable
|
||||
- beta
|
||||
- tags
|
||||
- triggers
|
||||
script:
|
||||
- scripts/gitlab/build-unix.sh
|
||||
- scripts/gitlab-build.sh armv7-unknown-linux-gnueabihf armv7-unknown-linux-gnueabihf armhf arm-linux-gnueabihf-gcc arm-linux-gnueabihf-g++
|
||||
tags:
|
||||
- rust-osx
|
||||
<<: *collect_artifacts
|
||||
|
||||
build-windows:
|
||||
stage: build
|
||||
only: *releaseable_branches
|
||||
variables:
|
||||
CARGO_TARGET: x86_64-pc-windows-msvc
|
||||
- rust-arm
|
||||
artifacts:
|
||||
paths:
|
||||
- parity.zip
|
||||
name: "armv7_unknown_linux_gnueabihf_parity"
|
||||
linux-arm:
|
||||
stage: build
|
||||
image: parity/rust-arm:gitlab-ci
|
||||
only:
|
||||
- stable
|
||||
- beta
|
||||
- tags
|
||||
- triggers
|
||||
script:
|
||||
- sh scripts/gitlab/build-windows.sh
|
||||
- scripts/gitlab-build.sh arm-unknown-linux-gnueabihf arm-unknown-linux-gnueabihf armhf arm-linux-gnueabihf-gcc arm-linux-gnueabihf-g++
|
||||
tags:
|
||||
- rust-arm
|
||||
artifacts:
|
||||
paths:
|
||||
- parity.zip
|
||||
name: "arm-unknown-linux-gnueabihf_parity"
|
||||
linux-aarch64:
|
||||
stage: build
|
||||
image: parity/rust-arm64:gitlab-ci
|
||||
only:
|
||||
- stable
|
||||
- beta
|
||||
- tags
|
||||
- triggers
|
||||
script:
|
||||
- scripts/gitlab-build.sh aarch64-unknown-linux-gnu aarch64-unknown-linux-gnu arm64 aarch64-linux-gnu-gcc aarch64-linux-gnu-g++
|
||||
tags:
|
||||
- rust-arm
|
||||
artifacts:
|
||||
paths:
|
||||
- parity.zip
|
||||
name: "aarch64-unknown-linux-gnu_parity"
|
||||
linux-snap:
|
||||
stage: build
|
||||
image: parity/snapcraft:gitlab-ci
|
||||
only:
|
||||
- stable
|
||||
- beta
|
||||
- tags
|
||||
- triggers
|
||||
script:
|
||||
- scripts/gitlab-build.sh x86_64-unknown-snap-gnu x86_64-unknown-linux-gnu amd64 gcc g++
|
||||
tags:
|
||||
- rust-stable
|
||||
artifacts:
|
||||
paths:
|
||||
- snap/parity.zip
|
||||
name: "stable-x86_64-unknown-snap-gnu_parity"
|
||||
allow_failure: true
|
||||
darwin:
|
||||
stage: build
|
||||
only:
|
||||
- stable
|
||||
- beta
|
||||
- tags
|
||||
- triggers
|
||||
script:
|
||||
- scripts/gitlab-build.sh x86_64-apple-darwin x86_64-apple-darwin macos gcc g++
|
||||
tags:
|
||||
- osx
|
||||
artifacts:
|
||||
paths:
|
||||
- parity.zip
|
||||
name: "x86_64-apple-darwin_parity"
|
||||
windows:
|
||||
cache:
|
||||
key: "%CI_BUILD_STAGE%-%CI_BUILD_REF_NAME%"
|
||||
untracked: true
|
||||
stage: build
|
||||
only:
|
||||
- stable
|
||||
- beta
|
||||
- tags
|
||||
- triggers
|
||||
script:
|
||||
- sh scripts/gitlab-build.sh x86_64-pc-windows-msvc x86_64-pc-windows-msvc installer "" "" ""
|
||||
tags:
|
||||
- rust-windows
|
||||
<<: *collect_artifacts
|
||||
|
||||
publish-docker:
|
||||
stage: publish
|
||||
only: *releaseable_branches
|
||||
cache: {}
|
||||
dependencies:
|
||||
- build-linux
|
||||
tags:
|
||||
- shell
|
||||
artifacts:
|
||||
paths:
|
||||
- parity.zip
|
||||
name: "x86_64-pc-windows-msvc_parity"
|
||||
docker-build:
|
||||
stage: build
|
||||
only:
|
||||
- stable
|
||||
- beta
|
||||
- tags
|
||||
- triggers
|
||||
before_script:
|
||||
- docker info
|
||||
script:
|
||||
- scripts/gitlab/publish-docker.sh parity
|
||||
|
||||
publish-snap: &publish-snap
|
||||
stage: publish
|
||||
only: *releaseable_branches
|
||||
image: snapcore/snapcraft
|
||||
variables:
|
||||
BUILD_ARCH: amd64
|
||||
cache: {}
|
||||
dependencies:
|
||||
- build-linux
|
||||
- DOCKER_TAG=$CI_BUILD_REF_NAME
|
||||
- echo "Tag:" $DOCKER_TAG
|
||||
- docker login -u $Docker_Hub_User_Parity -p $Docker_Hub_Pass_Parity
|
||||
- scripts/docker-build.sh $DOCKER_TAG
|
||||
- docker logout
|
||||
tags:
|
||||
- linux-docker
|
||||
- docker
|
||||
test-coverage:
|
||||
stage: test
|
||||
only:
|
||||
- master
|
||||
script:
|
||||
- scripts/gitlab/publish-snap.sh
|
||||
<<: *collect_artifacts
|
||||
|
||||
publish-snap-i386:
|
||||
<<: *publish-snap
|
||||
variables:
|
||||
BUILD_ARCH: i386
|
||||
dependencies:
|
||||
- build-linux-i386
|
||||
|
||||
publish-snap-arm64:
|
||||
<<: *publish-snap
|
||||
variables:
|
||||
BUILD_ARCH: arm64
|
||||
dependencies:
|
||||
- build-linux-arm64
|
||||
|
||||
publish-snap-armhf:
|
||||
<<: *publish-snap
|
||||
variables:
|
||||
BUILD_ARCH: armhf
|
||||
dependencies:
|
||||
- build-linux-armhf
|
||||
|
||||
publish-onchain:
|
||||
stage: publish
|
||||
only: *releaseable_branches
|
||||
cache: {}
|
||||
dependencies:
|
||||
- build-linux
|
||||
- build-darwin
|
||||
- build-windows
|
||||
script:
|
||||
- scripts/gitlab/publish-onchain.sh
|
||||
- scripts/gitlab-test.sh test-coverage
|
||||
tags:
|
||||
- linux-docker
|
||||
|
||||
publish-awss3-release:
|
||||
image: parity/awscli:latest
|
||||
stage: publish
|
||||
only: *releaseable_branches
|
||||
<<: *no_git
|
||||
cache: {}
|
||||
dependencies:
|
||||
- build-linux
|
||||
- build-darwin
|
||||
- build-windows
|
||||
- kcov
|
||||
allow_failure: true
|
||||
test-rust-stable:
|
||||
stage: test
|
||||
image: parity/rust:gitlab-ci
|
||||
script:
|
||||
- echo "__________Push binaries to AWS S3____________"
|
||||
- case "${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}" in
|
||||
(beta|stable|nightly)
|
||||
export BUCKET=releases.parity.io/ethereum;
|
||||
;;
|
||||
(*)
|
||||
export BUCKET=builds-parity;
|
||||
;;
|
||||
esac
|
||||
- aws s3 sync ./artifacts s3://${BUCKET}/${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}/
|
||||
after_script:
|
||||
- aws s3 ls s3://${BUCKET}/${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}/
|
||||
--recursive --human-readable --summarize
|
||||
- scripts/gitlab-test.sh stable
|
||||
tags:
|
||||
- linux-docker
|
||||
|
||||
publish-docs:
|
||||
stage: publish
|
||||
# <<: *no_git
|
||||
- rust-stable
|
||||
test-rust-beta:
|
||||
stage: test
|
||||
only:
|
||||
- triggers
|
||||
- master
|
||||
image: parity/rust:gitlab-ci
|
||||
script:
|
||||
- scripts/gitlab-test.sh beta
|
||||
tags:
|
||||
- rust-beta
|
||||
allow_failure: true
|
||||
test-rust-nightly:
|
||||
stage: test
|
||||
only:
|
||||
- triggers
|
||||
- master
|
||||
image: parity/rust:gitlab-ci
|
||||
script:
|
||||
- scripts/gitlab-test.sh nightly
|
||||
tags:
|
||||
- rust
|
||||
- rust-nightly
|
||||
allow_failure: true
|
||||
js-test:
|
||||
stage: test
|
||||
image: parity/rust:gitlab-ci
|
||||
script:
|
||||
- scripts/gitlab-test.sh js-test
|
||||
tags:
|
||||
- rust-stable
|
||||
js-release:
|
||||
stage: js-build
|
||||
only:
|
||||
- master
|
||||
- stable
|
||||
- beta
|
||||
- tags
|
||||
- triggers
|
||||
image: parity/rust:gitlab-ci
|
||||
script:
|
||||
- scripts/gitlab-test.sh js-release
|
||||
tags:
|
||||
- javascript
|
||||
push-release:
|
||||
stage: push-release
|
||||
only:
|
||||
- tags
|
||||
except:
|
||||
- nightly
|
||||
cache: {}
|
||||
- triggers
|
||||
image: parity/rust:gitlab-ci
|
||||
script:
|
||||
- scripts/gitlab/publish-docs.sh
|
||||
- rustup default stable
|
||||
- curl --data "secret=$RELEASES_SECRET" http://update.parity.io:1337/push-release/$CI_BUILD_REF_NAME/$CI_BUILD_REF
|
||||
- curl --data "secret=$RELEASES_SECRET" http://update.parity.io:1338/push-release/$CI_BUILD_REF_NAME/$CI_BUILD_REF
|
||||
tags:
|
||||
- linux-docker
|
||||
|
||||
build-android:
|
||||
stage: optional
|
||||
image: parity/rust-android:gitlab-ci
|
||||
variables:
|
||||
CARGO_TARGET: armv7-linux-androideabi
|
||||
script:
|
||||
- scripts/gitlab/build-unix.sh
|
||||
tags:
|
||||
- linux-docker
|
||||
allow_failure: true
|
||||
<<: *collect_artifacts
|
||||
- curl
|
||||
|
||||
531
CHANGELOG.md
531
CHANGELOG.md
@@ -1,174 +1,371 @@
|
||||
## Parity-Ethereum [v2.3.0](https://github.com/paritytech/parity-ethereum/releases/tag/v2.3.0) (2019-01-16)
|
||||
## Parity [v1.8.5](https://github.com/paritytech/parity/releases/tag/v1.8.5) (2017-12-29)
|
||||
|
||||
Parity-Ethereum 2.3.0-beta is a consensus-relevant security release that reverts Constantinople on the Ethereum network. Upgrading is mandatory for Ethereum, and strongly recommended for other networks.
|
||||
Parity 1.8.5 changes the default behavior of JSON-RPC CORS setting, detects same-key engine signers in Aura networks, and updates bootnodes for the Kovan and Foundation networks.
|
||||
|
||||
- **Consensus** - Ethereum Network: Pull Constantinople protocol upgrade on Ethereum (#10189)
|
||||
- Read more: [Security Alert: Ethereum Constantinople Postponement](https://blog.ethereum.org/2019/01/15/security-alert-ethereum-constantinople-postponement/)
|
||||
- **Networking** - All networks: Ping nodes from discovery (#10167)
|
||||
- **Wasm** - Kovan Network: Update pwasm-utils to 0.6.1 (#10134)
|
||||
|
||||
Other notable changes:
|
||||
|
||||
- Existing blocks in the database are now kept when restoring a Snapshot. (#8643)
|
||||
- Block and transaction propagation is improved significantly. (#9954)
|
||||
- The ERC-191 Signed Data Standard is now supported by `personal_sign191`. (#9701)
|
||||
- Add support for ERC-191/712 `eth_signTypedData` as a standard for machine-verifiable and human-readable typed data signing with Ethereum keys. (#9631)
|
||||
- Add support for ERC-1186 `eth_getProof` (#9001)
|
||||
- Add experimental RPCs flag to enable ERC-191, ERC-712, and ERC-1186 APIs via `--jsonrpc-experimental` (#9928)
|
||||
- Make `CALLCODE` to trace value to be the code address. (#9881)
|
||||
|
||||
Configuration changes:
|
||||
|
||||
- The EIP-98 transition is now disabled by default. If you previously had no `eip98transition` specified in your chain specification, you would enable this now manually on block `0x0`. (#9955)
|
||||
- Also, unknown fields in chain specs are now rejected. (#9972)
|
||||
- The Tendermint engine was removed from Parity Ethereum and is no longer available and maintained. (#9980)
|
||||
- Ropsten testnet data and keys moved from `test/` to `ropsten/` subdir. To reuse your old keys and data either copy or symlink them to the new location. (#10123)
|
||||
- Strict empty steps validation (#10041)
|
||||
- If you have a chain with`empty_steps` already running, some blocks most likely contain non-strict entries (unordered or duplicated empty steps). In this release `strict_empty_steps_transition` is enabled by default at block `0x0` for any chain with `empty_steps`.
|
||||
- If your network uses `empty_steps` you **must** (A) plan a hard fork and change `strict_empty_steps_transition` to the desired fork block and (B) update the clients of the whole network to 2.2.7-stable / 2.3.0-beta. If for some reason you don't want to do this please set`strict_empty_steps_transition` to `0xfffffffff` to disable it.
|
||||
|
||||
_Note:_ This release marks Parity 2.3 as _beta_. All versions of Parity 2.2 are now considered _stable_.
|
||||
Note: The default value of `--jsonrpc-cors` option has been altered to disallow (potentially malicious) websites from accessing the low-sensitivity RPCs (viewing exposed accounts, proposing transactions for signing). Currently domains need to be whitelisted manually. To bring back previous behaviour run with `--jsonrpc-cors all` or `--jsonrpc-cors http://example.com`.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Backports for 2.3.0 beta ([#10164](https://github.com/paritytech/parity-ethereum/pull/10164))
|
||||
- Snap: fix path in script ([#10157](https://github.com/paritytech/parity-ethereum/pull/10157))
|
||||
- Make sure parent block is not in importing queue when importing ancient blocks ([#10138](https://github.com/paritytech/parity-ethereum/pull/10138))
|
||||
- Ci: re-enable snap publishing ([#10142](https://github.com/paritytech/parity-ethereum/pull/10142))
|
||||
- Hf in POA Core (2019-01-18) - Constantinople ([#10155](https://github.com/paritytech/parity-ethereum/pull/10155))
|
||||
- Update EWF's tobalaba chainspec ([#10152](https://github.com/paritytech/parity-ethereum/pull/10152))
|
||||
- Replace ethcore-logger with env-logger. ([#10102](https://github.com/paritytech/parity-ethereum/pull/10102))
|
||||
- Finality: dont require chain head to be in the chain ([#10054](https://github.com/paritytech/parity-ethereum/pull/10054))
|
||||
- Remove caching for node connections ([#10143](https://github.com/paritytech/parity-ethereum/pull/10143))
|
||||
- Blooms file iterator empty on out of range position. ([#10145](https://github.com/paritytech/parity-ethereum/pull/10145))
|
||||
- Autogen docs for the "Configuring Parity Ethereum" wiki page. ([#10067](https://github.com/paritytech/parity-ethereum/pull/10067))
|
||||
- Misc: bump license header to 2019 ([#10135](https://github.com/paritytech/parity-ethereum/pull/10135))
|
||||
- Hide most of the logs from cpp example. ([#10139](https://github.com/paritytech/parity-ethereum/pull/10139))
|
||||
- Don't try to send oversized packets ([#10042](https://github.com/paritytech/parity-ethereum/pull/10042))
|
||||
- Private tx enabled flag added into STATUS packet ([#9999](https://github.com/paritytech/parity-ethereum/pull/9999))
|
||||
- Update pwasm-utils to 0.6.1 ([#10134](https://github.com/paritytech/parity-ethereum/pull/10134))
|
||||
- Extract blockchain from ethcore ([#10114](https://github.com/paritytech/parity-ethereum/pull/10114))
|
||||
- Ethcore: update hardcoded headers ([#10123](https://github.com/paritytech/parity-ethereum/pull/10123))
|
||||
- Identity fix ([#10128](https://github.com/paritytech/parity-ethereum/pull/10128))
|
||||
- Use LenCachingMutex to optimize verification. ([#10117](https://github.com/paritytech/parity-ethereum/pull/10117))
|
||||
- Pyethereum keystore support ([#9710](https://github.com/paritytech/parity-ethereum/pull/9710))
|
||||
- Bump rocksdb-sys to 0.5.5 ([#10124](https://github.com/paritytech/parity-ethereum/pull/10124))
|
||||
- Parity-clib: `async C bindings to RPC requests` + `subscribe/unsubscribe to websocket events` ([#9920](https://github.com/paritytech/parity-ethereum/pull/9920))
|
||||
- Refactor (hardware wallet) : reduce the number of threads ([#9644](https://github.com/paritytech/parity-ethereum/pull/9644))
|
||||
- Hf in POA Sokol (2019-01-04) ([#10077](https://github.com/paritytech/parity-ethereum/pull/10077))
|
||||
- Fix broken links ([#10119](https://github.com/paritytech/parity-ethereum/pull/10119))
|
||||
- Follow-up to [#10105](https://github.com/paritytech/parity-ethereum/issues/10105) ([#10107](https://github.com/paritytech/parity-ethereum/pull/10107))
|
||||
- Move EIP-712 crate back to parity-ethereum ([#10106](https://github.com/paritytech/parity-ethereum/pull/10106))
|
||||
- Move a bunch of stuff around ([#10101](https://github.com/paritytech/parity-ethereum/pull/10101))
|
||||
- Revert "Add --frozen when running cargo ([#10081](https://github.com/paritytech/parity-ethereum/pull/10081))" ([#10105](https://github.com/paritytech/parity-ethereum/pull/10105))
|
||||
- Fix left over small grumbles on whitespaces ([#10084](https://github.com/paritytech/parity-ethereum/pull/10084))
|
||||
- Add --frozen when running cargo ([#10081](https://github.com/paritytech/parity-ethereum/pull/10081))
|
||||
- Fix pubsub new_blocks notifications to include all blocks ([#9987](https://github.com/paritytech/parity-ethereum/pull/9987))
|
||||
- Update some dependencies for compilation with pc-windows-gnu ([#10082](https://github.com/paritytech/parity-ethereum/pull/10082))
|
||||
- Fill transaction hash on ethGetLog of light client. ([#9938](https://github.com/paritytech/parity-ethereum/pull/9938))
|
||||
- Update changelog update for 2.2.5-beta and 2.1.10-stable ([#10064](https://github.com/paritytech/parity-ethereum/pull/10064))
|
||||
- Implement len caching for parking_lot RwLock ([#10032](https://github.com/paritytech/parity-ethereum/pull/10032))
|
||||
- Update parking_lot to 0.7 ([#10050](https://github.com/paritytech/parity-ethereum/pull/10050))
|
||||
- Bump crossbeam. ([#10048](https://github.com/paritytech/parity-ethereum/pull/10048))
|
||||
- Ethcore: enable constantinople on ethereum ([#10031](https://github.com/paritytech/parity-ethereum/pull/10031))
|
||||
- Strict empty steps validation ([#10041](https://github.com/paritytech/parity-ethereum/pull/10041))
|
||||
- Center the Subtitle, use some CAPS ([#10034](https://github.com/paritytech/parity-ethereum/pull/10034))
|
||||
- Change test miner max memory to malloc reports. ([#10024](https://github.com/paritytech/parity-ethereum/pull/10024))
|
||||
- Sort the storage for private state ([#10018](https://github.com/paritytech/parity-ethereum/pull/10018))
|
||||
- Fix: test corpus_inaccessible panic ([#10019](https://github.com/paritytech/parity-ethereum/pull/10019))
|
||||
- Ci: move future releases to ethereum subdir on s3 ([#10017](https://github.com/paritytech/parity-ethereum/pull/10017))
|
||||
- Light(on_demand): decrease default time window to 10 secs ([#10016](https://github.com/paritytech/parity-ethereum/pull/10016))
|
||||
- Light client : failsafe crate (circuit breaker) ([#9790](https://github.com/paritytech/parity-ethereum/pull/9790))
|
||||
- Lencachingmutex ([#9988](https://github.com/paritytech/parity-ethereum/pull/9988))
|
||||
- Version and notification for private contract wrapper added ([#9761](https://github.com/paritytech/parity-ethereum/pull/9761))
|
||||
- Handle failing case for update account cache in require ([#9989](https://github.com/paritytech/parity-ethereum/pull/9989))
|
||||
- Add tokio runtime to ethcore io worker ([#9979](https://github.com/paritytech/parity-ethereum/pull/9979))
|
||||
- Move daemonize before creating account provider ([#10003](https://github.com/paritytech/parity-ethereum/pull/10003))
|
||||
- Docs: update changelogs ([#9990](https://github.com/paritytech/parity-ethereum/pull/9990))
|
||||
- Fix daemonize ([#10000](https://github.com/paritytech/parity-ethereum/pull/10000))
|
||||
- Fix Bloom migration ([#9992](https://github.com/paritytech/parity-ethereum/pull/9992))
|
||||
- Remove tendermint engine support ([#9980](https://github.com/paritytech/parity-ethereum/pull/9980))
|
||||
- Calculate gas for deployment transaction ([#9840](https://github.com/paritytech/parity-ethereum/pull/9840))
|
||||
- Fix unstable peers and slowness in sync ([#9967](https://github.com/paritytech/parity-ethereum/pull/9967))
|
||||
- Adds parity_verifySignature RPC method ([#9507](https://github.com/paritytech/parity-ethereum/pull/9507))
|
||||
- Improve block and transaction propagation ([#9954](https://github.com/paritytech/parity-ethereum/pull/9954))
|
||||
- Deny unknown fields for chainspec ([#9972](https://github.com/paritytech/parity-ethereum/pull/9972))
|
||||
- Fix docker build ([#9971](https://github.com/paritytech/parity-ethereum/pull/9971))
|
||||
- Ci: rearrange pipeline by logic ([#9970](https://github.com/paritytech/parity-ethereum/pull/9970))
|
||||
- Add changelogs for 2.0.9, 2.1.4, 2.1.6, and 2.2.1 ([#9963](https://github.com/paritytech/parity-ethereum/pull/9963))
|
||||
- Add Error message when sync is still in progress. ([#9475](https://github.com/paritytech/parity-ethereum/pull/9475))
|
||||
- Make CALLCODE to trace value to be the code address ([#9881](https://github.com/paritytech/parity-ethereum/pull/9881))
|
||||
- Fix light client informant while syncing ([#9932](https://github.com/paritytech/parity-ethereum/pull/9932))
|
||||
- Add a optional json dump state to evm-bin ([#9706](https://github.com/paritytech/parity-ethereum/pull/9706))
|
||||
- Disable EIP-98 transition by default ([#9955](https://github.com/paritytech/parity-ethereum/pull/9955))
|
||||
- Remove secret_store runtimes. ([#9888](https://github.com/paritytech/parity-ethereum/pull/9888))
|
||||
- Fix a deadlock ([#9952](https://github.com/paritytech/parity-ethereum/pull/9952))
|
||||
- Chore(eip712): remove unused `failure-derive` ([#9958](https://github.com/paritytech/parity-ethereum/pull/9958))
|
||||
- Do not use the home directory as the working dir in docker ([#9834](https://github.com/paritytech/parity-ethereum/pull/9834))
|
||||
- Prevent silent errors in daemon mode, closes [#9367](https://github.com/paritytech/parity-ethereum/issues/9367) ([#9946](https://github.com/paritytech/parity-ethereum/pull/9946))
|
||||
- Fix empty steps ([#9939](https://github.com/paritytech/parity-ethereum/pull/9939))
|
||||
- Adjust requests costs for light client ([#9925](https://github.com/paritytech/parity-ethereum/pull/9925))
|
||||
- Eip-1186: add `eth_getProof` RPC-Method ([#9001](https://github.com/paritytech/parity-ethereum/pull/9001))
|
||||
- Missing blocks in filter_changes RPC ([#9947](https://github.com/paritytech/parity-ethereum/pull/9947))
|
||||
- Allow rust-nightly builds fail in nightly builds ([#9944](https://github.com/paritytech/parity-ethereum/pull/9944))
|
||||
- Update eth-secp256k1 to include fix for BSDs ([#9935](https://github.com/paritytech/parity-ethereum/pull/9935))
|
||||
- Unbreak build on rust -stable ([#9934](https://github.com/paritytech/parity-ethereum/pull/9934))
|
||||
- Keep existing blocks when restoring a Snapshot ([#8643](https://github.com/paritytech/parity-ethereum/pull/8643))
|
||||
- Add experimental RPCs flag ([#9928](https://github.com/paritytech/parity-ethereum/pull/9928))
|
||||
- Clarify poll lifetime ([#9922](https://github.com/paritytech/parity-ethereum/pull/9922))
|
||||
- Docs(require rust 1.30) ([#9923](https://github.com/paritytech/parity-ethereum/pull/9923))
|
||||
- Use block header for building finality ([#9914](https://github.com/paritytech/parity-ethereum/pull/9914))
|
||||
- Simplify cargo audit ([#9918](https://github.com/paritytech/parity-ethereum/pull/9918))
|
||||
- Light-fetch: Differentiate between out-of-gas/manual throw and use required gas from response on failure ([#9824](https://github.com/paritytech/parity-ethereum/pull/9824))
|
||||
- Eip 191 ([#9701](https://github.com/paritytech/parity-ethereum/pull/9701))
|
||||
- Fix(logger): `reqwest` no longer a dependency ([#9908](https://github.com/paritytech/parity-ethereum/pull/9908))
|
||||
- Remove rust-toolchain file ([#9906](https://github.com/paritytech/parity-ethereum/pull/9906))
|
||||
- Foundation: 6692865, ropsten: 4417537, kovan: 9363457 ([#9907](https://github.com/paritytech/parity-ethereum/pull/9907))
|
||||
- Ethcore: use Machine::verify_transaction on parent block ([#9900](https://github.com/paritytech/parity-ethereum/pull/9900))
|
||||
- Chore(rpc-tests): remove unused rand ([#9896](https://github.com/paritytech/parity-ethereum/pull/9896))
|
||||
- Fix: Intermittent failing CI due to addr in use ([#9885](https://github.com/paritytech/parity-ethereum/pull/9885))
|
||||
- Chore(bump docopt): 0.8 -> 1.0 ([#9889](https://github.com/paritytech/parity-ethereum/pull/9889))
|
||||
- Use expect ([#9883](https://github.com/paritytech/parity-ethereum/pull/9883))
|
||||
- Use Weak reference in PubSubClient ([#9886](https://github.com/paritytech/parity-ethereum/pull/9886))
|
||||
- Ci: nuke the gitlab caches ([#9855](https://github.com/paritytech/parity-ethereum/pull/9855))
|
||||
- Remove unused code ([#9884](https://github.com/paritytech/parity-ethereum/pull/9884))
|
||||
- Fix json tracer overflow ([#9873](https://github.com/paritytech/parity-ethereum/pull/9873))
|
||||
- Allow to seal work on latest block ([#9876](https://github.com/paritytech/parity-ethereum/pull/9876))
|
||||
- Fix docker script ([#9854](https://github.com/paritytech/parity-ethereum/pull/9854))
|
||||
- Health endpoint ([#9847](https://github.com/paritytech/parity-ethereum/pull/9847))
|
||||
- Gitlab-ci: make android release build succeed ([#9743](https://github.com/paritytech/parity-ethereum/pull/9743))
|
||||
- Clean up existing benchmarks ([#9839](https://github.com/paritytech/parity-ethereum/pull/9839))
|
||||
- Update Callisto block reward code to support HF1 ([#9811](https://github.com/paritytech/parity-ethereum/pull/9811))
|
||||
- Option to disable keep alive for JSON-RPC http transport ([#9848](https://github.com/paritytech/parity-ethereum/pull/9848))
|
||||
- Classic.json Bootnode Update ([#9828](https://github.com/paritytech/parity-ethereum/pull/9828))
|
||||
- Support MIX. ([#9767](https://github.com/paritytech/parity-ethereum/pull/9767))
|
||||
- Ci: remove failing tests for android, windows, and macos ([#9788](https://github.com/paritytech/parity-ethereum/pull/9788))
|
||||
- Implement NoProof for json tests and update tests reference (replaces [#9744](https://github.com/paritytech/parity-ethereum/issues/9744)) ([#9814](https://github.com/paritytech/parity-ethereum/pull/9814))
|
||||
- Chore(bump regex) ([#9842](https://github.com/paritytech/parity-ethereum/pull/9842))
|
||||
- Ignore global cache for patched accounts ([#9752](https://github.com/paritytech/parity-ethereum/pull/9752))
|
||||
- Move state root verification before gas used ([#9841](https://github.com/paritytech/parity-ethereum/pull/9841))
|
||||
- Fix(docker-aarch64) : cross-compile config ([#9798](https://github.com/paritytech/parity-ethereum/pull/9798))
|
||||
- Version: bump nightly to 2.3.0 ([#9819](https://github.com/paritytech/parity-ethereum/pull/9819))
|
||||
- Tests modification for windows CI ([#9671](https://github.com/paritytech/parity-ethereum/pull/9671))
|
||||
- Eip-712 implementation ([#9631](https://github.com/paritytech/parity-ethereum/pull/9631))
|
||||
- Fix typo ([#9826](https://github.com/paritytech/parity-ethereum/pull/9826))
|
||||
- Clean up serde rename and use rename_all = camelCase when possible ([#9823](https://github.com/paritytech/parity-ethereum/pull/9823))
|
||||
- Beta Backports ([#7297](https://github.com/paritytech/parity/pull/7297))
|
||||
- New warp enodes ([#7287](https://github.com/paritytech/parity/pull/7287))
|
||||
- New warp enodes
|
||||
- Added one more warp enode; replaced spaces with tabs
|
||||
- Bump beta to 1.8.5
|
||||
- Update kovan boot nodes
|
||||
- Detect different node, same-key signing in aura ([#7245](https://github.com/paritytech/parity/pull/7245))
|
||||
- Detect different node, same-key signing in aura
|
||||
- Reduce scope of warning
|
||||
- Fix Cargo.lock
|
||||
- Updating mainnet bootnodes.
|
||||
- Update bootnodes ([#7363](https://github.com/paritytech/parity/pull/7363))
|
||||
- Updating mainnet bootnodes.
|
||||
- Add additional parity-beta bootnodes.
|
||||
- Restore old parity bootnodes and update foudation bootnodes
|
||||
- Fix default CORS. ([#7388](https://github.com/paritytech/parity/pull/7388))
|
||||
|
||||
## Previous releases
|
||||
## Parity [v1.8.4](https://github.com/paritytech/parity/releases/tag/v1.8.4) (2017-12-12)
|
||||
|
||||
- [CHANGELOG-2.2](docs/CHANGELOG-2.2.md) (_stable_)
|
||||
- [CHANGELOG-2.1](docs/CHANGELOG-2.1.md) (EOL: 2019-01-16)
|
||||
- [CHANGELOG-2.0](docs/CHANGELOG-2.0.md) (EOL: 2018-11-15)
|
||||
- [CHANGELOG-1.11](docs/CHANGELOG-1.11.md) (EOL: 2018-09-19)
|
||||
- [CHANGELOG-1.10](docs/CHANGELOG-1.10.md) (EOL: 2018-07-18)
|
||||
- [CHANGELOG-1.9](docs/CHANGELOG-1.9.md) (EOL: 2018-05-09)
|
||||
- [CHANGELOG-1.8](docs/CHANGELOG-1.8.md) (EOL: 2018-03-22)
|
||||
- [CHANGELOG-1.7](docs/CHANGELOG-1.7.md) (EOL: 2018-01-25)
|
||||
- [CHANGELOG-1.6](docs/CHANGELOG-1.6.md) (EOL: 2017-10-15)
|
||||
- [CHANGELOG-1.5](docs/CHANGELOG-1.5.md) (EOL: 2017-07-28)
|
||||
- [CHANGELOG-1.4](docs/CHANGELOG-1.4.md) (EOL: 2017-03-13)
|
||||
- [CHANGELOG-1.3](docs/CHANGELOG-1.3.md) (EOL: 2017-01-19)
|
||||
- [CHANGELOG-1.2](docs/CHANGELOG-1.2.md) (EOL: 2016-11-07)
|
||||
- [CHANGELOG-1.1](docs/CHANGELOG-1.1.md) (EOL: 2016-08-12)
|
||||
- [CHANGELOG-1.0](docs/CHANGELOG-1.0.md) (EOL: 2016-06-24)
|
||||
- [CHANGELOG-0.9](docs/CHANGELOG-0.9.md) (EOL: 2016-05-02)
|
||||
Parity 1.8.4 applies fixes for Proof-of-Authority networks and schedules the Kovan-Byzantium hard-fork.
|
||||
|
||||
- The Kovan testnet will fork on block `5067000` at `Thu Dec 14 2017 05:40:03 UTC`.
|
||||
- This enables Byzantium features on Kovan.
|
||||
- This disables uncles on Kovan for stability reasons.
|
||||
- Proof-of-Authority networks are advised to set `maximumUncleCount` to 0 in a future `maximumUncleCountTransition` for stability reasons.
|
||||
- See the [Kovan chain spec](https://github.com/paritytech/parity/blob/master/ethcore/res/ethereum/kovan.json) for an example.
|
||||
- New PoA networks created with Parity will have this feature enabled by default.
|
||||
|
||||
Furthermore, this release includes the ECIP-1039 Monetary policy rounding specification for Ethereum Classic, reduces the maximum Ethash-block timestamp drift to 15 seconds, and fixes various bugs for WASM and the RPC APIs.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Beta Backports and HF block update ([#7244](https://github.com/paritytech/parity/pull/7244))
|
||||
- Reduce max block timestamp drift to 15 seconds ([#7240](https://github.com/paritytech/parity/pull/7240))
|
||||
- Add test for block timestamp validation within allowed drift
|
||||
- Update kovan HF block number.
|
||||
- Beta Kovan HF ([#7234](https://github.com/paritytech/parity/pull/7234))
|
||||
- Kovan HF.
|
||||
- Bump version.
|
||||
- Fix aura difficulty race ([#7198](https://github.com/paritytech/parity/pull/7198))
|
||||
- Fix test key
|
||||
- Extract out score calculation
|
||||
- Fix build
|
||||
- Update kovan HF block number.
|
||||
- Add missing byzantium builtins.
|
||||
- Bump installers versions.
|
||||
- Increase allowed time drift to 10s. ([#7238](https://github.com/paritytech/parity/pull/7238))
|
||||
- Beta Backports ([#7197](https://github.com/paritytech/parity/pull/7197))
|
||||
- Maximum uncle count transition ([#7196](https://github.com/paritytech/parity/pull/7196))
|
||||
- Enable delayed maximum_uncle_count activation.
|
||||
- Fix tests.
|
||||
- Defer kovan HF.
|
||||
- Disable uncles by default ([#7006](https://github.com/paritytech/parity/pull/7006))
|
||||
- Escape inifinite loop in estimte_gas ([#7075](https://github.com/paritytech/parity/pull/7075))
|
||||
- ECIP-1039: Monetary policy rounding specification ([#7067](https://github.com/paritytech/parity/pull/7067))
|
||||
- WASM Remove blockhash error ([#7121](https://github.com/paritytech/parity/pull/7121))
|
||||
- Remove blockhash error
|
||||
- Update tests.
|
||||
- WASM storage_read and storage_write don't return anything ([#7110](https://github.com/paritytech/parity/pull/7110))
|
||||
- WASM parse payload from panics ([#7097](https://github.com/paritytech/parity/pull/7097))
|
||||
- Fix no-default-features. ([#7096](https://github.com/paritytech/parity/pull/7096))
|
||||
|
||||
## Parity [v1.8.3](https://github.com/paritytech/parity/releases/tag/v1.8.3) (2017-11-15)
|
||||
|
||||
Parity 1.8.3 contains several bug-fixes and removes the ability to deploy built-in multi-signature wallets.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Backports to beta ([#7043](https://github.com/paritytech/parity/pull/7043))
|
||||
- pwasm-std update ([#7018](https://github.com/paritytech/parity/pull/7018))
|
||||
- Version 1.8.3
|
||||
- Make CLI arguments parsing more backwards compatible ([#7004](https://github.com/paritytech/parity/pull/7004))
|
||||
- Skip nonce check for gas estimation ([#6997](https://github.com/paritytech/parity/pull/6997))
|
||||
- Events in WASM runtime ([#6967](https://github.com/paritytech/parity/pull/6967))
|
||||
- Return decoded seal fields. ([#6932](https://github.com/paritytech/parity/pull/6932))
|
||||
- Fix serialization of status in transaction receipts. ([#6926](https://github.com/paritytech/parity/pull/6926))
|
||||
- Windows fixes ([#6921](https://github.com/paritytech/parity/pull/6921))
|
||||
- Disallow built-in multi-sig deploy (only watch) ([#7014](https://github.com/paritytech/parity/pull/7014))
|
||||
- Add hint in ActionParams for splitting code/data ([#6968](https://github.com/paritytech/parity/pull/6968))
|
||||
- Action params and embedded params handling
|
||||
- Fix name-spaces
|
||||
|
||||
## Parity [v1.8.2](https://github.com/paritytech/parity/releases/tag/v1.8.2) (2017-10-26)
|
||||
|
||||
Parity 1.8.2 fixes an important potential consensus issue and a few additional minor issues:
|
||||
|
||||
- `blockNumber` transaction field is now returned correctly in RPC calls.
|
||||
- Possible crash when `--force-sealing` option is used.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Beta Backports ([#6891](https://github.com/paritytech/parity/pull/6891))
|
||||
- Bump to v1.8.2
|
||||
- Refactor static context check in CREATE. ([#6886](https://github.com/paritytech/parity/pull/6886))
|
||||
- Refactor static context check in CREATE.
|
||||
- Fix wasm.
|
||||
- Fix serialization of non-localized transactions ([#6868](https://github.com/paritytech/parity/pull/6868))
|
||||
- Fix serialization of non-localized transactions.
|
||||
- Return proper SignedTransactions representation.
|
||||
- Allow force sealing and reseal=0 for non-dev chains. ([#6878](https://github.com/paritytech/parity/pull/6878))
|
||||
|
||||
## Parity [v1.8.1](https://github.com/paritytech/parity/releases/tag/v1.8.1) (2017-10-20)
|
||||
|
||||
Parity 1.8.1 fixes several bugs with token balances, tweaks snapshot-sync, improves the performance of nodes with huge amounts of accounts and changes the Trezor account derivation path.
|
||||
|
||||
**Important Note**: The **Trezor** account derivation path was changed in this release ([#6815](https://github.com/paritytech/parity/pull/6815)) to always use the first account (`m/44'/60'/0'/0/0` instead of `m/44'/60'/0'/0`). This way we enable compatibility with other Ethereum wallets supporting Trezor hardware-wallets. However, **action is required** before upgrading, if you have funds on your Parity Trezor wallet. If you already upgraded to 1.8.1, please downgrade to 1.8.0 first to recover the funds with the following steps:
|
||||
|
||||
1. Make sure you have 1.8.0-beta and your Trezor plugged in.
|
||||
2. Create a new standard Parity account. Make sure you have backups of the recovery phrase and don't forget the password.
|
||||
3. Move your funds from the Trezor hardware-wallet account to the freshly generated Parity account.
|
||||
4. Upgrade to 1.8.1-beta and plug in your Trezor.
|
||||
5. Move your funds from your Parity account to the new Trezor account.
|
||||
6. Keep using Parity as normal.
|
||||
|
||||
If you don't want to downgrade or move your funds off your Trezor-device, you can also use the official Trezor application or other wallets allowing to select the derivation path to access the funds.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Add ECIP1017 to Morden config ([#6845](https://github.com/paritytech/parity/pull/6845))
|
||||
- Ethstore optimizations ([#6844](https://github.com/paritytech/parity/pull/6844))
|
||||
- Bumb to v1.8.1 ([#6843](https://github.com/paritytech/parity/pull/6843))
|
||||
- Backport ([#6837](https://github.com/paritytech/parity/pull/6837))
|
||||
- Tweaked snapshot sync threshold ([#6829](https://github.com/paritytech/parity/pull/6829))
|
||||
- Change keypath derivation logic ([#6815](https://github.com/paritytech/parity/pull/6815))
|
||||
- Refresh cached tokens based on registry info & random balances ([#6824](https://github.com/paritytech/parity/pull/6824))
|
||||
- Refresh cached tokens based on registry info & random balances ([#6818](https://github.com/paritytech/parity/pull/6818))
|
||||
- Don't display errored token images
|
||||
|
||||
## Parity [v1.8.0](https://github.com/paritytech/parity/releases/tag/v1.8.0) (2017-10-15)
|
||||
|
||||
We are happy to announce our newest Parity 1.8 release. Among others, it enables the following features:
|
||||
|
||||
- Full Whisper v6 integration
|
||||
- Trezor hardware-wallet support
|
||||
- WASM contract support
|
||||
- PICOPS KYC-certified accounts and vouching for community-dapps
|
||||
- Light client compatibility for Proof-of-Authority networks
|
||||
- Transaction permissioning and permissioned p2p-connections
|
||||
- Full Byzantium-fork compatibility
|
||||
- Full Musicoin MCIP-3 UBI-fork compatibility
|
||||
|
||||
Further, users upgrading from 1.7 should acknowledge the following changes:
|
||||
|
||||
- The chain-engine was further abstracted and chain-specs need to be upgraded. [#6134](https://github.com/paritytech/parity/pull/6134) [#6591](https://github.com/paritytech/parity/pull/6591)
|
||||
- `network_id` was renamed to `chain_id` where applicable. [#6345](https://github.com/paritytech/parity/pull/6345)
|
||||
- `trace_filter` RPC method now comes with pagination. [#6312](https://github.com/paritytech/parity/pull/6312)
|
||||
- Added tracing of rewards on closing blocks. [#6194](https://github.com/paritytech/parity/pull/6194)
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Updated ethabi to fix auto-update ([#6771](https://github.com/paritytech/parity/pull/6771))
|
||||
- Fixed kovan chain validation ([#6760](https://github.com/paritytech/parity/pull/6760))
|
||||
- Fixed kovan chain validation
|
||||
- Fork detection
|
||||
- Fixed typo
|
||||
- Bumped fork block number for auto-update ([#6755](https://github.com/paritytech/parity/pull/6755))
|
||||
- CLI: Reject invalid argument values rather than ignore them ([#6747](https://github.com/paritytech/parity/pull/6747))
|
||||
- Fixed modexp gas calculation overflow ([#6745](https://github.com/paritytech/parity/pull/6745))
|
||||
- Backport beta - Fixes Badges ([#6732](https://github.com/paritytech/parity/pull/6732))
|
||||
- Fix badges not showing up ([#6730](https://github.com/paritytech/parity/pull/6730))
|
||||
- Always fetch meta data first [badges]
|
||||
- Bump to v1.8.0 in beta
|
||||
- Fix tokens and badges ([#6725](https://github.com/paritytech/parity/pull/6725))
|
||||
- Update new token fetching
|
||||
- Working Certifications Monitoring
|
||||
- Update on Certification / Revoke
|
||||
- Fix none-fetched tokens value display
|
||||
- Fix tests
|
||||
- Check vouch status on appId in addition to contentHash ([#6719](https://github.com/paritytech/parity/pull/6719))
|
||||
- Check vouch status on appId in addition to contentHash
|
||||
- Simplify var expansion
|
||||
- Prevent going offline when restoring or taking a snapshot [#6694](https://github.com/paritytech/parity/pull/6694)
|
||||
- Graceful exit when invalid CLI flags are passed (#6485) [#6711](https://github.com/paritytech/parity/pull/6711)
|
||||
- Fixed RETURNDATA out of bounds check [#6718](https://github.com/paritytech/parity/pull/6718)
|
||||
- Display vouched overlay on dapps [#6710](https://github.com/paritytech/parity/pull/6710)
|
||||
- Fix gas estimation if `from` is not provided. [#6714](https://github.com/paritytech/parity/pull/6714)
|
||||
- Emulate signer pubsub on public node [#6708](https://github.com/paritytech/parity/pull/6708)
|
||||
- Removes dependency on rustc_serialize (#5988) [#6705](https://github.com/paritytech/parity/pull/6705)
|
||||
- Fixed potential modexp exp len overflow [#6686](https://github.com/paritytech/parity/pull/6686)
|
||||
- Fix asciiToHex for characters < 0x10 [#6702](https://github.com/paritytech/parity/pull/6702)
|
||||
- Fix address input [#6701](https://github.com/paritytech/parity/pull/6701)
|
||||
- Allow signer signing display of markdown [#6707](https://github.com/paritytech/parity/pull/6707)
|
||||
- Fixed build warnings [#6664](https://github.com/paritytech/parity/pull/6664)
|
||||
- Fix warp sync blockers detection [#6691](https://github.com/paritytech/parity/pull/6691)
|
||||
- Difficulty tests [#6687](https://github.com/paritytech/parity/pull/6687)
|
||||
- Separate migrations from util [#6690](https://github.com/paritytech/parity/pull/6690)
|
||||
- Changelog for 1.7.3 [#6678](https://github.com/paritytech/parity/pull/6678)
|
||||
- WASM gas schedule [#6638](https://github.com/paritytech/parity/pull/6638)
|
||||
- Fix wallet view [#6597](https://github.com/paritytech/parity/pull/6597)
|
||||
- Byzantium fork block number [#6660](https://github.com/paritytech/parity/pull/6660)
|
||||
- Fixed RETURNDATA size for built-ins [#6652](https://github.com/paritytech/parity/pull/6652)
|
||||
- Light Client: fetch transactions/receipts by transaction hash [#6641](https://github.com/paritytech/parity/pull/6641)
|
||||
- Add Musicoin and MCIP-3 UBI hardfork. [#6621](https://github.com/paritytech/parity/pull/6621)
|
||||
- fix 1.8 backcompat: revert to manual encoding/decoding of transition proofs [#6665](https://github.com/paritytech/parity/pull/6665)
|
||||
- Tweaked block download timeouts (#6595) [#6655](https://github.com/paritytech/parity/pull/6655)
|
||||
- Renamed RPC receipt statusCode field to status [#6650](https://github.com/paritytech/parity/pull/6650)
|
||||
- SecretStore: session level timeout [#6631](https://github.com/paritytech/parity/pull/6631)
|
||||
- SecretStore: ShareRemove of 'isolated' nodes [#6630](https://github.com/paritytech/parity/pull/6630)
|
||||
- SecretStore: exclusive sessions [#6624](https://github.com/paritytech/parity/pull/6624)
|
||||
- Fixed network protocol version negotiation [#6649](https://github.com/paritytech/parity/pull/6649)
|
||||
- Updated systemd files for linux (Resolves #6592) [#6598](https://github.com/paritytech/parity/pull/6598)
|
||||
- move additional_params to machine, fixes registry on non-ethash chains [#6646](https://github.com/paritytech/parity/pull/6646)
|
||||
- Fix Token Transfer in transaction list [#6589](https://github.com/paritytech/parity/pull/6589)
|
||||
- Update jsonrpc dependencies and rewrite dapps to futures. [#6522](https://github.com/paritytech/parity/pull/6522)
|
||||
- Balance queries implemented in WASM runtime [#6639](https://github.com/paritytech/parity/pull/6639)
|
||||
- Don't expose port 80 for parity anymore [#6633](https://github.com/paritytech/parity/pull/6633)
|
||||
- WASM Runtime refactoring [#6596](https://github.com/paritytech/parity/pull/6596)
|
||||
- Fix compilation [#6625](https://github.com/paritytech/parity/pull/6625)
|
||||
- Downgrade futures to suppress warnings. [#6620](https://github.com/paritytech/parity/pull/6620)
|
||||
- Add pagination for trace_filter rpc method [#6312](https://github.com/paritytech/parity/pull/6312)
|
||||
- Disallow pasting recovery phrases on first run [#6602](https://github.com/paritytech/parity/pull/6602)
|
||||
- fix typo: Unkown => Unknown [#6559](https://github.com/paritytech/parity/pull/6559)
|
||||
- SecretStore: administrative sessions prototypes [#6605](https://github.com/paritytech/parity/pull/6605)
|
||||
- fix parity.io link 404 [#6617](https://github.com/paritytech/parity/pull/6617)
|
||||
- SecretStore: add node to existing session poc + discussion [#6480](https://github.com/paritytech/parity/pull/6480)
|
||||
- Generalize engine trait [#6591](https://github.com/paritytech/parity/pull/6591)
|
||||
- Add RPC eth_chainId for querying the current blockchain chain ID [#6329](https://github.com/paritytech/parity/pull/6329)
|
||||
- Debounce sync status. [#6572](https://github.com/paritytech/parity/pull/6572)
|
||||
- [Public Node] Disable tx scheduling and hardware wallets [#6588](https://github.com/paritytech/parity/pull/6588)
|
||||
- Use memmap for dag cache [#6193](https://github.com/paritytech/parity/pull/6193)
|
||||
- Rename Requests to Batch [#6582](https://github.com/paritytech/parity/pull/6582)
|
||||
- Use host as ws/dapps url if present. [#6566](https://github.com/paritytech/parity/pull/6566)
|
||||
- Sync progress and error handling fixes [#6560](https://github.com/paritytech/parity/pull/6560)
|
||||
- Fixed receipt serialization and RPC [#6555](https://github.com/paritytech/parity/pull/6555)
|
||||
- Fix number of confirmations for transaction [#6552](https://github.com/paritytech/parity/pull/6552)
|
||||
- Fix #6540 [#6556](https://github.com/paritytech/parity/pull/6556)
|
||||
- Fix failing hardware tests [#6553](https://github.com/paritytech/parity/pull/6553)
|
||||
- Required validators >= num owners in Wallet Creation [#6551](https://github.com/paritytech/parity/pull/6551)
|
||||
- Random cleanups / improvements to a state [#6472](https://github.com/paritytech/parity/pull/6472)
|
||||
- Changelog for 1.7.2 [#6363](https://github.com/paritytech/parity/pull/6363)
|
||||
- Ropsten fork [#6533](https://github.com/paritytech/parity/pull/6533)
|
||||
- Byzantium updates [#5855](https://github.com/paritytech/parity/pull/5855)
|
||||
- Fix extension detection [#6452](https://github.com/paritytech/parity/pull/6452)
|
||||
- Downgrade futures to supress warnings [#6521](https://github.com/paritytech/parity/pull/6521)
|
||||
- separate trie from util and make its dependencies into libs [#6478](https://github.com/paritytech/parity/pull/6478)
|
||||
- WASM sha3 test [#6512](https://github.com/paritytech/parity/pull/6512)
|
||||
- Fix broken JavaScript tests [#6498](https://github.com/paritytech/parity/pull/6498)
|
||||
- SecretStore: use random key to encrypt channel + session-level nonce [#6470](https://github.com/paritytech/parity/pull/6470)
|
||||
- Trezor Support [#6403](https://github.com/paritytech/parity/pull/6403)
|
||||
- Fix compiler warning [#6491](https://github.com/paritytech/parity/pull/6491)
|
||||
- Fix typo [#6505](https://github.com/paritytech/parity/pull/6505)
|
||||
- WASM: added math overflow test [#6474](https://github.com/paritytech/parity/pull/6474)
|
||||
- Fix slow balances [#6471](https://github.com/paritytech/parity/pull/6471)
|
||||
- WASM runtime update [#6467](https://github.com/paritytech/parity/pull/6467)
|
||||
- Compatibility with whisper v6 [#6179](https://github.com/paritytech/parity/pull/6179)
|
||||
- light-poa round 2: allow optional casting of engine client to full client [#6468](https://github.com/paritytech/parity/pull/6468)
|
||||
- Moved attributes under docs [#6475](https://github.com/paritytech/parity/pull/6475)
|
||||
- cleanup util dependencies [#6464](https://github.com/paritytech/parity/pull/6464)
|
||||
- removed redundant earlymergedb trace guards [#6463](https://github.com/paritytech/parity/pull/6463)
|
||||
- UtilError utilizes error_chain! [#6461](https://github.com/paritytech/parity/pull/6461)
|
||||
- fixed master [#6465](https://github.com/paritytech/parity/pull/6465)
|
||||
- Refactor and port CLI from Docopt to Clap (#2066) [#6356](https://github.com/paritytech/parity/pull/6356)
|
||||
- Add language selector in production [#6317](https://github.com/paritytech/parity/pull/6317)
|
||||
- eth_call returns output of contract creations [#6420](https://github.com/paritytech/parity/pull/6420)
|
||||
- Refactor: Don't reexport bigint from util [#6459](https://github.com/paritytech/parity/pull/6459)
|
||||
- Transaction permissioning [#6441](https://github.com/paritytech/parity/pull/6441)
|
||||
- Added missing SecretStore tests - signing session [#6411](https://github.com/paritytech/parity/pull/6411)
|
||||
- Light-client sync for contract-based PoA [#6370](https://github.com/paritytech/parity/pull/6370)
|
||||
- triehash is separated from util [#6428](https://github.com/paritytech/parity/pull/6428)
|
||||
- remove re-export of parking_lot in util [#6435](https://github.com/paritytech/parity/pull/6435)
|
||||
- fix modexp bug: return 0 if base is zero [#6424](https://github.com/paritytech/parity/pull/6424)
|
||||
- separate semantic_version from util [#6438](https://github.com/paritytech/parity/pull/6438)
|
||||
- move timer.rs to ethcore [#6437](https://github.com/paritytech/parity/pull/6437)
|
||||
- remove re-export of ansi_term in util [#6433](https://github.com/paritytech/parity/pull/6433)
|
||||
- Pub sub blocks [#6139](https://github.com/paritytech/parity/pull/6139)
|
||||
- replace trait Hashable with fn keccak [#6423](https://github.com/paritytech/parity/pull/6423)
|
||||
- add more hash backward compatibility test for bloom [#6425](https://github.com/paritytech/parity/pull/6425)
|
||||
- remove the redundant hasher in Bloom [#6404](https://github.com/paritytech/parity/pull/6404)
|
||||
- Remove re-export of HeapSizeOf in util (part of #6418) [#6419](https://github.com/paritytech/parity/pull/6419)
|
||||
- Rewards on closing blocks [#6194](https://github.com/paritytech/parity/pull/6194)
|
||||
- ensure balances of constructor accounts are kept [#6413](https://github.com/paritytech/parity/pull/6413)
|
||||
- removed recursion from triedbmut::lookup [#6394](https://github.com/paritytech/parity/pull/6394)
|
||||
- do not activate genesis epoch in immediate transition validator contract [#6349](https://github.com/paritytech/parity/pull/6349)
|
||||
- Use git for the snap version [#6271](https://github.com/paritytech/parity/pull/6271)
|
||||
- Permissioned p2p connections [#6359](https://github.com/paritytech/parity/pull/6359)
|
||||
- Don't accept transactions above block gas limit. [#6408](https://github.com/paritytech/parity/pull/6408)
|
||||
- Fix memory tracing. [#6399](https://github.com/paritytech/parity/pull/6399)
|
||||
- earlydb optimizations [#6393](https://github.com/paritytech/parity/pull/6393)
|
||||
- Optimized PlainHasher hashing. Trie insertions are >15 faster [#6321](https://github.com/paritytech/parity/pull/6321)
|
||||
- Trie optimizations [#6389](https://github.com/paritytech/parity/pull/6389)
|
||||
- small optimizations for triehash [#6392](https://github.com/paritytech/parity/pull/6392)
|
||||
- Bring back IPFS tests. [#6398](https://github.com/paritytech/parity/pull/6398)
|
||||
- Running state test using parity-evm [#6355](https://github.com/paritytech/parity/pull/6355)
|
||||
- Wasm math tests extended [#6354](https://github.com/paritytech/parity/pull/6354)
|
||||
- Expose health status over RPC [#6274](https://github.com/paritytech/parity/pull/6274)
|
||||
- fix bloom bitvecjournal storage allocation [#6390](https://github.com/paritytech/parity/pull/6390)
|
||||
- fixed pending block panic [#6391](https://github.com/paritytech/parity/pull/6391)
|
||||
- Infoline less opaque for UI/visibility [#6364](https://github.com/paritytech/parity/pull/6364)
|
||||
- Fix eth_call. [#6365](https://github.com/paritytech/parity/pull/6365)
|
||||
- updated bigint [#6341](https://github.com/paritytech/parity/pull/6341)
|
||||
- Optimize trie iter by avoiding redundant copying [#6347](https://github.com/paritytech/parity/pull/6347)
|
||||
- Only keep a single rocksdb debug log file [#6346](https://github.com/paritytech/parity/pull/6346)
|
||||
- Tweaked snapshot params [#6344](https://github.com/paritytech/parity/pull/6344)
|
||||
- Rename network_id to chain_id where applicable. [#6345](https://github.com/paritytech/parity/pull/6345)
|
||||
- Itertools are no longer reexported from util, optimized triedb iter [#6322](https://github.com/paritytech/parity/pull/6322)
|
||||
- Better check the created accounts before showing Startup Wizard [#6331](https://github.com/paritytech/parity/pull/6331)
|
||||
- Better error messages for invalid types in RPC [#6311](https://github.com/paritytech/parity/pull/6311)
|
||||
- fix panic in parity-evm json tracer [#6338](https://github.com/paritytech/parity/pull/6338)
|
||||
- WASM math test [#6305](https://github.com/paritytech/parity/pull/6305)
|
||||
- rlp_derive [#6125](https://github.com/paritytech/parity/pull/6125)
|
||||
- Fix --chain parsing in parity-evm. [#6314](https://github.com/paritytech/parity/pull/6314)
|
||||
- Unexpose RPC methods on :8180 [#6295](https://github.com/paritytech/parity/pull/6295)
|
||||
- Ignore errors from dappsUrl when starting UI. [#6296](https://github.com/paritytech/parity/pull/6296)
|
||||
- updated bigint with optimized mul and from_big_indian [#6323](https://github.com/paritytech/parity/pull/6323)
|
||||
- SecretStore: bunch of fixes and improvements [#6168](https://github.com/paritytech/parity/pull/6168)
|
||||
- Master requires rust 1.19 [#6308](https://github.com/paritytech/parity/pull/6308)
|
||||
- Add more descriptive error when signing/decrypting using hw wallet. [#6302](https://github.com/paritytech/parity/pull/6302)
|
||||
- Increase default gas limit for eth_call. [#6299](https://github.com/paritytech/parity/pull/6299)
|
||||
- rust-toolchain file on master [#6266](https://github.com/paritytech/parity/pull/6266)
|
||||
- Migrate wasm-tests to updated runtime [#6278](https://github.com/paritytech/parity/pull/6278)
|
||||
- Extension fixes [#6284](https://github.com/paritytech/parity/pull/6284)
|
||||
- Fix a hash displayed in tooltip when signing arbitrary data [#6283](https://github.com/paritytech/parity/pull/6283)
|
||||
- Time should not contribue to overall status. [#6276](https://github.com/paritytech/parity/pull/6276)
|
||||
- Add --to and --gas-price to evmbin [#6277](https://github.com/paritytech/parity/pull/6277)
|
||||
- Fix dapps CSP when UI is exposed externally [#6178](https://github.com/paritytech/parity/pull/6178)
|
||||
- Add warning to web browser and fix links. [#6232](https://github.com/paritytech/parity/pull/6232)
|
||||
- Update Settings/Proxy view to match entries in proxy.pac [#4771](https://github.com/paritytech/parity/pull/4771)
|
||||
- Dapp refresh [#5752](https://github.com/paritytech/parity/pull/5752)
|
||||
- Add support for ConsenSys multisig wallet [#6153](https://github.com/paritytech/parity/pull/6153)
|
||||
- updated jsonrpc [#6264](https://github.com/paritytech/parity/pull/6264)
|
||||
- SecretStore: encrypt messages using private key from key store [#6146](https://github.com/paritytech/parity/pull/6146)
|
||||
- Wasm storage read test [#6255](https://github.com/paritytech/parity/pull/6255)
|
||||
- propagate stratum submit share error upstream [#6260](https://github.com/paritytech/parity/pull/6260)
|
||||
- Using multiple NTP servers [#6173](https://github.com/paritytech/parity/pull/6173)
|
||||
- Add GitHub issue templates. [#6259](https://github.com/paritytech/parity/pull/6259)
|
||||
- format instant change proofs correctly [#6241](https://github.com/paritytech/parity/pull/6241)
|
||||
- price-info does not depend on util [#6231](https://github.com/paritytech/parity/pull/6231)
|
||||
- native-contracts crate does not depend on util any more [#6233](https://github.com/paritytech/parity/pull/6233)
|
||||
- Bump master to 1.8.0 [#6256](https://github.com/paritytech/parity/pull/6256)
|
||||
- SecretStore: do not cache ACL contract + on-chain key servers configuration [#6107](https://github.com/paritytech/parity/pull/6107)
|
||||
- Fix the README badges [#6229](https://github.com/paritytech/parity/pull/6229)
|
||||
- updated tiny-keccak to 1.3 [#6248](https://github.com/paritytech/parity/pull/6248)
|
||||
- Small grammatical error [#6244](https://github.com/paritytech/parity/pull/6244)
|
||||
- Multi-call RPC [#6195](https://github.com/paritytech/parity/pull/6195)
|
||||
- InstantSeal fix [#6223](https://github.com/paritytech/parity/pull/6223)
|
||||
- Untrusted RLP length overflow check [#6227](https://github.com/paritytech/parity/pull/6227)
|
||||
- Chainspec validation [#6197](https://github.com/paritytech/parity/pull/6197)
|
||||
- Fix cache path when using --base-path [#6212](https://github.com/paritytech/parity/pull/6212)
|
||||
- removed std reexports from util && fixed broken tests [#6187](https://github.com/paritytech/parity/pull/6187)
|
||||
- WASM MVP continued [#6132](https://github.com/paritytech/parity/pull/6132)
|
||||
- Decouple virtual machines [#6184](https://github.com/paritytech/parity/pull/6184)
|
||||
- Realloc test added [#6177](https://github.com/paritytech/parity/pull/6177)
|
||||
- Re-enable wallets, fixed forgetting accounts [#6196](https://github.com/paritytech/parity/pull/6196)
|
||||
- Move more params to the common section. [#6134](https://github.com/paritytech/parity/pull/6134)
|
||||
- Whisper js [#6161](https://github.com/paritytech/parity/pull/6161)
|
||||
- typo in uninstaller [#6185](https://github.com/paritytech/parity/pull/6185)
|
||||
- fix #6052. honor --no-color for signer command [#6100](https://github.com/paritytech/parity/pull/6100)
|
||||
- Refactor --allow-ips to handle custom ip-ranges [#6144](https://github.com/paritytech/parity/pull/6144)
|
||||
- Update Changelog for 1.6.10 and 1.7.0 [#6183](https://github.com/paritytech/parity/pull/6183)
|
||||
- Fix unsoundness in ethash's unsafe code [#6140](https://github.com/paritytech/parity/pull/6140)
|
||||
|
||||
|
||||
### Previous releases
|
||||
|
||||
- [CHANGELOG-1.7](docs/CHANGELOG-1.7.md)
|
||||
- [CHANGELOG-1.6](docs/CHANGELOG-1.6.md)
|
||||
- [CHANGELOG-1.5](docs/CHANGELOG-1.5.md)
|
||||
- [CHANGELOG-1.4](docs/CHANGELOG-1.4.md)
|
||||
- [CHANGELOG-1.3](docs/CHANGELOG-1.3.md)
|
||||
- [CHANGELOG-1.2](docs/CHANGELOG-1.2.md)
|
||||
- [CHANGELOG-1.1](docs/CHANGELOG-1.1.md)
|
||||
- [CHANGELOG-1.0](docs/CHANGELOG-1.0.md)
|
||||
- [CHANGELOG-0.9](docs/CHANGELOG-0.9.md)
|
||||
|
||||
5180
Cargo.lock
generated
5180
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
147
Cargo.toml
147
Cargo.toml
@@ -1,77 +1,75 @@
|
||||
[package]
|
||||
description = "Parity Ethereum client"
|
||||
name = "parity-ethereum"
|
||||
# NOTE Make sure to update util/version/Cargo.toml as well
|
||||
version = "2.4.0"
|
||||
name = "parity"
|
||||
version = "1.9.2"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
blooms-db = { path = "util/blooms-db" }
|
||||
log = "0.4"
|
||||
log = "0.3"
|
||||
env_logger = "0.4"
|
||||
rustc-hex = "1.0"
|
||||
docopt = "1.0"
|
||||
docopt = "0.8"
|
||||
clap = "2"
|
||||
term_size = "0.3"
|
||||
textwrap = "0.9"
|
||||
time = "0.1"
|
||||
num_cpus = "1.2"
|
||||
number_prefix = "0.2"
|
||||
rpassword = "1.0"
|
||||
semver = "0.9"
|
||||
ansi_term = "0.10"
|
||||
parking_lot = "0.7"
|
||||
regex = "1.0"
|
||||
atty = "0.2.8"
|
||||
semver = "0.6"
|
||||
ansi_term = "0.9"
|
||||
parking_lot = "0.4"
|
||||
regex = "0.2"
|
||||
isatty = "0.1"
|
||||
toml = "0.4"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
serde_derive = "1.0"
|
||||
app_dirs = "1.1.1"
|
||||
futures = "0.1"
|
||||
futures-cpupool = "0.1"
|
||||
fdlimit = "0.1"
|
||||
ws2_32-sys = "0.2"
|
||||
ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" }
|
||||
jsonrpc-core = "10.0.1"
|
||||
parity-bytes = "0.1"
|
||||
common-types = { path = "ethcore/types" }
|
||||
ethcore = { path = "ethcore", features = ["parity"] }
|
||||
ethcore-accounts = { path = "accounts", optional = true }
|
||||
ethcore-blockchain = { path = "ethcore/blockchain" }
|
||||
ethcore-call-contract = { path = "ethcore/call-contract"}
|
||||
ethcore-db = { path = "ethcore/db" }
|
||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.9" }
|
||||
ethsync = { path = "sync" }
|
||||
ethcore = { path = "ethcore" }
|
||||
ethcore-util = { path = "util" }
|
||||
ethcore-bytes = { path = "util/bytes" }
|
||||
ethcore-bigint = { path = "util/bigint" }
|
||||
ethcore-io = { path = "util/io" }
|
||||
ethcore-devtools = { path = "devtools" }
|
||||
ethcore-light = { path = "ethcore/light" }
|
||||
ethcore-logger = { path = "parity/logger" }
|
||||
ethcore-miner = { path = "miner" }
|
||||
ethcore-logger = { path = "logger" }
|
||||
ethcore-stratum = { path = "stratum" }
|
||||
ethcore-network = { path = "util/network" }
|
||||
ethcore-private-tx = { path = "ethcore/private-tx" }
|
||||
ethcore-service = { path = "ethcore/service" }
|
||||
ethcore-sync = { path = "ethcore/sync" }
|
||||
ethereum-types = "0.4"
|
||||
ethkey = { path = "accounts/ethkey" }
|
||||
ethstore = { path = "accounts/ethstore" }
|
||||
node-filter = { path = "ethcore/node-filter" }
|
||||
rlp = { version = "0.3.0", features = ["ethereum"] }
|
||||
cli-signer= { path = "cli-signer" }
|
||||
parity-daemonize = "0.3"
|
||||
parity-hash-fetch = { path = "updater/hash-fetch" }
|
||||
node-filter = { path = "ethcore/node_filter" }
|
||||
ethkey = { path = "ethkey" }
|
||||
node-health = { path = "dapps/node-health" }
|
||||
rlp = { path = "util/rlp" }
|
||||
rpc-cli = { path = "rpc_cli" }
|
||||
parity-hash-fetch = { path = "hash-fetch" }
|
||||
parity-ipfs-api = { path = "ipfs" }
|
||||
parity-local-store = { path = "miner/local-store" }
|
||||
parity-runtime = { path = "util/runtime" }
|
||||
parity-local-store = { path = "local-store" }
|
||||
parity-reactor = { path = "util/reactor" }
|
||||
parity-rpc = { path = "rpc" }
|
||||
parity-rpc-client = { path = "rpc_client" }
|
||||
parity-updater = { path = "updater" }
|
||||
parity-version = { path = "util/version" }
|
||||
parity-whisper = { path = "whisper" }
|
||||
parity-path = "0.1"
|
||||
path = { path = "util/path" }
|
||||
dir = { path = "util/dir" }
|
||||
panic_hook = { path = "util/panic-hook" }
|
||||
keccak-hash = "0.1"
|
||||
migration-rocksdb = { path = "util/migration-rocksdb" }
|
||||
kvdb = "0.1"
|
||||
kvdb-rocksdb = "0.1.3"
|
||||
panic_hook = { path = "panic_hook" }
|
||||
keccak-hash = { path = "util/hash" }
|
||||
migration = { path = "util/migration" }
|
||||
kvdb = { path = "util/kvdb" }
|
||||
kvdb-rocksdb = { path = "util/kvdb-rocksdb" }
|
||||
journaldb = { path = "util/journaldb" }
|
||||
|
||||
ethcore-secretstore = { path = "secret-store", optional = true }
|
||||
|
||||
registrar = { path = "util/registrar" }
|
||||
parity-dapps = { path = "dapps", optional = true }
|
||||
ethcore-secretstore = { path = "secret_store", optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
rustc_version = "0.2"
|
||||
@@ -79,67 +77,54 @@ rustc_version = "0.2"
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "0.1"
|
||||
ipnetwork = "0.12.6"
|
||||
tempdir = "0.3"
|
||||
fake-fetch = { path = "util/fake-fetch" }
|
||||
lazy_static = "1.2.0"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3.4", features = ["winsock2", "winuser", "shellapi"] }
|
||||
winapi = "0.2"
|
||||
|
||||
[target.'cfg(not(windows))'.dependencies]
|
||||
daemonize = "0.2"
|
||||
|
||||
[features]
|
||||
default = ["accounts"]
|
||||
accounts = ["ethcore-accounts", "parity-rpc/accounts"]
|
||||
miner-debug = ["ethcore/miner-debug"]
|
||||
default = ["ui-precompiled"]
|
||||
ui = [
|
||||
"ui-enabled",
|
||||
"parity-dapps/ui",
|
||||
]
|
||||
ui-precompiled = [
|
||||
"ui-enabled",
|
||||
"parity-dapps/ui-precompiled",
|
||||
]
|
||||
ui-enabled = ["dapps"]
|
||||
dapps = ["parity-dapps"]
|
||||
jit = ["ethcore/jit"]
|
||||
json-tests = ["ethcore/json-tests"]
|
||||
ci-skip-tests = ["ethcore/ci-skip-tests"]
|
||||
test-heavy = ["ethcore/test-heavy"]
|
||||
evm-debug = ["ethcore/evm-debug"]
|
||||
evm-debug-tests = ["ethcore/evm-debug-tests"]
|
||||
slow-blocks = ["ethcore/slow-blocks"]
|
||||
secretstore = ["ethcore-secretstore", "ethcore-secretstore/accounts"]
|
||||
secretstore = ["ethcore-secretstore"]
|
||||
final = ["parity-version/final"]
|
||||
deadlock_detection = ["parking_lot/deadlock_detection"]
|
||||
# to create a memory profile (requires nightly rust), use e.g.
|
||||
# `heaptrack /path/to/parity <parity params>`,
|
||||
# to visualize a memory profile, use `heaptrack_gui`
|
||||
# or
|
||||
# `valgrind --tool=massif /path/to/parity <parity params>`
|
||||
# and `massif-visualizer` for visualization
|
||||
memory_profiling = []
|
||||
# hardcode version number 1.3.7 of parity to force an update
|
||||
# in order to manually test that parity fall-over to the local version
|
||||
# in case of invalid or deprecated command line arguments are entered
|
||||
test-updater = ["parity-updater/test-updater"]
|
||||
|
||||
[lib]
|
||||
path = "parity/lib.rs"
|
||||
|
||||
[[bin]]
|
||||
path = "parity/main.rs"
|
||||
name = "parity"
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
|
||||
[profile.release]
|
||||
debug = false
|
||||
lto = false
|
||||
panic = "abort"
|
||||
|
||||
[workspace]
|
||||
# This should only list projects that are not
|
||||
# in the dependency tree in any other way
|
||||
# (i.e. pretty much only standalone CLI tools)
|
||||
members = [
|
||||
"accounts/ethkey/cli",
|
||||
"accounts/ethstore/cli",
|
||||
"chainspec",
|
||||
"dapps/js-glue",
|
||||
"ethcore/wasm/run",
|
||||
"ethkey/cli",
|
||||
"ethstore/cli",
|
||||
"evmbin",
|
||||
"parity-clib",
|
||||
"whisper/cli",
|
||||
"util/triehash-ethereum",
|
||||
"util/keccak-hasher",
|
||||
"util/patricia-trie-ethereum",
|
||||
"util/fastmap"
|
||||
"transaction-pool",
|
||||
"whisper",
|
||||
]
|
||||
|
||||
[patch.crates-io]
|
||||
heapsize = { git = "https://github.com/cheme/heapsize.git", branch = "ec-macfix" }
|
||||
|
||||
187
README.md
187
README.md
@@ -1,75 +1,102 @@
|
||||

|
||||
# [Parity](https://parity.io/) - fast, light, and robust Ethereum client
|
||||
|
||||
<h2 align="center">The Fastest and most Advanced Ethereum Client.</h2>
|
||||
[](https://gitlab.parity.io/parity/parity/commits/master)
|
||||
[](https://build.snapcraft.io/user/paritytech/parity)
|
||||
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
|
||||
|
||||
<p align="center"><strong><a href="https://github.com/paritytech/parity-ethereum/releases/latest">» Download the latest release «</a></strong></p>
|
||||
- [Download the latest release here.](https://github.com/paritytech/parity/releases/latest)
|
||||
|
||||
<p align="center"><a href="https://gitlab.parity.io/parity/parity-ethereum/commits/master" target="_blank"><img src="https://gitlab.parity.io/parity/parity-ethereum/badges/master/build.svg" /></a>
|
||||
<a href="https://www.gnu.org/licenses/gpl-3.0.en.html" target="_blank"><img src="https://img.shields.io/badge/license-GPL%20v3-green.svg" /></a></p>
|
||||
### Join the chat!
|
||||
|
||||
**Built for mission-critical use**: Miners, service providers, and exchanges need fast synchronisation and maximum uptime. Parity Ethereum provides the core infrastructure essential for speedy and reliable services.
|
||||
Get in touch with us on Gitter:
|
||||
[](https://gitter.im/paritytech/parity)
|
||||
[](https://gitter.im/paritytech/parity.js)
|
||||
[](https://gitter.im/paritytech/parity/miners)
|
||||
[](https://gitter.im/paritytech/parity-poa)
|
||||
|
||||
- Clean, modular codebase for easy customisation
|
||||
- Advanced CLI-based client
|
||||
- Minimal memory and storage footprint
|
||||
- Synchronise in hours, not days with Warp Sync
|
||||
- Modular for light integration into your service or product
|
||||
Or join our community on Matrix:
|
||||
[](https://riot.im/app/#/group/+parity:matrix.parity.io)
|
||||
|
||||
## Technical Overview
|
||||
Be sure to check out [our wiki](https://paritytech.github.io/wiki/) and the [internal documentation](https://paritytech.github.io/parity/ethcore/index.html) for more information.
|
||||
|
||||
Parity Ethereum's goal is to be the fastest, lightest, and most secure Ethereum client. We are developing Parity Ethereum using the sophisticated and cutting-edge **Rust programming language**. Parity Ethereum is licensed under the GPLv3 and can be used for all your Ethereum needs.
|
||||
----
|
||||
|
||||
By default, Parity Ethereum runs a JSON-RPC HTTP server on port `:8545` and a Web-Sockets server on port `:8546`. This is fully configurable and supports a number of APIs.
|
||||
## About Parity
|
||||
|
||||
If you run into problems while using Parity Ethereum, check out the [wiki for documentation](https://wiki.parity.io/), feel free to [file an issue in this repository](https://github.com/paritytech/parity-ethereum/issues/new), or hop on our [Gitter](https://gitter.im/paritytech/parity) or [Riot](https://riot.im/app/#/group/+parity:matrix.parity.io) chat room to ask a question. We are glad to help! **For security-critical issues**, please refer to the security policy outlined in [SECURITY.md](SECURITY.md).
|
||||
Parity's goal is to be the fastest, lightest, and most secure Ethereum client. We are developing Parity using the sophisticated and cutting-edge Rust programming language. Parity is licensed under the GPLv3, and can be used for all your Ethereum needs.
|
||||
|
||||
Parity Ethereum's current beta-release is 2.1. You can download it at [the releases page](https://github.com/paritytech/parity-ethereum/releases) or follow the instructions below to build from source. Please, mind the [CHANGELOG.md](CHANGELOG.md) for a list of all changes between different versions.
|
||||
Parity comes with a built-in wallet. To access [Parity Wallet](http://web3.site/) simply go to http://web3.site/ (if you don't have access to the internet, but still want to use the service, you can also use http://127.0.0.1:8180/). It includes various functionality allowing you to:
|
||||
|
||||
## Build Dependencies
|
||||
- create and manage your Ethereum accounts;
|
||||
- manage your Ether and any Ethereum tokens;
|
||||
- create and register your own tokens;
|
||||
- and much more.
|
||||
|
||||
Parity Ethereum requires **latest stable Rust version** to build.
|
||||
By default, Parity will also run a JSONRPC server on `127.0.0.1:8545` and a websockets server on `127.0.0.1:8546`. This is fully configurable and supports a number of APIs.
|
||||
|
||||
We recommend installing Rust through [rustup](https://www.rustup.rs/). If you don't already have `rustup`, you can install it like this:
|
||||
If you run into an issue while using Parity, feel free to file one in this repository or hop on our [Gitter](https://gitter.im/paritytech/parity) or [Riot](https://riot.im/app/#/group/+parity:matrix.parity.io) chat room to ask a question. We are glad to help!
|
||||
|
||||
**For security-critical issues**, please refer to the security policy outlined in [SECURITY.MD](SECURITY.md).
|
||||
|
||||
Parity's current release is 1.8. You can download it at https://github.com/paritytech/parity/releases or follow the instructions below to build from source.
|
||||
|
||||
----
|
||||
|
||||
## Build dependencies
|
||||
|
||||
**Parity requires Rust version 1.21.0 to build**
|
||||
|
||||
We recommend installing Rust through [rustup](https://www.rustup.rs/). If you don't already have rustup, you can install it like this:
|
||||
|
||||
- Linux:
|
||||
```bash
|
||||
$ curl https://sh.rustup.rs -sSf | sh
|
||||
```
|
||||
```bash
|
||||
$ curl https://sh.rustup.rs -sSf | sh
|
||||
```
|
||||
|
||||
Parity Ethereum also requires `gcc`, `g++`, `libudev-dev`, `pkg-config`, `file`, `make`, and `cmake` packages to be installed.
|
||||
Parity also requires `gcc`, `g++`, `libssl-dev`/`openssl`, `libudev-dev` and `pkg-config` packages to be installed.
|
||||
|
||||
- OSX:
|
||||
```bash
|
||||
$ curl https://sh.rustup.rs -sSf | sh
|
||||
```
|
||||
```bash
|
||||
$ curl https://sh.rustup.rs -sSf | sh
|
||||
```
|
||||
|
||||
`clang` is required. It comes with Xcode command line tools or can be installed with homebrew.
|
||||
`clang` is required. It comes with Xcode command line tools or can be installed with homebrew.
|
||||
|
||||
- Windows
|
||||
Make sure you have Visual Studio 2015 with C++ support installed. Next, download and run the `rustup` installer from
|
||||
https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe, start "VS2015 x64 Native Tools Command Prompt", and use the following command to install and set up the `msvc` toolchain:
|
||||
Make sure you have Visual Studio 2015 with C++ support installed. Next, download and run the rustup installer from
|
||||
https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe, start "VS2015 x64 Native Tools Command Prompt", and use the following command to install and set up the msvc toolchain:
|
||||
```bash
|
||||
$ rustup default stable-x86_64-pc-windows-msvc
|
||||
$ rustup default stable-x86_64-pc-windows-msvc
|
||||
```
|
||||
|
||||
Once you have `rustup` installed, then you need to install:
|
||||
* [Perl](https://www.perl.org)
|
||||
* [Yasm](https://yasm.tortall.net)
|
||||
Once you have rustup, install Parity or download and build from source
|
||||
|
||||
Make sure that these binaries are in your `PATH`. After that, you should be able to build Parity Ethereum from source.
|
||||
----
|
||||
|
||||
## Build from Source Code
|
||||
## Install from the snap store
|
||||
|
||||
In any of the [supported Linux distros](https://snapcraft.io/docs/core/install):
|
||||
|
||||
```bash
|
||||
# download Parity Ethereum code
|
||||
$ git clone https://github.com/paritytech/parity-ethereum
|
||||
$ cd parity-ethereum
|
||||
|
||||
# build in release mode
|
||||
$ cargo build --release --features final
|
||||
sudo snap install parity --edge
|
||||
```
|
||||
|
||||
This produces an executable in the `./target/release` subdirectory.
|
||||
(Note that this is an experimental and unstable release, at the moment)
|
||||
|
||||
----
|
||||
|
||||
## Build from source
|
||||
|
||||
```bash
|
||||
# download Parity code
|
||||
$ git clone https://github.com/paritytech/parity
|
||||
$ cd parity
|
||||
|
||||
# build in release mode
|
||||
$ cargo build --release
|
||||
```
|
||||
|
||||
This will produce an executable in the `./target/release` subdirectory.
|
||||
|
||||
Note: if cargo fails to parse manifest try:
|
||||
|
||||
@@ -77,80 +104,46 @@ Note: if cargo fails to parse manifest try:
|
||||
$ ~/.cargo/bin/cargo build --release
|
||||
```
|
||||
|
||||
Note, when compiling a crate and you receive errors, it's in most cases your outdated version of Rust, or some of your crates have to be recompiled. Cleaning the repository will most likely solve the issue if you are on the latest stable version of Rust, try:
|
||||
Note: When compiling a crate and you receive the following error:
|
||||
|
||||
```
|
||||
error: the crate is compiled with the panic strategy `abort` which is incompatible with this crate's strategy of `unwind`
|
||||
```
|
||||
|
||||
Cleaning the repository will most likely solve the issue, try:
|
||||
|
||||
```bash
|
||||
$ cargo clean
|
||||
```
|
||||
|
||||
This always compiles the latest nightly builds. If you want to build stable or beta, do a
|
||||
This will always compile the latest nightly builds. If you want to build stable or beta, do a `git checkout stable` or `git checkout beta` first.
|
||||
|
||||
----
|
||||
|
||||
## Simple one-line installer for Mac and Ubuntu
|
||||
|
||||
```bash
|
||||
$ git checkout stable
|
||||
bash <(curl https://get.parity.io -Lk)
|
||||
```
|
||||
|
||||
or
|
||||
The one-line installer always defaults to the latest beta release.
|
||||
|
||||
```bash
|
||||
$ git checkout beta
|
||||
```
|
||||
|
||||
## Simple One-Line Installer for Mac and Linux
|
||||
|
||||
```bash
|
||||
bash <(curl https://get.parity.io -L)
|
||||
```
|
||||
|
||||
The one-line installer always defaults to the latest beta release. To install a stable release, run:
|
||||
|
||||
```bash
|
||||
bash <(curl https://get.parity.io -L) -r stable
|
||||
```
|
||||
|
||||
## Start Parity Ethereum
|
||||
## Start Parity
|
||||
|
||||
### Manually
|
||||
|
||||
To start Parity Ethereum manually, just run
|
||||
To start Parity manually, just run
|
||||
|
||||
```bash
|
||||
$ ./target/release/parity
|
||||
```
|
||||
|
||||
so Parity Ethereum begins syncing the Ethereum blockchain.
|
||||
and Parity will begin syncing the Ethereum blockchain.
|
||||
|
||||
### Using `systemd` service file
|
||||
### Using systemd service file
|
||||
|
||||
To start Parity Ethereum as a regular user using `systemd` init:
|
||||
To start Parity as a regular user using systemd init:
|
||||
|
||||
1. Copy `./scripts/parity.service` to your
|
||||
`systemd` user directory (usually `~/.config/systemd/user`).
|
||||
2. Copy release to bin folder, write `sudo install ./target/release/parity /usr/bin/parity`
|
||||
3. To configure Parity Ethereum, write a `/etc/parity/config.toml` config file, see [Configuring Parity Ethereum](https://paritytech.github.io/wiki/Configuring-Parity) for details.
|
||||
|
||||
## Parity Ethereum toolchain
|
||||
|
||||
In addition to the Parity Ethereum client, there are additional tools in this repository available:
|
||||
|
||||
- [evmbin](https://github.com/paritytech/parity-ethereum/blob/master/evmbin/) - EVM implementation for Parity Ethereum.
|
||||
- [ethabi](https://github.com/paritytech/ethabi) - Parity Ethereum function calls encoding.
|
||||
- [ethstore](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethstore) - Parity Ethereum key management.
|
||||
- [ethkey](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethkey) - Parity Ethereum keys generator.
|
||||
- [whisper](https://github.com/paritytech/parity-ethereum/blob/master/whisper/) - Implementation of Whisper-v2 PoC.
|
||||
|
||||
## Join the chat!
|
||||
|
||||
Questions? Get in touch with us on Gitter:
|
||||
[](https://gitter.im/paritytech/parity)
|
||||
[](https://gitter.im/paritytech/parity.js)
|
||||
[](https://gitter.im/paritytech/parity/miners)
|
||||
[](https://gitter.im/paritytech/parity-poa)
|
||||
|
||||
Alternatively, join our community on Matrix:
|
||||
[](https://riot.im/app/#/group/+parity:matrix.parity.io)
|
||||
|
||||
## Documentation
|
||||
|
||||
Official website: https://parity.io
|
||||
|
||||
Be sure to [check out our wiki](https://wiki.parity.io) for more information.
|
||||
systemd user directory (usually `~/.config/systemd/user`).
|
||||
2. To configure Parity, write a `/etc/parity/config.toml` config file, see [Configuring Parity](https://github.com/paritytech/parity/wiki/Configuring-Parity) for details.
|
||||
|
||||
56
SECURITY.md
56
SECURITY.md
@@ -1,49 +1,13 @@
|
||||
# Security Policy
|
||||
|
||||
Parity Technologies is committed to resolving security vulnerabilities in our software quickly and carefully. We take the necessary steps to minimize risk, provide timely information, and deliver vulnerability fixes and mitigations required to address security issues.
|
||||
For security inquiries or vulnerability reports, please send a message to security@parity.io.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
Please use a descriptive subject line so we can identify the report as such.
|
||||
|
||||
Security vulnerabilities in Parity software should be reported by email to security@parity.io. If you think your report might be eligible for the Parity Bug Bounty Program, your email should be send to bugbounty@parity.io.
|
||||
If you send a report, we will respond to the e-mail within 48 hours, and provide regular updates from that time onwards.
|
||||
|
||||
Your report should include the following:
|
||||
|
||||
- your name
|
||||
- description of the vulnerability
|
||||
- attack scenario (if any)
|
||||
- components
|
||||
- reproduction
|
||||
- other details
|
||||
|
||||
Try to include as much information in your report as you can, including a description of the vulnerability, its potential impact, and steps for reproducing it. Be sure to use a descriptive subject line.
|
||||
|
||||
You'll receive a response to your email within two business days indicating the next steps in handling your report. We encourage finders to use encrypted communication channels to protect the confidentiality of vulnerability reports. You can encrypt your report using our public key. This key is [on MIT's key server](https://pgp.mit.edu/pks/lookup?op=get&search=0x5D0F03018D07DE73) server and reproduced below.
|
||||
|
||||
After the initial reply to your report, our team will endeavor to keep you informed of the progress being made towards a fix. These updates will be sent at least every five business days.
|
||||
|
||||
Thank you for taking the time to responsibly disclose any vulnerabilities you find.
|
||||
|
||||
## Responsible Investigation and Reporting
|
||||
|
||||
Responsible investigation and reporting includes, but isn't limited to, the following:
|
||||
|
||||
- Don't violate the privacy of other users, destroy data, etc.
|
||||
- Don’t defraud or harm Parity Technologies Ltd or its users during your research; you should make a good faith effort to not interrupt or degrade our services.
|
||||
- Don't target our physical security measures, or attempt to use social engineering, spam, distributed denial of service (DDOS) attacks, etc.
|
||||
- Initially report the bug only to us and not to anyone else.
|
||||
- Give us a reasonable amount of time to fix the bug before disclosing it to anyone else, and give us adequate written warning before disclosing it to anyone else.
|
||||
- In general, please investigate and report bugs in a way that makes a reasonable, good faith effort not to be disruptive or harmful to us or our users. Otherwise your actions might be interpreted as an attack rather than an effort to be helpful.
|
||||
|
||||
## Bug Bounty Program
|
||||
|
||||
Our Bug Bounty Program allows us to recognise and reward members of the Parity community for helping us find and address significant bugs, in accordance with the terms of the Parity Bug Bounty Program. A detailed description on eligibility, rewards, legal information and terms & conditions for contributors can be found on [our website](https://paritytech.io/bug-bounty.html).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Plaintext PGP Key
|
||||
If you would like to encrypt your report, please use the PGP key provided below.
|
||||
It is also reproduced [on MIT's key server](https://pgp.mit.edu/pks/lookup?op=get&search=0x5D0F03018D07DE73)
|
||||
|
||||
```
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
@@ -78,3 +42,13 @@ ETBD1Q==
|
||||
=K9Qw
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
```
|
||||
|
||||
Important Legal Information:
|
||||
|
||||
Your submission might be eligible for a bug bounty. The bug bounty program is an experimental and discretionary rewards program for the Parity community to reward those who are helping to improve the Parity software. Rewards are at the sole discretion of Parity Technologies Ltd..
|
||||
|
||||
We are not able to issue rewards to individuals who are on sanctions lists or who are in countries on sanctions lists (e.g. North Korea, Iran, etc).
|
||||
|
||||
You are responsible for all taxes. All rewards are subject to applicable law.
|
||||
|
||||
Finally, your testing must not violate any law or compromise any data that is not yours.
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
[package]
|
||||
description = "Account management for Parity Ethereum"
|
||||
homepage = "http://parity.io"
|
||||
license = "GPL-3.0"
|
||||
name = "ethcore-accounts"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
common-types = { path = "../ethcore/types" }
|
||||
ethkey = { path = "ethkey" }
|
||||
ethstore = { path = "ethstore" }
|
||||
log = "0.4"
|
||||
parking_lot = "0.7"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))'.dependencies]
|
||||
hardware-wallet = { path = "hw" }
|
||||
|
||||
[target.'cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))'.dependencies]
|
||||
fake-hardware-wallet = { path = "fake-hardware-wallet" }
|
||||
|
||||
[dev-dependencies]
|
||||
ethereum-types = "0.4"
|
||||
tempdir = "0.3"
|
||||
@@ -1,189 +0,0 @@
|
||||
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum 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 Ethereum 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 Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use secp256k1;
|
||||
use std::io;
|
||||
use parity_crypto::error::SymmError;
|
||||
|
||||
quick_error! {
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Secp(e: secp256k1::Error) {
|
||||
display("secp256k1 error: {}", e)
|
||||
cause(e)
|
||||
from()
|
||||
}
|
||||
Io(e: io::Error) {
|
||||
display("i/o error: {}", e)
|
||||
cause(e)
|
||||
from()
|
||||
}
|
||||
InvalidMessage {
|
||||
display("invalid message")
|
||||
}
|
||||
Symm(e: SymmError) {
|
||||
cause(e)
|
||||
from()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ECDH functions
|
||||
pub mod ecdh {
|
||||
use secp256k1::{self, ecdh, key};
|
||||
use super::Error;
|
||||
use {Secret, Public, SECP256K1};
|
||||
|
||||
/// Agree on a shared secret
|
||||
pub fn agree(secret: &Secret, public: &Public) -> Result<Secret, Error> {
|
||||
let context = &SECP256K1;
|
||||
let pdata = {
|
||||
let mut temp = [4u8; 65];
|
||||
(&mut temp[1..65]).copy_from_slice(&public[0..64]);
|
||||
temp
|
||||
};
|
||||
|
||||
let publ = key::PublicKey::from_slice(context, &pdata)?;
|
||||
let sec = key::SecretKey::from_slice(context, &secret)?;
|
||||
let shared = ecdh::SharedSecret::new_raw(context, &publ, &sec);
|
||||
|
||||
Secret::from_unsafe_slice(&shared[0..32])
|
||||
.map_err(|_| Error::Secp(secp256k1::Error::InvalidSecretKey))
|
||||
}
|
||||
}
|
||||
|
||||
/// ECIES function
|
||||
pub mod ecies {
|
||||
use parity_crypto::{aes, digest, hmac, is_equal};
|
||||
use ethereum_types::H128;
|
||||
use super::{ecdh, Error};
|
||||
use {Random, Generator, Public, Secret};
|
||||
|
||||
/// Encrypt a message with a public key, writing an HMAC covering both
|
||||
/// the plaintext and authenticated data.
|
||||
///
|
||||
/// Authenticated data may be empty.
|
||||
pub fn encrypt(public: &Public, auth_data: &[u8], plain: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
let r = Random.generate()?;
|
||||
let z = ecdh::agree(r.secret(), public)?;
|
||||
let mut key = [0u8; 32];
|
||||
kdf(&z, &[0u8; 0], &mut key);
|
||||
|
||||
let ekey = &key[0..16];
|
||||
let mkey = hmac::SigKey::sha256(&digest::sha256(&key[16..32]));
|
||||
|
||||
let mut msg = vec![0u8; 1 + 64 + 16 + plain.len() + 32];
|
||||
msg[0] = 0x04u8;
|
||||
{
|
||||
let msgd = &mut msg[1..];
|
||||
msgd[0..64].copy_from_slice(r.public());
|
||||
let iv = H128::random();
|
||||
msgd[64..80].copy_from_slice(&iv);
|
||||
{
|
||||
let cipher = &mut msgd[(64 + 16)..(64 + 16 + plain.len())];
|
||||
aes::encrypt_128_ctr(ekey, &iv, plain, cipher)?;
|
||||
}
|
||||
let mut hmac = hmac::Signer::with(&mkey);
|
||||
{
|
||||
let cipher_iv = &msgd[64..(64 + 16 + plain.len())];
|
||||
hmac.update(cipher_iv);
|
||||
}
|
||||
hmac.update(auth_data);
|
||||
let sig = hmac.sign();
|
||||
msgd[(64 + 16 + plain.len())..].copy_from_slice(&sig);
|
||||
}
|
||||
Ok(msg)
|
||||
}
|
||||
|
||||
/// Decrypt a message with a secret key, checking HMAC for ciphertext
|
||||
/// and authenticated data validity.
|
||||
pub fn decrypt(secret: &Secret, auth_data: &[u8], encrypted: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
let meta_len = 1 + 64 + 16 + 32;
|
||||
if encrypted.len() < meta_len || encrypted[0] < 2 || encrypted[0] > 4 {
|
||||
return Err(Error::InvalidMessage); //invalid message: publickey
|
||||
}
|
||||
|
||||
let e = &encrypted[1..];
|
||||
let p = Public::from_slice(&e[0..64]);
|
||||
let z = ecdh::agree(secret, &p)?;
|
||||
let mut key = [0u8; 32];
|
||||
kdf(&z, &[0u8; 0], &mut key);
|
||||
|
||||
let ekey = &key[0..16];
|
||||
let mkey = hmac::SigKey::sha256(&digest::sha256(&key[16..32]));
|
||||
|
||||
let clen = encrypted.len() - meta_len;
|
||||
let cipher_with_iv = &e[64..(64+16+clen)];
|
||||
let cipher_iv = &cipher_with_iv[0..16];
|
||||
let cipher_no_iv = &cipher_with_iv[16..];
|
||||
let msg_mac = &e[(64+16+clen)..];
|
||||
|
||||
// Verify tag
|
||||
let mut hmac = hmac::Signer::with(&mkey);
|
||||
hmac.update(cipher_with_iv);
|
||||
hmac.update(auth_data);
|
||||
let mac = hmac.sign();
|
||||
|
||||
if !is_equal(&mac.as_ref()[..], msg_mac) {
|
||||
return Err(Error::InvalidMessage);
|
||||
}
|
||||
|
||||
let mut msg = vec![0u8; clen];
|
||||
aes::decrypt_128_ctr(ekey, cipher_iv, cipher_no_iv, &mut msg[..])?;
|
||||
Ok(msg)
|
||||
}
|
||||
|
||||
fn kdf(secret: &Secret, s1: &[u8], dest: &mut [u8]) {
|
||||
// SEC/ISO/Shoup specify counter size SHOULD be equivalent
|
||||
// to size of hash output, however, it also notes that
|
||||
// the 4 bytes is okay. NIST specifies 4 bytes.
|
||||
let mut ctr = 1u32;
|
||||
let mut written = 0usize;
|
||||
while written < dest.len() {
|
||||
let mut hasher = digest::Hasher::sha256();
|
||||
let ctrs = [(ctr >> 24) as u8, (ctr >> 16) as u8, (ctr >> 8) as u8, ctr as u8];
|
||||
hasher.update(&ctrs);
|
||||
hasher.update(secret);
|
||||
hasher.update(s1);
|
||||
let d = hasher.finish();
|
||||
&mut dest[written..(written + 32)].copy_from_slice(&d);
|
||||
written += 32;
|
||||
ctr += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ecies;
|
||||
use {Random, Generator};
|
||||
|
||||
#[test]
|
||||
fn ecies_shared() {
|
||||
let kp = Random.generate().unwrap();
|
||||
let message = b"So many books, so little time";
|
||||
|
||||
let shared = b"shared";
|
||||
let wrong_shared = b"incorrect";
|
||||
let encrypted = ecies::encrypt(kp.public(), shared, message).unwrap();
|
||||
assert!(encrypted[..] != message[..]);
|
||||
assert_eq!(encrypted[0], 0x04);
|
||||
|
||||
assert!(ecies::decrypt(kp.secret(), wrong_shared, &encrypted).is_err());
|
||||
let decrypted = ecies::decrypt(kp.secret(), shared, &encrypted).unwrap();
|
||||
assert_eq!(decrypted[..message.len()], message[..]);
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum 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 Ethereum 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 Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{fmt, ptr};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Password(String);
|
||||
|
||||
impl fmt::Debug for Password {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Password(******)")
|
||||
}
|
||||
}
|
||||
|
||||
impl Password {
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
self.0.as_bytes()
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.0.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
// Custom drop impl to zero out memory.
|
||||
impl Drop for Password {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
for byte_ref in self.0.as_mut_vec() {
|
||||
ptr::write_volatile(byte_ref, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Password {
|
||||
fn from(s: String) -> Password {
|
||||
Password(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for Password {
|
||||
fn from(s: &'a str) -> Password {
|
||||
Password::from(String::from(s))
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum 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 Ethereum 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 Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{cmp, thread};
|
||||
use std::sync::Arc;
|
||||
use std::collections::VecDeque;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use ethstore::{ethkey::Password, PresaleWallet, Error};
|
||||
use num_cpus;
|
||||
|
||||
pub fn run(passwords: VecDeque<Password>, wallet_path: &str) -> Result<(), Error> {
|
||||
let passwords = Arc::new(Mutex::new(passwords));
|
||||
|
||||
let mut handles = Vec::new();
|
||||
|
||||
for _ in 0..num_cpus::get() {
|
||||
let passwords = passwords.clone();
|
||||
let wallet = PresaleWallet::open(&wallet_path)?;
|
||||
handles.push(thread::spawn(move || {
|
||||
look_for_password(passwords, wallet);
|
||||
}));
|
||||
}
|
||||
|
||||
for handle in handles {
|
||||
handle.join().map_err(|err| Error::Custom(format!("Error finishing thread: {:?}", err)))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn look_for_password(passwords: Arc<Mutex<VecDeque<Password>>>, wallet: PresaleWallet) {
|
||||
let mut counter = 0;
|
||||
while !passwords.lock().is_empty() {
|
||||
let package = {
|
||||
let mut passwords = passwords.lock();
|
||||
let len = passwords.len();
|
||||
passwords.split_off(cmp::min(len, 32))
|
||||
};
|
||||
for pass in package {
|
||||
counter += 1;
|
||||
match wallet.decrypt(&pass) {
|
||||
Ok(_) => {
|
||||
println!("Found password: {}", pass.as_str());
|
||||
passwords.lock().clear();
|
||||
return;
|
||||
},
|
||||
_ if counter % 100 == 0 => print!("."),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum 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 Ethereum 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 Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate tempdir;
|
||||
use std::process::Command;
|
||||
use tempdir::TempDir;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
fn run(args: &[&str]) -> String {
|
||||
let output = Command::new("cargo")
|
||||
.args(&["run", "--"])
|
||||
.args(args)
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success());
|
||||
String::from_utf8(output.stdout).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli_cmd() {
|
||||
Command::new("cargo")
|
||||
.arg("build")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
let dir = TempDir::new("test-vault").unwrap();
|
||||
|
||||
let mut passwd = File::create(dir.path().join("test-password")).unwrap();
|
||||
writeln!(passwd, "password").unwrap();
|
||||
|
||||
let mut passwd2 = File::create(dir.path().join("test-vault-addr")).unwrap();
|
||||
writeln!(passwd2, "password2").unwrap();
|
||||
|
||||
let test_password_buf = dir.path().join("test-password");
|
||||
let test_password: &str = test_password_buf.to_str().unwrap();
|
||||
let dir_str: &str = dir.path().to_str().unwrap();
|
||||
let test_vault_addr_buf = dir.path().join("test-vault-addr");
|
||||
let test_vault_addr = test_vault_addr_buf.to_str().unwrap();
|
||||
|
||||
run(&["create-vault", "test-vault", test_password, "--dir", dir_str]);
|
||||
|
||||
let output = run(&["insert", "7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5",
|
||||
test_vault_addr,
|
||||
"--dir", dir_str,
|
||||
"--vault", "test-vault",
|
||||
"--vault-pwd", test_password]);
|
||||
let address = output.trim();
|
||||
|
||||
let output = run(&["list",
|
||||
"--dir", dir_str,
|
||||
"--vault", "test-vault",
|
||||
"--vault-pwd", test_password]);
|
||||
assert_eq!(output, " 0: 0xa8fa5dd30a87bb9e3288d604eb74949c515ab66e\n");
|
||||
|
||||
let output = run(&["sign", &address[2..],
|
||||
test_vault_addr,
|
||||
"7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5",
|
||||
"--dir", dir_str,
|
||||
"--vault", "test-vault",
|
||||
"--vault-pwd", test_password]);
|
||||
assert_eq!(output, "0x54ab6e5cf0c5cb40043fdca5d15d611a3a94285414a076dafecc8dc9c04183f413296a3defff61092c0bb478dc9887ec01070e1275234211208fb8f4be4a9b0101\n");
|
||||
|
||||
let output = run(&["public", &address[2..], test_vault_addr,
|
||||
"--dir", dir_str,
|
||||
"--vault", "test-vault",
|
||||
"--vault-pwd", test_password]);
|
||||
assert_eq!(output, "0x35f222d88b80151857a2877826d940104887376a94c1cbd2c8c7c192eb701df88a18a4ecb8b05b1466c5b3706042027b5e079fe3a3683e66d822b0e047aa3418\n");
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
[package]
|
||||
description = "Fake hardware-wallet, for OS' that don't support libusb"
|
||||
name = "fake-hardware-wallet"
|
||||
version = "0.0.1"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
ethereum-types = "0.4"
|
||||
ethkey = { path = "../../accounts/ethkey" }
|
||||
@@ -1,101 +0,0 @@
|
||||
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum 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 Ethereum 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 Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Dummy module for platforms that does not provide support for hardware wallets (libusb)
|
||||
|
||||
extern crate ethereum_types;
|
||||
extern crate ethkey;
|
||||
|
||||
use std::fmt;
|
||||
use ethereum_types::U256;
|
||||
use ethkey::{Address, Signature};
|
||||
|
||||
pub struct WalletInfo {
|
||||
pub address: Address,
|
||||
pub name: String,
|
||||
pub manufacturer: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// `ErrorType` for devices with no `hardware wallet`
|
||||
pub enum Error {
|
||||
NoWallet,
|
||||
KeyNotFound,
|
||||
}
|
||||
|
||||
pub struct TransactionInfo {
|
||||
/// Nonce
|
||||
pub nonce: U256,
|
||||
/// Gas price
|
||||
pub gas_price: U256,
|
||||
/// Gas limit
|
||||
pub gas_limit: U256,
|
||||
/// Receiver
|
||||
pub to: Option<Address>,
|
||||
/// Value
|
||||
pub value: U256,
|
||||
/// Data
|
||||
pub data: Vec<u8>,
|
||||
/// Chain ID
|
||||
pub chain_id: Option<u64>,
|
||||
}
|
||||
|
||||
pub enum KeyPath {
|
||||
/// Ethereum.
|
||||
Ethereum,
|
||||
/// Ethereum classic.
|
||||
EthereumClassic,
|
||||
}
|
||||
|
||||
/// `HardwareWalletManager` for devices with no `hardware wallet`
|
||||
pub struct HardwareWalletManager;
|
||||
|
||||
impl HardwareWalletManager {
|
||||
pub fn new() -> Result<Self, Error> {
|
||||
Err(Error::NoWallet)
|
||||
}
|
||||
|
||||
pub fn set_key_path(&self, _key_path: KeyPath) {}
|
||||
|
||||
pub fn wallet_info(&self, _: &Address) -> Option<WalletInfo> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn list_wallets(&self) -> Vec<WalletInfo> {
|
||||
Vec::with_capacity(0)
|
||||
}
|
||||
|
||||
pub fn list_locked_wallets(&self) -> Result<Vec<String>, Error> {
|
||||
Err(Error::NoWallet)
|
||||
}
|
||||
|
||||
pub fn pin_matrix_ack(&self, _: &str, _: &str) -> Result<bool, Error> {
|
||||
Err(Error::NoWallet)
|
||||
}
|
||||
|
||||
pub fn sign_transaction(&self, _address: &Address, _transaction: &TransactionInfo, _rlp_transaction: &[u8]) -> Result<Signature, Error> {
|
||||
Err(Error::NoWallet) }
|
||||
|
||||
pub fn sign_message(&self, _address: &Address, _msg: &[u8]) -> Result<Signature, Error> {
|
||||
Err(Error::NoWallet)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "No hardware wallet!!")
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,402 +0,0 @@
|
||||
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum 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 Ethereum 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 Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Hardware wallet management.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![warn(warnings)]
|
||||
|
||||
extern crate ethereum_types;
|
||||
extern crate ethkey;
|
||||
extern crate hidapi;
|
||||
extern crate libusb;
|
||||
extern crate parking_lot;
|
||||
extern crate protobuf;
|
||||
extern crate semver;
|
||||
extern crate trezor_sys;
|
||||
|
||||
#[macro_use] extern crate log;
|
||||
#[cfg(test)] extern crate rustc_hex;
|
||||
|
||||
mod ledger;
|
||||
mod trezor;
|
||||
|
||||
use std::sync::{Arc, atomic, atomic::AtomicBool, Weak};
|
||||
use std::{fmt, time::Duration};
|
||||
use std::thread;
|
||||
|
||||
use ethereum_types::U256;
|
||||
use ethkey::{Address, Signature};
|
||||
use parking_lot::Mutex;
|
||||
|
||||
const HID_GLOBAL_USAGE_PAGE: u16 = 0xFF00;
|
||||
const HID_USB_DEVICE_CLASS: u8 = 0;
|
||||
const MAX_POLLING_DURATION: Duration = Duration::from_millis(500);
|
||||
const USB_EVENT_POLLING_INTERVAL: Duration = Duration::from_millis(500);
|
||||
|
||||
/// `HardwareWallet` device
|
||||
#[derive(Debug)]
|
||||
pub struct Device {
|
||||
path: String,
|
||||
info: WalletInfo,
|
||||
}
|
||||
|
||||
/// `Wallet` trait
|
||||
pub trait Wallet<'a> {
|
||||
/// Error
|
||||
type Error;
|
||||
/// Transaction data format
|
||||
type Transaction;
|
||||
|
||||
/// Sign transaction data with wallet managing `address`.
|
||||
fn sign_transaction(&self, address: &Address, transaction: Self::Transaction) -> Result<Signature, Self::Error>;
|
||||
|
||||
/// Set key derivation path for a chain.
|
||||
fn set_key_path(&self, key_path: KeyPath);
|
||||
|
||||
/// Re-populate device list
|
||||
/// Note, this assumes all devices are iterated over and updated
|
||||
fn update_devices(&self, device_direction: DeviceDirection) -> Result<usize, Self::Error>;
|
||||
|
||||
/// Read device info
|
||||
fn read_device(&self, usb: &hidapi::HidApi, dev_info: &hidapi::HidDeviceInfo) -> Result<Device, Self::Error>;
|
||||
|
||||
/// List connected and acknowledged wallets
|
||||
fn list_devices(&self) -> Vec<WalletInfo>;
|
||||
|
||||
/// List locked wallets
|
||||
/// This may be moved if it is the wrong assumption, for example this is not supported by Ledger
|
||||
/// Then this method return a empty vector
|
||||
fn list_locked_devices(&self) -> Vec<String>;
|
||||
|
||||
/// Get wallet info.
|
||||
fn get_wallet(&self, address: &Address) -> Option<WalletInfo>;
|
||||
|
||||
/// Generate ethereum address for a Wallet
|
||||
fn get_address(&self, device: &hidapi::HidDevice) -> Result<Option<Address>, Self::Error>;
|
||||
|
||||
/// Open a device using `device path`
|
||||
/// Note, f - is a closure that borrows HidResult<HidDevice>
|
||||
/// HidDevice is in turn a type alias for a `c_void function pointer`
|
||||
/// For further information see:
|
||||
/// * <https://github.com/paritytech/hidapi-rs>
|
||||
/// * <https://github.com/rust-lang/libc>
|
||||
fn open_path<R, F>(&self, f: F) -> Result<R, Self::Error>
|
||||
where F: Fn() -> Result<R, &'static str>;
|
||||
}
|
||||
|
||||
/// Hardware wallet error.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Ledger device error.
|
||||
LedgerDevice(ledger::Error),
|
||||
/// Trezor device error
|
||||
TrezorDevice(trezor::Error),
|
||||
/// USB error.
|
||||
Usb(libusb::Error),
|
||||
/// HID error
|
||||
Hid(String),
|
||||
/// Hardware wallet not found for specified key.
|
||||
KeyNotFound,
|
||||
}
|
||||
|
||||
/// This is the transaction info we need to supply to Trezor message. It's more
|
||||
/// or less a duplicate of `ethcore::transaction::Transaction`, but we can't
|
||||
/// import ethcore here as that would be a circular dependency.
|
||||
pub struct TransactionInfo {
|
||||
/// Nonce
|
||||
pub nonce: U256,
|
||||
/// Gas price
|
||||
pub gas_price: U256,
|
||||
/// Gas limit
|
||||
pub gas_limit: U256,
|
||||
/// Receiver
|
||||
pub to: Option<Address>,
|
||||
/// Value
|
||||
pub value: U256,
|
||||
/// Data
|
||||
pub data: Vec<u8>,
|
||||
/// Chain ID
|
||||
pub chain_id: Option<u64>,
|
||||
}
|
||||
|
||||
/// Hardware wallet information.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WalletInfo {
|
||||
/// Wallet device name.
|
||||
pub name: String,
|
||||
/// Wallet device manufacturer.
|
||||
pub manufacturer: String,
|
||||
/// Wallet device serial number.
|
||||
pub serial: String,
|
||||
/// Ethereum address.
|
||||
pub address: Address,
|
||||
}
|
||||
|
||||
/// Key derivation paths used on hardware wallets.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum KeyPath {
|
||||
/// Ethereum.
|
||||
Ethereum,
|
||||
/// Ethereum classic.
|
||||
EthereumClassic,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
Error::KeyNotFound => write!(f, "Key not found for given address."),
|
||||
Error::LedgerDevice(ref e) => write!(f, "{}", e),
|
||||
Error::TrezorDevice(ref e) => write!(f, "{}", e),
|
||||
Error::Usb(ref e) => write!(f, "{}", e),
|
||||
Error::Hid(ref e) => write!(f, "{}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ledger::Error> for Error {
|
||||
fn from(err: ledger::Error) -> Self {
|
||||
match err {
|
||||
ledger::Error::KeyNotFound => Error::KeyNotFound,
|
||||
_ => Error::LedgerDevice(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<trezor::Error> for Error {
|
||||
fn from(err: trezor::Error) -> Self {
|
||||
match err {
|
||||
trezor::Error::KeyNotFound => Error::KeyNotFound,
|
||||
_ => Error::TrezorDevice(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<libusb::Error> for Error {
|
||||
fn from(err: libusb::Error) -> Self {
|
||||
Error::Usb(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// Specifies the direction of the `HardwareWallet` i.e, whether it arrived or left
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum DeviceDirection {
|
||||
/// Device arrived
|
||||
Arrived,
|
||||
/// Device left
|
||||
Left,
|
||||
}
|
||||
|
||||
impl fmt::Display for DeviceDirection {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
DeviceDirection::Arrived => write!(f, "arrived"),
|
||||
DeviceDirection::Left => write!(f, "left"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Hardware wallet management interface.
|
||||
pub struct HardwareWalletManager {
|
||||
exiting: Arc<AtomicBool>,
|
||||
ledger: Arc<ledger::Manager>,
|
||||
trezor: Arc<trezor::Manager>,
|
||||
}
|
||||
|
||||
impl HardwareWalletManager {
|
||||
/// Hardware wallet constructor
|
||||
pub fn new() -> Result<Self, Error> {
|
||||
let exiting = Arc::new(AtomicBool::new(false));
|
||||
let hidapi = Arc::new(Mutex::new(hidapi::HidApi::new().map_err(|e| Error::Hid(e.to_string().clone()))?));
|
||||
let ledger = ledger::Manager::new(hidapi.clone());
|
||||
let trezor = trezor::Manager::new(hidapi.clone());
|
||||
let usb_context = Arc::new(libusb::Context::new()?);
|
||||
|
||||
let l = ledger.clone();
|
||||
let t = trezor.clone();
|
||||
let exit = exiting.clone();
|
||||
|
||||
// Subscribe to all vendor IDs (VIDs) and product IDs (PIDs)
|
||||
// This means that the `HardwareWalletManager` is responsible to validate the detected device
|
||||
usb_context.register_callback(
|
||||
None, None, Some(HID_USB_DEVICE_CLASS),
|
||||
Box::new(EventHandler::new(
|
||||
Arc::downgrade(&ledger),
|
||||
Arc::downgrade(&trezor)
|
||||
))
|
||||
)?;
|
||||
|
||||
// Hardware event subscriber thread
|
||||
thread::Builder::new()
|
||||
.name("hw_wallet_manager".to_string())
|
||||
.spawn(move || {
|
||||
if let Err(e) = l.update_devices(DeviceDirection::Arrived) {
|
||||
debug!(target: "hw", "Ledger couldn't connect at startup, error: {}", e);
|
||||
}
|
||||
if let Err(e) = t.update_devices(DeviceDirection::Arrived) {
|
||||
debug!(target: "hw", "Trezor couldn't connect at startup, error: {}", e);
|
||||
}
|
||||
|
||||
while !exit.load(atomic::Ordering::Acquire) {
|
||||
if let Err(e) = usb_context.handle_events(Some(USB_EVENT_POLLING_INTERVAL)) {
|
||||
debug!(target: "hw", "HardwareWalletManager event handler error: {}", e);
|
||||
}
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
|
||||
Ok(Self {
|
||||
exiting,
|
||||
trezor,
|
||||
ledger,
|
||||
})
|
||||
}
|
||||
|
||||
/// Select key derivation path for a chain.
|
||||
/// Currently, only one hard-coded keypath is supported
|
||||
/// It is managed by `ethcore/account_provider`
|
||||
pub fn set_key_path(&self, key_path: KeyPath) {
|
||||
self.ledger.set_key_path(key_path);
|
||||
self.trezor.set_key_path(key_path);
|
||||
}
|
||||
|
||||
/// List connected wallets. This only returns wallets that are ready to be used.
|
||||
pub fn list_wallets(&self) -> Vec<WalletInfo> {
|
||||
let mut wallets = Vec::new();
|
||||
wallets.extend(self.ledger.list_devices());
|
||||
wallets.extend(self.trezor.list_devices());
|
||||
wallets
|
||||
}
|
||||
|
||||
/// Return a list of paths to locked hardware wallets
|
||||
/// This is only applicable to Trezor because Ledger only appears as
|
||||
/// a device when it is unlocked
|
||||
pub fn list_locked_wallets(&self) -> Result<Vec<String>, Error> {
|
||||
Ok(self.trezor.list_locked_devices())
|
||||
}
|
||||
|
||||
/// Get connected wallet info.
|
||||
pub fn wallet_info(&self, address: &Address) -> Option<WalletInfo> {
|
||||
if let Some(info) = self.ledger.get_wallet(address) {
|
||||
Some(info)
|
||||
} else {
|
||||
self.trezor.get_wallet(address)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sign a message with the wallet (only supported by Ledger)
|
||||
pub fn sign_message(&self, address: &Address, msg: &[u8]) -> Result<Signature, Error> {
|
||||
if self.ledger.get_wallet(address).is_some() {
|
||||
Ok(self.ledger.sign_message(address, msg)?)
|
||||
} else if self.trezor.get_wallet(address).is_some() {
|
||||
Err(Error::TrezorDevice(trezor::Error::NoSigningMessage))
|
||||
} else {
|
||||
Err(Error::KeyNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sign transaction data with wallet managing `address`.
|
||||
pub fn sign_transaction(&self, address: &Address, t_info: &TransactionInfo, encoded_transaction: &[u8]) -> Result<Signature, Error> {
|
||||
if self.ledger.get_wallet(address).is_some() {
|
||||
Ok(self.ledger.sign_transaction(address, encoded_transaction)?)
|
||||
} else if self.trezor.get_wallet(address).is_some() {
|
||||
Ok(self.trezor.sign_transaction(address, t_info)?)
|
||||
} else {
|
||||
Err(Error::KeyNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a pin to a device at a certain path to unlock it
|
||||
/// This is only applicable to Trezor because Ledger only appears as
|
||||
/// a device when it is unlocked
|
||||
pub fn pin_matrix_ack(&self, path: &str, pin: &str) -> Result<bool, Error> {
|
||||
self.trezor.pin_matrix_ack(path, pin).map_err(Error::TrezorDevice)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for HardwareWalletManager {
|
||||
fn drop(&mut self) {
|
||||
// Indicate to the USB Hotplug handler that it
|
||||
// shall terminate but don't wait for it to terminate.
|
||||
// If it doesn't terminate for some reason USB Hotplug events will be handled
|
||||
// even if the HardwareWalletManger has been dropped
|
||||
self.exiting.store(true, atomic::Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
/// Hardware wallet event handler
|
||||
///
|
||||
/// Note, that this runs to completion and race-conditions can't occur but it can
|
||||
/// stop other events for being processed with an infinite loop or similar
|
||||
struct EventHandler {
|
||||
ledger: Weak<ledger::Manager>,
|
||||
trezor: Weak<trezor::Manager>,
|
||||
}
|
||||
|
||||
impl EventHandler {
|
||||
/// Trezor event handler constructor
|
||||
pub fn new(ledger: Weak<ledger::Manager>, trezor: Weak<trezor::Manager>) -> Self {
|
||||
Self { ledger, trezor }
|
||||
}
|
||||
|
||||
fn extract_device_info(device: &libusb::Device) -> Result<(u16, u16), Error> {
|
||||
let desc = device.device_descriptor()?;
|
||||
Ok((desc.vendor_id(), desc.product_id()))
|
||||
}
|
||||
}
|
||||
|
||||
impl libusb::Hotplug for EventHandler {
|
||||
fn device_arrived(&mut self, device: libusb::Device) {
|
||||
// Upgrade reference to an Arc
|
||||
if let (Some(ledger), Some(trezor)) = (self.ledger.upgrade(), self.trezor.upgrade()) {
|
||||
// Version ID and Product ID are available
|
||||
if let Ok((vid, pid)) = Self::extract_device_info(&device) {
|
||||
if trezor::is_valid_trezor(vid, pid) {
|
||||
if !trezor::try_connect_polling(&trezor, &MAX_POLLING_DURATION, DeviceDirection::Arrived) {
|
||||
trace!(target: "hw", "Trezor device was detected but connection failed");
|
||||
}
|
||||
} else if ledger::is_valid_ledger(vid, pid) {
|
||||
if !ledger::try_connect_polling(&ledger, &MAX_POLLING_DURATION, DeviceDirection::Arrived) {
|
||||
trace!(target: "hw", "Ledger device was detected but connection failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn device_left(&mut self, device: libusb::Device) {
|
||||
// Upgrade reference to an Arc
|
||||
if let (Some(ledger), Some(trezor)) = (self.ledger.upgrade(), self.trezor.upgrade()) {
|
||||
// Version ID and Product ID are available
|
||||
if let Ok((vid, pid)) = Self::extract_device_info(&device) {
|
||||
if trezor::is_valid_trezor(vid, pid) {
|
||||
if !trezor::try_connect_polling(&trezor, &MAX_POLLING_DURATION, DeviceDirection::Left) {
|
||||
trace!(target: "hw", "Trezor device was detected but disconnection failed");
|
||||
}
|
||||
} else if ledger::is_valid_ledger(vid, pid) {
|
||||
if !ledger::try_connect_polling(&ledger, &MAX_POLLING_DURATION, DeviceDirection::Left) {
|
||||
trace!(target: "hw", "Ledger device was detected but disconnection failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper to determine if a device is a valid HID
|
||||
pub fn is_valid_hid_device(usage_page: u16, interface_number: i32) -> bool {
|
||||
usage_page == HID_GLOBAL_USAGE_PAGE || interface_number == HID_USB_DEVICE_CLASS as i32
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum 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 Ethereum 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 Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Account Metadata
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use ethkey::{Address, Password};
|
||||
use serde_derive::{Serialize, Deserialize};
|
||||
use serde_json;
|
||||
|
||||
/// Type of unlock.
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum Unlock {
|
||||
/// If account is unlocked temporarily, it should be locked after first usage.
|
||||
OneTime,
|
||||
/// Account unlocked permanently can always sign message.
|
||||
/// Use with caution.
|
||||
Perm,
|
||||
/// Account unlocked with a timeout
|
||||
Timed(Instant),
|
||||
}
|
||||
|
||||
/// Data associated with account.
|
||||
#[derive(Clone)]
|
||||
pub struct AccountData {
|
||||
pub unlock: Unlock,
|
||||
pub password: Password,
|
||||
}
|
||||
|
||||
/// Collected account metadata
|
||||
#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct AccountMeta {
|
||||
/// The name of the account.
|
||||
pub name: String,
|
||||
/// The rest of the metadata of the account.
|
||||
pub meta: String,
|
||||
/// The 128-bit Uuid of the account, if it has one (brain-wallets don't).
|
||||
pub uuid: Option<String>,
|
||||
}
|
||||
|
||||
impl AccountMeta {
|
||||
/// Read a hash map of Address -> AccountMeta
|
||||
pub fn read<R>(reader: R) -> Result<HashMap<Address, Self>, serde_json::Error> where
|
||||
R: ::std::io::Read,
|
||||
{
|
||||
serde_json::from_reader(reader)
|
||||
}
|
||||
|
||||
/// Write a hash map of Address -> AccountMeta
|
||||
pub fn write<W>(m: &HashMap<Address, Self>, writer: &mut W) -> Result<(), serde_json::Error> where
|
||||
W: ::std::io::Write,
|
||||
{
|
||||
serde_json::to_writer(writer, m)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
// Copyright 2015-2018 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::fmt;
|
||||
|
||||
use ethstore::{Error as SSError};
|
||||
use hardware_wallet::{Error as HardwareError};
|
||||
|
||||
/// Signing error
|
||||
#[derive(Debug)]
|
||||
pub enum SignError {
|
||||
/// Account is not unlocked
|
||||
NotUnlocked,
|
||||
/// Account does not exist.
|
||||
NotFound,
|
||||
/// Low-level hardware device error.
|
||||
Hardware(HardwareError),
|
||||
/// Low-level error from store
|
||||
SStore(SSError),
|
||||
}
|
||||
|
||||
impl fmt::Display for SignError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
SignError::NotUnlocked => write!(f, "Account is locked"),
|
||||
SignError::NotFound => write!(f, "Account does not exist"),
|
||||
SignError::Hardware(ref e) => write!(f, "{}", e),
|
||||
SignError::SStore(ref e) => write!(f, "{}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HardwareError> for SignError {
|
||||
fn from(e: HardwareError) -> Self {
|
||||
SignError::Hardware(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SSError> for SignError {
|
||||
fn from(e: SSError) -> Self {
|
||||
SignError::SStore(e)
|
||||
}
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum 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 Ethereum 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 Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Address Book Store
|
||||
|
||||
use std::{fs, fmt, hash, ops};
|
||||
use std::collections::HashMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use ethkey::Address;
|
||||
use log::{trace, warn};
|
||||
|
||||
use crate::AccountMeta;
|
||||
|
||||
/// Disk-backed map from Address to String. Uses JSON.
|
||||
pub struct AddressBook {
|
||||
cache: DiskMap<Address, AccountMeta>,
|
||||
}
|
||||
|
||||
impl AddressBook {
|
||||
/// Creates new address book at given directory.
|
||||
pub fn new(path: &Path) -> Self {
|
||||
let mut r = AddressBook {
|
||||
cache: DiskMap::new(path, "address_book.json")
|
||||
};
|
||||
r.cache.revert(AccountMeta::read);
|
||||
r
|
||||
}
|
||||
|
||||
/// Creates transient address book (no changes are saved to disk).
|
||||
pub fn transient() -> Self {
|
||||
AddressBook {
|
||||
cache: DiskMap::transient()
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the address book.
|
||||
pub fn get(&self) -> HashMap<Address, AccountMeta> {
|
||||
self.cache.clone()
|
||||
}
|
||||
|
||||
fn save(&self) {
|
||||
self.cache.save(AccountMeta::write)
|
||||
}
|
||||
|
||||
/// Sets new name for given address.
|
||||
pub fn set_name(&mut self, a: Address, name: String) {
|
||||
{
|
||||
let x = self.cache.entry(a)
|
||||
.or_insert_with(|| AccountMeta {name: Default::default(), meta: "{}".to_owned(), uuid: None});
|
||||
x.name = name;
|
||||
}
|
||||
self.save();
|
||||
}
|
||||
|
||||
/// Sets new meta for given address.
|
||||
pub fn set_meta(&mut self, a: Address, meta: String) {
|
||||
{
|
||||
let x = self.cache.entry(a)
|
||||
.or_insert_with(|| AccountMeta {name: "Anonymous".to_owned(), meta: Default::default(), uuid: None});
|
||||
x.meta = meta;
|
||||
}
|
||||
self.save();
|
||||
}
|
||||
|
||||
/// Removes an entry
|
||||
pub fn remove(&mut self, a: Address) {
|
||||
self.cache.remove(&a);
|
||||
self.save();
|
||||
}
|
||||
}
|
||||
|
||||
/// Disk-serializable HashMap
|
||||
#[derive(Debug)]
|
||||
struct DiskMap<K: hash::Hash + Eq, V> {
|
||||
path: PathBuf,
|
||||
cache: HashMap<K, V>,
|
||||
transient: bool,
|
||||
}
|
||||
|
||||
impl<K: hash::Hash + Eq, V> ops::Deref for DiskMap<K, V> {
|
||||
type Target = HashMap<K, V>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.cache
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: hash::Hash + Eq, V> ops::DerefMut for DiskMap<K, V> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.cache
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: hash::Hash + Eq, V> DiskMap<K, V> {
|
||||
pub fn new(path: &Path, file_name: &str) -> Self {
|
||||
let mut path = path.to_owned();
|
||||
path.push(file_name);
|
||||
trace!(target: "diskmap", "path={:?}", path);
|
||||
DiskMap {
|
||||
path: path,
|
||||
cache: HashMap::new(),
|
||||
transient: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn transient() -> Self {
|
||||
let mut map = DiskMap::new(&PathBuf::new(), "diskmap.json".into());
|
||||
map.transient = true;
|
||||
map
|
||||
}
|
||||
|
||||
fn revert<F, E>(&mut self, read: F) where
|
||||
F: Fn(fs::File) -> Result<HashMap<K, V>, E>,
|
||||
E: fmt::Display,
|
||||
{
|
||||
if self.transient { return; }
|
||||
trace!(target: "diskmap", "revert {:?}", self.path);
|
||||
let _ = fs::File::open(self.path.clone())
|
||||
.map_err(|e| trace!(target: "diskmap", "Couldn't open disk map: {}", e))
|
||||
.and_then(|f| read(f).map_err(|e| warn!(target: "diskmap", "Couldn't read disk map: {}", e)))
|
||||
.and_then(|m| {
|
||||
self.cache = m;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
fn save<F, E>(&self, write: F) where
|
||||
F: Fn(&HashMap<K, V>, &mut fs::File) -> Result<(), E>,
|
||||
E: fmt::Display,
|
||||
{
|
||||
if self.transient { return; }
|
||||
trace!(target: "diskmap", "save {:?}", self.path);
|
||||
let _ = fs::File::create(self.path.clone())
|
||||
.map_err(|e| warn!(target: "diskmap", "Couldn't open disk map for writing: {}", e))
|
||||
.and_then(|mut f| {
|
||||
write(&self.cache, &mut f).map_err(|e| warn!(target: "diskmap", "Couldn't write to disk map: {}", e))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::AddressBook;
|
||||
use std::collections::HashMap;
|
||||
use tempdir::TempDir;
|
||||
use crate::account_data::AccountMeta;
|
||||
|
||||
#[test]
|
||||
fn should_save_and_reload_address_book() {
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let mut b = AddressBook::new(tempdir.path());
|
||||
b.set_name(1.into(), "One".to_owned());
|
||||
b.set_meta(1.into(), "{1:1}".to_owned());
|
||||
let b = AddressBook::new(tempdir.path());
|
||||
assert_eq!(b.get(), vec![
|
||||
(1, AccountMeta {name: "One".to_owned(), meta: "{1:1}".to_owned(), uuid: None})
|
||||
].into_iter().map(|(a, b)| (a.into(), b)).collect::<HashMap<_, _>>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_remove_address() {
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let mut b = AddressBook::new(tempdir.path());
|
||||
|
||||
b.set_name(1.into(), "One".to_owned());
|
||||
b.set_name(2.into(), "Two".to_owned());
|
||||
b.set_name(3.into(), "Three".to_owned());
|
||||
b.remove(2.into());
|
||||
|
||||
let b = AddressBook::new(tempdir.path());
|
||||
assert_eq!(b.get(), vec![
|
||||
(1, AccountMeta{name: "One".to_owned(), meta: "{}".to_owned(), uuid: None}),
|
||||
(3, AccountMeta{name: "Three".to_owned(), meta: "{}".to_owned(), uuid: None}),
|
||||
].into_iter().map(|(a, b)| (a.into(), b)).collect::<HashMap<_, _>>());
|
||||
}
|
||||
}
|
||||
35
build.rs
Normal file
35
build.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
// 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/>.
|
||||
|
||||
extern crate rustc_version;
|
||||
|
||||
const MIN_RUSTC_VERSION: &'static str = "1.15.1";
|
||||
|
||||
fn main() {
|
||||
let is = rustc_version::version().unwrap();
|
||||
let required = MIN_RUSTC_VERSION.parse().unwrap();
|
||||
assert!(is >= required, format!("
|
||||
|
||||
It looks like you are compiling Parity with an old rustc compiler {}.
|
||||
Parity requires version {}. Please update your compiler.
|
||||
If you use rustup, try this:
|
||||
|
||||
rustup update stable
|
||||
|
||||
and try building Parity again.
|
||||
|
||||
", is, required));
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
[package]
|
||||
name = "chainspec"
|
||||
version = "0.1.0"
|
||||
authors = ["Marek Kotewicz <marek@parity.io>"]
|
||||
authors = ["debris <marek.kotewicz@gmail.com>"]
|
||||
|
||||
[dependencies]
|
||||
ethjson = { path = "../json" }
|
||||
serde_json = "1.0"
|
||||
serde_ignored = "0.0.4"
|
||||
|
||||
@@ -1,22 +1,8 @@
|
||||
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum 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 Ethereum 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 Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate serde_json;
|
||||
extern crate serde_ignored;
|
||||
extern crate ethjson;
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
use std::{fs, env, process};
|
||||
use ethjson::spec::Spec;
|
||||
|
||||
@@ -39,11 +25,24 @@ fn main() {
|
||||
Err(_) => quit(&format!("{} could not be opened", path)),
|
||||
};
|
||||
|
||||
let spec: Result<Spec, _> = serde_json::from_reader(file);
|
||||
let mut unused = BTreeSet::new();
|
||||
let mut deserializer = serde_json::Deserializer::from_reader(file);
|
||||
|
||||
let spec: Result<Spec, _> = serde_ignored::deserialize(&mut deserializer, |field| {
|
||||
unused.insert(field.to_string());
|
||||
});
|
||||
|
||||
if let Err(err) = spec {
|
||||
quit(&format!("{} {}", path, err.to_string()));
|
||||
}
|
||||
|
||||
if !unused.is_empty() {
|
||||
let err = unused.into_iter()
|
||||
.map(|field| format!("{} unexpected field `{}`", path, field))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
quit(&err);
|
||||
}
|
||||
|
||||
println!("{} is valid", path);
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
[package]
|
||||
authors = ["Parity <admin@parity.io>"]
|
||||
description = "Parity Rpc Client"
|
||||
homepage = "http://parity.io"
|
||||
license = "GPL-3.0"
|
||||
name = "parity-rpc-client"
|
||||
version = "1.4.0"
|
||||
|
||||
[dependencies]
|
||||
futures = "0.1"
|
||||
log = "0.4"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
url = "1.2.0"
|
||||
matches = "0.1"
|
||||
parking_lot = "0.7"
|
||||
jsonrpc-core = "10.0.1"
|
||||
jsonrpc-ws-server = "10.0.1"
|
||||
parity-rpc = { path = "../../rpc" }
|
||||
keccak-hash = "0.1"
|
||||
48
dapps/Cargo.toml
Normal file
48
dapps/Cargo.toml
Normal file
@@ -0,0 +1,48 @@
|
||||
[package]
|
||||
description = "Parity Dapps crate"
|
||||
name = "parity-dapps"
|
||||
version = "1.9.0"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[lib]
|
||||
|
||||
[dependencies]
|
||||
base32 = "0.3"
|
||||
futures = "0.1"
|
||||
futures-cpupool = "0.1"
|
||||
linked-hash-map = "0.5"
|
||||
log = "0.3"
|
||||
parity-dapps-glue = "1.9"
|
||||
parking_lot = "0.4"
|
||||
mime_guess = "2.0.0-alpha.2"
|
||||
rand = "0.3"
|
||||
rustc-hex = "1.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
unicase = "1.4"
|
||||
zip = { version = "0.1", default-features = false }
|
||||
itertools = "0.5"
|
||||
|
||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.9" }
|
||||
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.9" }
|
||||
|
||||
ethcore-util = { path = "../util" }
|
||||
ethcore-bigint = { path = "../util/bigint" }
|
||||
ethcore-bytes = { path = "../util/bytes" }
|
||||
fetch = { path = "../util/fetch" }
|
||||
node-health = { path = "./node-health" }
|
||||
parity-hash-fetch = { path = "../hash-fetch" }
|
||||
parity-reactor = { path = "../util/reactor" }
|
||||
parity-ui = { path = "./ui" }
|
||||
keccak-hash = { path = "../util/hash" }
|
||||
parity-version = { path = "../util/version" }
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.4"
|
||||
ethcore-devtools = { path = "../devtools" }
|
||||
|
||||
[features]
|
||||
ui = ["parity-ui/no-precompiled-js"]
|
||||
ui-precompiled = ["parity-ui/use-precompiled-js"]
|
||||
28
dapps/js-glue/Cargo.toml
Normal file
28
dapps/js-glue/Cargo.toml
Normal file
@@ -0,0 +1,28 @@
|
||||
[package]
|
||||
description = "Base Package for all Parity built-in dapps"
|
||||
name = "parity-dapps-glue"
|
||||
version = "1.9.1"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
build = "build.rs"
|
||||
|
||||
[build-dependencies]
|
||||
quasi_codegen = { version = "0.32", optional = true }
|
||||
syntex = { version = "0.58", optional = true }
|
||||
|
||||
[dependencies]
|
||||
glob = { version = "0.2.11" }
|
||||
mime_guess = { version = "2.0.0-alpha.2" }
|
||||
aster = { version = "0.41", default-features = false }
|
||||
quasi = { version = "0.32", default-features = false }
|
||||
quasi_macros = { version = "0.32", optional = true }
|
||||
syntex = { version = "0.58", optional = true }
|
||||
syntex_syntax = { version = "0.58", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["with-syntex"]
|
||||
nightly = ["quasi_macros"]
|
||||
with-syntex = ["quasi/with-syntex", "quasi_codegen", "quasi_codegen/with-syntex", "syntex", "syntex_syntax"]
|
||||
use-precompiled-js = []
|
||||
|
||||
|
||||
65
dapps/js-glue/README.md
Normal file
65
dapps/js-glue/README.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Parity Dapps (JS-glue)
|
||||
|
||||
Code generator to simplify creating a built-in Parity Dapp
|
||||
|
||||
# How to create new builtin Dapp.
|
||||
1. Clone this repository.
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/paritytech/parity.git
|
||||
```
|
||||
|
||||
1. Create a new directory for your Dapp. (`./myapp`)
|
||||
|
||||
```bash
|
||||
$ mkdir -p ./parity/dapps/myapp/src/web
|
||||
```
|
||||
|
||||
1. Copy your frontend files to `./dapps/myapp/src/web` (bundled ones)
|
||||
|
||||
```bash
|
||||
$ cp -r ./myapp-src/* ./parity/dapps/myapp/src/web
|
||||
```
|
||||
|
||||
1. Instead of creating `web3` in your app. Load (as the first script tag in `head`):
|
||||
|
||||
```html
|
||||
<script src="/parity-utils/inject.js"></script>
|
||||
```
|
||||
|
||||
The `inject.js` script will create global `web3` instance with proper provider that should be used by your dapp.
|
||||
|
||||
1. Create `./parity/dapps/myapp/Cargo.toml` with you apps details. See example here: [parity-status Cargo.toml](https://github.com/paritytech/parity-ui/blob/master/status/Cargo.toml).
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/paritytech/parity-ui.git
|
||||
$ cd ./parity-ui/
|
||||
$ cp ./home/Cargo.toml ../parity/dapps/myapp/Cargo.toml
|
||||
$ cp ./home/build.rs ../parity/dapps/myapp/build.rs
|
||||
$ cp ./home/src/lib.rs ../parity/dapps/myapp/src/lib.rs
|
||||
$ cp ./home/src/lib.rs.in ../parity/dapps/myapp/src/lib.rs.in
|
||||
# And edit the details of your app
|
||||
$ vim ../parity/dapps/myapp/Cargo.toml # Edit the details
|
||||
$ vim ./parity/dapps/myapp/src/lib.rs.in # Edit the details
|
||||
```
|
||||
# How to include your Dapp into `Parity`?
|
||||
1. Edit `dapps/Cargo.toml` and add dependency to your application (it can be optional)
|
||||
|
||||
```toml
|
||||
# Use git repo and version
|
||||
parity-dapps-myapp = { path="./myapp" }
|
||||
```
|
||||
|
||||
1. Edit `dapps/src/apps.rs` and add your application to `all_pages` (if it's optional you need to specify two functions - see `parity-dapps-wallet` example)
|
||||
|
||||
1. Compile parity.
|
||||
|
||||
```bash
|
||||
$ cargo build --release # While inside `parity`
|
||||
```
|
||||
|
||||
1. Commit the results.
|
||||
|
||||
```bash
|
||||
$ git add myapp && git commit -am "My first Parity Dapp".
|
||||
```
|
||||
43
dapps/js-glue/build.rs
Normal file
43
dapps/js-glue/build.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
// 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/>.
|
||||
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
mod inner {
|
||||
extern crate syntex;
|
||||
extern crate quasi_codegen;
|
||||
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
|
||||
pub fn main() {
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
|
||||
let src = Path::new("src/lib.rs.in");
|
||||
let dst = Path::new(&out_dir).join("lib.rs");
|
||||
|
||||
quasi_codegen::expand(&src, &dst).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "with-syntex"))]
|
||||
mod inner {
|
||||
pub fn main() {}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
inner::main();
|
||||
}
|
||||
65
dapps/js-glue/src/build.rs
Normal file
65
dapps/js-glue/src/build.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
pub mod inner {
|
||||
use syntex;
|
||||
use codegen;
|
||||
use syntax::{ast, fold};
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
|
||||
fn strip_attributes(krate: ast::Crate) -> ast::Crate {
|
||||
/// Helper folder that strips the serde attributes after the extensions have been expanded.
|
||||
struct StripAttributeFolder;
|
||||
|
||||
impl fold::Folder for StripAttributeFolder {
|
||||
fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
|
||||
if &*attr.value.name.as_str() == "webapp" {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(attr)
|
||||
}
|
||||
|
||||
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
|
||||
fold::noop_fold_mac(mac, self)
|
||||
}
|
||||
}
|
||||
|
||||
fold::Folder::fold_crate(&mut StripAttributeFolder, krate)
|
||||
}
|
||||
|
||||
pub fn register(reg: &mut syntex::Registry) {
|
||||
reg.add_attr("feature(custom_derive)");
|
||||
reg.add_attr("feature(custom_attribute)");
|
||||
|
||||
reg.add_decorator("derive_WebAppFiles", codegen::expand_webapp_implementation);
|
||||
reg.add_post_expansion_pass(strip_attributes);
|
||||
}
|
||||
|
||||
pub fn generate() {
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
let mut registry = syntex::Registry::new();
|
||||
register(&mut registry);
|
||||
|
||||
let src = Path::new("src/lib.rs.in");
|
||||
let dst = Path::new(&out_dir).join("lib.rs");
|
||||
|
||||
registry.expand("", &src, &dst).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "with-syntex"))]
|
||||
pub mod inner {
|
||||
use codegen;
|
||||
|
||||
pub fn register(reg: &mut rustc_plugin::Registry) {
|
||||
reg.register_syntax_extension(
|
||||
syntax::parse::token::intern("derive_WebAppFiles"),
|
||||
syntax::ext::base::MultiDecorator(
|
||||
Box::new(codegen::expand_webapp_implementation)));
|
||||
|
||||
reg.register_attribute("webapp".to_owned(), AttributeType::Normal);
|
||||
}
|
||||
|
||||
pub fn generate() {}
|
||||
}
|
||||
194
dapps/js-glue/src/codegen.rs
Normal file
194
dapps/js-glue/src/codegen.rs
Normal file
@@ -0,0 +1,194 @@
|
||||
// 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/>.
|
||||
|
||||
extern crate aster;
|
||||
extern crate glob;
|
||||
extern crate mime_guess;
|
||||
|
||||
use self::mime_guess::guess_mime_type;
|
||||
use std::path::{self, Path, PathBuf};
|
||||
use std::ops::Deref;
|
||||
|
||||
use syntax::attr;
|
||||
use syntax::ast::{self, MetaItem, Item};
|
||||
use syntax::codemap::Span;
|
||||
use syntax::ext::base::{Annotatable, ExtCtxt};
|
||||
use syntax::print::pprust::lit_to_string;
|
||||
use syntax::symbol::InternedString;
|
||||
|
||||
pub fn expand_webapp_implementation(
|
||||
cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
meta_item: &MetaItem,
|
||||
annotatable: &Annotatable,
|
||||
push: &mut FnMut(Annotatable)
|
||||
) {
|
||||
let item = match *annotatable {
|
||||
Annotatable::Item(ref item) => item,
|
||||
_ => {
|
||||
cx.span_err(meta_item.span, "`#[derive(WebAppFiles)]` may only be applied to struct implementations");
|
||||
return;
|
||||
},
|
||||
};
|
||||
let builder = aster::AstBuilder::new().span(span);
|
||||
implement_webapp(cx, &builder, item, push);
|
||||
}
|
||||
|
||||
fn implement_webapp(cx: &ExtCtxt, builder: &aster::AstBuilder, item: &Item, push: &mut FnMut(Annotatable)) {
|
||||
let static_files_dir = extract_path(cx, item);
|
||||
|
||||
let src = Path::new("src");
|
||||
let static_files = {
|
||||
let mut buf = src.to_path_buf();
|
||||
buf.push(static_files_dir.deref());
|
||||
buf
|
||||
};
|
||||
|
||||
let search_location = {
|
||||
let mut buf = static_files.to_path_buf();
|
||||
buf.push("**");
|
||||
buf.push("*");
|
||||
buf
|
||||
};
|
||||
|
||||
let files = glob::glob(search_location.to_str().expect("Valid UTF8 path"))
|
||||
.expect("The sources directory is missing.")
|
||||
.collect::<Result<Vec<PathBuf>, glob::GlobError>>()
|
||||
.expect("There should be no error when reading a list of files.");
|
||||
|
||||
let statements = files
|
||||
.iter()
|
||||
.filter(|path_buf| path_buf.is_file())
|
||||
.map(|path_buf| {
|
||||
let path = path_buf.as_path();
|
||||
let filename = path.file_name().and_then(|s| s.to_str()).expect("Only UTF8 paths.");
|
||||
let mime_type = guess_mime_type(filename).to_string();
|
||||
let file_path = as_uri(path.strip_prefix(&static_files).ok().expect("Prefix is always there, cause it's absolute path;qed"));
|
||||
let file_path_in_source = path.to_str().expect("Only UTF8 paths.");
|
||||
|
||||
let path_lit = builder.expr().str(file_path.as_str());
|
||||
let mime_lit = builder.expr().str(mime_type.as_str());
|
||||
let web_path_lit = builder.expr().str(file_path_in_source);
|
||||
let separator_lit = builder.expr().str(path::MAIN_SEPARATOR.to_string().as_str());
|
||||
let concat_id = builder.id("concat!");
|
||||
let env_id = builder.id("env!");
|
||||
let macro_id = builder.id("include_bytes!");
|
||||
|
||||
let content = quote_expr!(
|
||||
cx,
|
||||
$macro_id($concat_id($env_id("CARGO_MANIFEST_DIR"), $separator_lit, $web_path_lit))
|
||||
);
|
||||
quote_stmt!(
|
||||
cx,
|
||||
files.insert($path_lit, File { path: $path_lit, content_type: $mime_lit, content: $content });
|
||||
).expect("The statement is always ok, because it just uses literals.")
|
||||
}).collect::<Vec<ast::Stmt>>();
|
||||
|
||||
let type_name = item.ident;
|
||||
|
||||
let files_impl = quote_item!(cx,
|
||||
impl $type_name {
|
||||
#[allow(unused_mut)]
|
||||
fn files() -> ::std::collections::HashMap<&'static str, File> {
|
||||
let mut files = ::std::collections::HashMap::new();
|
||||
$statements
|
||||
files
|
||||
}
|
||||
}
|
||||
).unwrap();
|
||||
|
||||
push(Annotatable::Item(files_impl));
|
||||
}
|
||||
|
||||
fn extract_path(cx: &ExtCtxt, item: &Item) -> String {
|
||||
for meta_items in item.attrs.iter().filter_map(webapp_meta_items) {
|
||||
for meta_item in meta_items {
|
||||
let is_path = &*meta_item.name.as_str() == "path";
|
||||
match meta_item.node {
|
||||
ast::MetaItemKind::NameValue(ref lit) if is_path => {
|
||||
if let Some(s) = get_str_from_lit(cx, lit) {
|
||||
return s.deref().to_owned();
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// default
|
||||
"web".to_owned()
|
||||
}
|
||||
|
||||
fn webapp_meta_items(attr: &ast::Attribute) -> Option<Vec<ast::MetaItem>> {
|
||||
let is_webapp = &*attr.value.name.as_str() == "webapp";
|
||||
match attr.value.node {
|
||||
ast::MetaItemKind::List(ref items) if is_webapp => {
|
||||
attr::mark_used(&attr);
|
||||
Some(
|
||||
items.iter()
|
||||
.map(|item| item.node.clone())
|
||||
.filter_map(|item| match item {
|
||||
ast::NestedMetaItemKind::MetaItem(item) => Some(item),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
)
|
||||
}
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_str_from_lit(cx: &ExtCtxt, lit: &ast::Lit) -> Option<InternedString> {
|
||||
match lit.node {
|
||||
ast::LitKind::Str(ref s, _) => Some(s.clone().as_str()),
|
||||
_ => {
|
||||
cx.span_err(
|
||||
lit.span,
|
||||
&format!("webapp annotation path must be a string, not `{}`",
|
||||
lit_to_string(lit)
|
||||
)
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn as_uri(path: &Path) -> String {
|
||||
let mut s = String::new();
|
||||
for component in path.iter() {
|
||||
s.push_str(component.to_str().expect("Only UTF-8 filenames are supported."));
|
||||
s.push('/');
|
||||
}
|
||||
s[0..s.len()-1].into()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_convert_path_separators_on_all_platforms() {
|
||||
// given
|
||||
let p = {
|
||||
let mut p = PathBuf::new();
|
||||
p.push("web");
|
||||
p.push("src");
|
||||
p.push("index.html");
|
||||
p
|
||||
};
|
||||
|
||||
// when
|
||||
let path = as_uri(&p);
|
||||
|
||||
// then
|
||||
assert_eq!(path, "web/src/index.html".to_owned());
|
||||
}
|
||||
89
dapps/js-glue/src/js.rs
Normal file
89
dapps/js-glue/src/js.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
// 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/>.
|
||||
|
||||
#![cfg_attr(feature="use-precompiled-js", allow(dead_code))]
|
||||
#![cfg_attr(feature="use-precompiled-js", allow(unused_imports))]
|
||||
|
||||
use std::fmt;
|
||||
use std::process::Command;
|
||||
|
||||
#[cfg(not(windows))]
|
||||
mod platform {
|
||||
use std::process::Command;
|
||||
|
||||
pub static NPM_CMD: &'static str = "npm";
|
||||
pub fn handle_cmd(cmd: &mut Command) -> &mut Command {
|
||||
cmd
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
mod platform {
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
pub static NPM_CMD: &'static str = "cmd.exe";
|
||||
// NOTE [ToDr] For some reason on windows
|
||||
// The command doesn't have %~dp0 set properly
|
||||
// and it cannot load globally installed node.exe
|
||||
pub fn handle_cmd(cmd: &mut Command) -> &mut Command {
|
||||
cmd.stdin(Stdio::null())
|
||||
.arg("/c")
|
||||
.arg("npm.cmd")
|
||||
}
|
||||
}
|
||||
|
||||
fn die<T : fmt::Debug>(s: &'static str, e: T) -> ! {
|
||||
panic!("Error: {}: {:?}", s, e);
|
||||
}
|
||||
|
||||
#[cfg(feature = "use-precompiled-js")]
|
||||
pub fn test(_path: &str) {
|
||||
}
|
||||
#[cfg(feature = "use-precompiled-js")]
|
||||
pub fn build(_path: &str, _dest: &str) {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "use-precompiled-js"))]
|
||||
pub fn build(path: &str, dest: &str) {
|
||||
let child = platform::handle_cmd(&mut Command::new(platform::NPM_CMD))
|
||||
.arg("install")
|
||||
.arg("--no-progress")
|
||||
.current_dir(path)
|
||||
.status()
|
||||
.unwrap_or_else(|e| die("Installing node.js dependencies with npm", e));
|
||||
assert!(child.success(), "There was an error installing dependencies.");
|
||||
|
||||
let child = platform::handle_cmd(&mut Command::new(platform::NPM_CMD))
|
||||
.arg("run")
|
||||
.arg("build")
|
||||
.env("NODE_ENV", "production")
|
||||
.env("BUILD_DEST", dest)
|
||||
.current_dir(path)
|
||||
.status()
|
||||
.unwrap_or_else(|e| die("Building JS code", e));
|
||||
assert!(child.success(), "There was an error build JS code.");
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "use-precompiled-js"))]
|
||||
pub fn test(path: &str) {
|
||||
let child = Command::new(platform::NPM_CMD)
|
||||
.arg("run")
|
||||
.arg("test")
|
||||
.current_dir(path)
|
||||
.status()
|
||||
.unwrap_or_else(|e| die("Running test command", e));
|
||||
assert!(child.success(), "There was an error while running JS tests.");
|
||||
}
|
||||
38
dapps/js-glue/src/lib.rs
Normal file
38
dapps/js-glue/src/lib.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
// 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/>.
|
||||
|
||||
|
||||
#![cfg_attr(not(feature = "with-syntex"), feature(rustc_private, plugin))]
|
||||
#![cfg_attr(not(feature = "with-syntex"), plugin(quasi_macros))]
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
extern crate syntex;
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
extern crate syntex_syntax as syntax;
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
include!(concat!(env!("OUT_DIR"), "/lib.rs"));
|
||||
|
||||
#[cfg(not(feature = "with-syntex"))]
|
||||
#[macro_use]
|
||||
extern crate syntax;
|
||||
|
||||
#[cfg(not(feature = "with-syntex"))]
|
||||
extern crate rustc_plugin;
|
||||
|
||||
#[cfg(not(feature = "with-syntex"))]
|
||||
include!("lib.rs.in");
|
||||
46
dapps/js-glue/src/lib.rs.in
Normal file
46
dapps/js-glue/src/lib.rs.in
Normal file
@@ -0,0 +1,46 @@
|
||||
// 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/>.
|
||||
|
||||
extern crate quasi;
|
||||
|
||||
mod codegen;
|
||||
mod build;
|
||||
pub mod js;
|
||||
pub use build::inner::generate;
|
||||
|
||||
use std::default::Default;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct File {
|
||||
pub path: &'static str,
|
||||
pub content: &'static [u8],
|
||||
// TODO: use strongly-typed MIME.
|
||||
pub content_type: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Info {
|
||||
pub name: &'static str,
|
||||
pub version: &'static str,
|
||||
pub author: &'static str,
|
||||
pub description: &'static str,
|
||||
pub icon_url: &'static str,
|
||||
}
|
||||
|
||||
pub trait WebApp : Default + Send + Sync {
|
||||
fn file(&self, path: &str) -> Option<&File>;
|
||||
fn info(&self) -> Info;
|
||||
}
|
||||
18
dapps/node-health/Cargo.toml
Normal file
18
dapps/node-health/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "node-health"
|
||||
description = "Node's health status"
|
||||
version = "0.1.0"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
futures = "0.1"
|
||||
futures-cpupool = "0.1"
|
||||
log = "0.3"
|
||||
ntp = "0.3.0"
|
||||
parking_lot = "0.4"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
time = "0.1.35"
|
||||
|
||||
parity-reactor = { path = "../../util/reactor" }
|
||||
122
dapps/node-health/src/health.rs
Normal file
122
dapps/node-health/src/health.rs
Normal file
@@ -0,0 +1,122 @@
|
||||
// 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/>.
|
||||
|
||||
//! Reporting node's health.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::time;
|
||||
use futures::Future;
|
||||
use futures::sync::oneshot;
|
||||
use types::{HealthInfo, HealthStatus, Health};
|
||||
use time::{TimeChecker, MAX_DRIFT};
|
||||
use parity_reactor::Remote;
|
||||
use parking_lot::Mutex;
|
||||
use {SyncStatus};
|
||||
|
||||
const TIMEOUT_SECS: u64 = 5;
|
||||
const PROOF: &str = "Only one closure is invoked.";
|
||||
|
||||
/// A struct enabling you to query for node's health.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NodeHealth {
|
||||
sync_status: Arc<SyncStatus>,
|
||||
time: TimeChecker,
|
||||
remote: Remote,
|
||||
}
|
||||
|
||||
impl NodeHealth {
|
||||
/// Creates new `NodeHealth`.
|
||||
pub fn new(sync_status: Arc<SyncStatus>, time: TimeChecker, remote: Remote) -> Self {
|
||||
NodeHealth { sync_status, time, remote, }
|
||||
}
|
||||
|
||||
/// Query latest health report.
|
||||
pub fn health(&self) -> Box<Future<Item = Health, Error = ()> + Send> {
|
||||
trace!(target: "dapps", "Checking node health.");
|
||||
// Check timediff
|
||||
let sync_status = self.sync_status.clone();
|
||||
let time = self.time.time_drift();
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let tx = Arc::new(Mutex::new(Some(tx)));
|
||||
let tx2 = tx.clone();
|
||||
self.remote.spawn_with_timeout(
|
||||
move || time.then(move |result| {
|
||||
let _ = tx.lock().take().expect(PROOF).send(Ok(result));
|
||||
Ok(())
|
||||
}),
|
||||
time::Duration::from_secs(TIMEOUT_SECS),
|
||||
move || {
|
||||
let _ = tx2.lock().take().expect(PROOF).send(Err(()));
|
||||
},
|
||||
);
|
||||
|
||||
Box::new(rx.map_err(|err| {
|
||||
warn!(target: "dapps", "Health request cancelled: {:?}", err);
|
||||
}).and_then(move |time| {
|
||||
// Check peers
|
||||
let peers = {
|
||||
let (connected, max) = sync_status.peers();
|
||||
let (status, message) = match connected {
|
||||
0 => {
|
||||
(HealthStatus::Bad, "You are not connected to any peers. There is most likely some network issue. Fix connectivity.".into())
|
||||
},
|
||||
1 => (HealthStatus::NeedsAttention, "You are connected to only one peer. Your node might not be reliable. Check your network connection.".into()),
|
||||
_ => (HealthStatus::Ok, "".into()),
|
||||
};
|
||||
HealthInfo { status, message, details: (connected, max) }
|
||||
};
|
||||
|
||||
// Check sync
|
||||
let sync = {
|
||||
let is_syncing = sync_status.is_major_importing();
|
||||
let (status, message) = if is_syncing {
|
||||
(HealthStatus::NeedsAttention, "Your node is still syncing, the values you see might be outdated. Wait until it's fully synced.".into())
|
||||
} else {
|
||||
(HealthStatus::Ok, "".into())
|
||||
};
|
||||
HealthInfo { status, message, details: is_syncing }
|
||||
};
|
||||
|
||||
// Check time
|
||||
let time = {
|
||||
let (status, message, details) = match time {
|
||||
Ok(Ok(diff)) if diff < MAX_DRIFT && diff > -MAX_DRIFT => {
|
||||
(HealthStatus::Ok, "".into(), diff)
|
||||
},
|
||||
Ok(Ok(diff)) => {
|
||||
(HealthStatus::Bad, format!(
|
||||
"Your clock is not in sync. Detected difference is too big for the protocol to work: {}ms. Synchronize your clock.",
|
||||
diff,
|
||||
), diff)
|
||||
},
|
||||
Ok(Err(err)) => {
|
||||
(HealthStatus::NeedsAttention, format!(
|
||||
"Unable to reach time API: {}. Make sure that your clock is synchronized.",
|
||||
err,
|
||||
), 0)
|
||||
},
|
||||
Err(_) => {
|
||||
(HealthStatus::NeedsAttention, "Time API request timed out. Make sure that the clock is synchronized.".into(), 0)
|
||||
},
|
||||
};
|
||||
|
||||
HealthInfo { status, message, details, }
|
||||
};
|
||||
|
||||
Ok(Health { peers, sync, time})
|
||||
}))
|
||||
}
|
||||
}
|
||||
49
dapps/node-health/src/lib.rs
Normal file
49
dapps/node-health/src/lib.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
// 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/>.
|
||||
|
||||
//! Node Health status reporting.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
extern crate futures;
|
||||
extern crate futures_cpupool;
|
||||
extern crate ntp;
|
||||
extern crate time as time_crate;
|
||||
extern crate parity_reactor;
|
||||
extern crate parking_lot;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
mod health;
|
||||
mod time;
|
||||
mod types;
|
||||
|
||||
pub use futures_cpupool::CpuPool;
|
||||
pub use health::NodeHealth;
|
||||
pub use types::{Health, HealthInfo, HealthStatus};
|
||||
pub use time::{TimeChecker, Error};
|
||||
|
||||
/// Indicates sync status
|
||||
pub trait SyncStatus: ::std::fmt::Debug + Send + Sync {
|
||||
/// Returns true if there is a major sync happening.
|
||||
fn is_major_importing(&self) -> bool;
|
||||
|
||||
/// Returns number of connected and ideal peers.
|
||||
fn peers(&self) -> (usize, usize);
|
||||
}
|
||||
357
dapps/node-health/src/time.rs
Normal file
357
dapps/node-health/src/time.rs
Normal file
@@ -0,0 +1,357 @@
|
||||
// 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/>.
|
||||
|
||||
//! Periodically checks node's time drift using [SNTP](https://tools.ietf.org/html/rfc1769).
|
||||
//!
|
||||
//! An NTP packet is sent to the server with a local timestamp, the server then completes the packet, yielding the
|
||||
//! following timestamps:
|
||||
//!
|
||||
//! Timestamp Name ID When Generated
|
||||
//! ------------------------------------------------------------
|
||||
//! Originate Timestamp T1 time request sent by client
|
||||
//! Receive Timestamp T2 time request received at server
|
||||
//! Transmit Timestamp T3 time reply sent by server
|
||||
//! Destination Timestamp T4 time reply received at client
|
||||
//!
|
||||
//! The drift is defined as:
|
||||
//!
|
||||
//! drift = ((T2 - T1) + (T3 - T4)) / 2.
|
||||
//!
|
||||
|
||||
use std::io;
|
||||
use std::{fmt, mem, time};
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::atomic::{self, AtomicUsize};
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures::{self, Future};
|
||||
use futures::future::{self, IntoFuture};
|
||||
use futures_cpupool::{CpuPool, CpuFuture};
|
||||
use ntp;
|
||||
use parking_lot::RwLock;
|
||||
use time_crate::{Duration, Timespec};
|
||||
|
||||
/// Time checker error.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Error {
|
||||
/// No servers are currently available for a query.
|
||||
NoServersAvailable,
|
||||
/// There was an error when trying to reach the NTP server.
|
||||
Ntp(String),
|
||||
/// IO error when reading NTP response.
|
||||
Io(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
use self::Error::*;
|
||||
|
||||
match *self {
|
||||
NoServersAvailable => write!(fmt, "No NTP servers available"),
|
||||
Ntp(ref err) => write!(fmt, "NTP error: {}", err),
|
||||
Io(ref err) => write!(fmt, "Connection Error: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(err: io::Error) -> Self { Error::Io(format!("{}", err)) }
|
||||
}
|
||||
|
||||
impl From<ntp::errors::Error> for Error {
|
||||
fn from(err: ntp::errors::Error) -> Self { Error::Ntp(format!("{}", err)) }
|
||||
}
|
||||
|
||||
/// NTP time drift checker.
|
||||
pub trait Ntp {
|
||||
/// Returned Future.
|
||||
type Future: IntoFuture<Item=Duration, Error=Error>;
|
||||
|
||||
/// Returns the current time drift.
|
||||
fn drift(&self) -> Self::Future;
|
||||
}
|
||||
|
||||
const SERVER_MAX_POLL_INTERVAL_SECS: u64 = 60;
|
||||
#[derive(Debug)]
|
||||
struct Server {
|
||||
pub address: String,
|
||||
next_call: RwLock<time::Instant>,
|
||||
failures: AtomicUsize,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
pub fn is_available(&self) -> bool {
|
||||
*self.next_call.read() < time::Instant::now()
|
||||
}
|
||||
|
||||
pub fn report_success(&self) {
|
||||
self.failures.store(0, atomic::Ordering::SeqCst);
|
||||
self.update_next_call(1)
|
||||
}
|
||||
|
||||
pub fn report_failure(&self) {
|
||||
let errors = self.failures.fetch_add(1, atomic::Ordering::SeqCst);
|
||||
self.update_next_call(1 << errors)
|
||||
}
|
||||
|
||||
fn update_next_call(&self, delay: usize) {
|
||||
*self.next_call.write() = time::Instant::now() + time::Duration::from_secs(delay as u64 * SERVER_MAX_POLL_INTERVAL_SECS);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> From<T> for Server {
|
||||
fn from(t: T) -> Self {
|
||||
Server {
|
||||
address: t.as_ref().to_owned(),
|
||||
next_call: RwLock::new(time::Instant::now()),
|
||||
failures: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// NTP client using the SNTP algorithm for calculating drift.
|
||||
#[derive(Clone)]
|
||||
pub struct SimpleNtp {
|
||||
addresses: Vec<Arc<Server>>,
|
||||
pool: CpuPool,
|
||||
}
|
||||
|
||||
impl fmt::Debug for SimpleNtp {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f
|
||||
.debug_struct("SimpleNtp")
|
||||
.field("addresses", &self.addresses)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl SimpleNtp {
|
||||
fn new<T: AsRef<str>>(addresses: &[T], pool: CpuPool) -> SimpleNtp {
|
||||
SimpleNtp {
|
||||
addresses: addresses.iter().map(Server::from).map(Arc::new).collect(),
|
||||
pool: pool,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ntp for SimpleNtp {
|
||||
type Future = future::Either<
|
||||
CpuFuture<Duration, Error>,
|
||||
future::FutureResult<Duration, Error>,
|
||||
>;
|
||||
|
||||
fn drift(&self) -> Self::Future {
|
||||
use self::future::Either::{A, B};
|
||||
|
||||
let server = self.addresses.iter().find(|server| server.is_available());
|
||||
server.map(|server| {
|
||||
let server = server.clone();
|
||||
A(self.pool.spawn_fn(move || {
|
||||
debug!(target: "dapps", "Fetching time from {}.", server.address);
|
||||
|
||||
match ntp::request(&server.address) {
|
||||
Ok(packet) => {
|
||||
let dest_time = ::time_crate::now_utc().to_timespec();
|
||||
let orig_time = Timespec::from(packet.orig_time);
|
||||
let recv_time = Timespec::from(packet.recv_time);
|
||||
let transmit_time = Timespec::from(packet.transmit_time);
|
||||
|
||||
let drift = ((recv_time - orig_time) + (transmit_time - dest_time)) / 2;
|
||||
|
||||
server.report_success();
|
||||
Ok(drift)
|
||||
},
|
||||
Err(err) => {
|
||||
server.report_failure();
|
||||
Err(err.into())
|
||||
},
|
||||
}
|
||||
}))
|
||||
}).unwrap_or_else(|| B(future::err(Error::NoServersAvailable)))
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE In a positive scenario first results will be seen after:
|
||||
// MAX_RESULTS * UPDATE_TIMEOUT_INCOMPLETE_SECS seconds.
|
||||
const MAX_RESULTS: usize = 4;
|
||||
const UPDATE_TIMEOUT_OK_SECS: u64 = 6 * 60 * 60;
|
||||
const UPDATE_TIMEOUT_WARN_SECS: u64 = 15 * 60;
|
||||
const UPDATE_TIMEOUT_ERR_SECS: u64 = 60;
|
||||
const UPDATE_TIMEOUT_INCOMPLETE_SECS: u64 = 10;
|
||||
|
||||
/// Maximal valid time drift.
|
||||
pub const MAX_DRIFT: i64 = 10_000;
|
||||
|
||||
type BoxFuture<A, B> = Box<Future<Item = A, Error = B> + Send>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// A time checker.
|
||||
pub struct TimeChecker<N: Ntp = SimpleNtp> {
|
||||
ntp: N,
|
||||
last_result: Arc<RwLock<(time::Instant, VecDeque<Result<i64, Error>>)>>,
|
||||
}
|
||||
|
||||
impl TimeChecker<SimpleNtp> {
|
||||
/// Creates new time checker given the NTP server address.
|
||||
pub fn new<T: AsRef<str>>(ntp_addresses: &[T], pool: CpuPool) -> Self {
|
||||
let last_result = Arc::new(RwLock::new(
|
||||
// Assume everything is ok at the very beginning.
|
||||
(time::Instant::now(), vec![Ok(0)].into())
|
||||
));
|
||||
|
||||
let ntp = SimpleNtp::new(ntp_addresses, pool);
|
||||
|
||||
TimeChecker {
|
||||
ntp,
|
||||
last_result,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Ntp> TimeChecker<N> where <N::Future as IntoFuture>::Future: Send + 'static {
|
||||
/// Updates the time
|
||||
pub fn update(&self) -> BoxFuture<i64, Error> {
|
||||
trace!(target: "dapps", "Updating time from NTP.");
|
||||
let last_result = self.last_result.clone();
|
||||
Box::new(self.ntp.drift().into_future().then(move |res| {
|
||||
let res = res.map(|d| d.num_milliseconds());
|
||||
|
||||
if let Err(Error::NoServersAvailable) = res {
|
||||
debug!(target: "dapps", "No NTP servers available. Selecting an older result.");
|
||||
return select_result(last_result.read().1.iter());
|
||||
}
|
||||
|
||||
// Update the results.
|
||||
let mut results = mem::replace(&mut last_result.write().1, VecDeque::new());
|
||||
let has_all_results = results.len() >= MAX_RESULTS;
|
||||
let valid_till = time::Instant::now() + time::Duration::from_secs(
|
||||
match res {
|
||||
Ok(time) if has_all_results && time < MAX_DRIFT => UPDATE_TIMEOUT_OK_SECS,
|
||||
Ok(_) if has_all_results => UPDATE_TIMEOUT_WARN_SECS,
|
||||
Err(_) if has_all_results => UPDATE_TIMEOUT_ERR_SECS,
|
||||
_ => UPDATE_TIMEOUT_INCOMPLETE_SECS,
|
||||
}
|
||||
);
|
||||
|
||||
trace!(target: "dapps", "New time drift received: {:?}", res);
|
||||
// Push the result.
|
||||
results.push_back(res);
|
||||
while results.len() > MAX_RESULTS {
|
||||
results.pop_front();
|
||||
}
|
||||
|
||||
// Select a response and update last result.
|
||||
let res = select_result(results.iter());
|
||||
*last_result.write() = (valid_till, results);
|
||||
res
|
||||
}))
|
||||
}
|
||||
|
||||
/// Returns a current time drift or error if last request to NTP server failed.
|
||||
pub fn time_drift(&self) -> BoxFuture<i64, Error> {
|
||||
// return cached result
|
||||
{
|
||||
let res = self.last_result.read();
|
||||
if res.0 > time::Instant::now() {
|
||||
return Box::new(futures::done(select_result(res.1.iter())));
|
||||
}
|
||||
}
|
||||
// or update and return result
|
||||
self.update()
|
||||
}
|
||||
}
|
||||
|
||||
fn select_result<'a, T: Iterator<Item=&'a Result<i64, Error>>>(results: T) -> Result<i64, Error> {
|
||||
let mut min = None;
|
||||
for res in results {
|
||||
min = Some(match (min.take(), res) {
|
||||
(Some(Ok(min)), &Ok(ref new)) => Ok(::std::cmp::min(min, *new)),
|
||||
(Some(Ok(old)), &Err(_)) => Ok(old),
|
||||
(_, ref new) => (*new).clone(),
|
||||
})
|
||||
}
|
||||
|
||||
min.unwrap_or_else(|| Err(Error::Ntp("NTP server unavailable.".into())))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::time::Instant;
|
||||
use time::Duration;
|
||||
use futures::{future, Future};
|
||||
use super::{Ntp, TimeChecker, Error};
|
||||
use parking_lot::RwLock;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct FakeNtp(RefCell<Vec<Duration>>, Cell<u64>);
|
||||
impl FakeNtp {
|
||||
fn new() -> FakeNtp {
|
||||
FakeNtp(
|
||||
RefCell::new(vec![Duration::milliseconds(150)]),
|
||||
Cell::new(0))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ntp for FakeNtp {
|
||||
type Future = future::FutureResult<Duration, Error>;
|
||||
|
||||
fn drift(&self) -> Self::Future {
|
||||
self.1.set(self.1.get() + 1);
|
||||
future::ok(self.0.borrow_mut().pop().expect("Unexpected call to drift()."))
|
||||
}
|
||||
}
|
||||
|
||||
fn time_checker() -> TimeChecker<FakeNtp> {
|
||||
let last_result = Arc::new(RwLock::new(
|
||||
(Instant::now(), vec![Err(Error::Ntp("NTP server unavailable".into()))].into())
|
||||
));
|
||||
|
||||
TimeChecker {
|
||||
ntp: FakeNtp::new(),
|
||||
last_result: last_result,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_fetch_time_on_start() {
|
||||
// given
|
||||
let time = time_checker();
|
||||
|
||||
// when
|
||||
let diff = time.time_drift().wait().unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(diff, 150);
|
||||
assert_eq!(time.ntp.1.get(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_fetch_twice_if_timeout_has_not_passed() {
|
||||
// given
|
||||
let time = time_checker();
|
||||
|
||||
// when
|
||||
let diff1 = time.time_drift().wait().unwrap();
|
||||
let diff2 = time.time_drift().wait().unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(diff1, 150);
|
||||
assert_eq!(diff2, 150);
|
||||
assert_eq!(time.ntp.1.get(), 1);
|
||||
}
|
||||
}
|
||||
57
dapps/node-health/src/types.rs
Normal file
57
dapps/node-health/src/types.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
// 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/>.
|
||||
|
||||
//! Base health types.
|
||||
|
||||
/// Health API endpoint status.
|
||||
#[derive(Debug, PartialEq, Serialize)]
|
||||
pub enum HealthStatus {
|
||||
/// Everything's OK.
|
||||
#[serde(rename = "ok")]
|
||||
Ok,
|
||||
/// Node health need attention
|
||||
/// (the issue is not critical, but may need investigation)
|
||||
#[serde(rename = "needsAttention")]
|
||||
NeedsAttention,
|
||||
/// There is something bad detected with the node.
|
||||
#[serde(rename = "bad")]
|
||||
Bad,
|
||||
}
|
||||
|
||||
/// Represents a single check in node health.
|
||||
/// Cointains the status of that check and apropriate message and details.
|
||||
#[derive(Debug, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct HealthInfo<T> {
|
||||
/// Check status.
|
||||
pub status: HealthStatus,
|
||||
/// Human-readable message.
|
||||
pub message: String,
|
||||
/// Technical details of the check.
|
||||
pub details: T,
|
||||
}
|
||||
|
||||
/// Node Health status.
|
||||
#[derive(Debug, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Health {
|
||||
/// Status of peers.
|
||||
pub peers: HealthInfo<(usize, usize)>,
|
||||
/// Sync status.
|
||||
pub sync: HealthInfo<bool>,
|
||||
/// Time diff info.
|
||||
pub time: HealthInfo<i64>,
|
||||
}
|
||||
BIN
dapps/res/gavcoin.zip
Normal file
BIN
dapps/res/gavcoin.zip
Normal file
Binary file not shown.
97
dapps/src/api/api.rs
Normal file
97
dapps/src/api/api.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
// 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::{Method, StatusCode};
|
||||
|
||||
use api::response;
|
||||
use apps::fetcher::Fetcher;
|
||||
use endpoint::{Endpoint, Request, Response, EndpointPath};
|
||||
use futures::{future, Future};
|
||||
use node_health::{NodeHealth, HealthStatus};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RestApi {
|
||||
fetcher: Arc<Fetcher>,
|
||||
health: NodeHealth,
|
||||
}
|
||||
|
||||
impl Endpoint for RestApi {
|
||||
fn respond(&self, mut path: EndpointPath, req: Request) -> Response {
|
||||
if let Method::Options = *req.method() {
|
||||
return Box::new(future::ok(response::empty()));
|
||||
}
|
||||
|
||||
let endpoint = path.app_params.get(0).map(String::to_owned);
|
||||
let hash = path.app_params.get(1).map(String::to_owned);
|
||||
|
||||
// at this point path.app_id contains 'api', adjust it to the hash properly, otherwise
|
||||
// we will try and retrieve 'api' as the hash when doing the /api/content route
|
||||
if let Some(ref hash) = hash {
|
||||
path.app_id = hash.to_owned();
|
||||
}
|
||||
|
||||
trace!(target: "dapps", "Handling /api request: {:?}/{:?}", endpoint, hash);
|
||||
match endpoint.as_ref().map(String::as_str) {
|
||||
Some("ping") => Box::new(future::ok(response::ping(req))),
|
||||
Some("health") => self.health(),
|
||||
Some("content") => self.resolve_content(hash.as_ref().map(String::as_str), path, req),
|
||||
_ => Box::new(future::ok(response::not_found())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RestApi {
|
||||
pub fn new(
|
||||
fetcher: Arc<Fetcher>,
|
||||
health: NodeHealth,
|
||||
) -> Box<Endpoint> {
|
||||
Box::new(RestApi {
|
||||
fetcher,
|
||||
health,
|
||||
})
|
||||
}
|
||||
|
||||
fn resolve_content(&self, hash: Option<&str>, path: EndpointPath, req: Request) -> Response {
|
||||
trace!(target: "dapps", "Resolving content: {:?} from path: {:?}", hash, path);
|
||||
match hash {
|
||||
Some(hash) if self.fetcher.contains(hash) => {
|
||||
self.fetcher.respond(path, req)
|
||||
},
|
||||
_ => Box::new(future::ok(response::not_found())),
|
||||
}
|
||||
}
|
||||
|
||||
fn health(&self) -> Response {
|
||||
Box::new(self.health.health()
|
||||
.then(|health| {
|
||||
let status = match health {
|
||||
Ok(ref health) => {
|
||||
if [&health.peers.status, &health.sync.status].iter().any(|x| *x != &HealthStatus::Ok) {
|
||||
StatusCode::PreconditionFailed // HTTP 412
|
||||
} else {
|
||||
StatusCode::Ok // HTTP 200
|
||||
}
|
||||
},
|
||||
_ => StatusCode::ServiceUnavailable, // HTTP 503
|
||||
};
|
||||
|
||||
Ok(response::as_json(status, &health).into())
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,23 @@
|
||||
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// 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 Ethereum is distributed in the hope that it will be useful,
|
||||
// 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 Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Ethereum Transactions
|
||||
//! REST API
|
||||
|
||||
mod error;
|
||||
mod transaction;
|
||||
mod api;
|
||||
mod response;
|
||||
mod types;
|
||||
|
||||
pub use self::error::Error;
|
||||
pub use self::transaction::*;
|
||||
pub use self::api::RestApi;
|
||||
43
dapps/src/api/response.rs
Normal file
43
dapps/src/api/response.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
// 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 serde::Serialize;
|
||||
use serde_json;
|
||||
use hyper::{self, mime, StatusCode};
|
||||
|
||||
use handlers::{ContentHandler, EchoHandler};
|
||||
|
||||
pub fn empty() -> hyper::Response {
|
||||
ContentHandler::ok("".into(), mime::TEXT_PLAIN).into()
|
||||
}
|
||||
|
||||
pub fn as_json<T: Serialize>(status: StatusCode, val: &T) -> hyper::Response {
|
||||
let json = serde_json::to_string(val)
|
||||
.expect("serialization to string is infallible; qed");
|
||||
ContentHandler::new(status, json, mime::APPLICATION_JSON).into()
|
||||
}
|
||||
|
||||
pub fn ping(req: hyper::Request) -> hyper::Response {
|
||||
EchoHandler::new(req).into()
|
||||
}
|
||||
|
||||
pub fn not_found() -> hyper::Response {
|
||||
as_json(StatusCode::NotFound, &::api::types::ApiError {
|
||||
code: "404".into(),
|
||||
title: "Not Found".into(),
|
||||
detail: "Resource you requested has not been found.".into(),
|
||||
})
|
||||
}
|
||||
27
dapps/src/api/types.rs
Normal file
27
dapps/src/api/types.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
// 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/>.
|
||||
|
||||
/// A structure representing any error in REST API.
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ApiError {
|
||||
/// Error code.
|
||||
pub code: String,
|
||||
/// Human-readable error summary.
|
||||
pub title: String,
|
||||
/// More technical error details.
|
||||
pub detail: String,
|
||||
}
|
||||
59
dapps/src/apps/app.rs
Normal file
59
dapps/src/apps/app.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
// 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,
|
||||
#[serde(rename="localUrl")]
|
||||
pub local_url: Option<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(),
|
||||
local_url: info.local_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,
|
||||
local_url: self.local_url,
|
||||
}
|
||||
}
|
||||
}
|
||||
128
dapps/src/apps/cache.rs
Normal file
128
dapps/src/apps/cache.rs
Normal file
@@ -0,0 +1,128 @@
|
||||
// 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/>.
|
||||
|
||||
//! Fetchable Dapps support.
|
||||
|
||||
use std::fs;
|
||||
|
||||
use linked_hash_map::LinkedHashMap;
|
||||
use page::local;
|
||||
use handlers::FetchControl;
|
||||
|
||||
pub enum ContentStatus {
|
||||
Fetching(FetchControl),
|
||||
Ready(local::Dapp),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ContentCache {
|
||||
cache: LinkedHashMap<String, ContentStatus>,
|
||||
}
|
||||
|
||||
impl ContentCache {
|
||||
pub fn insert(&mut self, content_id: String, status: ContentStatus) -> Option<ContentStatus> {
|
||||
self.cache.insert(content_id, status)
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, content_id: &str) -> Option<ContentStatus> {
|
||||
self.cache.remove(content_id)
|
||||
}
|
||||
|
||||
pub fn get(&mut self, content_id: &str) -> Option<&mut ContentStatus> {
|
||||
self.cache.get_refresh(content_id)
|
||||
}
|
||||
|
||||
pub fn clear_garbage(&mut self, expected_size: usize) -> Vec<(String, ContentStatus)> {
|
||||
let len = self.cache.len();
|
||||
|
||||
if len <= expected_size {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut removed = Vec::with_capacity(len - expected_size);
|
||||
|
||||
while self.cache.len() > expected_size {
|
||||
let entry = self.cache.pop_front().expect("expected_size bounded at 0, len is greater; qed");
|
||||
|
||||
match entry.1 {
|
||||
ContentStatus::Fetching(ref fetch) => {
|
||||
trace!(target: "dapps", "Aborting {} because of limit.", entry.0);
|
||||
// Mark as aborted
|
||||
fetch.abort()
|
||||
},
|
||||
ContentStatus::Ready(ref endpoint) => {
|
||||
trace!(target: "dapps", "Removing {} because of limit.", entry.0);
|
||||
// Remove path (dir or file)
|
||||
let res = fs::remove_dir_all(&endpoint.path()).or_else(|_| fs::remove_file(&endpoint.path()));
|
||||
if let Err(e) = res {
|
||||
warn!(target: "dapps", "Unable to remove dapp/content from cache: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
removed.push(entry);
|
||||
}
|
||||
removed
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.cache.len()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn only_keys(data: Vec<(String, ContentStatus)>) -> Vec<String> {
|
||||
data.into_iter().map(|x| x.0).collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_remove_least_recently_used() {
|
||||
// given
|
||||
let mut cache = ContentCache::default();
|
||||
cache.insert("a".into(), ContentStatus::Fetching(Default::default()));
|
||||
cache.insert("b".into(), ContentStatus::Fetching(Default::default()));
|
||||
cache.insert("c".into(), ContentStatus::Fetching(Default::default()));
|
||||
|
||||
// when
|
||||
let res = cache.clear_garbage(2);
|
||||
|
||||
// then
|
||||
assert_eq!(cache.len(), 2);
|
||||
assert_eq!(only_keys(res), vec!["a"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_update_lru_if_accessed() {
|
||||
// given
|
||||
let mut cache = ContentCache::default();
|
||||
cache.insert("a".into(), ContentStatus::Fetching(Default::default()));
|
||||
cache.insert("b".into(), ContentStatus::Fetching(Default::default()));
|
||||
cache.insert("c".into(), ContentStatus::Fetching(Default::default()));
|
||||
|
||||
// when
|
||||
cache.get("a");
|
||||
let res = cache.clear_garbage(2);
|
||||
|
||||
// then
|
||||
assert_eq!(cache.len(), 2);
|
||||
assert_eq!(only_keys(res), vec!["b"]);
|
||||
}
|
||||
|
||||
}
|
||||
267
dapps/src/apps/fetcher/installers.rs
Normal file
267
dapps/src/apps/fetcher/installers.rs
Normal file
@@ -0,0 +1,267 @@
|
||||
// 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 zip;
|
||||
use std::{fs, fmt};
|
||||
use std::io::{self, Read, Write};
|
||||
use std::path::PathBuf;
|
||||
use bigint::hash::H256;
|
||||
use fetch::{self, Mime};
|
||||
use futures_cpupool::CpuPool;
|
||||
use hash::keccak_buffer;
|
||||
|
||||
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest, serialize_manifest, Manifest};
|
||||
use handlers::{ContentValidator, ValidatorResponse};
|
||||
use page::{local, PageCache};
|
||||
use Embeddable;
|
||||
|
||||
type OnDone = Box<Fn(Option<local::Dapp>) + Send>;
|
||||
|
||||
fn write_response_and_check_hash(
|
||||
id: &str,
|
||||
mut content_path: PathBuf,
|
||||
filename: &str,
|
||||
response: fetch::Response
|
||||
) -> Result<(fs::File, PathBuf), ValidationError> {
|
||||
// try to parse id
|
||||
let id = id.parse().map_err(|_| ValidationError::InvalidContentId)?;
|
||||
|
||||
// check if content exists
|
||||
if content_path.exists() {
|
||||
warn!(target: "dapps", "Overwriting existing content at 0x{:?}", id);
|
||||
fs::remove_dir_all(&content_path)?
|
||||
}
|
||||
|
||||
// create directory
|
||||
fs::create_dir_all(&content_path)?;
|
||||
|
||||
// append filename
|
||||
content_path.push(filename);
|
||||
|
||||
// Now write the response
|
||||
let mut file = io::BufWriter::new(fs::File::create(&content_path)?);
|
||||
let mut reader = io::BufReader::new(response);
|
||||
io::copy(&mut reader, &mut file)?;
|
||||
file.flush()?;
|
||||
|
||||
// Validate hash
|
||||
// TODO [ToDr] calculate keccak in-flight while reading the response
|
||||
let mut file = io::BufReader::new(fs::File::open(&content_path)?);
|
||||
let hash = keccak_buffer(&mut file)?;
|
||||
if id == hash {
|
||||
Ok((file.into_inner(), content_path))
|
||||
} else {
|
||||
Err(ValidationError::HashMismatch {
|
||||
expected: id,
|
||||
got: hash,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Content {
|
||||
id: String,
|
||||
mime: Mime,
|
||||
content_path: PathBuf,
|
||||
on_done: OnDone,
|
||||
pool: CpuPool,
|
||||
}
|
||||
|
||||
impl Content {
|
||||
pub fn new(id: String, mime: Mime, content_path: PathBuf, on_done: OnDone, pool: CpuPool) -> Self {
|
||||
Content {
|
||||
id,
|
||||
mime,
|
||||
content_path,
|
||||
on_done,
|
||||
pool,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ContentValidator for Content {
|
||||
type Error = ValidationError;
|
||||
|
||||
fn validate_and_install(self, response: fetch::Response) -> Result<ValidatorResponse, ValidationError> {
|
||||
let pool = self.pool;
|
||||
let id = self.id.clone();
|
||||
let mime = self.mime;
|
||||
let validate = move |content_path: PathBuf| {
|
||||
// Create dir
|
||||
let (_, content_path) = write_response_and_check_hash(&id, content_path, &id, response)?;
|
||||
|
||||
Ok(local::Dapp::single_file(pool, content_path, mime, PageCache::Enabled))
|
||||
};
|
||||
|
||||
// Prepare path for a file
|
||||
let content_path = self.content_path.join(&self.id);
|
||||
// Make sure to always call on_done (even in case of errors)!
|
||||
let result = validate(content_path.clone());
|
||||
// remove the file if there was an error
|
||||
if result.is_err() {
|
||||
// Ignore errors since the file might not exist
|
||||
let _ = fs::remove_dir_all(&content_path);
|
||||
}
|
||||
(self.on_done)(result.as_ref().ok().cloned());
|
||||
result.map(ValidatorResponse::Local)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Dapp {
|
||||
id: String,
|
||||
dapps_path: PathBuf,
|
||||
on_done: OnDone,
|
||||
embeddable_on: Embeddable,
|
||||
pool: CpuPool,
|
||||
}
|
||||
|
||||
impl Dapp {
|
||||
pub fn new(id: String, dapps_path: PathBuf, on_done: OnDone, embeddable_on: Embeddable, pool: CpuPool) -> Self {
|
||||
Dapp {
|
||||
id,
|
||||
dapps_path,
|
||||
on_done,
|
||||
embeddable_on,
|
||||
pool,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_manifest(zip: &mut zip::ZipArchive<fs::File>) -> Result<(Manifest, PathBuf), ValidationError> {
|
||||
for i in 0..zip.len() {
|
||||
let mut file = zip.by_index(i)?;
|
||||
|
||||
if !file.name().ends_with(MANIFEST_FILENAME) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// try to read manifest
|
||||
let mut manifest = String::new();
|
||||
let manifest = file
|
||||
.read_to_string(&mut manifest).ok()
|
||||
.and_then(|_| deserialize_manifest(manifest).ok());
|
||||
|
||||
if let Some(manifest) = manifest {
|
||||
let mut manifest_location = PathBuf::from(file.name());
|
||||
manifest_location.pop(); // get rid of filename
|
||||
return Ok((manifest, manifest_location));
|
||||
}
|
||||
}
|
||||
|
||||
Err(ValidationError::ManifestNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
impl ContentValidator for Dapp {
|
||||
type Error = ValidationError;
|
||||
|
||||
fn validate_and_install(self, response: fetch::Response) -> Result<ValidatorResponse, ValidationError> {
|
||||
let id = self.id.clone();
|
||||
let pool = self.pool;
|
||||
let embeddable_on = self.embeddable_on;
|
||||
let validate = move |dapp_path: PathBuf| {
|
||||
let (file, zip_path) = write_response_and_check_hash(&id, dapp_path.clone(), &format!("{}.zip", id), response)?;
|
||||
trace!(target: "dapps", "Opening dapp bundle at {:?}", zip_path);
|
||||
// Unpack archive
|
||||
let mut zip = zip::ZipArchive::new(file)?;
|
||||
// First find manifest file
|
||||
let (mut manifest, manifest_dir) = Self::find_manifest(&mut zip)?;
|
||||
// Overwrite id to match hash
|
||||
manifest.id = id;
|
||||
|
||||
// Unpack zip
|
||||
for i in 0..zip.len() {
|
||||
let mut file = zip.by_index(i)?;
|
||||
let is_dir = file.name().chars().rev().next() == Some('/');
|
||||
|
||||
let file_path = PathBuf::from(file.name());
|
||||
let location_in_manifest_base = file_path.strip_prefix(&manifest_dir);
|
||||
// Create files that are inside manifest directory
|
||||
if let Ok(location_in_manifest_base) = location_in_manifest_base {
|
||||
let p = dapp_path.join(location_in_manifest_base);
|
||||
// Check if it's a directory
|
||||
if is_dir {
|
||||
fs::create_dir_all(p)?;
|
||||
} else {
|
||||
let mut target = fs::File::create(p)?;
|
||||
io::copy(&mut file, &mut target)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove zip
|
||||
fs::remove_file(&zip_path)?;
|
||||
|
||||
// Write manifest
|
||||
let manifest_str = serialize_manifest(&manifest).map_err(ValidationError::ManifestSerialization)?;
|
||||
let manifest_path = dapp_path.join(MANIFEST_FILENAME);
|
||||
let mut manifest_file = fs::File::create(manifest_path)?;
|
||||
manifest_file.write_all(manifest_str.as_bytes())?;
|
||||
// Create endpoint
|
||||
let endpoint = local::Dapp::new(pool, dapp_path, manifest.into(), PageCache::Enabled, embeddable_on);
|
||||
Ok(endpoint)
|
||||
};
|
||||
|
||||
// Prepare directory for dapp
|
||||
let target = self.dapps_path.join(&self.id);
|
||||
// Validate the dapp
|
||||
let result = validate(target.clone());
|
||||
// remove the file if there was an error
|
||||
if result.is_err() {
|
||||
// Ignore errors since the file might not exist
|
||||
let _ = fs::remove_dir_all(&target);
|
||||
}
|
||||
(self.on_done)(result.as_ref().ok().cloned());
|
||||
result.map(ValidatorResponse::Local)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ValidationError {
|
||||
Io(io::Error),
|
||||
Zip(zip::result::ZipError),
|
||||
InvalidContentId,
|
||||
ManifestNotFound,
|
||||
ManifestSerialization(String),
|
||||
HashMismatch { expected: H256, got: H256, },
|
||||
}
|
||||
|
||||
impl fmt::Display for ValidationError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
ValidationError::Io(ref io) => write!(f, "Unexpected IO error occured: {:?}", io),
|
||||
ValidationError::Zip(ref zip) => write!(f, "Unable to read ZIP archive: {:?}", zip),
|
||||
ValidationError::InvalidContentId => write!(f, "ID is invalid. It should be 256 bits keccak hash of content."),
|
||||
ValidationError::ManifestNotFound => write!(f, "Downloaded Dapp bundle did not contain valid manifest.json file."),
|
||||
ValidationError::ManifestSerialization(ref err) => {
|
||||
write!(f, "There was an error during Dapp Manifest serialization: {:?}", err)
|
||||
},
|
||||
ValidationError::HashMismatch { ref expected, ref got } => {
|
||||
write!(f, "Hash of downloaded content did not match. Expected:{:?}, Got:{:?}.", expected, got)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for ValidationError {
|
||||
fn from(err: io::Error) -> Self {
|
||||
ValidationError::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<zip::result::ZipError> for ValidationError {
|
||||
fn from(err: zip::result::ZipError) -> Self {
|
||||
ValidationError::Zip(err)
|
||||
}
|
||||
}
|
||||
339
dapps/src/apps/fetcher/mod.rs
Normal file
339
dapps/src/apps/fetcher/mod.rs
Normal file
@@ -0,0 +1,339 @@
|
||||
// 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/>.
|
||||
|
||||
//! Fetchable Dapps support.
|
||||
//! Manages downloaded (cached) Dapps and downloads them when necessary.
|
||||
//! Uses `URLHint` to resolve addresses into Dapps bundle file location.
|
||||
|
||||
mod installers;
|
||||
|
||||
use std::{fs, env};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use rustc_hex::FromHex;
|
||||
use futures::{future, Future};
|
||||
use futures_cpupool::CpuPool;
|
||||
use fetch::{Client as FetchClient, Fetch};
|
||||
use hash_fetch::urlhint::{URLHintContract, URLHint, URLHintResult};
|
||||
|
||||
use hyper::StatusCode;
|
||||
|
||||
use {Embeddable, SyncStatus, random_filename};
|
||||
use parking_lot::Mutex;
|
||||
use page::local;
|
||||
use handlers::{ContentHandler, ContentFetcherHandler};
|
||||
use endpoint::{self, Endpoint, EndpointPath};
|
||||
use apps::cache::{ContentCache, ContentStatus};
|
||||
|
||||
/// Limit of cached dapps/content
|
||||
const MAX_CACHED_DAPPS: usize = 20;
|
||||
|
||||
pub trait Fetcher: Endpoint + 'static {
|
||||
fn contains(&self, content_id: &str) -> bool;
|
||||
}
|
||||
|
||||
pub struct ContentFetcher<F: Fetch = FetchClient, R: URLHint + 'static = URLHintContract> {
|
||||
cache_path: PathBuf,
|
||||
resolver: R,
|
||||
cache: Arc<Mutex<ContentCache>>,
|
||||
sync: Arc<SyncStatus>,
|
||||
embeddable_on: Embeddable,
|
||||
fetch: F,
|
||||
pool: CpuPool,
|
||||
only_content: bool,
|
||||
}
|
||||
|
||||
impl<R: URLHint + 'static, F: Fetch> Drop for ContentFetcher<F, R> {
|
||||
fn drop(&mut self) {
|
||||
// Clear cache path
|
||||
let _ = fs::remove_dir_all(&self.cache_path);
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: URLHint + 'static, F: Fetch> ContentFetcher<F, R> {
|
||||
pub fn new(
|
||||
resolver: R,
|
||||
sync: Arc<SyncStatus>,
|
||||
fetch: F,
|
||||
pool: CpuPool,
|
||||
) -> Self {
|
||||
let mut cache_path = env::temp_dir();
|
||||
cache_path.push(random_filename());
|
||||
|
||||
ContentFetcher {
|
||||
cache_path,
|
||||
resolver,
|
||||
sync,
|
||||
cache: Arc::new(Mutex::new(ContentCache::default())),
|
||||
embeddable_on: None,
|
||||
fetch,
|
||||
pool,
|
||||
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: Embeddable) -> Self {
|
||||
self.embeddable_on = embeddable_on;
|
||||
self
|
||||
}
|
||||
|
||||
fn not_found(embeddable: Embeddable) -> endpoint::Response {
|
||||
Box::new(future::ok(ContentHandler::error(
|
||||
StatusCode::NotFound,
|
||||
"Resource Not Found",
|
||||
"Requested resource was not found.",
|
||||
None,
|
||||
embeddable,
|
||||
).into()))
|
||||
}
|
||||
|
||||
fn still_syncing(embeddable: Embeddable) -> endpoint::Response {
|
||||
Box::new(future::ok(ContentHandler::error(
|
||||
StatusCode::ServiceUnavailable,
|
||||
"Sync In Progress",
|
||||
"Your node is still syncing. We cannot resolve any content before it's fully synced.",
|
||||
Some("<a href=\"javascript:window.location.reload()\">Refresh</a>"),
|
||||
embeddable,
|
||||
).into()))
|
||||
}
|
||||
|
||||
fn dapps_disabled(address: Embeddable) -> endpoint::Response {
|
||||
Box::new(future::ok(ContentHandler::error(
|
||||
StatusCode::ServiceUnavailable,
|
||||
"Network Dapps Not Available",
|
||||
"This interface doesn't support network dapps for security reasons.",
|
||||
None,
|
||||
address,
|
||||
).into()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn set_status(&self, content_id: &str, status: ContentStatus) {
|
||||
self.cache.lock().insert(content_id.to_owned(), status);
|
||||
}
|
||||
|
||||
// resolve contract call synchronously.
|
||||
// TODO: port to futures-based hyper and make it all async.
|
||||
fn resolve(&self, content_id: Vec<u8>) -> Option<URLHintResult> {
|
||||
self.resolver.resolve(content_id)
|
||||
.wait()
|
||||
.unwrap_or_else(|e| { warn!("Error resolving content-id: {}", e); None })
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: URLHint + 'static, F: Fetch> Fetcher for ContentFetcher<F, R> {
|
||||
fn contains(&self, content_id: &str) -> bool {
|
||||
{
|
||||
let mut cache = self.cache.lock();
|
||||
// Check if we already have the app
|
||||
if cache.get(content_id).is_some() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// fallback to resolver
|
||||
if let Ok(content_id) = content_id.from_hex() {
|
||||
// if there is content or we are syncing return true
|
||||
self.sync.is_major_importing() || self.resolve(content_id).is_some()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: URLHint + 'static, F: Fetch> Endpoint for ContentFetcher<F, R> {
|
||||
fn respond(&self, path: EndpointPath, req: endpoint::Request) -> endpoint::Response {
|
||||
let mut cache = self.cache.lock();
|
||||
let content_id = path.app_id.clone();
|
||||
|
||||
let (new_status, handler) = {
|
||||
let status = cache.get(&content_id);
|
||||
match status {
|
||||
// Just serve the content
|
||||
Some(&mut ContentStatus::Ready(ref endpoint)) => {
|
||||
(None, endpoint.to_response(&path))
|
||||
},
|
||||
// Content is already being fetched
|
||||
Some(&mut ContentStatus::Fetching(ref fetch_control)) if !fetch_control.is_deadline_reached() => {
|
||||
trace!(target: "dapps", "Content fetching in progress. Waiting...");
|
||||
(None, fetch_control.to_response(path))
|
||||
},
|
||||
// We need to start fetching the content
|
||||
_ => {
|
||||
trace!(target: "dapps", "Content unavailable. Fetching... {:?}", content_id);
|
||||
let content_hex = content_id.from_hex().expect("to_handler is called only when `contains` returns true.");
|
||||
let content = self.resolve(content_hex);
|
||||
|
||||
let cache = self.cache.clone();
|
||||
let id = content_id.clone();
|
||||
let on_done = move |result: Option<local::Dapp>| {
|
||||
let mut cache = cache.lock();
|
||||
match result {
|
||||
Some(endpoint) => cache.insert(id.clone(), ContentStatus::Ready(endpoint)),
|
||||
// In case of error
|
||||
None => cache.remove(&id),
|
||||
};
|
||||
};
|
||||
|
||||
match content {
|
||||
// Don't serve dapps if we are still syncing (but serve content)
|
||||
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(content) => {
|
||||
let handler = match content {
|
||||
URLHintResult::Dapp(dapp) => {
|
||||
ContentFetcherHandler::new(
|
||||
req.method(),
|
||||
&dapp.url(),
|
||||
path,
|
||||
installers::Dapp::new(
|
||||
content_id.clone(),
|
||||
self.cache_path.clone(),
|
||||
Box::new(on_done),
|
||||
self.embeddable_on.clone(),
|
||||
self.pool.clone(),
|
||||
),
|
||||
self.embeddable_on.clone(),
|
||||
self.fetch.clone(),
|
||||
)
|
||||
},
|
||||
URLHintResult::GithubDapp(content) => {
|
||||
ContentFetcherHandler::new(
|
||||
req.method(),
|
||||
&content.url,
|
||||
path,
|
||||
installers::Dapp::new(
|
||||
content_id.clone(),
|
||||
self.cache_path.clone(),
|
||||
Box::new(on_done),
|
||||
self.embeddable_on.clone(),
|
||||
self.pool.clone(),
|
||||
),
|
||||
self.embeddable_on.clone(),
|
||||
self.fetch.clone(),
|
||||
)
|
||||
},
|
||||
URLHintResult::Content(content) => {
|
||||
ContentFetcherHandler::new(
|
||||
req.method(),
|
||||
&content.url,
|
||||
path,
|
||||
installers::Content::new(
|
||||
content_id.clone(),
|
||||
content.mime,
|
||||
self.cache_path.clone(),
|
||||
Box::new(on_done),
|
||||
self.pool.clone(),
|
||||
),
|
||||
self.embeddable_on.clone(),
|
||||
self.fetch.clone(),
|
||||
)
|
||||
},
|
||||
};
|
||||
|
||||
(Some(ContentStatus::Fetching(handler.fetch_control())), Box::new(handler) as endpoint::Response)
|
||||
},
|
||||
None if self.sync.is_major_importing() => {
|
||||
(None, Self::still_syncing(self.embeddable_on.clone()))
|
||||
},
|
||||
None => {
|
||||
// This may happen when sync status changes in between
|
||||
// `contains` and `to_handler`
|
||||
(None, Self::not_found(self.embeddable_on.clone()))
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(status) = new_status {
|
||||
cache.clear_garbage(MAX_CACHED_DAPPS);
|
||||
cache.insert(content_id, status);
|
||||
}
|
||||
|
||||
handler
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::env;
|
||||
use std::sync::Arc;
|
||||
use bytes::Bytes;
|
||||
use fetch::{Fetch, Client};
|
||||
use futures::future;
|
||||
use hash_fetch::urlhint::{URLHint, URLHintResult, BoxFuture};
|
||||
|
||||
use apps::cache::ContentStatus;
|
||||
use endpoint::EndpointInfo;
|
||||
use page::local;
|
||||
use super::{ContentFetcher, Fetcher};
|
||||
use {SyncStatus};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct FakeResolver;
|
||||
impl URLHint for FakeResolver {
|
||||
fn resolve(&self, _id: Bytes) -> BoxFuture<Option<URLHintResult>, String> {
|
||||
Box::new(future::ok(None))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct FakeSync(bool);
|
||||
impl SyncStatus for FakeSync {
|
||||
fn is_major_importing(&self) -> bool { self.0 }
|
||||
fn peers(&self) -> (usize, usize) { (0, 5) }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_true_if_contains_the_app() {
|
||||
// given
|
||||
let pool = ::futures_cpupool::CpuPool::new(1);
|
||||
let path = env::temp_dir();
|
||||
let fetcher = ContentFetcher::new(
|
||||
FakeResolver,
|
||||
Arc::new(FakeSync(false)),
|
||||
Client::new().unwrap(),
|
||||
pool.clone(),
|
||||
).allow_dapps(true);
|
||||
|
||||
let handler = local::Dapp::new(pool, path, EndpointInfo {
|
||||
name: "fake".into(),
|
||||
description: "".into(),
|
||||
version: "".into(),
|
||||
author: "".into(),
|
||||
icon_url: "".into(),
|
||||
local_url: Some("".into()),
|
||||
}, Default::default(), None);
|
||||
|
||||
// when
|
||||
fetcher.set_status("test", ContentStatus::Ready(handler));
|
||||
fetcher.set_status("test2", ContentStatus::Fetching(Default::default()));
|
||||
|
||||
// then
|
||||
assert_eq!(fetcher.contains("test"), true);
|
||||
assert_eq!(fetcher.contains("test2"), true);
|
||||
assert_eq!(fetcher.contains("test3"), false);
|
||||
}
|
||||
}
|
||||
138
dapps/src/apps/fs.rs
Normal file
138
dapps/src/apps/fs.rs
Normal file
@@ -0,0 +1,138 @@
|
||||
// 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::collections::BTreeMap;
|
||||
use std::io;
|
||||
use std::io::Read;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use futures_cpupool::CpuPool;
|
||||
|
||||
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest};
|
||||
use endpoint::{Endpoint, EndpointInfo};
|
||||
use page::{local, PageCache};
|
||||
use Embeddable;
|
||||
|
||||
struct LocalDapp {
|
||||
id: String,
|
||||
path: PathBuf,
|
||||
info: EndpointInfo,
|
||||
}
|
||||
|
||||
/// Tries to find and read manifest file in given `path` to extract `EndpointInfo`
|
||||
/// If manifest is not found sensible default `EndpointInfo` is returned based on given `name`.
|
||||
fn read_manifest(name: &str, mut path: PathBuf) -> EndpointInfo {
|
||||
path.push(MANIFEST_FILENAME);
|
||||
|
||||
fs::File::open(path.clone())
|
||||
.map_err(|e| format!("{:?}", e))
|
||||
.and_then(|mut f| {
|
||||
// Reat file
|
||||
let mut s = String::new();
|
||||
f.read_to_string(&mut s).map_err(|e| format!("{:?}", e))?;
|
||||
// Try to deserialize manifest
|
||||
deserialize_manifest(s)
|
||||
})
|
||||
.map(Into::into)
|
||||
.unwrap_or_else(|e| {
|
||||
warn!(target: "dapps", "Cannot read manifest file at: {:?}. Error: {:?}", path, e);
|
||||
|
||||
EndpointInfo {
|
||||
name: name.into(),
|
||||
description: name.into(),
|
||||
version: "0.0.0".into(),
|
||||
author: "?".into(),
|
||||
icon_url: "icon.png".into(),
|
||||
local_url: None,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns Dapp Id and Local Dapp Endpoint for given filesystem path.
|
||||
/// Parses the path to extract last component (for name).
|
||||
/// `None` is returned when path is invalid or non-existent.
|
||||
pub fn local_endpoint<P: AsRef<Path>>(path: P, embeddable: Embeddable, pool: CpuPool) -> Option<(String, Box<local::Dapp>)> {
|
||||
let path = path.as_ref().to_owned();
|
||||
path.canonicalize().ok().and_then(|path| {
|
||||
let name = path.file_name().and_then(|name| name.to_str());
|
||||
name.map(|name| {
|
||||
let dapp = local_dapp(name.into(), path.clone());
|
||||
(dapp.id, Box::new(local::Dapp::new(
|
||||
pool.clone(), dapp.path, dapp.info, PageCache::Disabled, embeddable.clone())
|
||||
))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
fn local_dapp(name: String, path: PathBuf) -> LocalDapp {
|
||||
// try to get manifest file
|
||||
let info = read_manifest(&name, path.clone());
|
||||
LocalDapp {
|
||||
id: name,
|
||||
path: path,
|
||||
info: info,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns endpoints for Local Dapps found for given filesystem path.
|
||||
/// Scans the directory and collects `local::Dapp`.
|
||||
pub fn local_endpoints<P: AsRef<Path>>(dapps_path: P, embeddable: Embeddable, pool: CpuPool) -> 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,
|
||||
Box::new(local::Dapp::new(pool.clone(), dapp.path, dapp.info, PageCache::Disabled, embeddable.clone()))
|
||||
);
|
||||
}
|
||||
pages
|
||||
}
|
||||
|
||||
|
||||
fn local_dapps(dapps_path: &Path) -> Vec<LocalDapp> {
|
||||
let files = fs::read_dir(dapps_path);
|
||||
if let Err(e) = files {
|
||||
warn!(target: "dapps", "Unable to load local dapps from: {}. Reason: {:?}", dapps_path.display(), e);
|
||||
return vec![];
|
||||
}
|
||||
|
||||
let files = files.expect("Check is done earlier");
|
||||
files.map(|dir| {
|
||||
let entry = dir?;
|
||||
let file_type = entry.file_type()?;
|
||||
|
||||
// skip files
|
||||
if file_type.is_file() {
|
||||
return Err(io::Error::new(io::ErrorKind::NotFound, "Not a file"));
|
||||
}
|
||||
|
||||
// take directory name and path
|
||||
entry.file_name().into_string()
|
||||
.map(|name| (name, entry.path()))
|
||||
.map_err(|e| {
|
||||
info!(target: "dapps", "Unable to load dapp: {:?}. Reason: {:?}", entry.path(), e);
|
||||
io::Error::new(io::ErrorKind::NotFound, "Invalid name")
|
||||
})
|
||||
})
|
||||
.filter_map(|m| {
|
||||
if let Err(ref e) = m {
|
||||
debug!(target: "dapps", "Ignoring local dapp: {:?}", e);
|
||||
}
|
||||
m.ok()
|
||||
})
|
||||
.map(|(name, path)| local_dapp(name, path))
|
||||
.collect()
|
||||
}
|
||||
29
dapps/src/apps/manifest.rs
Normal file
29
dapps/src/apps/manifest.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
// 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 serde_json;
|
||||
pub use apps::App as Manifest;
|
||||
|
||||
pub const MANIFEST_FILENAME: &'static str = "manifest.json";
|
||||
|
||||
pub fn deserialize_manifest(manifest: String) -> Result<Manifest, String> {
|
||||
serde_json::from_str::<Manifest>(&manifest).map_err(|e| format!("{:?}", e))
|
||||
// TODO [todr] Manifest validation (especialy: id (used as path))
|
||||
}
|
||||
|
||||
pub fn serialize_manifest(manifest: &Manifest) -> Result<String, String> {
|
||||
serde_json::to_string_pretty(manifest).map_err(|e| format!("{:?}", e))
|
||||
}
|
||||
100
dapps/src/apps/mod.rs
Normal file
100
dapps/src/apps/mod.rs
Normal file
@@ -0,0 +1,100 @@
|
||||
// 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::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use endpoint::{Endpoints, Endpoint};
|
||||
use futures_cpupool::CpuPool;
|
||||
use page;
|
||||
use proxypac::ProxyPac;
|
||||
use web::Web;
|
||||
use fetch::Fetch;
|
||||
use parity_dapps::WebApp;
|
||||
use parity_ui;
|
||||
use {WebProxyTokens, ParentFrameSettings};
|
||||
|
||||
mod app;
|
||||
mod cache;
|
||||
mod ui;
|
||||
pub mod fs;
|
||||
pub mod fetcher;
|
||||
pub mod manifest;
|
||||
|
||||
pub use self::app::App;
|
||||
|
||||
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";
|
||||
pub const WEB_PATH: &'static str = "web";
|
||||
pub const URL_REFERER: &'static str = "__referer=";
|
||||
|
||||
pub fn utils(pool: CpuPool) -> Box<Endpoint> {
|
||||
Box::new(page::builtin::Dapp::new(pool, parity_ui::App::default()))
|
||||
}
|
||||
|
||||
pub fn ui(pool: CpuPool) -> Box<Endpoint> {
|
||||
Box::new(page::builtin::Dapp::with_fallback_to_index(pool, parity_ui::App::default()))
|
||||
}
|
||||
|
||||
pub fn ui_redirection(embeddable: Option<ParentFrameSettings>) -> Box<Endpoint> {
|
||||
Box::new(ui::Redirection::new(embeddable))
|
||||
}
|
||||
|
||||
pub fn all_endpoints<F: Fetch>(
|
||||
dapps_path: PathBuf,
|
||||
extra_dapps: Vec<PathBuf>,
|
||||
dapps_domain: &str,
|
||||
embeddable: Option<ParentFrameSettings>,
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
fetch: F,
|
||||
pool: CpuPool,
|
||||
) -> (Vec<String>, Endpoints) {
|
||||
// fetch fs dapps at first to avoid overwriting builtins
|
||||
let mut pages = fs::local_endpoints(dapps_path.clone(), embeddable.clone(), pool.clone());
|
||||
let local_endpoints: Vec<String> = pages.keys().cloned().collect();
|
||||
for path in extra_dapps {
|
||||
if let Some((id, endpoint)) = fs::local_endpoint(path.clone(), embeddable.clone(), pool.clone()) {
|
||||
pages.insert(id, endpoint);
|
||||
} else {
|
||||
warn!(target: "dapps", "Ignoring invalid dapp at {}", path.display());
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE [ToDr] Dapps will be currently embeded on 8180
|
||||
insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(embeddable.clone()), pool.clone());
|
||||
// old version
|
||||
insert::<parity_ui::old::App>(&mut pages, "v1", Embeddable::Yes(embeddable.clone()), pool.clone());
|
||||
|
||||
pages.insert("proxy".into(), ProxyPac::boxed(embeddable.clone(), dapps_domain.to_owned()));
|
||||
pages.insert(WEB_PATH.into(), Web::boxed(embeddable.clone(), web_proxy_tokens.clone(), fetch.clone()));
|
||||
|
||||
(local_endpoints, pages)
|
||||
}
|
||||
|
||||
fn insert<T : WebApp + Default + 'static>(pages: &mut Endpoints, id: &str, embed_at: Embeddable, pool: CpuPool) {
|
||||
pages.insert(id.to_owned(), Box::new(match embed_at {
|
||||
Embeddable::Yes(address) => page::builtin::Dapp::new_safe_to_embed(pool, T::default(), address),
|
||||
Embeddable::No => page::builtin::Dapp::new(pool, T::default()),
|
||||
}));
|
||||
}
|
||||
|
||||
enum Embeddable {
|
||||
Yes(Option<ParentFrameSettings>),
|
||||
#[allow(dead_code)]
|
||||
No,
|
||||
}
|
||||
57
dapps/src/apps/ui.rs
Normal file
57
dapps/src/apps/ui.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
// 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::StatusCode;
|
||||
use futures::future;
|
||||
|
||||
use endpoint::{Endpoint, Request, Response, EndpointPath};
|
||||
use {handlers, Embeddable};
|
||||
|
||||
/// Redirection to UI server.
|
||||
pub struct Redirection {
|
||||
embeddable_on: Embeddable,
|
||||
}
|
||||
|
||||
impl Redirection {
|
||||
pub fn new(
|
||||
embeddable_on: Embeddable,
|
||||
) -> Self {
|
||||
Redirection {
|
||||
embeddable_on,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Endpoint for Redirection {
|
||||
fn respond(&self, _path: EndpointPath, req: Request) -> Response {
|
||||
Box::new(future::ok(if let Some(ref frame) = self.embeddable_on {
|
||||
trace!(target: "dapps", "Redirecting to signer interface.");
|
||||
let protocol = req.uri().scheme().unwrap_or("http");
|
||||
handlers::Redirection::new(format!("{}://{}:{}", protocol, &frame.host, frame.port)).into()
|
||||
} else {
|
||||
trace!(target: "dapps", "Signer disabled, returning 404.");
|
||||
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."),
|
||||
None,
|
||||
).into()
|
||||
}))
|
||||
}
|
||||
}
|
||||
58
dapps/src/endpoint.rs
Normal file
58
dapps/src/endpoint.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
// 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/>.
|
||||
|
||||
//! URL Endpoint traits
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use futures::Future;
|
||||
use hyper;
|
||||
|
||||
#[derive(Debug, PartialEq, Default, Clone)]
|
||||
pub struct EndpointPath {
|
||||
pub app_id: String,
|
||||
pub app_params: Vec<String>,
|
||||
pub query: Option<String>,
|
||||
pub host: String,
|
||||
pub port: u16,
|
||||
pub using_dapps_domains: bool,
|
||||
}
|
||||
|
||||
impl EndpointPath {
|
||||
pub fn has_no_params(&self) -> bool {
|
||||
self.app_params.is_empty() || self.app_params.iter().all(|x| x.is_empty())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct EndpointInfo {
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub version: String,
|
||||
pub author: String,
|
||||
pub icon_url: String,
|
||||
pub local_url: Option<String>,
|
||||
}
|
||||
|
||||
pub type Endpoints = BTreeMap<String, Box<Endpoint>>;
|
||||
pub type Response = Box<Future<Item=hyper::Response, Error=hyper::Error> + Send>;
|
||||
pub type Request = hyper::Request;
|
||||
|
||||
pub trait Endpoint : Send + Sync {
|
||||
fn info(&self) -> Option<&EndpointInfo> { None }
|
||||
|
||||
fn respond(&self, path: EndpointPath, req: Request) -> Response;
|
||||
}
|
||||
21
dapps/src/error_tpl.html
Normal file
21
dapps/src/error_tpl.html
Normal file
@@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>{title}</title>
|
||||
<link rel="stylesheet" href="/parity-utils/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="parity-navbar">
|
||||
</div>
|
||||
<div class="parity-box">
|
||||
<h1>{title}</h1>
|
||||
<h3>{message}</h3>
|
||||
<p><code>{details}</code></p>
|
||||
</div>
|
||||
<div class="parity-status">
|
||||
<small>{version}</small>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
88
dapps/src/handlers/content.rs
Normal file
88
dapps/src/handlers/content.rs
Normal file
@@ -0,0 +1,88 @@
|
||||
// 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/>.
|
||||
|
||||
//! Simple Content Handler
|
||||
|
||||
use hyper::{self, mime, header};
|
||||
use hyper::StatusCode;
|
||||
|
||||
use parity_version::version;
|
||||
|
||||
use handlers::add_security_headers;
|
||||
use Embeddable;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ContentHandler {
|
||||
code: StatusCode,
|
||||
content: String,
|
||||
mimetype: mime::Mime,
|
||||
safe_to_embed_on: Embeddable,
|
||||
}
|
||||
|
||||
impl ContentHandler {
|
||||
pub fn ok(content: String, mimetype: mime::Mime) -> Self {
|
||||
Self::new(StatusCode::Ok, content, mimetype)
|
||||
}
|
||||
|
||||
pub fn html(code: StatusCode, content: String, embeddable_on: Embeddable) -> Self {
|
||||
Self::new_embeddable(code, content, mime::TEXT_HTML, embeddable_on)
|
||||
}
|
||||
|
||||
pub fn error(
|
||||
code: StatusCode,
|
||||
title: &str,
|
||||
message: &str,
|
||||
details: Option<&str>,
|
||||
embeddable_on: Embeddable,
|
||||
) -> Self {
|
||||
Self::html(code, format!(
|
||||
include_str!("../error_tpl.html"),
|
||||
title=title,
|
||||
message=message,
|
||||
details=details.unwrap_or_else(|| ""),
|
||||
version=version(),
|
||||
), embeddable_on)
|
||||
}
|
||||
|
||||
pub fn new(code: StatusCode, content: String, mimetype: mime::Mime) -> Self {
|
||||
Self::new_embeddable(code, content, mimetype, None)
|
||||
}
|
||||
|
||||
pub fn new_embeddable(
|
||||
code: StatusCode,
|
||||
content: String,
|
||||
mimetype: mime::Mime,
|
||||
safe_to_embed_on: Embeddable,
|
||||
) -> Self {
|
||||
ContentHandler {
|
||||
code,
|
||||
content,
|
||||
mimetype,
|
||||
safe_to_embed_on,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<hyper::Response> for ContentHandler {
|
||||
fn into(self) -> hyper::Response {
|
||||
let mut res = hyper::Response::new()
|
||||
.with_status(self.code)
|
||||
.with_header(header::ContentType(self.mimetype))
|
||||
.with_body(self.content);
|
||||
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on);
|
||||
res
|
||||
}
|
||||
}
|
||||
46
dapps/src/handlers/echo.rs
Normal file
46
dapps/src/handlers/echo.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
// 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/>.
|
||||
|
||||
//! Echo Handler
|
||||
|
||||
use hyper::{self, header};
|
||||
|
||||
use handlers::add_security_headers;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EchoHandler {
|
||||
request: hyper::Request,
|
||||
}
|
||||
|
||||
impl EchoHandler {
|
||||
pub fn new(request: hyper::Request) -> Self {
|
||||
EchoHandler {
|
||||
request,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<hyper::Response> for EchoHandler {
|
||||
fn into(self) -> hyper::Response {
|
||||
let content_type = self.request.headers().get().cloned();
|
||||
let mut res = hyper::Response::new()
|
||||
.with_header(content_type.unwrap_or(header::ContentType::json()))
|
||||
.with_body(self.request.body());
|
||||
|
||||
add_security_headers(res.headers_mut(), None);
|
||||
res
|
||||
}
|
||||
}
|
||||
369
dapps/src/handlers/fetch.rs
Normal file
369
dapps/src/handlers/fetch.rs
Normal file
@@ -0,0 +1,369 @@
|
||||
// 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/>.
|
||||
|
||||
//! Hyper Server Handler that fetches a file during a request (proxy).
|
||||
|
||||
use std::{fmt, mem};
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::{Instant, Duration};
|
||||
use fetch::{self, Fetch};
|
||||
use futures::sync::oneshot;
|
||||
use futures::{self, Future};
|
||||
use hyper::{self, Method, StatusCode};
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use endpoint::{self, EndpointPath};
|
||||
use handlers::{ContentHandler, StreamingHandler};
|
||||
use page::local;
|
||||
use {Embeddable};
|
||||
|
||||
const FETCH_TIMEOUT: u64 = 300;
|
||||
|
||||
pub enum ValidatorResponse {
|
||||
Local(local::Dapp),
|
||||
Streaming(StreamingHandler<fetch::Response>),
|
||||
}
|
||||
|
||||
pub trait ContentValidator: Sized + Send + 'static {
|
||||
type Error: fmt::Debug + fmt::Display;
|
||||
|
||||
fn validate_and_install(self, fetch::Response) -> Result<ValidatorResponse, Self::Error>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FetchControl {
|
||||
abort: Arc<AtomicBool>,
|
||||
listeners: Arc<Mutex<Vec<oneshot::Sender<WaitResult>>>>,
|
||||
deadline: Instant,
|
||||
}
|
||||
|
||||
impl Default for FetchControl {
|
||||
fn default() -> Self {
|
||||
FetchControl {
|
||||
abort: Arc::new(AtomicBool::new(false)),
|
||||
listeners: Arc::new(Mutex::new(Vec::new())),
|
||||
deadline: Instant::now() + Duration::from_secs(FETCH_TIMEOUT),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FetchControl {
|
||||
pub fn is_deadline_reached(&self) -> bool {
|
||||
self.deadline < Instant::now()
|
||||
}
|
||||
|
||||
pub fn abort(&self) {
|
||||
self.abort.store(true, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
pub fn to_response(&self, path: EndpointPath) -> endpoint::Response {
|
||||
let (tx, receiver) = oneshot::channel();
|
||||
self.listeners.lock().push(tx);
|
||||
|
||||
Box::new(WaitingHandler {
|
||||
path,
|
||||
state: WaitState::Waiting(receiver),
|
||||
})
|
||||
}
|
||||
|
||||
fn notify<F: Fn() -> WaitResult>(&self, status: F) {
|
||||
let mut listeners = self.listeners.lock();
|
||||
for sender in listeners.drain(..) {
|
||||
trace!(target: "dapps", "Resuming request waiting for content...");
|
||||
if let Err(_) = sender.send(status()) {
|
||||
trace!(target: "dapps", "Waiting listener notification failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_status(&self, status: &FetchState) {
|
||||
match *status {
|
||||
FetchState::Error(ref handler) => self.notify(|| WaitResult::Error(handler.clone())),
|
||||
FetchState::Done(ref endpoint, _) => self.notify(|| WaitResult::Done(endpoint.clone())),
|
||||
FetchState::Streaming(_) => self.notify(|| WaitResult::NonAwaitable),
|
||||
FetchState::InProgress(_) => {},
|
||||
FetchState::Empty => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum WaitState {
|
||||
Waiting(oneshot::Receiver<WaitResult>),
|
||||
Done(endpoint::Response),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum WaitResult {
|
||||
Error(ContentHandler),
|
||||
Done(local::Dapp),
|
||||
NonAwaitable,
|
||||
}
|
||||
|
||||
pub struct WaitingHandler {
|
||||
path: EndpointPath,
|
||||
state: WaitState,
|
||||
}
|
||||
|
||||
impl Future for WaitingHandler {
|
||||
type Item = hyper::Response;
|
||||
type Error = hyper::Error;
|
||||
|
||||
fn poll(&mut self) -> futures::Poll<Self::Item, Self::Error> {
|
||||
loop {
|
||||
let new_state = match self.state {
|
||||
WaitState::Waiting(ref mut receiver) => {
|
||||
let result = try_ready!(receiver.poll().map_err(|_| hyper::Error::Timeout));
|
||||
|
||||
match result {
|
||||
WaitResult::Error(handler) => {
|
||||
return Ok(futures::Async::Ready(handler.into()));
|
||||
},
|
||||
WaitResult::NonAwaitable => {
|
||||
let errors = Errors { embeddable_on: None };
|
||||
return Ok(futures::Async::Ready(errors.streaming().into()));
|
||||
},
|
||||
WaitResult::Done(endpoint) => {
|
||||
WaitState::Done(endpoint.to_response(&self.path).into())
|
||||
},
|
||||
}
|
||||
},
|
||||
WaitState::Done(ref mut response) => {
|
||||
return response.poll()
|
||||
},
|
||||
};
|
||||
|
||||
self.state = new_state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Errors {
|
||||
embeddable_on: Embeddable,
|
||||
}
|
||||
|
||||
impl Errors {
|
||||
fn streaming(&self) -> ContentHandler {
|
||||
ContentHandler::error(
|
||||
StatusCode::BadGateway,
|
||||
"Streaming Error",
|
||||
"This content is being streamed in other place.",
|
||||
None,
|
||||
self.embeddable_on.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
fn download_error<E: fmt::Debug>(&self, e: E) -> ContentHandler {
|
||||
ContentHandler::error(
|
||||
StatusCode::BadGateway,
|
||||
"Download Error",
|
||||
"There was an error when fetching the content.",
|
||||
Some(&format!("{:?}", e)),
|
||||
self.embeddable_on.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
fn invalid_content<E: fmt::Debug>(&self, e: E) -> ContentHandler {
|
||||
ContentHandler::error(
|
||||
StatusCode::BadGateway,
|
||||
"Invalid Dapp",
|
||||
"Downloaded bundle does not contain a valid content.",
|
||||
Some(&format!("{:?}", e)),
|
||||
self.embeddable_on.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
fn timeout_error(&self) -> ContentHandler {
|
||||
ContentHandler::error(
|
||||
StatusCode::GatewayTimeout,
|
||||
"Download Timeout",
|
||||
&format!("Could not fetch content within {} seconds.", FETCH_TIMEOUT),
|
||||
None,
|
||||
self.embeddable_on.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
fn method_not_allowed(&self) -> ContentHandler {
|
||||
ContentHandler::error(
|
||||
StatusCode::MethodNotAllowed,
|
||||
"Method Not Allowed",
|
||||
"Only <code>GET</code> requests are allowed.",
|
||||
None,
|
||||
self.embeddable_on.clone(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
enum FetchState {
|
||||
Error(ContentHandler),
|
||||
InProgress(Box<Future<Item=FetchState, Error=()> + Send>),
|
||||
Streaming(hyper::Response),
|
||||
Done(local::Dapp, endpoint::Response),
|
||||
Empty,
|
||||
}
|
||||
|
||||
impl fmt::Debug for FetchState {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
use self::FetchState::*;
|
||||
|
||||
write!(fmt, "FetchState(")?;
|
||||
match *self {
|
||||
Error(ref error) => write!(fmt, "error: {:?}", error),
|
||||
InProgress(_) => write!(fmt, "in progress"),
|
||||
Streaming(ref res) => write!(fmt, "streaming: {:?}", res),
|
||||
Done(ref endpoint, _) => write!(fmt, "done: {:?}", endpoint),
|
||||
Empty => write!(fmt, "?"),
|
||||
}?;
|
||||
write!(fmt, ")")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ContentFetcherHandler {
|
||||
fetch_control: FetchControl,
|
||||
status: FetchState,
|
||||
errors: Errors,
|
||||
}
|
||||
|
||||
impl ContentFetcherHandler {
|
||||
pub fn fetch_control(&self) -> FetchControl {
|
||||
self.fetch_control.clone()
|
||||
}
|
||||
|
||||
pub fn new<H: ContentValidator, F: Fetch>(
|
||||
method: &hyper::Method,
|
||||
url: &str,
|
||||
path: EndpointPath,
|
||||
installer: H,
|
||||
embeddable_on: Embeddable,
|
||||
fetch: F,
|
||||
) -> Self {
|
||||
let fetch_control = FetchControl::default();
|
||||
let errors = Errors { embeddable_on };
|
||||
|
||||
// Validation of method
|
||||
let status = match *method {
|
||||
// Start fetching content
|
||||
Method::Get => {
|
||||
trace!(target: "dapps", "Fetching content from: {:?}", url);
|
||||
FetchState::InProgress(Self::fetch_content(
|
||||
fetch,
|
||||
url,
|
||||
fetch_control.abort.clone(),
|
||||
path,
|
||||
errors.clone(),
|
||||
installer,
|
||||
))
|
||||
},
|
||||
// or return error
|
||||
_ => FetchState::Error(errors.method_not_allowed()),
|
||||
};
|
||||
|
||||
ContentFetcherHandler {
|
||||
fetch_control,
|
||||
status,
|
||||
errors,
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_content<H: ContentValidator, F: Fetch>(
|
||||
fetch: F,
|
||||
url: &str,
|
||||
abort: Arc<AtomicBool>,
|
||||
path: EndpointPath,
|
||||
errors: Errors,
|
||||
installer: H,
|
||||
) -> Box<Future<Item=FetchState, Error=()> + Send> {
|
||||
// Start fetching the content
|
||||
let fetch2 = fetch.clone();
|
||||
let future = fetch.fetch_with_abort(url, abort.into()).then(move |result| {
|
||||
trace!(target: "dapps", "Fetching content finished. Starting validation: {:?}", result);
|
||||
Ok(match result {
|
||||
Ok(response) => match installer.validate_and_install(response) {
|
||||
Ok(ValidatorResponse::Local(endpoint)) => {
|
||||
trace!(target: "dapps", "Validation OK. Returning response.");
|
||||
let response = endpoint.to_response(&path);
|
||||
FetchState::Done(endpoint, response)
|
||||
},
|
||||
Ok(ValidatorResponse::Streaming(stream)) => {
|
||||
trace!(target: "dapps", "Validation OK. Streaming response.");
|
||||
let (reading, response) = stream.into_response();
|
||||
fetch2.process_and_forget(reading);
|
||||
FetchState::Streaming(response)
|
||||
},
|
||||
Err(e) => {
|
||||
trace!(target: "dapps", "Error while validating content: {:?}", e);
|
||||
FetchState::Error(errors.invalid_content(e))
|
||||
},
|
||||
},
|
||||
Err(e) => {
|
||||
warn!(target: "dapps", "Unable to fetch content: {:?}", e);
|
||||
FetchState::Error(errors.download_error(e))
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
// make sure to run within fetch thread pool.
|
||||
fetch.process(future)
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for ContentFetcherHandler {
|
||||
type Item = hyper::Response;
|
||||
type Error = hyper::Error;
|
||||
|
||||
fn poll(&mut self) -> futures::Poll<Self::Item, Self::Error> {
|
||||
loop {
|
||||
trace!(target: "dapps", "Polling status: {:?}", self.status);
|
||||
self.status = match mem::replace(&mut self.status, FetchState::Empty) {
|
||||
FetchState::Error(error) => {
|
||||
return Ok(futures::Async::Ready(error.into()));
|
||||
},
|
||||
FetchState::Streaming(response) => {
|
||||
return Ok(futures::Async::Ready(response));
|
||||
},
|
||||
any => any,
|
||||
};
|
||||
|
||||
let status = match self.status {
|
||||
// Request may time out
|
||||
FetchState::InProgress(_) if self.fetch_control.is_deadline_reached() => {
|
||||
trace!(target: "dapps", "Fetching dapp failed because of timeout.");
|
||||
FetchState::Error(self.errors.timeout_error())
|
||||
},
|
||||
FetchState::InProgress(ref mut receiver) => {
|
||||
// Check if there is a response
|
||||
trace!(target: "dapps", "Polling streaming response.");
|
||||
try_ready!(receiver.poll().map_err(|err| {
|
||||
warn!(target: "dapps", "Error while fetching response: {:?}", err);
|
||||
hyper::Error::Timeout
|
||||
}))
|
||||
},
|
||||
FetchState::Done(_, ref mut response) => {
|
||||
return response.poll()
|
||||
},
|
||||
FetchState::Empty => panic!("Future polled twice."),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
trace!(target: "dapps", "New status: {:?}", status);
|
||||
self.fetch_control.set_status(&status);
|
||||
self.status = status;
|
||||
}
|
||||
}
|
||||
}
|
||||
118
dapps/src/handlers/mod.rs
Normal file
118
dapps/src/handlers/mod.rs
Normal file
@@ -0,0 +1,118 @@
|
||||
// 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/>.
|
||||
|
||||
//! Hyper handlers implementations.
|
||||
|
||||
mod content;
|
||||
mod echo;
|
||||
mod fetch;
|
||||
mod reader;
|
||||
mod redirect;
|
||||
mod streaming;
|
||||
|
||||
pub use self::content::ContentHandler;
|
||||
pub use self::echo::EchoHandler;
|
||||
pub use self::fetch::{ContentFetcherHandler, ContentValidator, FetchControl, ValidatorResponse};
|
||||
pub use self::reader::Reader;
|
||||
pub use self::redirect::Redirection;
|
||||
pub use self::streaming::StreamingHandler;
|
||||
|
||||
use std::iter;
|
||||
use itertools::Itertools;
|
||||
use hyper::header;
|
||||
use {apps, address, Embeddable};
|
||||
|
||||
/// Adds security-related headers to the Response.
|
||||
pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Embeddable) {
|
||||
headers.set_raw("X-XSS-Protection", "1; mode=block");
|
||||
headers.set_raw("X-Content-Type-Options", "nosniff");
|
||||
|
||||
// Embedding header:
|
||||
if let None = embeddable_on {
|
||||
headers.set_raw("X-Frame-Options", "SAMEORIGIN");
|
||||
}
|
||||
|
||||
// Content Security Policy headers
|
||||
headers.set_raw("Content-Security-Policy", String::new()
|
||||
// Restrict everything to the same origin by default.
|
||||
+ "default-src 'self';"
|
||||
// Allow connecting to WS servers and HTTP(S) servers.
|
||||
// We could be more restrictive and allow only RPC server URL.
|
||||
+ "connect-src http: https: ws: wss:;"
|
||||
// Allow framing any content from HTTP(S).
|
||||
// Again we could only allow embedding from RPC server URL.
|
||||
// (deprecated)
|
||||
+ "frame-src 'self' http: https:;"
|
||||
// Allow framing and web workers from HTTP(S).
|
||||
+ "child-src 'self' http: https:;"
|
||||
// We allow data: blob: and HTTP(s) images.
|
||||
// We could get rid of wildcarding HTTP and only allow RPC server URL.
|
||||
// (http required for local dapps icons)
|
||||
+ "img-src 'self' 'unsafe-inline' data: blob: http: https:;"
|
||||
// Allow style from data: blob: and HTTPS.
|
||||
+ "style-src 'self' 'unsafe-inline' data: blob: https:;"
|
||||
// Allow fonts from data: and HTTPS.
|
||||
+ "font-src 'self' data: https:;"
|
||||
// Disallow objects
|
||||
+ "object-src 'none';"
|
||||
// Allow scripts
|
||||
+ {
|
||||
let script_src = embeddable_on.as_ref()
|
||||
.map(|e| e.extra_script_src.iter()
|
||||
.map(|&(ref host, port)| address(host, port))
|
||||
.join(" ")
|
||||
).unwrap_or_default();
|
||||
&format!(
|
||||
"script-src 'self' {};",
|
||||
script_src
|
||||
)
|
||||
}
|
||||
// Same restrictions as script-src with additional
|
||||
// blob: that is required for camera access (worker)
|
||||
+ "worker-src 'self' https: blob:;"
|
||||
// Run in sandbox mode (although it's not fully safe since we allow same-origin and script)
|
||||
+ "sandbox allow-same-origin allow-forms allow-modals allow-popups allow-presentation allow-scripts;"
|
||||
// Disallow submitting forms from any dapps
|
||||
+ "form-action 'none';"
|
||||
// Never allow mixed content
|
||||
+ "block-all-mixed-content;"
|
||||
// Specify if the site can be embedded.
|
||||
+ &match embeddable_on {
|
||||
Some(ref embed) => {
|
||||
let std = address(&embed.host, embed.port);
|
||||
let proxy = format!("{}.{}", apps::HOME_PAGE, embed.dapps_domain);
|
||||
let domain = format!("*.{}:{}", embed.dapps_domain, embed.port);
|
||||
|
||||
let mut ancestors = vec![std, domain, proxy]
|
||||
.into_iter()
|
||||
.chain(embed.extra_embed_on
|
||||
.iter()
|
||||
.map(|&(ref host, port)| address(host, port))
|
||||
);
|
||||
|
||||
let ancestors = if embed.host == "127.0.0.1" {
|
||||
let localhost = address("localhost", embed.port);
|
||||
ancestors.chain(iter::once(localhost)).join(" ")
|
||||
} else {
|
||||
ancestors.join(" ")
|
||||
};
|
||||
|
||||
format!("frame-ancestors {};", ancestors)
|
||||
},
|
||||
None => format!("frame-ancestors 'self';"),
|
||||
}
|
||||
);
|
||||
}
|
||||
73
dapps/src/handlers/reader.rs
Normal file
73
dapps/src/handlers/reader.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
// 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/>.
|
||||
|
||||
//! A chunk-producing io::Read wrapper.
|
||||
|
||||
use std::io::{self, Read};
|
||||
|
||||
use futures::{self, sink, Sink, Future};
|
||||
use futures::sync::mpsc;
|
||||
use hyper;
|
||||
|
||||
type Sender = mpsc::Sender<Result<hyper::Chunk, hyper::Error>>;
|
||||
|
||||
const MAX_CHUNK_SIZE: usize = 32 * 1024;
|
||||
|
||||
/// A Reader is essentially a stream of `hyper::Chunks`.
|
||||
/// The chunks are read from given `io::Read` instance.
|
||||
///
|
||||
/// Unfortunately `hyper` doesn't allow you to pass `Stream`
|
||||
/// directly to the response, so you need to create
|
||||
/// a `Body::pair()` and send over chunks using `sink::Send`.
|
||||
/// Also `Chunks` need to take `Vec` by value, so we need
|
||||
/// to allocate it for each chunk being sent.
|
||||
pub struct Reader<R: io::Read> {
|
||||
buffer: [u8; MAX_CHUNK_SIZE],
|
||||
content: io::BufReader<R>,
|
||||
sending: sink::Send<Sender>,
|
||||
}
|
||||
|
||||
impl<R: io::Read> Reader<R> {
|
||||
pub fn pair(content: R, initial: Vec<u8>) -> (Self, hyper::Body) {
|
||||
let (tx, rx) = hyper::Body::pair();
|
||||
let reader = Reader {
|
||||
buffer: [0; MAX_CHUNK_SIZE],
|
||||
content: io::BufReader::new(content),
|
||||
sending: tx.send(Ok(initial.into())),
|
||||
};
|
||||
|
||||
(reader, rx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: io::Read> Future for Reader<R> {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> futures::Poll<Self::Item, Self::Error> {
|
||||
loop {
|
||||
let next = try_ready!(self.sending.poll().map_err(|err| {
|
||||
warn!(target: "dapps", "Unable to send next chunk: {:?}", err);
|
||||
}));
|
||||
|
||||
self.sending = match self.content.read(&mut self.buffer) {
|
||||
Ok(0) => return Ok(futures::Async::Ready(())),
|
||||
Ok(read) => next.send(Ok(self.buffer[..read].to_vec().into())),
|
||||
Err(err) => next.send(Err(hyper::Error::Io(err))),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
41
dapps/src/handlers/redirect.rs
Normal file
41
dapps/src/handlers/redirect.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
// 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/>.
|
||||
|
||||
//! HTTP Redirection hyper handler
|
||||
|
||||
use hyper::{self, header, StatusCode};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Redirection {
|
||||
to_url: String
|
||||
}
|
||||
|
||||
impl Redirection {
|
||||
pub fn new<T: Into<String>>(url: T) -> Self {
|
||||
Redirection {
|
||||
to_url: url.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<hyper::Response> for Redirection {
|
||||
fn into(self) -> hyper::Response {
|
||||
// Don't use `MovedPermanently` here to prevent browser from caching the redirections.
|
||||
hyper::Response::new()
|
||||
.with_status(StatusCode::Found)
|
||||
.with_header(header::Location::new(self.to_url))
|
||||
}
|
||||
}
|
||||
58
dapps/src/handlers/streaming.rs
Normal file
58
dapps/src/handlers/streaming.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
// 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/>.
|
||||
|
||||
//! Content Stream Response
|
||||
|
||||
use std::io;
|
||||
use hyper::{self, header, mime, StatusCode};
|
||||
|
||||
use handlers::{add_security_headers, Reader};
|
||||
use Embeddable;
|
||||
|
||||
pub struct StreamingHandler<R> {
|
||||
initial: Vec<u8>,
|
||||
content: R,
|
||||
status: StatusCode,
|
||||
mimetype: mime::Mime,
|
||||
safe_to_embed_on: Embeddable,
|
||||
}
|
||||
|
||||
impl<R: io::Read> StreamingHandler<R> {
|
||||
pub fn new(content: R, status: StatusCode, mimetype: mime::Mime, safe_to_embed_on: Embeddable) -> Self {
|
||||
StreamingHandler {
|
||||
initial: Vec::new(),
|
||||
content,
|
||||
status,
|
||||
mimetype,
|
||||
safe_to_embed_on,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_initial_content(&mut self, content: &str) {
|
||||
self.initial = content.as_bytes().to_vec();
|
||||
}
|
||||
|
||||
pub fn into_response(self) -> (Reader<R>, hyper::Response) {
|
||||
let (reader, body) = Reader::pair(self.content, self.initial);
|
||||
let mut res = hyper::Response::new()
|
||||
.with_status(self.status)
|
||||
.with_header(header::ContentType(self.mimetype))
|
||||
.with_body(body);
|
||||
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on);
|
||||
|
||||
(reader, res)
|
||||
}
|
||||
}
|
||||
321
dapps/src/lib.rs
Normal file
321
dapps/src/lib.rs
Normal file
@@ -0,0 +1,321 @@
|
||||
// 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/>.
|
||||
|
||||
//! Ethcore Webapplications for Parity
|
||||
#![warn(missing_docs)]
|
||||
|
||||
extern crate base32;
|
||||
extern crate futures_cpupool;
|
||||
extern crate itertools;
|
||||
extern crate linked_hash_map;
|
||||
extern crate mime_guess;
|
||||
extern crate parking_lot;
|
||||
extern crate rand;
|
||||
extern crate rustc_hex;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
extern crate unicase;
|
||||
extern crate zip;
|
||||
|
||||
extern crate jsonrpc_http_server;
|
||||
|
||||
extern crate ethcore_util as util;
|
||||
extern crate ethcore_bigint as bigint;
|
||||
extern crate ethcore_bytes as bytes;
|
||||
extern crate fetch;
|
||||
extern crate node_health;
|
||||
extern crate parity_dapps_glue as parity_dapps;
|
||||
extern crate parity_hash_fetch as hash_fetch;
|
||||
extern crate parity_ui;
|
||||
extern crate keccak_hash as hash;
|
||||
extern crate parity_version;
|
||||
|
||||
#[macro_use]
|
||||
extern crate futures;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate env_logger;
|
||||
#[cfg(test)]
|
||||
extern crate ethcore_devtools as devtools;
|
||||
#[cfg(test)]
|
||||
extern crate jsonrpc_core;
|
||||
#[cfg(test)]
|
||||
extern crate parity_reactor;
|
||||
|
||||
mod endpoint;
|
||||
mod apps;
|
||||
mod page;
|
||||
mod router;
|
||||
mod handlers;
|
||||
mod api;
|
||||
mod proxypac;
|
||||
mod web;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::mem;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use futures_cpupool::CpuPool;
|
||||
use jsonrpc_http_server::{self as http, hyper, Origin};
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use fetch::Fetch;
|
||||
use node_health::NodeHealth;
|
||||
|
||||
pub use hash_fetch::urlhint::ContractClient;
|
||||
pub use node_health::SyncStatus;
|
||||
|
||||
|
||||
/// Validates Web Proxy tokens
|
||||
pub trait WebProxyTokens: Send + Sync {
|
||||
/// Should return a domain allowed to be accessed by this token or `None` if the token is not valid
|
||||
fn domain(&self, token: &str) -> Option<Origin>;
|
||||
}
|
||||
|
||||
impl<F> WebProxyTokens for F where F: Fn(String) -> Option<Origin> + Send + Sync {
|
||||
fn domain(&self, token: &str) -> Option<Origin> { self(token.to_owned()) }
|
||||
}
|
||||
|
||||
/// Current supported endpoints.
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Endpoints {
|
||||
local_endpoints: Arc<RwLock<Vec<String>>>,
|
||||
endpoints: Arc<RwLock<endpoint::Endpoints>>,
|
||||
dapps_path: PathBuf,
|
||||
embeddable: Option<ParentFrameSettings>,
|
||||
pool: Option<CpuPool>,
|
||||
}
|
||||
|
||||
impl Endpoints {
|
||||
/// Returns a current list of app endpoints.
|
||||
pub fn list(&self) -> Vec<apps::App> {
|
||||
self.endpoints.read().iter().filter_map(|(ref k, ref e)| {
|
||||
e.info().map(|ref info| apps::App::from_info(k, info))
|
||||
}).collect()
|
||||
}
|
||||
|
||||
/// Check for any changes in the local dapps folder and update.
|
||||
pub fn refresh_local_dapps(&self) {
|
||||
let pool = match self.pool.as_ref() {
|
||||
None => return,
|
||||
Some(pool) => pool,
|
||||
};
|
||||
let new_local = apps::fs::local_endpoints(&self.dapps_path, self.embeddable.clone(), pool.clone());
|
||||
let old_local = mem::replace(&mut *self.local_endpoints.write(), new_local.keys().cloned().collect());
|
||||
let (_, to_remove): (_, Vec<_>) = old_local
|
||||
.into_iter()
|
||||
.partition(|k| new_local.contains_key(&k.clone()));
|
||||
|
||||
let mut endpoints = self.endpoints.write();
|
||||
// remove the dead dapps
|
||||
for k in to_remove {
|
||||
endpoints.remove(&k);
|
||||
}
|
||||
// new dapps to be added
|
||||
for (k, v) in new_local {
|
||||
if !endpoints.contains_key(&k) {
|
||||
endpoints.insert(k, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Dapps server as `jsonrpc-http-server` request middleware.
|
||||
pub struct Middleware {
|
||||
endpoints: Endpoints,
|
||||
router: router::Router,
|
||||
}
|
||||
|
||||
impl Middleware {
|
||||
/// Get local endpoints handle.
|
||||
pub fn endpoints(&self) -> &Endpoints {
|
||||
&self.endpoints
|
||||
}
|
||||
|
||||
/// Creates new middleware for UI server.
|
||||
pub fn ui<F: Fetch>(
|
||||
pool: CpuPool,
|
||||
health: NodeHealth,
|
||||
dapps_domain: &str,
|
||||
registrar: Arc<ContractClient>,
|
||||
sync_status: Arc<SyncStatus>,
|
||||
fetch: F,
|
||||
) -> Self {
|
||||
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
|
||||
hash_fetch::urlhint::URLHintContract::new(registrar),
|
||||
sync_status.clone(),
|
||||
fetch.clone(),
|
||||
pool.clone(),
|
||||
).embeddable_on(None).allow_dapps(false));
|
||||
let special = {
|
||||
let mut special = special_endpoints(
|
||||
pool.clone(),
|
||||
health,
|
||||
content_fetcher.clone(),
|
||||
);
|
||||
special.insert(router::SpecialEndpoint::Home, Some(apps::ui(pool.clone())));
|
||||
special
|
||||
};
|
||||
let router = router::Router::new(
|
||||
content_fetcher,
|
||||
None,
|
||||
special,
|
||||
None,
|
||||
dapps_domain.to_owned(),
|
||||
);
|
||||
|
||||
Middleware {
|
||||
endpoints: Default::default(),
|
||||
router: router,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates new Dapps server middleware.
|
||||
pub fn dapps<F: Fetch>(
|
||||
pool: CpuPool,
|
||||
health: NodeHealth,
|
||||
ui_address: Option<(String, u16)>,
|
||||
extra_embed_on: Vec<(String, u16)>,
|
||||
extra_script_src: Vec<(String, u16)>,
|
||||
dapps_path: PathBuf,
|
||||
extra_dapps: Vec<PathBuf>,
|
||||
dapps_domain: &str,
|
||||
registrar: Arc<ContractClient>,
|
||||
sync_status: Arc<SyncStatus>,
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
fetch: F,
|
||||
) -> Self {
|
||||
let embeddable = as_embeddable(ui_address, extra_embed_on, extra_script_src, dapps_domain);
|
||||
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
|
||||
hash_fetch::urlhint::URLHintContract::new(registrar),
|
||||
sync_status.clone(),
|
||||
fetch.clone(),
|
||||
pool.clone(),
|
||||
).embeddable_on(embeddable.clone()).allow_dapps(true));
|
||||
let (local_endpoints, endpoints) = apps::all_endpoints(
|
||||
dapps_path.clone(),
|
||||
extra_dapps,
|
||||
dapps_domain,
|
||||
embeddable.clone(),
|
||||
web_proxy_tokens,
|
||||
fetch.clone(),
|
||||
pool.clone(),
|
||||
);
|
||||
let endpoints = Endpoints {
|
||||
endpoints: Arc::new(RwLock::new(endpoints)),
|
||||
dapps_path,
|
||||
local_endpoints: Arc::new(RwLock::new(local_endpoints)),
|
||||
embeddable: embeddable.clone(),
|
||||
pool: Some(pool.clone()),
|
||||
};
|
||||
|
||||
let special = {
|
||||
let mut special = special_endpoints(
|
||||
pool.clone(),
|
||||
health,
|
||||
content_fetcher.clone(),
|
||||
);
|
||||
special.insert(
|
||||
router::SpecialEndpoint::Home,
|
||||
Some(apps::ui_redirection(embeddable.clone())),
|
||||
);
|
||||
special
|
||||
};
|
||||
|
||||
let router = router::Router::new(
|
||||
content_fetcher,
|
||||
Some(endpoints.clone()),
|
||||
special,
|
||||
embeddable,
|
||||
dapps_domain.to_owned(),
|
||||
);
|
||||
|
||||
Middleware {
|
||||
endpoints,
|
||||
router,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl http::RequestMiddleware for Middleware {
|
||||
fn on_request(&self, req: hyper::Request) -> http::RequestMiddlewareAction {
|
||||
self.router.on_request(req)
|
||||
}
|
||||
}
|
||||
|
||||
fn special_endpoints(
|
||||
pool: CpuPool,
|
||||
health: NodeHealth,
|
||||
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(pool)));
|
||||
special.insert(router::SpecialEndpoint::Api, Some(api::RestApi::new(
|
||||
content_fetcher,
|
||||
health,
|
||||
)));
|
||||
special
|
||||
}
|
||||
|
||||
fn address(host: &str, port: u16) -> String {
|
||||
format!("{}:{}", host, port)
|
||||
}
|
||||
|
||||
fn as_embeddable(
|
||||
ui_address: Option<(String, u16)>,
|
||||
extra_embed_on: Vec<(String, u16)>,
|
||||
extra_script_src: Vec<(String, u16)>,
|
||||
dapps_domain: &str,
|
||||
) -> Option<ParentFrameSettings> {
|
||||
ui_address.map(|(host, port)| ParentFrameSettings {
|
||||
host,
|
||||
port,
|
||||
extra_embed_on,
|
||||
extra_script_src,
|
||||
dapps_domain: dapps_domain.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Random filename
|
||||
fn random_filename() -> String {
|
||||
use ::rand::Rng;
|
||||
let mut rng = ::rand::OsRng::new().unwrap();
|
||||
rng.gen_ascii_chars().take(12).collect()
|
||||
}
|
||||
|
||||
type Embeddable = Option<ParentFrameSettings>;
|
||||
|
||||
/// Parent frame host and port allowed to embed the content.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ParentFrameSettings {
|
||||
/// Hostname
|
||||
pub host: String,
|
||||
/// Port
|
||||
pub port: u16,
|
||||
/// Additional URLs the dapps can be embedded on.
|
||||
pub extra_embed_on: Vec<(String, u16)>,
|
||||
/// Additional URLs the dapp scripts can be loaded from.
|
||||
pub extra_script_src: Vec<(String, u16)>,
|
||||
/// Dapps Domain (web3.site)
|
||||
pub dapps_domain: String,
|
||||
}
|
||||
157
dapps/src/page/builtin.rs
Normal file
157
dapps/src/page/builtin.rs
Normal file
@@ -0,0 +1,157 @@
|
||||
// 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::io;
|
||||
use futures::future;
|
||||
use futures_cpupool::CpuPool;
|
||||
use hyper::mime::{self, Mime};
|
||||
use itertools::Itertools;
|
||||
use parity_dapps::{WebApp, Info};
|
||||
|
||||
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Request, Response};
|
||||
use page::{handler, PageCache};
|
||||
use Embeddable;
|
||||
|
||||
pub struct Dapp<T: WebApp + 'static> {
|
||||
/// futures cpu pool
|
||||
pool: CpuPool,
|
||||
/// Content of the files
|
||||
app: T,
|
||||
/// Safe to be loaded in frame by other origin. (use wisely!)
|
||||
safe_to_embed_on: Embeddable,
|
||||
info: EndpointInfo,
|
||||
fallback_to_index_html: bool,
|
||||
}
|
||||
|
||||
impl<T: WebApp + 'static> Dapp<T> {
|
||||
/// Creates new `Dapp` for builtin (compile time) Dapp.
|
||||
pub fn new(pool: CpuPool, app: T) -> Self {
|
||||
let info = app.info();
|
||||
Dapp {
|
||||
pool,
|
||||
app,
|
||||
safe_to_embed_on: None,
|
||||
info: EndpointInfo::from(info),
|
||||
fallback_to_index_html: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `Dapp` for builtin (compile time) Dapp.
|
||||
/// Instead of returning 404 this endpoint will always server index.html.
|
||||
pub fn with_fallback_to_index(pool: CpuPool, app: T) -> Self {
|
||||
let info = app.info();
|
||||
Dapp {
|
||||
pool,
|
||||
app,
|
||||
safe_to_embed_on: None,
|
||||
info: EndpointInfo::from(info),
|
||||
fallback_to_index_html: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates new `Dapp` which can be safely used in iframe
|
||||
/// even from different origin. It might be dangerous (clickjacking).
|
||||
/// Use wisely!
|
||||
pub fn new_safe_to_embed(pool: CpuPool, app: T, address: Embeddable) -> Self {
|
||||
let info = app.info();
|
||||
Dapp {
|
||||
pool,
|
||||
app,
|
||||
safe_to_embed_on: address,
|
||||
info: EndpointInfo::from(info),
|
||||
fallback_to_index_html: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: WebApp> Endpoint for Dapp<T> {
|
||||
fn info(&self) -> Option<&EndpointInfo> {
|
||||
Some(&self.info)
|
||||
}
|
||||
|
||||
fn respond(&self, path: EndpointPath, _req: Request) -> Response {
|
||||
trace!(target: "dapps", "Builtin file path: {:?}", path);
|
||||
let file_path = if path.has_no_params() {
|
||||
"index.html".to_owned()
|
||||
} else {
|
||||
path.app_params.into_iter().filter(|x| !x.is_empty()).join("/")
|
||||
};
|
||||
trace!(target: "dapps", "Builtin file: {:?}", file_path);
|
||||
|
||||
let file = {
|
||||
let file = |path| self.app.file(path).map(|file| {
|
||||
let content_type = match file.content_type.parse() {
|
||||
Ok(mime) => mime,
|
||||
Err(_) => {
|
||||
warn!(target: "dapps", "invalid MIME type: {}", file.content_type);
|
||||
mime::TEXT_HTML
|
||||
},
|
||||
};
|
||||
BuiltinFile {
|
||||
content_type,
|
||||
content: io::Cursor::new(file.content),
|
||||
}
|
||||
});
|
||||
let res = file(&file_path);
|
||||
if self.fallback_to_index_html {
|
||||
res.or_else(|| file("index.html"))
|
||||
} else {
|
||||
res
|
||||
}
|
||||
};
|
||||
|
||||
let (reader, response) = handler::PageHandler {
|
||||
file,
|
||||
cache: PageCache::Disabled,
|
||||
safe_to_embed_on: self.safe_to_embed_on.clone(),
|
||||
}.into_response();
|
||||
|
||||
self.pool.spawn(reader).forget();
|
||||
|
||||
Box::new(future::ok(response))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Info> for EndpointInfo {
|
||||
fn from(info: Info) -> Self {
|
||||
EndpointInfo {
|
||||
name: info.name.into(),
|
||||
description: info.description.into(),
|
||||
author: info.author.into(),
|
||||
icon_url: info.icon_url.into(),
|
||||
local_url: None,
|
||||
version: info.version.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct BuiltinFile {
|
||||
content_type: Mime,
|
||||
content: io::Cursor<&'static [u8]>,
|
||||
}
|
||||
|
||||
impl handler::DappFile for BuiltinFile {
|
||||
type Reader = io::Cursor<&'static [u8]>;
|
||||
|
||||
fn content_type(&self) -> &Mime {
|
||||
&self.content_type
|
||||
}
|
||||
|
||||
fn into_reader(self) -> Self::Reader {
|
||||
self.content
|
||||
}
|
||||
}
|
||||
114
dapps/src/page/handler.rs
Normal file
114
dapps/src/page/handler.rs
Normal file
@@ -0,0 +1,114 @@
|
||||
// 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::io;
|
||||
use std::time::{Duration, SystemTime};
|
||||
use hyper::{self, header, StatusCode};
|
||||
use hyper::mime::{self, Mime};
|
||||
|
||||
use apps;
|
||||
use handlers::{Reader, ContentHandler, add_security_headers};
|
||||
use {Embeddable};
|
||||
|
||||
/// Represents a file that can be sent to client.
|
||||
/// Implementation should keep track of bytes already sent internally.
|
||||
pub trait DappFile {
|
||||
/// A reader type returned by this file.
|
||||
type Reader: io::Read;
|
||||
|
||||
/// Returns a content-type of this file.
|
||||
fn content_type(&self) -> &Mime;
|
||||
|
||||
/// Convert this file into io::Read instance.
|
||||
fn into_reader(self) -> Self::Reader where Self: Sized;
|
||||
}
|
||||
|
||||
/// Defines what cache headers should be appended to returned resources.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum PageCache {
|
||||
Enabled,
|
||||
Disabled,
|
||||
}
|
||||
|
||||
impl Default for PageCache {
|
||||
fn default() -> Self {
|
||||
PageCache::Disabled
|
||||
}
|
||||
}
|
||||
|
||||
/// A handler for a single webapp.
|
||||
/// Resolves correct paths and serves as a plumbing code between
|
||||
/// hyper server and dapp.
|
||||
pub struct PageHandler<T: DappFile> {
|
||||
/// File currently being served
|
||||
pub file: Option<T>,
|
||||
/// Flag indicating if the file can be safely embeded (put in iframe).
|
||||
pub safe_to_embed_on: Embeddable,
|
||||
/// Cache settings for this page.
|
||||
pub cache: PageCache,
|
||||
}
|
||||
|
||||
impl<T: DappFile> PageHandler<T> {
|
||||
pub fn into_response(self) -> (Option<Reader<T::Reader>>, hyper::Response) {
|
||||
let file = match self.file {
|
||||
None => return (None, ContentHandler::error(
|
||||
StatusCode::NotFound,
|
||||
"File not found",
|
||||
"Requested file has not been found.",
|
||||
None,
|
||||
self.safe_to_embed_on,
|
||||
).into()),
|
||||
Some(file) => file,
|
||||
};
|
||||
|
||||
let mut res = hyper::Response::new()
|
||||
.with_status(StatusCode::Ok);
|
||||
|
||||
// headers
|
||||
{
|
||||
let mut headers = res.headers_mut();
|
||||
|
||||
if let PageCache::Enabled = self.cache {
|
||||
let validity_secs = 365u32 * 24 * 3600;
|
||||
let validity = Duration::from_secs(validity_secs as u64);
|
||||
headers.set(header::CacheControl(vec![
|
||||
header::CacheDirective::Public,
|
||||
header::CacheDirective::MaxAge(validity_secs),
|
||||
]));
|
||||
headers.set(header::Expires(header::HttpDate::from(SystemTime::now() + validity)));
|
||||
}
|
||||
|
||||
headers.set(header::ContentType(file.content_type().to_owned()));
|
||||
|
||||
add_security_headers(&mut headers, self.safe_to_embed_on);
|
||||
}
|
||||
|
||||
let initial_content = if file.content_type().to_owned() == mime::TEXT_HTML {
|
||||
let content = &format!(
|
||||
r#"<script src="/{}/inject.js"></script>"#,
|
||||
apps::UTILS_PATH,
|
||||
);
|
||||
|
||||
content.as_bytes().to_vec()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
let (reader, body) = Reader::pair(file.into_reader(), initial_content);
|
||||
res.set_body(body);
|
||||
(Some(reader), res)
|
||||
}
|
||||
}
|
||||
147
dapps/src/page/local.rs
Normal file
147
dapps/src/page/local.rs
Normal file
@@ -0,0 +1,147 @@
|
||||
// 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 mime_guess;
|
||||
use std::{fs, fmt};
|
||||
use std::path::{Path, PathBuf};
|
||||
use futures::{future};
|
||||
use futures_cpupool::CpuPool;
|
||||
use page::handler::{self, PageCache};
|
||||
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Request, Response};
|
||||
use hyper::mime::Mime;
|
||||
use Embeddable;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Dapp {
|
||||
pool: CpuPool,
|
||||
path: PathBuf,
|
||||
mime: Option<Mime>,
|
||||
info: Option<EndpointInfo>,
|
||||
cache: PageCache,
|
||||
embeddable_on: Embeddable,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Dapp {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Dapp")
|
||||
.field("path", &self.path)
|
||||
.field("mime", &self.mime)
|
||||
.field("info", &self.info)
|
||||
.field("cache", &self.cache)
|
||||
.field("embeddable_on", &self.embeddable_on)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Dapp {
|
||||
pub fn new(pool: CpuPool, path: PathBuf, info: EndpointInfo, cache: PageCache, embeddable_on: Embeddable) -> Self {
|
||||
Dapp {
|
||||
pool,
|
||||
path,
|
||||
mime: None,
|
||||
info: Some(info),
|
||||
cache,
|
||||
embeddable_on,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn single_file(pool: CpuPool, path: PathBuf, mime: Mime, cache: PageCache) -> Self {
|
||||
Dapp {
|
||||
pool,
|
||||
path,
|
||||
mime: Some(mime),
|
||||
info: None,
|
||||
cache,
|
||||
embeddable_on: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path(&self) -> PathBuf {
|
||||
self.path.clone()
|
||||
}
|
||||
|
||||
fn get_file(&self, path: &EndpointPath) -> Option<LocalFile> {
|
||||
if let Some(ref mime) = self.mime {
|
||||
return LocalFile::from_path(&self.path, mime.to_owned());
|
||||
}
|
||||
|
||||
let mut file_path = self.path.to_owned();
|
||||
|
||||
if path.has_no_params() {
|
||||
file_path.push("index.html");
|
||||
} else {
|
||||
for part in &path.app_params {
|
||||
file_path.push(part);
|
||||
}
|
||||
}
|
||||
|
||||
let mime = mime_guess::guess_mime_type(&file_path);
|
||||
LocalFile::from_path(&file_path, mime)
|
||||
}
|
||||
|
||||
|
||||
pub fn to_response(&self, path: &EndpointPath) -> Response {
|
||||
let (reader, response) = handler::PageHandler {
|
||||
file: self.get_file(path),
|
||||
cache: self.cache,
|
||||
safe_to_embed_on: self.embeddable_on.clone(),
|
||||
}.into_response();
|
||||
|
||||
self.pool.spawn(reader).forget();
|
||||
|
||||
Box::new(future::ok(response))
|
||||
}
|
||||
}
|
||||
|
||||
impl Endpoint for Dapp {
|
||||
fn info(&self) -> Option<&EndpointInfo> {
|
||||
self.info.as_ref()
|
||||
}
|
||||
|
||||
fn respond(&self, path: EndpointPath, _req: Request) -> Response {
|
||||
self.to_response(&path)
|
||||
}
|
||||
}
|
||||
|
||||
struct LocalFile {
|
||||
content_type: Mime,
|
||||
file: fs::File,
|
||||
}
|
||||
|
||||
impl LocalFile {
|
||||
fn from_path<P: AsRef<Path>>(path: P, content_type: Mime) -> Option<Self> {
|
||||
trace!(target: "dapps", "Local file: {:?}", path.as_ref());
|
||||
// Check if file exists
|
||||
fs::File::open(&path).ok().map(|file| {
|
||||
LocalFile {
|
||||
content_type,
|
||||
file,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl handler::DappFile for LocalFile {
|
||||
type Reader = fs::File;
|
||||
|
||||
fn content_type(&self) -> &Mime {
|
||||
&self.content_type
|
||||
}
|
||||
|
||||
fn into_reader(self) -> Self::Reader {
|
||||
self.file
|
||||
}
|
||||
}
|
||||
23
dapps/src/page/mod.rs
Normal file
23
dapps/src/page/mod.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
// 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/>.
|
||||
|
||||
|
||||
pub mod builtin;
|
||||
pub mod local;
|
||||
mod handler;
|
||||
|
||||
pub use self::handler::PageCache;
|
||||
|
||||
68
dapps/src/proxypac.rs
Normal file
68
dapps/src/proxypac.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
// 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/>.
|
||||
|
||||
//! Serving ProxyPac file
|
||||
|
||||
use apps::HOME_PAGE;
|
||||
use endpoint::{Endpoint, Request, Response, EndpointPath};
|
||||
use futures::future;
|
||||
use handlers::ContentHandler;
|
||||
use hyper::mime;
|
||||
use {address, Embeddable};
|
||||
|
||||
pub struct ProxyPac {
|
||||
embeddable: Embeddable,
|
||||
dapps_domain: String,
|
||||
}
|
||||
|
||||
impl ProxyPac {
|
||||
pub fn boxed(embeddable: Embeddable, dapps_domain: String) -> Box<Endpoint> {
|
||||
Box::new(ProxyPac { embeddable, dapps_domain })
|
||||
}
|
||||
}
|
||||
|
||||
impl Endpoint for ProxyPac {
|
||||
fn respond(&self, path: EndpointPath, _req: Request) -> Response {
|
||||
let ui = self.embeddable
|
||||
.as_ref()
|
||||
.map(|ref parent| address(&parent.host, parent.port))
|
||||
.unwrap_or_else(|| format!("{}:{}", path.host, path.port));
|
||||
|
||||
let content = format!(
|
||||
r#"
|
||||
function FindProxyForURL(url, host) {{
|
||||
if (shExpMatch(host, "{0}.{1}"))
|
||||
{{
|
||||
return "PROXY {4}";
|
||||
}}
|
||||
|
||||
if (shExpMatch(host, "*.{1}"))
|
||||
{{
|
||||
return "PROXY {2}:{3}";
|
||||
}}
|
||||
|
||||
return "DIRECT";
|
||||
}}
|
||||
"#,
|
||||
HOME_PAGE, self.dapps_domain, path.host, path.port, ui);
|
||||
|
||||
Box::new(future::ok(
|
||||
ContentHandler::ok(content, mime::TEXT_JAVASCRIPT).into()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
407
dapps/src/router.rs
Normal file
407
dapps/src/router.rs
Normal file
@@ -0,0 +1,407 @@
|
||||
// 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/>.
|
||||
|
||||
//! Router implementation
|
||||
//! Dispatch requests to proper application.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use futures::future;
|
||||
use hyper::{self, header, Uri};
|
||||
use jsonrpc_http_server as http;
|
||||
|
||||
use apps;
|
||||
use apps::fetcher::Fetcher;
|
||||
use endpoint::{self, Endpoint, EndpointPath};
|
||||
use Endpoints;
|
||||
use handlers;
|
||||
use Embeddable;
|
||||
|
||||
/// Special endpoints are accessible on every domain (every dapp)
|
||||
#[derive(Debug, PartialEq, Hash, Eq)]
|
||||
pub enum SpecialEndpoint {
|
||||
Rpc,
|
||||
Api,
|
||||
Utils,
|
||||
Home,
|
||||
None,
|
||||
}
|
||||
|
||||
enum Response {
|
||||
Some(endpoint::Response),
|
||||
None(hyper::Request),
|
||||
}
|
||||
|
||||
/// An endpoint router.
|
||||
/// Dispatches the request to particular Endpoint by requested uri/path.
|
||||
pub struct Router {
|
||||
endpoints: Option<Endpoints>,
|
||||
fetch: Arc<Fetcher>,
|
||||
special: HashMap<SpecialEndpoint, Option<Box<Endpoint>>>,
|
||||
embeddable_on: Embeddable,
|
||||
dapps_domain: String,
|
||||
}
|
||||
|
||||
impl Router {
|
||||
fn resolve_request(&self, req: hyper::Request, refresh_dapps: bool) -> (bool, Response) {
|
||||
// Choose proper handler depending on path / domain
|
||||
let endpoint = extract_endpoint(req.uri(), req.headers().get(), &self.dapps_domain);
|
||||
let referer = extract_referer_endpoint(&req, &self.dapps_domain);
|
||||
let is_utils = endpoint.1 == SpecialEndpoint::Utils;
|
||||
let is_get_request = *req.method() == hyper::Method::Get;
|
||||
let is_head_request = *req.method() == hyper::Method::Head;
|
||||
let has_dapp = |dapp: &str| self.endpoints
|
||||
.as_ref()
|
||||
.map_or(false, |endpoints| endpoints.endpoints.read().contains_key(dapp));
|
||||
|
||||
trace!(target: "dapps", "Routing request to {:?}. Details: {:?}", req.uri(), req);
|
||||
debug!(target: "dapps", "Handling endpoint request: {:?}, referer: {:?}", endpoint, referer);
|
||||
|
||||
(is_utils, match (endpoint.0, endpoint.1, referer) {
|
||||
// Handle invalid web requests that we can recover from
|
||||
(ref path, SpecialEndpoint::None, Some(ref referer))
|
||||
if referer.app_id == apps::WEB_PATH
|
||||
&& has_dapp(apps::WEB_PATH)
|
||||
&& !is_web_endpoint(path)
|
||||
=>
|
||||
{
|
||||
let token = referer.app_params.get(0).map(String::as_str).unwrap_or("");
|
||||
let requested = req.uri().path();
|
||||
let query = req.uri().query().map_or_else(String::new, |query| format!("?{}", query));
|
||||
let redirect_url = format!("/{}/{}{}{}", apps::WEB_PATH, token, requested, query);
|
||||
trace!(target: "dapps", "Redirecting to correct web request: {:?}", redirect_url);
|
||||
Response::Some(Box::new(future::ok(
|
||||
handlers::Redirection::new(redirect_url).into()
|
||||
)))
|
||||
},
|
||||
// First check special endpoints
|
||||
(ref path, ref endpoint, _) if self.special.contains_key(endpoint) => {
|
||||
trace!(target: "dapps", "Resolving to special endpoint.");
|
||||
let special = self.special.get(endpoint).expect("special known to contain key; qed");
|
||||
match *special {
|
||||
Some(ref special) => Response::Some(special.respond(path.clone().unwrap_or_default(), req)),
|
||||
None => Response::None(req),
|
||||
}
|
||||
},
|
||||
// Then delegate to dapp
|
||||
(Some(ref path), _, _) if has_dapp(&path.app_id) => {
|
||||
trace!(target: "dapps", "Resolving to local/builtin dapp.");
|
||||
Response::Some(self.endpoints
|
||||
.as_ref()
|
||||
.expect("endpoints known to be set; qed")
|
||||
.endpoints
|
||||
.read()
|
||||
.get(&path.app_id)
|
||||
.expect("endpoints known to contain key; qed")
|
||||
.respond(path.clone(), req))
|
||||
},
|
||||
// Try to resolve and fetch the dapp
|
||||
(Some(ref path), _, _) if self.fetch.contains(&path.app_id) => {
|
||||
trace!(target: "dapps", "Resolving to fetchable content.");
|
||||
Response::Some(self.fetch.respond(path.clone(), req))
|
||||
},
|
||||
// 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.");
|
||||
if refresh_dapps {
|
||||
debug!(target: "dapps", "Refreshing dapps and re-trying.");
|
||||
self.endpoints.as_ref().map(|endpoints| endpoints.refresh_local_dapps());
|
||||
return self.resolve_request(req, false);
|
||||
} else {
|
||||
Response::Some(Box::new(future::ok(handlers::ContentHandler::error(
|
||||
hyper::StatusCode::NotFound,
|
||||
"404 Not Found",
|
||||
"Requested content was not found.",
|
||||
None,
|
||||
self.embeddable_on.clone(),
|
||||
).into())))
|
||||
}
|
||||
},
|
||||
// Any other GET|HEAD requests to home page.
|
||||
_ if (is_get_request || is_head_request) && self.special.contains_key(&SpecialEndpoint::Home) => {
|
||||
trace!(target: "dapps", "Resolving to home page.");
|
||||
let special = self.special.get(&SpecialEndpoint::Home).expect("special known to contain key; qed");
|
||||
match *special {
|
||||
Some(ref special) => {
|
||||
let mut endpoint = EndpointPath::default();
|
||||
endpoint.app_params = req.uri().path().split('/').map(str::to_owned).collect();
|
||||
Response::Some(special.respond(endpoint, req))
|
||||
},
|
||||
None => Response::None(req),
|
||||
}
|
||||
},
|
||||
// RPC by default
|
||||
_ => {
|
||||
trace!(target: "dapps", "Resolving to RPC call.");
|
||||
Response::None(req)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl http::RequestMiddleware for Router {
|
||||
fn on_request(&self, req: hyper::Request) -> http::RequestMiddlewareAction {
|
||||
let is_origin_set = req.headers().get::<header::Origin>().is_some();
|
||||
let (is_utils, response) = self.resolve_request(req, self.endpoints.is_some());
|
||||
match response {
|
||||
Response::Some(response) => http::RequestMiddlewareAction::Respond {
|
||||
should_validate_hosts: !is_utils,
|
||||
response,
|
||||
},
|
||||
Response::None(request) => http::RequestMiddlewareAction::Proceed {
|
||||
should_continue_on_invalid_cors: !is_origin_set,
|
||||
request,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Router {
|
||||
pub fn new(
|
||||
content_fetcher: Arc<Fetcher>,
|
||||
endpoints: Option<Endpoints>,
|
||||
special: HashMap<SpecialEndpoint, Option<Box<Endpoint>>>,
|
||||
embeddable_on: Embeddable,
|
||||
dapps_domain: String,
|
||||
) -> Self {
|
||||
Router {
|
||||
endpoints: endpoints,
|
||||
fetch: content_fetcher,
|
||||
special: special,
|
||||
embeddable_on: embeddable_on,
|
||||
dapps_domain: format!(".{}", dapps_domain),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_web_endpoint(path: &Option<EndpointPath>) -> bool {
|
||||
match *path {
|
||||
Some(ref path) if path.app_id == apps::WEB_PATH => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_referer_endpoint(req: &hyper::Request, dapps_domain: &str) -> Option<EndpointPath> {
|
||||
let referer = req.headers().get::<header::Referer>();
|
||||
|
||||
let url = referer.and_then(|referer| referer.parse().ok());
|
||||
url.and_then(|url| {
|
||||
extract_url_referer_endpoint(&url, dapps_domain).or_else(|| {
|
||||
extract_endpoint(&url, None, dapps_domain).0
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn extract_url_referer_endpoint(url: &Uri, dapps_domain: &str) -> Option<EndpointPath> {
|
||||
let query = url.query();
|
||||
match query {
|
||||
Some(query) if query.starts_with(apps::URL_REFERER) => {
|
||||
let scheme = url.scheme().unwrap_or("http");
|
||||
let host = url.host().unwrap_or("unknown");
|
||||
let port = default_port(url, None);
|
||||
let referer_url = format!("{}://{}:{}/{}", scheme, host, port, &query[apps::URL_REFERER.len()..]);
|
||||
debug!(target: "dapps", "Recovering referer from query parameter: {}", referer_url);
|
||||
|
||||
if let Some(referer_url) = referer_url.parse().ok() {
|
||||
extract_endpoint(&referer_url, None, dapps_domain).0
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_endpoint(url: &Uri, extra_host: Option<&header::Host>, dapps_domain: &str) -> (Option<EndpointPath>, SpecialEndpoint) {
|
||||
fn special_endpoint(path: &[&str]) -> SpecialEndpoint {
|
||||
if path.len() <= 1 {
|
||||
return SpecialEndpoint::None;
|
||||
}
|
||||
|
||||
match path[0].as_ref() {
|
||||
apps::RPC_PATH => SpecialEndpoint::Rpc,
|
||||
apps::API_PATH => SpecialEndpoint::Api,
|
||||
apps::UTILS_PATH => SpecialEndpoint::Utils,
|
||||
apps::HOME_PAGE => SpecialEndpoint::Home,
|
||||
_ => SpecialEndpoint::None,
|
||||
}
|
||||
}
|
||||
|
||||
let port = default_port(url, extra_host.as_ref().and_then(|h| h.port()));
|
||||
let host = url.host().or_else(|| extra_host.as_ref().map(|h| h.hostname()));
|
||||
let query = url.query().map(str::to_owned);
|
||||
let mut path_segments = url.path().split('/').skip(1).collect::<Vec<_>>();
|
||||
trace!(
|
||||
target: "dapps",
|
||||
"Extracting endpoint from: {:?} (dapps: {}). Got host {:?}:{} with path {:?}",
|
||||
url, dapps_domain, host, port, path_segments
|
||||
);
|
||||
match host {
|
||||
Some(host) if host.ends_with(dapps_domain) => {
|
||||
let id = &host[0..(host.len() - dapps_domain.len())];
|
||||
let special = special_endpoint(&path_segments);
|
||||
|
||||
// remove special endpoint id from params
|
||||
if special != SpecialEndpoint::None {
|
||||
path_segments.remove(0);
|
||||
}
|
||||
|
||||
let (app_id, app_params) = if let Some(split) = id.rfind('.') {
|
||||
let (params, id) = id.split_at(split);
|
||||
path_segments.insert(0, params);
|
||||
(id[1..].to_owned(), path_segments)
|
||||
} else {
|
||||
(id.to_owned(), path_segments)
|
||||
};
|
||||
|
||||
(Some(EndpointPath {
|
||||
app_id,
|
||||
app_params: app_params.into_iter().map(Into::into).collect(),
|
||||
query,
|
||||
host: host.to_owned(),
|
||||
port,
|
||||
using_dapps_domains: true,
|
||||
}), special)
|
||||
},
|
||||
Some(host) if path_segments.len() > 1 => {
|
||||
let special = special_endpoint(&path_segments);
|
||||
let id = path_segments.remove(0);
|
||||
(Some(EndpointPath {
|
||||
app_id: id.to_owned(),
|
||||
app_params: path_segments.into_iter().map(Into::into).collect(),
|
||||
query,
|
||||
host: host.to_owned(),
|
||||
port,
|
||||
using_dapps_domains: false,
|
||||
}), special)
|
||||
},
|
||||
_ => (None, special_endpoint(&path_segments)),
|
||||
}
|
||||
}
|
||||
|
||||
fn default_port(url: &Uri, extra_port: Option<u16>) -> u16 {
|
||||
let scheme = url.scheme().unwrap_or("http");
|
||||
url.port().or(extra_port).unwrap_or_else(|| match scheme {
|
||||
"http" => 80,
|
||||
"https" => 443,
|
||||
_ => 80,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{SpecialEndpoint, EndpointPath, extract_endpoint};
|
||||
|
||||
#[test]
|
||||
fn should_extract_endpoint() {
|
||||
let dapps_domain = ".web3.site";
|
||||
|
||||
// With path prefix
|
||||
assert_eq!(
|
||||
extract_endpoint(&"http://localhost:8080/status/index.html?q=1".parse().unwrap(), None, dapps_domain),
|
||||
(Some(EndpointPath {
|
||||
app_id: "status".to_owned(),
|
||||
app_params: vec!["index.html".to_owned()],
|
||||
query: Some("q=1".into()),
|
||||
host: "localhost".to_owned(),
|
||||
port: 8080,
|
||||
using_dapps_domains: false,
|
||||
}), SpecialEndpoint::None)
|
||||
);
|
||||
|
||||
// With path prefix
|
||||
assert_eq!(
|
||||
extract_endpoint(&"http://localhost:8080/rpc/".parse().unwrap(), None, dapps_domain),
|
||||
(Some(EndpointPath {
|
||||
app_id: "rpc".to_owned(),
|
||||
app_params: vec!["".to_owned()],
|
||||
query: None,
|
||||
host: "localhost".to_owned(),
|
||||
port: 8080,
|
||||
using_dapps_domains: false,
|
||||
}), SpecialEndpoint::Rpc)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
extract_endpoint(&"http://my.status.web3.site/parity-utils/inject.js".parse().unwrap(), None, dapps_domain),
|
||||
(Some(EndpointPath {
|
||||
app_id: "status".to_owned(),
|
||||
app_params: vec!["my".into(), "inject.js".into()],
|
||||
query: None,
|
||||
host: "my.status.web3.site".to_owned(),
|
||||
port: 80,
|
||||
using_dapps_domains: true,
|
||||
}), SpecialEndpoint::Utils)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
extract_endpoint(&"http://my.status.web3.site/inject.js".parse().unwrap(), None, dapps_domain),
|
||||
(Some(EndpointPath {
|
||||
app_id: "status".to_owned(),
|
||||
app_params: vec!["my".into(), "inject.js".into()],
|
||||
query: None,
|
||||
host: "my.status.web3.site".to_owned(),
|
||||
port: 80,
|
||||
using_dapps_domains: true,
|
||||
}), SpecialEndpoint::None)
|
||||
);
|
||||
|
||||
// By Subdomain
|
||||
assert_eq!(
|
||||
extract_endpoint(&"http://status.web3.site/test.html".parse().unwrap(), None, dapps_domain),
|
||||
(Some(EndpointPath {
|
||||
app_id: "status".to_owned(),
|
||||
app_params: vec!["test.html".to_owned()],
|
||||
query: None,
|
||||
host: "status.web3.site".to_owned(),
|
||||
port: 80,
|
||||
using_dapps_domains: true,
|
||||
}), SpecialEndpoint::None)
|
||||
);
|
||||
|
||||
// RPC by subdomain
|
||||
assert_eq!(
|
||||
extract_endpoint(&"http://my.status.web3.site/rpc/".parse().unwrap(), None, dapps_domain),
|
||||
(Some(EndpointPath {
|
||||
app_id: "status".to_owned(),
|
||||
app_params: vec!["my".into(), "".into()],
|
||||
query: None,
|
||||
host: "my.status.web3.site".to_owned(),
|
||||
port: 80,
|
||||
using_dapps_domains: true,
|
||||
}), SpecialEndpoint::Rpc)
|
||||
);
|
||||
|
||||
// API by subdomain
|
||||
assert_eq!(
|
||||
extract_endpoint(&"http://my.status.web3.site/api/".parse().unwrap(), None, dapps_domain),
|
||||
(Some(EndpointPath {
|
||||
app_id: "status".to_owned(),
|
||||
app_params: vec!["my".into(), "".into()],
|
||||
query: None,
|
||||
host: "my.status.web3.site".to_owned(),
|
||||
port: 80,
|
||||
using_dapps_domains: true,
|
||||
}), SpecialEndpoint::Api)
|
||||
);
|
||||
}
|
||||
}
|
||||
86
dapps/src/tests/api.rs
Normal file
86
dapps/src/tests/api.rs
Normal file
@@ -0,0 +1,86 @@
|
||||
// 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 tests::helpers::{serve, serve_with_registrar, request, assert_security_headers};
|
||||
|
||||
#[test]
|
||||
fn should_return_error() {
|
||||
// given
|
||||
let server = serve();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /api/empty 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 404 Not Found");
|
||||
response.assert_header("Content-Type", "application/json");
|
||||
assert_eq!(response.body, format!("58\n{}\n0\n\n", r#"{"code":"404","title":"Not Found","detail":"Resource you requested has not been found."}"#));
|
||||
assert_security_headers(&response.headers);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_handle_ping() {
|
||||
// given
|
||||
let server = serve();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
POST /api/ping HTTP/1.1\r\n\
|
||||
Host: home.parity\r\n\
|
||||
Content-Type: application/json\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_eq!(response.body, "0\n\n".to_owned());
|
||||
assert_security_headers(&response.headers);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn should_try_to_resolve_dapp() {
|
||||
// given
|
||||
let (server, registrar) = serve_with_registrar();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /api/content/1472a9e190620cdf6b31f383373e45efcfe869a820c91f9ccd7eb9fb45e4985d HTTP/1.1\r\n\
|
||||
Host: home.parity\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 404 Not Found");
|
||||
assert_eq!(registrar.calls.lock().len(), 2);
|
||||
assert_security_headers(&response.headers);
|
||||
}
|
||||
544
dapps/src/tests/fetch.rs
Normal file
544
dapps/src/tests/fetch.rs
Normal file
@@ -0,0 +1,544 @@
|
||||
// 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 devtools::http_client;
|
||||
use rustc_hex::FromHex;
|
||||
use tests::helpers::{
|
||||
serve_with_registrar, serve_with_registrar_and_sync, serve_with_fetch,
|
||||
serve_with_registrar_and_fetch,
|
||||
request, assert_security_headers_for_embed,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn should_resolve_dapp() {
|
||||
// given
|
||||
let (server, registrar) = serve_with_registrar();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET / HTTP/1.1\r\n\
|
||||
Host: 1472a9e190620cdf6b31f383373e45efcfe869a820c91f9ccd7eb9fb45e4985d.web3.site\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 404 Not Found");
|
||||
assert_eq!(registrar.calls.lock().len(), 4);
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_503_when_syncing_but_should_make_the_calls() {
|
||||
// given
|
||||
let (server, registrar) = serve_with_registrar_and_sync();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET / HTTP/1.1\r\n\
|
||||
Host: 1472a9e190620cdf6b31f383373e45efcfe869a820c91f9ccd7eb9fb45e4985d.web3.site\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 503 Service Unavailable");
|
||||
assert_eq!(registrar.calls.lock().len(), 2);
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
}
|
||||
|
||||
const GAVCOIN_DAPP: &'static str = "00000000000000000000000000000000000000000000000000000000000000609faf32e1e3845e237cc6efd27187cee13b3b99db000000000000000000000000000000000000000000000000d8bd350823e28ff75e74a34215faefdc8a52fd8e00000000000000000000000000000000000000000000000000000000000000116761766f66796f726b2f676176636f696e000000000000000000000000000000";
|
||||
const GAVCOIN_ICON: &'static str = "00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d8bd350823e28ff75e74a34215faefdc8a52fd8e000000000000000000000000000000000000000000000000000000000000007768747470733a2f2f7261772e67697468756275736572636f6e74656e742e636f6d2f657468636f72652f646170702d6173736574732f623838653938336162616131613661363334356238643934343863313562313137646462353430652f746f6b656e732f676176636f696e2d36347836342e706e67000000000000000000";
|
||||
|
||||
#[test]
|
||||
fn should_return_502_on_hash_mismatch() {
|
||||
// given
|
||||
let (server, fetch, registrar) = serve_with_registrar_and_fetch();
|
||||
let gavcoin = GAVCOIN_DAPP.from_hex().unwrap();
|
||||
registrar.set_result(
|
||||
"94f093625c06887d94d9fee0d5f9cc4aaa46f33d24d1c7e4b5237e7c37d547dd".parse().unwrap(),
|
||||
Ok(gavcoin.clone())
|
||||
);
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET / HTTP/1.1\r\n\
|
||||
Host: 94f093625c06887d94d9fee0d5f9cc4aaa46f33d24d1c7e4b5237e7c37d547dd.web3.site\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
assert_eq!(registrar.calls.lock().len(), 4);
|
||||
|
||||
fetch.assert_requested("https://codeload.github.com/gavofyork/gavcoin/zip/9faf32e1e3845e237cc6efd27187cee13b3b99db");
|
||||
fetch.assert_no_more_requests();
|
||||
|
||||
response.assert_status("HTTP/1.1 502 Bad Gateway");
|
||||
assert!(response.body.contains("HashMismatch"), "Expected hash mismatch response, got: {:?}", response.body);
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_error_for_invalid_dapp_zip() {
|
||||
// given
|
||||
let (server, fetch, registrar) = serve_with_registrar_and_fetch();
|
||||
let gavcoin = GAVCOIN_DAPP.from_hex().unwrap();
|
||||
registrar.set_result(
|
||||
"2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e".parse().unwrap(),
|
||||
Ok(gavcoin.clone())
|
||||
);
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET / HTTP/1.1\r\n\
|
||||
Host: 2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e.web3.site\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
assert_eq!(registrar.calls.lock().len(), 4);
|
||||
|
||||
fetch.assert_requested("https://codeload.github.com/gavofyork/gavcoin/zip/9faf32e1e3845e237cc6efd27187cee13b3b99db");
|
||||
fetch.assert_no_more_requests();
|
||||
|
||||
response.assert_status("HTTP/1.1 502 Bad Gateway");
|
||||
assert!(response.body.contains("InvalidArchive"), "Expected invalid zip response, got: {:?}", response.body);
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_fetched_dapp_content() {
|
||||
// given
|
||||
let (server, fetch, registrar) = serve_with_registrar_and_fetch();
|
||||
let gavcoin = GAVCOIN_DAPP.from_hex().unwrap();
|
||||
registrar.set_result(
|
||||
"9c94e154dab8acf859b30ee80fc828fb1d38359d938751b65db71d460588d82a".parse().unwrap(),
|
||||
Ok(gavcoin.clone())
|
||||
);
|
||||
fetch.set_response(include_bytes!("../../res/gavcoin.zip"));
|
||||
|
||||
// when
|
||||
let response1 = http_client::request(server.addr(),
|
||||
"\
|
||||
GET /index.html HTTP/1.1\r\n\
|
||||
Host: 9c94e154dab8acf859b30ee80fc828fb1d38359d938751b65db71d460588d82a.web3.site\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
let response2 = http_client::request(server.addr(),
|
||||
"\
|
||||
GET /manifest.json HTTP/1.1\r\n\
|
||||
Host: 9c94e154dab8acf859b30ee80fc828fb1d38359d938751b65db71d460588d82a.web3.site\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
assert_eq!(registrar.calls.lock().len(), 4);
|
||||
|
||||
fetch.assert_requested("https://codeload.github.com/gavofyork/gavcoin/zip/9faf32e1e3845e237cc6efd27187cee13b3b99db");
|
||||
fetch.assert_no_more_requests();
|
||||
|
||||
response1.assert_status("HTTP/1.1 200 OK");
|
||||
assert_security_headers_for_embed(&response1.headers);
|
||||
assert!(
|
||||
response1.body.contains(r#"18
|
||||
<h1>Hello Gavcoin!</h1>
|
||||
|
||||
0
|
||||
|
||||
"#),
|
||||
"Expected Gavcoin body: {}",
|
||||
response1.body
|
||||
);
|
||||
|
||||
response2.assert_status("HTTP/1.1 200 OK");
|
||||
assert_security_headers_for_embed(&response2.headers);
|
||||
assert_eq!(
|
||||
response2.body,
|
||||
r#"D2
|
||||
{
|
||||
"id": "9c94e154dab8acf859b30ee80fc828fb1d38359d938751b65db71d460588d82a",
|
||||
"name": "Gavcoin",
|
||||
"description": "Gavcoin",
|
||||
"version": "1.0.0",
|
||||
"author": "",
|
||||
"iconUrl": "icon.png",
|
||||
"localUrl": null
|
||||
}
|
||||
0
|
||||
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_fetched_content() {
|
||||
// given
|
||||
let (server, fetch, registrar) = serve_with_registrar_and_fetch();
|
||||
let gavcoin = GAVCOIN_ICON.from_hex().unwrap();
|
||||
registrar.set_result(
|
||||
"2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e".parse().unwrap(),
|
||||
Ok(gavcoin.clone())
|
||||
);
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET / HTTP/1.1\r\n\
|
||||
Host: 2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e.web3.site\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
assert_eq!(registrar.calls.lock().len(), 4);
|
||||
|
||||
fetch.assert_requested("https://raw.githubusercontent.com/ethcore/dapp-assets/b88e983abaa1a6a6345b8d9448c15b117ddb540e/tokens/gavcoin-64x64.png");
|
||||
fetch.assert_no_more_requests();
|
||||
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
response.assert_security_headers_present(None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_cache_content() {
|
||||
// given
|
||||
let (server, fetch, registrar) = serve_with_registrar_and_fetch();
|
||||
let gavcoin = GAVCOIN_ICON.from_hex().unwrap();
|
||||
registrar.set_result(
|
||||
"2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e".parse().unwrap(),
|
||||
Ok(gavcoin.clone())
|
||||
);
|
||||
let request_str = "\
|
||||
GET / HTTP/1.1\r\n\
|
||||
Host: 2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e.web3.site\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
";
|
||||
|
||||
let response = http_client::request(server.addr(), request_str);
|
||||
fetch.assert_requested("https://raw.githubusercontent.com/ethcore/dapp-assets/b88e983abaa1a6a6345b8d9448c15b117ddb540e/tokens/gavcoin-64x64.png");
|
||||
fetch.assert_no_more_requests();
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
|
||||
// when
|
||||
let response = http_client::request(server.addr(), request_str);
|
||||
|
||||
// then
|
||||
fetch.assert_no_more_requests();
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_request_content_twice() {
|
||||
use std::thread;
|
||||
|
||||
// given
|
||||
let (server, fetch, registrar) = serve_with_registrar_and_fetch();
|
||||
let gavcoin = GAVCOIN_ICON.from_hex().unwrap();
|
||||
registrar.set_result(
|
||||
"2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e".parse().unwrap(),
|
||||
Ok(gavcoin.clone())
|
||||
);
|
||||
let request_str = "\
|
||||
GET / HTTP/1.1\r\n\
|
||||
Host: 2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e.web3.site\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
";
|
||||
let fire_request = || {
|
||||
let addr = server.addr().to_owned();
|
||||
let req = request_str.to_owned();
|
||||
thread::spawn(move || {
|
||||
http_client::request(&addr, &req)
|
||||
})
|
||||
};
|
||||
let control = fetch.manual();
|
||||
|
||||
// when
|
||||
|
||||
// Fire two requests at the same time
|
||||
let r1 = fire_request();
|
||||
let r2 = fire_request();
|
||||
|
||||
// wait for single request in fetch, the second one should go into waiting state.
|
||||
control.wait_for_requests(1);
|
||||
control.respond();
|
||||
|
||||
let response1 = r1.join().unwrap();
|
||||
let response2 = r2.join().unwrap();
|
||||
|
||||
// then
|
||||
fetch.assert_requested("https://raw.githubusercontent.com/ethcore/dapp-assets/b88e983abaa1a6a6345b8d9448c15b117ddb540e/tokens/gavcoin-64x64.png");
|
||||
fetch.assert_no_more_requests();
|
||||
response1.assert_status("HTTP/1.1 200 OK");
|
||||
response2.assert_status("HTTP/1.1 200 OK");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_encode_and_decode_base32() {
|
||||
use base32;
|
||||
|
||||
let encoded = base32::encode(base32::Alphabet::Crockford, "token+https://parity.io".as_bytes());
|
||||
assert_eq!("EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY", &encoded);
|
||||
|
||||
let data = base32::decode(base32::Alphabet::Crockford, "EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY").unwrap();
|
||||
assert_eq!("token+https://parity.io", &String::from_utf8(data).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_stream_web_content() {
|
||||
// given
|
||||
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET / HTTP/1.1\r\n\
|
||||
Host: EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY.web.web3.site\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
|
||||
fetch.assert_requested("https://parity.io/");
|
||||
fetch.assert_no_more_requests();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_support_base32_encoded_web_urls() {
|
||||
// given
|
||||
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /styles.css?test=123 HTTP/1.1\r\n\
|
||||
Host: EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY.web.web3.site\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
|
||||
fetch.assert_requested("https://parity.io/styles.css?test=123");
|
||||
fetch.assert_no_more_requests();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_correctly_handle_long_label_when_splitted() {
|
||||
// given
|
||||
let (server, fetch) = serve_with_fetch("xolrg9fePeQyKLnL", "https://contribution.melonport.com");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /styles.css?test=123 HTTP/1.1\r\n\
|
||||
Host: f1qprwk775k6am35a5wmpk3e9gnpgx3me1sk.mbsfcdqpwx3jd5h7ax39dxq2wvb5dhqpww3fe9t2wrvfdm.web.web3.site\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
|
||||
fetch.assert_requested("https://contribution.melonport.com/styles.css?test=123");
|
||||
fetch.assert_no_more_requests();
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn should_support_base32_encoded_web_urls_as_path() {
|
||||
// given
|
||||
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /web/EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY/styles.css?test=123 HTTP/1.1\r\n\
|
||||
Host: localhost:8080\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
|
||||
fetch.assert_requested("https://parity.io/styles.css?test=123");
|
||||
fetch.assert_no_more_requests();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_error_on_non_whitelisted_domain() {
|
||||
// given
|
||||
let (server, fetch) = serve_with_fetch("token", "https://ethcore.io");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET / HTTP/1.1\r\n\
|
||||
Host: EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY.web.web3.site\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 400 Bad Request");
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
|
||||
fetch.assert_no_more_requests();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_error_on_invalid_token() {
|
||||
// given
|
||||
let (server, fetch) = serve_with_fetch("test", "https://parity.io");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET / HTTP/1.1\r\n\
|
||||
Host: EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY.web.web3.site\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 400 Bad Request");
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
|
||||
fetch.assert_no_more_requests();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_error_on_invalid_protocol() {
|
||||
// given
|
||||
let (server, fetch) = serve_with_fetch("token", "ftp://parity.io");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /web/token/ftp/parity.io/ HTTP/1.1\r\n\
|
||||
Host: localhost:8080\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 400 Bad Request");
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
|
||||
fetch.assert_no_more_requests();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_disallow_non_get_requests() {
|
||||
// given
|
||||
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
POST / HTTP/1.1\r\n\
|
||||
Host: EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY.web.web3.site\r\n\
|
||||
Content-Type: application/json\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
123\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 405 Method Not Allowed");
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
|
||||
fetch.assert_no_more_requests();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_fix_absolute_requests_based_on_referer() {
|
||||
// given
|
||||
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /styles.css HTTP/1.1\r\n\
|
||||
Host: localhost:8080\r\n\
|
||||
Connection: close\r\n\
|
||||
Referer: http://localhost:8080/web/EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY/\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 302 Found");
|
||||
response.assert_header("Location", "/web/EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY/styles.css");
|
||||
|
||||
fetch.assert_no_more_requests();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_fix_absolute_requests_based_on_referer_in_url() {
|
||||
// given
|
||||
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /styles.css HTTP/1.1\r\n\
|
||||
Host: localhost:8080\r\n\
|
||||
Connection: close\r\n\
|
||||
Referer: http://localhost:8080/?__referer=web/EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY/\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 302 Found");
|
||||
response.assert_header("Location", "/web/EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY/styles.css");
|
||||
|
||||
fetch.assert_no_more_requests();
|
||||
}
|
||||
133
dapps/src/tests/helpers/fetch.rs
Normal file
133
dapps/src/tests/helpers/fetch.rs
Normal file
@@ -0,0 +1,133 @@
|
||||
// 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::{io, thread, time};
|
||||
use std::sync::{atomic, mpsc, Arc};
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use futures::{self, Future};
|
||||
use fetch::{self, Fetch};
|
||||
|
||||
pub struct FetchControl {
|
||||
sender: mpsc::Sender<()>,
|
||||
fetch: FakeFetch,
|
||||
}
|
||||
|
||||
impl FetchControl {
|
||||
pub fn respond(self) {
|
||||
self.sender.send(())
|
||||
.expect("Fetch cannot be finished without sending a response at least once.");
|
||||
}
|
||||
|
||||
pub fn wait_for_requests(&self, len: usize) {
|
||||
const MAX_TIMEOUT_MS: u64 = 5000;
|
||||
const ATTEMPTS: u64 = 10;
|
||||
let mut attempts_left = ATTEMPTS;
|
||||
loop {
|
||||
let current = self.fetch.requested.lock().len();
|
||||
|
||||
if current == len {
|
||||
break;
|
||||
} else if attempts_left == 0 {
|
||||
panic!(
|
||||
"Timeout reached when waiting for pending requests. Expected: {}, current: {}",
|
||||
len, current
|
||||
);
|
||||
} else {
|
||||
attempts_left -= 1;
|
||||
// Should we handle spurious timeouts better?
|
||||
thread::park_timeout(time::Duration::from_millis(MAX_TIMEOUT_MS / ATTEMPTS));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct FakeFetch {
|
||||
manual: Arc<Mutex<Option<mpsc::Receiver<()>>>>,
|
||||
response: Arc<Mutex<Option<&'static [u8]>>>,
|
||||
asserted: Arc<atomic::AtomicUsize>,
|
||||
requested: Arc<Mutex<Vec<String>>>,
|
||||
}
|
||||
|
||||
impl FakeFetch {
|
||||
pub fn set_response(&self, data: &'static [u8]) {
|
||||
*self.response.lock() = Some(data);
|
||||
}
|
||||
|
||||
pub fn manual(&self) -> FetchControl {
|
||||
assert!(self.manual.lock().is_none(), "Only one manual control may be active.");
|
||||
let (tx, rx) = mpsc::channel();
|
||||
*self.manual.lock() = Some(rx);
|
||||
|
||||
FetchControl {
|
||||
sender: tx,
|
||||
fetch: self.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assert_requested(&self, url: &str) {
|
||||
let requests = self.requested.lock();
|
||||
let idx = self.asserted.fetch_add(1, atomic::Ordering::SeqCst);
|
||||
|
||||
assert_eq!(requests.get(idx), Some(&url.to_owned()), "Expected fetch from specific URL.");
|
||||
}
|
||||
|
||||
pub fn assert_no_more_requests(&self) {
|
||||
let requests = self.requested.lock();
|
||||
let len = self.asserted.load(atomic::Ordering::SeqCst);
|
||||
assert_eq!(requests.len(), len, "Didn't expect any more requests, got: {:?}", &requests[len..]);
|
||||
}
|
||||
}
|
||||
|
||||
impl Fetch for FakeFetch {
|
||||
type Result = Box<Future<Item = fetch::Response, Error = fetch::Error> + Send>;
|
||||
|
||||
fn new() -> Result<Self, fetch::Error> where Self: Sized {
|
||||
Ok(FakeFetch::default())
|
||||
}
|
||||
|
||||
fn fetch_with_abort(&self, url: &str, _abort: fetch::Abort) -> Self::Result {
|
||||
self.requested.lock().push(url.into());
|
||||
let manual = self.manual.clone();
|
||||
let response = self.response.clone();
|
||||
|
||||
let (tx, rx) = futures::oneshot();
|
||||
thread::spawn(move || {
|
||||
if let Some(rx) = manual.lock().take() {
|
||||
// wait for manual resume
|
||||
let _ = rx.recv();
|
||||
}
|
||||
|
||||
let data = response.lock().take().unwrap_or(b"Some content");
|
||||
let cursor = io::Cursor::new(data);
|
||||
tx.send(fetch::Response::from_reader(cursor)).unwrap();
|
||||
});
|
||||
|
||||
Box::new(rx.map_err(|_| fetch::Error::Aborted))
|
||||
}
|
||||
|
||||
fn process_and_forget<F, I, E>(&self, f: F) where
|
||||
F: Future<Item=I, Error=E> + Send + 'static,
|
||||
I: Send + 'static,
|
||||
E: Send + 'static,
|
||||
{
|
||||
// Spawn the task in a separate thread.
|
||||
thread::spawn(|| {
|
||||
let _ = f.wait();
|
||||
});
|
||||
}
|
||||
}
|
||||
297
dapps/src/tests/helpers/mod.rs
Normal file
297
dapps/src/tests/helpers/mod.rs
Normal file
@@ -0,0 +1,297 @@
|
||||
// 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::{env, io, str};
|
||||
use std::net::SocketAddr;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use env_logger::LogBuilder;
|
||||
use jsonrpc_core::IoHandler;
|
||||
use jsonrpc_http_server::{self as http, Host, DomainsValidation};
|
||||
use parity_reactor::Remote;
|
||||
|
||||
use devtools::http_client;
|
||||
use hash_fetch::urlhint::ContractClient;
|
||||
use fetch::{Fetch, Client as FetchClient};
|
||||
use node_health::{NodeHealth, TimeChecker, CpuPool};
|
||||
|
||||
use {Middleware, SyncStatus, WebProxyTokens};
|
||||
|
||||
mod registrar;
|
||||
mod fetch;
|
||||
|
||||
use self::registrar::FakeRegistrar;
|
||||
use self::fetch::FakeFetch;
|
||||
|
||||
const SIGNER_PORT: u16 = 18180;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct FakeSync(bool);
|
||||
impl SyncStatus for FakeSync {
|
||||
fn is_major_importing(&self) -> bool { self.0 }
|
||||
fn peers(&self) -> (usize, usize) { (0, 5) }
|
||||
}
|
||||
|
||||
fn init_logger() {
|
||||
// Initialize logger
|
||||
if let Ok(log) = env::var("RUST_LOG") {
|
||||
let mut builder = LogBuilder::new();
|
||||
builder.parse(&log);
|
||||
let _ = builder.init(); // ignore errors since ./test.sh will call this multiple times.
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_server<F, B>(process: F, io: IoHandler) -> (Server, Arc<FakeRegistrar>) where
|
||||
F: FnOnce(ServerBuilder) -> ServerBuilder<B>,
|
||||
B: Fetch,
|
||||
{
|
||||
init_logger();
|
||||
let registrar = Arc::new(FakeRegistrar::new());
|
||||
let mut dapps_path = env::temp_dir();
|
||||
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
|
||||
|
||||
let mut builder = ServerBuilder::new(&dapps_path, registrar.clone());
|
||||
builder.signer_address = Some(("127.0.0.1".into(), SIGNER_PORT));
|
||||
let server = process(builder).start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), io).unwrap();
|
||||
(
|
||||
server,
|
||||
registrar,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn serve_with_rpc(io: IoHandler) -> Server {
|
||||
init_server(|builder| builder, io).0
|
||||
}
|
||||
|
||||
pub fn serve_hosts(hosts: Option<Vec<String>>) -> Server {
|
||||
let hosts = hosts.map(|hosts| hosts.into_iter().map(Into::into).collect());
|
||||
init_server(|mut builder| {
|
||||
builder.allowed_hosts = hosts.into();
|
||||
builder
|
||||
}, Default::default()).0
|
||||
}
|
||||
|
||||
pub fn serve_with_registrar() -> (Server, Arc<FakeRegistrar>) {
|
||||
init_server(|builder| builder, Default::default())
|
||||
}
|
||||
|
||||
pub fn serve_with_registrar_and_sync() -> (Server, Arc<FakeRegistrar>) {
|
||||
init_server(|mut builder| {
|
||||
builder.sync_status = Arc::new(FakeSync(true));
|
||||
builder
|
||||
}, Default::default())
|
||||
}
|
||||
|
||||
pub fn serve_with_registrar_and_fetch() -> (Server, FakeFetch, Arc<FakeRegistrar>) {
|
||||
let fetch = FakeFetch::default();
|
||||
let f = fetch.clone();
|
||||
let (server, reg) = init_server(move |builder| {
|
||||
builder.fetch(f.clone())
|
||||
}, Default::default());
|
||||
|
||||
(server, fetch, reg)
|
||||
}
|
||||
|
||||
pub fn serve_with_fetch(web_token: &'static str, domain: &'static str) -> (Server, FakeFetch) {
|
||||
let fetch = FakeFetch::default();
|
||||
let f = fetch.clone();
|
||||
let (server, _) = init_server(move |mut builder| {
|
||||
builder.web_proxy_tokens = Arc::new(move |token| {
|
||||
if &token == web_token { Some(domain.into()) } else { None }
|
||||
});
|
||||
builder.fetch(f.clone())
|
||||
}, Default::default());
|
||||
|
||||
(server, fetch)
|
||||
}
|
||||
|
||||
pub fn serve() -> Server {
|
||||
init_server(|builder| builder, Default::default()).0
|
||||
}
|
||||
|
||||
pub fn serve_ui() -> Server {
|
||||
init_server(|mut builder| {
|
||||
builder.serve_ui = true;
|
||||
builder
|
||||
}, Default::default()).0
|
||||
}
|
||||
|
||||
pub fn request(server: Server, request: &str) -> http_client::Response {
|
||||
http_client::request(server.addr(), request)
|
||||
}
|
||||
|
||||
pub fn assert_security_headers(headers: &[String]) {
|
||||
http_client::assert_security_headers_present(headers, None)
|
||||
}
|
||||
pub fn assert_security_headers_for_embed(headers: &[String]) {
|
||||
http_client::assert_security_headers_present(headers, Some(SIGNER_PORT))
|
||||
}
|
||||
|
||||
|
||||
/// Webapps HTTP+RPC server build.
|
||||
pub struct ServerBuilder<T: Fetch = FetchClient> {
|
||||
dapps_path: PathBuf,
|
||||
registrar: Arc<ContractClient>,
|
||||
sync_status: Arc<SyncStatus>,
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
signer_address: Option<(String, u16)>,
|
||||
allowed_hosts: DomainsValidation<Host>,
|
||||
fetch: Option<T>,
|
||||
serve_ui: bool,
|
||||
}
|
||||
|
||||
impl ServerBuilder {
|
||||
/// Construct new dapps server
|
||||
pub fn new<P: AsRef<Path>>(dapps_path: P, registrar: Arc<ContractClient>) -> Self {
|
||||
ServerBuilder {
|
||||
dapps_path: dapps_path.as_ref().to_owned(),
|
||||
registrar: registrar,
|
||||
sync_status: Arc::new(FakeSync(false)),
|
||||
web_proxy_tokens: Arc::new(|_| None),
|
||||
signer_address: None,
|
||||
allowed_hosts: DomainsValidation::Disabled,
|
||||
fetch: None,
|
||||
serve_ui: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Fetch> ServerBuilder<T> {
|
||||
/// Set a fetch client to use.
|
||||
pub fn fetch<X: Fetch>(self, fetch: X) -> ServerBuilder<X> {
|
||||
ServerBuilder {
|
||||
dapps_path: self.dapps_path,
|
||||
registrar: self.registrar,
|
||||
sync_status: self.sync_status,
|
||||
web_proxy_tokens: self.web_proxy_tokens,
|
||||
signer_address: self.signer_address,
|
||||
allowed_hosts: self.allowed_hosts,
|
||||
fetch: Some(fetch),
|
||||
serve_ui: self.serve_ui,
|
||||
}
|
||||
}
|
||||
|
||||
/// Asynchronously start server with no authentication,
|
||||
/// returns result with `Server` handle on success or an error.
|
||||
pub fn start_unsecured_http(self, addr: &SocketAddr, io: IoHandler) -> io::Result<Server> {
|
||||
let fetch = self.fetch_client();
|
||||
Server::start_http(
|
||||
addr,
|
||||
io,
|
||||
self.allowed_hosts,
|
||||
self.signer_address,
|
||||
self.dapps_path,
|
||||
vec![],
|
||||
self.registrar,
|
||||
self.sync_status,
|
||||
self.web_proxy_tokens,
|
||||
Remote::new_sync(),
|
||||
fetch,
|
||||
self.serve_ui,
|
||||
)
|
||||
}
|
||||
|
||||
fn fetch_client(&self) -> T {
|
||||
match self.fetch.clone() {
|
||||
Some(fetch) => fetch,
|
||||
None => T::new().unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const DAPPS_DOMAIN: &'static str = "web3.site";
|
||||
|
||||
/// Webapps HTTP server.
|
||||
pub struct Server {
|
||||
server: Option<http::Server>,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
fn start_http<F: Fetch>(
|
||||
addr: &SocketAddr,
|
||||
io: IoHandler,
|
||||
allowed_hosts: DomainsValidation<Host>,
|
||||
signer_address: Option<(String, u16)>,
|
||||
dapps_path: PathBuf,
|
||||
extra_dapps: Vec<PathBuf>,
|
||||
registrar: Arc<ContractClient>,
|
||||
sync_status: Arc<SyncStatus>,
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
remote: Remote,
|
||||
fetch: F,
|
||||
serve_ui: bool,
|
||||
) -> io::Result<Server> {
|
||||
let health = NodeHealth::new(
|
||||
sync_status.clone(),
|
||||
TimeChecker::new::<String>(&[], CpuPool::new(1)),
|
||||
remote.clone(),
|
||||
);
|
||||
let pool = ::futures_cpupool::CpuPool::new(1);
|
||||
let middleware = if serve_ui {
|
||||
Middleware::ui(
|
||||
pool,
|
||||
health,
|
||||
DAPPS_DOMAIN.into(),
|
||||
registrar,
|
||||
sync_status,
|
||||
fetch,
|
||||
)
|
||||
} else {
|
||||
Middleware::dapps(
|
||||
pool,
|
||||
health,
|
||||
signer_address,
|
||||
vec![],
|
||||
vec![],
|
||||
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(|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.into())
|
||||
.cors(http::DomainsValidation::Disabled)
|
||||
.start_http(addr)
|
||||
.map(|server| Server {
|
||||
server: Some(server),
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns address that this server is bound to.
|
||||
pub fn addr(&self) -> &SocketAddr {
|
||||
self.server.as_ref()
|
||||
.expect("server is always Some at the start; it's consumed only when object is dropped; qed")
|
||||
.address()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Server {
|
||||
fn drop(&mut self) {
|
||||
self.server.take().unwrap().close()
|
||||
}
|
||||
}
|
||||
76
dapps/src/tests/helpers/registrar.rs
Normal file
76
dapps/src/tests/helpers/registrar.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
// 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::str;
|
||||
use std::sync::Arc;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use bigint::hash::H256;
|
||||
use bytes::{Bytes, ToPretty};
|
||||
use hash_fetch::urlhint::{ContractClient, BoxFuture};
|
||||
use parking_lot::Mutex;
|
||||
use rustc_hex::FromHex;
|
||||
use util::Address;
|
||||
|
||||
const REGISTRAR: &'static str = "8e4e9b13d4b45cb0befc93c3061b1408f67316b2";
|
||||
const URLHINT: &'static str = "deadbeefcafe0000000000000000000000000000";
|
||||
const URLHINT_RESOLVE: &'static str = "267b6922";
|
||||
const DEFAULT_HASH: &'static str = "1472a9e190620cdf6b31f383373e45efcfe869a820c91f9ccd7eb9fb45e4985d";
|
||||
|
||||
pub struct FakeRegistrar {
|
||||
pub calls: Arc<Mutex<Vec<(String, String)>>>,
|
||||
pub responses: Mutex<HashMap<(String, String), Result<Bytes, String>>>,
|
||||
}
|
||||
|
||||
impl FakeRegistrar {
|
||||
pub fn new() -> Self {
|
||||
FakeRegistrar {
|
||||
calls: Arc::new(Mutex::new(Vec::new())),
|
||||
responses: Mutex::new({
|
||||
let mut map = HashMap::new();
|
||||
map.insert(
|
||||
(REGISTRAR.into(), "6795dbcd058740ee9a5a3fb9f1cfa10752baec87e09cc45cd7027fd54708271aca300c75000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000014100000000000000000000000000000000000000000000000000000000000000".into()),
|
||||
Ok(format!("000000000000000000000000{}", URLHINT).from_hex().unwrap()),
|
||||
);
|
||||
map.insert(
|
||||
(URLHINT.into(), format!("{}{}", URLHINT_RESOLVE, DEFAULT_HASH)),
|
||||
Ok(vec![])
|
||||
);
|
||||
map
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_result(&self, hash: H256, result: Result<Bytes, String>) {
|
||||
self.responses.lock().insert(
|
||||
(URLHINT.into(), format!("{}{:?}", URLHINT_RESOLVE, hash)),
|
||||
result
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl ContractClient for FakeRegistrar {
|
||||
fn registrar(&self) -> Result<Address, String> {
|
||||
Ok(REGISTRAR.parse().unwrap())
|
||||
}
|
||||
|
||||
fn call(&self, address: Address, data: Bytes) -> BoxFuture<Bytes, String> {
|
||||
let call = (address.to_hex(), data.to_hex());
|
||||
self.calls.lock().push(call.clone());
|
||||
let res = self.responses.lock().get(&call).cloned().expect(&format!("No response for call: {:?}", call));
|
||||
Box::new(::futures::future::done(res))
|
||||
}
|
||||
}
|
||||
91
dapps/src/tests/home.rs
Normal file
91
dapps/src/tests/home.rs
Normal file
@@ -0,0 +1,91 @@
|
||||
// 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 tests::helpers::{serve_ui, request, assert_security_headers};
|
||||
|
||||
#[test]
|
||||
fn should_serve_home_js() {
|
||||
// given
|
||||
let server = serve_ui();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /inject.js 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/javascript");
|
||||
assert_eq!(response.body.contains("function(){"), true, "Expected function in: {}", response.body);
|
||||
assert_security_headers(&response.headers);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_serve_home() {
|
||||
// given
|
||||
let server = serve_ui();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET / 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", "text/html");
|
||||
assert_security_headers(&response.headers);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn should_inject_js() {
|
||||
// given
|
||||
let server = serve_ui();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET / 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", "text/html");
|
||||
assert_eq!(
|
||||
response.body.contains(r#"/inject.js"></script>"#),
|
||||
true,
|
||||
"Expected inject script tag in: {}",
|
||||
response.body
|
||||
);
|
||||
assert_security_headers(&response.headers);
|
||||
}
|
||||
27
dapps/src/tests/mod.rs
Normal file
27
dapps/src/tests/mod.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
// 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/>.
|
||||
|
||||
//! Dapps server test suite
|
||||
|
||||
mod helpers;
|
||||
|
||||
mod api;
|
||||
mod fetch;
|
||||
mod home;
|
||||
mod redirection;
|
||||
mod rpc;
|
||||
mod validation;
|
||||
|
||||
207
dapps/src/tests/redirection.rs
Normal file
207
dapps/src/tests/redirection.rs
Normal file
@@ -0,0 +1,207 @@
|
||||
// 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 tests::helpers::{serve, request, assert_security_headers, assert_security_headers_for_embed};
|
||||
|
||||
#[test]
|
||||
fn should_redirect_to_home() {
|
||||
// given
|
||||
let server = serve();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET / 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 302 Found");
|
||||
assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_redirect_to_home_with_domain() {
|
||||
// given
|
||||
let server = serve();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET / HTTP/1.1\r\n\
|
||||
Host: home.web3.site\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 302 Found");
|
||||
assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_redirect_to_home_when_trailing_slash_is_missing() {
|
||||
// given
|
||||
let server = serve();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /app 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 302 Found");
|
||||
assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_display_404_on_invalid_dapp() {
|
||||
// given
|
||||
let server = serve();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /invaliddapp/ 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 404 Not Found");
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_display_404_on_invalid_dapp_with_domain() {
|
||||
// given
|
||||
let server = serve();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET / HTTP/1.1\r\n\
|
||||
Host: invaliddapp.web3.site\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 404 Not Found");
|
||||
assert_security_headers_for_embed(&response.headers);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_serve_rpc() {
|
||||
// given
|
||||
let server = serve();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
POST / HTTP/1.1\r\n\
|
||||
Host: 127.0.0.1:8080\r\n\
|
||||
Connection: close\r\n\
|
||||
Content-Type: application/json\r\n
|
||||
\r\n\
|
||||
{}
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
assert_eq!(response.body, format!("4C\n{}\n\n0\n\n", r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":null}"#));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_serve_rpc_at_slash_rpc() {
|
||||
// given
|
||||
let server = serve();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
POST /rpc HTTP/1.1\r\n\
|
||||
Host: 127.0.0.1:8080\r\n\
|
||||
Connection: close\r\n\
|
||||
Content-Type: application/json\r\n
|
||||
\r\n\
|
||||
{}
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
assert_eq!(response.body, format!("4C\n{}\n\n0\n\n", r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":null}"#));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn should_serve_proxy_pac() {
|
||||
// given
|
||||
let server = serve();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /proxy/proxy.pac 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");
|
||||
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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_serve_utils() {
|
||||
// given
|
||||
let server = serve();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /parity-utils/inject.js 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/javascript");
|
||||
assert_eq!(response.body.contains("function(){"), true, "Expected function in: {}", response.body);
|
||||
assert_security_headers(&response.headers);
|
||||
}
|
||||
49
dapps/src/tests/rpc.rs
Normal file
49
dapps/src/tests/rpc.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
// 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 jsonrpc_core::{IoHandler, Value};
|
||||
|
||||
use tests::helpers::{serve_with_rpc, request};
|
||||
|
||||
#[test]
|
||||
fn should_serve_rpc() {
|
||||
// given
|
||||
let mut io = IoHandler::default();
|
||||
io.add_method("rpc_test", |_| {
|
||||
Ok(Value::String("Hello World!".into()))
|
||||
});
|
||||
let server = serve_with_rpc(io);
|
||||
|
||||
// when
|
||||
let req = r#"{"jsonrpc":"2.0","id":1,"method":"rpc_test","params":[]}"#;
|
||||
let response = request(server, &format!(
|
||||
"\
|
||||
POST /rpc/ HTTP/1.1\r\n\
|
||||
Host: 127.0.0.1:8080\r\n\
|
||||
Connection: close\r\n\
|
||||
Content-Type: application/json\r\n\
|
||||
Content-Length: {}\r\n\
|
||||
\r\n\
|
||||
{}\r\n\
|
||||
",
|
||||
req.as_bytes().len(),
|
||||
req,
|
||||
));
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
assert_eq!(response.body, "31\n{\"jsonrpc\":\"2.0\",\"result\":\"Hello World!\",\"id\":1}\n\n0\n\n".to_owned());
|
||||
}
|
||||
99
dapps/src/tests/validation.rs
Normal file
99
dapps/src/tests/validation.rs
Normal file
@@ -0,0 +1,99 @@
|
||||
// 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 tests::helpers::{serve_hosts, request};
|
||||
|
||||
#[test]
|
||||
fn should_reject_invalid_host() {
|
||||
// given
|
||||
let server = serve_hosts(Some(vec!["localhost:8080".into()]));
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET / 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 403 Forbidden");
|
||||
assert!(response.body.contains("Provided Host header is not whitelisted."), response.body);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_allow_valid_host() {
|
||||
// given
|
||||
let server = serve_hosts(Some(vec!["localhost:8080".into()]));
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /ui/ HTTP/1.1\r\n\
|
||||
Host: localhost:8080\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
{}
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_serve_dapps_domains() {
|
||||
// given
|
||||
let server = serve_hosts(Some(vec!["localhost:8080".into()]));
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET / HTTP/1.1\r\n\
|
||||
Host: ui.web3.site\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
{}
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
}
|
||||
|
||||
#[test]
|
||||
// NOTE [todr] This is required for error pages to be styled properly.
|
||||
fn should_allow_parity_utils_even_on_invalid_domain() {
|
||||
// given
|
||||
let server = serve_hosts(Some(vec!["localhost:8080".into()]));
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /parity-utils/styles.css 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");
|
||||
}
|
||||
166
dapps/src/web.rs
Normal file
166
dapps/src/web.rs
Normal file
@@ -0,0 +1,166 @@
|
||||
// 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/>.
|
||||
|
||||
//! Serving web-based content (proxying)
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use base32;
|
||||
use fetch::{self, Fetch};
|
||||
use hyper::{mime, StatusCode};
|
||||
|
||||
use apps;
|
||||
use endpoint::{Endpoint, EndpointPath, Request, Response};
|
||||
use futures::future;
|
||||
use handlers::{
|
||||
ContentFetcherHandler, ContentHandler, ContentValidator, ValidatorResponse,
|
||||
StreamingHandler,
|
||||
};
|
||||
use {Embeddable, WebProxyTokens};
|
||||
|
||||
pub struct Web<F> {
|
||||
embeddable_on: Embeddable,
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
fetch: F,
|
||||
}
|
||||
|
||||
impl<F: Fetch> Web<F> {
|
||||
pub fn boxed(
|
||||
embeddable_on: Embeddable,
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
fetch: F,
|
||||
) -> Box<Endpoint> {
|
||||
Box::new(Web {
|
||||
embeddable_on,
|
||||
web_proxy_tokens,
|
||||
fetch,
|
||||
})
|
||||
}
|
||||
|
||||
fn extract_target_url(&self, path: &EndpointPath) -> Result<String, ContentHandler> {
|
||||
let token_and_url = path.app_params.get(0)
|
||||
.map(|encoded| encoded.replace('.', ""))
|
||||
.and_then(|encoded| base32::decode(base32::Alphabet::Crockford, &encoded.to_uppercase()))
|
||||
.and_then(|data| String::from_utf8(data).ok())
|
||||
.ok_or_else(|| ContentHandler::error(
|
||||
StatusCode::BadRequest,
|
||||
"Invalid parameter",
|
||||
"Couldn't parse given parameter:",
|
||||
path.app_params.get(0).map(String::as_str),
|
||||
self.embeddable_on.clone()
|
||||
))?;
|
||||
|
||||
let mut token_it = token_and_url.split('+');
|
||||
let token = token_it.next();
|
||||
let target_url = token_it.next();
|
||||
|
||||
// Check if token supplied in URL is correct.
|
||||
let domain = match token.and_then(|token| self.web_proxy_tokens.domain(token)) {
|
||||
Some(domain) => domain,
|
||||
_ => {
|
||||
return Err(ContentHandler::error(
|
||||
StatusCode::BadRequest, "Invalid Access Token", "Invalid or old web proxy access token supplied.", Some("Try refreshing the page."), self.embeddable_on.clone()
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
// Validate protocol
|
||||
let mut target_url = match target_url {
|
||||
Some(url) if url.starts_with("http://") || url.starts_with("https://") => url.to_owned(),
|
||||
_ => {
|
||||
return Err(ContentHandler::error(
|
||||
StatusCode::BadRequest, "Invalid Protocol", "Invalid protocol used.", None, self.embeddable_on.clone()
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
if !target_url.starts_with(&*domain) {
|
||||
return Err(ContentHandler::error(
|
||||
StatusCode::BadRequest, "Invalid Domain", "Dapp attempted to access invalid domain.", Some(&target_url), self.embeddable_on.clone(),
|
||||
));
|
||||
}
|
||||
|
||||
if !target_url.ends_with("/") {
|
||||
target_url = format!("{}/", target_url);
|
||||
}
|
||||
|
||||
// Skip the token
|
||||
let query = path.query.as_ref().map_or_else(String::new, |query| format!("?{}", query));
|
||||
let path = path.app_params[1..].join("/");
|
||||
|
||||
Ok(format!("{}{}{}", target_url, path, query))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Fetch> Endpoint for Web<F> {
|
||||
fn respond(&self, path: EndpointPath, req: Request) -> Response {
|
||||
// First extract the URL (reject invalid URLs)
|
||||
let target_url = match self.extract_target_url(&path) {
|
||||
Ok(url) => url,
|
||||
Err(response) => {
|
||||
return Box::new(future::ok(response.into()));
|
||||
}
|
||||
};
|
||||
|
||||
let token = path.app_params.get(0)
|
||||
.expect("`target_url` is valid; app_params is not empty;qed")
|
||||
.to_owned();
|
||||
|
||||
Box::new(ContentFetcherHandler::new(
|
||||
req.method(),
|
||||
&target_url,
|
||||
path,
|
||||
WebInstaller {
|
||||
embeddable_on: self.embeddable_on.clone(),
|
||||
token,
|
||||
},
|
||||
self.embeddable_on.clone(),
|
||||
self.fetch.clone(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
struct WebInstaller {
|
||||
embeddable_on: Embeddable,
|
||||
token: String,
|
||||
}
|
||||
|
||||
impl ContentValidator for WebInstaller {
|
||||
type Error = String;
|
||||
|
||||
fn validate_and_install(self, response: fetch::Response) -> Result<ValidatorResponse, String> {
|
||||
let status = response.status();
|
||||
let is_html = response.is_html();
|
||||
let mime = response.content_type().unwrap_or(mime::TEXT_HTML);
|
||||
let mut handler = StreamingHandler::new(
|
||||
response,
|
||||
status,
|
||||
mime,
|
||||
self.embeddable_on,
|
||||
);
|
||||
if is_html {
|
||||
handler.set_initial_content(&format!(
|
||||
r#"<script src="/{}/inject.js"></script><script>history.replaceState({{}}, "", "/?{}{}/{}")</script>"#,
|
||||
apps::UTILS_PATH,
|
||||
apps::URL_REFERER,
|
||||
apps::WEB_PATH,
|
||||
&self.token,
|
||||
));
|
||||
}
|
||||
Ok(ValidatorResponse::Streaming(handler))
|
||||
}
|
||||
}
|
||||
|
||||
21
dapps/ui/Cargo.toml
Normal file
21
dapps/ui/Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
[package]
|
||||
description = "Ethcore Parity UI"
|
||||
homepage = "http://parity.io"
|
||||
license = "GPL-3.0"
|
||||
name = "parity-ui"
|
||||
version = "1.9.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[build-dependencies]
|
||||
rustc_version = "0.1"
|
||||
|
||||
[dependencies]
|
||||
parity-ui-dev = { path = "../../js", optional = true }
|
||||
parity-ui-old-dev = { path = "../../js-old", optional = true }
|
||||
# This is managed by the js/scripts/release.sh script on CI - keep it in a single line
|
||||
parity-ui-old-precompiled = { git = "https://github.com/js-dist-paritytech/parity-beta-1-9-v1.git", optional = true }
|
||||
parity-ui-precompiled = { git = "https://github.com/js-dist-paritytech/parity-beta-1-9-shell.git", optional = true }
|
||||
|
||||
[features]
|
||||
no-precompiled-js = ["parity-ui-dev", "parity-ui-old-dev"]
|
||||
use-precompiled-js = ["parity-ui-precompiled", "parity-ui-old-precompiled"]
|
||||
46
dapps/ui/src/lib.rs
Normal file
46
dapps/ui/src/lib.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
// 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/>.
|
||||
|
||||
|
||||
#[cfg(feature = "parity-ui-dev")]
|
||||
mod inner {
|
||||
extern crate parity_ui_dev;
|
||||
|
||||
pub use self::parity_ui_dev::*;
|
||||
}
|
||||
|
||||
#[cfg(feature = "parity-ui-precompiled")]
|
||||
mod inner {
|
||||
extern crate parity_ui_precompiled;
|
||||
|
||||
pub use self::parity_ui_precompiled::*;
|
||||
}
|
||||
|
||||
#[cfg(feature = "parity-ui-old-dev")]
|
||||
pub mod old {
|
||||
extern crate parity_ui_old_dev;
|
||||
|
||||
pub use self::parity_ui_old_dev::*;
|
||||
}
|
||||
|
||||
#[cfg(feature = "parity-ui-old-precompiled")]
|
||||
pub mod old {
|
||||
extern crate parity_ui_old_precompiled;
|
||||
|
||||
pub use self::parity_ui_old_precompiled::*;
|
||||
}
|
||||
|
||||
pub use self::inner::*;
|
||||
16
devtools/Cargo.toml
Normal file
16
devtools/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
description = "Ethcore development/test/build tools"
|
||||
homepage = "http://parity.io"
|
||||
license = "GPL-3.0"
|
||||
name = "ethcore-devtools"
|
||||
version = "1.9.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
rand = "0.3"
|
||||
|
||||
[features]
|
||||
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
test = true
|
||||
@@ -1,18 +1,18 @@
|
||||
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// 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 Ethereum is distributed in the hope that it will be useful,
|
||||
// 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 Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
29
devtools/src/lib.rs
Normal file
29
devtools/src/lib.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
// 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/>.
|
||||
|
||||
//! dev-tools
|
||||
|
||||
|
||||
extern crate rand;
|
||||
|
||||
mod random_path;
|
||||
mod test_socket;
|
||||
mod stop_guard;
|
||||
pub mod http_client;
|
||||
|
||||
pub use random_path::*;
|
||||
pub use test_socket::*;
|
||||
pub use stop_guard::*;
|
||||
148
devtools/src/random_path.rs
Normal file
148
devtools/src/random_path.rs
Normal file
@@ -0,0 +1,148 @@
|
||||
// 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/>.
|
||||
|
||||
//! Random path
|
||||
|
||||
use std::path::*;
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use rand::random;
|
||||
|
||||
pub struct RandomTempPath {
|
||||
path: PathBuf,
|
||||
pub panic_on_drop_failure: bool,
|
||||
}
|
||||
|
||||
pub fn random_filename() -> String {
|
||||
random_str(8)
|
||||
}
|
||||
|
||||
pub fn random_str(len: usize) -> String {
|
||||
(0..len).map(|_| ((random::<f32>() * 26.0) as u8 + 97) as char).collect()
|
||||
}
|
||||
|
||||
impl RandomTempPath {
|
||||
pub fn new() -> RandomTempPath {
|
||||
let mut dir = env::temp_dir();
|
||||
dir.push(random_filename());
|
||||
RandomTempPath {
|
||||
path: dir.clone(),
|
||||
panic_on_drop_failure: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_dir() -> RandomTempPath {
|
||||
let mut dir = env::temp_dir();
|
||||
dir.push(random_filename());
|
||||
fs::create_dir_all(dir.as_path()).unwrap();
|
||||
RandomTempPath {
|
||||
path: dir.clone(),
|
||||
panic_on_drop_failure: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_path(&self) -> &PathBuf {
|
||||
&self.path
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.path.to_str().unwrap()
|
||||
}
|
||||
|
||||
pub fn new_in(&self, name: &str) -> String {
|
||||
let mut path = self.path.clone();
|
||||
path.push(name);
|
||||
path.to_str().unwrap().to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Path> for RandomTempPath {
|
||||
fn as_ref(&self) -> &Path {
|
||||
self.as_path()
|
||||
}
|
||||
}
|
||||
impl Deref for RandomTempPath {
|
||||
type Target = Path;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_path()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RandomTempPath {
|
||||
fn drop(&mut self) {
|
||||
if let Err(_) = fs::remove_dir_all(&self) {
|
||||
if let Err(e) = fs::remove_file(&self) {
|
||||
if self.panic_on_drop_failure {
|
||||
panic!("Failed to remove temp directory. Here's what prevented this from happening: ({})", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GuardedTempResult<T> {
|
||||
pub result: Option<T>,
|
||||
pub _temp: RandomTempPath,
|
||||
}
|
||||
|
||||
impl<T> GuardedTempResult<T> {
|
||||
pub fn reference(&self) -> &T {
|
||||
self.result.as_ref().unwrap()
|
||||
}
|
||||
|
||||
pub fn reference_mut(&mut self) -> &mut T {
|
||||
self.result.as_mut().unwrap()
|
||||
}
|
||||
|
||||
pub fn take(&mut self) -> T {
|
||||
self.result.take().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for GuardedTempResult<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T { self.result.as_ref().unwrap() }
|
||||
}
|
||||
|
||||
impl<T> DerefMut for GuardedTempResult<T> {
|
||||
fn deref_mut(&mut self) -> &mut T { self.result.as_mut().unwrap() }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn creates_dir() {
|
||||
let temp = RandomTempPath::create_dir();
|
||||
assert!(fs::metadata(temp.as_path()).unwrap().is_dir());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn destroys_dir() {
|
||||
let path_buf = {
|
||||
let temp = RandomTempPath::create_dir();
|
||||
assert!(fs::metadata(temp.as_path()).unwrap().is_dir());
|
||||
let path_buf = temp.as_path().to_path_buf();
|
||||
path_buf
|
||||
};
|
||||
|
||||
assert!(fs::metadata(&path_buf).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn provides_random() {
|
||||
let temp = RandomTempPath::create_dir();
|
||||
assert!(temp.as_path().to_str().is_some());
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// 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 Ethereum is distributed in the hope that it will be useful,
|
||||
// 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 Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Stop guard mod
|
||||
|
||||
@@ -31,6 +31,11 @@ impl StopGuard {
|
||||
flag: Arc::new(AtomicBool::new(false))
|
||||
}
|
||||
}
|
||||
|
||||
/// Share stop guard between the threads
|
||||
pub fn share(&self) -> Arc<AtomicBool> {
|
||||
self.flag.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for StopGuard {
|
||||
95
devtools/src/test_socket.rs
Normal file
95
devtools/src/test_socket.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
// 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::io::*;
|
||||
use std::cmp;
|
||||
|
||||
pub struct TestSocket {
|
||||
pub read_buffer: Vec<u8>,
|
||||
pub write_buffer: Vec<u8>,
|
||||
pub cursor: usize,
|
||||
pub buf_size: usize,
|
||||
}
|
||||
|
||||
impl Default for TestSocket {
|
||||
fn default() -> Self {
|
||||
TestSocket::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl TestSocket {
|
||||
pub fn new() -> Self {
|
||||
TestSocket {
|
||||
read_buffer: vec![],
|
||||
write_buffer: vec![],
|
||||
cursor: 0,
|
||||
buf_size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_buf(buf_size: usize) -> TestSocket {
|
||||
TestSocket {
|
||||
read_buffer: vec![],
|
||||
write_buffer: vec![],
|
||||
cursor: 0,
|
||||
buf_size: buf_size,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_ready(data: Vec<u8>) -> TestSocket {
|
||||
TestSocket {
|
||||
read_buffer: data,
|
||||
write_buffer: vec![],
|
||||
cursor: 0,
|
||||
buf_size: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for TestSocket {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
let end_position = cmp::min(self.read_buffer.len(), self.cursor+buf.len());
|
||||
if self.cursor > end_position { return Ok(0) }
|
||||
let len = cmp::max(end_position - self.cursor, 0);
|
||||
match len {
|
||||
0 => Ok(0),
|
||||
_ => {
|
||||
for i in self.cursor..end_position {
|
||||
buf[i-self.cursor] = self.read_buffer[i];
|
||||
}
|
||||
self.cursor = end_position;
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for TestSocket {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
if self.buf_size == 0 || buf.len() < self.buf_size {
|
||||
self.write_buffer.extend(buf.iter().cloned());
|
||||
Ok(buf.len())
|
||||
}
|
||||
else {
|
||||
self.write_buffer.extend(buf.iter().take(self.buf_size).cloned());
|
||||
Ok(self.buf_size)
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<()> {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
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 .```
|
||||
36
docker/centos/Dockerfile
Normal file
36
docker/centos/Dockerfile
Normal file
@@ -0,0 +1,36 @@
|
||||
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
|
||||
ADD . /build/parity
|
||||
RUN cd parity&&\
|
||||
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"]
|
||||
83
docker/hub/Dockerfile
Normal file
83
docker/hub/Dockerfile
Normal file
@@ -0,0 +1,83 @@
|
||||
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:" $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"]
|
||||
45
docker/ubuntu-aarch64/Dockerfile
Normal file
45
docker/ubuntu-aarch64/Dockerfile
Normal file
@@ -0,0 +1,45 @@
|
||||
FROM ubuntu:14.04
|
||||
WORKDIR /build
|
||||
|
||||
# install tools and dependencies
|
||||
RUN apt-get -y update && \
|
||||
apt-get install -y --force-yes --no-install-recommends \
|
||||
curl git make g++ gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \
|
||||
libc6-arm64-cross libc6-dev-arm64-cross wget file ca-certificates \
|
||||
binutils-aarch64-linux-gnu \
|
||||
&& \
|
||||
apt-get clean
|
||||
|
||||
# install rustup
|
||||
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||
|
||||
# rustup directory
|
||||
ENV PATH /root/.cargo/bin:$PATH
|
||||
|
||||
ENV RUST_TARGETS="aarch64-unknown-linux-gnu"
|
||||
|
||||
# multirust add arm--linux-gnuabhf toolchain
|
||||
RUN rustup target add aarch64-unknown-linux-gnu
|
||||
|
||||
# show backtraces
|
||||
ENV RUST_BACKTRACE 1
|
||||
|
||||
# show tools
|
||||
RUN rustc -vV && cargo -V
|
||||
|
||||
# build parity
|
||||
ADD . /build/parity
|
||||
RUN cd parity && \
|
||||
mkdir -p .cargo && \
|
||||
echo '[target.aarch64-unknown-linux-gnu]\n\
|
||||
linker = "aarch64-linux-gnu-gcc"\n'\
|
||||
>>.cargo/config && \
|
||||
cat .cargo/config && \
|
||||
cargo build --target aarch64-unknown-linux-gnu --release --verbose && \
|
||||
ls /build/parity/target/aarch64-unknown-linux-gnu/release/parity && \
|
||||
/usr/bin/aarch64-linux-gnu-strip /build/parity/target/aarch64-unknown-linux-gnu/release/parity
|
||||
|
||||
RUN file /build/parity/target/aarch64-unknown-linux-gnu/release/parity
|
||||
|
||||
EXPOSE 8080 8545 8180
|
||||
ENTRYPOINT ["/build/parity/target/aarch64-unknown-linux-gnu/release/parity"]
|
||||
@@ -6,7 +6,7 @@ RUN apt-get -y update && \
|
||||
apt-get install -y --force-yes --no-install-recommends \
|
||||
curl git make g++ gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf \
|
||||
libc6-dev-armhf-cross wget file ca-certificates \
|
||||
binutils-arm-linux-gnueabihf cmake3 libudev-dev \
|
||||
binutils-arm-linux-gnueabihf \
|
||||
&& \
|
||||
apt-get clean
|
||||
|
||||
58
docker/ubuntu-jit/Dockerfile
Normal file
58
docker/ubuntu-jit/Dockerfile
Normal file
@@ -0,0 +1,58 @@
|
||||
FROM ubuntu:14.04
|
||||
WORKDIR /build
|
||||
|
||||
# install tools and dependencies
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
# make
|
||||
build-essential \
|
||||
# add-apt-repository
|
||||
software-properties-common \
|
||||
curl \
|
||||
wget \
|
||||
git \
|
||||
g++ \
|
||||
binutils \
|
||||
file \
|
||||
# evmjit dependencies
|
||||
zlib1g-dev \
|
||||
libedit-dev
|
||||
|
||||
# cmake and llvm 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
|
||||
|
||||
# show tools
|
||||
RUN rustc -vV && \
|
||||
cargo -V && \
|
||||
gcc -v &&\
|
||||
g++ -v
|
||||
|
||||
# build parity
|
||||
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
|
||||
|
||||
RUN file /build/parity/target/release/parity
|
||||
|
||||
EXPOSE 8080 8545 8180
|
||||
ENTRYPOINT ["/build/parity/target/release/parity"]
|
||||
42
docker/ubuntu/Dockerfile
Normal file
42
docker/ubuntu/Dockerfile
Normal file
@@ -0,0 +1,42 @@
|
||||
FROM ubuntu:14.04
|
||||
WORKDIR /build
|
||||
|
||||
# install tools and dependencies
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
g++ \
|
||||
build-essential \
|
||||
curl \
|
||||
git \
|
||||
file \
|
||||
binutils \
|
||||
libssl-dev \
|
||||
pkg-config \
|
||||
libudev-dev
|
||||
|
||||
# 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
|
||||
ADD . /build/parity
|
||||
RUN cd parity && \
|
||||
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,4 +1,3 @@
|
||||
Note: Parity 0.9 reached End-of-Life on 2016-05-02 (EOL).
|
||||
|
||||
## Parity [beta-0.9.1](https://github.com/paritytech/parity/releases/tag/beta-0.9.1) (2016-02-16)
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
Note: Parity 1.0 reached End-of-Life on 2016-06-24 (EOL).
|
||||
|
||||
## Parity [v1.0.2](https://github.com/paritytech/parity/releases/tag/v1.0.2) (2016-04-11)
|
||||
|
||||
Parity 1.0.2 release improves Json RPC compatibility and fixes a number of stability issues.
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
Note: Parity 1.1 reached End-of-Life on 2016-08-12 (EOL).
|
||||
|
||||
## Parity [v1.1.0](https://github.com/paritytech/parity/releases/tag/v1.1.0) (2016-05-02)
|
||||
|
||||
Parity 1.1.0 introduces:
|
||||
|
||||
@@ -1,469 +0,0 @@
|
||||
Note: Parity 1.10 reached End-of-Life on 2018-07-18 (EOL).
|
||||
|
||||
## Parity [v1.10.9](https://github.com/paritytech/parity/releases/tag/v1.10.9) (2018-07-07)
|
||||
|
||||
Parity 1.10.9 is a bug-fix release to improve performance and stability.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Stable: 1.10.9 backports ([#9016](https://github.com/paritytech/parity/pull/9016))
|
||||
- Parity-version: bump stable to 1.10.9
|
||||
- Scripts: remove md5 checksums ([#8884](https://github.com/paritytech/parity/pull/8884))
|
||||
- Add support for --chain tobalaba ([#8870](https://github.com/paritytech/parity/pull/8870))
|
||||
- Add support for --chain tobalaba
|
||||
- Only return error log for rustls ([#9025](https://github.com/paritytech/parity/pull/9025))
|
||||
- Fixes for misbehavior reporting in AuthorityRound ([#8998](https://github.com/paritytech/parity/pull/8998))
|
||||
- Aura: only report after checking for repeated skipped primaries
|
||||
- Aura: refactor duplicate code for getting epoch validator set
|
||||
- Aura: verify_external: report on validator set contract instance
|
||||
- Aura: use correct validator set epoch number when reporting
|
||||
- Aura: use epoch set when verifying blocks
|
||||
- Aura: report skipped primaries when generating seal
|
||||
- Aura: handle immediate transitions
|
||||
- Aura: don't report skipped steps from genesis to first block
|
||||
- Aura: fix reporting test
|
||||
- Aura: refactor duplicate code to handle immediate_transitions
|
||||
- Aura: let reporting fail on verify_block_basic
|
||||
- Aura: add comment about possible failure of reporting
|
||||
|
||||
## Parity [v1.10.8](https://github.com/paritytech/parity/releases/tag/v1.10.8) (2018-06-29)
|
||||
|
||||
Parity 1.10.8 is a bug-fix release to improve performance and stability.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Backports ([#8986](https://github.com/paritytech/parity/pull/8986))
|
||||
- Snap: downgrade rust to revision 1.26.2, ref snapcraft/+bug/1778530 ([#8984](https://github.com/paritytech/parity/pull/8984))
|
||||
- Snap: downgrade rust to revision 1.26.2, ref snapcraft/+bug/1778530
|
||||
- Snap: use plugin rust
|
||||
- Fix deadlock in blockchain. ([#8977](https://github.com/paritytech/parity/pull/8977))
|
||||
- Remove js-glue from workspace
|
||||
- Bump stable to 1.10.8 ([#8951](https://github.com/paritytech/parity/pull/8951))
|
||||
- Parity-version: bump stable to 1.10.8
|
||||
- Update ropsten.json ([#8926](https://github.com/paritytech/parity/pull/8926))
|
||||
- Scripts: minor improvements ([#8930](https://github.com/paritytech/parity/pull/8930))
|
||||
- CI: enable 'latest' docker tag on master pipeline
|
||||
- CI: mark both beta and stable as stable snap.
|
||||
- CI: sign all windows binaries
|
||||
- Scripts: remove whisper target not available in stable
|
||||
- Scripts: fix gitlab strip binaries
|
||||
- Scripts: fix docker build tag on latest using master ([#8952](https://github.com/paritytech/parity/pull/8952))
|
||||
- Rpc: cap gas limit of local calls ([#8943](https://github.com/paritytech/parity/pull/8943))
|
||||
|
||||
## Parity [v1.10.7](https://github.com/paritytech/parity/releases/tag/v1.10.7) (2018-06-20)
|
||||
|
||||
Parity 1.10.7 is a bug-fix release to improve performance and stability.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Backports ([#8919](https://github.com/paritytech/parity/pull/8919))
|
||||
- Fixed AuthorityRound deadlock on shutdown, closes [#8088](https://github.com/paritytech/parity/issues/8088) ([#8803](https://github.com/paritytech/parity/pull/8803))
|
||||
- CI: Fix docker tags ([#8822](https://github.com/paritytech/parity/pull/8822))
|
||||
- Scripts: enable docker builds for beta and stable
|
||||
- Scripts: docker latest should be beta not master
|
||||
- Scripts: docker latest is master
|
||||
- Fix concurrent access to signer queue ([#8854](https://github.com/paritytech/parity/pull/8854))
|
||||
- Fix concurrent access to signer queue
|
||||
- Put request back to the queue if confirmation failed
|
||||
- Typo: fix docs and rename functions to be more specific
|
||||
- Change trace info "Transaction" -> "Request"
|
||||
- Add new ovh bootnodes and fix port for foundation bootnode 3.2 ([#8886](https://github.com/paritytech/parity/pull/8886))
|
||||
- Add new ovh bootnodes and fix port for foundation bootnode 3.2
|
||||
- Remove old bootnodes.
|
||||
- Remove duplicate 1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082
|
||||
- Block 0 is valid in queries ([#8891](https://github.com/paritytech/parity/pull/8891))
|
||||
- Update jsonrpc libs, fixed ipc leak, closes [#8774](https://github.com/paritytech/parity/issues/8774) ([#8876](https://github.com/paritytech/parity/pull/8876))
|
||||
- Add ETC Cooperative-run load balanced parity node ([#8892](https://github.com/paritytech/parity/pull/8892))
|
||||
- Minor fix in chain supplier and light provider ([#8906](https://github.com/paritytech/parity/pull/8906))
|
||||
- Fix chain supplier increment
|
||||
- Fix light provider block_headers
|
||||
- Parity-version: stable release 1.10.7 ([#8855](https://github.com/paritytech/parity/pull/8855))
|
||||
- Cherry-pick network-specific release flag ([#8821](https://github.com/paritytech/parity/pull/8821))
|
||||
- Parity-version: bump stable to 1.10.7
|
||||
|
||||
## Parity [v1.10.6](https://github.com/paritytech/parity/releases/tag/v1.10.6) (2018-06-05)
|
||||
|
||||
Parity 1.10.6 is a security-relevant release. Please upgrade your nodes as soon as possible.
|
||||
|
||||
If you can not upgrade to 1.10+ yet, please use the following branches and build your own binaries from source:
|
||||
|
||||
- git checkout [old-stable-1.9](https://github.com/paritytech/parity/tree/old-stable-1.9) # `v1.9.8` (EOL)
|
||||
- git checkout [old-stable-1.8](https://github.com/paritytech/parity/tree/old-stable-1.8) # `v1.8.12` (EOL)
|
||||
- git checkout [old-stable-1.7](https://github.com/paritytech/parity/tree/old-stable-1.7) # `v1.7.14` (EOL)
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Parity-version: bump stable to 1.10.6 ([#8805](https://github.com/paritytech/parity/pull/8805))
|
||||
- Parity-version: bump stable to 1.10.6
|
||||
- Disallow unsigned transactions in case EIP-86 is disabled ([#8802](https://github.com/paritytech/parity/pull/8802))
|
||||
- Update shell32-sys to fix windows build ([#8793](https://github.com/paritytech/parity/pull/8793))
|
||||
- Backports ([#8782](https://github.com/paritytech/parity/pull/8782))
|
||||
- Fix light sync with initial validator-set contract ([#8528](https://github.com/paritytech/parity/pull/8528))
|
||||
- Fix #8468
|
||||
- Use U256::max_value() instead
|
||||
- Fix again
|
||||
- Also change initial transaction gas
|
||||
- Don't open Browser post-install on Mac ([#8641](https://github.com/paritytech/parity/pull/8641))
|
||||
- Prefix uint fmt with `0x` with alternate flag
|
||||
- Set the request index to that of the current request ([#8683](https://github.com/paritytech/parity/pull/8683))
|
||||
- Set the request index to that of the current request
|
||||
- Node table sorting according to last contact data ([#8541](https://github.com/paritytech/parity/pull/8541))
|
||||
- Network-devp2p: sort nodes in node table using last contact data
|
||||
- Network-devp2p: rename node contact types in node table json output
|
||||
- Network-devp2p: fix node table tests
|
||||
- Network-devp2p: note node failure when failed to establish connection
|
||||
- Network-devp2p: handle UselessPeer error
|
||||
- Network-devp2p: note failure when marking node as useless
|
||||
- Network-devp2p: handle UselessPeer disconnect ([#8686](https://github.com/paritytech/parity/pull/8686))
|
||||
- Parity: bump stable version to 1.10.5 ([#8749](https://github.com/paritytech/parity/pull/8749))
|
||||
- Parity: bump stable version to 1.10.5
|
||||
- Fix failing doc tests running on non-code
|
||||
|
||||
## Parity [v1.10.4](https://github.com/paritytech/parity/releases/tag/v1.10.4) (2018-05-15)
|
||||
|
||||
Parity 1.10.4 is a bug-fix release to improve performance and stability.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Backports ([#8623](https://github.com/paritytech/parity/pull/8623))
|
||||
- Fix account list double 0x display ([#8596](https://github.com/paritytech/parity/pull/8596))
|
||||
- Remove unused self import
|
||||
- Fix account list double 0x display
|
||||
- Trace precompiled contracts when the transfer value is not zero ([#8486](https://github.com/paritytech/parity/pull/8486))
|
||||
- Trace precompiled contracts when the transfer value is not zero
|
||||
- Add tests for precompiled CALL tracing
|
||||
- Use byzantium test machine for the new test
|
||||
- Add notes in comments on why we don't trace all precompileds
|
||||
- Use is_transferred instead of transferred
|
||||
- Gitlab test script fixes ([#8573](https://github.com/paritytech/parity/pull/8573))
|
||||
- Exclude /docs from modified files.
|
||||
- Ensure all references in the working tree are available
|
||||
- Remove duplicated line from test script
|
||||
- Bump stable to 1.10.4 ([#8626](https://github.com/paritytech/parity/pull/8626))
|
||||
- Allow stable snaps to be stable. ([#8582](https://github.com/paritytech/parity/pull/8582))
|
||||
|
||||
## Parity [v1.10.3](https://github.com/paritytech/parity/releases/tag/v1.10.3) (2018-05-08)
|
||||
|
||||
Parity 1.10.3 marks the first stable release on the 1.10 track. Among others, it improves performance and stability.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Backports ([#8557](https://github.com/paritytech/parity/pull/8557))
|
||||
- Update wasmi and pwasm-utils ([#8493](https://github.com/paritytech/parity/pull/8493))
|
||||
- Update wasmi to 0.2
|
||||
- Update pwasm-utils to 0.1.5
|
||||
- Fetching logs by hash in blockchain database ([#8463](https://github.com/paritytech/parity/pull/8463))
|
||||
- Fetch logs by hash in blockchain database
|
||||
- Fix tests
|
||||
- Add unit test for branch block logs fetching
|
||||
- Add docs that blocks must already be sorted
|
||||
- Handle branch block cases properly
|
||||
- typo: empty -> is_empty
|
||||
- Remove return_empty_if_none by using a closure
|
||||
- Use BTreeSet to avoid sorting again
|
||||
- Move is_canon to BlockChain
|
||||
- typo: pass value by reference
|
||||
- Use loop and wrap inside blocks to simplify the code
|
||||
- typo: missed a comment
|
||||
- Pass on storage keys tracing to handle the case when it is not modified ([#8491](https://github.com/paritytech/parity/pull/8491))
|
||||
- Pass on storage keys even if it is not modified
|
||||
- typo: account and storage query `to_pod_diff` builds both `touched_addresses` merge and storage keys merge.
|
||||
- Fix tests
|
||||
- Use state query directly because of suicided accounts
|
||||
- Fix a RefCell borrow issue
|
||||
- Add tests for unmodified storage trace
|
||||
- Address grumbles
|
||||
- typo: remove unwanted empty line
|
||||
- ensure_cached compiles with the original signature
|
||||
- Enable WebAssembly and Byzantium for Ellaism ([#8520](https://github.com/paritytech/parity/pull/8520))
|
||||
- Enable WebAssembly and Byzantium for Ellaism
|
||||
- Fix indentation
|
||||
- Remove empty lines
|
||||
- Fix compilation.
|
||||
- Stabilize 1.10.3 ([#8474](https://github.com/paritytech/parity/pull/8474))
|
||||
- Stabelize 1.10
|
||||
- Bump stable to 1.10.3
|
||||
- Update Gitlab scripts
|
||||
- Fix snap builds ([#8483](https://github.com/paritytech/parity/pull/8483))
|
||||
- Fix docker build ([#8462](https://github.com/paritytech/parity/pull/8462))
|
||||
- Use `master` as Docker's `latest` (`beta-release` is not used anymore)
|
||||
|
||||
## Parity [v1.10.2](https://github.com/paritytech/parity/releases/tag/v1.10.2) (2018-04-24)
|
||||
|
||||
Parity 1.10.2 is a bug-fix release to improve performance and stability.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Update Parity beta to 1.10.2 + Backports ([#8455](https://github.com/paritytech/parity/pull/8455))
|
||||
- Update Parity beta to 1.10.2
|
||||
- Allow 32-bit pipelines to fail ([#8454](https://github.com/paritytech/parity/pull/8454))
|
||||
- Disable 32-bit targets for Gitlab
|
||||
- Rename Linux pipelines
|
||||
- Update wasmi ([#8452](https://github.com/paritytech/parity/pull/8452))
|
||||
- Fix Cargo.lock
|
||||
- Backports ([#8450](https://github.com/paritytech/parity/pull/8450))
|
||||
- Use forked app_dirs crate for reverted Windows dir behavior ([#8438](https://github.com/paritytech/parity/pull/8438))
|
||||
- Remove unused app_dirs dependency in CLI
|
||||
- Use forked app_dirs crate for reverted Windows dir behavior
|
||||
- Remove Tendermint extra_info due to seal inconsistencies ([#8367](https://github.com/paritytech/parity/pull/8367))
|
||||
- Handle queue import errors a bit more gracefully ([#8385](https://github.com/paritytech/parity/pull/8385))
|
||||
- Improve VM executor stack size estimation rules ([#8439](https://github.com/paritytech/parity/pull/8439))
|
||||
- Improve VM executor stack size estimation rules
|
||||
- Typo: docs add "(Debug build)" comment
|
||||
- Fix an off by one typo and set minimal stack size
|
||||
- Use saturating_sub to avoid potential overflow
|
||||
|
||||
## Parity [v1.10.1](https://github.com/paritytech/parity/releases/tag/v1.10.1) (2018-04-17)
|
||||
|
||||
Parity 1.10.1 is a bug-fix release to improve performance and stability. Among other changes, you can now use `--warp-barrier [BLOCK]` to specify a minimum block number to `--warp` to. This is useful in cases where clients restore to outdated snapshots far behind the latest chain head.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Bump beta to 1.10.1 ([#8350](https://github.com/paritytech/parity/pull/8350))
|
||||
- Bump beta to 1.10.1
|
||||
- Unflag critical release
|
||||
- Backports ([#8346](https://github.com/paritytech/parity/pull/8346))
|
||||
- Warp-only sync with warp-barrier [blocknumber] flag. ([#8228](https://github.com/paritytech/parity/pull/8228))
|
||||
- Warp-only sync with warp-after [blocknumber] flag.
|
||||
- Fix tests.
|
||||
- Fix configuration tests.
|
||||
- Rename to warp barrier.
|
||||
- Allow unsafe js eval on Parity Wallet. ([#8204](https://github.com/paritytech/parity/pull/8204))
|
||||
- Update musicoin spec in line with gmc v2.6.2 ([#8242](https://github.com/paritytech/parity/pull/8242))
|
||||
- Supress TemporaryInvalid verification failures. ([#8256](https://github.com/paritytech/parity/pull/8256))
|
||||
- Include suicided accounts in state diff ([#8297](https://github.com/paritytech/parity/pull/8297))
|
||||
- Include suicided accounts in state diff
|
||||
- Shorten form match -> if let
|
||||
- Test suicide trace diff in State
|
||||
- Replace_home for password_files, reserved_peers and log_file ([#8324](https://github.com/paritytech/parity/pull/8324))
|
||||
- Replace_home for password_files, reserved_peers and log_file
|
||||
- Typo: arg_log_file is Option
|
||||
- Enable UI by default, but only display info page.
|
||||
- Fix test.
|
||||
- Fix naming and remove old todo.
|
||||
- Change "wallet" with "browser UI"
|
||||
- Change name Wallet -> UI ([#8164](https://github.com/paritytech/parity/pull/8164)) ([#8205](https://github.com/paritytech/parity/pull/8205))
|
||||
- Change name Wallet -> UI
|
||||
- Make warning bold
|
||||
- Backport [#8099](https://github.com/paritytech/parity/pull/8099) ([#8132](https://github.com/paritytech/parity/pull/8132))
|
||||
- WASM libs ([#8220](https://github.com/paritytech/parity/pull/8220))
|
||||
- Bump wasm libs ([#8171](https://github.com/paritytech/parity/pull/8171))
|
||||
- Bump wasmi version ([#8209](https://github.com/paritytech/parity/pull/8209))
|
||||
- Update hyper to 0.11.24 ([#8203](https://github.com/paritytech/parity/pull/8203))
|
||||
- Updated jsonrpc to include latest backports (beta) ([#8181](https://github.com/paritytech/parity/pull/8181))
|
||||
- Updated jsonrpc to include latest backports
|
||||
- Update dependencies.
|
||||
|
||||
## Parity [v1.10.0](https://github.com/paritytech/parity/releases/tag/v1.10.0) (2018-03-22)
|
||||
|
||||
This is the Parity 1.10.0-beta release! Cool!
|
||||
|
||||
### Disabling the Parity Wallet
|
||||
|
||||
The **Parity Wallet (a.k.a. "UI") is now disabled by default**. We are preparing to split the wallet from the core client.
|
||||
|
||||
To reactivate the parity wallet, you have to run Parity either with `parity --force-ui` (not recommended) or `parity ui` (deprecated) from the command line. Or, if you feel super fancy and want to test our pre-releases of the stand-alone electron wallet, head over to the [Parity-JS repositories and check the releases](https://github.com/Parity-JS/shell/releases).
|
||||
|
||||
Further reading:
|
||||
|
||||
- [Docs: Parity Wallet](https://wiki.parity.io/Parity-Wallet)
|
||||
- [Docs: How to customize Parity UI?](https://wiki.parity.io/FAQ-Customize-Parity-UI.html)
|
||||
- [Github: Parity-JS](https://github.com/parity-js)
|
||||
|
||||
### Introducing the Wasm VM
|
||||
|
||||
We are excited to announce support for **Wasm Smart Contracts on Kovan network**. The hard-fork to activate the Wasm-VM will take place on block `6_600_000`.
|
||||
|
||||
To enable Wasm contracts on your custom network, just schedule a `wasmActivationTransition` at your favorite block number (e.g., `42`, `666`, or `0xbada55`). To hack your first Wasm smart contracts in Rust, have a look at the [Parity Wasm Tutorials](https://github.com/paritytech/pwasm-tutorial).
|
||||
|
||||
Further reading:
|
||||
|
||||
- [Docs: WebAssembly (wasm)](https://wiki.parity.io/WebAssembly-Home)
|
||||
- [Docs: Wasm VM Design](https://wiki.parity.io/WebAssembly-Design)
|
||||
- [Docs: Wasm tutorials and examples](https://wiki.parity.io/WebAssembly-Links)
|
||||
|
||||
### Empty step messages in PoA
|
||||
|
||||
To **reduce blockchain bloat, proof-of-authority networks can now enable _empty step messages_ which replace empty blocks**. Each step message will be signed and broadcasted by the issuing authorities, and included and rewarded in the next non-empty block.
|
||||
|
||||
To enable empty step messages, set the `emptyStepsTransition` to your favorite block number. You can also specify a maximum number of empty steps with `maximumEmptySteps` in your chain spec.
|
||||
|
||||
### Other noteworthy changes
|
||||
|
||||
We removed the old database migrations from 2016. In case you upgrade Parity from a really, really old version, you will have to reset your database manually first with `parity <options> db kill`.
|
||||
|
||||
We fixed DELEGATECALL `from` and `to` fields, see [#7166](https://github.com/paritytech/parity/issues/7166).
|
||||
|
||||
We reduced the default USD per transaction value to 0.0001. Thanks, @MysticRyuujin!
|
||||
|
||||
The Musicoin chain is now enabled with Byzantium features starting at block `2_222_222`.
|
||||
|
||||
### Overview of all changes included
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Re-enable signer, even with no UI. ([#8167](https://github.com/paritytech/parity/pull/8167)) ([#8168](https://github.com/paritytech/parity/pull/8168))
|
||||
- Re-enable signer, even with no UI.
|
||||
- Fix message.
|
||||
- Beta Backports ([#8136](https://github.com/paritytech/parity/pull/8136))
|
||||
- Support parity protocol. ([#8035](https://github.com/paritytech/parity/pull/8035))
|
||||
- updater: apply exponential backoff after download failure ([#8059](https://github.com/paritytech/parity/pull/8059))
|
||||
- updater: apply exponential backoff after download failure
|
||||
- updater: reset backoff on new release
|
||||
- Max code size on Kovan ([#8067](https://github.com/paritytech/parity/pull/8067))
|
||||
- Enable code size limit on kovan
|
||||
- Fix formatting.
|
||||
- Limit incoming connections. ([#8060](https://github.com/paritytech/parity/pull/8060))
|
||||
- Limit ingress connections
|
||||
- Optimized handshakes logging
|
||||
- WASM libraries bump ([#7970](https://github.com/paritytech/parity/pull/7970))
|
||||
- update wasmi, parity-wasm, wasm-utils to latest version
|
||||
- Update to new wasmi & error handling
|
||||
- also utilize new stack limiter
|
||||
- fix typo
|
||||
- replace dependency url
|
||||
- Cargo.lock update
|
||||
- add some dos protection ([#8084](https://github.com/paritytech/parity/pull/8084))
|
||||
- revert removing blooms ([#8066](https://github.com/paritytech/parity/pull/8066))
|
||||
- Revert "fix traces, removed bloomchain crate, closes [#7228](https://github.com/paritytech/parity/issues/7228), closes [#7167](https://github.com/paritytech/parity/issues/7167)"
|
||||
- Revert "fixed broken logs ([#7934](https://github.com/paritytech/parity/pull/7934))"
|
||||
- fixed broken logs
|
||||
- bring back old lock order
|
||||
- remove migration v13
|
||||
- revert CURRENT_VERSION to 12 in migration.rs
|
||||
- more dos protection ([#8104](https://github.com/paritytech/parity/pull/8104))
|
||||
- Const time comparison ([#8113](https://github.com/paritytech/parity/pull/8113))
|
||||
- Use `subtle::slices_equal` for constant time comparison.
|
||||
- Also update the existing version of subtle in `ethcrypto` from 0.1 to 0.5
|
||||
- Test specifically for InvalidPassword error.
|
||||
- fix trace filter returning returning unrelated reward calls, closes #8070 ([#8098](https://github.com/paritytech/parity/pull/8098))
|
||||
- network: init discovery using healthy nodes ([#8061](https://github.com/paritytech/parity/pull/8061))
|
||||
- network: init discovery using healthy nodes
|
||||
- network: fix style grumble
|
||||
- network: fix typo
|
||||
- Postpone Kovan hard fork ([#8137](https://github.com/paritytech/parity/pull/8137))
|
||||
- ethcore: postpone Kovan hard fork
|
||||
- util: update version fork metadata
|
||||
- Disable UI by default. ([#8105](https://github.com/paritytech/parity/pull/8105))
|
||||
- dapps: update parity-ui dependencies ([#8160](https://github.com/paritytech/parity/pull/8160))
|
||||
- Probe changes one step deeper ([#8134](https://github.com/paritytech/parity/pull/8134)) ([#8135](https://github.com/paritytech/parity/pull/8135))
|
||||
- Beta backports ([#8053](https://github.com/paritytech/parity/pull/8053))
|
||||
- CI: Fix cargo cache ([#7968](https://github.com/paritytech/parity/pull/7968))
|
||||
- Fix cache
|
||||
- Only clean locked cargo cache on windows
|
||||
- fixed ethstore sign ([#8026](https://github.com/paritytech/parity/pull/8026))
|
||||
- fixed parsing ethash seals and verify_block_undordered ([#8031](https://github.com/paritytech/parity/pull/8031))
|
||||
- fix for verify_block_basic crashing on invalid transaction rlp ([#8032](https://github.com/paritytech/parity/pull/8032))
|
||||
- fix cache & snapcraft CI build ([#8052](https://github.com/paritytech/parity/pull/8052))
|
||||
- Add MCIP-6 Byzyantium transition to Musicoin spec ([#7841](https://github.com/paritytech/parity/pull/7841))
|
||||
- Add test chain spec for musicoin byzantium testnet
|
||||
- Add MCIP-6 Byzyantium transition to Musicoin spec
|
||||
- Update mcip6_byz.json
|
||||
- ethcore: update musicoin byzantium block number
|
||||
- ethcore: update musicoin bootnodes
|
||||
- Update musicoin.json
|
||||
- More bootnodes.
|
||||
- Make 1.10 beta ([#8022](https://github.com/paritytech/parity/pull/8022))
|
||||
- Make 1.10 beta
|
||||
- Fix gitlab builds
|
||||
- SecretStore: secretstore_generateDocumentKey RPC ([#7864](https://github.com/paritytech/parity/pull/7864))
|
||||
- SecretStore: ECDSA session for cases when 2*t < N ([#7739](https://github.com/paritytech/parity/pull/7739))
|
||||
- bump tiny-keccak ([#8019](https://github.com/paritytech/parity/pull/8019))
|
||||
- Remove un-necessary comment ([#8014](https://github.com/paritytech/parity/pull/8014))
|
||||
- clean up account fmt::Debug ([#7983](https://github.com/paritytech/parity/pull/7983))
|
||||
- improve quality of vote_collector module ([#7984](https://github.com/paritytech/parity/pull/7984))
|
||||
- ExecutedBlock cleanup ([#7991](https://github.com/paritytech/parity/pull/7991))
|
||||
- Hardware-wallet/usb-subscribe-refactor ([#7860](https://github.com/paritytech/parity/pull/7860))
|
||||
- remove wildcard imports from views, make tests more idiomatic ([#7986](https://github.com/paritytech/parity/pull/7986))
|
||||
- moved PerfTimer to a separate crate - "trace-time" ([#7985](https://github.com/paritytech/parity/pull/7985))
|
||||
- clean up ethcore::spec module imports ([#7990](https://github.com/paritytech/parity/pull/7990))
|
||||
- rpc: don't include current block in new_block_filter ([#7982](https://github.com/paritytech/parity/pull/7982))
|
||||
- fix traces, removed bloomchain crate ([#7979](https://github.com/paritytech/parity/pull/7979))
|
||||
- simplify compression and move it out of rlp crate ([#7957](https://github.com/paritytech/parity/pull/7957))
|
||||
- removed old migrations ([#7974](https://github.com/paritytech/parity/pull/7974))
|
||||
- Reject too large packets in snapshot sync. ([#7977](https://github.com/paritytech/parity/pull/7977))
|
||||
- fixed broken logs ([#7934](https://github.com/paritytech/parity/pull/7934))
|
||||
- Increase max download limit to 128MB ([#7965](https://github.com/paritytech/parity/pull/7965))
|
||||
- Calculate proper keccak256/sha3 using parity. ([#7953](https://github.com/paritytech/parity/pull/7953))
|
||||
- Add changelog for 1.8.10 stable and 1.9.3 beta ([#7947](https://github.com/paritytech/parity/pull/7947))
|
||||
- kvdb-rocksdb: remove buffered operations when committing transaction ([#7950](https://github.com/paritytech/parity/pull/7950))
|
||||
- Bump WebSockets ([#7952](https://github.com/paritytech/parity/pull/7952))
|
||||
- removed redundant Bloom conversions ([#7932](https://github.com/paritytech/parity/pull/7932))
|
||||
- simplify RefInfo fmt ([#7929](https://github.com/paritytech/parity/pull/7929))
|
||||
- Kovan WASM fork code ([#7849](https://github.com/paritytech/parity/pull/7849))
|
||||
- bring back trie and triehash benches ([#7926](https://github.com/paritytech/parity/pull/7926))
|
||||
- removed redundant PodAccount::new method ([#7928](https://github.com/paritytech/parity/pull/7928))
|
||||
- removed dummy wrapper structure - LogGroupPosition ([#7922](https://github.com/paritytech/parity/pull/7922))
|
||||
- spec: Validate required divisor fields are not 0 ([#7933](https://github.com/paritytech/parity/pull/7933))
|
||||
- simplify Client::filter_traces method ([#7936](https://github.com/paritytech/parity/pull/7936))
|
||||
- gitlab cache ([#7921](https://github.com/paritytech/parity/pull/7921))
|
||||
- Fix a division by zero in light client RPC handler ([#7917](https://github.com/paritytech/parity/pull/7917))
|
||||
- triehash optimisations ([#7920](https://github.com/paritytech/parity/pull/7920))
|
||||
- removed redundant Blockchain::db method ([#7919](https://github.com/paritytech/parity/pull/7919))
|
||||
- removed redundant Blockchain::rewind method ([#7918](https://github.com/paritytech/parity/pull/7918))
|
||||
- Pending transactions subscription ([#7906](https://github.com/paritytech/parity/pull/7906))
|
||||
- removed redundant otry! macro from ethcore ([#7916](https://github.com/paritytech/parity/pull/7916))
|
||||
- Make block generator easier to use ([#7888](https://github.com/paritytech/parity/pull/7888))
|
||||
- ECIP 1041 - Remove Difficulty Bomb ([#7905](https://github.com/paritytech/parity/pull/7905))
|
||||
- Fix CSP for dapps that require eval. ([#7867](https://github.com/paritytech/parity/pull/7867))
|
||||
- Fix gitlab ([#7901](https://github.com/paritytech/parity/pull/7901))
|
||||
- Gitlb snap master patch ([#7900](https://github.com/paritytech/parity/pull/7900))
|
||||
- fix snap build master ([#7896](https://github.com/paritytech/parity/pull/7896))
|
||||
- Fix wallet import ([#7873](https://github.com/paritytech/parity/pull/7873))
|
||||
- Fix snapcraft nightly ([#7884](https://github.com/paritytech/parity/pull/7884))
|
||||
- Add a timeout for light client sync requests ([#7848](https://github.com/paritytech/parity/pull/7848))
|
||||
- SecretStore: fixed test ([#7878](https://github.com/paritytech/parity/pull/7878))
|
||||
- Fix checksums and auto-update push ([#7846](https://github.com/paritytech/parity/pull/7846))
|
||||
- Forward-port snap fixes ([#7831](https://github.com/paritytech/parity/pull/7831))
|
||||
- Update gitlab-test.sh ([#7883](https://github.com/paritytech/parity/pull/7883))
|
||||
- Fix installer binary names for macos and windows ([#7881](https://github.com/paritytech/parity/pull/7881))
|
||||
- Fix string typo: "develoopment" -> "development" ([#7874](https://github.com/paritytech/parity/pull/7874))
|
||||
- Update the instructions to install the stable snap ([#7876](https://github.com/paritytech/parity/pull/7876))
|
||||
- SecretStore: 'broadcast' decryption session ([#7843](https://github.com/paritytech/parity/pull/7843))
|
||||
- Flush keyfiles. Resolves #7632 ([#7868](https://github.com/paritytech/parity/pull/7868))
|
||||
- Read registry_address from given block ([#7866](https://github.com/paritytech/parity/pull/7866))
|
||||
- Clean up docs formatting for Wasm runtime ([#7869](https://github.com/paritytech/parity/pull/7869))
|
||||
- WASM: Disable internal memory ([#7842](https://github.com/paritytech/parity/pull/7842))
|
||||
- Update gitlab-build.sh ([#7855](https://github.com/paritytech/parity/pull/7855))
|
||||
- ethabi version 5 ([#7723](https://github.com/paritytech/parity/pull/7723))
|
||||
- Light client: randomize the peer we dispatch requests to ([#7844](https://github.com/paritytech/parity/pull/7844))
|
||||
- Store updater metadata in a single place ([#7832](https://github.com/paritytech/parity/pull/7832))
|
||||
- Add new EF ropstens nodes. ([#7824](https://github.com/paritytech/parity/pull/7824))
|
||||
- refactor stratum to remove retain cycle ([#7827](https://github.com/paritytech/parity/pull/7827))
|
||||
- Bump jsonrpc. ([#7828](https://github.com/paritytech/parity/pull/7828))
|
||||
- Add binary identifiers and sha256sum to builds ([#7830](https://github.com/paritytech/parity/pull/7830))
|
||||
- Update references to UI shell & wallet ([#7808](https://github.com/paritytech/parity/pull/7808))
|
||||
- Adjust storage update evm-style ([#7812](https://github.com/paritytech/parity/pull/7812))
|
||||
- Updated WASM Runtime & new interpreter (wasmi) ([#7796](https://github.com/paritytech/parity/pull/7796))
|
||||
- SecretStore: ignore removed authorities when running auto-migration ([#7674](https://github.com/paritytech/parity/pull/7674))
|
||||
- Fix build ([#7807](https://github.com/paritytech/parity/pull/7807))
|
||||
- Move js & js-old code to github.com/parity-js ([#7685](https://github.com/paritytech/parity/pull/7685))
|
||||
- More changelogs :) ([#7782](https://github.com/paritytech/parity/pull/7782))
|
||||
- Actualized API set in help ([#7790](https://github.com/paritytech/parity/pull/7790))
|
||||
- Removed obsolete file ([#7788](https://github.com/paritytech/parity/pull/7788))
|
||||
- Update ropsten bootnodes ([#7776](https://github.com/paritytech/parity/pull/7776))
|
||||
- CHANGELOG for 1.9.1 and 1.8.8 ([#7775](https://github.com/paritytech/parity/pull/7775))
|
||||
- Enable byzantium features on non-ethash chains ([#7753](https://github.com/paritytech/parity/pull/7753))
|
||||
- Fix client not being dropped on shutdown ([#7695](https://github.com/paritytech/parity/pull/7695))
|
||||
- Filter-out nodes.json ([#7716](https://github.com/paritytech/parity/pull/7716))
|
||||
- Removes redundant parentheses ([#7721](https://github.com/paritytech/parity/pull/7721))
|
||||
- Transaction-pool fixes ([#7741](https://github.com/paritytech/parity/pull/7741))
|
||||
- More visible download link in README.md ([#7707](https://github.com/paritytech/parity/pull/7707))
|
||||
- Changelog for 1.9.0 ([#7664](https://github.com/paritytech/parity/pull/7664))
|
||||
- Add scroll when too many accounts ([#7677](https://github.com/paritytech/parity/pull/7677))
|
||||
- SecretStore: return HTTP 403 (access denied) if consensus is unreachable ([#7656](https://github.com/paritytech/parity/pull/7656))
|
||||
- Moved StopGaurd to it's own crate ([#7635](https://github.com/paritytech/parity/pull/7635))
|
||||
|
||||
## Previous releases
|
||||
|
||||
- [CHANGELOG-1.9](docs/CHANGELOG-1.9.md) (_stable_)
|
||||
- [CHANGELOG-1.8](docs/CHANGELOG-1.8.md) (EOL: 2018-03-22)
|
||||
- [CHANGELOG-1.7](docs/CHANGELOG-1.7.md) (EOL: 2018-01-25)
|
||||
- [CHANGELOG-1.6](docs/CHANGELOG-1.6.md) (EOL: 2017-10-15)
|
||||
- [CHANGELOG-1.5](docs/CHANGELOG-1.5.md) (EOL: 2017-07-28)
|
||||
- [CHANGELOG-1.4](docs/CHANGELOG-1.4.md) (EOL: 2017-03-13)
|
||||
- [CHANGELOG-1.3](docs/CHANGELOG-1.3.md) (EOL: 2017-01-19)
|
||||
- [CHANGELOG-1.2](docs/CHANGELOG-1.2.md) (EOL: 2016-11-07)
|
||||
- [CHANGELOG-1.1](docs/CHANGELOG-1.1.md) (EOL: 2016-08-12)
|
||||
- [CHANGELOG-1.0](docs/CHANGELOG-1.0.md) (EOL: 2016-06-24)
|
||||
- [CHANGELOG-0.9](docs/CHANGELOG-0.9.md) (EOL: 2016-05-02)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user