Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b00a21f39a | ||
|
|
e9396e158b | ||
|
|
7fbcdfeed4 | ||
|
|
f44d885b6d | ||
|
|
6b0a280685 | ||
|
|
78ceec6c6e | ||
|
|
5c56fc5023 | ||
|
|
5a7e54fb9e |
@@ -1,3 +1,3 @@
|
||||
[target.x86_64-pc-windows-msvc]
|
||||
# Link the C runtime statically ; https://github.com/openethereum/openethereum/issues/6643
|
||||
# Link the C runtime statically ; https://github.com/paritytech/parity-ethereum/issues/6643
|
||||
rustflags = ["-Ctarget-feature=+crt-static"]
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Reformat the source code
|
||||
610d9baba4af83b5767c659ca2ccfed337af1056
|
||||
8
.github/CODE_OF_CONDUCT.md
vendored
8
.github/CODE_OF_CONDUCT.md
vendored
@@ -2,11 +2,11 @@
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
A primary goal of OpenEthereum is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof).
|
||||
A primary goal of Parity is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof).
|
||||
|
||||
This code of conduct outlines our expectations for all those who participate in our community, as well as the consequences for unacceptable behavior.
|
||||
|
||||
We invite all those who participate in OpenEthereum to help us create safe and positive experiences for everyone.
|
||||
We invite all those who participate in Parity to help us create safe and positive experiences for everyone.
|
||||
|
||||
## 2. Open Source Citizenship
|
||||
|
||||
@@ -63,7 +63,7 @@ Additionally, community organizers are available to help community members engag
|
||||
|
||||
## 7. Addressing Grievances
|
||||
|
||||
If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify OpenEthereum Technologies with a concise description of your grievance. Your grievance will be handled in accordance with our existing governing policies.
|
||||
If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify Parity Technologies with a concise description of your grievance. Your grievance will be handled in accordance with our existing governing policies.
|
||||
|
||||
## 8. Scope
|
||||
|
||||
@@ -73,7 +73,7 @@ This code of conduct and its related procedures also applies to unacceptable beh
|
||||
|
||||
## 9. Contact info
|
||||
|
||||
You can contact OpenEthereum via Email: community@parity.io
|
||||
You can contact Parity via Email: community@parity.io
|
||||
|
||||
## 10. License and attribution
|
||||
|
||||
|
||||
47
.github/CONTRIBUTING.md
vendored
47
.github/CONTRIBUTING.md
vendored
@@ -2,7 +2,7 @@
|
||||
|
||||
## Do you have a question?
|
||||
|
||||
Check out our [Basic Usage](https://openethereum.github.io/wiki/Basic-Usage), [Configuration](https://openethereum.github.io/wiki/Configuring-OpenEthereum), and [FAQ](https://openethereum.github.io/wiki/FAQ) articles on our [wiki](https://openethereum.github.io/wiki)!
|
||||
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/)!
|
||||
|
||||
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/openethereum/openethereum/issues/new) in our repository and state:
|
||||
Otherwise, just create a [new issue](https://github.com/paritytech/parity-ethereum/issues/new) in our repository and state:
|
||||
|
||||
- What's your OpenEthereum version?
|
||||
- What's your Parity Ethereum version?
|
||||
- What's your operating system and version?
|
||||
- How did you install OpenEthereum?
|
||||
- How did you install Parity Ethereum?
|
||||
- Is your node fully synchronized?
|
||||
- Did you try turning it off and on again?
|
||||
|
||||
@@ -22,44 +22,9 @@ Also, try to include **steps to reproduce** the issue and expand on the **actual
|
||||
|
||||
## Contribute!
|
||||
|
||||
If you would like to contribute to OpenEthereum, please **fork it**, fix bugs or implement features, and [propose a pull request](https://github.com/openethereum/openethereum/compare).
|
||||
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).
|
||||
|
||||
### Labels & Milestones
|
||||
|
||||
We use [labels](https://github.com/openethereum/openethereum/labels) to manage PRs and issues and communicate the state of a PR. Please familiarize yourself with them. Furthermore we are organizing issues in [milestones](https://github.com/openethereum/openethereum/milestones). Best way to get started is to a pick a ticket from the current milestone tagged [`easy`](https://github.com/openethereum/openethereum/labels/Q2-easy%20%F0%9F%92%83) and get going, or [`mentor`](https://github.com/openethereum/openethereum/labels/Q1-mentor%20%F0%9F%95%BA) and get in contact with the mentor offering their support on that larger task.
|
||||
|
||||
### Rules
|
||||
|
||||
There are a few basic ground-rules for contributors (including the maintainer(s) of the project):
|
||||
|
||||
* **No pushing directly to the master branch**.
|
||||
* **All modifications** must be made in a **pull-request** to solicit feedback from other contributors.
|
||||
* Pull-requests cannot be merged before CI runs green and two reviewers have given their approval.
|
||||
* Contributors should adhere to the [Parity Ethereum Style Guide](https://openethereum.github.io/wiki/Parity-Ethereum-Style-Guide).
|
||||
|
||||
### Recommendations
|
||||
|
||||
* **Non-master branch names** *should* be prefixed with a short name moniker, followed by the associated Github Issue ID (if any), and a brief description of the task using the format `<GITHUB_USERNAME>-<ISSUE_ID>-<BRIEF_DESCRIPTION>` (e.g. `gavin-123-readme`). The name moniker helps people to inquiry about their unfinished work, and the GitHub Issue ID helps your future self and other developers (particularly those who are onboarding) find out about and understand the original scope of the task, and where it fits into Parity Ethereum [Projects](https://github.com/openethereum/openethereum/projects).
|
||||
* **Remove stale branches periodically**
|
||||
|
||||
### Preparing Pull Requests
|
||||
|
||||
* If your PR does not alter any logic (e.g. comments, dependencies, docs), then it may be tagged [`insubstantial`](https://github.com/openethereum/openethereum/pulls?q=is%3Aopen+is%3Apr+label%3A%22A2-insubstantial+%F0%9F%91%B6%22).
|
||||
|
||||
* Once a PR is ready for review please add the [`pleasereview`](https://github.com/openethereum/openethereum/pulls?utf8=%E2%9C%93&q=is%3Aopen+is%3Apr+label%3A%22A0-pleasereview+%F0%9F%A4%93%22+) label.
|
||||
|
||||
### Reviewing Pull Requests*:
|
||||
|
||||
* At least two reviewers are required to review PRs (even for PRs tagged [`insubstantial`](https://github.com/openethereum/openethereum/pulls?q=is%3Aopen+is%3Apr+label%3A%22A2-insubstantial+%F0%9F%91%B6%22)).
|
||||
|
||||
When doing a review, make sure to look for any:
|
||||
|
||||
* Buggy behavior.
|
||||
* Undue maintenance burden.
|
||||
* Breaking with house coding style.
|
||||
* Pessimization (i.e. reduction of speed as measured in the projects benchmarks).
|
||||
* Breaking changes should be carefuly reviewed and tagged as such so they end up in the [changelog](../CHANGELOG.md).
|
||||
* Uselessness (i.e. it does not strictly add a feature or fix a known issue).
|
||||
Please, refer to the [Coding Guide](https://wiki.parity.io/Coding-guide) in our wiki for more details about hacking on Parity.
|
||||
|
||||
## License.
|
||||
|
||||
|
||||
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,6 +1,6 @@
|
||||
_Before filing a new issue, please **provide the following information**._
|
||||
|
||||
- **OpenEthereum version**: 0.0.0
|
||||
- **Parity Ethereum version**: 0.0.0
|
||||
- **Operating system**: Windows / MacOS / Linux
|
||||
- **Installation**: homebrew / one-line installer / built from source
|
||||
- **Fully synchronized**: no / yes
|
||||
|
||||
99
.github/workflows/build-test.yml
vendored
99
.github/workflows/build-test.yml
vendored
@@ -1,99 +0,0 @@
|
||||
name: Build and Test Suite
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- stable
|
||||
jobs:
|
||||
build-tests:
|
||||
name: Test and Build
|
||||
env:
|
||||
SCCACHE_CACHE_SIZE: "1G"
|
||||
SCCACHE_IDLE_TIMEOUT: 0
|
||||
strategy:
|
||||
matrix:
|
||||
platform:
|
||||
- ubuntu-16.04
|
||||
- macos-latest
|
||||
- windows-latest
|
||||
toolchain:
|
||||
- stable
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@main
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.toolchain }}
|
||||
profile: minimal
|
||||
override: true
|
||||
- name: Cache cargo registry
|
||||
uses: actions/cache@v1.1.2
|
||||
with:
|
||||
path: ~/.cargo/registry
|
||||
key: ${{ runner.os }}-cargo-registry-build-tests-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Cache cargo index
|
||||
uses: actions/cache@v1.1.2
|
||||
with:
|
||||
path: ~/.cargo/git
|
||||
key: ${{ runner.os }}-cargo-git-build-tests-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Cache cargo build
|
||||
uses: actions/cache@v1.1.2
|
||||
with:
|
||||
path: target
|
||||
key: ${{ runner.os }}-cargo-build-target-build-tests-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Cache sccache linux
|
||||
if: matrix.platform == 'ubuntu-16.04'
|
||||
uses: actions/cache@v1.1.2
|
||||
with:
|
||||
path: "/home/runner/.cache/sccache"
|
||||
key: ${{ runner.os }}-sccache-build-tests-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Cache sccache MacOS
|
||||
if: matrix.platform == 'macos-latest'
|
||||
uses: actions/cache@v1.1.2
|
||||
with:
|
||||
path: "/Users/runner/Library/Caches/Mozilla.sccache"
|
||||
key: ${{ runner.os }}-sccache-build-tests-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Cache sccache Windows
|
||||
if: matrix.platform == 'windows-latest'
|
||||
uses: actions/cache@v1.1.2
|
||||
with:
|
||||
path: "C:\\Users\\runneradmin\\AppData\\Local\\Mozilla\\sccache\\cache"
|
||||
key: ${{ runner.os }}-sccache-build-tests-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Install sccache for ${{ matrix.platform }}
|
||||
shell: pwsh
|
||||
run: pwsh scripts/actions/install-sccache.ps1 ${{ runner.os}}
|
||||
- name: Install LLVM for Windows
|
||||
if: matrix.platform == 'windows-latest'
|
||||
run: choco install llvm
|
||||
- name: Sccache statistics
|
||||
run: sccache --show-stats
|
||||
- name: Build tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --locked --all --release --features "json-tests" --verbose --no-run
|
||||
- name: Run tests for ${{ matrix.platform }}
|
||||
if: matrix.platform == 'windows-latest'
|
||||
continue-on-error: true #Skip step if Windows tests failure
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --locked --all --release --features "json-tests" --verbose
|
||||
- name: Run tests for ${{ matrix.platform }}
|
||||
if: matrix.platform != 'windows-latest'
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --locked --all --release --features "json-tests" --verbose
|
||||
- name: Stop sccache
|
||||
if: always()
|
||||
run: sccache --stop-server
|
||||
- name: Prepare build directory for cache
|
||||
shell: bash
|
||||
run: bash scripts/actions/clean-target.sh
|
||||
341
.github/workflows/build.yml
vendored
341
.github/workflows/build.yml
vendored
@@ -1,341 +0,0 @@
|
||||
name: Build Release Suite
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- stable
|
||||
tags:
|
||||
- v*
|
||||
|
||||
# Global vars
|
||||
env:
|
||||
AWS_REGION: "us-east-1"
|
||||
AWS_S3_ARTIFACTS_BUCKET: "openethereum-releases"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Release
|
||||
env:
|
||||
SCCACHE_CACHE_SIZE: "1G"
|
||||
SCCACHE_IDLE_TIMEOUT: 0
|
||||
strategy:
|
||||
matrix:
|
||||
platform:
|
||||
- ubuntu-16.04
|
||||
- macos-latest
|
||||
- windows-latest
|
||||
toolchain:
|
||||
- stable
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@main
|
||||
- name: Install toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.toolchain }}
|
||||
profile: minimal
|
||||
override: true
|
||||
- name: Cache cargo registry
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cargo/registry
|
||||
key: ${{ runner.os }}-cargo-registry-build-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Cache cargo index
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cargo/git
|
||||
key: ${{ runner.os }}-cargo-git-build-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Cache cargo build
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: target
|
||||
key: ${{ runner.os }}-cargo-build-target-build-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Cache sccache linux
|
||||
if: matrix.platform == 'ubuntu-16.04'
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: "/home/runner/.cache/sccache"
|
||||
key: ${{ runner.os }}-sccache-build-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Cache sccache MacOS
|
||||
if: matrix.platform == 'macos-latest'
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: "/Users/runner/Library/Caches/Mozilla.sccache"
|
||||
key: ${{ runner.os }}-sccache-build-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Cache sccache Windows
|
||||
if: matrix.platform == 'windows-latest'
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: "C:\\Users\\runneradmin\\AppData\\Local\\Mozilla\\sccache\\cache"
|
||||
key: ${{ runner.os }}-sccache-build-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Install sccache for ${{ matrix.platform }}
|
||||
shell: pwsh
|
||||
run: pwsh scripts/actions/install-sccache.ps1 ${{ runner.os}}
|
||||
|
||||
# ==============================
|
||||
# Windows Build
|
||||
# ==============================
|
||||
|
||||
- name: Install LLVM for Windows
|
||||
if: matrix.platform == 'windows-latest'
|
||||
run: choco install llvm
|
||||
|
||||
- name: Sccache statistics
|
||||
run: sccache --show-stats
|
||||
|
||||
- name: Build OpenEthereum for Windows
|
||||
if: matrix.platform == 'windows-latest'
|
||||
run: sh scripts/actions/build-windows.sh ${{matrix.platform}}
|
||||
|
||||
- name: Upload Windows build
|
||||
uses: actions/upload-artifact@v2
|
||||
if: matrix.platform == 'windows-latest'
|
||||
with:
|
||||
name: windows-artifacts
|
||||
path: artifacts
|
||||
|
||||
# ==============================
|
||||
# Linux/Macos Build
|
||||
# ==============================
|
||||
|
||||
- name: Build OpenEthereum for ${{matrix.platform}}
|
||||
if: matrix.platform != 'windows-latest'
|
||||
run: sh scripts/actions/build-linux.sh ${{matrix.platform}}
|
||||
|
||||
- name: Upload Linux build
|
||||
uses: actions/upload-artifact@v2
|
||||
if: matrix.platform == 'ubuntu-16.04'
|
||||
with:
|
||||
name: linux-artifacts
|
||||
path: artifacts
|
||||
|
||||
- name: Upload MacOS build
|
||||
uses: actions/upload-artifact@v2
|
||||
if: matrix.platform == 'macos-latest'
|
||||
with:
|
||||
name: macos-artifacts
|
||||
path: artifacts
|
||||
|
||||
# ==============================
|
||||
# End builds
|
||||
# ==============================
|
||||
|
||||
- name: Stop sccache
|
||||
if: always()
|
||||
run: sccache --stop-server
|
||||
|
||||
- name: Prepare build directory for cache
|
||||
shell: bash
|
||||
run: bash scripts/actions/clean-target.sh
|
||||
|
||||
zip-artifacts-creator:
|
||||
name: Create zip artifacts
|
||||
needs: build
|
||||
runs-on: ubuntu-16.04
|
||||
steps:
|
||||
- name: Set env
|
||||
run: echo ::set-env name=RELEASE_VERSION::${GITHUB_REF#refs/*/}
|
||||
|
||||
# ==============================
|
||||
# Create ZIP files
|
||||
# ==============================
|
||||
|
||||
- name: Download Windows artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: windows-artifacts
|
||||
path: windows-artifacts
|
||||
|
||||
- name: Download Linux artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: linux-artifacts
|
||||
path: linux-artifacts
|
||||
|
||||
- name: Download MacOS artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: macos-artifacts
|
||||
path: macos-artifacts
|
||||
|
||||
- name: Display structure of downloaded files
|
||||
run: ls
|
||||
|
||||
- name: Create zip Linux
|
||||
id: create_zip_linux
|
||||
run: |
|
||||
cd linux-artifacts/
|
||||
zip -rT openethereum-linux-${{ env.RELEASE_VERSION }}.zip *
|
||||
ls openethereum-linux-${{ env.RELEASE_VERSION }}.zip
|
||||
cd ..
|
||||
mv linux-artifacts/openethereum-linux-${{ env.RELEASE_VERSION }}.zip .
|
||||
|
||||
echo "Setting outputs..."
|
||||
echo ::set-output name=LINUX_ARTIFACT::openethereum-linux-${{ env.RELEASE_VERSION }}.zip
|
||||
echo ::set-output name=LINUX_SHASUM::$(shasum -a 256 openethereum-linux-${{ env.RELEASE_VERSION }}.zip | awk '{print $1}')
|
||||
|
||||
- name: Create zip MacOS
|
||||
id: create_zip_macos
|
||||
run: |
|
||||
cd macos-artifacts/
|
||||
zip -rT openethereum-macos-${{ env.RELEASE_VERSION }}.zip *
|
||||
ls openethereum-macos-${{ env.RELEASE_VERSION }}.zip
|
||||
cd ..
|
||||
mv macos-artifacts/openethereum-macos-${{ env.RELEASE_VERSION }}.zip .
|
||||
|
||||
echo "Setting outputs..."
|
||||
echo ::set-output name=MACOS_ARTIFACT::openethereum-macos-${{ env.RELEASE_VERSION }}.zip
|
||||
echo ::set-output name=MACOS_SHASUM::$(shasum -a 256 openethereum-macos-${{ env.RELEASE_VERSION }}.zip | awk '{print $1}')
|
||||
|
||||
- name: Create zip Windows
|
||||
id: create_zip_windows
|
||||
run: |
|
||||
cd windows-artifacts/
|
||||
zip -rT openethereum-windows-${{ env.RELEASE_VERSION }}.zip *
|
||||
ls openethereum-windows-${{ env.RELEASE_VERSION }}.zip
|
||||
cd ..
|
||||
mv windows-artifacts/openethereum-windows-${{ env.RELEASE_VERSION }}.zip .
|
||||
|
||||
echo "Setting outputs..."
|
||||
echo ::set-output name=WINDOWS_ARTIFACT::openethereum-windows-${{ env.RELEASE_VERSION }}.zip
|
||||
echo ::set-output name=WINDOWS_SHASUM::$(shasum -a 256 openethereum-windows-${{ env.RELEASE_VERSION }}.zip | awk '{print $1}')
|
||||
|
||||
# =======================================================================
|
||||
# Upload artifacts
|
||||
# This is required to share artifacts between different jobs
|
||||
# =======================================================================
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: openethereum-linux-${{ env.RELEASE_VERSION }}.zip
|
||||
path: openethereum-linux-${{ env.RELEASE_VERSION }}.zip
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: openethereum-macos-${{ env.RELEASE_VERSION }}.zip
|
||||
path: openethereum-macos-${{ env.RELEASE_VERSION }}.zip
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: openethereum-windows-${{ env.RELEASE_VERSION }}.zip
|
||||
path: openethereum-windows-${{ env.RELEASE_VERSION }}.zip
|
||||
|
||||
# =======================================================================
|
||||
# Upload artifacts to S3
|
||||
# This is required by some software distribution systems which require
|
||||
# artifacts to be downloadable, like Brew on MacOS.
|
||||
# =======================================================================
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v1
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ env.AWS_REGION }}
|
||||
|
||||
- name: Copy files to S3 with the AWS CLI
|
||||
run: |
|
||||
# Deploy zip artifacts to S3 bucket to a directory whose name is the tagged release version.
|
||||
# Deploy macos binary artifact (if required, add more `aws s3 cp` commands to deploy specific OS versions)
|
||||
aws s3 cp macos-artifacts/openethereum s3://${{ env.AWS_S3_ARTIFACTS_BUCKET }}/${{ env.RELEASE_VERSION }}/macos/ --region ${{ env.AWS_REGION }}
|
||||
|
||||
outputs:
|
||||
linux-artifact: ${{ steps.create_zip_linux.outputs.LINUX_ARTIFACT }}
|
||||
linux-shasum: ${{ steps.create_zip_linux.outputs.LINUX_SHASUM }}
|
||||
macos-artifact: ${{ steps.create_zip_macos.outputs.MACOS_ARTIFACT }}
|
||||
macos-shasum: ${{ steps.create_zip_macos.outputs.MACOS_SHASUM }}
|
||||
windows-artifact: ${{ steps.create_zip_windows.outputs.WINDOWS_ARTIFACT }}
|
||||
windows-shasum: ${{ steps.create_zip_windows.outputs.WINDOWS_SHASUM }}
|
||||
|
||||
draft-release:
|
||||
name: Draft Release
|
||||
needs: zip-artifacts-creator
|
||||
runs-on: ubuntu-16.04
|
||||
steps:
|
||||
- name: Set env
|
||||
run: echo ::set-env name=RELEASE_VERSION::${GITHUB_REF#refs/*/}
|
||||
|
||||
# ==============================
|
||||
# Download artifacts
|
||||
# ==============================
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: openethereum-linux-${{ env.RELEASE_VERSION }}.zip
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: openethereum-macos-${{ env.RELEASE_VERSION }}.zip
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: openethereum-windows-${{ env.RELEASE_VERSION }}.zip
|
||||
|
||||
- name: Display structure of downloaded files
|
||||
run: ls
|
||||
|
||||
# ==============================
|
||||
# Create release draft
|
||||
# ==============================
|
||||
|
||||
- name: Create Release Draft
|
||||
id: create_release_draft
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: OpenEthereum ${{ github.ref }}
|
||||
body: |
|
||||
This release contains <ADD_TEXT>
|
||||
|
||||
| System | Architecture | Binary | Sha256 Checksum |
|
||||
|:---:|:---:|:---:|:---|
|
||||
| <img src="https://gist.github.com/5chdn/1fce888fde1d773761f809b607757f76/raw/44c4f0fc63f1ea8e61a9513af5131ef65eaa6c75/apple.png" alt="Apple Icon by Pixel Perfect from https://www.flaticon.com/authors/pixel-perfect" style="width: 32px;"/> | x64 | [${{ needs.zip-artifacts-creator.outputs.macos-artifact }}](https://github.com/openethereum/openethereum/releases/download/${{ env.RELEASE_VERSION }}/${{ needs.zip-artifacts-creator.outputs.macos-artifact }}) | `${{ needs.zip-artifacts-creator.outputs.macos-shasum }}` |
|
||||
| <img src="https://gist.github.com/5chdn/1fce888fde1d773761f809b607757f76/raw/44c4f0fc63f1ea8e61a9513af5131ef65eaa6c75/linux.png" alt="Linux Icon by Pixel Perfect from https://www.flaticon.com/authors/pixel-perfect" style="width: 32px;"/> | x64 | [${{ needs.zip-artifacts-creator.outputs.linux-artifact }}](https://github.com/openethereum/openethereum/releases/download/${{ env.RELEASE_VERSION }}/${{ needs.zip-artifacts-creator.outputs.linux-artifact }}) | `${{ needs.zip-artifacts-creator.outputs.linux-shasum }}` |
|
||||
| <img src="https://gist.github.com/5chdn/1fce888fde1d773761f809b607757f76/raw/44c4f0fc63f1ea8e61a9513af5131ef65eaa6c75/windows.png" alt="Windows Icon by Pixel Perfect from https://www.flaticon.com/authors/pixel-perfect" style="width: 32px;"/> | x64 | [${{ needs.zip-artifacts-creator.outputs.windows-artifact }}](https://github.com/openethereum/openethereum/releases/download/${{ env.RELEASE_VERSION }}/${{ needs.zip-artifacts-creator.outputs.windows-artifact }}) | `${{ needs.zip-artifacts-creator.outputs.windows-shasum }}` |
|
||||
| | | | |
|
||||
| **System** | **Option** | - | **Resource** |
|
||||
| <img src="https://gist.github.com/5chdn/1fce888fde1d773761f809b607757f76/raw/44c4f0fc63f1ea8e61a9513af5131ef65eaa6c75/settings.png" alt="Settings Icon by Pixel Perfect from https://www.flaticon.com/authors/pixel-perfect" style="width: 32px;"/> | Docker | - | [hub.docker.com/r/openethereum/openethereum](https://hub.docker.com/r/openethereum/openethereum) |
|
||||
|
||||
draft: true
|
||||
prerelease: true
|
||||
|
||||
- name: Upload Release Asset - Linux
|
||||
id: upload_release_asset_linux
|
||||
uses: actions/upload-release-asset@v1.0.1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release_draft.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
|
||||
asset_path: ./openethereum-linux-${{ env.RELEASE_VERSION }}.zip
|
||||
asset_name: openethereum-linux-${{ env.RELEASE_VERSION }}.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
- name: Upload Release Asset - MacOS
|
||||
id: upload_release_asset_macos
|
||||
uses: actions/upload-release-asset@v1.0.1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release_draft.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
|
||||
asset_path: ./openethereum-macos-${{ env.RELEASE_VERSION }}.zip
|
||||
asset_name: openethereum-macos-${{ env.RELEASE_VERSION }}.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
- name: Upload Release Asset - Windows
|
||||
id: upload_release_asset_windows
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release_draft.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
|
||||
asset_path: ./openethereum-windows-${{ env.RELEASE_VERSION }}.zip
|
||||
asset_name: openethereum-windows-${{ env.RELEASE_VERSION }}.zip
|
||||
asset_content_type: application/zip
|
||||
86
.github/workflows/check.yml
vendored
86
.github/workflows/check.yml
vendored
@@ -1,86 +0,0 @@
|
||||
name: Check
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- stable
|
||||
jobs:
|
||||
check:
|
||||
name: Check
|
||||
runs-on: ubuntu-16.04
|
||||
env:
|
||||
SCCACHE_CACHE_SIZE: "1G"
|
||||
SCCACHE_IDLE_TIMEOUT: 0
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@main
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install stable toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
override: true
|
||||
- name: Cache cargo registry
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cargo/registry
|
||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Cache cargo index
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cargo/git
|
||||
key: ${{ runner.os }}-cargo-git-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Cache cargo build
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: target
|
||||
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
|
||||
# Install sccache based on https://github.com/denoland/rusty_v8/blob/master/.github/workflows/ci.yml#L69
|
||||
- name: Cache sccache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: "/home/runner/.cache/sccache"
|
||||
key: ${{ runner.os }}-sccache-check-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Install sccache for Linux
|
||||
shell: pwsh
|
||||
run: pwsh scripts/actions/install-sccache.ps1 ${{ runner.os}}
|
||||
- name: Sccache statistics
|
||||
run: sccache --show-stats
|
||||
- name: Run cargo check 1/3
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
args: --locked --no-default-features --verbose
|
||||
- name: Run cargo check 2/3
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
args: --locked --manifest-path util/io/Cargo.toml --no-default-features --verbose
|
||||
- name: Run cargo check 3/3
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
args: --locked --manifest-path util/io/Cargo.toml --features "mio" --verbose
|
||||
- name: Run cargo check evmbin
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
args: --locked -p evmbin --verbose
|
||||
- name: Run cargo check benches
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
args: --locked --all --benches --verbose
|
||||
- name: Run validate chainspecs
|
||||
run: ./scripts/actions/validate-chainspecs.sh
|
||||
- name: Stop sccache
|
||||
if: always()
|
||||
run: sccache --stop-server
|
||||
continue-on-error: true
|
||||
- name: Prepare build directory for cache
|
||||
shell: bash
|
||||
run: bash scripts/actions/clean-target.sh
|
||||
30
.github/workflows/deploy-docker.yml
vendored
30
.github/workflows/deploy-docker.yml
vendored
@@ -1,30 +0,0 @@
|
||||
name: Docker Image Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- v*
|
||||
|
||||
jobs:
|
||||
deploy-docker:
|
||||
name: Build Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@master
|
||||
- name: Install toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
override: true
|
||||
- name: Deploy to docker hub
|
||||
uses: elgohr/Publish-Docker-Github-Action@master
|
||||
with:
|
||||
name: openethereum/openethereum
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
dockerfile: scripts/docker/alpine/Dockerfile
|
||||
tag_names: true
|
||||
20
.github/workflows/fmt.yml
vendored
20
.github/workflows/fmt.yml
vendored
@@ -1,20 +0,0 @@
|
||||
on: [push, pull_request]
|
||||
|
||||
name: rustfmt
|
||||
|
||||
jobs:
|
||||
fmt:
|
||||
name: Rustfmt
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
- run: rustup component add rustfmt
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check --config merge_imports=true
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -38,10 +38,8 @@ node_modules
|
||||
|
||||
# Build artifacts
|
||||
out/
|
||||
parity-clib-examples/cpp/build/
|
||||
|
||||
.vscode
|
||||
rls/
|
||||
/parity.*
|
||||
|
||||
# cargo remote artifacts
|
||||
remote-target
|
||||
|
||||
170
.gitlab-ci.yml
Normal file
170
.gitlab-ci.yml
Normal file
@@ -0,0 +1,170 @@
|
||||
stages:
|
||||
- test
|
||||
- build
|
||||
- publish
|
||||
- optional
|
||||
|
||||
image: parity/rust:gitlab-ci
|
||||
|
||||
variables:
|
||||
CI_SERVER_NAME: "GitLab CI"
|
||||
CARGO_HOME: "${CI_PROJECT_DIR}/.cargo"
|
||||
CARGO_TARGET: x86_64-unknown-linux-gnu
|
||||
|
||||
.releaseable_branches: # list of git refs for building GitLab artifacts (think "pre-release binaries")
|
||||
only: &releaseable_branches
|
||||
- stable
|
||||
- beta
|
||||
- tags
|
||||
- schedules
|
||||
|
||||
|
||||
.collect_artifacts: &collect_artifacts
|
||||
artifacts:
|
||||
name: "${CI_JOB_NAME}_${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}"
|
||||
when: on_success
|
||||
expire_in: 1 mos
|
||||
paths:
|
||||
- artifacts/
|
||||
|
||||
.determine_version: &determine_version
|
||||
- VERSION="$(sed -r -n '1,/^version/s/^version = "([^"]+)".*$/\1/p' Cargo.toml)"
|
||||
- DATE_STR="$(date +%Y%m%d)"
|
||||
- ID_SHORT="$(echo ${CI_COMMIT_SHA} | cut -c 1-7)"
|
||||
- test "${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}" = "nightly" && VERSION="${VERSION}-${ID_SHORT}-${DATE_STR}"
|
||||
- export VERSION
|
||||
- echo "Version = ${VERSION}"
|
||||
|
||||
test-linux:
|
||||
stage: test
|
||||
variables:
|
||||
RUN_TESTS: all
|
||||
script:
|
||||
- scripts/gitlab/test-all.sh stable
|
||||
tags:
|
||||
- rust-stable
|
||||
|
||||
test-audit:
|
||||
stage: test
|
||||
script:
|
||||
- scripts/gitlab/cargo-audit.sh
|
||||
tags:
|
||||
- rust-stable
|
||||
|
||||
build-linux:
|
||||
stage: build
|
||||
only: *releaseable_branches
|
||||
script:
|
||||
- scripts/gitlab/build-unix.sh
|
||||
<<: *collect_artifacts
|
||||
tags:
|
||||
- rust-stable
|
||||
|
||||
build-darwin:
|
||||
stage: build
|
||||
only: *releaseable_branches
|
||||
variables:
|
||||
CARGO_TARGET: x86_64-apple-darwin
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
script:
|
||||
- scripts/gitlab/build-unix.sh
|
||||
tags:
|
||||
- rust-osx
|
||||
<<: *collect_artifacts
|
||||
|
||||
build-windows:
|
||||
stage: build
|
||||
only: *releaseable_branches
|
||||
variables:
|
||||
CARGO_TARGET: x86_64-pc-windows-msvc
|
||||
script:
|
||||
- sh scripts/gitlab/build-windows.sh
|
||||
tags:
|
||||
- rust-windows
|
||||
<<: *collect_artifacts
|
||||
|
||||
publish-docker:
|
||||
stage: publish
|
||||
only: *releaseable_branches
|
||||
cache: {}
|
||||
dependencies:
|
||||
- build-linux
|
||||
tags:
|
||||
- shell
|
||||
script:
|
||||
- scripts/gitlab/publish-docker.sh parity
|
||||
|
||||
publish-snap:
|
||||
stage: publish
|
||||
only: *releaseable_branches
|
||||
image: parity/snapcraft:gitlab-ci
|
||||
variables:
|
||||
BUILD_ARCH: amd64
|
||||
cache: {}
|
||||
before_script: *determine_version
|
||||
dependencies:
|
||||
- build-linux
|
||||
tags:
|
||||
- rust-stable
|
||||
script:
|
||||
- scripts/gitlab/publish-snap.sh
|
||||
allow_failure: true
|
||||
<<: *collect_artifacts
|
||||
|
||||
publish-awss3:
|
||||
stage: publish
|
||||
only: *releaseable_branches
|
||||
cache: {}
|
||||
dependencies:
|
||||
- build-linux
|
||||
- build-darwin
|
||||
- build-windows
|
||||
before_script: *determine_version
|
||||
script:
|
||||
- scripts/gitlab/publish-awss3.sh
|
||||
tags:
|
||||
- shell
|
||||
|
||||
publish-docs:
|
||||
stage: publish
|
||||
only:
|
||||
- tags
|
||||
except:
|
||||
- nightly
|
||||
cache: {}
|
||||
script:
|
||||
- scripts/gitlab/publish-docs.sh
|
||||
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:
|
||||
- rust-arm
|
||||
allow_failure: true
|
||||
|
||||
test-beta:
|
||||
stage: optional
|
||||
variables:
|
||||
RUN_TESTS: cargo
|
||||
script:
|
||||
- scripts/gitlab/test-all.sh beta
|
||||
tags:
|
||||
- rust-beta
|
||||
allow_failure: true
|
||||
|
||||
test-nightly:
|
||||
stage: optional
|
||||
variables:
|
||||
RUN_TESTS: all
|
||||
script:
|
||||
- scripts/gitlab/test-all.sh nightly
|
||||
tags:
|
||||
- rust-nightly
|
||||
allow_failure: true
|
||||
897
CHANGELOG.md
897
CHANGELOG.md
@@ -1,34 +1,873 @@
|
||||
## OpenEthereum v3.1RC1
|
||||
## Parity-Ethereum [v2.1.3](https://github.com/paritytech/parity-ethereum/releases/tag/v2.1.3) (2018-10-16)
|
||||
|
||||
OpenEthereum 3.1rc1 is a candidate release based on v2.5.13 which is the last stable version known of the client that does not include any of the issues introduced in v2.7.
|
||||
It removes non core features like Ethereum Classic, Private Transactions, Light Client, Updater, IPFS and Swarm support, currently deprecated flags such as expanse, kotti, mordor testnets.
|
||||
Parity-Ethereum 2.1.3-stable is a release that fixes a consensus issue with the recent Constantinople release. Upgrading is mandatory whatever network you are connected to that plans enabling EIP-1283, e.g., Ropsten, Kovan, Ethereum.
|
||||
|
||||
Database migration utility currently in beta: https://github.com/openethereum/3.1-db-upgrade-tool
|
||||
The full list of included changes:
|
||||
|
||||
The full list of included changes from v2.5.13 to v3.1:
|
||||
- Beta release 2.1.3 backports ([#9749](https://github.com/paritytech/parity-ethereum/pull/9749))
|
||||
- Parity-version: mark 2.1.3 beta as critical
|
||||
- Use signed 256-bit integer for sstore gas refund substate ([#9746](https://github.com/paritytech/parity-ethereum/pull/9746))
|
||||
- Add --force to cargo audit install script ([#9735](https://github.com/paritytech/parity-ethereum/pull/9735))
|
||||
- Heads ref not present for branches beta and stable ([#9741](https://github.com/paritytech/parity-ethereum/pull/9741))
|
||||
- Aura: fix panic on extra_info with unsealed block ([#9755](https://github.com/paritytech/parity-ethereum/pull/9755))
|
||||
|
||||
- Remove classic, kotti, mordor, expanse (#52)
|
||||
- Added bad block header hash for ropsten (#49)
|
||||
- Remove accounts bloom (#33)
|
||||
- Bump jsonrpc-- to v15
|
||||
- Implement eth/64, remove eth/62 (#46)
|
||||
- No snapshotting by default (#11814)
|
||||
- Update Ellaism chainspec
|
||||
- Prometheus, heavy memory calls removed (#27)
|
||||
- Update ethereum/tests
|
||||
- Implement JSON test suite (#11801)
|
||||
- Fix issues during block sync (#11265)
|
||||
- Fix race same block (#11400)
|
||||
- EIP-2537: Precompile for BLS12-381 curve operations (#11707)
|
||||
- Remove private transactions
|
||||
- Remove GetNodeData
|
||||
- Remove IPFS integration (#11532)
|
||||
- Remove updater
|
||||
- Remove light client
|
||||
- Remove C and Java bindings (#11346)
|
||||
- Remove whisper (#10855)
|
||||
- EIP-2315: Simple Subroutines for the EVM (#11629)
|
||||
- Remove deprecated flags (removal of --geth flag)
|
||||
- Remove support for hardware wallets (#10678)
|
||||
- Update bootnodes
|
||||
## Parity-Ethereum [v2.1.2](https://github.com/paritytech/parity-ethereum/releases/tag/v2.1.2) (2018-10-12)
|
||||
|
||||
Parity-Ethereum 2.1.2-beta is a release that introduces **Constantinople** to the Ethereum client. Upgrading is strongly recommended.
|
||||
|
||||

|
||||
|
||||
The following hardforks are supported by this release:
|
||||
|
||||
- Ropsten testnet block `4_230_000` on October 14, 2018 (Constantinople)
|
||||
- POA core mainnet block `5_329_160` on October 22, 2018 (CORE HF 2)
|
||||
- Kovan testnet block `9_200_000` on October 25, 2018 (Constantinople, KIP-{4,6})
|
||||
|
||||
Running one of these networks, an upgrade to 2.0.7 or 2.1.2 is mandatory. More details can be found in Changelog below.
|
||||
|
||||
Please note, the following deprecations in our distribution of binaries:
|
||||
|
||||
- `arm*` targets are no longer served by parity, please consider (cross-)compiling from source yourself.
|
||||
- `i*86` targets are no longer served by parity, please consider upgrading your operating system.
|
||||
- Snapcraft is no longer maintained. please use binaries directly or your distro's repositories.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- More backports for 2.1.2 ([#9733](https://github.com/paritytech/parity-ethereum/pull/9733))
|
||||
- Produce portable binaries ([#9725](https://github.com/paritytech/parity-ethereum/pull/9725))
|
||||
- HF in POA Core (2018-10-22) ([#9724](https://github.com/paritytech/parity-ethereum/pull/9724))
|
||||
- Use static call and apparent value transfer for block reward contract code ([#9603](https://github.com/paritytech/parity-ethereum/pull/9603))
|
||||
- Verify block syncing responses against requests ([#9670](https://github.com/paritytech/parity-ethereum/pull/9670))
|
||||
- Fix ancient blocks sync ([#9531](https://github.com/paritytech/parity-ethereum/pull/9531))
|
||||
- Add hardcoded headers ([#9730](https://github.com/paritytech/parity-ethereum/pull/9730))
|
||||
- Gitlab ci: releasable_branches: change variables condition to schedule ([#9729](https://github.com/paritytech/parity-ethereum/pull/9729))
|
||||
- Backports for beta 2.1.2 ([#9649](https://github.com/paritytech/parity-ethereum/pull/9649))
|
||||
- Parity-version: bump beta to 2.1.2
|
||||
- Ethcore: fix detection of major import ([#9552](https://github.com/paritytech/parity-ethereum/pull/9552))
|
||||
- Hardfork the testnets ([#9562](https://github.com/paritytech/parity-ethereum/pull/9562))
|
||||
- Docs(rpc): push the branch along with tags ([#9578](https://github.com/paritytech/parity-ethereum/pull/9578))
|
||||
- Remove snapcraft clean ([#9585](https://github.com/paritytech/parity-ethereum/pull/9585))
|
||||
- Fix (light/provider) : Make `read_only executions` only read-only ([#9591](https://github.com/paritytech/parity-ethereum/pull/9591))
|
||||
- Ci: fix regex 🙄 ([#9597](https://github.com/paritytech/parity-ethereum/pull/9597))
|
||||
- Docs(rpc): annotate tag with the provided message ([#9601](https://github.com/paritytech/parity-ethereum/pull/9601))
|
||||
- Update ropsten bootnodes ([#9602](https://github.com/paritytech/parity-ethereum/pull/9602))
|
||||
- HF in POA Sokol (2018-09-19) ([#9607](https://github.com/paritytech/parity-ethereum/pull/9607))
|
||||
- Fix(network): don't disconnect reserved peers ([#9608](https://github.com/paritytech/parity-ethereum/pull/9608))
|
||||
- Fix failing node-table tests on mac os ([#9633](https://github.com/paritytech/parity-ethereum/pull/9633))
|
||||
- Fix bad-block reporting no reason ([#9638](https://github.com/paritytech/parity-ethereum/pull/9638))
|
||||
- Ethcore-io retries failed work steal ([#9651](https://github.com/paritytech/parity-ethereum/pull/9651))
|
||||
- Remove master from releasable branches ([#9655](https://github.com/paritytech/parity-ethereum/pull/9655))
|
||||
- Test fix for windows cache name... ([#9658](https://github.com/paritytech/parity-ethereum/pull/9658))
|
||||
- Fix(light_fetch): avoid race with BlockNumber::Latest ([#9665](https://github.com/paritytech/parity-ethereum/pull/9665))
|
||||
- Calculate sha3 instead of sha256 for push-release. ([#9673](https://github.com/paritytech/parity-ethereum/pull/9673))
|
||||
- Ci: fix push script ([#9679](https://github.com/paritytech/parity-ethereum/pull/9679))
|
||||
- CI: Remove unnecessary pipes ([#9681](https://github.com/paritytech/parity-ethereum/pull/9681))
|
||||
- Don't hash the init_code of CREATE. ([#9688](https://github.com/paritytech/parity-ethereum/pull/9688))
|
||||
- Docker: run parity as normal user ([#9689](https://github.com/paritytech/parity-ethereum/pull/9689))
|
||||
- CI: Skip docs job for master and nightly ([#9693](https://github.com/paritytech/parity-ethereum/pull/9693))
|
||||
- Implement CREATE2 gas changes and fix some potential overflowing ([#9694](https://github.com/paritytech/parity-ethereum/pull/9694))
|
||||
- Make instantSeal engine backwards compatible ([#9700](https://github.com/paritytech/parity-ethereum/pull/9700))
|
||||
- Ethcore: delay ropsten hardfork ([#9704](https://github.com/paritytech/parity-ethereum/pull/9704))
|
||||
|
||||
## Parity-Ethereum [v2.1.1](https://github.com/paritytech/parity-ethereum/releases/tag/v2.1.1) (2018-09-20)
|
||||
|
||||
Parity-Ethereum 2.1.1-beta is a release that does not improve performance and stability; no changes were made.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Backports for 2.1.1 beta ([#9599](https://github.com/paritytech/parity-ethereum/pull/9599))
|
||||
- Parity: bump version to 2.1.1 beta
|
||||
- Ci: fix regex roll_eyes
|
||||
- Docs(rpc): annotate tag with the provided message
|
||||
|
||||
## Parity-Ethereum [v2.1.0](https://github.com/paritytech/parity-ethereum/releases/tag/v2.1.0) (2018-09-19)
|
||||
|
||||
Parity-Ethereum 2.1.0-beta is released! Look at this!
|
||||
|
||||
Important notices:
|
||||
|
||||
- This release moves the 2.1 track of Parity-Ethereum to beta.
|
||||
- This release contains a low-severity issue with the web-sockets ports. [#9545](https://github.com/paritytech/parity-ethereum/pull/9545)
|
||||
- This release resolves a potential network fragmentation issue. [#9526](https://github.com/paritytech/parity-ethereum/pull/9526)
|
||||
- With this release, all versions of Parity Ethereum 1.x prior to 2.0 reached end of life.
|
||||
- Users are urged to upgrade to 2.0.5-stable or 2.1.0-beta.
|
||||
|
||||
Further changes worth highlighting:
|
||||
|
||||
- Generalized `blockReward` and `difficultyBombDelays` config ([#9480](https://github.com/paritytech/parity-ethereum/pull/9480)): This removes `eip649*` parameters from chain specs and allows `blockReward` to accept `multi`. Please review your chain-specs!
|
||||
- Implement EIP234 `block_hash` for `eth_getLogs` ([#9256](https://github.com/paritytech/parity-ethereum/pull/9256)): If `block_hash` and `from_block`/`to_block` present, return error. This also changes `eth_getLogs` to return error if any of `block_hash`/`from_block`/`to_block` cannot be found.
|
||||
- The default `gas_floor_target` was increased to `8_000_000`, the default `gas_cap` to `10_000_000`.
|
||||
- Light clients provide the actual account for `eth_coinbase` RPC ([#9383](https://github.com/paritytech/parity-ethereum/pull/9383)). Note, this behavior is different from the full client where we return the `0x0` address if no accounts are found!
|
||||
- Light servers give free credits for reserved peers ([#9448](https://github.com/paritytech/parity-ethereum/pull/9448)): When connecting to a reserved peers in light client, assign free credits to them. This fixes the scenario where a home server is running a full node, and another light client connects to the full node as a reserved peer.
|
||||
- Multi-threaded snapshot creation ([#9239](https://github.com/paritytech/parity-ethereum/pull/9239)): to speed up the generation of snapshots on disk, this can be parallelized now. By default N/2 CPU cores are being used. Use `--snapshot-threads` flag to customize the number of threads.
|
||||
- Remove all dapp permissions related settings ([#9120](https://github.com/paritytech/parity-ethereum/pull/9120)). This completely removes dapp permission settings from `AccountProvider` and JSON-RPC: In JSON-RPC, all available accounts are returned, regardless of the origin; previously we return accounts based on dapps policy. It's not possible to set a "default account" (as for dapps) any more; this is now always the first account in the available account list.
|
||||
- Remove unused `--tx_queue_gas` parameter ([#9153](https://github.com/paritytech/parity-ethereum/pull/9153)). Please use `parity --help` to learn about available transaction queue strategies.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Add snapcraft package image ([#9583](https://github.com/paritytech/parity-ethereum/pull/9583))
|
||||
- Add snapcraft package image
|
||||
- Update .gitlab-ci.yml
|
||||
- Remove snapcraft clean
|
||||
- Backports for 2.1.0 beta ([#9518](https://github.com/paritytech/parity-ethereum/pull/9518))
|
||||
- Parity-version: mark 2.1.0 track beta
|
||||
- Ci: update branch version references
|
||||
- Docker: release master to latest
|
||||
- Fix checkpointing when creating contract failed ([#9514](https://github.com/paritytech/parity-ethereum/pull/9514))
|
||||
- Ci: fix json docs generation ([#9515](https://github.com/paritytech/parity-ethereum/pull/9515))
|
||||
- Fix typo in version string ([#9516](https://github.com/paritytech/parity-ethereum/pull/9516))
|
||||
- Update patricia trie to 0.2.2 crates. Default dependencies on minor version only.
|
||||
- Putting back ethereum tests to the right commit
|
||||
- Enable all Constantinople hard fork changes in constantinople_test.json ([#9505](https://github.com/paritytech/parity-ethereum/pull/9505))
|
||||
- Enable all Constantinople hard fork changes in constantinople_test.json
|
||||
- Address grumbles
|
||||
- Remove EIP-210 activation
|
||||
- 8m -> 5m
|
||||
- Temporarily add back eip210 transition so we can get test passed
|
||||
- Add eip210_test and remove eip210 transition from const_test
|
||||
- In create memory calculation is the same for create2 because the additional parameter was popped before. ([#9522](https://github.com/paritytech/parity-ethereum/pull/9522))
|
||||
- Deps: bump fs-swap and kvdb-rocksdb
|
||||
- Multithreaded snapshot creation ([#9239](https://github.com/paritytech/parity-ethereum/pull/9239))
|
||||
- Add Progress to Snapshot Secondary chunks creation
|
||||
- Use half of CPUs to multithread snapshot creation
|
||||
- Use env var to define number of threads
|
||||
- Info to debug logs
|
||||
- Add Snapshot threads as CLI option
|
||||
- Randomize chunks per thread
|
||||
- Remove randomness, add debugging
|
||||
- Add warning
|
||||
- Add tracing
|
||||
- Use parity-common fix seek branch
|
||||
- Fix log
|
||||
- Fix tests
|
||||
- Fix tests
|
||||
- Pr Grumbles
|
||||
- Pr Grumble II
|
||||
- Update Cargo.lock
|
||||
- Pr Grumbles
|
||||
- Default snapshot threads to half number of CPUs
|
||||
- Fix default snapshot threads // min 1
|
||||
- Correct before_script for nightly build versions ([#9543](https://github.com/paritytech/parity-ethereum/pull/9543))
|
||||
- Fix gitlab array of strings syntax error
|
||||
- Get proper commit id
|
||||
- Avoid colon in stings
|
||||
- Remove initial token for WS. ([#9545](https://github.com/paritytech/parity-ethereum/pull/9545))
|
||||
- Version: mark release critical
|
||||
- Ci: fix rpc docs generation 2 ([#9550](https://github.com/paritytech/parity-ethereum/pull/9550))
|
||||
- Improve P2P discovery ([#9526](https://github.com/paritytech/parity-ethereum/pull/9526))
|
||||
- Add `target` to Rust traces
|
||||
- Network-devp2p: Don't remove discovery peer in main sync
|
||||
- Network-p2p: Refresh discovery more often
|
||||
- Update Peer discovery protocol
|
||||
- Run discovery more often when not enough nodes connected
|
||||
- Start the first discovery early
|
||||
- Update fast discovery rate
|
||||
- Fix tests
|
||||
- Fix `ping` tests
|
||||
- Fixing remote Node address ; adding PingPong round
|
||||
- Fix tests: update new +1 PingPong round
|
||||
- Increase slow Discovery rate
|
||||
- Check in flight FindNode before pings
|
||||
- Add `deprecated` to deprecated_echo_hash
|
||||
- Refactor `discovery_round` branching
|
||||
- Net_version caches network_id to avoid redundant acquire of sync read lock ([#9544](https://github.com/paritytech/parity-ethereum/pull/9544))
|
||||
- Net_version caches network_id to avoid redundant acquire of sync read lock, [#8746](https://github.com/paritytech/parity-ethereum/issues/8746)
|
||||
- Use lower_hex display formatting for net_peerCount rpc method
|
||||
- Increase Gas-floor-target and Gas Cap ([#9564](https://github.com/paritytech/parity-ethereum/pull/9564))
|
||||
- Gas-floor-target increased to 8M by default
|
||||
- Gas-cap increased to 10M by default
|
||||
- Revert to old parity-tokio-ipc.
|
||||
- Downgrade named pipes.
|
||||
- Fix checkpointing when creating contract failed ([#9514](https://github.com/paritytech/parity-ethereum/pull/9514))
|
||||
- Ci: fix json docs generation ([#9515](https://github.com/paritytech/parity-ethereum/pull/9515))
|
||||
- Update state tests execution model ([#9440](https://github.com/paritytech/parity-ethereum/pull/9440))
|
||||
- Update & fix JSON state tests.
|
||||
- Update tests to be able to run ethtest at 021fe3d410773024cd5f0387e62db6e6ec800f32.
|
||||
- Touch user in state
|
||||
- Adjust transaction tests to new json format
|
||||
- Switch to same commit for submodule ethereum/test as geth (next includes constantinople changes).
|
||||
- Added test `json_tests::trie::generic::TrieTests_trieanyorder` and a few difficulty tests.
|
||||
- Remove trietestnextprev as it would require to parse differently and implement it.
|
||||
- Support new (shitty) format of transaction tests.
|
||||
- Ignore junk in ethereum/tests repo.
|
||||
- Ignore incorrect test.
|
||||
- Update to a later commit
|
||||
- Move block number to a constant.
|
||||
- Fix ZK2 test - touched account should also be cleared.
|
||||
- Fix conflict resolution
|
||||
- Upload will fail if a file with the same hash is already up ([#9479](https://github.com/paritytech/parity-ethereum/pull/9479))
|
||||
- Upload will fail if a file with the same hash is already up
|
||||
- Compose version string for nightly releases
|
||||
- Fix Snapshot restoration failure on Windows ([#9491](https://github.com/paritytech/parity-ethereum/pull/9491))
|
||||
- Close Blooms DB files before DB restoration
|
||||
- Don't error when Snapshot is aborted ([#9492](https://github.com/paritytech/parity-ethereum/pull/9492))
|
||||
- Light: give free credits for reserved peers ([#9448](https://github.com/paritytech/parity-ethereum/pull/9448))
|
||||
- Light: give free credits for reserved peers
|
||||
- Fix ethcore-light tests
|
||||
- Test free_flow_params
|
||||
- Parity: print correct keys path on startup ([#9501](https://github.com/paritytech/parity-ethereum/pull/9501))
|
||||
- Generalized blockReward and difficultyBombDelays config ([#9480](https://github.com/paritytech/parity-ethereum/pull/9480))
|
||||
- Implement multi blockReward
|
||||
- Implement difficultyBombDelays
|
||||
- Fix json crate compile
|
||||
- Json keys can only be string
|
||||
- Rpc(debug_getBadBlocks): fix test ([#9502](https://github.com/paritytech/parity-ethereum/pull/9502))
|
||||
- Bad blocks RPC + reporting ([#9433](https://github.com/paritytech/parity-ethereum/pull/9433))
|
||||
- Bad blocks RPC.
|
||||
- Return bad blocks via RPC.
|
||||
- Fix test.
|
||||
- More verbose bad block message.
|
||||
- Expose via CLI.
|
||||
- Remove stray whitespace.
|
||||
- Remove stray newline.
|
||||
- Fix tests.
|
||||
- Eip 1283: Net gas metering for SSTORE without dirty maps ([#9319](https://github.com/paritytech/parity-ethereum/pull/9319))
|
||||
- Implement last_checkpoint_storage_at
|
||||
- Add reverted_storage_at for externalities
|
||||
- Sstore_clears_count -> sstore_clears_refund
|
||||
- Implement eip1283 for evm
|
||||
- Add eip1283Transition params
|
||||
- Evm: fix tests
|
||||
- Jsontests: fix test
|
||||
- Return checkpoint index when creating
|
||||
- Comply with spec Version II
|
||||
- Fix docs
|
||||
- Fix jsontests feature compile
|
||||
- Address grumbles
|
||||
- Fix no-checkpoint-entry case
|
||||
- Remove unnecessary expect
|
||||
- Add test for State::checkpoint_storage_at
|
||||
- Add executive level test for eip1283
|
||||
- Hard-code transaction_checkpoint_index to 0
|
||||
- Fix jsontests
|
||||
- Add tests for checkpoint discard/revert
|
||||
- Require checkpoint to be empty for kill_account and commit
|
||||
- Get code coverage
|
||||
- Use saturating_add/saturating_sub
|
||||
- Fix issues in insert_cache
|
||||
- Clear the state again
|
||||
- Fix original_storage_at
|
||||
- Early return for empty RLP trie storage
|
||||
- Update comments
|
||||
- Fix borrow_mut issue
|
||||
- Simplify checkpoint_storage_at if branches
|
||||
- Better commenting for gas handling code
|
||||
- Address naming grumbles
|
||||
- More tests
|
||||
- Fix an issue in overwrite_with
|
||||
- Add another test
|
||||
- Fix comment
|
||||
- Remove unnecessary bracket
|
||||
- Move orig to inner if
|
||||
- Remove test coverage for this PR
|
||||
- Add tests for executive original value
|
||||
- Add warn! for an unreachable cause
|
||||
- Light `clippy(fy)` ([#9473](https://github.com/paritytech/parity-ethereum/pull/9473))
|
||||
- Wasm tests
|
||||
- `clippyfy` light-client
|
||||
- Revert inefficient change `collect_ready()`
|
||||
- Aura: don't report skipped primaries when empty steps are enabled ([#9435](https://github.com/paritytech/parity-ethereum/pull/9435))
|
||||
- Support millisecond timestamp for instant seal engine ([#9469](https://github.com/paritytech/parity-ethereum/pull/9469))
|
||||
- Support millisecond timestamp for instant seal engine
|
||||
- Forgot to checkin instant_seal mod
|
||||
- Fix instant seal config
|
||||
- Fix json crate compile
|
||||
- Fix private_spec.json
|
||||
- Option<bool> -> bool
|
||||
- Ethcore: don't validate difficulty when ignoring seal check ([#9470](https://github.com/paritytech/parity-ethereum/pull/9470))
|
||||
- Ethcore: don't validate difficulty when ignoring seal check
|
||||
- Ethcore: fix block verification test
|
||||
- Ethcore: document skipped verifications when check_seal is disabled
|
||||
- [light/jsonrpc] Provide the actual account for `eth_coinbase` RPC and unify error handling for light and full client ([#9383](https://github.com/paritytech/parity-ethereum/pull/9383))
|
||||
- Provide the actual `account` for eth_coinbase
|
||||
- The previous implementation always provided the `zero address` on `eth_coinbase` RPC. Now, instead the actual address is returned on success or an error when no account(s) is found!
|
||||
- Full client `eth_coinbase` return err
|
||||
- In the full-client return an error when no account is found instead of returning the `zero address`
|
||||
- Remove needless blocks on single import
|
||||
- Remove needless `static` lifetime on const
|
||||
- Fix `rpc_eth_author` test
|
||||
- Add a Java interface ([#9346](https://github.com/paritytech/parity-ethereum/pull/9346))
|
||||
- Add a Java interface
|
||||
- Use system ABI
|
||||
- Forgot exception
|
||||
- Fix param for parity_rpc
|
||||
- Address concerns
|
||||
- Fetch `parity-common` crates from crates.io ([#9410](https://github.com/paritytech/parity-ethereum/pull/9410))
|
||||
- Fetch `parity-common` crates from crates.io
|
||||
- Add doc tests from `patricia-trie` to `patricia-trie-ethereum`
|
||||
- Fix/update a few deps
|
||||
- [ethkey] upgrade ethereum-types
|
||||
- [whisper] update deps
|
||||
- [network] deps
|
||||
- [network-devp2p] deps
|
||||
- [journaldb] deps
|
||||
- [fastmap] deps
|
||||
- [miner] deps and test fixes
|
||||
- [machine] deps
|
||||
- [json] deps
|
||||
- [hw] deps
|
||||
- [ethash] deps
|
||||
- [registrar] deps
|
||||
- Update a few more dependencies with new ethabi-*
|
||||
- [updater] Update deps
|
||||
- Deps
|
||||
- [ethcore] Update deps
|
||||
- Use new parity-snappy and parity-rocksdb crates
|
||||
- Updated submodules
|
||||
- Use parity-snappy 0.1
|
||||
- Use kvdb-rocksdb 0.1.2
|
||||
- Don't use latest ethereum/tests
|
||||
- Fix merge conflicts errors
|
||||
- Remove superseeded comment
|
||||
- Address grumbles: add newlines, add/remove spaces
|
||||
- Fixed typo ([#9467](https://github.com/paritytech/parity-ethereum/pull/9467))
|
||||
- Fix light client deadlock ([#9385](https://github.com/paritytech/parity-ethereum/pull/9385))
|
||||
- This PR is fixing deadlock for [#8918](https://github.com/paritytech/parity-ethereum/issues/8918)
|
||||
- It avoids some recursive calls on light_sync by making state check optional for Informant.
|
||||
- The current behavior is to display the information when informant checks if block is major version.
|
||||
- This change a bit the informant behavior, but not on most cases.
|
||||
- To remember where and how this kind of deadlock are likely to happen (not seen with Parkinglot deadlock detection because it uses std condvar), I am adding a description of the deadlock.
|
||||
- Also, for the reviewers there may be better solution than modifying the informant.
|
||||
- Fix docs of address_hash ([#9463](https://github.com/paritytech/parity-ethereum/pull/9463))
|
||||
- Fix typo in bash script ([#9462](https://github.com/paritytech/parity-ethereum/pull/9462))
|
||||
- Fix a bug in evmbin initial_gas display ([#9457](https://github.com/paritytech/parity-ethereum/pull/9457))
|
||||
- Evmbin: escape newlines in json errors ([#9458](https://github.com/paritytech/parity-ethereum/pull/9458))
|
||||
- Use kvdb-* and parity-snappy crates from crates.io ([#9441](https://github.com/paritytech/parity-ethereum/pull/9441))
|
||||
- Use kvdb-* and parity-snappy crates from crates.io
|
||||
- Update rocksdb-sys and snappy-sys
|
||||
- Add EIP-1014 transition config flag ([#9268](https://github.com/paritytech/parity-ethereum/pull/9268))
|
||||
- Add EIP-1014 transition config flag
|
||||
- Remove EIP-86 configs
|
||||
- Change CREATE2 opcode index to 0xf5
|
||||
- Move salt to the last item in the stack
|
||||
- Change sendersaltandaddress scheme to comply with current EIP-1014
|
||||
- Fix json configs
|
||||
- Fix create2 test
|
||||
- Fix deprecated comments
|
||||
- Add tags for runner selection of build-linux jobs ([#9451](https://github.com/paritytech/parity-ethereum/pull/9451))
|
||||
- Remove unused BlockStatus::Pending ([#9447](https://github.com/paritytech/parity-ethereum/pull/9447))
|
||||
- Pending case never instantiated, and only ever matched together with Unknown
|
||||
- Ci: only include local paths in coverage script (except target) ([#9437](https://github.com/paritytech/parity-ethereum/pull/9437))
|
||||
- Add POA Networks: Core and Sokol ([#9413](https://github.com/paritytech/parity-ethereum/pull/9413))
|
||||
- Ethcore: add poa network and sokol chainspecs
|
||||
- Rpc: simplify chain spec docs
|
||||
- Cli: rearrange networks by main/test and size/range
|
||||
- Parity: don't blacklist 0x00a328 on sokol testnet
|
||||
- Parity: add sokol and poanet to params and clean up a bit, add tests
|
||||
- Ethcore: add the poa networks and clean up a bit
|
||||
- Ethcore: fix path to poacore chain spec
|
||||
- Parity: rename poa networks to poacore and poasokol
|
||||
- Parity: fix configuration tests
|
||||
- Parity: fix parameter tests
|
||||
- Ethcore: rename POA Core and POA Sokol
|
||||
- Docker: install missing dependencies in arm target dockerfiles ([#9436](https://github.com/paritytech/parity-ethereum/pull/9436))
|
||||
- Random small cleanups ([#9423](https://github.com/paritytech/parity-ethereum/pull/9423))
|
||||
- Clean up toml files
|
||||
- Update the parity ethereum toolchain docs
|
||||
- Update contribution guide and issue templates
|
||||
- Update desktop and service files
|
||||
- Build clib examples with 8 threads
|
||||
- Update header templates
|
||||
- Replace parity technologies with parity ethereum logo
|
||||
- Evmbin: Fix gas_used issue in state root mismatch and handle output better ([#9418](https://github.com/paritytech/parity-ethereum/pull/9418))
|
||||
- Fix gas used in staterootmismatch, and print full state root hash
|
||||
- Write trace info for stdjson to stderr
|
||||
- Fix tests
|
||||
- Remove struct trait bound
|
||||
- Update hardcoded sync ([#9421](https://github.com/paritytech/parity-ethereum/pull/9421))
|
||||
- Update foundation hardcoded header to block 6219777
|
||||
- Update ropsten hardcoded header to block 3917825
|
||||
- Update kovan hardcoded header to block 8511489
|
||||
- Add block reward contract config to ethash and allow off-chain contracts ([#9312](https://github.com/paritytech/parity-ethereum/pull/9312))
|
||||
- This adds block reward contract config to ethash. A new config `blockRewardContractCode` is also added to both Aura and ethash. When specified, it will execute the code directly and overrides any `blockRewardContractAddress` config. Having this `blockRewardContractCode` config allows chains to deploy hard fork by simply replacing the current config value, without the need from us to support any `multi` block reward scheme.
|
||||
- Private packets verification and queue refactoring ([#8715](https://github.com/paritytech/parity-ethereum/pull/8715))
|
||||
- Verify private transaction before propagating
|
||||
- Private transactions queue reworked with tx pool queue direct usage
|
||||
- Styling fixed
|
||||
- Prevent resending private packets to the sender
|
||||
- Process signed private transaction packets via io queue
|
||||
- Test fixed
|
||||
- Build and test fixed after merge
|
||||
- Comments after review fixed
|
||||
- Signed transaction taken from verified
|
||||
- Fix after merge
|
||||
- Pool scoring generalized in order to use externally
|
||||
- Lib refactored according to the review comments
|
||||
- Ready state refactored
|
||||
- Redundant bound and copying removed
|
||||
- Fixed build after the merge
|
||||
- Forgotten case reworked
|
||||
- Review comments fixed
|
||||
- Logging reworked, target added
|
||||
- Fix after merge
|
||||
- Update tobalaba.json ([#9419](https://github.com/paritytech/parity-ethereum/pull/9419))
|
||||
- Docs: add parity ethereum logo to readme ([#9415](https://github.com/paritytech/parity-ethereum/pull/9415))
|
||||
- Docs: add parity ethereum logo
|
||||
- Docs: add logo to readme
|
||||
- Docs: align logo center
|
||||
- Docs: remove separators from readme
|
||||
- Docs: restructure readme
|
||||
- Docs: check spelling and grammar in readme
|
||||
- Docs: clarify readme
|
||||
- Docs: improve readme significantly
|
||||
- Build: update rocksdb crate ([#9414](https://github.com/paritytech/parity-ethereum/pull/9414))
|
||||
- Updating the CI system ([#8765](https://github.com/paritytech/parity-ethereum/pull/8765))
|
||||
- Updating the CI system with the publication of releases and binary files on github
|
||||
- Add missed scripts
|
||||
- Chmod +x scripts
|
||||
- Fix download link for github
|
||||
- Rebuilding CI scripts
|
||||
- Small fixes
|
||||
- Update submodule wasm tests
|
||||
- Ci: fix merge leftovers
|
||||
- Ci: remove gitlab-next from ci triggers
|
||||
- Ci: fix git add in docs script
|
||||
- Ci: use nightly instead of master for publish triggers
|
||||
- Ci: remove sleep from gitlab config
|
||||
- Ci: replace ':' with '-' in gitlab targets
|
||||
- Ci: fix recursive copy in docs script
|
||||
- Better support for eth_getLogs in light mode ([#9186](https://github.com/paritytech/parity-ethereum/pull/9186))
|
||||
- Light client on-demand request for headers range.
|
||||
- Cache headers in HeaderWithAncestors response.
|
||||
- Also fulfills request locally if all headers are in cache.
|
||||
- Lightfetch::logs fetches missing headers on demand.
|
||||
- Lightfetch::logs limit the number of headers requested at a time.
|
||||
- Lightfetch::logs refactor header fetching logic.
|
||||
- Enforce limit on header range length in light client logs request.
|
||||
- Fix light request tests after struct change.
|
||||
- Respond to review comments.
|
||||
- Add update docs script to CI ([#9219](https://github.com/paritytech/parity-ethereum/pull/9219))
|
||||
- Add update docs script to CI
|
||||
- Added a script to CI that will use the jsonrpc tool to update rpc documentation then commit and push those to the wiki repo.
|
||||
- Fix gitlab ci lint
|
||||
- Only apply jsonrpc docs update on tags
|
||||
- Update gitlab-rpc-docs.sh
|
||||
- Copy correct parity repo to jsonrpc folder
|
||||
- Copy correct parity repo to jsonrpc folder before attempting to build docs since the CI runner clones the repo as parity and not parity-ethereum.
|
||||
- Fix JSONRPC docs CI job
|
||||
- Update remote config in wiki repo before pushing changes using a github token for authentication. Add message to wiki tag when pushing changes. Use project directory to correctly copy parity code base into the jsonrpc repo for doc generation.
|
||||
- Fix set_remote_wiki function call in CI
|
||||
- `gasleft` extern implemented for WASM runtime (kip-6) ([#9357](https://github.com/paritytech/parity-ethereum/pull/9357))
|
||||
- Wasm gasleft extern added
|
||||
- Wasm_gasleft_activation_transition -> kip4_transition
|
||||
- Use kip-6 switch
|
||||
- Gasleft_panic -> gasleft_fail rename
|
||||
- Call_msg_gasleft test added and gas_left agustments because this paritytech/wasm-tests#52
|
||||
- Change .. to _
|
||||
- Fix comment for the have_gasleft param
|
||||
- Update tests (paritytech/wasm-tests-0edbf86)
|
||||
- Block view! removal in progress ([#9397](https://github.com/paritytech/parity-ethereum/pull/9397))
|
||||
- Prevent sync restart if import queue full ([#9381](https://github.com/paritytech/parity-ethereum/pull/9381))
|
||||
- Nonroot CentOS Docker image ([#9280](https://github.com/paritytech/parity-ethereum/pull/9280))
|
||||
- Updates CentOS Docker image build process
|
||||
- Rename build.Dockerfile
|
||||
- Ethcore: kovan: delay activation of strict score validation ([#9406](https://github.com/paritytech/parity-ethereum/pull/9406))
|
||||
- Revert "Use std::sync::Condvar ([#1732](https://github.com/paritytech/parity-ethereum/pull/1732))" ([#9392](https://github.com/paritytech/parity-ethereum/pull/9392))
|
||||
- Revert "Use std::sync::Condvar ([#1732](https://github.com/paritytech/parity-ethereum/pull/1732))"
|
||||
- This reverts commit c65ee93.
|
||||
- Verification_queue: remove redundant mutexes
|
||||
- Replace `std::env::home_dir()` with `home` crate impl. ([#9293](https://github.com/paritytech/parity-ethereum/pull/9293))
|
||||
- Import the `home` crate in `util/dir`.
|
||||
- Replace uses of `env::home_dir()` with `home::home_dir()`.
|
||||
- `home` uses a 'correct' impl. on windows and the stdlib impl. of `::home_dir` otherwise.
|
||||
- Reexport `home::home_dir` from `util/dir`.
|
||||
- Bump `util/dir` to 0.1.2.
|
||||
- Docs: restore readme ([#9391](https://github.com/paritytech/parity-ethereum/pull/9391))
|
||||
- Replace `Duration::new()` w/ `Duration::from_nanos` ([#9387](https://github.com/paritytech/parity-ethereum/pull/9387))
|
||||
- Delete Dockerfile ([#9386](https://github.com/paritytech/parity-ethereum/pull/9386))
|
||||
- Network-devp2p `Fix some clippy errors/warnings` ([#9378](https://github.com/paritytech/parity-ethereum/pull/9378))
|
||||
- Fix some clippy warnings
|
||||
- Remove `shallow-copy` of Node's
|
||||
- Make `NonReservedPeerMode` Copy and pass-by-value
|
||||
- Allow calling contracts in genesis state. ([#9375](https://github.com/paritytech/parity-ethereum/pull/9375))
|
||||
- Make `Capabilities struct` Copy ([#9372](https://github.com/paritytech/parity-ethereum/pull/9372))
|
||||
- Light client "Enable more logs for light client `on_demand`" ([#9374](https://github.com/paritytech/parity-ethereum/pull/9374))
|
||||
- Enable more logs for light client `on_demand`
|
||||
- Remove extra whitespace
|
||||
- Fix indentation
|
||||
- Better logging when mining own transactions. ([#9363](https://github.com/paritytech/parity-ethereum/pull/9363))
|
||||
- Fix typos in `network-devp2p` ([#9371](https://github.com/paritytech/parity-ethereum/pull/9371))
|
||||
- Light client `Provide default nonce in transactions when it´s missing` ([#9370](https://github.com/paritytech/parity-ethereum/pull/9370))
|
||||
- Provide `default_nonce` in tx`s when it´s missing
|
||||
- When `nonce` is missing in a `EthTransaction` will cause it to fall in these cases provide `default_nonce` value instead!
|
||||
- Changed http:// to https:// on Yasm link ([#9369](https://github.com/paritytech/parity-ethereum/pull/9369))
|
||||
- Changed http:// to https:// on Yasm link in README.md
|
||||
- Provide `default_nonce` in tx`s when it´s missing
|
||||
- When `nonce` is missing in a `EthTransaction` will cause it to fall in these cases provide `default_nonce` value instead!
|
||||
- Address grumbles
|
||||
- Changed http:// to https:// on Yasm link ([#9369](https://github.com/paritytech/parity-ethereum/pull/9369))
|
||||
- Changed http:// to https:// on Yasm link in README.md
|
||||
- Fix no line breaks in logs ([#9355](https://github.com/paritytech/parity-ethereum/pull/9355))
|
||||
- Lower the max size of transaction packet to prevent going oversize. ([#9308](https://github.com/paritytech/parity-ethereum/pull/9308))
|
||||
- Lower the max size of transaction packet to prevent going oversize.
|
||||
- Log RLP size.
|
||||
- Remove prepare_trace_output and make sure prepare_trace_call and trace*call are balanced ([#9353](https://github.com/paritytech/parity-ethereum/pull/9353))
|
||||
- This refactors `prepare_trace_output` to instead directly take the reference of return values, so that it's simpler and we save a stack item. This should also fixes [the issue]([#9236](https://github.com/paritytech/parity-ethereum/pull/9236) (comment)) udoprog is facing. Replaces [#9236](https://github.com/paritytech/parity-ethereum/issues/9236)
|
||||
- More details in logs returned by light client ([#9324](https://github.com/paritytech/parity-ethereum/pull/9324))
|
||||
- Log details for light logs.
|
||||
- Create Log directly.
|
||||
- Expose UnorderedIterator. ([#9347](https://github.com/paritytech/parity-ethereum/pull/9347))
|
||||
- Light client logs should include 'from_block' when querying logs ([#9331](https://github.com/paritytech/parity-ethereum/pull/9331))
|
||||
- Fix PubSub for logs when using light client:
|
||||
- Prior to this fix the pubsub process did send a query for each new block header (and for each subs: there is something to optimize here) by setting from and to of the filter at this block number; but there was a bug in the code that fetch logs : it was non inclusive for its start bound, meaning that with start bound = end bound we never query any block (and attached logs).
|
||||
- Option iter instead of once.
|
||||
- Use of bloom existing function to check if a bloom contains another.
|
||||
- Makes from block header checking explicit
|
||||
- Remove pass-by-reference return data value from executive ([#9211](https://github.com/paritytech/parity-ethereum/pull/9211))
|
||||
- Remove pass-by-reference return data value from executive
|
||||
- Fix tests
|
||||
- Fix a missing test output
|
||||
- Typo: wasm_activation_test
|
||||
- Tracing change in output
|
||||
- Json_tests: fix compile
|
||||
- Typo: 0..32 -> ..32 to keep it consistent with other occurance
|
||||
- Fix tests
|
||||
- Allow single opcode stepping for EVM ([#9051](https://github.com/paritytech/parity-ethereum/pull/9051))
|
||||
- Feed in ActionParams on VM creation
|
||||
- Fix ethcore after Vm interface change
|
||||
- Move informant inside Interpreter struct
|
||||
- Move do_trace to Interpreter struct
|
||||
- Move all remaining exec variables to Interpreter struct
|
||||
- Refactor VM to allow single opcode step
|
||||
- Fix all EVM tests
|
||||
- Fix all wasm tests
|
||||
- Fix wasm runner tests
|
||||
- Fix a check case where code length is zero
|
||||
- Fix jsontests compile
|
||||
- Fix cargo lock
|
||||
- Use match instead of expect
|
||||
- Use cheaper check reader.len() == 0 for the initial special case
|
||||
- Get rid of try_and_done! macro by using Result<(), ReturnType>
|
||||
- Use Never instead of ()
|
||||
- Fix parity-bytes path
|
||||
- Bypass gasometer lifetime problem by borrow only for a instance
|
||||
- Typo: missing {
|
||||
- Fix ethcore test compile
|
||||
- Fix evm tests
|
||||
- Fix load share ([#9321](https://github.com/paritytech/parity-ethereum/pull/9321))
|
||||
- Fix(light_sync): calculate `load_share` properly
|
||||
- Refactor(api.rs): extract `light_params` fn, add test
|
||||
- Style(api.rs): add trailing commas
|
||||
- Implement EIP234 block_hash for eth_getLogs ([#9256](https://github.com/paritytech/parity-ethereum/pull/9256))
|
||||
- Implement EIP234
|
||||
- Make filter conversion returns error if both blockHash and from/toBlock is found
|
||||
- This also changes PollFilter to store the EthFilter type, instead of the jsonrpc one, saving repeated conversion.
|
||||
- Return error if block filtering target is not found in eth_getLogs
|
||||
- Use the old behavior (unwrap_or_default) for anywhere else.
|
||||
- Fix test: secret_store
|
||||
- Fix weird indentation
|
||||
- Make client log filter return error in case a block cannot be found
|
||||
- Return blockId error in rpc
|
||||
- Test_client: allow return error on logs
|
||||
- Add a mocked test for eth_getLogs error
|
||||
- Fix: should return error if from_block/to_block greater than best block number
|
||||
- Add notes on pending
|
||||
- Add comment for UNSUPPORTED_REQUEST
|
||||
- Address grumbles
|
||||
- Return err if from > to
|
||||
- Ethcore: fix pow difficulty validation ([#9328](https://github.com/paritytech/parity-ethereum/pull/9328))
|
||||
- Ethcore: fix pow difficulty validation
|
||||
- Ethcore: validate difficulty is not zero
|
||||
- Ethcore: add issue link to regression test
|
||||
- Ethcore: fix tests
|
||||
- Ethcore: move difficulty_to_boundary to ethash crate
|
||||
- Ethcore: reuse difficulty_to_boundary and boundary_to_difficulty
|
||||
- Ethcore: fix grumbles in difficulty_to_boundary_aux
|
||||
- Ethcore/sync `Make view macro only visible to test` ([#9316](https://github.com/paritytech/parity-ethereum/pull/9316))
|
||||
- Remove needless macro import
|
||||
- Enable ethcore/macros in tests
|
||||
- Allow setting the panic hook with parity-clib ([#9292](https://github.com/paritytech/parity-ethereum/pull/9292))
|
||||
- Allow setting the panic hook with parity-clib
|
||||
- Make all FFI functions unsafe
|
||||
- Fix comment
|
||||
- Fix concern
|
||||
- Prevent blockchain & miner racing when accessing pending block. ([#9310](https://github.com/paritytech/parity-ethereum/pull/9310))
|
||||
- Prevent blockchain & miner racing when accessing pending block.
|
||||
- Fix unavailability of pending block during reseal.
|
||||
- Docker alpine: use multi-stage concept ([#9269](https://github.com/paritytech/parity-ethereum/pull/9269))
|
||||
- Docker alpine: use multi-stage concept
|
||||
- Docker alpine: create config directory
|
||||
- Update `log` -> 0.4, `env_logger` -> 0.5. ([#9294](https://github.com/paritytech/parity-ethereum/pull/9294))
|
||||
- Rename a few types & methods.
|
||||
- Change `(Log)Builder::format` (closure) arg.
|
||||
- Update tobalaba.json ([#9313](https://github.com/paritytech/parity-ethereum/pull/9313))
|
||||
- Allow tx pool to be Send ([#9315](https://github.com/paritytech/parity-ethereum/pull/9315))
|
||||
- Fix codecov.io badge in README ([#9327](https://github.com/paritytech/parity-ethereum/pull/9327))
|
||||
- Move ethereum-specific H256FastMap type to own crate ([#9307](https://github.com/paritytech/parity-ethereum/pull/9307))
|
||||
- Add a `fastmap` crate that provides the H256FastMap specialized HashMap
|
||||
- Use `fastmap` instead of `plain_hasher`
|
||||
- Update submodules for Reasons™
|
||||
- Submodule update
|
||||
- Ethcore sync decodes rlp less often ([#9264](https://github.com/paritytech/parity-ethereum/pull/9264))
|
||||
- Deserialize block only once during verification
|
||||
- Ethcore-sync uses Unverified
|
||||
- Ethcore-sync uses Unverified
|
||||
- Fixed build error
|
||||
- Removed Block::is_good
|
||||
- Applied review suggestions
|
||||
- Ethcore-sync deserializes headers and blocks only once
|
||||
- Ethcore: add transition flag for transaction permission contract ([#9275](https://github.com/paritytech/parity-ethereum/pull/9275))
|
||||
- Ethcore: add transition flag for transaction permission contract
|
||||
- Ethcore: fix transaction permission contract tests
|
||||
- Remove all dapp permissions related settings ([#9120](https://github.com/paritytech/parity-ethereum/pull/9120))
|
||||
- Completely remove all dapps struct from rpc
|
||||
- Remove unused pub use
|
||||
- Remove dapp policy/permission func in ethcore
|
||||
- Remove all dapps settings from rpc
|
||||
- Fix rpc tests
|
||||
- Use both origin and user_agent
|
||||
- Address grumbles
|
||||
- Address grumbles
|
||||
- Fix tests
|
||||
- Improve return data truncate logic ([#9254](https://github.com/paritytech/parity-ethereum/pull/9254))
|
||||
- Improve return data truncate logic
|
||||
- Fix: size -> offset + size
|
||||
- Update wasm-tests hash ([#9295](https://github.com/paritytech/parity-ethereum/pull/9295))
|
||||
- Implement KIP4: create2 for wasm ([#9277](https://github.com/paritytech/parity-ethereum/pull/9277))
|
||||
- Basic implementation for kip4
|
||||
- Add KIP-4 config flags
|
||||
- Typo: docs fix
|
||||
- Fix args offset
|
||||
- Add tests for create2
|
||||
- Tests: evm
|
||||
- Update wasm-tests and fix all gas costs
|
||||
- Update wasm-tests
|
||||
- Update wasm-tests and fix gas costs
|
||||
- Fix loop start value ([#9285](https://github.com/paritytech/parity-ethereum/pull/9285))
|
||||
- Avoid using $HOME if not necessary ([#9273](https://github.com/paritytech/parity-ethereum/pull/9273))
|
||||
- Avoid using $HOME if not necessary
|
||||
- Fix concerns and issues
|
||||
- Fix path to parity.h ([#9274](https://github.com/paritytech/parity-ethereum/pull/9274))
|
||||
- Fix path to parity.h
|
||||
- Fix other paths as well
|
||||
- Propagate transactions for next 4 blocks. ([#9265](https://github.com/paritytech/parity-ethereum/pull/9265))
|
||||
- Closes [#9255](https://github.com/paritytech/parity-ethereum/issues/9255)
|
||||
- This PR also removes the limit of max 64 transactions per packet, currently we only attempt to prevent the packet size to go over 8MB. This will only be the case for super-large transactions or high-block-gas-limit chains. Patching this is important only for chains that have blocks that can fit more than 4k transactions (over 86M block gas limit) For mainnet, we should actually see a tiny bit faster propagation since instead of computing 4k pending set, we only need `4 * 8M / 21k = 1523` transactions. Running some tests on `dekompile` node right now, to check how it performs in the wild.
|
||||
- Decode block rlp less often ([#9252](https://github.com/paritytech/parity-ethereum/pull/9252))
|
||||
- Removed 4 redundant rlp deserializations
|
||||
- Avoid 1 redundant block data copy
|
||||
- Fix eternalities tests can_create (missing parameter) ([#9270](https://github.com/paritytech/parity-ethereum/pull/9270))
|
||||
- Update ref to `parity-common` and update `seek` behaviour ([#9257](https://github.com/paritytech/parity-ethereum/pull/9257))
|
||||
- Update ref to `parity-common` and update `seek` behaviour
|
||||
- Remove reference to `ng-fix-triedb-seek` branch
|
||||
- Comply EIP-86 with the new definition ([#9140](https://github.com/paritytech/parity-ethereum/pull/9140))
|
||||
- Comply EIP-86 with the new CREATE2 opcode
|
||||
- Fix rpc compile
|
||||
- Fix interpreter CREATE/CREATE2 stack pop difference
|
||||
- Add unreachable! to fix compile
|
||||
- Fix instruction_info
|
||||
- Fix gas check due to new stack item
|
||||
- Add new tests in executive
|
||||
- Fix have_create2 comment
|
||||
- Remove all unused references of eip86_transition and block_number
|
||||
- Check if synced when using eth_getWork ([#9193](https://github.com/paritytech/parity-ethereum/pull/9193)) ([#9210](https://github.com/paritytech/parity-ethereum/pull/9210))
|
||||
- Check if synced when using eth_getWork ([#9193](https://github.com/paritytech/parity-ethereum/pull/9193))
|
||||
- Don't use fn syncing
|
||||
- Fix identation
|
||||
- Fix typo
|
||||
- Don't check for warping
|
||||
- Rpc: avoid calling queue_info twice on eth_getWork
|
||||
- Removed client error ([#9253](https://github.com/paritytech/parity-ethereum/pull/9253))
|
||||
- Implement EIP-1052 (EXTCODEHASH) and fix several issues in state account cache ([#9234](https://github.com/paritytech/parity-ethereum/pull/9234))
|
||||
- Implement EIP-1052 and fix several issues related to account cache
|
||||
- Fix jsontests
|
||||
- Merge two matches together
|
||||
- Avoid making unnecessary Arc<Vec>
|
||||
- Address grumbles
|
||||
- Improve Tracer documentation ([#9237](https://github.com/paritytech/parity-ethereum/pull/9237))
|
||||
- Update Dockerfile ([#9242](https://github.com/paritytech/parity-ethereum/pull/9242))
|
||||
- Update Dockerfile
|
||||
- Fix Docker build
|
||||
- Fix dockerfile paths: parity -> parity-ethereum ([#9248](https://github.com/paritytech/parity-ethereum/pull/9248))
|
||||
- Block cleanup ([#9117](https://github.com/paritytech/parity-ethereum/pull/9117))
|
||||
- Blockchain insert expects owned block instead of block reference
|
||||
- Reduce a number of times a block is deserialized
|
||||
- Removed cached uncle_bytes from block
|
||||
- Removed is_finalized from OpenBlock
|
||||
- Removed unused parity_machine::WithMetadata trait
|
||||
- Removed commented out code
|
||||
- Remove unused metadata from block
|
||||
- Remove unused metadata from block
|
||||
- Blockdetails extras may have at most 5 elements
|
||||
- Increase the number of sessions. ([#9203](https://github.com/paritytech/parity-ethereum/pull/9203))
|
||||
- Add changelog for 1.11.8 stable and 2.0.1 beta ([#9230](https://github.com/paritytech/parity-ethereum/pull/9230))
|
||||
- Docs: add changelog for 1.11.8 stable
|
||||
- Docs: add changelog for 2.0.1 beta
|
||||
- Fix typo ([#9232](https://github.com/paritytech/parity-ethereum/pull/9232))
|
||||
- Fix potential as_usize overflow when casting from U256 in miner ([#9221](https://github.com/paritytech/parity-ethereum/pull/9221))
|
||||
- Allow old blocks from peers with lower difficulty ([#9226](https://github.com/paritytech/parity-ethereum/pull/9226))
|
||||
- Previously we only allow downloading of old blocks if the peer difficulty was greater than our syncing difficulty. This change allows downloading of blocks from peers where the difficulty is greater then the last downloaded old block.
|
||||
- Removes duplicate libudev-dev from Dockerfile ([#9220](https://github.com/paritytech/parity-ethereum/pull/9220))
|
||||
- Snap: remove ssl dependencies from snapcraft definition ([#9222](https://github.com/paritytech/parity-ethereum/pull/9222))
|
||||
- Remove ssl from dockerfiles, closes [#8880](https://github.com/paritytech/parity-ethereum/issues/8880) ([#9195](https://github.com/paritytech/parity-ethereum/pull/9195))
|
||||
- Insert PROOF messages for some cases in blockchain ([#9141](https://github.com/paritytech/parity-ethereum/pull/9141))
|
||||
- Insert PROOF messages for some cases in blockchain
|
||||
- Break expect to its own line to avoid things being too long
|
||||
- Be more specific for all low-level database error cases
|
||||
- Fix BranchBecomingCanonChain expect
|
||||
- Ethcore: fix typo in expect proof message
|
||||
- [chain] Add more bootnodes ([#9174](https://github.com/paritytech/parity-ethereum/pull/9174))
|
||||
- For ETC, ELLA, EXP, Morden, MUSIC
|
||||
- Ethcore: update bn version ([#9217](https://github.com/paritytech/parity-ethereum/pull/9217))
|
||||
- Deserialize block only once during verification ([#9161](https://github.com/paritytech/parity-ethereum/pull/9161))
|
||||
- Simple build instruction fix ([#9215](https://github.com/paritytech/parity-ethereum/pull/9215))
|
||||
- Changed `parity` dir name into `parity-ethereum`
|
||||
- Added --tx-queue-no-early-reject flag to disable early tx queue rejects ([#9143](https://github.com/paritytech/parity-ethereum/pull/9143))
|
||||
- Added --tx-queue-no-early-reject flag to disable early tx queue rejects because of low gas price
|
||||
- Fixed failing tests, clarified comments and simplified no_early_reject field name.
|
||||
- Added test case for the --tx-queue-no-early-reject flag
|
||||
- Avoid schedule copying in nested call/create ([#9190](https://github.com/paritytech/parity-ethereum/pull/9190))
|
||||
- Avoid schedule copying in nested call/create
|
||||
- Fix tests
|
||||
- Fix test: wrong Schedule used
|
||||
- Fix private-tx test
|
||||
- Fix jsontests compilation
|
||||
- Ethcore: add builtin benchmarks based on geth ([#9179](https://github.com/paritytech/parity-ethereum/pull/9179))
|
||||
- Ethcore: add geth benchmarks for all builtins
|
||||
- Ethcore: remove old builtin benchmarks
|
||||
- Rpc: fix is_major_importing sync state condition ([#9112](https://github.com/paritytech/parity-ethereum/pull/9112))
|
||||
- Rpc: fix is_major_importing sync state condition
|
||||
- Rpc: fix informant printout when waiting for peers
|
||||
- Docs: update repository links ([#9159](https://github.com/paritytech/parity-ethereum/pull/9159))
|
||||
- Docs: update repository links
|
||||
- Docs: update repository links in contribution guide
|
||||
- Parity: fix UserDefaults json parser ([#9189](https://github.com/paritytech/parity-ethereum/pull/9189))
|
||||
- Parity: fix UserDefaults json parser
|
||||
- Parity: use serde_derive for UserDefaults
|
||||
- Parity: support deserialization of old UserDefault json format
|
||||
- Parity: make UserDefaults serde backwards compatible
|
||||
- Parity: tabify indentation in UserDefaults
|
||||
- Update "This is a bug. Please report it at:" link ([#9191](https://github.com/paritytech/parity-ethereum/pull/9191))
|
||||
- Docker: update hub dockerfile ([#9173](https://github.com/paritytech/parity-ethereum/pull/9173))
|
||||
- Update Dockerfile for hub
|
||||
- Update to Ubuntu Xenial 16.04
|
||||
- Fix cmake version
|
||||
- Docker: fix tab indentation in hub dockerfile
|
||||
- Ci: update version strings for snaps ([#9160](https://github.com/paritytech/parity-ethereum/pull/9160))
|
||||
- Ethcore: add missing builtins benchmarks ([#9170](https://github.com/paritytech/parity-ethereum/pull/9170))
|
||||
- Ethcore: add modexp benchmarks
|
||||
- Ethcore: add_bn_128_add benchmark
|
||||
- Fix bugfix hard fork logic ([#9138](https://github.com/paritytech/parity-ethereum/pull/9138))
|
||||
- Fix bugfix hard fork logic
|
||||
- Remove dustProtectionTransition from bugfix category
|
||||
- Eip-168 is not enabled by default
|
||||
- Remove unnecessary 'static
|
||||
- Be more graceful on Aura difficulty validation ([#9164](https://github.com/paritytech/parity-ethereum/pull/9164))
|
||||
- Be more graceful on Aura difficulty validation
|
||||
- Test: rejects_step_backwards
|
||||
- Test: proposer_switching
|
||||
- Test: rejects_future_block
|
||||
- Test: reports_skipped
|
||||
- Test: verify_empty_seal_steps
|
||||
- Handle SyncHandler errors properly ([#9151](https://github.com/paritytech/parity-ethereum/pull/9151))
|
||||
- Handle SyncHandler errors properly, closes [#9150](https://github.com/paritytech/parity-ethereum/issues/9150)
|
||||
- Applied review suggestions
|
||||
- Remove node-health ([#9119](https://github.com/paritytech/parity-ethereum/pull/9119))
|
||||
- Remove node-health
|
||||
- Remove ntp_servers
|
||||
- Add --ntp-servers as legacy instead of removing it
|
||||
- Add --ntp-servers to deprecated args
|
||||
- Remove unused stuff
|
||||
- Remove _legacy_ntp_servers
|
||||
- Remove unused tx_queue_gas parameter. ([#9153](https://github.com/paritytech/parity-ethereum/pull/9153))
|
||||
- Changelogs for 1.11.7-stable and 2.0.0-beta ([#9105](https://github.com/paritytech/parity-ethereum/pull/9105))
|
||||
- Docs: mark 1.10 as end-of-life
|
||||
- Docs: move changelog for 1.11
|
||||
- Docs: Add changelog for 1.11.7-stable
|
||||
- Docs: add changelog for 2.0.0-beta
|
||||
- Docs: add release notes for 2.0.0 beta
|
||||
- Docs: fix links in changelog
|
||||
- Docs: Update changelog for 1.11.7-stable
|
||||
- Docs: Update changelog for 2.0.0-beta
|
||||
- Docs: address Tbaut's comments for the 2.0.0-beta changelog
|
||||
- Docs: add note regarding txqueue changes as recommended by tomusdrw
|
||||
- Disable per-sender limit for local transactions. ([#9148](https://github.com/paritytech/parity-ethereum/pull/9148))
|
||||
- Disable per-sender limit for local transactions.
|
||||
- Add a missing new line.
|
||||
- Parity: fix logging cli parameter example ([#9154](https://github.com/paritytech/parity-ethereum/pull/9154))
|
||||
- Be more specific for `-l` CLI arguments ([#9149](https://github.com/paritytech/parity-ethereum/pull/9149))
|
||||
- Update mod.rs
|
||||
- Receipt constructor `Allocate less stack for blooms` ([#9146](https://github.com/paritytech/parity-ethereum/pull/9146))
|
||||
- Allocate less stack in `Receipt ctor`
|
||||
- Ethcore: use accrue_bloom when computing transaction receipt
|
||||
- `evm bench` fix broken dependencies ([#9134](https://github.com/paritytech/parity-ethereum/pull/9134))
|
||||
- `evm bench` use valid dependencies
|
||||
- Benchmarks of the `evm` used stale versions of a couple a crates that this commit fixes!
|
||||
- Fix warnings
|
||||
- Update snapcraft.yaml ([#9132](https://github.com/paritytech/parity-ethereum/pull/9132))
|
||||
- Fix verification in ethcore-sync collect_blocks ([#9135](https://github.com/paritytech/parity-ethereum/pull/9135))
|
||||
- Unify engine error to reject blocks ([#9085](https://github.com/paritytech/parity-ethereum/pull/9085))
|
||||
- Reject if Engine::on_close_block returns error
|
||||
- Unify open block behaviors
|
||||
- Fix tests in ethcore
|
||||
- Fix Aura tests
|
||||
- Fix RPC test
|
||||
- Print a warning if open block failed
|
||||
- Print the actual error when closing the block
|
||||
- Update comments for prepare_pending_block
|
||||
- Add BlockPreparationStatus to distingish three different state after prepare_pending_block
|
||||
- Fix `todo` in `ethcore/types::Receipt` constructor ([#9086](https://github.com/paritytech/parity-ethereum/pull/9086))
|
||||
- Remove needless mutable variable and assignment
|
||||
- Completely remove all dapps struct from rpc ([#9107](https://github.com/paritytech/parity-ethereum/pull/9107))
|
||||
- Completely remove all dapps struct from rpc
|
||||
- Remove unused pub use
|
||||
- Removed redundant struct bounds and unnecessary data copying ([#9096](https://github.com/paritytech/parity-ethereum/pull/9096))
|
||||
- Removed redundant struct bounds and unnecessary data copying
|
||||
- Updated docs, removed redundant bindings
|
||||
- Insert ETC (classic) hardcoded headers until block 6170625 ([#9121](https://github.com/paritytech/parity-ethereum/pull/9121))
|
||||
- Make sure to produce full blocks. ([#9115](https://github.com/paritytech/parity-ethereum/pull/9115))
|
||||
- Update light client hardcoded headers ([#9098](https://github.com/paritytech/parity-ethereum/pull/9098))
|
||||
- Insert Kovan hardcoded headers until 7690241
|
||||
- Insert Kovan hardcoded headers until block 7690241
|
||||
- Insert Ropsten hardcoded headers until 3612673
|
||||
- Insert Mainnet hardcoded headers until block 5941249
|
||||
- Parity-version: bump nightly version to 2.1 ([#9095](https://github.com/paritytech/parity-ethereum/pull/9095))
|
||||
- Fix work-notify. ([#9104](https://github.com/paritytech/parity-ethereum/pull/9104))
|
||||
- Update snappy ([#9082](https://github.com/paritytech/parity-ethereum/pull/9082))
|
||||
- Offload cull to IoWorker. ([#9099](https://github.com/paritytech/parity-ethereum/pull/9099))
|
||||
- Docker: add cmake dependency ([#9111](https://github.com/paritytech/parity-ethereum/pull/9111))
|
||||
- Update hidapi, fixes [#7542](https://github.com/paritytech/parity-ethereum/issues/7542) ([#9108](https://github.com/paritytech/parity-ethereum/pull/9108))
|
||||
- Update README.md ([#9084](https://github.com/paritytech/parity-ethereum/pull/9084))
|
||||
- Update README.md
|
||||
- Rename parity client
|
||||
- Docs: remove UI stuff from readme.
|
||||
- Docs: add changelog link to readme
|
||||
- Revert "Replace `std::env::home_dir` with `dirs::home_dir` ([#9077](https://github.com/paritytech/parity-ethereum/pull/9077))" ([#9097](https://github.com/paritytech/parity-ethereum/pull/9097))
|
||||
- Revert "Replace `std::env::home_dir` with `dirs::home_dir` ([#9077](https://github.com/paritytech/parity-ethereum/pull/9077))"
|
||||
- This reverts commit 7e77932.
|
||||
- Restore some of the changes
|
||||
- Update parity-common
|
||||
- Multiple improvements to discovery ping handling ([#8771](https://github.com/paritytech/parity-ethereum/pull/8771))
|
||||
- Discovery: Only add nodes to routing table after receiving pong.
|
||||
- Previously the discovery algorithm would add nodes to the routing table before confirming that the endpoint is participating in the protocol. This now tracks in-flight pings and adds to the routing table only after receiving a response.
|
||||
- Discovery: Refactor packet creation into its own function.
|
||||
- This function is useful inside unit tests.
|
||||
- Discovery: Additional testing for new add_node behavior.
|
||||
- Discovery: Track expiration of pings to non-yet-in-bucket nodes.
|
||||
- Now that we may ping nodes before adding to a k-bucket, the timeout tracking must be separate from BucketEntry.
|
||||
- Discovery: Verify echo hash on pong packets.
|
||||
- Stores packet hash with in-flight requests and matches with pong response.
|
||||
- Discovery: Track timeouts on FIND_NODE requests.
|
||||
- Discovery: Retry failed pings with exponential backoff.
|
||||
- Udp packets may get dropped, so instead of immediately booting nodes that fail to respond to a ping, retry 4 times with exponential backoff.
|
||||
- !fixup Use slice instead of Vec for request_backoff.
|
||||
- Add separate database directory for light client ([#8927](https://github.com/paritytech/parity-ethereum/pull/8927)) ([#9064](https://github.com/paritytech/parity-ethereum/pull/9064))
|
||||
- Add seperate default DB path for light client ([#8927](https://github.com/paritytech/parity-ethereum/pull/8927))
|
||||
- Improve readability
|
||||
|
||||
## Previous releases
|
||||
|
||||
- [CHANGELOG-2.0](docs/CHANGELOG-2.0.md) (_stable_)
|
||||
- [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)
|
||||
|
||||
5733
Cargo.lock
generated
5733
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
95
Cargo.toml
95
Cargo.toml
@@ -1,19 +1,17 @@
|
||||
[package]
|
||||
description = "OpenEthereum"
|
||||
name = "openethereum"
|
||||
description = "Parity Ethereum client"
|
||||
name = "parity-ethereum"
|
||||
# NOTE Make sure to update util/version/Cargo.toml as well
|
||||
version = "3.1.0"
|
||||
version = "2.2.7"
|
||||
license = "GPL-3.0"
|
||||
authors = [
|
||||
"OpenEthereum developers",
|
||||
"Parity Technologies <admin@parity.io>"
|
||||
]
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
blooms-db = { path = "util/blooms-db" }
|
||||
log = "0.4"
|
||||
env_logger = "0.5"
|
||||
rustc-hex = "1.0"
|
||||
docopt = "1.0"
|
||||
docopt = "0.8"
|
||||
clap = "2"
|
||||
term_size = "0.3"
|
||||
textwrap = "0.9"
|
||||
@@ -22,57 +20,55 @@ number_prefix = "0.2"
|
||||
rpassword = "1.0"
|
||||
semver = "0.9"
|
||||
ansi_term = "0.10"
|
||||
parking_lot = "0.7"
|
||||
regex = "1.0"
|
||||
parking_lot = "0.6"
|
||||
regex = "0.2"
|
||||
atty = "0.2.8"
|
||||
toml = "0.4"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
serde_derive = "1.0"
|
||||
futures = "0.1"
|
||||
hyper = { version = "0.12" }
|
||||
fdlimit = "0.1"
|
||||
ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" }
|
||||
jsonrpc-core = "15.0.0"
|
||||
parity-bytes = "0.1"
|
||||
common-types = { path = "ethcore/types" }
|
||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" }
|
||||
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" }
|
||||
parity-bytes = "0.1"
|
||||
ethcore-io = { path = "util/io" }
|
||||
ethcore-logger = { path = "parity/logger" }
|
||||
ethcore-light = { path = "ethcore/light" }
|
||||
ethcore-logger = { path = "logger" }
|
||||
ethcore-miner = { path = "miner" }
|
||||
ethcore-network = { path = "util/network" }
|
||||
ethcore-private-tx = { path = "ethcore/private-tx" }
|
||||
ethcore-service = { path = "ethcore/service" }
|
||||
ethcore-sync = { path = "ethcore/sync" }
|
||||
ethcore-transaction = { path = "ethcore/transaction" }
|
||||
ethereum-types = "0.4"
|
||||
ethkey = { path = "accounts/ethkey" }
|
||||
ethstore = { path = "accounts/ethstore" }
|
||||
fetch = { path = "util/fetch" }
|
||||
node-filter = { path = "ethcore/node-filter" }
|
||||
node-filter = { path = "ethcore/node_filter" }
|
||||
ethkey = { path = "ethkey" }
|
||||
rlp = { version = "0.3.0", features = ["ethereum"] }
|
||||
cli-signer= { path = "cli-signer" }
|
||||
parity-daemonize = "0.3"
|
||||
parity-local-store = { path = "miner/local-store" }
|
||||
rpc-cli = { path = "rpc_cli" }
|
||||
parity-hash-fetch = { path = "hash-fetch" }
|
||||
parity-ipfs-api = { path = "ipfs" }
|
||||
parity-local-store = { path = "local-store" }
|
||||
parity-runtime = { path = "util/runtime" }
|
||||
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"
|
||||
dir = { path = "util/dir" }
|
||||
panic_hook = { path = "util/panic-hook" }
|
||||
panic_hook = { path = "util/panic_hook" }
|
||||
keccak-hash = "0.1"
|
||||
migration-rocksdb = { path = "util/migration-rocksdb" }
|
||||
kvdb = "0.1"
|
||||
kvdb-rocksdb = "0.1.3"
|
||||
journaldb = { path = "util/journaldb" }
|
||||
stats = { path = "util/stats" }
|
||||
prometheus = "0.9.0"
|
||||
mem = { path = "util/mem" }
|
||||
|
||||
ethcore-secretstore = { path = "secret-store", optional = true }
|
||||
ethcore-secretstore = { path = "secret_store", optional = true }
|
||||
|
||||
registrar = { path = "util/registrar" }
|
||||
registrar = { path = "registrar" }
|
||||
|
||||
[build-dependencies]
|
||||
rustc_version = "0.2"
|
||||
@@ -82,22 +78,22 @@ 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"] }
|
||||
|
||||
[target.'cfg(not(windows))'.dependencies]
|
||||
daemonize = "0.3"
|
||||
|
||||
[features]
|
||||
default = ["accounts"]
|
||||
accounts = ["ethcore-accounts", "parity-rpc/accounts"]
|
||||
miner-debug = ["ethcore/miner-debug"]
|
||||
json-tests = ["ethcore/json-tests"]
|
||||
ci-skip-tests = ["ethcore/ci-skip-tests"]
|
||||
ci-skip-issue = ["ethcore/ci-skip-issue"]
|
||||
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.
|
||||
@@ -107,38 +103,37 @@ deadlock_detection = ["parking_lot/deadlock_detection"]
|
||||
# `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 = "openethereum"
|
||||
name = "parity"
|
||||
|
||||
[profile.test]
|
||||
lto = false
|
||||
opt-level = 3 # makes tests slower to compile, but faster to run
|
||||
[profile.dev]
|
||||
|
||||
[profile.release]
|
||||
debug = false
|
||||
lto = true
|
||||
|
||||
[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",
|
||||
"ethcore/wasm/run",
|
||||
"ethcore/types",
|
||||
"ethkey/cli",
|
||||
"ethstore/cli",
|
||||
"evmbin",
|
||||
"miner",
|
||||
"parity-clib",
|
||||
"whisper",
|
||||
"whisper/cli",
|
||||
"util/triehash-ethereum",
|
||||
"util/keccak-hasher",
|
||||
"util/patricia-trie-ethereum",
|
||||
"util/fastmap",
|
||||
"util/time-utils"
|
||||
]
|
||||
|
||||
[patch.crates-io]
|
||||
heapsize = { git = "https://github.com/cheme/heapsize.git", branch = "ec-macfix" }
|
||||
|
||||
299
README.md
299
README.md
@@ -1,38 +1,13 @@
|
||||
# OpenEthereum
|
||||

|
||||
|
||||
Fast and feature-rich multi-network Ethereum client.
|
||||
## The fastest and most advanced Ethereum client.
|
||||
|
||||
[» Download the latest release «](https://github.com/openethereum/openethereum/releases/latest)
|
||||
<p align="center"><strong><a href="https://github.com/paritytech/parity-ethereum/releases/latest">» Download the latest release «</a></strong></p>
|
||||
|
||||
[![GPL licensed][license-badge]][license-url]
|
||||
[![Build Status][ci-badge]][ci-url]
|
||||
[![Discord chat][chat-badge]][chat-url]
|
||||
<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>
|
||||
|
||||
[license-badge]: https://img.shields.io/badge/license-GPL_v3-green.svg
|
||||
[license-url]: LICENSE
|
||||
[ci-badge]: https://github.com/openethereum/openethereum/workflows/Build%20and%20Test%20Suite/badge.svg
|
||||
[ci-url]: https://github.com/openethereum/openethereum/actions
|
||||
[chat-badge]: https://img.shields.io/discord/669192218728202270.svg?logo=discord
|
||||
[chat-url]: https://discord.io/openethereum
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Description](#chapter-001)
|
||||
2. [Technical Overview](#chapter-002)
|
||||
3. [Building](#chapter-003)<br>
|
||||
3.1 [Building Dependencies](#chapter-0031)<br>
|
||||
3.2 [Building from Source Code](#chapter-0032)<br>
|
||||
3.3 [Starting OpenEthereum](#chapter-0034)
|
||||
4. [Testing](#chapter-004)
|
||||
5. [Documentation](#chapter-005)
|
||||
6. [Toolchain](#chapter-006)
|
||||
7. [Contributing](#chapter-008)
|
||||
8. [License](#chapter-009)
|
||||
|
||||
|
||||
## 1. Description <a id="chapter-001"></a>
|
||||
|
||||
**Built for mission-critical use**: Miners, service providers, and exchanges need fast synchronisation and maximum uptime. OpenEthereum provides the core infrastructure essential for speedy and reliable services.
|
||||
**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.
|
||||
|
||||
- Clean, modular codebase for easy customisation
|
||||
- Advanced CLI-based client
|
||||
@@ -40,21 +15,19 @@ Fast and feature-rich multi-network Ethereum client.
|
||||
- Synchronise in hours, not days with Warp Sync
|
||||
- Modular for light integration into your service or product
|
||||
|
||||
## 2. Technical Overview <a id="chapter-002"></a>
|
||||
## Technical Overview
|
||||
|
||||
OpenEthereum's goal is to be the fastest, lightest, and most secure Ethereum client. We are developing OpenEthereum using the **Rust programming language**. OpenEthereum is licensed under the GPLv3 and can be used for all your Ethereum needs.
|
||||
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, OpenEthereum 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.
|
||||
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.
|
||||
|
||||
If you run into problems while using OpenEthereum, check out the [old wiki for documentation](https://openethereum.github.io/wiki/), feel free to [file an issue in this repository](https://github.com/openethereum/openethereum/issues/new), or hop on our [Discord](https://discord.io/openethereum) chat room to ask a question. We are glad to help!
|
||||
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).
|
||||
|
||||
You can download OpenEthereum's latest release at [the releases page](https://github.com/openethereum/openethereum/releases) or follow the instructions below to build from source. Read the [CHANGELOG.md](CHANGELOG.md) for a list of all changes between different versions.
|
||||
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.
|
||||
|
||||
## 3. Building <a id="chapter-003"></a>
|
||||
## Build Dependencies
|
||||
|
||||
### 3.1 Build Dependencies <a id="chapter-0031"></a>
|
||||
|
||||
OpenEthereum requires **latest stable Rust version** to build.
|
||||
Parity Ethereum requires **Rust version 1.29.x** 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:
|
||||
|
||||
@@ -63,7 +36,7 @@ We recommend installing Rust through [rustup](https://www.rustup.rs/). If you do
|
||||
$ curl https://sh.rustup.rs -sSf | sh
|
||||
```
|
||||
|
||||
OpenEthereum also requires `clang` (>= 9.0), `clang++`, `pkg-config`, `file`, `make`, and `cmake` packages to be installed.
|
||||
Parity Ethereum also requires `gcc`, `g++`, `libudev-dev`, `pkg-config`, `file`, `make`, and `cmake` packages to be installed.
|
||||
|
||||
- OSX:
|
||||
```bash
|
||||
@@ -72,7 +45,7 @@ We recommend installing Rust through [rustup](https://www.rustup.rs/). If you do
|
||||
|
||||
`clang` is required. It comes with Xcode command line tools or can be installed with homebrew.
|
||||
|
||||
- Windows:
|
||||
- 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:
|
||||
```bash
|
||||
@@ -83,14 +56,14 @@ Once you have `rustup` installed, then you need to install:
|
||||
* [Perl](https://www.perl.org)
|
||||
* [Yasm](https://yasm.tortall.net)
|
||||
|
||||
Make sure that these binaries are in your `PATH`. After that, you should be able to build OpenEthereum from source.
|
||||
Make sure that these binaries are in your `PATH`. After that, you should be able to build Parity Ethereum from source.
|
||||
|
||||
### 3.2 Build from Source Code <a id="chapter-0032"></a>
|
||||
## Build from Source Code
|
||||
|
||||
```bash
|
||||
# download OpenEthereum code
|
||||
$ git clone https://github.com/openethereum/openethereum
|
||||
$ cd openethereum
|
||||
# download Parity Ethereum code
|
||||
$ git clone https://github.com/paritytech/parity-ethereum
|
||||
$ cd parity-ethereum
|
||||
|
||||
# build in release mode
|
||||
$ cargo build --release --features final
|
||||
@@ -110,209 +83,73 @@ Note, when compiling a crate and you receive errors, it's in most cases your out
|
||||
$ cargo clean
|
||||
```
|
||||
|
||||
This always compiles the latest nightly builds. If you want to build stable, do a
|
||||
This always compiles the latest nightly builds. If you want to build stable or beta, do a
|
||||
|
||||
```bash
|
||||
$ git checkout stable
|
||||
```
|
||||
|
||||
### 3.3 Starting OpenEthereum <a id="chapter-0034"></a>
|
||||
|
||||
#### Manually
|
||||
|
||||
To start OpenEthereum manually, just run
|
||||
or
|
||||
|
||||
```bash
|
||||
$ ./target/release/openethereum
|
||||
$ git checkout beta
|
||||
```
|
||||
|
||||
so OpenEthereum begins syncing the Ethereum blockchain.
|
||||
## Simple One-Line Installer for Mac and Linux
|
||||
|
||||
#### Using `systemd` service file
|
||||
```bash
|
||||
bash <(curl https://get.parity.io -L)
|
||||
```
|
||||
|
||||
To start OpenEthereum as a regular user using `systemd` init:
|
||||
The one-line installer always defaults to the latest beta release. To install a stable release, run:
|
||||
|
||||
1. Copy `./scripts/openethereum.service` to your
|
||||
```bash
|
||||
bash <(curl https://get.parity.io -L) -r stable
|
||||
```
|
||||
|
||||
## Start Parity Ethereum
|
||||
|
||||
### Manually
|
||||
|
||||
To start Parity Ethereum manually, just run
|
||||
|
||||
```bash
|
||||
$ ./target/release/parity
|
||||
```
|
||||
|
||||
so Parity Ethereum begins syncing the Ethereum blockchain.
|
||||
|
||||
### Using `systemd` service file
|
||||
|
||||
To start Parity Ethereum 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/openethereum /usr/bin/openethereum`
|
||||
3. To configure OpenEthereum, see [our old wiki](https://openethereum.github.io/wiki/Configuring-OpenEthereum) for details.
|
||||
2. 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.
|
||||
|
||||
## 4. Testing <a id="chapter-004"></a>
|
||||
## Parity Ethereum toolchain
|
||||
|
||||
Download the required test files: `git submodule update --init --recursive`. You can run tests with the following commands:
|
||||
In addition to the Parity Ethereum client, there are additional tools in this repository available:
|
||||
|
||||
* **All** packages
|
||||
```
|
||||
cargo test --all
|
||||
```
|
||||
- [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/ethstore/) - Parity Ethereum key management.
|
||||
- [ethkey](https://github.com/paritytech/parity-ethereum/blob/master/ethkey/) - Parity Ethereum keys generator.
|
||||
- [whisper](https://github.com/paritytech/parity-ethereum/blob/master/whisper/) - Implementation of Whisper-v2 PoC.
|
||||
|
||||
* Specific package
|
||||
```
|
||||
cargo test --package <spec>
|
||||
```
|
||||
## Join the chat!
|
||||
|
||||
Replace `<spec>` with one of the packages from the [package list](#package-list) (e.g. `cargo test --package evmbin`).
|
||||
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)
|
||||
|
||||
You can show your logs in the test output by passing `--nocapture` (i.e. `cargo test --package evmbin -- --nocapture`)
|
||||
Alternatively, join our community on Matrix:
|
||||
[](https://riot.im/app/#/group/+parity:matrix.parity.io)
|
||||
|
||||
## 5. Documentation <a id="chapter-005"></a>
|
||||
## Documentation
|
||||
|
||||
Be sure to [check out our old wiki](https://openethereum.github.io/wiki/) for more information.
|
||||
Official website: https://parity.io
|
||||
|
||||
### Viewing documentation for OpenEthereum packages
|
||||
|
||||
You can generate documentation for OpenEthereum Rust packages that automatically opens in your web browser using [rustdoc with Cargo](https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html#using-rustdoc-with-cargo) (of the The Rustdoc Book), by running the the following commands:
|
||||
|
||||
* **All** packages
|
||||
```
|
||||
cargo doc --document-private-items --open
|
||||
```
|
||||
|
||||
* Specific package
|
||||
```
|
||||
cargo doc --package <spec> -- --document-private-items --open
|
||||
```
|
||||
|
||||
Use`--document-private-items` to also view private documentation and `--no-deps` to exclude building documentation for dependencies.
|
||||
|
||||
Replacing `<spec>` with one of the following from the details section below (i.e. `cargo doc --package openethereum --open`):
|
||||
|
||||
<a id="package-list"></a>
|
||||
**Package List**
|
||||
<details><p>
|
||||
|
||||
* OpenEthereum Client Application
|
||||
```bash
|
||||
openethereum
|
||||
```
|
||||
* OpenEthereum Account Management, Key Management Tool, and Keys Generator
|
||||
```bash
|
||||
ethcore-accounts, ethkey-cli, ethstore, ethstore-cli
|
||||
```
|
||||
* OpenEthereum Chain Specification
|
||||
```bash
|
||||
chainspec
|
||||
```
|
||||
* OpenEthereum CLI Signer Tool & RPC Client
|
||||
```bash
|
||||
cli-signer parity-rpc-client
|
||||
```
|
||||
* OpenEthereum Ethash & ProgPoW Implementations
|
||||
```bash
|
||||
ethash
|
||||
```
|
||||
* EthCore Library
|
||||
```bash
|
||||
ethcore
|
||||
```
|
||||
* OpenEthereum Blockchain Database, Test Generator, Configuration,
|
||||
Caching, Importing Blocks, and Block Information
|
||||
```bash
|
||||
ethcore-blockchain
|
||||
```
|
||||
* OpenEthereum Contract Calls and Blockchain Service & Registry Information
|
||||
```bash
|
||||
ethcore-call-contract
|
||||
```
|
||||
* OpenEthereum Database Access & Utilities, Database Cache Manager
|
||||
```bash
|
||||
ethcore-db
|
||||
```
|
||||
* OpenEthereum Virtual Machine (EVM) Rust Implementation
|
||||
```bash
|
||||
evm
|
||||
```
|
||||
* OpenEthereum Light Client Implementation
|
||||
```bash
|
||||
ethcore-light
|
||||
```
|
||||
* Smart Contract based Node Filter, Manage Permissions of Network Connections
|
||||
```bash
|
||||
node-filter
|
||||
```
|
||||
* OpenEthereum Client & Network Service Creation & Registration with the I/O Subsystem
|
||||
```bash
|
||||
ethcore-service
|
||||
```
|
||||
* OpenEthereum Blockchain Synchronization
|
||||
```bash
|
||||
ethcore-sync
|
||||
```
|
||||
* OpenEthereum Common Types
|
||||
```bash
|
||||
common-types
|
||||
```
|
||||
* OpenEthereum Virtual Machines (VM) Support Library
|
||||
```bash
|
||||
vm
|
||||
```
|
||||
* OpenEthereum WASM Interpreter
|
||||
```bash
|
||||
wasm
|
||||
```
|
||||
* OpenEthereum WASM Test Runner
|
||||
```bash
|
||||
pwasm-run-test
|
||||
```
|
||||
* OpenEthereum EVM Implementation
|
||||
```bash
|
||||
evmbin
|
||||
```
|
||||
* OpenEthereum JSON Deserialization
|
||||
```bash
|
||||
ethjson
|
||||
```
|
||||
* OpenEthereum State Machine Generalization for Consensus Engines
|
||||
```bash
|
||||
parity-machine
|
||||
```
|
||||
* OpenEthereum Miner Interface
|
||||
```bash
|
||||
ethcore-miner parity-local-store price-info ethcore-stratum using_queue
|
||||
```
|
||||
* OpenEthereum Logger Implementation
|
||||
```bash
|
||||
ethcore-logger
|
||||
```
|
||||
* OpenEthereum JSON-RPC Servers
|
||||
```bash
|
||||
parity-rpc
|
||||
```
|
||||
* OpenEthereum Updater Service
|
||||
```bash
|
||||
parity-updater parity-hash-fetch
|
||||
```
|
||||
* OpenEthereum Core Libraries (`util`)
|
||||
```bash
|
||||
accounts-bloom blooms-db dir eip-712 fake-fetch fastmap fetch ethcore-io
|
||||
journaldb keccak-hasher len-caching-lock memory-cache memzero
|
||||
migration-rocksdb ethcore-network ethcore-network-devp2p panic_hook
|
||||
patricia-trie-ethereum registrar rlp_compress stats
|
||||
time-utils triehash-ethereum unexpected parity-version
|
||||
```
|
||||
|
||||
</p></details>
|
||||
|
||||
## 6. Toolchain <a id="chapter-006"></a>
|
||||
|
||||
In addition to the OpenEthereum client, there are additional tools in this repository available:
|
||||
|
||||
- [evmbin](./evmbin) - OpenEthereum EVM Implementation.
|
||||
- [ethstore](./accounts/ethstore) - OpenEthereum Key Management.
|
||||
- [ethkey](./accounts/ethkey) - OpenEthereum Keys Generator.
|
||||
|
||||
The following tools are available in a separate repository:
|
||||
- [ethabi](https://github.com/openethereum/ethabi) - OpenEthereum Encoding of Function Calls. [Docs here](https://crates.io/crates/ethabi)
|
||||
- [whisper](https://github.com/openethereum/whisper) - OpenEthereum Whisper-v2 PoC Implementation.
|
||||
|
||||
## 7. Contributing <a id="chapter-007"></a>
|
||||
|
||||
An introduction has been provided in the ["So You Want to be a Core Developer" presentation slides by Hernando Castano](http://tiny.cc/contrib-to-parity-eth). Additional guidelines are provided in [CONTRIBUTING](./.github/CONTRIBUTING.md).
|
||||
|
||||
### Contributor Code of Conduct
|
||||
|
||||
[CODE_OF_CONDUCT](./.github/CODE_OF_CONDUCT.md)
|
||||
|
||||
## 8. License <a id="chapter-008"></a>
|
||||
|
||||
[LICENSE](./LICENSE)
|
||||
Be sure to [check out our wiki](https://wiki.parity.io) for more information.
|
||||
|
||||
80
SECURITY.md
Normal file
80
SECURITY.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# 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.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
```
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQENBFlyIAwBCACe0keNPjgYzZ1Oy/8t3zj/Qw9bHHqrzx7FWy8NbXnYBM19NqOZ
|
||||
DIP7Oe0DvCaf/uruBskCS0iVstHlEFQ2AYe0Ei0REt9lQdy61GylU/DEB3879IG+
|
||||
6FO0SnFeYeerv1/hFI2K6uv8v7PyyVDiiJSW0I1KIs2OBwJicTKmWxLAeQsRgx9G
|
||||
yRGalrVk4KP+6pWTA7k3DxmDZKZyfYV/Ej10NtuzmsemwDbv98HKeomp/kgFOfSy
|
||||
3AZjeCpctlsNqpjUuXa0/HudmH2WLxZ0fz8XeoRh8XM9UudNIecjrDqmAFrt/btQ
|
||||
/3guvlzhFCdhYPVGsUusKMECk/JG+Xx1/1ZjABEBAAG0LFBhcml0eSBTZWN1cml0
|
||||
eSBDb250YWN0IDxzZWN1cml0eUBwYXJpdHkuaW8+iQFUBBMBCAA+FiEE2uUVYCjP
|
||||
N6B8aTiDXQ8DAY0H3nMFAllyIAwCGwMFCQPCZwAFCwkIBwIGFQgJCgsCBBYCAwEC
|
||||
HgECF4AACgkQXQ8DAY0H3nM60wgAkS3A36Zc+upiaxU7tumcGv+an17j7gin0sif
|
||||
+0ELSjVfrXInM6ovai+NhUdcLkJ7tCrKS90fvlaELK5Sg9CXBWCTFccKN4A/B7ey
|
||||
rOg2NPXUecnyBB/XqQgKYH7ujYlOlqBDXMfz6z8Hj6WToxg9PPMGGomyMGh8AWxM
|
||||
3yRPFs5RKt0VKgN++5N00oly5Y8ri5pgCidDvCLYMGTVDHFKwkuc9w6BlWlu1R1e
|
||||
/hXFWUFAP1ffTAul3QwyKhjPn2iotCdxXjvt48KaU8DN4iL7aMBN/ZBKqGS7yRdF
|
||||
D/JbJyaaJ0ZRvFSTSXy/sWY3z1B5mtCPBxco8hqqNfRkCwuZ6LkBDQRZciAMAQgA
|
||||
8BP8xrwe12TOUTqL/Vrbxv/FLdhKh53J6TrPKvC2TEEKOrTNo5ahRq+XOS5E7G2N
|
||||
x3b+fq8gR9BzFcldAx0XWUtGs/Wv++ulaSNqTBxj13J3G3WGsUfMKxRgj//piCUD
|
||||
bCFLQfGZdKk0M1o9QkPVARwwmvCNiNB/l++xGqPtfc44H5jWj3GoGvL2MkShPzrN
|
||||
yN/bJ+m+R5gtFGdInqa5KXBuxxuW25eDKJ+LzjbgUgeC76wNcfOiQHTdMkcupjdO
|
||||
bbGFwo10hcbRAOcZEv6//Zrlmk/6nPxEd2hN20St2bSN0+FqfZ267mWEu3ejsgF8
|
||||
ArdCpv5h4fBvJyNwiTZwIQARAQABiQE8BBgBCAAmFiEE2uUVYCjPN6B8aTiDXQ8D
|
||||
AY0H3nMFAllyIAwCGwwFCQPCZwAACgkQXQ8DAY0H3nNisggAl4fqhRlA34wIb190
|
||||
sqXHVxiCuzPaqS6krE9xAa1+gncX485OtcJNqnjugHm2rFE48lv7oasviuPXuInE
|
||||
/OgVFnXYv9d/Xx2JUeDs+bFTLouCDRY2Unh7KJZasfqnMcCHWcxHx5FvRNZRssaB
|
||||
WTZVo6sizPurGUtbpYe4/OLFhadBqAE0EUmVRFEUMc1YTnu4eLaRBzoWN4d2UWwi
|
||||
LN25RSrVSke7LTSFbgn9ntQrQ2smXSR+cdNkkfRCjFcpUaecvFl9HwIqoyVbT4Ym
|
||||
0hbpbbX/cJdc91tKa+psa29uMeGL/cgL9fAu19yNFRyOTMxjZnvql1X/WE1pLmoP
|
||||
ETBD1Q==
|
||||
=K9Qw
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
```
|
||||
@@ -1,22 +0,0 @@
|
||||
[package]
|
||||
description = "OpenEthereum Account Management"
|
||||
homepage = "https://github.com/openethereum/openethereum"
|
||||
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"
|
||||
|
||||
[dev-dependencies]
|
||||
ethereum-types = "0.4"
|
||||
tempdir = "0.3"
|
||||
@@ -1,494 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate docopt;
|
||||
extern crate env_logger;
|
||||
extern crate ethkey;
|
||||
extern crate panic_hook;
|
||||
extern crate parity_wordlist;
|
||||
extern crate rustc_hex;
|
||||
extern crate serde;
|
||||
extern crate threadpool;
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
use std::{env, fmt, io, num::ParseIntError, process, sync};
|
||||
|
||||
use docopt::Docopt;
|
||||
use ethkey::{
|
||||
brain_recover, sign, verify_address, verify_public, Brain, BrainPrefix, Error as EthkeyError,
|
||||
Generator, KeyPair, Prefix, Random,
|
||||
};
|
||||
use rustc_hex::{FromHex, FromHexError};
|
||||
|
||||
const USAGE: &'static str = r#"
|
||||
Parity Ethereum keys generator.
|
||||
Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||
|
||||
Usage:
|
||||
ethkey info <secret-or-phrase> [options]
|
||||
ethkey generate random [options]
|
||||
ethkey generate prefix <prefix> [options]
|
||||
ethkey sign <secret> <message>
|
||||
ethkey verify public <public> <signature> <message>
|
||||
ethkey verify address <address> <signature> <message>
|
||||
ethkey recover <address> <known-phrase>
|
||||
ethkey [-h | --help]
|
||||
|
||||
Options:
|
||||
-h, --help Display this message and exit.
|
||||
-s, --secret Display only the secret key.
|
||||
-p, --public Display only the public key.
|
||||
-a, --address Display only the address.
|
||||
-b, --brain Use parity brain wallet algorithm. Not recommended.
|
||||
|
||||
Commands:
|
||||
info Display public key and address of the secret.
|
||||
generate random Generates new random Ethereum key.
|
||||
generate prefix Random generation, but address must start with a prefix ("vanity address").
|
||||
sign Sign message using a secret key.
|
||||
verify Verify signer of the signature by public key or address.
|
||||
recover Try to find brain phrase matching given address from partial phrase.
|
||||
"#;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Args {
|
||||
cmd_info: bool,
|
||||
cmd_generate: bool,
|
||||
cmd_random: bool,
|
||||
cmd_prefix: bool,
|
||||
cmd_sign: bool,
|
||||
cmd_verify: bool,
|
||||
cmd_public: bool,
|
||||
cmd_address: bool,
|
||||
cmd_recover: bool,
|
||||
arg_prefix: String,
|
||||
arg_secret: String,
|
||||
arg_secret_or_phrase: String,
|
||||
arg_known_phrase: String,
|
||||
arg_message: String,
|
||||
arg_public: String,
|
||||
arg_address: String,
|
||||
arg_signature: String,
|
||||
flag_secret: bool,
|
||||
flag_public: bool,
|
||||
flag_address: bool,
|
||||
flag_brain: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Error {
|
||||
Ethkey(EthkeyError),
|
||||
FromHex(FromHexError),
|
||||
ParseInt(ParseIntError),
|
||||
Docopt(docopt::Error),
|
||||
Io(io::Error),
|
||||
}
|
||||
|
||||
impl From<EthkeyError> for Error {
|
||||
fn from(err: EthkeyError) -> Self {
|
||||
Error::Ethkey(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FromHexError> for Error {
|
||||
fn from(err: FromHexError) -> Self {
|
||||
Error::FromHex(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseIntError> for Error {
|
||||
fn from(err: ParseIntError) -> Self {
|
||||
Error::ParseInt(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<docopt::Error> for Error {
|
||||
fn from(err: docopt::Error) -> Self {
|
||||
Error::Docopt(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(err: io::Error) -> Self {
|
||||
Error::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
Error::Ethkey(ref e) => write!(f, "{}", e),
|
||||
Error::FromHex(ref e) => write!(f, "{}", e),
|
||||
Error::ParseInt(ref e) => write!(f, "{}", e),
|
||||
Error::Docopt(ref e) => write!(f, "{}", e),
|
||||
Error::Io(ref e) => write!(f, "{}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum DisplayMode {
|
||||
KeyPair,
|
||||
Secret,
|
||||
Public,
|
||||
Address,
|
||||
}
|
||||
|
||||
impl DisplayMode {
|
||||
fn new(args: &Args) -> Self {
|
||||
if args.flag_secret {
|
||||
DisplayMode::Secret
|
||||
} else if args.flag_public {
|
||||
DisplayMode::Public
|
||||
} else if args.flag_address {
|
||||
DisplayMode::Address
|
||||
} else {
|
||||
DisplayMode::KeyPair
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
panic_hook::set_abort();
|
||||
env_logger::try_init().expect("Logger initialized only once.");
|
||||
|
||||
match execute(env::args()) {
|
||||
Ok(ok) => println!("{}", ok),
|
||||
Err(Error::Docopt(ref e)) => e.exit(),
|
||||
Err(err) => {
|
||||
eprintln!("{}", err);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn display(result: (KeyPair, Option<String>), mode: DisplayMode) -> String {
|
||||
let keypair = result.0;
|
||||
match mode {
|
||||
DisplayMode::KeyPair => match result.1 {
|
||||
Some(extra_data) => format!("{}\n{}", extra_data, keypair),
|
||||
None => format!("{}", keypair),
|
||||
},
|
||||
DisplayMode::Secret => format!("{:x}", keypair.secret()),
|
||||
DisplayMode::Public => format!("{:x}", keypair.public()),
|
||||
DisplayMode::Address => format!("{:x}", keypair.address()),
|
||||
}
|
||||
}
|
||||
|
||||
fn execute<S, I>(command: I) -> Result<String, Error>
|
||||
where
|
||||
I: IntoIterator<Item = S>,
|
||||
S: AsRef<str>,
|
||||
{
|
||||
let args: Args = Docopt::new(USAGE).and_then(|d| d.argv(command).deserialize())?;
|
||||
|
||||
return if args.cmd_info {
|
||||
let display_mode = DisplayMode::new(&args);
|
||||
|
||||
let result = if args.flag_brain {
|
||||
let phrase = args.arg_secret_or_phrase;
|
||||
let phrase_info = validate_phrase(&phrase);
|
||||
let keypair = Brain::new(phrase)
|
||||
.generate()
|
||||
.expect("Brain wallet generator is infallible; qed");
|
||||
(keypair, Some(phrase_info))
|
||||
} else {
|
||||
let secret = args
|
||||
.arg_secret_or_phrase
|
||||
.parse()
|
||||
.map_err(|_| EthkeyError::InvalidSecret)?;
|
||||
(KeyPair::from_secret(secret)?, None)
|
||||
};
|
||||
Ok(display(result, display_mode))
|
||||
} else if args.cmd_generate {
|
||||
let display_mode = DisplayMode::new(&args);
|
||||
let result = if args.cmd_random {
|
||||
if args.flag_brain {
|
||||
let mut brain = BrainPrefix::new(vec![0], usize::max_value(), BRAIN_WORDS);
|
||||
let keypair = brain.generate()?;
|
||||
let phrase = format!("recovery phrase: {}", brain.phrase());
|
||||
(keypair, Some(phrase))
|
||||
} else {
|
||||
(Random.generate()?, None)
|
||||
}
|
||||
} else if args.cmd_prefix {
|
||||
let prefix = args.arg_prefix.from_hex()?;
|
||||
let brain = args.flag_brain;
|
||||
in_threads(move || {
|
||||
let iterations = 1024;
|
||||
let prefix = prefix.clone();
|
||||
move || {
|
||||
let prefix = prefix.clone();
|
||||
let res = if brain {
|
||||
let mut brain = BrainPrefix::new(prefix, iterations, BRAIN_WORDS);
|
||||
let result = brain.generate();
|
||||
let phrase = format!("recovery phrase: {}", brain.phrase());
|
||||
result.map(|keypair| (keypair, Some(phrase)))
|
||||
} else {
|
||||
let result = Prefix::new(prefix, iterations).generate();
|
||||
result.map(|res| (res, None))
|
||||
};
|
||||
|
||||
Ok(res.map(Some).unwrap_or(None))
|
||||
}
|
||||
})?
|
||||
} else {
|
||||
return Ok(format!("{}", USAGE));
|
||||
};
|
||||
Ok(display(result, display_mode))
|
||||
} else if args.cmd_sign {
|
||||
let secret = args
|
||||
.arg_secret
|
||||
.parse()
|
||||
.map_err(|_| EthkeyError::InvalidSecret)?;
|
||||
let message = args
|
||||
.arg_message
|
||||
.parse()
|
||||
.map_err(|_| EthkeyError::InvalidMessage)?;
|
||||
let signature = sign(&secret, &message)?;
|
||||
Ok(format!("{}", signature))
|
||||
} else if args.cmd_verify {
|
||||
let signature = args
|
||||
.arg_signature
|
||||
.parse()
|
||||
.map_err(|_| EthkeyError::InvalidSignature)?;
|
||||
let message = args
|
||||
.arg_message
|
||||
.parse()
|
||||
.map_err(|_| EthkeyError::InvalidMessage)?;
|
||||
let ok = if args.cmd_public {
|
||||
let public = args
|
||||
.arg_public
|
||||
.parse()
|
||||
.map_err(|_| EthkeyError::InvalidPublic)?;
|
||||
verify_public(&public, &signature, &message)?
|
||||
} else if args.cmd_address {
|
||||
let address = args
|
||||
.arg_address
|
||||
.parse()
|
||||
.map_err(|_| EthkeyError::InvalidAddress)?;
|
||||
verify_address(&address, &signature, &message)?
|
||||
} else {
|
||||
return Ok(format!("{}", USAGE));
|
||||
};
|
||||
Ok(format!("{}", ok))
|
||||
} else if args.cmd_recover {
|
||||
let display_mode = DisplayMode::new(&args);
|
||||
let known_phrase = args.arg_known_phrase;
|
||||
let address = args
|
||||
.arg_address
|
||||
.parse()
|
||||
.map_err(|_| EthkeyError::InvalidAddress)?;
|
||||
let (phrase, keypair) = in_threads(move || {
|
||||
let mut it =
|
||||
brain_recover::PhrasesIterator::from_known_phrase(&known_phrase, BRAIN_WORDS);
|
||||
move || {
|
||||
let mut i = 0;
|
||||
while let Some(phrase) = it.next() {
|
||||
i += 1;
|
||||
|
||||
let keypair = Brain::new(phrase.clone()).generate().unwrap();
|
||||
if keypair.address() == address {
|
||||
return Ok(Some((phrase, keypair)));
|
||||
}
|
||||
|
||||
if i >= 1024 {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
|
||||
Err(EthkeyError::Custom("Couldn't find any results.".into()))
|
||||
}
|
||||
})?;
|
||||
Ok(display((keypair, Some(phrase)), display_mode))
|
||||
} else {
|
||||
Ok(format!("{}", USAGE))
|
||||
};
|
||||
}
|
||||
|
||||
const BRAIN_WORDS: usize = 12;
|
||||
|
||||
fn validate_phrase(phrase: &str) -> String {
|
||||
match Brain::validate_phrase(phrase, BRAIN_WORDS) {
|
||||
Ok(()) => format!("The recovery phrase looks correct.\n"),
|
||||
Err(err) => format!("The recover phrase was not generated by Parity: {}", err),
|
||||
}
|
||||
}
|
||||
|
||||
fn in_threads<F, X, O>(prepare: F) -> Result<O, EthkeyError>
|
||||
where
|
||||
O: Send + 'static,
|
||||
X: Send + 'static,
|
||||
F: Fn() -> X,
|
||||
X: FnMut() -> Result<Option<O>, EthkeyError>,
|
||||
{
|
||||
let pool = threadpool::Builder::new().build();
|
||||
|
||||
let (tx, rx) = sync::mpsc::sync_channel(1);
|
||||
let is_done = sync::Arc::new(sync::atomic::AtomicBool::default());
|
||||
|
||||
for _ in 0..pool.max_count() {
|
||||
let is_done = is_done.clone();
|
||||
let tx = tx.clone();
|
||||
let mut task = prepare();
|
||||
pool.execute(move || {
|
||||
loop {
|
||||
if is_done.load(sync::atomic::Ordering::SeqCst) {
|
||||
return;
|
||||
}
|
||||
|
||||
let res = match task() {
|
||||
Ok(None) => continue,
|
||||
Ok(Some(v)) => Ok(v),
|
||||
Err(err) => Err(err),
|
||||
};
|
||||
|
||||
// We are interested only in the first response.
|
||||
let _ = tx.send(res);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if let Ok(solution) = rx.recv() {
|
||||
is_done.store(true, sync::atomic::Ordering::SeqCst);
|
||||
return solution;
|
||||
}
|
||||
|
||||
Err(EthkeyError::Custom("No results found.".into()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::execute;
|
||||
|
||||
#[test]
|
||||
fn info() {
|
||||
let command = vec![
|
||||
"ethkey",
|
||||
"info",
|
||||
"17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55",
|
||||
]
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let expected =
|
||||
"secret: 17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55
|
||||
public: 689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124
|
||||
address: 26d1ec50b4e62c1d1a40d16e7cacc6a6580757d5".to_owned();
|
||||
assert_eq!(execute(command).unwrap(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn brain() {
|
||||
let command = vec!["ethkey", "info", "--brain", "this is sparta"]
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let expected =
|
||||
"The recover phrase was not generated by Parity: The word 'this' does not come from the dictionary.
|
||||
|
||||
secret: aa22b54c0cb43ee30a014afe5ef3664b1cde299feabca46cd3167a85a57c39f2
|
||||
public: c4c5398da6843632c123f543d714d2d2277716c11ff612b2a2f23c6bda4d6f0327c31cd58c55a9572c3cc141dade0c32747a13b7ef34c241b26c84adbb28fcf4
|
||||
address: 006e27b6a72e1f34c626762f3c4761547aff1421".to_owned();
|
||||
assert_eq!(execute(command).unwrap(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret() {
|
||||
let command = vec!["ethkey", "info", "--brain", "this is sparta", "--secret"]
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let expected =
|
||||
"aa22b54c0cb43ee30a014afe5ef3664b1cde299feabca46cd3167a85a57c39f2".to_owned();
|
||||
assert_eq!(execute(command).unwrap(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public() {
|
||||
let command = vec!["ethkey", "info", "--brain", "this is sparta", "--public"]
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let expected = "c4c5398da6843632c123f543d714d2d2277716c11ff612b2a2f23c6bda4d6f0327c31cd58c55a9572c3cc141dade0c32747a13b7ef34c241b26c84adbb28fcf4".to_owned();
|
||||
assert_eq!(execute(command).unwrap(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn address() {
|
||||
let command = vec!["ethkey", "info", "-b", "this is sparta", "--address"]
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let expected = "006e27b6a72e1f34c626762f3c4761547aff1421".to_owned();
|
||||
assert_eq!(execute(command).unwrap(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sign() {
|
||||
let command = vec![
|
||||
"ethkey",
|
||||
"sign",
|
||||
"17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55",
|
||||
"bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987",
|
||||
]
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let expected = "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200".to_owned();
|
||||
assert_eq!(execute(command).unwrap(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_valid_public() {
|
||||
let command = vec!["ethkey", "verify", "public", "689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987"]
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let expected = "true".to_owned();
|
||||
assert_eq!(execute(command).unwrap(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_valid_address() {
|
||||
let command = vec!["ethkey", "verify", "address", "26d1ec50b4e62c1d1a40d16e7cacc6a6580757d5", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987"]
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let expected = "true".to_owned();
|
||||
assert_eq!(execute(command).unwrap(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_invalid() {
|
||||
let command = vec!["ethkey", "verify", "public", "689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec986"]
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let expected = "false".to_owned();
|
||||
assert_eq!(execute(command).unwrap(), expected);
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::{Generator, KeyPair, Secret};
|
||||
use keccak::Keccak256;
|
||||
use parity_wordlist;
|
||||
|
||||
/// Simple brainwallet.
|
||||
pub struct Brain(String);
|
||||
|
||||
impl Brain {
|
||||
pub fn new(s: String) -> Self {
|
||||
Brain(s)
|
||||
}
|
||||
|
||||
pub fn validate_phrase(phrase: &str, expected_words: usize) -> Result<(), ::WordlistError> {
|
||||
parity_wordlist::validate_phrase(phrase, expected_words)
|
||||
}
|
||||
}
|
||||
|
||||
impl Generator for Brain {
|
||||
type Error = ::Void;
|
||||
|
||||
fn generate(&mut self) -> Result<KeyPair, Self::Error> {
|
||||
let seed = self.0.clone();
|
||||
let mut secret = seed.into_bytes().keccak256();
|
||||
|
||||
let mut i = 0;
|
||||
loop {
|
||||
secret = secret.keccak256();
|
||||
|
||||
match i > 16384 {
|
||||
false => i += 1,
|
||||
true => {
|
||||
if let Ok(pair) =
|
||||
Secret::from_unsafe_slice(&secret).and_then(KeyPair::from_secret)
|
||||
{
|
||||
if pair.address()[0] == 0 {
|
||||
trace!("Testing: {}, got: {:?}", self.0, pair.address());
|
||||
return Ok(pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use Brain;
|
||||
use Generator;
|
||||
|
||||
#[test]
|
||||
fn test_brain() {
|
||||
let words = "this is sparta!".to_owned();
|
||||
let first_keypair = Brain::new(words.clone()).generate().unwrap();
|
||||
let second_keypair = Brain::new(words.clone()).generate().unwrap();
|
||||
assert_eq!(first_keypair.secret(), second_keypair.secret());
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::{Brain, Error, Generator, KeyPair};
|
||||
use parity_wordlist as wordlist;
|
||||
|
||||
/// Tries to find brain-seed keypair with address starting with given prefix.
|
||||
pub struct BrainPrefix {
|
||||
prefix: Vec<u8>,
|
||||
iterations: usize,
|
||||
no_of_words: usize,
|
||||
last_phrase: String,
|
||||
}
|
||||
|
||||
impl BrainPrefix {
|
||||
pub fn new(prefix: Vec<u8>, iterations: usize, no_of_words: usize) -> Self {
|
||||
BrainPrefix {
|
||||
prefix,
|
||||
iterations,
|
||||
no_of_words,
|
||||
last_phrase: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn phrase(&self) -> &str {
|
||||
&self.last_phrase
|
||||
}
|
||||
}
|
||||
|
||||
impl Generator for BrainPrefix {
|
||||
type Error = Error;
|
||||
|
||||
fn generate(&mut self) -> Result<KeyPair, Error> {
|
||||
for _ in 0..self.iterations {
|
||||
let phrase = wordlist::random_phrase(self.no_of_words);
|
||||
let keypair = Brain::new(phrase.clone()).generate().unwrap();
|
||||
if keypair.address().starts_with(&self.prefix) {
|
||||
self.last_phrase = phrase;
|
||||
return Ok(keypair);
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::Custom("Could not find keypair".into()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use BrainPrefix;
|
||||
use Generator;
|
||||
|
||||
#[test]
|
||||
fn prefix_generator() {
|
||||
let prefix = vec![0x00u8];
|
||||
let keypair = BrainPrefix::new(prefix.clone(), usize::max_value(), 12)
|
||||
.generate()
|
||||
.unwrap();
|
||||
assert!(keypair.address().starts_with(&prefix));
|
||||
}
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use edit_distance::edit_distance;
|
||||
use parity_wordlist;
|
||||
|
||||
use super::{Address, Brain, Generator};
|
||||
|
||||
/// Tries to find a phrase for address, given the number
|
||||
/// of expected words and a partial phrase.
|
||||
///
|
||||
/// Returns `None` if phrase couldn't be found.
|
||||
pub fn brain_recover(
|
||||
address: &Address,
|
||||
known_phrase: &str,
|
||||
expected_words: usize,
|
||||
) -> Option<String> {
|
||||
let it = PhrasesIterator::from_known_phrase(known_phrase, expected_words);
|
||||
for phrase in it {
|
||||
let keypair = Brain::new(phrase.clone())
|
||||
.generate()
|
||||
.expect("Brain wallets are infallible; qed");
|
||||
trace!("Testing: {}, got: {:?}", phrase, keypair.address());
|
||||
if &keypair.address() == address {
|
||||
return Some(phrase);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn generate_substitutions(word: &str) -> Vec<&'static str> {
|
||||
let mut words = parity_wordlist::WORDS
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|w| (edit_distance(w, word), w))
|
||||
.collect::<Vec<_>>();
|
||||
words.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
|
||||
words.into_iter().map(|pair| pair.1).collect()
|
||||
}
|
||||
|
||||
/// Iterator over possible
|
||||
pub struct PhrasesIterator {
|
||||
words: Vec<Vec<&'static str>>,
|
||||
combinations: u64,
|
||||
indexes: Vec<usize>,
|
||||
has_next: bool,
|
||||
}
|
||||
|
||||
impl PhrasesIterator {
|
||||
pub fn from_known_phrase(known_phrase: &str, expected_words: usize) -> Self {
|
||||
let known_words = parity_wordlist::WORDS
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<HashSet<_>>();
|
||||
let mut words = known_phrase
|
||||
.split(' ')
|
||||
.map(|word| match known_words.get(word) {
|
||||
None => {
|
||||
info!(
|
||||
"Invalid word '{}', looking for potential substitutions.",
|
||||
word
|
||||
);
|
||||
let substitutions = generate_substitutions(word);
|
||||
info!("Closest words: {:?}", &substitutions[..10]);
|
||||
substitutions
|
||||
}
|
||||
Some(word) => vec![*word],
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// add missing words
|
||||
if words.len() < expected_words {
|
||||
let to_add = expected_words - words.len();
|
||||
info!("Number of words is insuficcient adding {} more.", to_add);
|
||||
for _ in 0..to_add {
|
||||
words.push(parity_wordlist::WORDS.iter().cloned().collect());
|
||||
}
|
||||
}
|
||||
|
||||
// start searching
|
||||
PhrasesIterator::new(words)
|
||||
}
|
||||
|
||||
pub fn new(words: Vec<Vec<&'static str>>) -> Self {
|
||||
let combinations = words.iter().fold(1u64, |acc, x| acc * x.len() as u64);
|
||||
let indexes = words.iter().map(|_| 0).collect();
|
||||
info!("Starting to test {} possible combinations.", combinations);
|
||||
|
||||
PhrasesIterator {
|
||||
words,
|
||||
combinations,
|
||||
indexes,
|
||||
has_next: combinations > 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn combinations(&self) -> u64 {
|
||||
self.combinations
|
||||
}
|
||||
|
||||
fn current(&self) -> String {
|
||||
let mut s = self.words[0][self.indexes[0]].to_owned();
|
||||
for i in 1..self.indexes.len() {
|
||||
s.push(' ');
|
||||
s.push_str(self.words[i][self.indexes[i]]);
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
fn next_index(&mut self) -> bool {
|
||||
let mut pos = self.indexes.len();
|
||||
while pos > 0 {
|
||||
pos -= 1;
|
||||
self.indexes[pos] += 1;
|
||||
if self.indexes[pos] >= self.words[pos].len() {
|
||||
self.indexes[pos] = 0;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for PhrasesIterator {
|
||||
type Item = String;
|
||||
|
||||
fn next(&mut self) -> Option<String> {
|
||||
if !self.has_next {
|
||||
return None;
|
||||
}
|
||||
|
||||
let phrase = self.current();
|
||||
self.has_next = self.next_index();
|
||||
Some(phrase)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::PhrasesIterator;
|
||||
|
||||
#[test]
|
||||
fn should_generate_possible_combinations() {
|
||||
let mut it =
|
||||
PhrasesIterator::new(vec![vec!["1", "2", "3"], vec!["test"], vec!["a", "b", "c"]]);
|
||||
|
||||
assert_eq!(it.combinations(), 9);
|
||||
assert_eq!(it.next(), Some("1 test a".to_owned()));
|
||||
assert_eq!(it.next(), Some("1 test b".to_owned()));
|
||||
assert_eq!(it.next(), Some("1 test c".to_owned()));
|
||||
assert_eq!(it.next(), Some("2 test a".to_owned()));
|
||||
assert_eq!(it.next(), Some("2 test b".to_owned()));
|
||||
assert_eq!(it.next(), Some("2 test c".to_owned()));
|
||||
assert_eq!(it.next(), Some("3 test a".to_owned()));
|
||||
assert_eq!(it.next(), Some("3 test b".to_owned()));
|
||||
assert_eq!(it.next(), Some("3 test c".to_owned()));
|
||||
assert_eq!(it.next(), None);
|
||||
}
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#![allow(deprecated)]
|
||||
|
||||
use parity_crypto::error::SymmError;
|
||||
use secp256k1;
|
||||
use std::io;
|
||||
|
||||
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 super::Error;
|
||||
use secp256k1::{self, ecdh, key};
|
||||
use Public;
|
||||
use Secret;
|
||||
use 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 super::{ecdh, Error};
|
||||
use ethereum_types::H128;
|
||||
use parity_crypto::{aes, digest, hmac, is_equal};
|
||||
use Generator;
|
||||
use Public;
|
||||
use Random;
|
||||
use 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 Generator;
|
||||
use Random;
|
||||
|
||||
#[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,81 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{error, fmt};
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Crypto error
|
||||
pub enum Error {
|
||||
/// Invalid secret key
|
||||
InvalidSecret,
|
||||
/// Invalid public key
|
||||
InvalidPublic,
|
||||
/// Invalid address
|
||||
InvalidAddress,
|
||||
/// Invalid EC signature
|
||||
InvalidSignature,
|
||||
/// Invalid AES message
|
||||
InvalidMessage,
|
||||
/// IO Error
|
||||
Io(::std::io::Error),
|
||||
/// Custom
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let msg = match *self {
|
||||
Error::InvalidSecret => "Invalid secret".into(),
|
||||
Error::InvalidPublic => "Invalid public".into(),
|
||||
Error::InvalidAddress => "Invalid address".into(),
|
||||
Error::InvalidSignature => "Invalid EC signature".into(),
|
||||
Error::InvalidMessage => "Invalid AES message".into(),
|
||||
Error::Io(ref err) => format!("I/O error: {}", err),
|
||||
Error::Custom(ref s) => s.clone(),
|
||||
};
|
||||
|
||||
f.write_fmt(format_args!("Crypto error ({})", msg))
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for Error {
|
||||
fn description(&self) -> &str {
|
||||
"Crypto error"
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<String> for Error {
|
||||
fn into(self) -> String {
|
||||
format!("{}", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<::secp256k1::Error> for Error {
|
||||
fn from(e: ::secp256k1::Error) -> Error {
|
||||
match e {
|
||||
::secp256k1::Error::InvalidMessage => Error::InvalidMessage,
|
||||
::secp256k1::Error::InvalidPublicKey => Error::InvalidPublic,
|
||||
::secp256k1::Error::InvalidSecretKey => Error::InvalidSecret,
|
||||
_ => Error::InvalidSignature,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<::std::io::Error> for Error {
|
||||
fn from(err: ::std::io::Error) -> Error {
|
||||
Error::Io(err)
|
||||
}
|
||||
}
|
||||
@@ -1,589 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Extended keys
|
||||
|
||||
pub use self::derivation::Error as DerivationError;
|
||||
use ethereum_types::H256;
|
||||
use secret::Secret;
|
||||
use Public;
|
||||
|
||||
/// Represents label that can be stored as a part of key derivation
|
||||
pub trait Label {
|
||||
/// Length of the data that label occupies
|
||||
fn len() -> usize;
|
||||
|
||||
/// Store label data to the key derivation sequence
|
||||
/// Must not use more than `len()` bytes from slice
|
||||
fn store(&self, target: &mut [u8]);
|
||||
}
|
||||
|
||||
impl Label for u32 {
|
||||
fn len() -> usize {
|
||||
4
|
||||
}
|
||||
|
||||
fn store(&self, target: &mut [u8]) {
|
||||
let bytes = self.to_be_bytes();
|
||||
target[0..4].copy_from_slice(&bytes);
|
||||
}
|
||||
}
|
||||
|
||||
/// Key derivation over generic label `T`
|
||||
pub enum Derivation<T: Label> {
|
||||
/// Soft key derivation (allow proof of parent)
|
||||
Soft(T),
|
||||
/// Hard key derivation (does not allow proof of parent)
|
||||
Hard(T),
|
||||
}
|
||||
|
||||
impl From<u32> for Derivation<u32> {
|
||||
fn from(index: u32) -> Self {
|
||||
if index < (2 << 30) {
|
||||
Derivation::Soft(index)
|
||||
} else {
|
||||
Derivation::Hard(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Label for H256 {
|
||||
fn len() -> usize {
|
||||
32
|
||||
}
|
||||
|
||||
fn store(&self, target: &mut [u8]) {
|
||||
self.copy_to(&mut target[0..32]);
|
||||
}
|
||||
}
|
||||
|
||||
/// Extended secret key, allows deterministic derivation of subsequent keys.
|
||||
pub struct ExtendedSecret {
|
||||
secret: Secret,
|
||||
chain_code: H256,
|
||||
}
|
||||
|
||||
impl ExtendedSecret {
|
||||
/// New extended key from given secret and chain code.
|
||||
pub fn with_code(secret: Secret, chain_code: H256) -> ExtendedSecret {
|
||||
ExtendedSecret {
|
||||
secret: secret,
|
||||
chain_code: chain_code,
|
||||
}
|
||||
}
|
||||
|
||||
/// New extended key from given secret with the random chain code.
|
||||
pub fn new_random(secret: Secret) -> ExtendedSecret {
|
||||
ExtendedSecret::with_code(secret, H256::random())
|
||||
}
|
||||
|
||||
/// New extended key from given secret.
|
||||
/// Chain code will be derived from the secret itself (in a deterministic way).
|
||||
pub fn new(secret: Secret) -> ExtendedSecret {
|
||||
let chain_code = derivation::chain_code(*secret);
|
||||
ExtendedSecret::with_code(secret, chain_code)
|
||||
}
|
||||
|
||||
/// Derive new private key
|
||||
pub fn derive<T>(&self, index: Derivation<T>) -> ExtendedSecret
|
||||
where
|
||||
T: Label,
|
||||
{
|
||||
let (derived_key, next_chain_code) =
|
||||
derivation::private(*self.secret, self.chain_code, index);
|
||||
|
||||
let derived_secret = Secret::from(derived_key.0);
|
||||
|
||||
ExtendedSecret::with_code(derived_secret, next_chain_code)
|
||||
}
|
||||
|
||||
/// Private key component of the extended key.
|
||||
pub fn as_raw(&self) -> &Secret {
|
||||
&self.secret
|
||||
}
|
||||
}
|
||||
|
||||
/// Extended public key, allows deterministic derivation of subsequent keys.
|
||||
pub struct ExtendedPublic {
|
||||
public: Public,
|
||||
chain_code: H256,
|
||||
}
|
||||
|
||||
impl ExtendedPublic {
|
||||
/// New extended public key from known parent and chain code
|
||||
pub fn new(public: Public, chain_code: H256) -> Self {
|
||||
ExtendedPublic {
|
||||
public: public,
|
||||
chain_code: chain_code,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create new extended public key from known secret
|
||||
pub fn from_secret(secret: &ExtendedSecret) -> Result<Self, DerivationError> {
|
||||
Ok(ExtendedPublic::new(
|
||||
derivation::point(**secret.as_raw())?,
|
||||
secret.chain_code.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Derive new public key
|
||||
/// Operation is defined only for index belongs [0..2^31)
|
||||
pub fn derive<T>(&self, index: Derivation<T>) -> Result<Self, DerivationError>
|
||||
where
|
||||
T: Label,
|
||||
{
|
||||
let (derived_key, next_chain_code) =
|
||||
derivation::public(self.public, self.chain_code, index)?;
|
||||
Ok(ExtendedPublic::new(derived_key, next_chain_code))
|
||||
}
|
||||
|
||||
pub fn public(&self) -> &Public {
|
||||
&self.public
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ExtendedKeyPair {
|
||||
secret: ExtendedSecret,
|
||||
public: ExtendedPublic,
|
||||
}
|
||||
|
||||
impl ExtendedKeyPair {
|
||||
pub fn new(secret: Secret) -> Self {
|
||||
let extended_secret = ExtendedSecret::new(secret);
|
||||
let extended_public = ExtendedPublic::from_secret(&extended_secret)
|
||||
.expect("Valid `Secret` always produces valid public; qed");
|
||||
ExtendedKeyPair {
|
||||
secret: extended_secret,
|
||||
public: extended_public,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_code(secret: Secret, public: Public, chain_code: H256) -> Self {
|
||||
ExtendedKeyPair {
|
||||
secret: ExtendedSecret::with_code(secret, chain_code.clone()),
|
||||
public: ExtendedPublic::new(public, chain_code),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_secret(secret: Secret, chain_code: H256) -> Self {
|
||||
let extended_secret = ExtendedSecret::with_code(secret, chain_code);
|
||||
let extended_public = ExtendedPublic::from_secret(&extended_secret)
|
||||
.expect("Valid `Secret` always produces valid public; qed");
|
||||
ExtendedKeyPair {
|
||||
secret: extended_secret,
|
||||
public: extended_public,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_seed(seed: &[u8]) -> Result<ExtendedKeyPair, DerivationError> {
|
||||
let (master_key, chain_code) = derivation::seed_pair(seed);
|
||||
Ok(ExtendedKeyPair::with_secret(
|
||||
Secret::from_unsafe_slice(&*master_key).map_err(|_| DerivationError::InvalidSeed)?,
|
||||
chain_code,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn secret(&self) -> &ExtendedSecret {
|
||||
&self.secret
|
||||
}
|
||||
|
||||
pub fn public(&self) -> &ExtendedPublic {
|
||||
&self.public
|
||||
}
|
||||
|
||||
pub fn derive<T>(&self, index: Derivation<T>) -> Result<Self, DerivationError>
|
||||
where
|
||||
T: Label,
|
||||
{
|
||||
let derived = self.secret.derive(index);
|
||||
|
||||
Ok(ExtendedKeyPair {
|
||||
public: ExtendedPublic::from_secret(&derived)?,
|
||||
secret: derived,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Derivation functions for private and public keys
|
||||
// Work is based on BIP0032
|
||||
// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
||||
mod derivation {
|
||||
use super::{Derivation, Label};
|
||||
use ethereum_types::{H256, H512, U256, U512};
|
||||
use keccak;
|
||||
use math::curve_order;
|
||||
use parity_crypto::hmac;
|
||||
use secp256k1::key::{PublicKey, SecretKey};
|
||||
use SECP256K1;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
InvalidHardenedUse,
|
||||
InvalidPoint,
|
||||
MissingIndex,
|
||||
InvalidSeed,
|
||||
}
|
||||
|
||||
// Deterministic derivation of the key using secp256k1 elliptic curve.
|
||||
// Derivation can be either hardened or not.
|
||||
// For hardened derivation, pass u32 index at least 2^31 or custom Derivation::Hard(T) enum
|
||||
//
|
||||
// Can panic if passed `private_key` is not a valid secp256k1 private key
|
||||
// (outside of (0..curve_order()]) field
|
||||
pub fn private<T>(private_key: H256, chain_code: H256, index: Derivation<T>) -> (H256, H256)
|
||||
where
|
||||
T: Label,
|
||||
{
|
||||
match index {
|
||||
Derivation::Soft(index) => private_soft(private_key, chain_code, index),
|
||||
Derivation::Hard(index) => private_hard(private_key, chain_code, index),
|
||||
}
|
||||
}
|
||||
|
||||
fn hmac_pair(data: &[u8], private_key: H256, chain_code: H256) -> (H256, H256) {
|
||||
let private: U256 = private_key.into();
|
||||
|
||||
// produces 512-bit derived hmac (I)
|
||||
let skey = hmac::SigKey::sha512(&*chain_code);
|
||||
let i_512 = hmac::sign(&skey, &data[..]);
|
||||
|
||||
// left most 256 bits are later added to original private key
|
||||
let hmac_key: U256 = H256::from_slice(&i_512[0..32]).into();
|
||||
// right most 256 bits are new chain code for later derivations
|
||||
let next_chain_code = H256::from(&i_512[32..64]);
|
||||
|
||||
let child_key = private_add(hmac_key, private).into();
|
||||
(child_key, next_chain_code)
|
||||
}
|
||||
|
||||
// Can panic if passed `private_key` is not a valid secp256k1 private key
|
||||
// (outside of (0..curve_order()]) field
|
||||
fn private_soft<T>(private_key: H256, chain_code: H256, index: T) -> (H256, H256)
|
||||
where
|
||||
T: Label,
|
||||
{
|
||||
let mut data = vec![0u8; 33 + T::len()];
|
||||
|
||||
let sec_private = SecretKey::from_slice(&SECP256K1, &*private_key)
|
||||
.expect("Caller should provide valid private key");
|
||||
let sec_public = PublicKey::from_secret_key(&SECP256K1, &sec_private)
|
||||
.expect("Caller should provide valid private key");
|
||||
let public_serialized = sec_public.serialize_vec(&SECP256K1, true);
|
||||
|
||||
// curve point (compressed public key) -- index
|
||||
// 0.33 -- 33..end
|
||||
data[0..33].copy_from_slice(&public_serialized);
|
||||
index.store(&mut data[33..]);
|
||||
|
||||
hmac_pair(&data, private_key, chain_code)
|
||||
}
|
||||
|
||||
// Deterministic derivation of the key using secp256k1 elliptic curve
|
||||
// This is hardened derivation and does not allow to associate
|
||||
// corresponding public keys of the original and derived private keys
|
||||
fn private_hard<T>(private_key: H256, chain_code: H256, index: T) -> (H256, H256)
|
||||
where
|
||||
T: Label,
|
||||
{
|
||||
let mut data: Vec<u8> = vec![0u8; 33 + T::len()];
|
||||
let private: U256 = private_key.into();
|
||||
|
||||
// 0x00 (padding) -- private_key -- index
|
||||
// 0 -- 1..33 -- 33..end
|
||||
private.to_big_endian(&mut data[1..33]);
|
||||
index.store(&mut data[33..(33 + T::len())]);
|
||||
|
||||
hmac_pair(&data, private_key, chain_code)
|
||||
}
|
||||
|
||||
fn private_add(k1: U256, k2: U256) -> U256 {
|
||||
let sum = U512::from(k1) + U512::from(k2);
|
||||
modulo(sum, curve_order())
|
||||
}
|
||||
|
||||
// todo: surely can be optimized
|
||||
fn modulo(u1: U512, u2: U256) -> U256 {
|
||||
let dv = u1 / U512::from(u2);
|
||||
let md = u1 - (dv * U512::from(u2));
|
||||
md.into()
|
||||
}
|
||||
|
||||
pub fn public<T>(
|
||||
public_key: H512,
|
||||
chain_code: H256,
|
||||
derivation: Derivation<T>,
|
||||
) -> Result<(H512, H256), Error>
|
||||
where
|
||||
T: Label,
|
||||
{
|
||||
let index = match derivation {
|
||||
Derivation::Soft(index) => index,
|
||||
Derivation::Hard(_) => {
|
||||
return Err(Error::InvalidHardenedUse);
|
||||
}
|
||||
};
|
||||
|
||||
let mut public_sec_raw = [0u8; 65];
|
||||
public_sec_raw[0] = 4;
|
||||
public_sec_raw[1..65].copy_from_slice(&*public_key);
|
||||
let public_sec =
|
||||
PublicKey::from_slice(&SECP256K1, &public_sec_raw).map_err(|_| Error::InvalidPoint)?;
|
||||
let public_serialized = public_sec.serialize_vec(&SECP256K1, true);
|
||||
|
||||
let mut data = vec![0u8; 33 + T::len()];
|
||||
// curve point (compressed public key) -- index
|
||||
// 0.33 -- 33..end
|
||||
data[0..33].copy_from_slice(&public_serialized);
|
||||
index.store(&mut data[33..(33 + T::len())]);
|
||||
|
||||
// HMAC512SHA produces [derived private(256); new chain code(256)]
|
||||
let skey = hmac::SigKey::sha512(&*chain_code);
|
||||
let i_512 = hmac::sign(&skey, &data[..]);
|
||||
|
||||
let new_private = H256::from(&i_512[0..32]);
|
||||
let new_chain_code = H256::from(&i_512[32..64]);
|
||||
|
||||
// Generated private key can (extremely rarely) be out of secp256k1 key field
|
||||
if curve_order() <= new_private.clone().into() {
|
||||
return Err(Error::MissingIndex);
|
||||
}
|
||||
let new_private_sec = SecretKey::from_slice(&SECP256K1, &*new_private)
|
||||
.expect("Private key belongs to the field [0..CURVE_ORDER) (checked above); So initializing can never fail; qed");
|
||||
let mut new_public = PublicKey::from_secret_key(&SECP256K1, &new_private_sec)
|
||||
.expect("Valid private key produces valid public key");
|
||||
|
||||
// Adding two points on the elliptic curves (combining two public keys)
|
||||
new_public
|
||||
.add_assign(&SECP256K1, &public_sec)
|
||||
.expect("Addition of two valid points produce valid point");
|
||||
|
||||
let serialized = new_public.serialize_vec(&SECP256K1, false);
|
||||
|
||||
Ok((H512::from(&serialized[1..65]), new_chain_code))
|
||||
}
|
||||
|
||||
fn sha3(slc: &[u8]) -> H256 {
|
||||
keccak::Keccak256::keccak256(slc).into()
|
||||
}
|
||||
|
||||
pub fn chain_code(secret: H256) -> H256 {
|
||||
// 10,000 rounds of sha3
|
||||
let mut running_sha3 = sha3(&*secret);
|
||||
for _ in 0..99999 {
|
||||
running_sha3 = sha3(&*running_sha3);
|
||||
}
|
||||
running_sha3
|
||||
}
|
||||
|
||||
pub fn point(secret: H256) -> Result<H512, Error> {
|
||||
let sec = SecretKey::from_slice(&SECP256K1, &*secret).map_err(|_| Error::InvalidPoint)?;
|
||||
let public_sec =
|
||||
PublicKey::from_secret_key(&SECP256K1, &sec).map_err(|_| Error::InvalidPoint)?;
|
||||
let serialized = public_sec.serialize_vec(&SECP256K1, false);
|
||||
Ok(H512::from(&serialized[1..65]))
|
||||
}
|
||||
|
||||
pub fn seed_pair(seed: &[u8]) -> (H256, H256) {
|
||||
let skey = hmac::SigKey::sha512(b"Bitcoin seed");
|
||||
let i_512 = hmac::sign(&skey, seed);
|
||||
|
||||
let master_key = H256::from_slice(&i_512[0..32]);
|
||||
let chain_code = H256::from_slice(&i_512[32..64]);
|
||||
|
||||
(master_key, chain_code)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{derivation, Derivation, ExtendedKeyPair, ExtendedPublic, ExtendedSecret};
|
||||
use ethereum_types::{H128, H256};
|
||||
use secret::Secret;
|
||||
use std::str::FromStr;
|
||||
|
||||
fn master_chain_basic() -> (H256, H256) {
|
||||
let seed = H128::from_str("000102030405060708090a0b0c0d0e0f")
|
||||
.expect("Seed should be valid H128")
|
||||
.to_vec();
|
||||
|
||||
derivation::seed_pair(&*seed)
|
||||
}
|
||||
|
||||
fn test_extended<F>(f: F, test_private: H256)
|
||||
where
|
||||
F: Fn(ExtendedSecret) -> ExtendedSecret,
|
||||
{
|
||||
let (private_seed, chain_code) = master_chain_basic();
|
||||
let extended_secret = ExtendedSecret::with_code(Secret::from(private_seed.0), chain_code);
|
||||
let derived = f(extended_secret);
|
||||
assert_eq!(**derived.as_raw(), test_private);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smoky() {
|
||||
let secret =
|
||||
Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65")
|
||||
.unwrap();
|
||||
let extended_secret = ExtendedSecret::with_code(secret.clone(), 0u64.into());
|
||||
|
||||
// hardened
|
||||
assert_eq!(&**extended_secret.as_raw(), &*secret);
|
||||
assert_eq!(
|
||||
&**extended_secret.derive(2147483648.into()).as_raw(),
|
||||
&"0927453daed47839608e414a3738dfad10aed17c459bbd9ab53f89b026c834b6".into()
|
||||
);
|
||||
assert_eq!(
|
||||
&**extended_secret.derive(2147483649.into()).as_raw(),
|
||||
&"44238b6a29c6dcbe9b401364141ba11e2198c289a5fed243a1c11af35c19dc0f".into()
|
||||
);
|
||||
|
||||
// normal
|
||||
assert_eq!(
|
||||
&**extended_secret.derive(0.into()).as_raw(),
|
||||
&"bf6a74e3f7b36fc4c96a1e12f31abc817f9f5904f5a8fc27713163d1f0b713f6".into()
|
||||
);
|
||||
assert_eq!(
|
||||
&**extended_secret.derive(1.into()).as_raw(),
|
||||
&"bd4fca9eb1f9c201e9448c1eecd66e302d68d4d313ce895b8c134f512205c1bc".into()
|
||||
);
|
||||
assert_eq!(
|
||||
&**extended_secret.derive(2.into()).as_raw(),
|
||||
&"86932b542d6cab4d9c65490c7ef502d89ecc0e2a5f4852157649e3251e2a3268".into()
|
||||
);
|
||||
|
||||
let extended_public = ExtendedPublic::from_secret(&extended_secret)
|
||||
.expect("Extended public should be created");
|
||||
let derived_public = extended_public
|
||||
.derive(0.into())
|
||||
.expect("First derivation of public should succeed");
|
||||
assert_eq!(&*derived_public.public(), &"f7b3244c96688f92372bfd4def26dc4151529747bab9f188a4ad34e141d47bd66522ff048bc6f19a0a4429b04318b1a8796c000265b4fa200dae5f6dda92dd94".into());
|
||||
|
||||
let keypair = ExtendedKeyPair::with_secret(
|
||||
Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65")
|
||||
.unwrap(),
|
||||
064.into(),
|
||||
);
|
||||
assert_eq!(
|
||||
&**keypair
|
||||
.derive(2147483648u32.into())
|
||||
.expect("Derivation of keypair should succeed")
|
||||
.secret()
|
||||
.as_raw(),
|
||||
&"edef54414c03196557cf73774bc97a645c9a1df2164ed34f0c2a78d1375a930c".into()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn h256_soft_match() {
|
||||
let secret =
|
||||
Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65")
|
||||
.unwrap();
|
||||
let derivation_secret =
|
||||
H256::from_str("51eaf04f9dbbc1417dc97e789edd0c37ecda88bac490434e367ea81b71b7b015")
|
||||
.unwrap();
|
||||
|
||||
let extended_secret = ExtendedSecret::with_code(secret.clone(), 0u64.into());
|
||||
let extended_public = ExtendedPublic::from_secret(&extended_secret)
|
||||
.expect("Extended public should be created");
|
||||
|
||||
let derived_secret0 = extended_secret.derive(Derivation::Soft(derivation_secret));
|
||||
let derived_public0 = extended_public
|
||||
.derive(Derivation::Soft(derivation_secret))
|
||||
.expect("First derivation of public should succeed");
|
||||
|
||||
let public_from_secret0 = ExtendedPublic::from_secret(&derived_secret0)
|
||||
.expect("Extended public should be created");
|
||||
|
||||
assert_eq!(public_from_secret0.public(), derived_public0.public());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn h256_hard() {
|
||||
let secret =
|
||||
Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65")
|
||||
.unwrap();
|
||||
let derivation_secret =
|
||||
H256::from_str("51eaf04f9dbbc1417dc97e789edd0c37ecda88bac490434e367ea81b71b7b015")
|
||||
.unwrap();
|
||||
let extended_secret = ExtendedSecret::with_code(secret.clone(), 1u64.into());
|
||||
|
||||
assert_eq!(
|
||||
&**extended_secret
|
||||
.derive(Derivation::Hard(derivation_secret))
|
||||
.as_raw(),
|
||||
&"2bc2d696fb744d77ff813b4a1ef0ad64e1e5188b622c54ba917acc5ebc7c5486".into()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_() {
|
||||
let secret =
|
||||
Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65")
|
||||
.unwrap();
|
||||
let extended_secret = ExtendedSecret::with_code(secret.clone(), 1.into());
|
||||
let extended_public = ExtendedPublic::from_secret(&extended_secret)
|
||||
.expect("Extended public should be created");
|
||||
|
||||
let derived_secret0 = extended_secret.derive(0.into());
|
||||
let derived_public0 = extended_public
|
||||
.derive(0.into())
|
||||
.expect("First derivation of public should succeed");
|
||||
|
||||
let public_from_secret0 = ExtendedPublic::from_secret(&derived_secret0)
|
||||
.expect("Extended public should be created");
|
||||
|
||||
assert_eq!(public_from_secret0.public(), derived_public0.public());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_seeds() {
|
||||
let seed = H128::from_str("000102030405060708090a0b0c0d0e0f")
|
||||
.expect("Seed should be valid H128")
|
||||
.to_vec();
|
||||
|
||||
// private key from bitcoin test vector
|
||||
// xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs
|
||||
let test_private =
|
||||
H256::from_str("e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35")
|
||||
.expect("Private should be decoded ok");
|
||||
|
||||
let (private_seed, _) = derivation::seed_pair(&*seed);
|
||||
|
||||
assert_eq!(private_seed, test_private);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vector_1() {
|
||||
// xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7
|
||||
// H(0)
|
||||
test_extended(
|
||||
|secret| secret.derive(2147483648.into()),
|
||||
H256::from_str("edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea")
|
||||
.expect("Private should be decoded ok"),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vector_2() {
|
||||
// xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs
|
||||
// H(0)/1
|
||||
test_extended(
|
||||
|secret| secret.derive(2147483648.into()).derive(1.into()),
|
||||
H256::from_str("3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368")
|
||||
.expect("Private should be decoded ok"),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use tiny_keccak::Keccak;
|
||||
|
||||
pub trait Keccak256<T> {
|
||||
fn keccak256(&self) -> T
|
||||
where
|
||||
T: Sized;
|
||||
}
|
||||
|
||||
impl Keccak256<[u8; 32]> for [u8] {
|
||||
fn keccak256(&self) -> [u8; 32] {
|
||||
let mut keccak = Keccak::new_keccak256();
|
||||
let mut result = [0u8; 32];
|
||||
keccak.update(self);
|
||||
keccak.finalize(&mut result);
|
||||
result
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::{Address, Error, Public, Secret, SECP256K1};
|
||||
use keccak::Keccak256;
|
||||
use rustc_hex::ToHex;
|
||||
use secp256k1::key;
|
||||
use std::fmt;
|
||||
|
||||
pub fn public_to_address(public: &Public) -> Address {
|
||||
let hash = public.keccak256();
|
||||
let mut result = Address::default();
|
||||
result.copy_from_slice(&hash[12..]);
|
||||
result
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
/// secp256k1 key pair
|
||||
pub struct KeyPair {
|
||||
secret: Secret,
|
||||
public: Public,
|
||||
}
|
||||
|
||||
impl fmt::Display for KeyPair {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
writeln!(f, "secret: {}", self.secret.to_hex())?;
|
||||
writeln!(f, "public: {}", self.public.to_hex())?;
|
||||
write!(f, "address: {}", self.address().to_hex())
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyPair {
|
||||
/// Create a pair from secret key
|
||||
pub fn from_secret(secret: Secret) -> Result<KeyPair, Error> {
|
||||
let context = &SECP256K1;
|
||||
let s: key::SecretKey = key::SecretKey::from_slice(context, &secret[..])?;
|
||||
let pub_key = key::PublicKey::from_secret_key(context, &s)?;
|
||||
let serialized = pub_key.serialize_vec(context, false);
|
||||
|
||||
let mut public = Public::default();
|
||||
public.copy_from_slice(&serialized[1..65]);
|
||||
|
||||
let keypair = KeyPair {
|
||||
secret: secret,
|
||||
public: public,
|
||||
};
|
||||
|
||||
Ok(keypair)
|
||||
}
|
||||
|
||||
pub fn from_secret_slice(slice: &[u8]) -> Result<KeyPair, Error> {
|
||||
Self::from_secret(Secret::from_unsafe_slice(slice)?)
|
||||
}
|
||||
|
||||
pub fn from_keypair(sec: key::SecretKey, publ: key::PublicKey) -> Self {
|
||||
let context = &SECP256K1;
|
||||
let serialized = publ.serialize_vec(context, false);
|
||||
let secret = Secret::from(sec);
|
||||
let mut public = Public::default();
|
||||
public.copy_from_slice(&serialized[1..65]);
|
||||
|
||||
KeyPair {
|
||||
secret: secret,
|
||||
public: public,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn secret(&self) -> &Secret {
|
||||
&self.secret
|
||||
}
|
||||
|
||||
pub fn public(&self) -> &Public {
|
||||
&self.public
|
||||
}
|
||||
|
||||
pub fn address(&self) -> Address {
|
||||
public_to_address(&self.public)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
use KeyPair;
|
||||
use Secret;
|
||||
|
||||
#[test]
|
||||
fn from_secret() {
|
||||
let secret =
|
||||
Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65")
|
||||
.unwrap();
|
||||
let _ = KeyPair::from_secret(secret).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keypair_display() {
|
||||
let expected =
|
||||
"secret: a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65
|
||||
public: 8ce0db0b0359ffc5866ba61903cc2518c3675ef2cf380a7e54bde7ea20e6fa1ab45b7617346cd11b7610001ee6ae5b0155c41cad9527cbcdff44ec67848943a4
|
||||
address: 5b073e9233944b5e729e46d618f0d8edf3d9c34a".to_owned();
|
||||
let secret =
|
||||
Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65")
|
||||
.unwrap();
|
||||
let kp = KeyPair::from_secret(secret).unwrap();
|
||||
assert_eq!(format!("{}", kp), expected);
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::{Error, Public, Secret, SECP256K1};
|
||||
use ethereum_types::{H256, U256};
|
||||
use secp256k1::{
|
||||
constants::{CURVE_ORDER, GENERATOR_X, GENERATOR_Y},
|
||||
key,
|
||||
};
|
||||
|
||||
/// Whether the public key is valid.
|
||||
pub fn public_is_valid(public: &Public) -> bool {
|
||||
to_secp256k1_public(public)
|
||||
.ok()
|
||||
.map_or(false, |p| p.is_valid())
|
||||
}
|
||||
|
||||
/// Inplace multiply public key by secret key (EC point * scalar)
|
||||
pub fn public_mul_secret(public: &mut Public, secret: &Secret) -> Result<(), Error> {
|
||||
let key_secret = secret.to_secp256k1_secret()?;
|
||||
let mut key_public = to_secp256k1_public(public)?;
|
||||
key_public.mul_assign(&SECP256K1, &key_secret)?;
|
||||
set_public(public, &key_public);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Inplace add one public key to another (EC point + EC point)
|
||||
pub fn public_add(public: &mut Public, other: &Public) -> Result<(), Error> {
|
||||
let mut key_public = to_secp256k1_public(public)?;
|
||||
let other_public = to_secp256k1_public(other)?;
|
||||
key_public.add_assign(&SECP256K1, &other_public)?;
|
||||
set_public(public, &key_public);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Inplace sub one public key from another (EC point - EC point)
|
||||
pub fn public_sub(public: &mut Public, other: &Public) -> Result<(), Error> {
|
||||
let mut key_neg_other = to_secp256k1_public(other)?;
|
||||
key_neg_other.mul_assign(&SECP256K1, &key::MINUS_ONE_KEY)?;
|
||||
|
||||
let mut key_public = to_secp256k1_public(public)?;
|
||||
key_public.add_assign(&SECP256K1, &key_neg_other)?;
|
||||
set_public(public, &key_public);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Replace public key with its negation (EC point = - EC point)
|
||||
pub fn public_negate(public: &mut Public) -> Result<(), Error> {
|
||||
let mut key_public = to_secp256k1_public(public)?;
|
||||
key_public.mul_assign(&SECP256K1, &key::MINUS_ONE_KEY)?;
|
||||
set_public(public, &key_public);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Return base point of secp256k1
|
||||
pub fn generation_point() -> Public {
|
||||
let mut public_sec_raw = [0u8; 65];
|
||||
public_sec_raw[0] = 4;
|
||||
public_sec_raw[1..33].copy_from_slice(&GENERATOR_X);
|
||||
public_sec_raw[33..65].copy_from_slice(&GENERATOR_Y);
|
||||
|
||||
let public_key = key::PublicKey::from_slice(&SECP256K1, &public_sec_raw)
|
||||
.expect("constructing using predefined constants; qed");
|
||||
let mut public = Public::default();
|
||||
set_public(&mut public, &public_key);
|
||||
public
|
||||
}
|
||||
|
||||
/// Return secp256k1 elliptic curve order
|
||||
pub fn curve_order() -> U256 {
|
||||
H256::from_slice(&CURVE_ORDER).into()
|
||||
}
|
||||
|
||||
fn to_secp256k1_public(public: &Public) -> Result<key::PublicKey, Error> {
|
||||
let public_data = {
|
||||
let mut temp = [4u8; 65];
|
||||
(&mut temp[1..65]).copy_from_slice(&public[0..64]);
|
||||
temp
|
||||
};
|
||||
|
||||
Ok(key::PublicKey::from_slice(&SECP256K1, &public_data)?)
|
||||
}
|
||||
|
||||
fn set_public(public: &mut Public, key_public: &key::PublicKey) {
|
||||
let key_public_serialized = key_public.serialize_vec(&SECP256K1, false);
|
||||
public.copy_from_slice(&key_public_serialized[1..65]);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{
|
||||
super::{Generator, Random},
|
||||
public_add, public_sub,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn public_addition_is_commutative() {
|
||||
let public1 = Random.generate().unwrap().public().clone();
|
||||
let public2 = Random.generate().unwrap().public().clone();
|
||||
|
||||
let mut left = public1.clone();
|
||||
public_add(&mut left, &public2).unwrap();
|
||||
|
||||
let mut right = public2.clone();
|
||||
public_add(&mut right, &public1).unwrap();
|
||||
|
||||
assert_eq!(left, right);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public_addition_is_reversible_with_subtraction() {
|
||||
let public1 = Random.generate().unwrap().public().clone();
|
||||
let public2 = Random.generate().unwrap().public().clone();
|
||||
|
||||
let mut sum = public1.clone();
|
||||
public_add(&mut sum, &public2).unwrap();
|
||||
public_sub(&mut sum, &public2).unwrap();
|
||||
|
||||
assert_eq!(sum, public1);
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. 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,62 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::{Error, Generator, KeyPair, Random};
|
||||
|
||||
/// Tries to find keypair with address starting with given prefix.
|
||||
pub struct Prefix {
|
||||
prefix: Vec<u8>,
|
||||
iterations: usize,
|
||||
}
|
||||
|
||||
impl Prefix {
|
||||
pub fn new(prefix: Vec<u8>, iterations: usize) -> Self {
|
||||
Prefix {
|
||||
prefix: prefix,
|
||||
iterations: iterations,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Generator for Prefix {
|
||||
type Error = Error;
|
||||
|
||||
fn generate(&mut self) -> Result<KeyPair, Error> {
|
||||
for _ in 0..self.iterations {
|
||||
let keypair = Random.generate()?;
|
||||
if keypair.address().starts_with(&self.prefix) {
|
||||
return Ok(keypair);
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::Custom("Could not find keypair".into()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use Generator;
|
||||
use Prefix;
|
||||
|
||||
#[test]
|
||||
fn prefix_generator() {
|
||||
let prefix = vec![0xffu8];
|
||||
let keypair = Prefix::new(prefix.clone(), usize::max_value())
|
||||
.generate()
|
||||
.unwrap();
|
||||
assert!(keypair.address().starts_with(&prefix));
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::{Generator, KeyPair, SECP256K1};
|
||||
use rand::os::OsRng;
|
||||
|
||||
/// Randomly generates new keypair, instantiating the RNG each time.
|
||||
pub struct Random;
|
||||
|
||||
impl Generator for Random {
|
||||
type Error = ::std::io::Error;
|
||||
|
||||
fn generate(&mut self) -> Result<KeyPair, Self::Error> {
|
||||
let mut rng = OsRng::new()?;
|
||||
match rng.generate() {
|
||||
Ok(pair) => Ok(pair),
|
||||
Err(void) => match void {}, // LLVM unreachable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Generator for OsRng {
|
||||
type Error = ::Void;
|
||||
|
||||
fn generate(&mut self) -> Result<KeyPair, Self::Error> {
|
||||
let (sec, publ) = SECP256K1
|
||||
.generate_keypair(self)
|
||||
.expect("context always created with full capabilities; qed");
|
||||
|
||||
Ok(KeyPair::from_keypair(sec, publ))
|
||||
}
|
||||
}
|
||||
@@ -1,322 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use ethereum_types::H256;
|
||||
use memzero::Memzero;
|
||||
use rustc_hex::ToHex;
|
||||
use secp256k1::{constants::SECRET_KEY_SIZE as SECP256K1_SECRET_KEY_SIZE, key};
|
||||
use std::{fmt, ops::Deref, str::FromStr};
|
||||
use Error;
|
||||
use SECP256K1;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct Secret {
|
||||
inner: Memzero<H256>,
|
||||
}
|
||||
|
||||
impl ToHex for Secret {
|
||||
fn to_hex(&self) -> String {
|
||||
format!("{:x}", *self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::LowerHex for Secret {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.inner.fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Secret {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.inner.fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Secret {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
fmt,
|
||||
"Secret: 0x{:x}{:x}..{:x}{:x}",
|
||||
self.inner[0], self.inner[1], self.inner[30], self.inner[31]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Secret {
|
||||
/// Creates a `Secret` from the given slice, returning `None` if the slice length != 32.
|
||||
pub fn from_slice(key: &[u8]) -> Option<Self> {
|
||||
if key.len() != 32 {
|
||||
return None;
|
||||
}
|
||||
let mut h = H256::default();
|
||||
h.copy_from_slice(&key[0..32]);
|
||||
Some(Secret {
|
||||
inner: Memzero::from(h),
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates zero key, which is invalid for crypto operations, but valid for math operation.
|
||||
pub fn zero() -> Self {
|
||||
Secret {
|
||||
inner: Memzero::from(H256::default()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Imports and validates the key.
|
||||
pub fn from_unsafe_slice(key: &[u8]) -> Result<Self, Error> {
|
||||
let secret = key::SecretKey::from_slice(&super::SECP256K1, key)?;
|
||||
Ok(secret.into())
|
||||
}
|
||||
|
||||
/// Checks validity of this key.
|
||||
pub fn check_validity(&self) -> Result<(), Error> {
|
||||
self.to_secp256k1_secret().map(|_| ())
|
||||
}
|
||||
|
||||
/// Inplace add one secret key to another (scalar + scalar)
|
||||
pub fn add(&mut self, other: &Secret) -> Result<(), Error> {
|
||||
match (self.is_zero(), other.is_zero()) {
|
||||
(true, true) | (false, true) => Ok(()),
|
||||
(true, false) => {
|
||||
*self = other.clone();
|
||||
Ok(())
|
||||
}
|
||||
(false, false) => {
|
||||
let mut key_secret = self.to_secp256k1_secret()?;
|
||||
let other_secret = other.to_secp256k1_secret()?;
|
||||
key_secret.add_assign(&SECP256K1, &other_secret)?;
|
||||
|
||||
*self = key_secret.into();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Inplace subtract one secret key from another (scalar - scalar)
|
||||
pub fn sub(&mut self, other: &Secret) -> Result<(), Error> {
|
||||
match (self.is_zero(), other.is_zero()) {
|
||||
(true, true) | (false, true) => Ok(()),
|
||||
(true, false) => {
|
||||
*self = other.clone();
|
||||
self.neg()
|
||||
}
|
||||
(false, false) => {
|
||||
let mut key_secret = self.to_secp256k1_secret()?;
|
||||
let mut other_secret = other.to_secp256k1_secret()?;
|
||||
other_secret.mul_assign(&SECP256K1, &key::MINUS_ONE_KEY)?;
|
||||
key_secret.add_assign(&SECP256K1, &other_secret)?;
|
||||
|
||||
*self = key_secret.into();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Inplace decrease secret key (scalar - 1)
|
||||
pub fn dec(&mut self) -> Result<(), Error> {
|
||||
match self.is_zero() {
|
||||
true => {
|
||||
*self = key::MINUS_ONE_KEY.into();
|
||||
Ok(())
|
||||
}
|
||||
false => {
|
||||
let mut key_secret = self.to_secp256k1_secret()?;
|
||||
key_secret.add_assign(&SECP256K1, &key::MINUS_ONE_KEY)?;
|
||||
|
||||
*self = key_secret.into();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Inplace multiply one secret key to another (scalar * scalar)
|
||||
pub fn mul(&mut self, other: &Secret) -> Result<(), Error> {
|
||||
match (self.is_zero(), other.is_zero()) {
|
||||
(true, true) | (true, false) => Ok(()),
|
||||
(false, true) => {
|
||||
*self = Self::zero();
|
||||
Ok(())
|
||||
}
|
||||
(false, false) => {
|
||||
let mut key_secret = self.to_secp256k1_secret()?;
|
||||
let other_secret = other.to_secp256k1_secret()?;
|
||||
key_secret.mul_assign(&SECP256K1, &other_secret)?;
|
||||
|
||||
*self = key_secret.into();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Inplace negate secret key (-scalar)
|
||||
pub fn neg(&mut self) -> Result<(), Error> {
|
||||
match self.is_zero() {
|
||||
true => Ok(()),
|
||||
false => {
|
||||
let mut key_secret = self.to_secp256k1_secret()?;
|
||||
key_secret.mul_assign(&SECP256K1, &key::MINUS_ONE_KEY)?;
|
||||
|
||||
*self = key_secret.into();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Inplace inverse secret key (1 / scalar)
|
||||
pub fn inv(&mut self) -> Result<(), Error> {
|
||||
let mut key_secret = self.to_secp256k1_secret()?;
|
||||
key_secret.inv_assign(&SECP256K1)?;
|
||||
|
||||
*self = key_secret.into();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compute power of secret key inplace (secret ^ pow).
|
||||
/// This function is not intended to be used with large powers.
|
||||
pub fn pow(&mut self, pow: usize) -> Result<(), Error> {
|
||||
if self.is_zero() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match pow {
|
||||
0 => *self = key::ONE_KEY.into(),
|
||||
1 => (),
|
||||
_ => {
|
||||
let c = self.clone();
|
||||
for _ in 1..pow {
|
||||
self.mul(&c)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create `secp256k1::key::SecretKey` based on this secret
|
||||
pub fn to_secp256k1_secret(&self) -> Result<key::SecretKey, Error> {
|
||||
Ok(key::SecretKey::from_slice(&SECP256K1, &self[..])?)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Secret {
|
||||
type Err = Error;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(H256::from_str(s)
|
||||
.map_err(|e| Error::Custom(format!("{:?}", e)))?
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; 32]> for Secret {
|
||||
fn from(k: [u8; 32]) -> Self {
|
||||
Secret {
|
||||
inner: Memzero::from(H256(k)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<H256> for Secret {
|
||||
fn from(s: H256) -> Self {
|
||||
s.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static str> for Secret {
|
||||
fn from(s: &'static str) -> Self {
|
||||
s.parse().expect(&format!(
|
||||
"invalid string literal for {}: '{}'",
|
||||
stringify!(Self),
|
||||
s
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<key::SecretKey> for Secret {
|
||||
fn from(key: key::SecretKey) -> Self {
|
||||
let mut a = [0; SECP256K1_SECRET_KEY_SIZE];
|
||||
a.copy_from_slice(&key[0..SECP256K1_SECRET_KEY_SIZE]);
|
||||
a.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Secret {
|
||||
type Target = H256;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{
|
||||
super::{Generator, Random},
|
||||
Secret,
|
||||
};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn multiplicating_secret_inversion_with_secret_gives_one() {
|
||||
let secret = Random.generate().unwrap().secret().clone();
|
||||
let mut inversion = secret.clone();
|
||||
inversion.inv().unwrap();
|
||||
inversion.mul(&secret).unwrap();
|
||||
assert_eq!(
|
||||
inversion,
|
||||
Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001")
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_inversion_is_reversible_with_inversion() {
|
||||
let secret = Random.generate().unwrap().secret().clone();
|
||||
let mut inversion = secret.clone();
|
||||
inversion.inv().unwrap();
|
||||
inversion.inv().unwrap();
|
||||
assert_eq!(inversion, secret);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_pow() {
|
||||
let secret = Random.generate().unwrap().secret().clone();
|
||||
|
||||
let mut pow0 = secret.clone();
|
||||
pow0.pow(0).unwrap();
|
||||
assert_eq!(
|
||||
pow0,
|
||||
Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001")
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
let mut pow1 = secret.clone();
|
||||
pow1.pow(1).unwrap();
|
||||
assert_eq!(pow1, secret);
|
||||
|
||||
let mut pow2 = secret.clone();
|
||||
pow2.pow(2).unwrap();
|
||||
let mut pow2_expected = secret.clone();
|
||||
pow2_expected.mul(&secret).unwrap();
|
||||
assert_eq!(pow2, pow2_expected);
|
||||
|
||||
let mut pow3 = secret.clone();
|
||||
pow3.pow(3).unwrap();
|
||||
let mut pow3_expected = secret.clone();
|
||||
pow3_expected.mul(&secret).unwrap();
|
||||
pow3_expected.mul(&secret).unwrap();
|
||||
assert_eq!(pow3, pow3_expected);
|
||||
}
|
||||
}
|
||||
@@ -1,325 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use ethereum_types::{H256, H520};
|
||||
use public_to_address;
|
||||
use rustc_hex::{FromHex, ToHex};
|
||||
use secp256k1::{
|
||||
key::{PublicKey, SecretKey},
|
||||
Error as SecpError, Message as SecpMessage, RecoverableSignature, RecoveryId,
|
||||
};
|
||||
use std::{
|
||||
cmp::PartialEq,
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
ops::{Deref, DerefMut},
|
||||
str::FromStr,
|
||||
};
|
||||
use Address;
|
||||
use Error;
|
||||
use Message;
|
||||
use Public;
|
||||
use Secret;
|
||||
use SECP256K1;
|
||||
|
||||
/// Signature encoded as RSV components
|
||||
#[repr(C)]
|
||||
pub struct Signature([u8; 65]);
|
||||
|
||||
impl Signature {
|
||||
/// Get a slice into the 'r' portion of the data.
|
||||
pub fn r(&self) -> &[u8] {
|
||||
&self.0[0..32]
|
||||
}
|
||||
|
||||
/// Get a slice into the 's' portion of the data.
|
||||
pub fn s(&self) -> &[u8] {
|
||||
&self.0[32..64]
|
||||
}
|
||||
|
||||
/// Get the recovery byte.
|
||||
pub fn v(&self) -> u8 {
|
||||
self.0[64]
|
||||
}
|
||||
|
||||
/// Encode the signature into RSV array (V altered to be in "Electrum" notation).
|
||||
pub fn into_electrum(mut self) -> [u8; 65] {
|
||||
self.0[64] += 27;
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Parse bytes as a signature encoded as RSV (V in "Electrum" notation).
|
||||
/// May return empty (invalid) signature if given data has invalid length.
|
||||
pub fn from_electrum(data: &[u8]) -> Self {
|
||||
if data.len() != 65 || data[64] < 27 {
|
||||
// fallback to empty (invalid) signature
|
||||
return Signature::default();
|
||||
}
|
||||
|
||||
let mut sig = [0u8; 65];
|
||||
sig.copy_from_slice(data);
|
||||
sig[64] -= 27;
|
||||
Signature(sig)
|
||||
}
|
||||
|
||||
/// Create a signature object from the sig.
|
||||
pub fn from_rsv(r: &H256, s: &H256, v: u8) -> Self {
|
||||
let mut sig = [0u8; 65];
|
||||
sig[0..32].copy_from_slice(&r);
|
||||
sig[32..64].copy_from_slice(&s);
|
||||
sig[64] = v;
|
||||
Signature(sig)
|
||||
}
|
||||
|
||||
/// Check if this is a "low" signature.
|
||||
pub fn is_low_s(&self) -> bool {
|
||||
H256::from_slice(self.s())
|
||||
<= "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0".into()
|
||||
}
|
||||
|
||||
/// Check if each component of the signature is in range.
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.v() <= 1
|
||||
&& H256::from_slice(self.r())
|
||||
< "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".into()
|
||||
&& H256::from_slice(self.r()) >= 1.into()
|
||||
&& H256::from_slice(self.s())
|
||||
< "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".into()
|
||||
&& H256::from_slice(self.s()) >= 1.into()
|
||||
}
|
||||
}
|
||||
|
||||
// manual implementation large arrays don't have trait impls by default.
|
||||
// remove when integer generics exist
|
||||
impl PartialEq for Signature {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
&self.0[..] == &other.0[..]
|
||||
}
|
||||
}
|
||||
|
||||
// manual implementation required in Rust 1.13+, see `std::cmp::AssertParamIsEq`.
|
||||
impl Eq for Signature {}
|
||||
|
||||
// also manual for the same reason, but the pretty printing might be useful.
|
||||
impl fmt::Debug for Signature {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
f.debug_struct("Signature")
|
||||
.field("r", &self.0[0..32].to_hex())
|
||||
.field("s", &self.0[32..64].to_hex())
|
||||
.field("v", &self.0[64..65].to_hex())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Signature {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(f, "{}", self.to_hex())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Signature {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.from_hex() {
|
||||
Ok(ref hex) if hex.len() == 65 => {
|
||||
let mut data = [0; 65];
|
||||
data.copy_from_slice(&hex[0..65]);
|
||||
Ok(Signature(data))
|
||||
}
|
||||
_ => Err(Error::InvalidSignature),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Signature {
|
||||
fn default() -> Self {
|
||||
Signature([0; 65])
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Signature {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
H520::from(self.0).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Signature {
|
||||
fn clone(&self) -> Self {
|
||||
Signature(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; 65]> for Signature {
|
||||
fn from(s: [u8; 65]) -> Self {
|
||||
Signature(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<[u8; 65]> for Signature {
|
||||
fn into(self) -> [u8; 65] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Signature> for H520 {
|
||||
fn from(s: Signature) -> Self {
|
||||
H520::from(s.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<H520> for Signature {
|
||||
fn from(bytes: H520) -> Self {
|
||||
Signature(bytes.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Signature {
|
||||
type Target = [u8; 65];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Signature {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sign(secret: &Secret, message: &Message) -> Result<Signature, Error> {
|
||||
let context = &SECP256K1;
|
||||
let sec = SecretKey::from_slice(context, &secret)?;
|
||||
let s = context.sign_recoverable(&SecpMessage::from_slice(&message[..])?, &sec)?;
|
||||
let (rec_id, data) = s.serialize_compact(context);
|
||||
let mut data_arr = [0; 65];
|
||||
|
||||
// no need to check if s is low, it always is
|
||||
data_arr[0..64].copy_from_slice(&data[0..64]);
|
||||
data_arr[64] = rec_id.to_i32() as u8;
|
||||
Ok(Signature(data_arr))
|
||||
}
|
||||
|
||||
pub fn verify_public(
|
||||
public: &Public,
|
||||
signature: &Signature,
|
||||
message: &Message,
|
||||
) -> Result<bool, Error> {
|
||||
let context = &SECP256K1;
|
||||
let rsig = RecoverableSignature::from_compact(
|
||||
context,
|
||||
&signature[0..64],
|
||||
RecoveryId::from_i32(signature[64] as i32)?,
|
||||
)?;
|
||||
let sig = rsig.to_standard(context);
|
||||
|
||||
let pdata: [u8; 65] = {
|
||||
let mut temp = [4u8; 65];
|
||||
temp[1..65].copy_from_slice(&**public);
|
||||
temp
|
||||
};
|
||||
|
||||
let publ = PublicKey::from_slice(context, &pdata)?;
|
||||
match context.verify(&SecpMessage::from_slice(&message[..])?, &sig, &publ) {
|
||||
Ok(_) => Ok(true),
|
||||
Err(SecpError::IncorrectSignature) => Ok(false),
|
||||
Err(x) => Err(Error::from(x)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verify_address(
|
||||
address: &Address,
|
||||
signature: &Signature,
|
||||
message: &Message,
|
||||
) -> Result<bool, Error> {
|
||||
let public = recover(signature, message)?;
|
||||
let recovered_address = public_to_address(&public);
|
||||
Ok(address == &recovered_address)
|
||||
}
|
||||
|
||||
pub fn recover(signature: &Signature, message: &Message) -> Result<Public, Error> {
|
||||
let context = &SECP256K1;
|
||||
let rsig = RecoverableSignature::from_compact(
|
||||
context,
|
||||
&signature[0..64],
|
||||
RecoveryId::from_i32(signature[64] as i32)?,
|
||||
)?;
|
||||
let pubkey = context.recover(&SecpMessage::from_slice(&message[..])?, &rsig)?;
|
||||
let serialized = pubkey.serialize_vec(context, false);
|
||||
|
||||
let mut public = Public::default();
|
||||
public.copy_from_slice(&serialized[1..65]);
|
||||
Ok(public)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{recover, sign, verify_address, verify_public, Signature};
|
||||
use std::str::FromStr;
|
||||
use Generator;
|
||||
use Message;
|
||||
use Random;
|
||||
|
||||
#[test]
|
||||
fn vrs_conversion() {
|
||||
// given
|
||||
let keypair = Random.generate().unwrap();
|
||||
let message = Message::default();
|
||||
let signature = sign(keypair.secret(), &message).unwrap();
|
||||
|
||||
// when
|
||||
let vrs = signature.clone().into_electrum();
|
||||
let from_vrs = Signature::from_electrum(&vrs);
|
||||
|
||||
// then
|
||||
assert_eq!(signature, from_vrs);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signature_to_and_from_str() {
|
||||
let keypair = Random.generate().unwrap();
|
||||
let message = Message::default();
|
||||
let signature = sign(keypair.secret(), &message).unwrap();
|
||||
let string = format!("{}", signature);
|
||||
let deserialized = Signature::from_str(&string).unwrap();
|
||||
assert_eq!(signature, deserialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sign_and_recover_public() {
|
||||
let keypair = Random.generate().unwrap();
|
||||
let message = Message::default();
|
||||
let signature = sign(keypair.secret(), &message).unwrap();
|
||||
assert_eq!(keypair.public(), &recover(&signature, &message).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sign_and_verify_public() {
|
||||
let keypair = Random.generate().unwrap();
|
||||
let message = Message::default();
|
||||
let signature = sign(keypair.secret(), &message).unwrap();
|
||||
assert!(verify_public(keypair.public(), &signature, &message).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sign_and_verify_address() {
|
||||
let keypair = Random.generate().unwrap();
|
||||
let message = Message::default();
|
||||
let signature = sign(keypair.secret(), &message).unwrap();
|
||||
assert!(verify_address(&keypair.address(), &signature, &message).unwrap());
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use parking_lot::Mutex;
|
||||
use std::{cmp, collections::VecDeque, sync::Arc, thread};
|
||||
|
||||
use ethstore::{ethkey::Password, Error, PresaleWallet};
|
||||
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,363 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate dir;
|
||||
extern crate docopt;
|
||||
extern crate ethstore;
|
||||
extern crate num_cpus;
|
||||
extern crate panic_hook;
|
||||
extern crate parking_lot;
|
||||
extern crate rustc_hex;
|
||||
extern crate serde;
|
||||
|
||||
extern crate env_logger;
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
use std::{collections::VecDeque, env, fmt, fs, io::Read, process};
|
||||
|
||||
use docopt::Docopt;
|
||||
use ethstore::{
|
||||
accounts_dir::{KeyDirectory, RootDiskDirectory},
|
||||
ethkey::{Address, Password},
|
||||
import_accounts, EthStore, PresaleWallet, SecretStore, SecretVaultRef, SimpleSecretStore,
|
||||
StoreAccountRef,
|
||||
};
|
||||
|
||||
mod crack;
|
||||
|
||||
pub const USAGE: &'static str = r#"
|
||||
Parity Ethereum key management tool.
|
||||
Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||
|
||||
Usage:
|
||||
ethstore insert <secret> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore change-pwd <address> <old-pwd> <new-pwd> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore list [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore import [<password>] [--src DIR] [--dir DIR]
|
||||
ethstore import-wallet <path> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore find-wallet-pass <path> <password>
|
||||
ethstore remove <address> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore sign <address> <password> <message> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore public <address> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore list-vaults [--dir DIR]
|
||||
ethstore create-vault <vault> <password> [--dir DIR]
|
||||
ethstore change-vault-pwd <vault> <old-pwd> <new-pwd> [--dir DIR]
|
||||
ethstore move-to-vault <address> <vault> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore move-from-vault <address> <vault> <password> [--dir DIR]
|
||||
ethstore [-h | --help]
|
||||
|
||||
Options:
|
||||
-h, --help Display this message and exit.
|
||||
--dir DIR Specify the secret store directory. It may be either
|
||||
parity, parity-(chain), geth, geth-test
|
||||
or a path [default: parity].
|
||||
--vault VAULT Specify vault to use in this operation.
|
||||
--vault-pwd VAULTPWD Specify vault password to use in this operation. Please note
|
||||
that this option is required when vault option is set.
|
||||
Otherwise it is ignored.
|
||||
--src DIR Specify import source. It may be either
|
||||
parity, parity-(chain), geth, geth-test
|
||||
or a path [default: geth].
|
||||
|
||||
Commands:
|
||||
insert Save account with password.
|
||||
change-pwd Change password.
|
||||
list List accounts.
|
||||
import Import accounts from src.
|
||||
import-wallet Import presale wallet.
|
||||
find-wallet-pass Tries to open a wallet with list of passwords given.
|
||||
remove Remove account.
|
||||
sign Sign message.
|
||||
public Displays public key for an address.
|
||||
list-vaults List vaults.
|
||||
create-vault Create new vault.
|
||||
change-vault-pwd Change vault password.
|
||||
move-to-vault Move account to vault from another vault/root directory.
|
||||
move-from-vault Move account to root directory from given vault.
|
||||
"#;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Args {
|
||||
cmd_insert: bool,
|
||||
cmd_change_pwd: bool,
|
||||
cmd_list: bool,
|
||||
cmd_import: bool,
|
||||
cmd_import_wallet: bool,
|
||||
cmd_find_wallet_pass: bool,
|
||||
cmd_remove: bool,
|
||||
cmd_sign: bool,
|
||||
cmd_public: bool,
|
||||
cmd_list_vaults: bool,
|
||||
cmd_create_vault: bool,
|
||||
cmd_change_vault_pwd: bool,
|
||||
cmd_move_to_vault: bool,
|
||||
cmd_move_from_vault: bool,
|
||||
arg_secret: String,
|
||||
arg_password: String,
|
||||
arg_old_pwd: String,
|
||||
arg_new_pwd: String,
|
||||
arg_address: String,
|
||||
arg_message: String,
|
||||
arg_path: String,
|
||||
arg_vault: String,
|
||||
flag_src: String,
|
||||
flag_dir: String,
|
||||
flag_vault: String,
|
||||
flag_vault_pwd: String,
|
||||
}
|
||||
|
||||
enum Error {
|
||||
Ethstore(ethstore::Error),
|
||||
Docopt(docopt::Error),
|
||||
}
|
||||
|
||||
impl From<ethstore::Error> for Error {
|
||||
fn from(err: ethstore::Error) -> Self {
|
||||
Error::Ethstore(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<docopt::Error> for Error {
|
||||
fn from(err: docopt::Error) -> Self {
|
||||
Error::Docopt(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Error::Ethstore(ref err) => fmt::Display::fmt(err, f),
|
||||
Error::Docopt(ref err) => fmt::Display::fmt(err, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
panic_hook::set_abort();
|
||||
if env::var("RUST_LOG").is_err() {
|
||||
env::set_var("RUST_LOG", "warn")
|
||||
}
|
||||
env_logger::try_init().expect("Logger initialized only once.");
|
||||
|
||||
match execute(env::args()) {
|
||||
Ok(result) => println!("{}", result),
|
||||
Err(Error::Docopt(ref e)) => e.exit(),
|
||||
Err(err) => {
|
||||
eprintln!("{}", err);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn key_dir(location: &str, password: Option<Password>) -> Result<Box<dyn KeyDirectory>, Error> {
|
||||
let dir: RootDiskDirectory = match location {
|
||||
path if path.starts_with("parity") => {
|
||||
let chain = path.split('-').nth(1).unwrap_or("ethereum");
|
||||
let mut path = dir::default_data_pathbuf();
|
||||
path.push("keys");
|
||||
path.push(chain);
|
||||
RootDiskDirectory::create(path)?
|
||||
}
|
||||
path => RootDiskDirectory::create(path)?,
|
||||
};
|
||||
|
||||
Ok(Box::new(dir.with_password(password)))
|
||||
}
|
||||
|
||||
fn open_args_vault(store: &EthStore, args: &Args) -> Result<SecretVaultRef, Error> {
|
||||
if args.flag_vault.is_empty() {
|
||||
return Ok(SecretVaultRef::Root);
|
||||
}
|
||||
|
||||
let vault_pwd = load_password(&args.flag_vault_pwd)?;
|
||||
store.open_vault(&args.flag_vault, &vault_pwd)?;
|
||||
Ok(SecretVaultRef::Vault(args.flag_vault.clone()))
|
||||
}
|
||||
|
||||
fn open_args_vault_account(
|
||||
store: &EthStore,
|
||||
address: Address,
|
||||
args: &Args,
|
||||
) -> Result<StoreAccountRef, Error> {
|
||||
match open_args_vault(store, args)? {
|
||||
SecretVaultRef::Root => Ok(StoreAccountRef::root(address)),
|
||||
SecretVaultRef::Vault(name) => Ok(StoreAccountRef::vault(&name, address)),
|
||||
}
|
||||
}
|
||||
|
||||
fn format_accounts(accounts: &[Address]) -> String {
|
||||
accounts
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, a)| format!("{:2}: 0x{:x}", i, a))
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
fn format_vaults(vaults: &[String]) -> String {
|
||||
vaults.join("\n")
|
||||
}
|
||||
|
||||
fn load_password(path: &str) -> Result<Password, Error> {
|
||||
let mut file = fs::File::open(path).map_err(|e| {
|
||||
ethstore::Error::Custom(format!("Error opening password file '{}': {}", path, e))
|
||||
})?;
|
||||
let mut password = String::new();
|
||||
file.read_to_string(&mut password).map_err(|e| {
|
||||
ethstore::Error::Custom(format!("Error reading password file '{}': {}", path, e))
|
||||
})?;
|
||||
// drop EOF
|
||||
let _ = password.pop();
|
||||
Ok(password.into())
|
||||
}
|
||||
|
||||
fn execute<S, I>(command: I) -> Result<String, Error>
|
||||
where
|
||||
I: IntoIterator<Item = S>,
|
||||
S: AsRef<str>,
|
||||
{
|
||||
let args: Args = Docopt::new(USAGE).and_then(|d| d.argv(command).deserialize())?;
|
||||
|
||||
let store = EthStore::open(key_dir(&args.flag_dir, None)?)?;
|
||||
|
||||
return if args.cmd_insert {
|
||||
let secret = args
|
||||
.arg_secret
|
||||
.parse()
|
||||
.map_err(|_| ethstore::Error::InvalidSecret)?;
|
||||
let password = load_password(&args.arg_password)?;
|
||||
let vault_ref = open_args_vault(&store, &args)?;
|
||||
let account_ref = store.insert_account(vault_ref, secret, &password)?;
|
||||
Ok(format!("0x{:x}", account_ref.address))
|
||||
} else if args.cmd_change_pwd {
|
||||
let address = args
|
||||
.arg_address
|
||||
.parse()
|
||||
.map_err(|_| ethstore::Error::InvalidAccount)?;
|
||||
let old_pwd = load_password(&args.arg_old_pwd)?;
|
||||
let new_pwd = load_password(&args.arg_new_pwd)?;
|
||||
let account_ref = open_args_vault_account(&store, address, &args)?;
|
||||
let ok = store
|
||||
.change_password(&account_ref, &old_pwd, &new_pwd)
|
||||
.is_ok();
|
||||
Ok(format!("{}", ok))
|
||||
} else if args.cmd_list {
|
||||
let vault_ref = open_args_vault(&store, &args)?;
|
||||
let accounts = store.accounts()?;
|
||||
let accounts: Vec<_> = accounts
|
||||
.into_iter()
|
||||
.filter(|a| &a.vault == &vault_ref)
|
||||
.map(|a| a.address)
|
||||
.collect();
|
||||
Ok(format_accounts(&accounts))
|
||||
} else if args.cmd_import {
|
||||
let password = match args.arg_password.as_ref() {
|
||||
"" => None,
|
||||
_ => Some(load_password(&args.arg_password)?),
|
||||
};
|
||||
let src = key_dir(&args.flag_src, password)?;
|
||||
let dst = key_dir(&args.flag_dir, None)?;
|
||||
|
||||
let accounts = import_accounts(&*src, &*dst)?;
|
||||
Ok(format_accounts(&accounts))
|
||||
} else if args.cmd_import_wallet {
|
||||
let wallet = PresaleWallet::open(&args.arg_path)?;
|
||||
let password = load_password(&args.arg_password)?;
|
||||
let kp = wallet.decrypt(&password)?;
|
||||
let vault_ref = open_args_vault(&store, &args)?;
|
||||
let account_ref = store.insert_account(vault_ref, kp.secret().clone(), &password)?;
|
||||
Ok(format!("0x{:x}", account_ref.address))
|
||||
} else if args.cmd_find_wallet_pass {
|
||||
let passwords = load_password(&args.arg_password)?;
|
||||
let passwords = passwords
|
||||
.as_str()
|
||||
.lines()
|
||||
.map(|line| str::to_owned(line).into())
|
||||
.collect::<VecDeque<_>>();
|
||||
crack::run(passwords, &args.arg_path)?;
|
||||
Ok(format!("Password not found."))
|
||||
} else if args.cmd_remove {
|
||||
let address = args
|
||||
.arg_address
|
||||
.parse()
|
||||
.map_err(|_| ethstore::Error::InvalidAccount)?;
|
||||
let password = load_password(&args.arg_password)?;
|
||||
let account_ref = open_args_vault_account(&store, address, &args)?;
|
||||
let ok = store.remove_account(&account_ref, &password).is_ok();
|
||||
Ok(format!("{}", ok))
|
||||
} else if args.cmd_sign {
|
||||
let address = args
|
||||
.arg_address
|
||||
.parse()
|
||||
.map_err(|_| ethstore::Error::InvalidAccount)?;
|
||||
let message = args
|
||||
.arg_message
|
||||
.parse()
|
||||
.map_err(|_| ethstore::Error::InvalidMessage)?;
|
||||
let password = load_password(&args.arg_password)?;
|
||||
let account_ref = open_args_vault_account(&store, address, &args)?;
|
||||
let signature = store.sign(&account_ref, &password, &message)?;
|
||||
Ok(format!("0x{}", signature))
|
||||
} else if args.cmd_public {
|
||||
let address = args
|
||||
.arg_address
|
||||
.parse()
|
||||
.map_err(|_| ethstore::Error::InvalidAccount)?;
|
||||
let password = load_password(&args.arg_password)?;
|
||||
let account_ref = open_args_vault_account(&store, address, &args)?;
|
||||
let public = store.public(&account_ref, &password)?;
|
||||
Ok(format!("0x{:x}", public))
|
||||
} else if args.cmd_list_vaults {
|
||||
let vaults = store.list_vaults()?;
|
||||
Ok(format_vaults(&vaults))
|
||||
} else if args.cmd_create_vault {
|
||||
let password = load_password(&args.arg_password)?;
|
||||
store.create_vault(&args.arg_vault, &password)?;
|
||||
Ok("OK".to_owned())
|
||||
} else if args.cmd_change_vault_pwd {
|
||||
let old_pwd = load_password(&args.arg_old_pwd)?;
|
||||
let new_pwd = load_password(&args.arg_new_pwd)?;
|
||||
store.open_vault(&args.arg_vault, &old_pwd)?;
|
||||
store.change_vault_password(&args.arg_vault, &new_pwd)?;
|
||||
Ok("OK".to_owned())
|
||||
} else if args.cmd_move_to_vault {
|
||||
let address = args
|
||||
.arg_address
|
||||
.parse()
|
||||
.map_err(|_| ethstore::Error::InvalidAccount)?;
|
||||
let password = load_password(&args.arg_password)?;
|
||||
let account_ref = open_args_vault_account(&store, address, &args)?;
|
||||
store.open_vault(&args.arg_vault, &password)?;
|
||||
store.change_account_vault(SecretVaultRef::Vault(args.arg_vault), account_ref)?;
|
||||
Ok("OK".to_owned())
|
||||
} else if args.cmd_move_from_vault {
|
||||
let address = args
|
||||
.arg_address
|
||||
.parse()
|
||||
.map_err(|_| ethstore::Error::InvalidAccount)?;
|
||||
let password = load_password(&args.arg_password)?;
|
||||
store.open_vault(&args.arg_vault, &password)?;
|
||||
store.change_account_vault(
|
||||
SecretVaultRef::Root,
|
||||
StoreAccountRef::vault(&args.arg_vault, address),
|
||||
)?;
|
||||
Ok("OK".to_owned())
|
||||
} else {
|
||||
Ok(format!("{}", USAGE))
|
||||
};
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use json;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Aes128Ctr {
|
||||
pub iv: [u8; 16],
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Cipher {
|
||||
Aes128Ctr(Aes128Ctr),
|
||||
}
|
||||
|
||||
impl From<json::Aes128Ctr> for Aes128Ctr {
|
||||
fn from(json: json::Aes128Ctr) -> Self {
|
||||
Aes128Ctr { iv: json.iv.into() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<json::Aes128Ctr> for Aes128Ctr {
|
||||
fn into(self) -> json::Aes128Ctr {
|
||||
json::Aes128Ctr {
|
||||
iv: From::from(self.iv),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<json::Cipher> for Cipher {
|
||||
fn from(json: json::Cipher) -> Self {
|
||||
match json {
|
||||
json::Cipher::Aes128Ctr(params) => Cipher::Aes128Ctr(From::from(params)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<json::Cipher> for Cipher {
|
||||
fn into(self) -> json::Cipher {
|
||||
match self {
|
||||
Cipher::Aes128Ctr(params) => json::Cipher::Aes128Ctr(params.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,234 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use account::{Aes128Ctr, Cipher, Kdf, Pbkdf2, Prf};
|
||||
use crypto::{self, Keccak256};
|
||||
use ethkey::{Password, Secret};
|
||||
use json;
|
||||
use random::Random;
|
||||
use smallvec::SmallVec;
|
||||
use std::{num::NonZeroU32, str};
|
||||
use Error;
|
||||
|
||||
/// Encrypted data
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Crypto {
|
||||
/// Encryption parameters
|
||||
pub cipher: Cipher,
|
||||
/// Encrypted data buffer
|
||||
pub ciphertext: Vec<u8>,
|
||||
/// Key derivation function parameters
|
||||
pub kdf: Kdf,
|
||||
/// Message authentication code
|
||||
pub mac: [u8; 32],
|
||||
}
|
||||
|
||||
impl From<json::Crypto> for Crypto {
|
||||
fn from(json: json::Crypto) -> Self {
|
||||
Crypto {
|
||||
cipher: json.cipher.into(),
|
||||
ciphertext: json.ciphertext.into(),
|
||||
kdf: json.kdf.into(),
|
||||
mac: json.mac.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Crypto> for json::Crypto {
|
||||
fn from(c: Crypto) -> Self {
|
||||
json::Crypto {
|
||||
cipher: c.cipher.into(),
|
||||
ciphertext: c.ciphertext.into(),
|
||||
kdf: c.kdf.into(),
|
||||
mac: c.mac.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl str::FromStr for Crypto {
|
||||
type Err = <json::Crypto as str::FromStr>::Err;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
s.parse::<json::Crypto>().map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Crypto> for String {
|
||||
fn from(c: Crypto) -> Self {
|
||||
json::Crypto::from(c).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Crypto {
|
||||
/// Encrypt account secret
|
||||
pub fn with_secret(
|
||||
secret: &Secret,
|
||||
password: &Password,
|
||||
iterations: NonZeroU32,
|
||||
) -> Result<Self, crypto::Error> {
|
||||
Crypto::with_plain(&*secret, password, iterations)
|
||||
}
|
||||
|
||||
/// Encrypt custom plain data
|
||||
pub fn with_plain(
|
||||
plain: &[u8],
|
||||
password: &Password,
|
||||
iterations: NonZeroU32,
|
||||
) -> Result<Self, crypto::Error> {
|
||||
let salt: [u8; 32] = Random::random();
|
||||
let iv: [u8; 16] = Random::random();
|
||||
|
||||
// two parts of derived key
|
||||
// DK = [ DK[0..15] DK[16..31] ] = [derived_left_bits, derived_right_bits]
|
||||
let (derived_left_bits, derived_right_bits) =
|
||||
crypto::derive_key_iterations(password.as_bytes(), &salt, iterations);
|
||||
|
||||
// preallocated (on-stack in case of `Secret`) buffer to hold cipher
|
||||
// length = length(plain) as we are using CTR-approach
|
||||
let plain_len = plain.len();
|
||||
let mut ciphertext: SmallVec<[u8; 32]> = SmallVec::from_vec(vec![0; plain_len]);
|
||||
|
||||
// aes-128-ctr with initial vector of iv
|
||||
crypto::aes::encrypt_128_ctr(&derived_left_bits, &iv, plain, &mut *ciphertext)?;
|
||||
|
||||
// KECCAK(DK[16..31] ++ <ciphertext>), where DK[16..31] - derived_right_bits
|
||||
let mac = crypto::derive_mac(&derived_right_bits, &*ciphertext).keccak256();
|
||||
|
||||
Ok(Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr { iv: iv }),
|
||||
ciphertext: ciphertext.into_vec(),
|
||||
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
||||
dklen: crypto::KEY_LENGTH as u32,
|
||||
salt: salt.to_vec(),
|
||||
c: iterations,
|
||||
prf: Prf::HmacSha256,
|
||||
}),
|
||||
mac: mac,
|
||||
})
|
||||
}
|
||||
|
||||
/// Try to decrypt and convert result to account secret
|
||||
pub fn secret(&self, password: &Password) -> Result<Secret, Error> {
|
||||
if self.ciphertext.len() > 32 {
|
||||
return Err(Error::InvalidSecret);
|
||||
}
|
||||
|
||||
let secret = self.do_decrypt(password, 32)?;
|
||||
Ok(Secret::from_unsafe_slice(&secret)?)
|
||||
}
|
||||
|
||||
/// Try to decrypt and return result as is
|
||||
pub fn decrypt(&self, password: &Password) -> Result<Vec<u8>, Error> {
|
||||
let expected_len = self.ciphertext.len();
|
||||
self.do_decrypt(password, expected_len)
|
||||
}
|
||||
|
||||
fn do_decrypt(&self, password: &Password, expected_len: usize) -> Result<Vec<u8>, Error> {
|
||||
let (derived_left_bits, derived_right_bits) = match self.kdf {
|
||||
Kdf::Pbkdf2(ref params) => {
|
||||
crypto::derive_key_iterations(password.as_bytes(), ¶ms.salt, params.c)
|
||||
}
|
||||
Kdf::Scrypt(ref params) => crypto::scrypt::derive_key(
|
||||
password.as_bytes(),
|
||||
¶ms.salt,
|
||||
params.n,
|
||||
params.p,
|
||||
params.r,
|
||||
)?,
|
||||
};
|
||||
|
||||
let mac = crypto::derive_mac(&derived_right_bits, &self.ciphertext).keccak256();
|
||||
|
||||
if !crypto::is_equal(&mac, &self.mac) {
|
||||
return Err(Error::InvalidPassword);
|
||||
}
|
||||
|
||||
let mut plain: SmallVec<[u8; 32]> = SmallVec::from_vec(vec![0; expected_len]);
|
||||
|
||||
match self.cipher {
|
||||
Cipher::Aes128Ctr(ref params) => {
|
||||
// checker by callers
|
||||
debug_assert!(expected_len >= self.ciphertext.len());
|
||||
|
||||
let from = expected_len - self.ciphertext.len();
|
||||
crypto::aes::decrypt_128_ctr(
|
||||
&derived_left_bits,
|
||||
¶ms.iv,
|
||||
&self.ciphertext,
|
||||
&mut plain[from..],
|
||||
)?;
|
||||
Ok(plain.into_iter().collect())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Crypto, Error, NonZeroU32};
|
||||
use ethkey::{Generator, Random};
|
||||
|
||||
lazy_static! {
|
||||
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crypto_with_secret_create() {
|
||||
let keypair = Random.generate().unwrap();
|
||||
let passwd = "this is sparta".into();
|
||||
let crypto = Crypto::with_secret(keypair.secret(), &passwd, *ITERATIONS).unwrap();
|
||||
let secret = crypto.secret(&passwd).unwrap();
|
||||
assert_eq!(keypair.secret(), &secret);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crypto_with_secret_invalid_password() {
|
||||
let keypair = Random.generate().unwrap();
|
||||
let crypto =
|
||||
Crypto::with_secret(keypair.secret(), &"this is sparta".into(), *ITERATIONS).unwrap();
|
||||
assert_matches!(
|
||||
crypto.secret(&"this is sparta!".into()),
|
||||
Err(Error::InvalidPassword)
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crypto_with_null_plain_data() {
|
||||
let original_data = b"";
|
||||
let passwd = "this is sparta".into();
|
||||
let crypto = Crypto::with_plain(&original_data[..], &passwd, *ITERATIONS).unwrap();
|
||||
let decrypted_data = crypto.decrypt(&passwd).unwrap();
|
||||
assert_eq!(original_data[..], *decrypted_data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crypto_with_tiny_plain_data() {
|
||||
let original_data = b"{}";
|
||||
let passwd = "this is sparta".into();
|
||||
let crypto = Crypto::with_plain(&original_data[..], &passwd, *ITERATIONS).unwrap();
|
||||
let decrypted_data = crypto.decrypt(&passwd).unwrap();
|
||||
assert_eq!(original_data[..], *decrypted_data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crypto_with_huge_plain_data() {
|
||||
let original_data: Vec<_> = (1..65536).map(|i| (i % 256) as u8).collect();
|
||||
let passwd = "this is sparta".into();
|
||||
let crypto = Crypto::with_plain(&original_data, &passwd, *ITERATIONS).unwrap();
|
||||
let decrypted_data = crypto.decrypt(&passwd).unwrap();
|
||||
assert_eq!(&original_data, &decrypted_data);
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use json;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Prf {
|
||||
HmacSha256,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Pbkdf2 {
|
||||
pub c: NonZeroU32,
|
||||
pub dklen: u32,
|
||||
pub prf: Prf,
|
||||
pub salt: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Scrypt {
|
||||
pub dklen: u32,
|
||||
pub p: u32,
|
||||
pub n: u32,
|
||||
pub r: u32,
|
||||
pub salt: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Kdf {
|
||||
Pbkdf2(Pbkdf2),
|
||||
Scrypt(Scrypt),
|
||||
}
|
||||
|
||||
impl From<json::Prf> for Prf {
|
||||
fn from(json: json::Prf) -> Self {
|
||||
match json {
|
||||
json::Prf::HmacSha256 => Prf::HmacSha256,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<json::Prf> for Prf {
|
||||
fn into(self) -> json::Prf {
|
||||
match self {
|
||||
Prf::HmacSha256 => json::Prf::HmacSha256,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<json::Pbkdf2> for Pbkdf2 {
|
||||
fn from(json: json::Pbkdf2) -> Self {
|
||||
Pbkdf2 {
|
||||
c: json.c,
|
||||
dklen: json.dklen,
|
||||
prf: From::from(json.prf),
|
||||
salt: json.salt.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<json::Pbkdf2> for Pbkdf2 {
|
||||
fn into(self) -> json::Pbkdf2 {
|
||||
json::Pbkdf2 {
|
||||
c: self.c,
|
||||
dklen: self.dklen,
|
||||
prf: self.prf.into(),
|
||||
salt: From::from(self.salt),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<json::Scrypt> for Scrypt {
|
||||
fn from(json: json::Scrypt) -> Self {
|
||||
Scrypt {
|
||||
dklen: json.dklen,
|
||||
p: json.p,
|
||||
n: json.n,
|
||||
r: json.r,
|
||||
salt: json.salt.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<json::Scrypt> for Scrypt {
|
||||
fn into(self) -> json::Scrypt {
|
||||
json::Scrypt {
|
||||
dklen: self.dklen,
|
||||
p: self.p,
|
||||
n: self.n,
|
||||
r: self.r,
|
||||
salt: From::from(self.salt),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<json::Kdf> for Kdf {
|
||||
fn from(json: json::Kdf) -> Self {
|
||||
match json {
|
||||
json::Kdf::Pbkdf2(params) => Kdf::Pbkdf2(From::from(params)),
|
||||
json::Kdf::Scrypt(params) => Kdf::Scrypt(From::from(params)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<json::Kdf> for Kdf {
|
||||
fn into(self) -> json::Kdf {
|
||||
match self {
|
||||
Kdf::Pbkdf2(params) => json::Kdf::Pbkdf2(params.into()),
|
||||
Kdf::Scrypt(params) => json::Kdf::Scrypt(params.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,286 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::crypto::Crypto;
|
||||
use account::Version;
|
||||
use crypto;
|
||||
use ethkey::{
|
||||
self, crypto::ecdh::agree, sign, Address, KeyPair, Message, Password, Public, Secret, Signature,
|
||||
};
|
||||
use json;
|
||||
use std::num::NonZeroU32;
|
||||
use Error;
|
||||
|
||||
/// Account representation.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct SafeAccount {
|
||||
/// Account ID
|
||||
pub id: [u8; 16],
|
||||
/// Account version
|
||||
pub version: Version,
|
||||
/// Account address
|
||||
pub address: Address,
|
||||
/// Account private key derivation definition.
|
||||
pub crypto: Crypto,
|
||||
/// Account filename
|
||||
pub filename: Option<String>,
|
||||
/// Account name
|
||||
pub name: String,
|
||||
/// Account metadata
|
||||
pub meta: String,
|
||||
}
|
||||
|
||||
impl Into<json::KeyFile> for SafeAccount {
|
||||
fn into(self) -> json::KeyFile {
|
||||
json::KeyFile {
|
||||
id: From::from(self.id),
|
||||
version: self.version.into(),
|
||||
address: Some(self.address.into()),
|
||||
crypto: self.crypto.into(),
|
||||
name: Some(self.name.into()),
|
||||
meta: Some(self.meta.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SafeAccount {
|
||||
/// Create a new account
|
||||
pub fn create(
|
||||
keypair: &KeyPair,
|
||||
id: [u8; 16],
|
||||
password: &Password,
|
||||
iterations: NonZeroU32,
|
||||
name: String,
|
||||
meta: String,
|
||||
) -> Result<Self, crypto::Error> {
|
||||
Ok(SafeAccount {
|
||||
id: id,
|
||||
version: Version::V3,
|
||||
crypto: Crypto::with_secret(keypair.secret(), password, iterations)?,
|
||||
address: keypair.address(),
|
||||
filename: None,
|
||||
name: name,
|
||||
meta: meta,
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a new `SafeAccount` from the given `json`; if it was read from a
|
||||
/// file, the `filename` should be `Some` name. If it is as yet anonymous, then it
|
||||
/// can be left `None`.
|
||||
/// In case `password` is provided, we will attempt to read the secret from the keyfile
|
||||
/// and derive the address from it instead of reading it directly.
|
||||
/// Providing password is required for `json::KeyFile`s with no address.
|
||||
pub fn from_file(
|
||||
json: json::KeyFile,
|
||||
filename: Option<String>,
|
||||
password: &Option<Password>,
|
||||
) -> Result<Self, Error> {
|
||||
let crypto = Crypto::from(json.crypto);
|
||||
let address = match (password, &json.address) {
|
||||
(None, Some(json_address)) => json_address.into(),
|
||||
(None, None) => Err(Error::Custom(
|
||||
"This keystore does not contain address. You need to provide password to import it"
|
||||
.into(),
|
||||
))?,
|
||||
(Some(password), json_address) => {
|
||||
let derived_address = KeyPair::from_secret(
|
||||
crypto
|
||||
.secret(&password)
|
||||
.map_err(|_| Error::InvalidPassword)?,
|
||||
)?
|
||||
.address();
|
||||
|
||||
match json_address {
|
||||
Some(json_address) => {
|
||||
let json_address = json_address.into();
|
||||
if derived_address != json_address {
|
||||
warn!("Detected address mismatch when opening an account. Derived: {:?}, in json got: {:?}",
|
||||
derived_address, json_address);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
derived_address
|
||||
}
|
||||
};
|
||||
|
||||
Ok(SafeAccount {
|
||||
id: json.id.into(),
|
||||
version: json.version.into(),
|
||||
address,
|
||||
crypto,
|
||||
filename,
|
||||
name: json.name.unwrap_or(String::new()),
|
||||
meta: json.meta.unwrap_or("{}".to_owned()),
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a new `SafeAccount` from the given vault `json`; if it was read from a
|
||||
/// file, the `filename` should be `Some` name. If it is as yet anonymous, then it
|
||||
/// can be left `None`.
|
||||
pub fn from_vault_file(
|
||||
password: &Password,
|
||||
json: json::VaultKeyFile,
|
||||
filename: Option<String>,
|
||||
) -> Result<Self, Error> {
|
||||
let meta_crypto: Crypto = json.metacrypto.into();
|
||||
let meta_plain = meta_crypto.decrypt(password)?;
|
||||
let meta_plain =
|
||||
json::VaultKeyMeta::load(&meta_plain).map_err(|e| Error::Custom(format!("{:?}", e)))?;
|
||||
|
||||
SafeAccount::from_file(
|
||||
json::KeyFile {
|
||||
id: json.id,
|
||||
version: json.version,
|
||||
crypto: json.crypto,
|
||||
address: Some(meta_plain.address),
|
||||
name: meta_plain.name,
|
||||
meta: meta_plain.meta,
|
||||
},
|
||||
filename,
|
||||
&None,
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a new `VaultKeyFile` from the given `self`
|
||||
pub fn into_vault_file(
|
||||
self,
|
||||
iterations: NonZeroU32,
|
||||
password: &Password,
|
||||
) -> Result<json::VaultKeyFile, Error> {
|
||||
let meta_plain = json::VaultKeyMeta {
|
||||
address: self.address.into(),
|
||||
name: Some(self.name),
|
||||
meta: Some(self.meta),
|
||||
};
|
||||
let meta_plain = meta_plain
|
||||
.write()
|
||||
.map_err(|e| Error::Custom(format!("{:?}", e)))?;
|
||||
let meta_crypto = Crypto::with_plain(&meta_plain, password, iterations)?;
|
||||
|
||||
Ok(json::VaultKeyFile {
|
||||
id: self.id.into(),
|
||||
version: self.version.into(),
|
||||
crypto: self.crypto.into(),
|
||||
metacrypto: meta_crypto.into(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Sign a message.
|
||||
pub fn sign(&self, password: &Password, message: &Message) -> Result<Signature, Error> {
|
||||
let secret = self.crypto.secret(password)?;
|
||||
sign(&secret, message).map_err(From::from)
|
||||
}
|
||||
|
||||
/// Decrypt a message.
|
||||
pub fn decrypt(
|
||||
&self,
|
||||
password: &Password,
|
||||
shared_mac: &[u8],
|
||||
message: &[u8],
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
let secret = self.crypto.secret(password)?;
|
||||
ethkey::crypto::ecies::decrypt(&secret, shared_mac, message).map_err(From::from)
|
||||
}
|
||||
|
||||
/// Agree on shared key.
|
||||
pub fn agree(&self, password: &Password, other: &Public) -> Result<Secret, Error> {
|
||||
let secret = self.crypto.secret(password)?;
|
||||
agree(&secret, other).map_err(From::from)
|
||||
}
|
||||
|
||||
/// Derive public key.
|
||||
pub fn public(&self, password: &Password) -> Result<Public, Error> {
|
||||
let secret = self.crypto.secret(password)?;
|
||||
Ok(KeyPair::from_secret(secret)?.public().clone())
|
||||
}
|
||||
|
||||
/// Change account's password.
|
||||
pub fn change_password(
|
||||
&self,
|
||||
old_password: &Password,
|
||||
new_password: &Password,
|
||||
iterations: NonZeroU32,
|
||||
) -> Result<Self, Error> {
|
||||
let secret = self.crypto.secret(old_password)?;
|
||||
let result = SafeAccount {
|
||||
id: self.id.clone(),
|
||||
version: self.version.clone(),
|
||||
crypto: Crypto::with_secret(&secret, new_password, iterations)?,
|
||||
address: self.address.clone(),
|
||||
filename: self.filename.clone(),
|
||||
name: self.name.clone(),
|
||||
meta: self.meta.clone(),
|
||||
};
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Check if password matches the account.
|
||||
pub fn check_password(&self, password: &Password) -> bool {
|
||||
self.crypto.secret(password).is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{NonZeroU32, SafeAccount};
|
||||
use ethkey::{verify_public, Generator, Message, Random};
|
||||
|
||||
lazy_static! {
|
||||
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sign_and_verify_public() {
|
||||
let keypair = Random.generate().unwrap();
|
||||
let password = "hello world".into();
|
||||
let message = Message::default();
|
||||
let account = SafeAccount::create(
|
||||
&keypair,
|
||||
[0u8; 16],
|
||||
&password,
|
||||
*ITERATIONS,
|
||||
"Test".to_owned(),
|
||||
"{}".to_owned(),
|
||||
);
|
||||
let signature = account.unwrap().sign(&password, &message).unwrap();
|
||||
assert!(verify_public(keypair.public(), &signature, &message).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_password() {
|
||||
let keypair = Random.generate().unwrap();
|
||||
let first_password = "hello world".into();
|
||||
let sec_password = "this is sparta".into();
|
||||
let message = Message::default();
|
||||
let account = SafeAccount::create(
|
||||
&keypair,
|
||||
[0u8; 16],
|
||||
&first_password,
|
||||
*ITERATIONS,
|
||||
"Test".to_owned(),
|
||||
"{}".to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
let new_account = account
|
||||
.change_password(&first_password, &sec_password, *ITERATIONS)
|
||||
.unwrap();
|
||||
assert!(account.sign(&first_password, &message).is_ok());
|
||||
assert!(account.sign(&sec_password, &message).is_err());
|
||||
assert!(new_account.sign(&first_password, &message).is_err());
|
||||
assert!(new_account.sign(&sec_password, &message).is_ok());
|
||||
}
|
||||
}
|
||||
@@ -1,608 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::{
|
||||
vault::{VaultDiskDirectory, VAULT_FILE_NAME},
|
||||
KeyDirectory, VaultKey, VaultKeyDirectory, VaultKeyDirectoryProvider,
|
||||
};
|
||||
use ethkey::Password;
|
||||
use json::{self, Uuid};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs, io,
|
||||
io::Write,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use time;
|
||||
use Error;
|
||||
use SafeAccount;
|
||||
|
||||
const IGNORED_FILES: &'static [&'static str] = &[
|
||||
"thumbs.db",
|
||||
"address_book.json",
|
||||
"dapps_policy.json",
|
||||
"dapps_accounts.json",
|
||||
"dapps_history.json",
|
||||
"vault.json",
|
||||
];
|
||||
|
||||
/// Find a unique filename that does not exist using four-letter random suffix.
|
||||
pub fn find_unique_filename_using_random_suffix(
|
||||
parent_path: &Path,
|
||||
original_filename: &str,
|
||||
) -> io::Result<String> {
|
||||
let mut path = parent_path.join(original_filename);
|
||||
let mut deduped_filename = original_filename.to_string();
|
||||
|
||||
if path.exists() {
|
||||
const MAX_RETRIES: usize = 500;
|
||||
let mut retries = 0;
|
||||
|
||||
while path.exists() {
|
||||
if retries >= MAX_RETRIES {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"Exceeded maximum retries when deduplicating filename.",
|
||||
));
|
||||
}
|
||||
|
||||
let suffix = ::random::random_string(4);
|
||||
deduped_filename = format!("{}-{}", original_filename, suffix);
|
||||
path.set_file_name(&deduped_filename);
|
||||
retries += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(deduped_filename)
|
||||
}
|
||||
|
||||
/// Create a new file and restrict permissions to owner only. It errors if the file already exists.
|
||||
#[cfg(unix)]
|
||||
pub fn create_new_file_with_permissions_to_owner(file_path: &Path) -> io::Result<fs::File> {
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
|
||||
fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.mode((libc::S_IWUSR | libc::S_IRUSR) as u32)
|
||||
.open(file_path)
|
||||
}
|
||||
|
||||
/// Create a new file and restrict permissions to owner only. It errors if the file already exists.
|
||||
#[cfg(not(unix))]
|
||||
pub fn create_new_file_with_permissions_to_owner(file_path: &Path) -> io::Result<fs::File> {
|
||||
fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(file_path)
|
||||
}
|
||||
|
||||
/// Create a new file and restrict permissions to owner only. It replaces the existing file if it already exists.
|
||||
#[cfg(unix)]
|
||||
pub fn replace_file_with_permissions_to_owner(file_path: &Path) -> io::Result<fs::File> {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let file = fs::File::create(file_path)?;
|
||||
let mut permissions = file.metadata()?.permissions();
|
||||
permissions.set_mode((libc::S_IWUSR | libc::S_IRUSR) as u32);
|
||||
file.set_permissions(permissions)?;
|
||||
|
||||
Ok(file)
|
||||
}
|
||||
|
||||
/// Create a new file and restrict permissions to owner only. It replaces the existing file if it already exists.
|
||||
#[cfg(not(unix))]
|
||||
pub fn replace_file_with_permissions_to_owner(file_path: &Path) -> io::Result<fs::File> {
|
||||
fs::File::create(file_path)
|
||||
}
|
||||
|
||||
/// Root keys directory implementation
|
||||
pub type RootDiskDirectory = DiskDirectory<DiskKeyFileManager>;
|
||||
|
||||
/// Disk directory key file manager
|
||||
pub trait KeyFileManager: Send + Sync {
|
||||
/// Read `SafeAccount` from given key file stream
|
||||
fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error>
|
||||
where
|
||||
T: io::Read;
|
||||
|
||||
/// Write `SafeAccount` to given key file stream
|
||||
fn write<T>(&self, account: SafeAccount, writer: &mut T) -> Result<(), Error>
|
||||
where
|
||||
T: io::Write;
|
||||
}
|
||||
|
||||
/// Disk-based keys directory implementation
|
||||
pub struct DiskDirectory<T>
|
||||
where
|
||||
T: KeyFileManager,
|
||||
{
|
||||
path: PathBuf,
|
||||
key_manager: T,
|
||||
}
|
||||
|
||||
/// Keys file manager for root keys directory
|
||||
#[derive(Default)]
|
||||
pub struct DiskKeyFileManager {
|
||||
password: Option<Password>,
|
||||
}
|
||||
|
||||
impl RootDiskDirectory {
|
||||
pub fn create<P>(path: P) -> Result<Self, Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
fs::create_dir_all(&path)?;
|
||||
Ok(Self::at(path))
|
||||
}
|
||||
|
||||
/// allows to read keyfiles with given password (needed for keyfiles w/o address)
|
||||
pub fn with_password(&self, password: Option<Password>) -> Self {
|
||||
DiskDirectory::new(&self.path, DiskKeyFileManager { password })
|
||||
}
|
||||
|
||||
pub fn at<P>(path: P) -> Self
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
DiskDirectory::new(path, DiskKeyFileManager::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DiskDirectory<T>
|
||||
where
|
||||
T: KeyFileManager,
|
||||
{
|
||||
/// Create new disk directory instance
|
||||
pub fn new<P>(path: P, key_manager: T) -> Self
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
DiskDirectory {
|
||||
path: path.as_ref().to_path_buf(),
|
||||
key_manager: key_manager,
|
||||
}
|
||||
}
|
||||
|
||||
fn files(&self) -> Result<Vec<PathBuf>, Error> {
|
||||
Ok(fs::read_dir(&self.path)?
|
||||
.flat_map(Result::ok)
|
||||
.filter(|entry| {
|
||||
let metadata = entry.metadata().ok();
|
||||
let file_name = entry.file_name();
|
||||
let name = file_name.to_string_lossy();
|
||||
// filter directories
|
||||
metadata.map_or(false, |m| !m.is_dir()) &&
|
||||
// hidden files
|
||||
!name.starts_with(".") &&
|
||||
// other ignored files
|
||||
!IGNORED_FILES.contains(&&*name)
|
||||
})
|
||||
.map(|entry| entry.path())
|
||||
.collect::<Vec<PathBuf>>())
|
||||
}
|
||||
|
||||
pub fn files_hash(&self) -> Result<u64, Error> {
|
||||
use std::{collections::hash_map::DefaultHasher, hash::Hasher};
|
||||
|
||||
let mut hasher = DefaultHasher::new();
|
||||
let files = self.files()?;
|
||||
for file in files {
|
||||
hasher.write(file.to_str().unwrap_or("").as_bytes())
|
||||
}
|
||||
|
||||
Ok(hasher.finish())
|
||||
}
|
||||
|
||||
fn last_modification_date(&self) -> Result<u64, Error> {
|
||||
use std::time::{Duration, UNIX_EPOCH};
|
||||
let duration = fs::metadata(&self.path)?
|
||||
.modified()?
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap_or(Duration::default());
|
||||
let timestamp = duration.as_secs() ^ (duration.subsec_nanos() as u64);
|
||||
Ok(timestamp)
|
||||
}
|
||||
|
||||
/// all accounts found in keys directory
|
||||
fn files_content(&self) -> Result<HashMap<PathBuf, SafeAccount>, Error> {
|
||||
// it's not done using one iterator cause
|
||||
// there is an issue with rustc and it takes tooo much time to compile
|
||||
let paths = self.files()?;
|
||||
Ok(paths
|
||||
.into_iter()
|
||||
.filter_map(|path| {
|
||||
let filename = Some(
|
||||
path.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.expect("Keys have valid UTF8 names only.")
|
||||
.to_owned(),
|
||||
);
|
||||
fs::File::open(path.clone())
|
||||
.map_err(Into::into)
|
||||
.and_then(|file| self.key_manager.read(filename, file))
|
||||
.map_err(|err| {
|
||||
warn!("Invalid key file: {:?} ({})", path, err);
|
||||
err
|
||||
})
|
||||
.map(|account| (path, account))
|
||||
.ok()
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// insert account with given filename. if the filename is a duplicate of any stored account and dedup is set to
|
||||
/// true, a random suffix is appended to the filename.
|
||||
pub fn insert_with_filename(
|
||||
&self,
|
||||
account: SafeAccount,
|
||||
mut filename: String,
|
||||
dedup: bool,
|
||||
) -> Result<SafeAccount, Error> {
|
||||
if dedup {
|
||||
filename = find_unique_filename_using_random_suffix(&self.path, &filename)?;
|
||||
}
|
||||
|
||||
// path to keyfile
|
||||
let keyfile_path = self.path.join(filename.as_str());
|
||||
|
||||
// update account filename
|
||||
let original_account = account.clone();
|
||||
let mut account = account;
|
||||
account.filename = Some(filename);
|
||||
|
||||
{
|
||||
// save the file
|
||||
let mut file = if dedup {
|
||||
create_new_file_with_permissions_to_owner(&keyfile_path)?
|
||||
} else {
|
||||
replace_file_with_permissions_to_owner(&keyfile_path)?
|
||||
};
|
||||
|
||||
// write key content
|
||||
self.key_manager
|
||||
.write(original_account, &mut file)
|
||||
.map_err(|e| Error::Custom(format!("{:?}", e)))?;
|
||||
|
||||
file.flush()?;
|
||||
file.sync_all()?;
|
||||
}
|
||||
|
||||
Ok(account)
|
||||
}
|
||||
|
||||
/// Get key file manager referece
|
||||
pub fn key_manager(&self) -> &T {
|
||||
&self.key_manager
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> KeyDirectory for DiskDirectory<T>
|
||||
where
|
||||
T: KeyFileManager,
|
||||
{
|
||||
fn load(&self) -> Result<Vec<SafeAccount>, Error> {
|
||||
let accounts = self
|
||||
.files_content()?
|
||||
.into_iter()
|
||||
.map(|(_, account)| account)
|
||||
.collect();
|
||||
Ok(accounts)
|
||||
}
|
||||
|
||||
fn update(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
|
||||
// Disk store handles updates correctly iff filename is the same
|
||||
let filename = account_filename(&account);
|
||||
self.insert_with_filename(account, filename, false)
|
||||
}
|
||||
|
||||
fn insert(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
|
||||
let filename = account_filename(&account);
|
||||
self.insert_with_filename(account, filename, true)
|
||||
}
|
||||
|
||||
fn remove(&self, account: &SafeAccount) -> Result<(), Error> {
|
||||
// enumerate all entries in keystore
|
||||
// and find entry with given address
|
||||
let to_remove = self
|
||||
.files_content()?
|
||||
.into_iter()
|
||||
.find(|&(_, ref acc)| acc.id == account.id && acc.address == account.address);
|
||||
|
||||
// remove it
|
||||
match to_remove {
|
||||
None => Err(Error::InvalidAccount),
|
||||
Some((path, _)) => fs::remove_file(path).map_err(From::from),
|
||||
}
|
||||
}
|
||||
|
||||
fn path(&self) -> Option<&PathBuf> {
|
||||
Some(&self.path)
|
||||
}
|
||||
|
||||
fn as_vault_provider(&self) -> Option<&dyn VaultKeyDirectoryProvider> {
|
||||
Some(self)
|
||||
}
|
||||
|
||||
fn unique_repr(&self) -> Result<u64, Error> {
|
||||
self.last_modification_date()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> VaultKeyDirectoryProvider for DiskDirectory<T>
|
||||
where
|
||||
T: KeyFileManager,
|
||||
{
|
||||
fn create(&self, name: &str, key: VaultKey) -> Result<Box<dyn VaultKeyDirectory>, Error> {
|
||||
let vault_dir = VaultDiskDirectory::create(&self.path, name, key)?;
|
||||
Ok(Box::new(vault_dir))
|
||||
}
|
||||
|
||||
fn open(&self, name: &str, key: VaultKey) -> Result<Box<dyn VaultKeyDirectory>, Error> {
|
||||
let vault_dir = VaultDiskDirectory::at(&self.path, name, key)?;
|
||||
Ok(Box::new(vault_dir))
|
||||
}
|
||||
|
||||
fn list_vaults(&self) -> Result<Vec<String>, Error> {
|
||||
Ok(fs::read_dir(&self.path)?
|
||||
.filter_map(|e| e.ok().map(|e| e.path()))
|
||||
.filter_map(|path| {
|
||||
let mut vault_file_path = path.clone();
|
||||
vault_file_path.push(VAULT_FILE_NAME);
|
||||
if vault_file_path.is_file() {
|
||||
path.file_name()
|
||||
.and_then(|f| f.to_str())
|
||||
.map(|f| f.to_owned())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn vault_meta(&self, name: &str) -> Result<String, Error> {
|
||||
VaultDiskDirectory::meta_at(&self.path, name)
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyFileManager for DiskKeyFileManager {
|
||||
fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error>
|
||||
where
|
||||
T: io::Read,
|
||||
{
|
||||
let key_file =
|
||||
json::KeyFile::load(reader).map_err(|e| Error::Custom(format!("{:?}", e)))?;
|
||||
SafeAccount::from_file(key_file, filename, &self.password)
|
||||
}
|
||||
|
||||
fn write<T>(&self, mut account: SafeAccount, writer: &mut T) -> Result<(), Error>
|
||||
where
|
||||
T: io::Write,
|
||||
{
|
||||
// when account is moved back to root directory from vault
|
||||
// => remove vault field from meta
|
||||
account.meta = json::remove_vault_name_from_json_meta(&account.meta)
|
||||
.map_err(|err| Error::Custom(format!("{:?}", err)))?;
|
||||
|
||||
let key_file: json::KeyFile = account.into();
|
||||
key_file
|
||||
.write(writer)
|
||||
.map_err(|e| Error::Custom(format!("{:?}", e)))
|
||||
}
|
||||
}
|
||||
|
||||
fn account_filename(account: &SafeAccount) -> String {
|
||||
// build file path
|
||||
account.filename.clone().unwrap_or_else(|| {
|
||||
let timestamp = time::strftime("%Y-%m-%dT%H-%M-%S", &time::now_utc())
|
||||
.expect("Time-format string is valid.");
|
||||
format!("UTC--{}Z--{}", timestamp, Uuid::from(account.id))
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
extern crate tempdir;
|
||||
|
||||
use self::tempdir::TempDir;
|
||||
use super::{KeyDirectory, RootDiskDirectory, VaultKey};
|
||||
use account::SafeAccount;
|
||||
use ethkey::{Generator, Random};
|
||||
use std::{env, fs, num::NonZeroU32};
|
||||
|
||||
lazy_static! {
|
||||
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(1024).expect("1024 > 0; qed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_create_new_account() {
|
||||
// given
|
||||
let mut dir = env::temp_dir();
|
||||
dir.push("ethstore_should_create_new_account");
|
||||
let keypair = Random.generate().unwrap();
|
||||
let password = "hello world".into();
|
||||
let directory = RootDiskDirectory::create(dir.clone()).unwrap();
|
||||
|
||||
// when
|
||||
let account = SafeAccount::create(
|
||||
&keypair,
|
||||
[0u8; 16],
|
||||
&password,
|
||||
*ITERATIONS,
|
||||
"Test".to_owned(),
|
||||
"{}".to_owned(),
|
||||
);
|
||||
let res = directory.insert(account.unwrap());
|
||||
|
||||
// then
|
||||
assert!(res.is_ok(), "Should save account succesfuly.");
|
||||
assert!(
|
||||
res.unwrap().filename.is_some(),
|
||||
"Filename has been assigned."
|
||||
);
|
||||
|
||||
// cleanup
|
||||
let _ = fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_handle_duplicate_filenames() {
|
||||
// given
|
||||
let mut dir = env::temp_dir();
|
||||
dir.push("ethstore_should_handle_duplicate_filenames");
|
||||
let keypair = Random.generate().unwrap();
|
||||
let password = "hello world".into();
|
||||
let directory = RootDiskDirectory::create(dir.clone()).unwrap();
|
||||
|
||||
// when
|
||||
let account = SafeAccount::create(
|
||||
&keypair,
|
||||
[0u8; 16],
|
||||
&password,
|
||||
*ITERATIONS,
|
||||
"Test".to_owned(),
|
||||
"{}".to_owned(),
|
||||
)
|
||||
.unwrap();
|
||||
let filename = "test".to_string();
|
||||
let dedup = true;
|
||||
|
||||
directory
|
||||
.insert_with_filename(account.clone(), "foo".to_string(), dedup)
|
||||
.unwrap();
|
||||
let file1 = directory
|
||||
.insert_with_filename(account.clone(), filename.clone(), dedup)
|
||||
.unwrap()
|
||||
.filename
|
||||
.unwrap();
|
||||
let file2 = directory
|
||||
.insert_with_filename(account.clone(), filename.clone(), dedup)
|
||||
.unwrap()
|
||||
.filename
|
||||
.unwrap();
|
||||
let file3 = directory
|
||||
.insert_with_filename(account.clone(), filename.clone(), dedup)
|
||||
.unwrap()
|
||||
.filename
|
||||
.unwrap();
|
||||
|
||||
// then
|
||||
// the first file should have the original names
|
||||
assert_eq!(file1, filename);
|
||||
|
||||
// the following duplicate files should have a suffix appended
|
||||
assert!(file2 != file3);
|
||||
assert_eq!(file2.len(), filename.len() + 5);
|
||||
assert_eq!(file3.len(), filename.len() + 5);
|
||||
|
||||
// cleanup
|
||||
let _ = fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_manage_vaults() {
|
||||
// given
|
||||
let mut dir = env::temp_dir();
|
||||
dir.push("should_create_new_vault");
|
||||
let directory = RootDiskDirectory::create(dir.clone()).unwrap();
|
||||
let vault_name = "vault";
|
||||
let password = "password".into();
|
||||
|
||||
// then
|
||||
assert!(directory.as_vault_provider().is_some());
|
||||
|
||||
// and when
|
||||
let before_root_items_count = fs::read_dir(&dir).unwrap().count();
|
||||
let vault = directory
|
||||
.as_vault_provider()
|
||||
.unwrap()
|
||||
.create(vault_name, VaultKey::new(&password, *ITERATIONS));
|
||||
|
||||
// then
|
||||
assert!(vault.is_ok());
|
||||
let after_root_items_count = fs::read_dir(&dir).unwrap().count();
|
||||
assert!(after_root_items_count > before_root_items_count);
|
||||
|
||||
// and when
|
||||
let vault = directory
|
||||
.as_vault_provider()
|
||||
.unwrap()
|
||||
.open(vault_name, VaultKey::new(&password, *ITERATIONS));
|
||||
|
||||
// then
|
||||
assert!(vault.is_ok());
|
||||
let after_root_items_count2 = fs::read_dir(&dir).unwrap().count();
|
||||
assert!(after_root_items_count == after_root_items_count2);
|
||||
|
||||
// cleanup
|
||||
let _ = fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_list_vaults() {
|
||||
// given
|
||||
let temp_path = TempDir::new("").unwrap();
|
||||
let directory = RootDiskDirectory::create(&temp_path).unwrap();
|
||||
let vault_provider = directory.as_vault_provider().unwrap();
|
||||
let iter = NonZeroU32::new(1).expect("1 > 0; qed");
|
||||
vault_provider
|
||||
.create("vault1", VaultKey::new(&"password1".into(), iter))
|
||||
.unwrap();
|
||||
vault_provider
|
||||
.create("vault2", VaultKey::new(&"password2".into(), iter))
|
||||
.unwrap();
|
||||
|
||||
// then
|
||||
let vaults = vault_provider.list_vaults().unwrap();
|
||||
assert_eq!(vaults.len(), 2);
|
||||
assert!(vaults.iter().any(|v| &*v == "vault1"));
|
||||
assert!(vaults.iter().any(|v| &*v == "vault2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hash_of_files() {
|
||||
let temp_path = TempDir::new("").unwrap();
|
||||
let directory = RootDiskDirectory::create(&temp_path).unwrap();
|
||||
|
||||
let hash = directory
|
||||
.files_hash()
|
||||
.expect("Files hash should be calculated ok");
|
||||
assert_eq!(hash, 15130871412783076140);
|
||||
|
||||
let keypair = Random.generate().unwrap();
|
||||
let password = "test pass".into();
|
||||
let account = SafeAccount::create(
|
||||
&keypair,
|
||||
[0u8; 16],
|
||||
&password,
|
||||
*ITERATIONS,
|
||||
"Test".to_owned(),
|
||||
"{}".to_owned(),
|
||||
);
|
||||
directory
|
||||
.insert(account.unwrap())
|
||||
.expect("Account should be inserted ok");
|
||||
|
||||
let new_hash = directory
|
||||
.files_hash()
|
||||
.expect("New files hash should be calculated ok");
|
||||
|
||||
assert!(
|
||||
new_hash != hash,
|
||||
"hash of the file list should change once directory content changed"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use ethkey::Address;
|
||||
use itertools;
|
||||
use parking_lot::RwLock;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::KeyDirectory;
|
||||
use Error;
|
||||
use SafeAccount;
|
||||
|
||||
/// Accounts in-memory storage.
|
||||
#[derive(Default)]
|
||||
pub struct MemoryDirectory {
|
||||
accounts: RwLock<HashMap<Address, Vec<SafeAccount>>>,
|
||||
}
|
||||
|
||||
impl KeyDirectory for MemoryDirectory {
|
||||
fn load(&self) -> Result<Vec<SafeAccount>, Error> {
|
||||
Ok(itertools::Itertools::flatten(self.accounts.read().values().cloned()).collect())
|
||||
}
|
||||
|
||||
fn update(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
|
||||
let mut lock = self.accounts.write();
|
||||
let accounts = lock.entry(account.address.clone()).or_insert_with(Vec::new);
|
||||
// If the filename is the same we just need to replace the entry
|
||||
accounts.retain(|acc| acc.filename != account.filename);
|
||||
accounts.push(account.clone());
|
||||
Ok(account)
|
||||
}
|
||||
|
||||
fn insert(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
|
||||
let mut lock = self.accounts.write();
|
||||
let accounts = lock.entry(account.address.clone()).or_insert_with(Vec::new);
|
||||
accounts.push(account.clone());
|
||||
Ok(account)
|
||||
}
|
||||
|
||||
fn remove(&self, account: &SafeAccount) -> Result<(), Error> {
|
||||
let mut accounts = self.accounts.write();
|
||||
let is_empty = if let Some(accounts) = accounts.get_mut(&account.address) {
|
||||
if let Some(position) = accounts.iter().position(|acc| acc == account) {
|
||||
accounts.remove(position);
|
||||
}
|
||||
accounts.is_empty()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if is_empty {
|
||||
accounts.remove(&account.address);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unique_repr(&self) -> Result<u64, Error> {
|
||||
let mut val = 0u64;
|
||||
let accounts = self.accounts.read();
|
||||
for acc in accounts.keys() {
|
||||
val = val ^ acc.low_u64()
|
||||
}
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Accounts Directory
|
||||
|
||||
use ethkey::Password;
|
||||
use std::{num::NonZeroU32, path::PathBuf};
|
||||
use Error;
|
||||
use SafeAccount;
|
||||
|
||||
mod disk;
|
||||
mod memory;
|
||||
mod vault;
|
||||
|
||||
/// `VaultKeyDirectory::set_key` error
|
||||
#[derive(Debug)]
|
||||
pub enum SetKeyError {
|
||||
/// Error is fatal and directory is probably in inconsistent state
|
||||
Fatal(Error),
|
||||
/// Error is non fatal, directory is reverted to pre-operation state
|
||||
NonFatalOld(Error),
|
||||
/// Error is non fatal, directory is consistent with new key
|
||||
NonFatalNew(Error),
|
||||
}
|
||||
|
||||
/// Vault key
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct VaultKey {
|
||||
/// Vault password
|
||||
pub password: Password,
|
||||
/// Number of iterations to produce a derived key from password
|
||||
pub iterations: NonZeroU32,
|
||||
}
|
||||
|
||||
/// Keys directory
|
||||
pub trait KeyDirectory: Send + Sync {
|
||||
/// Read keys from directory
|
||||
fn load(&self) -> Result<Vec<SafeAccount>, Error>;
|
||||
/// Insert new key to directory
|
||||
fn insert(&self, account: SafeAccount) -> Result<SafeAccount, Error>;
|
||||
/// Update key in the directory
|
||||
fn update(&self, account: SafeAccount) -> Result<SafeAccount, Error>;
|
||||
/// Remove key from directory
|
||||
fn remove(&self, account: &SafeAccount) -> Result<(), Error>;
|
||||
/// Get directory filesystem path, if available
|
||||
fn path(&self) -> Option<&PathBuf> {
|
||||
None
|
||||
}
|
||||
/// Return vault provider, if available
|
||||
fn as_vault_provider(&self) -> Option<&dyn VaultKeyDirectoryProvider> {
|
||||
None
|
||||
}
|
||||
/// Unique representation of directory account collection
|
||||
fn unique_repr(&self) -> Result<u64, Error>;
|
||||
}
|
||||
|
||||
/// Vaults provider
|
||||
pub trait VaultKeyDirectoryProvider {
|
||||
/// Create new vault with given key
|
||||
fn create(&self, name: &str, key: VaultKey) -> Result<Box<dyn VaultKeyDirectory>, Error>;
|
||||
/// Open existing vault with given key
|
||||
fn open(&self, name: &str, key: VaultKey) -> Result<Box<dyn VaultKeyDirectory>, Error>;
|
||||
/// List all vaults
|
||||
fn list_vaults(&self) -> Result<Vec<String>, Error>;
|
||||
/// Get vault meta
|
||||
fn vault_meta(&self, name: &str) -> Result<String, Error>;
|
||||
}
|
||||
|
||||
/// Vault directory
|
||||
pub trait VaultKeyDirectory: KeyDirectory {
|
||||
/// Cast to `KeyDirectory`
|
||||
fn as_key_directory(&self) -> &dyn KeyDirectory;
|
||||
/// Vault name
|
||||
fn name(&self) -> &str;
|
||||
/// Get vault key
|
||||
fn key(&self) -> VaultKey;
|
||||
/// Set new key for vault
|
||||
fn set_key(&self, key: VaultKey) -> Result<(), SetKeyError>;
|
||||
/// Get vault meta
|
||||
fn meta(&self) -> String;
|
||||
/// Set vault meta
|
||||
fn set_meta(&self, meta: &str) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
pub use self::{
|
||||
disk::{DiskKeyFileManager, KeyFileManager, RootDiskDirectory},
|
||||
memory::MemoryDirectory,
|
||||
vault::VaultDiskDirectory,
|
||||
};
|
||||
|
||||
impl VaultKey {
|
||||
/// Create new vault key
|
||||
pub fn new(password: &Password, iterations: NonZeroU32) -> Self {
|
||||
VaultKey {
|
||||
password: password.clone(),
|
||||
iterations: iterations,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,527 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::{
|
||||
super::account::Crypto,
|
||||
disk::{self, DiskDirectory, KeyFileManager},
|
||||
KeyDirectory, SetKeyError, VaultKey, VaultKeyDirectory,
|
||||
};
|
||||
use crypto::Keccak256;
|
||||
use json;
|
||||
use parking_lot::Mutex;
|
||||
use std::{
|
||||
fs, io,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use Error;
|
||||
use SafeAccount;
|
||||
|
||||
/// Name of vault metadata file
|
||||
pub const VAULT_FILE_NAME: &'static str = "vault.json";
|
||||
/// Name of temporary vault metadata file
|
||||
pub const VAULT_TEMP_FILE_NAME: &'static str = "vault_temp.json";
|
||||
|
||||
/// Vault directory implementation
|
||||
pub type VaultDiskDirectory = DiskDirectory<VaultKeyFileManager>;
|
||||
|
||||
/// Vault key file manager
|
||||
pub struct VaultKeyFileManager {
|
||||
name: String,
|
||||
key: VaultKey,
|
||||
meta: Mutex<String>,
|
||||
}
|
||||
|
||||
impl VaultDiskDirectory {
|
||||
/// Create new vault directory with given key
|
||||
pub fn create<P>(root: P, name: &str, key: VaultKey) -> Result<Self, Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
// check that vault directory does not exists
|
||||
let vault_dir_path = make_vault_dir_path(root, name, true)?;
|
||||
if vault_dir_path.exists() {
|
||||
return Err(Error::CreationFailed);
|
||||
}
|
||||
|
||||
// create vault && vault file
|
||||
let vault_meta = "{}";
|
||||
fs::create_dir_all(&vault_dir_path)?;
|
||||
if let Err(err) = create_vault_file(&vault_dir_path, &key, vault_meta) {
|
||||
let _ = fs::remove_dir_all(&vault_dir_path); // can't do anything with this
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
Ok(DiskDirectory::new(
|
||||
vault_dir_path,
|
||||
VaultKeyFileManager::new(name, key, vault_meta),
|
||||
))
|
||||
}
|
||||
|
||||
/// Open existing vault directory with given key
|
||||
pub fn at<P>(root: P, name: &str, key: VaultKey) -> Result<Self, Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
// check that vault directory exists
|
||||
let vault_dir_path = make_vault_dir_path(root, name, true)?;
|
||||
if !vault_dir_path.is_dir() {
|
||||
return Err(Error::CreationFailed);
|
||||
}
|
||||
|
||||
// check that passed key matches vault file
|
||||
let meta = read_vault_file(&vault_dir_path, Some(&key))?;
|
||||
|
||||
Ok(DiskDirectory::new(
|
||||
vault_dir_path,
|
||||
VaultKeyFileManager::new(name, key, &meta),
|
||||
))
|
||||
}
|
||||
|
||||
/// Read vault meta without actually opening the vault
|
||||
pub fn meta_at<P>(root: P, name: &str) -> Result<String, Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
// check that vault directory exists
|
||||
let vault_dir_path = make_vault_dir_path(root, name, true)?;
|
||||
if !vault_dir_path.is_dir() {
|
||||
return Err(Error::VaultNotFound);
|
||||
}
|
||||
|
||||
// check that passed key matches vault file
|
||||
read_vault_file(&vault_dir_path, None)
|
||||
}
|
||||
|
||||
fn create_temp_vault(&self, key: VaultKey) -> Result<VaultDiskDirectory, Error> {
|
||||
let original_path = self
|
||||
.path()
|
||||
.expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed");
|
||||
let mut path: PathBuf = original_path.clone();
|
||||
let name = self.name();
|
||||
|
||||
path.push(name); // to jump to the next level
|
||||
|
||||
let mut index = 0;
|
||||
loop {
|
||||
let name = format!("{}_temp_{}", name, index);
|
||||
path.set_file_name(&name);
|
||||
if !path.exists() {
|
||||
return VaultDiskDirectory::create(original_path, &name, key);
|
||||
}
|
||||
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_to_vault(&self, vault: &VaultDiskDirectory) -> Result<(), Error> {
|
||||
for account in self.load()? {
|
||||
let filename = account.filename.clone().expect(
|
||||
"self is instance of DiskDirectory; DiskDirectory fills filename in load; qed",
|
||||
);
|
||||
vault.insert_with_filename(account, filename, true)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn delete(&self) -> Result<(), Error> {
|
||||
let path = self
|
||||
.path()
|
||||
.expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed");
|
||||
fs::remove_dir_all(path).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl VaultKeyDirectory for VaultDiskDirectory {
|
||||
fn as_key_directory(&self) -> &dyn KeyDirectory {
|
||||
self
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.key_manager().name
|
||||
}
|
||||
|
||||
fn key(&self) -> VaultKey {
|
||||
self.key_manager().key.clone()
|
||||
}
|
||||
|
||||
fn set_key(&self, new_key: VaultKey) -> Result<(), SetKeyError> {
|
||||
let temp_vault = VaultDiskDirectory::create_temp_vault(self, new_key.clone())
|
||||
.map_err(|err| SetKeyError::NonFatalOld(err))?;
|
||||
let mut source_path = temp_vault
|
||||
.path()
|
||||
.expect(
|
||||
"temp_vault is instance of DiskDirectory; DiskDirectory always returns path; qed",
|
||||
)
|
||||
.clone();
|
||||
let mut target_path = self
|
||||
.path()
|
||||
.expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed")
|
||||
.clone();
|
||||
|
||||
// preserve meta
|
||||
temp_vault
|
||||
.set_meta(&self.meta())
|
||||
.map_err(SetKeyError::NonFatalOld)?;
|
||||
|
||||
// jump to next fs level
|
||||
source_path.push("next");
|
||||
target_path.push("next");
|
||||
|
||||
let temp_accounts = self
|
||||
.copy_to_vault(&temp_vault)
|
||||
.and_then(|_| temp_vault.load())
|
||||
.map_err(|err| {
|
||||
// ignore error, as we already processing error
|
||||
let _ = temp_vault.delete();
|
||||
SetKeyError::NonFatalOld(err)
|
||||
})?;
|
||||
|
||||
// we can't just delete temp vault until all files moved, because
|
||||
// original vault content has already been partially replaced
|
||||
// => when error or crash happens here, we can't do anything
|
||||
for temp_account in temp_accounts {
|
||||
let filename = temp_account.filename.expect(
|
||||
"self is instance of DiskDirectory; DiskDirectory fills filename in load; qed",
|
||||
);
|
||||
source_path.set_file_name(&filename);
|
||||
target_path.set_file_name(&filename);
|
||||
fs::rename(&source_path, &target_path).map_err(|err| SetKeyError::Fatal(err.into()))?;
|
||||
}
|
||||
source_path.set_file_name(VAULT_FILE_NAME);
|
||||
target_path.set_file_name(VAULT_FILE_NAME);
|
||||
fs::rename(source_path, target_path).map_err(|err| SetKeyError::Fatal(err.into()))?;
|
||||
|
||||
temp_vault
|
||||
.delete()
|
||||
.map_err(|err| SetKeyError::NonFatalNew(err))
|
||||
}
|
||||
|
||||
fn meta(&self) -> String {
|
||||
self.key_manager().meta.lock().clone()
|
||||
}
|
||||
|
||||
fn set_meta(&self, meta: &str) -> Result<(), Error> {
|
||||
let key_manager = self.key_manager();
|
||||
let vault_path = self
|
||||
.path()
|
||||
.expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed");
|
||||
create_vault_file(vault_path, &key_manager.key, meta)?;
|
||||
*key_manager.meta.lock() = meta.to_owned();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl VaultKeyFileManager {
|
||||
pub fn new(name: &str, key: VaultKey, meta: &str) -> Self {
|
||||
VaultKeyFileManager {
|
||||
name: name.into(),
|
||||
key: key,
|
||||
meta: Mutex::new(meta.to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyFileManager for VaultKeyFileManager {
|
||||
fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error>
|
||||
where
|
||||
T: io::Read,
|
||||
{
|
||||
let vault_file =
|
||||
json::VaultKeyFile::load(reader).map_err(|e| Error::Custom(format!("{:?}", e)))?;
|
||||
let mut safe_account =
|
||||
SafeAccount::from_vault_file(&self.key.password, vault_file, filename.clone())?;
|
||||
|
||||
safe_account.meta = json::insert_vault_name_to_json_meta(&safe_account.meta, &self.name)
|
||||
.map_err(|err| Error::Custom(format!("{:?}", err)))?;
|
||||
Ok(safe_account)
|
||||
}
|
||||
|
||||
fn write<T>(&self, mut account: SafeAccount, writer: &mut T) -> Result<(), Error>
|
||||
where
|
||||
T: io::Write,
|
||||
{
|
||||
account.meta = json::remove_vault_name_from_json_meta(&account.meta)
|
||||
.map_err(|err| Error::Custom(format!("{:?}", err)))?;
|
||||
|
||||
let vault_file: json::VaultKeyFile =
|
||||
account.into_vault_file(self.key.iterations, &self.key.password)?;
|
||||
vault_file
|
||||
.write(writer)
|
||||
.map_err(|e| Error::Custom(format!("{:?}", e)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes path to vault directory, checking that vault name is appropriate
|
||||
fn make_vault_dir_path<P>(root: P, name: &str, check_name: bool) -> Result<PathBuf, Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
// check vault name
|
||||
if check_name && !check_vault_name(name) {
|
||||
return Err(Error::InvalidVaultName);
|
||||
}
|
||||
|
||||
let mut vault_dir_path: PathBuf = root.as_ref().into();
|
||||
vault_dir_path.push(name);
|
||||
Ok(vault_dir_path)
|
||||
}
|
||||
|
||||
/// Every vault must have unique name => we rely on filesystem to check this
|
||||
/// => vault name must not contain any fs-special characters to avoid directory traversal
|
||||
/// => we only allow alphanumeric + separator characters in vault name.
|
||||
fn check_vault_name(name: &str) -> bool {
|
||||
!name.is_empty()
|
||||
&& name
|
||||
.chars()
|
||||
.all(|c| c.is_alphanumeric() || c.is_whitespace() || c == '-' || c == '_')
|
||||
}
|
||||
|
||||
/// Vault can be empty, but still must be pluggable => we store vault password in separate file
|
||||
fn create_vault_file<P>(vault_dir_path: P, key: &VaultKey, meta: &str) -> Result<(), Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let password_hash = key.password.as_bytes().keccak256();
|
||||
let crypto = Crypto::with_plain(&password_hash, &key.password, key.iterations)?;
|
||||
|
||||
let vault_file_path = vault_dir_path.as_ref().join(VAULT_FILE_NAME);
|
||||
let temp_vault_file_name = disk::find_unique_filename_using_random_suffix(
|
||||
vault_dir_path.as_ref(),
|
||||
&VAULT_TEMP_FILE_NAME,
|
||||
)?;
|
||||
let temp_vault_file_path = vault_dir_path.as_ref().join(&temp_vault_file_name);
|
||||
|
||||
// this method is used to rewrite existing vault file
|
||||
// => write to temporary file first, then rename temporary file to vault file
|
||||
let mut vault_file = disk::create_new_file_with_permissions_to_owner(&temp_vault_file_path)?;
|
||||
let vault_file_contents = json::VaultFile {
|
||||
crypto: crypto.into(),
|
||||
meta: Some(meta.to_owned()),
|
||||
};
|
||||
vault_file_contents
|
||||
.write(&mut vault_file)
|
||||
.map_err(|e| Error::Custom(format!("{:?}", e)))?;
|
||||
drop(vault_file);
|
||||
fs::rename(&temp_vault_file_path, &vault_file_path)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// When vault is opened => we must check that password matches && read metadata
|
||||
fn read_vault_file<P>(vault_dir_path: P, key: Option<&VaultKey>) -> Result<String, Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let mut vault_file_path: PathBuf = vault_dir_path.as_ref().into();
|
||||
vault_file_path.push(VAULT_FILE_NAME);
|
||||
|
||||
let vault_file = fs::File::open(vault_file_path)?;
|
||||
let vault_file_contents =
|
||||
json::VaultFile::load(vault_file).map_err(|e| Error::Custom(format!("{:?}", e)))?;
|
||||
let vault_file_meta = vault_file_contents.meta.unwrap_or("{}".to_owned());
|
||||
let vault_file_crypto: Crypto = vault_file_contents.crypto.into();
|
||||
|
||||
if let Some(key) = key {
|
||||
let password_bytes = vault_file_crypto.decrypt(&key.password)?;
|
||||
let password_hash = key.password.as_bytes().keccak256();
|
||||
if password_hash != password_bytes.as_slice() {
|
||||
return Err(Error::InvalidPassword);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(vault_file_meta)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
extern crate tempdir;
|
||||
|
||||
use self::tempdir::TempDir;
|
||||
use super::{
|
||||
check_vault_name, create_vault_file, make_vault_dir_path, read_vault_file,
|
||||
VaultDiskDirectory, VaultKey, VAULT_FILE_NAME,
|
||||
};
|
||||
use std::{fs, io::Write, num::NonZeroU32, path::PathBuf};
|
||||
|
||||
lazy_static! {
|
||||
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(1024).expect("1024 > 0; qed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_vault_name_succeeds() {
|
||||
assert!(check_vault_name("vault"));
|
||||
assert!(check_vault_name("vault with spaces"));
|
||||
assert!(check_vault_name("vault with tabs"));
|
||||
assert!(check_vault_name("vault_with_underscores"));
|
||||
assert!(check_vault_name("vault-with-dashes"));
|
||||
assert!(check_vault_name("vault-with-digits-123"));
|
||||
assert!(check_vault_name("vault中文名字"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_vault_name_fails() {
|
||||
assert!(!check_vault_name(""));
|
||||
assert!(!check_vault_name("."));
|
||||
assert!(!check_vault_name("*"));
|
||||
assert!(!check_vault_name("../.bash_history"));
|
||||
assert!(!check_vault_name("/etc/passwd"));
|
||||
assert!(!check_vault_name("c:\\windows"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn make_vault_dir_path_succeeds() {
|
||||
use std::path::Path;
|
||||
|
||||
assert_eq!(
|
||||
&make_vault_dir_path("/home/user/parity", "vault", true).unwrap(),
|
||||
&Path::new("/home/user/parity/vault")
|
||||
);
|
||||
assert_eq!(
|
||||
&make_vault_dir_path("/home/user/parity", "*bad-name*", false).unwrap(),
|
||||
&Path::new("/home/user/parity/*bad-name*")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn make_vault_dir_path_fails() {
|
||||
assert!(make_vault_dir_path("/home/user/parity", "*bad-name*", true).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_vault_file_succeeds() {
|
||||
// given
|
||||
let temp_path = TempDir::new("").unwrap();
|
||||
let key = VaultKey::new(&"password".into(), *ITERATIONS);
|
||||
let mut vault_dir: PathBuf = temp_path.path().into();
|
||||
vault_dir.push("vault");
|
||||
fs::create_dir_all(&vault_dir).unwrap();
|
||||
|
||||
// when
|
||||
let result = create_vault_file(&vault_dir, &key, "{}");
|
||||
|
||||
// then
|
||||
assert!(result.is_ok());
|
||||
let mut vault_file_path = vault_dir.clone();
|
||||
vault_file_path.push(VAULT_FILE_NAME);
|
||||
assert!(vault_file_path.exists() && vault_file_path.is_file());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_vault_file_succeeds() {
|
||||
// given
|
||||
let temp_path = TempDir::new("").unwrap();
|
||||
let key = VaultKey::new(&"password".into(), *ITERATIONS);
|
||||
let vault_file_contents = r#"{"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"758696c8dc6378ab9b25bb42790da2f5"},"ciphertext":"54eb50683717d41caaeb12ea969f2c159daada5907383f26f327606a37dc7168","kdf":"pbkdf2","kdfparams":{"c":1024,"dklen":32,"prf":"hmac-sha256","salt":"3c320fa566a1a7963ac8df68a19548d27c8f40bf92ef87c84594dcd5bbc402b6"},"mac":"9e5c2314c2a0781962db85611417c614bd6756666b6b1e93840f5b6ed895f003"}}"#;
|
||||
let dir: PathBuf = temp_path.path().into();
|
||||
let mut vault_file_path: PathBuf = dir.clone();
|
||||
vault_file_path.push(VAULT_FILE_NAME);
|
||||
{
|
||||
let mut vault_file = fs::File::create(vault_file_path).unwrap();
|
||||
vault_file
|
||||
.write_all(vault_file_contents.as_bytes())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// when
|
||||
let result = read_vault_file(&dir, Some(&key));
|
||||
|
||||
// then
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_vault_file_fails() {
|
||||
// given
|
||||
let temp_path = TempDir::new("").unwrap();
|
||||
let key = VaultKey::new(&"password1".into(), *ITERATIONS);
|
||||
let dir: PathBuf = temp_path.path().into();
|
||||
let mut vault_file_path: PathBuf = dir.clone();
|
||||
vault_file_path.push(VAULT_FILE_NAME);
|
||||
|
||||
// when
|
||||
let result = read_vault_file(&dir, Some(&key));
|
||||
|
||||
// then
|
||||
assert!(result.is_err());
|
||||
|
||||
// and when given
|
||||
let vault_file_contents = r#"{"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"0155e3690be19fbfbecabcd440aa284b"},"ciphertext":"4d6938a1f49b7782","kdf":"pbkdf2","kdfparams":{"c":1024,"dklen":32,"prf":"hmac-sha256","salt":"b6a9338a7ccd39288a86dba73bfecd9101b4f3db9c9830e7c76afdbd4f6872e5"},"mac":"16381463ea11c6eb2239a9f339c2e780516d29d234ce30ac5f166f9080b5a262"}}"#;
|
||||
{
|
||||
let mut vault_file = fs::File::create(vault_file_path).unwrap();
|
||||
vault_file
|
||||
.write_all(vault_file_contents.as_bytes())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// when
|
||||
let result = read_vault_file(&dir, Some(&key));
|
||||
|
||||
// then
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vault_directory_can_be_created() {
|
||||
// given
|
||||
let temp_path = TempDir::new("").unwrap();
|
||||
let key = VaultKey::new(&"password".into(), *ITERATIONS);
|
||||
let dir: PathBuf = temp_path.path().into();
|
||||
|
||||
// when
|
||||
let vault = VaultDiskDirectory::create(&dir, "vault", key.clone());
|
||||
|
||||
// then
|
||||
assert!(vault.is_ok());
|
||||
|
||||
// and when
|
||||
let vault = VaultDiskDirectory::at(&dir, "vault", key);
|
||||
|
||||
// then
|
||||
assert!(vault.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vault_directory_cannot_be_created_if_already_exists() {
|
||||
// given
|
||||
let temp_path = TempDir::new("").unwrap();
|
||||
let key = VaultKey::new(&"password".into(), *ITERATIONS);
|
||||
let dir: PathBuf = temp_path.path().into();
|
||||
let mut vault_dir = dir.clone();
|
||||
vault_dir.push("vault");
|
||||
fs::create_dir_all(&vault_dir).unwrap();
|
||||
|
||||
// when
|
||||
let vault = VaultDiskDirectory::create(&dir, "vault", key);
|
||||
|
||||
// then
|
||||
assert!(vault.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vault_directory_cannot_be_opened_if_not_exists() {
|
||||
// given
|
||||
let temp_path = TempDir::new("").unwrap();
|
||||
let key = VaultKey::new(&"password".into(), *ITERATIONS);
|
||||
let dir: PathBuf = temp_path.path().into();
|
||||
|
||||
// when
|
||||
let vault = VaultDiskDirectory::at(&dir, "vault", key);
|
||||
|
||||
// then
|
||||
assert!(vault.is_err());
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crypto::{self, Error as EthCryptoError};
|
||||
use ethkey::{self, DerivationError, Error as EthKeyError};
|
||||
use std::{fmt, io::Error as IoError};
|
||||
|
||||
/// Account-related errors.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// IO error
|
||||
Io(IoError),
|
||||
/// Invalid Password
|
||||
InvalidPassword,
|
||||
/// Account's secret is invalid.
|
||||
InvalidSecret,
|
||||
/// Invalid Vault Crypto meta.
|
||||
InvalidCryptoMeta,
|
||||
/// Invalid Account.
|
||||
InvalidAccount,
|
||||
/// Invalid Message.
|
||||
InvalidMessage,
|
||||
/// Invalid Key File
|
||||
InvalidKeyFile(String),
|
||||
/// Vaults are not supported.
|
||||
VaultsAreNotSupported,
|
||||
/// Unsupported vault
|
||||
UnsupportedVault,
|
||||
/// Invalid vault name
|
||||
InvalidVaultName,
|
||||
/// Vault not found
|
||||
VaultNotFound,
|
||||
/// Account creation failed.
|
||||
CreationFailed,
|
||||
/// `EthKey` error
|
||||
EthKey(EthKeyError),
|
||||
/// `ethkey::crypto::Error`
|
||||
EthKeyCrypto(ethkey::crypto::Error),
|
||||
/// `EthCrypto` error
|
||||
EthCrypto(EthCryptoError),
|
||||
/// Derivation error
|
||||
Derivation(DerivationError),
|
||||
/// Custom error
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
let s = match *self {
|
||||
Error::Io(ref err) => err.to_string(),
|
||||
Error::InvalidPassword => "Invalid password".into(),
|
||||
Error::InvalidSecret => "Invalid secret".into(),
|
||||
Error::InvalidCryptoMeta => "Invalid crypted metadata".into(),
|
||||
Error::InvalidAccount => "Invalid account".into(),
|
||||
Error::InvalidMessage => "Invalid message".into(),
|
||||
Error::InvalidKeyFile(ref reason) => format!("Invalid key file: {}", reason),
|
||||
Error::VaultsAreNotSupported => "Vaults are not supported".into(),
|
||||
Error::UnsupportedVault => "Vault is not supported for this operation".into(),
|
||||
Error::InvalidVaultName => "Invalid vault name".into(),
|
||||
Error::VaultNotFound => "Vault not found".into(),
|
||||
Error::CreationFailed => "Account creation failed".into(),
|
||||
Error::EthKey(ref err) => err.to_string(),
|
||||
Error::EthKeyCrypto(ref err) => err.to_string(),
|
||||
Error::EthCrypto(ref err) => err.to_string(),
|
||||
Error::Derivation(ref err) => format!("Derivation error: {:?}", err),
|
||||
Error::Custom(ref s) => s.clone(),
|
||||
};
|
||||
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IoError> for Error {
|
||||
fn from(err: IoError) -> Self {
|
||||
Error::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EthKeyError> for Error {
|
||||
fn from(err: EthKeyError) -> Self {
|
||||
Error::EthKey(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ethkey::crypto::Error> for Error {
|
||||
fn from(err: ethkey::crypto::Error) -> Self {
|
||||
Error::EthKeyCrypto(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EthCryptoError> for Error {
|
||||
fn from(err: EthCryptoError) -> Self {
|
||||
Error::EthCrypto(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crypto::error::ScryptError> for Error {
|
||||
fn from(err: crypto::error::ScryptError) -> Self {
|
||||
Error::EthCrypto(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crypto::error::SymmError> for Error {
|
||||
fn from(err: crypto::error::SymmError) -> Self {
|
||||
Error::EthCrypto(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DerivationError> for Error {
|
||||
fn from(err: DerivationError) -> Self {
|
||||
Error::Derivation(err)
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! ethkey reexport to make documentation look pretty.
|
||||
pub use _ethkey::*;
|
||||
use json;
|
||||
|
||||
impl Into<json::H160> for Address {
|
||||
fn into(self) -> json::H160 {
|
||||
let a: [u8; 20] = self.into();
|
||||
From::from(a)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<json::H160> for Address {
|
||||
fn from(json: json::H160) -> Self {
|
||||
let a: [u8; 20] = json.into();
|
||||
From::from(a)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a json::H160> for Address {
|
||||
fn from(json: &'a json::H160) -> Self {
|
||||
let mut a = [0u8; 20];
|
||||
a.copy_from_slice(json);
|
||||
From::from(a)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,67 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{collections::HashSet, fs, path::Path};
|
||||
|
||||
use accounts_dir::{DiskKeyFileManager, KeyDirectory, KeyFileManager};
|
||||
use ethkey::Address;
|
||||
use Error;
|
||||
|
||||
/// Import an account from a file.
|
||||
pub fn import_account(path: &Path, dst: &dyn KeyDirectory) -> Result<Address, Error> {
|
||||
let key_manager = DiskKeyFileManager::default();
|
||||
let existing_accounts = dst
|
||||
.load()?
|
||||
.into_iter()
|
||||
.map(|a| a.address)
|
||||
.collect::<HashSet<_>>();
|
||||
let filename = path
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.map(|f| f.to_owned());
|
||||
let account = fs::File::open(&path)
|
||||
.map_err(Into::into)
|
||||
.and_then(|file| key_manager.read(filename, file))?;
|
||||
|
||||
let address = account.address.clone();
|
||||
if !existing_accounts.contains(&address) {
|
||||
dst.insert(account)?;
|
||||
}
|
||||
Ok(address)
|
||||
}
|
||||
|
||||
/// Import all accounts from one directory to the other.
|
||||
pub fn import_accounts(
|
||||
src: &dyn KeyDirectory,
|
||||
dst: &dyn KeyDirectory,
|
||||
) -> Result<Vec<Address>, Error> {
|
||||
let accounts = src.load()?;
|
||||
let existing_accounts = dst
|
||||
.load()?
|
||||
.into_iter()
|
||||
.map(|a| a.address)
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
accounts
|
||||
.into_iter()
|
||||
.filter(|a| !existing_accounts.contains(&a.address))
|
||||
.map(|a| {
|
||||
let address = a.address.clone();
|
||||
dst.insert(a)?;
|
||||
Ok(address)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use rustc_hex::{FromHex, FromHexError, ToHex};
|
||||
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::{ops, str};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Bytes(Vec<u8>);
|
||||
|
||||
impl ops::Deref for Bytes {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for Bytes {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
let data = s
|
||||
.from_hex()
|
||||
.map_err(|e| Error::custom(format!("Invalid hex value {}", e)))?;
|
||||
Ok(Bytes(data))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Bytes {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(&self.0.to_hex())
|
||||
}
|
||||
}
|
||||
|
||||
impl str::FromStr for Bytes {
|
||||
type Err = FromHexError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
s.from_hex().map(Bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static str> for Bytes {
|
||||
fn from(s: &'static str) -> Self {
|
||||
s.parse().expect(&format!(
|
||||
"invalid string literal for {}: '{}'",
|
||||
stringify!(Self),
|
||||
s
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for Bytes {
|
||||
fn from(v: Vec<u8>) -> Self {
|
||||
Bytes(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Bytes> for Vec<u8> {
|
||||
fn from(b: Bytes) -> Self {
|
||||
b.0
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::{Error, H128};
|
||||
use serde::{
|
||||
de::{Error as SerdeError, Visitor},
|
||||
Deserialize, Deserializer, Serialize, Serializer,
|
||||
};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum CipherSer {
|
||||
Aes128Ctr,
|
||||
}
|
||||
|
||||
impl Serialize for CipherSer {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match *self {
|
||||
CipherSer::Aes128Ctr => serializer.serialize_str("aes-128-ctr"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for CipherSer {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
deserializer.deserialize_any(CipherSerVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
struct CipherSerVisitor;
|
||||
|
||||
impl<'a> Visitor<'a> for CipherSerVisitor {
|
||||
type Value = CipherSer;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "a valid cipher identifier")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: SerdeError,
|
||||
{
|
||||
match value {
|
||||
"aes-128-ctr" => Ok(CipherSer::Aes128Ctr),
|
||||
_ => Err(SerdeError::custom(Error::UnsupportedCipher)),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
|
||||
where
|
||||
E: SerdeError,
|
||||
{
|
||||
self.visit_str(value.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Aes128Ctr {
|
||||
pub iv: H128,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum CipherSerParams {
|
||||
Aes128Ctr(Aes128Ctr),
|
||||
}
|
||||
|
||||
impl Serialize for CipherSerParams {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match *self {
|
||||
CipherSerParams::Aes128Ctr(ref params) => params.serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for CipherSerParams {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
Aes128Ctr::deserialize(deserializer)
|
||||
.map(CipherSerParams::Aes128Ctr)
|
||||
.map_err(|_| Error::InvalidCipherParams)
|
||||
.map_err(SerdeError::custom)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Cipher {
|
||||
Aes128Ctr(Aes128Ctr),
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::{Bytes, Cipher, CipherSer, CipherSerParams, Kdf, KdfSer, KdfSerParams, H256};
|
||||
use serde::{
|
||||
de::{Error, MapAccess, Visitor},
|
||||
ser::SerializeStruct,
|
||||
Deserialize, Deserializer, Serialize, Serializer,
|
||||
};
|
||||
use serde_json;
|
||||
use std::{fmt, str};
|
||||
|
||||
pub type CipherText = Bytes;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Crypto {
|
||||
pub cipher: Cipher,
|
||||
pub ciphertext: CipherText,
|
||||
pub kdf: Kdf,
|
||||
pub mac: H256,
|
||||
}
|
||||
|
||||
impl str::FromStr for Crypto {
|
||||
type Err = serde_json::error::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
serde_json::from_str(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Crypto> for String {
|
||||
fn from(c: Crypto) -> Self {
|
||||
serde_json::to_string(&c)
|
||||
.expect("serialization cannot fail, cause all crypto keys are strings")
|
||||
}
|
||||
}
|
||||
|
||||
enum CryptoField {
|
||||
Cipher,
|
||||
CipherParams,
|
||||
CipherText,
|
||||
Kdf,
|
||||
KdfParams,
|
||||
Mac,
|
||||
Version,
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for CryptoField {
|
||||
fn deserialize<D>(deserializer: D) -> Result<CryptoField, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
deserializer.deserialize_any(CryptoFieldVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
struct CryptoFieldVisitor;
|
||||
|
||||
impl<'a> Visitor<'a> for CryptoFieldVisitor {
|
||||
type Value = CryptoField;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "a valid crypto struct description")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
match value {
|
||||
"cipher" => Ok(CryptoField::Cipher),
|
||||
"cipherparams" => Ok(CryptoField::CipherParams),
|
||||
"ciphertext" => Ok(CryptoField::CipherText),
|
||||
"kdf" => Ok(CryptoField::Kdf),
|
||||
"kdfparams" => Ok(CryptoField::KdfParams),
|
||||
"mac" => Ok(CryptoField::Mac),
|
||||
"version" => Ok(CryptoField::Version),
|
||||
_ => Err(Error::custom(format!("Unknown field: '{}'", value))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for Crypto {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Crypto, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
static FIELDS: &'static [&'static str] = &["id", "version", "crypto", "Crypto", "address"];
|
||||
deserializer.deserialize_struct("Crypto", FIELDS, CryptoVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
struct CryptoVisitor;
|
||||
|
||||
impl<'a> Visitor<'a> for CryptoVisitor {
|
||||
type Value = Crypto;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "a valid vault crypto object")
|
||||
}
|
||||
|
||||
fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
|
||||
where
|
||||
V: MapAccess<'a>,
|
||||
{
|
||||
let mut cipher = None;
|
||||
let mut cipherparams = None;
|
||||
let mut ciphertext = None;
|
||||
let mut kdf = None;
|
||||
let mut kdfparams = None;
|
||||
let mut mac = None;
|
||||
|
||||
loop {
|
||||
match visitor.next_key()? {
|
||||
Some(CryptoField::Cipher) => {
|
||||
cipher = Some(visitor.next_value()?);
|
||||
}
|
||||
Some(CryptoField::CipherParams) => {
|
||||
cipherparams = Some(visitor.next_value()?);
|
||||
}
|
||||
Some(CryptoField::CipherText) => {
|
||||
ciphertext = Some(visitor.next_value()?);
|
||||
}
|
||||
Some(CryptoField::Kdf) => {
|
||||
kdf = Some(visitor.next_value()?);
|
||||
}
|
||||
Some(CryptoField::KdfParams) => {
|
||||
kdfparams = Some(visitor.next_value()?);
|
||||
}
|
||||
Some(CryptoField::Mac) => {
|
||||
mac = Some(visitor.next_value()?);
|
||||
}
|
||||
// skip not required version field (it appears in pyethereum generated keystores)
|
||||
Some(CryptoField::Version) => visitor.next_value().unwrap_or(()),
|
||||
None => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let cipher = match (cipher, cipherparams) {
|
||||
(Some(CipherSer::Aes128Ctr), Some(CipherSerParams::Aes128Ctr(params))) => {
|
||||
Cipher::Aes128Ctr(params)
|
||||
}
|
||||
(None, _) => return Err(V::Error::missing_field("cipher")),
|
||||
(Some(_), None) => return Err(V::Error::missing_field("cipherparams")),
|
||||
};
|
||||
|
||||
let ciphertext = match ciphertext {
|
||||
Some(ciphertext) => ciphertext,
|
||||
None => return Err(V::Error::missing_field("ciphertext")),
|
||||
};
|
||||
|
||||
let kdf = match (kdf, kdfparams) {
|
||||
(Some(KdfSer::Pbkdf2), Some(KdfSerParams::Pbkdf2(params))) => Kdf::Pbkdf2(params),
|
||||
(Some(KdfSer::Scrypt), Some(KdfSerParams::Scrypt(params))) => Kdf::Scrypt(params),
|
||||
(Some(_), Some(_)) => return Err(V::Error::custom("Invalid cipherparams")),
|
||||
(None, _) => return Err(V::Error::missing_field("kdf")),
|
||||
(Some(_), None) => return Err(V::Error::missing_field("kdfparams")),
|
||||
};
|
||||
|
||||
let mac = match mac {
|
||||
Some(mac) => mac,
|
||||
None => return Err(V::Error::missing_field("mac")),
|
||||
};
|
||||
|
||||
let result = Crypto {
|
||||
cipher: cipher,
|
||||
ciphertext: ciphertext,
|
||||
kdf: kdf,
|
||||
mac: mac,
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Crypto {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut crypto = serializer.serialize_struct("Crypto", 6)?;
|
||||
match self.cipher {
|
||||
Cipher::Aes128Ctr(ref params) => {
|
||||
crypto.serialize_field("cipher", &CipherSer::Aes128Ctr)?;
|
||||
crypto.serialize_field("cipherparams", params)?;
|
||||
}
|
||||
}
|
||||
crypto.serialize_field("ciphertext", &self.ciphertext)?;
|
||||
match self.kdf {
|
||||
Kdf::Pbkdf2(ref params) => {
|
||||
crypto.serialize_field("kdf", &KdfSer::Pbkdf2)?;
|
||||
crypto.serialize_field("kdfparams", params)?;
|
||||
}
|
||||
Kdf::Scrypt(ref params) => {
|
||||
crypto.serialize_field("kdf", &KdfSer::Scrypt)?;
|
||||
crypto.serialize_field("kdfparams", params)?;
|
||||
}
|
||||
}
|
||||
|
||||
crypto.serialize_field("mac", &self.mac)?;
|
||||
crypto.end()
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
UnsupportedCipher,
|
||||
InvalidCipherParams,
|
||||
UnsupportedKdf,
|
||||
InvalidUuid,
|
||||
UnsupportedVersion,
|
||||
InvalidCiphertext,
|
||||
InvalidH256,
|
||||
InvalidPrf,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
Error::InvalidUuid => write!(f, "Invalid Uuid"),
|
||||
Error::UnsupportedVersion => write!(f, "Unsupported version"),
|
||||
Error::UnsupportedKdf => write!(f, "Unsupported kdf"),
|
||||
Error::InvalidCiphertext => write!(f, "Invalid ciphertext"),
|
||||
Error::UnsupportedCipher => write!(f, "Unsupported cipher"),
|
||||
Error::InvalidCipherParams => write!(f, "Invalid cipher params"),
|
||||
Error::InvalidH256 => write!(f, "Invalid hash"),
|
||||
Error::InvalidPrf => write!(f, "Invalid prf"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<String> for Error {
|
||||
fn into(self) -> String {
|
||||
format!("{}", self)
|
||||
}
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::Error;
|
||||
use rustc_hex::{FromHex, ToHex};
|
||||
use serde::{
|
||||
de::{Error as SerdeError, Visitor},
|
||||
Deserialize, Deserializer, Serialize, Serializer,
|
||||
};
|
||||
use std::{fmt, ops, str};
|
||||
|
||||
macro_rules! impl_hash {
|
||||
($name: ident, $size: expr) => {
|
||||
pub struct $name([u8; $size]);
|
||||
|
||||
impl fmt::Debug for $name {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
let self_ref: &[u8] = &self.0;
|
||||
write!(f, "{:?}", self_ref)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for $name {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
let self_ref: &[u8] = &self.0;
|
||||
let other_ref: &[u8] = &other.0;
|
||||
self_ref == other_ref
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for $name {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for $name {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(&self.0.to_hex())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for $name {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
struct HashVisitor;
|
||||
|
||||
impl<'b> Visitor<'b> for HashVisitor {
|
||||
type Value = $name;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "a hex-encoded {}", stringify!($name))
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: SerdeError,
|
||||
{
|
||||
value.parse().map_err(SerdeError::custom)
|
||||
}
|
||||
|
||||
fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
|
||||
where
|
||||
E: SerdeError,
|
||||
{
|
||||
self.visit_str(value.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(HashVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl str::FromStr for $name {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
||||
match value.from_hex() {
|
||||
Ok(ref hex) if hex.len() == $size => {
|
||||
let mut hash = [0u8; $size];
|
||||
hash.clone_from_slice(hex);
|
||||
Ok($name(hash))
|
||||
}
|
||||
_ => Err(Error::InvalidH256),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static str> for $name {
|
||||
fn from(s: &'static str) -> Self {
|
||||
s.parse().expect(&format!(
|
||||
"invalid string literal for {}: '{}'",
|
||||
stringify!($name),
|
||||
s
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; $size]> for $name {
|
||||
fn from(bytes: [u8; $size]) -> Self {
|
||||
$name(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<[u8; $size]> for $name {
|
||||
fn into(self) -> [u8; $size] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_hash!(H128, 16);
|
||||
impl_hash!(H160, 20);
|
||||
impl_hash!(H256, 32);
|
||||
@@ -1,179 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Universaly unique identifier.
|
||||
use super::Error;
|
||||
use rustc_hex::{FromHex, ToHex};
|
||||
use serde::{
|
||||
de::{Error as SerdeError, Visitor},
|
||||
Deserialize, Deserializer, Serialize, Serializer,
|
||||
};
|
||||
use std::{fmt, str};
|
||||
|
||||
/// Universaly unique identifier.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Uuid([u8; 16]);
|
||||
|
||||
impl From<[u8; 16]> for Uuid {
|
||||
fn from(uuid: [u8; 16]) -> Self {
|
||||
Uuid(uuid)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<String> for &'a Uuid {
|
||||
fn into(self) -> String {
|
||||
let d1 = &self.0[0..4];
|
||||
let d2 = &self.0[4..6];
|
||||
let d3 = &self.0[6..8];
|
||||
let d4 = &self.0[8..10];
|
||||
let d5 = &self.0[10..16];
|
||||
[d1, d2, d3, d4, d5]
|
||||
.iter()
|
||||
.map(|d| d.to_hex())
|
||||
.collect::<Vec<String>>()
|
||||
.join("-")
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<String> for Uuid {
|
||||
fn into(self) -> String {
|
||||
Into::into(&self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<[u8; 16]> for Uuid {
|
||||
fn into(self) -> [u8; 16] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Uuid {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
let s: String = (self as &Uuid).into();
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_into(from: &str, into: &mut [u8]) -> Result<(), Error> {
|
||||
let from = from.from_hex().map_err(|_| Error::InvalidUuid)?;
|
||||
|
||||
if from.len() != into.len() {
|
||||
return Err(Error::InvalidUuid);
|
||||
}
|
||||
|
||||
into.copy_from_slice(&from);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl str::FromStr for Uuid {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let parts: Vec<&str> = s.split("-").collect();
|
||||
|
||||
if parts.len() != 5 {
|
||||
return Err(Error::InvalidUuid);
|
||||
}
|
||||
|
||||
let mut uuid = [0u8; 16];
|
||||
|
||||
copy_into(parts[0], &mut uuid[0..4])?;
|
||||
copy_into(parts[1], &mut uuid[4..6])?;
|
||||
copy_into(parts[2], &mut uuid[6..8])?;
|
||||
copy_into(parts[3], &mut uuid[8..10])?;
|
||||
copy_into(parts[4], &mut uuid[10..16])?;
|
||||
|
||||
Ok(Uuid(uuid))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static str> for Uuid {
|
||||
fn from(s: &'static str) -> Self {
|
||||
s.parse().expect(&format!(
|
||||
"invalid string literal for {}: '{}'",
|
||||
stringify!(Self),
|
||||
s
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Uuid {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let s: String = self.into();
|
||||
serializer.serialize_str(&s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for Uuid {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
deserializer.deserialize_any(UuidVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
struct UuidVisitor;
|
||||
|
||||
impl<'a> Visitor<'a> for UuidVisitor {
|
||||
type Value = Uuid;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "a valid hex-encoded UUID")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: SerdeError,
|
||||
{
|
||||
value.parse().map_err(SerdeError::custom)
|
||||
}
|
||||
|
||||
fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
|
||||
where
|
||||
E: SerdeError,
|
||||
{
|
||||
self.visit_str(value.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Uuid;
|
||||
|
||||
#[test]
|
||||
fn uuid_from_str() {
|
||||
let uuid: Uuid = "3198bc9c-6672-5ab3-d995-4942343ae5b6".into();
|
||||
assert_eq!(
|
||||
uuid,
|
||||
Uuid::from([
|
||||
0x31, 0x98, 0xbc, 0x9c, 0x66, 0x72, 0x5a, 0xb3, 0xd9, 0x95, 0x49, 0x42, 0x34, 0x3a,
|
||||
0xe5, 0xb6
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uuid_from_and_to_str() {
|
||||
let from = "3198bc9c-6672-5ab3-d995-4942343ae5b6";
|
||||
let uuid: Uuid = from.into();
|
||||
let to: String = uuid.into();
|
||||
assert_eq!(from, &to);
|
||||
}
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::{Bytes, Error};
|
||||
use serde::{
|
||||
de::{Error as SerdeError, Visitor},
|
||||
Deserialize, Deserializer, Serialize, Serializer,
|
||||
};
|
||||
use std::{fmt, num::NonZeroU32};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum KdfSer {
|
||||
Pbkdf2,
|
||||
Scrypt,
|
||||
}
|
||||
|
||||
impl Serialize for KdfSer {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match *self {
|
||||
KdfSer::Pbkdf2 => serializer.serialize_str("pbkdf2"),
|
||||
KdfSer::Scrypt => serializer.serialize_str("scrypt"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for KdfSer {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
deserializer.deserialize_any(KdfSerVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
struct KdfSerVisitor;
|
||||
|
||||
impl<'a> Visitor<'a> for KdfSerVisitor {
|
||||
type Value = KdfSer;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "a kdf algorithm identifier")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: SerdeError,
|
||||
{
|
||||
match value {
|
||||
"pbkdf2" => Ok(KdfSer::Pbkdf2),
|
||||
"scrypt" => Ok(KdfSer::Scrypt),
|
||||
_ => Err(SerdeError::custom(Error::UnsupportedKdf)),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
|
||||
where
|
||||
E: SerdeError,
|
||||
{
|
||||
self.visit_str(value.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Prf {
|
||||
HmacSha256,
|
||||
}
|
||||
|
||||
impl Serialize for Prf {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match *self {
|
||||
Prf::HmacSha256 => serializer.serialize_str("hmac-sha256"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for Prf {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
deserializer.deserialize_any(PrfVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
struct PrfVisitor;
|
||||
|
||||
impl<'a> Visitor<'a> for PrfVisitor {
|
||||
type Value = Prf;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "a prf algorithm identifier")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: SerdeError,
|
||||
{
|
||||
match value {
|
||||
"hmac-sha256" => Ok(Prf::HmacSha256),
|
||||
_ => Err(SerdeError::custom(Error::InvalidPrf)),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
|
||||
where
|
||||
E: SerdeError,
|
||||
{
|
||||
self.visit_str(value.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Pbkdf2 {
|
||||
pub c: NonZeroU32,
|
||||
pub dklen: u32,
|
||||
pub prf: Prf,
|
||||
pub salt: Bytes,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Scrypt {
|
||||
pub dklen: u32,
|
||||
pub p: u32,
|
||||
pub n: u32,
|
||||
pub r: u32,
|
||||
pub salt: Bytes,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum KdfSerParams {
|
||||
Pbkdf2(Pbkdf2),
|
||||
Scrypt(Scrypt),
|
||||
}
|
||||
|
||||
impl Serialize for KdfSerParams {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match *self {
|
||||
KdfSerParams::Pbkdf2(ref params) => params.serialize(serializer),
|
||||
KdfSerParams::Scrypt(ref params) => params.serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for KdfSerParams {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
use serde_json::{from_value, Value};
|
||||
|
||||
let v: Value = Deserialize::deserialize(deserializer)?;
|
||||
|
||||
from_value(v.clone())
|
||||
.map(KdfSerParams::Pbkdf2)
|
||||
.or_else(|_| from_value(v).map(KdfSerParams::Scrypt))
|
||||
.map_err(|_| D::Error::custom("Invalid KDF algorithm"))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Kdf {
|
||||
Pbkdf2(Pbkdf2),
|
||||
Scrypt(Scrypt),
|
||||
}
|
||||
@@ -1,359 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::{Crypto, Uuid, Version, H160};
|
||||
use serde::{
|
||||
de::{DeserializeOwned, Error, MapAccess, Visitor},
|
||||
Deserialize, Deserializer, Serialize, Serializer,
|
||||
};
|
||||
use serde_json;
|
||||
use std::{
|
||||
fmt,
|
||||
io::{Read, Write},
|
||||
};
|
||||
|
||||
/// Public opaque type representing serializable `KeyFile`.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct OpaqueKeyFile {
|
||||
key_file: KeyFile,
|
||||
}
|
||||
|
||||
impl Serialize for OpaqueKeyFile {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.key_file.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for OpaqueKeyFile
|
||||
where
|
||||
T: Into<KeyFile>,
|
||||
{
|
||||
fn from(val: T) -> Self {
|
||||
OpaqueKeyFile {
|
||||
key_file: val.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize)]
|
||||
pub struct KeyFile {
|
||||
pub id: Uuid,
|
||||
pub version: Version,
|
||||
pub crypto: Crypto,
|
||||
pub address: Option<H160>,
|
||||
pub name: Option<String>,
|
||||
pub meta: Option<String>,
|
||||
}
|
||||
|
||||
enum KeyFileField {
|
||||
Id,
|
||||
Version,
|
||||
Crypto,
|
||||
Address,
|
||||
Name,
|
||||
Meta,
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for KeyFileField {
|
||||
fn deserialize<D>(deserializer: D) -> Result<KeyFileField, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
deserializer.deserialize_any(KeyFileFieldVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
struct KeyFileFieldVisitor;
|
||||
|
||||
impl<'a> Visitor<'a> for KeyFileFieldVisitor {
|
||||
type Value = KeyFileField;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "a valid key file field")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
match value {
|
||||
"id" => Ok(KeyFileField::Id),
|
||||
"version" => Ok(KeyFileField::Version),
|
||||
"crypto" => Ok(KeyFileField::Crypto),
|
||||
"Crypto" => Ok(KeyFileField::Crypto),
|
||||
"address" => Ok(KeyFileField::Address),
|
||||
"name" => Ok(KeyFileField::Name),
|
||||
"meta" => Ok(KeyFileField::Meta),
|
||||
_ => Err(Error::custom(format!("Unknown field: '{}'", value))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for KeyFile {
|
||||
fn deserialize<D>(deserializer: D) -> Result<KeyFile, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
static FIELDS: &'static [&'static str] = &["id", "version", "crypto", "Crypto", "address"];
|
||||
deserializer.deserialize_struct("KeyFile", FIELDS, KeyFileVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
fn none_if_empty<'a, T>(v: Option<serde_json::Value>) -> Option<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
v.and_then(|v| {
|
||||
if v.is_null() {
|
||||
None
|
||||
} else {
|
||||
serde_json::from_value(v).ok()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
struct KeyFileVisitor;
|
||||
impl<'a> Visitor<'a> for KeyFileVisitor {
|
||||
type Value = KeyFile;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "a valid key object")
|
||||
}
|
||||
|
||||
fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
|
||||
where
|
||||
V: MapAccess<'a>,
|
||||
{
|
||||
let mut id = None;
|
||||
let mut version = None;
|
||||
let mut crypto = None;
|
||||
let mut address = None;
|
||||
let mut name = None;
|
||||
let mut meta = None;
|
||||
|
||||
loop {
|
||||
match visitor.next_key()? {
|
||||
Some(KeyFileField::Id) => {
|
||||
id = Some(visitor.next_value()?);
|
||||
}
|
||||
Some(KeyFileField::Version) => {
|
||||
version = Some(visitor.next_value()?);
|
||||
}
|
||||
Some(KeyFileField::Crypto) => {
|
||||
crypto = Some(visitor.next_value()?);
|
||||
}
|
||||
Some(KeyFileField::Address) => {
|
||||
address = Some(visitor.next_value()?);
|
||||
}
|
||||
Some(KeyFileField::Name) => name = none_if_empty(visitor.next_value().ok()),
|
||||
Some(KeyFileField::Meta) => meta = none_if_empty(visitor.next_value().ok()),
|
||||
None => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let id = match id {
|
||||
Some(id) => id,
|
||||
None => return Err(V::Error::missing_field("id")),
|
||||
};
|
||||
|
||||
let version = match version {
|
||||
Some(version) => version,
|
||||
None => return Err(V::Error::missing_field("version")),
|
||||
};
|
||||
|
||||
let crypto = match crypto {
|
||||
Some(crypto) => crypto,
|
||||
None => return Err(V::Error::missing_field("crypto")),
|
||||
};
|
||||
|
||||
let result = KeyFile {
|
||||
id: id,
|
||||
version: version,
|
||||
crypto: crypto,
|
||||
address: address,
|
||||
name: name,
|
||||
meta: meta,
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyFile {
|
||||
pub fn load<R>(reader: R) -> Result<Self, serde_json::Error>
|
||||
where
|
||||
R: Read,
|
||||
{
|
||||
serde_json::from_reader(reader)
|
||||
}
|
||||
|
||||
pub fn write<W>(&self, writer: &mut W) -> Result<(), serde_json::Error>
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
serde_json::to_writer(writer, self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use json::{Aes128Ctr, Cipher, Crypto, Kdf, KeyFile, Scrypt, Uuid, Version};
|
||||
use serde_json;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn basic_keyfile() {
|
||||
let json = r#"
|
||||
{
|
||||
"address": "6edddfc6349aff20bc6467ccf276c5b52487f7a8",
|
||||
"crypto": {
|
||||
"cipher": "aes-128-ctr",
|
||||
"ciphertext": "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc",
|
||||
"cipherparams": {
|
||||
"iv": "b5a7ec855ec9e2c405371356855fec83"
|
||||
},
|
||||
"kdf": "scrypt",
|
||||
"kdfparams": {
|
||||
"dklen": 32,
|
||||
"n": 262144,
|
||||
"p": 1,
|
||||
"r": 8,
|
||||
"salt": "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209"
|
||||
},
|
||||
"mac": "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f"
|
||||
},
|
||||
"id": "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73",
|
||||
"version": 3,
|
||||
"name": "Test",
|
||||
"meta": "{}"
|
||||
}"#;
|
||||
|
||||
let expected = KeyFile {
|
||||
id: Uuid::from_str("8777d9f6-7860-4b9b-88b7-0b57ee6b3a73").unwrap(),
|
||||
version: Version::V3,
|
||||
address: Some("6edddfc6349aff20bc6467ccf276c5b52487f7a8".into()),
|
||||
crypto: Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||
iv: "b5a7ec855ec9e2c405371356855fec83".into(),
|
||||
}),
|
||||
ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc"
|
||||
.into(),
|
||||
kdf: Kdf::Scrypt(Scrypt {
|
||||
n: 262144,
|
||||
dklen: 32,
|
||||
p: 1,
|
||||
r: 8,
|
||||
salt: "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209".into(),
|
||||
}),
|
||||
mac: "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f".into(),
|
||||
},
|
||||
name: Some("Test".to_owned()),
|
||||
meta: Some("{}".to_owned()),
|
||||
};
|
||||
|
||||
let keyfile: KeyFile = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(keyfile, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn capital_crypto_keyfile() {
|
||||
let json = r#"
|
||||
{
|
||||
"address": "6edddfc6349aff20bc6467ccf276c5b52487f7a8",
|
||||
"Crypto": {
|
||||
"cipher": "aes-128-ctr",
|
||||
"ciphertext": "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc",
|
||||
"cipherparams": {
|
||||
"iv": "b5a7ec855ec9e2c405371356855fec83"
|
||||
},
|
||||
"kdf": "scrypt",
|
||||
"kdfparams": {
|
||||
"dklen": 32,
|
||||
"n": 262144,
|
||||
"p": 1,
|
||||
"r": 8,
|
||||
"salt": "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209"
|
||||
},
|
||||
"mac": "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f"
|
||||
},
|
||||
"id": "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73",
|
||||
"version": 3
|
||||
}"#;
|
||||
|
||||
let expected = KeyFile {
|
||||
id: "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73".into(),
|
||||
version: Version::V3,
|
||||
address: Some("6edddfc6349aff20bc6467ccf276c5b52487f7a8".into()),
|
||||
crypto: Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||
iv: "b5a7ec855ec9e2c405371356855fec83".into(),
|
||||
}),
|
||||
ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc"
|
||||
.into(),
|
||||
kdf: Kdf::Scrypt(Scrypt {
|
||||
n: 262144,
|
||||
dklen: 32,
|
||||
p: 1,
|
||||
r: 8,
|
||||
salt: "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209".into(),
|
||||
}),
|
||||
mac: "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f".into(),
|
||||
},
|
||||
name: None,
|
||||
meta: None,
|
||||
};
|
||||
|
||||
let keyfile: KeyFile = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(keyfile, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_and_from_json() {
|
||||
let file = KeyFile {
|
||||
id: "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73".into(),
|
||||
version: Version::V3,
|
||||
address: Some("6edddfc6349aff20bc6467ccf276c5b52487f7a8".into()),
|
||||
crypto: Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||
iv: "b5a7ec855ec9e2c405371356855fec83".into(),
|
||||
}),
|
||||
ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc"
|
||||
.into(),
|
||||
kdf: Kdf::Scrypt(Scrypt {
|
||||
n: 262144,
|
||||
dklen: 32,
|
||||
p: 1,
|
||||
r: 8,
|
||||
salt: "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209".into(),
|
||||
}),
|
||||
mac: "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f".into(),
|
||||
},
|
||||
name: Some("Test".to_owned()),
|
||||
meta: None,
|
||||
};
|
||||
|
||||
let serialized = serde_json::to_string(&file).unwrap();
|
||||
println!("{}", serialized);
|
||||
let deserialized = serde_json::from_str(&serialized).unwrap();
|
||||
|
||||
assert_eq!(file, deserialized);
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Contract interface specification.
|
||||
|
||||
mod bytes;
|
||||
mod cipher;
|
||||
mod crypto;
|
||||
mod error;
|
||||
mod hash;
|
||||
mod id;
|
||||
mod kdf;
|
||||
mod key_file;
|
||||
mod presale;
|
||||
mod vault_file;
|
||||
mod vault_key_file;
|
||||
mod version;
|
||||
|
||||
pub use self::{
|
||||
bytes::Bytes,
|
||||
cipher::{Aes128Ctr, Cipher, CipherSer, CipherSerParams},
|
||||
crypto::{CipherText, Crypto},
|
||||
error::Error,
|
||||
hash::{H128, H160, H256},
|
||||
id::Uuid,
|
||||
kdf::{Kdf, KdfSer, KdfSerParams, Pbkdf2, Prf, Scrypt},
|
||||
key_file::{KeyFile, OpaqueKeyFile},
|
||||
presale::{Encseed, PresaleWallet},
|
||||
vault_file::VaultFile,
|
||||
vault_key_file::{
|
||||
insert_vault_name_to_json_meta, remove_vault_name_from_json_meta, VaultKeyFile,
|
||||
VaultKeyMeta,
|
||||
},
|
||||
version::Version,
|
||||
};
|
||||
@@ -1,105 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::Crypto;
|
||||
use serde_json;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
/// Vault meta file
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct VaultFile {
|
||||
/// Vault password, encrypted with vault password
|
||||
pub crypto: Crypto,
|
||||
/// Vault metadata string
|
||||
pub meta: Option<String>,
|
||||
}
|
||||
|
||||
impl VaultFile {
|
||||
pub fn load<R>(reader: R) -> Result<Self, serde_json::Error>
|
||||
where
|
||||
R: Read,
|
||||
{
|
||||
serde_json::from_reader(reader)
|
||||
}
|
||||
|
||||
pub fn write<W>(&self, writer: &mut W) -> Result<(), serde_json::Error>
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
serde_json::to_writer(writer, self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use json::{Aes128Ctr, Cipher, Crypto, Kdf, Pbkdf2, Prf, VaultFile};
|
||||
use serde_json;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
lazy_static! {
|
||||
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(1024).expect("1024 > 0; qed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_and_from_json() {
|
||||
let file = VaultFile {
|
||||
crypto: Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||
iv: "0155e3690be19fbfbecabcd440aa284b".into(),
|
||||
}),
|
||||
ciphertext: "4d6938a1f49b7782".into(),
|
||||
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
||||
c: *ITERATIONS,
|
||||
dklen: 32,
|
||||
prf: Prf::HmacSha256,
|
||||
salt: "b6a9338a7ccd39288a86dba73bfecd9101b4f3db9c9830e7c76afdbd4f6872e5".into(),
|
||||
}),
|
||||
mac: "16381463ea11c6eb2239a9f339c2e780516d29d234ce30ac5f166f9080b5a262".into(),
|
||||
},
|
||||
meta: Some("{}".into()),
|
||||
};
|
||||
|
||||
let serialized = serde_json::to_string(&file).unwrap();
|
||||
let deserialized = serde_json::from_str(&serialized).unwrap();
|
||||
|
||||
assert_eq!(file, deserialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_and_from_json_no_meta() {
|
||||
let file = VaultFile {
|
||||
crypto: Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||
iv: "0155e3690be19fbfbecabcd440aa284b".into(),
|
||||
}),
|
||||
ciphertext: "4d6938a1f49b7782".into(),
|
||||
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
||||
c: *ITERATIONS,
|
||||
dklen: 32,
|
||||
prf: Prf::HmacSha256,
|
||||
salt: "b6a9338a7ccd39288a86dba73bfecd9101b4f3db9c9830e7c76afdbd4f6872e5".into(),
|
||||
}),
|
||||
mac: "16381463ea11c6eb2239a9f339c2e780516d29d234ce30ac5f166f9080b5a262".into(),
|
||||
},
|
||||
meta: None,
|
||||
};
|
||||
|
||||
let serialized = serde_json::to_string(&file).unwrap();
|
||||
let deserialized = serde_json::from_str(&serialized).unwrap();
|
||||
|
||||
assert_eq!(file, deserialized);
|
||||
}
|
||||
}
|
||||
@@ -1,205 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::{Crypto, Uuid, Version, H160};
|
||||
use serde::de::Error;
|
||||
use serde_json::{self, error, value::Value};
|
||||
use std::io::{Read, Write};
|
||||
|
||||
/// Meta key name for vault field
|
||||
const VAULT_NAME_META_KEY: &'static str = "vault";
|
||||
|
||||
/// Key file as stored in vaults
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct VaultKeyFile {
|
||||
/// Key id
|
||||
pub id: Uuid,
|
||||
/// Key version
|
||||
pub version: Version,
|
||||
/// Secret, encrypted with account password
|
||||
pub crypto: Crypto,
|
||||
/// Serialized `VaultKeyMeta`, encrypted with vault password
|
||||
pub metacrypto: Crypto,
|
||||
}
|
||||
|
||||
/// Data, stored in `VaultKeyFile::metacrypto`
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct VaultKeyMeta {
|
||||
/// Key address
|
||||
pub address: H160,
|
||||
/// Key name
|
||||
pub name: Option<String>,
|
||||
/// Key metadata
|
||||
pub meta: Option<String>,
|
||||
}
|
||||
|
||||
/// Insert vault name to the JSON meta field
|
||||
pub fn insert_vault_name_to_json_meta(
|
||||
meta: &str,
|
||||
vault_name: &str,
|
||||
) -> Result<String, error::Error> {
|
||||
let mut meta = if meta.is_empty() {
|
||||
Value::Object(serde_json::Map::new())
|
||||
} else {
|
||||
serde_json::from_str(meta)?
|
||||
};
|
||||
|
||||
if let Some(meta_obj) = meta.as_object_mut() {
|
||||
meta_obj.insert(
|
||||
VAULT_NAME_META_KEY.to_owned(),
|
||||
Value::String(vault_name.to_owned()),
|
||||
);
|
||||
serde_json::to_string(meta_obj)
|
||||
} else {
|
||||
Err(error::Error::custom(
|
||||
"Meta is expected to be a serialized JSON object",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove vault name from the JSON meta field
|
||||
pub fn remove_vault_name_from_json_meta(meta: &str) -> Result<String, error::Error> {
|
||||
let mut meta = if meta.is_empty() {
|
||||
Value::Object(serde_json::Map::new())
|
||||
} else {
|
||||
serde_json::from_str(meta)?
|
||||
};
|
||||
|
||||
if let Some(meta_obj) = meta.as_object_mut() {
|
||||
meta_obj.remove(VAULT_NAME_META_KEY);
|
||||
serde_json::to_string(meta_obj)
|
||||
} else {
|
||||
Err(error::Error::custom(
|
||||
"Meta is expected to be a serialized JSON object",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl VaultKeyFile {
|
||||
pub fn load<R>(reader: R) -> Result<Self, serde_json::Error>
|
||||
where
|
||||
R: Read,
|
||||
{
|
||||
serde_json::from_reader(reader)
|
||||
}
|
||||
|
||||
pub fn write<W>(&self, writer: &mut W) -> Result<(), serde_json::Error>
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
serde_json::to_writer(writer, self)
|
||||
}
|
||||
}
|
||||
|
||||
impl VaultKeyMeta {
|
||||
pub fn load(bytes: &[u8]) -> Result<Self, serde_json::Error> {
|
||||
serde_json::from_slice(&bytes)
|
||||
}
|
||||
|
||||
pub fn write(&self) -> Result<Vec<u8>, serde_json::Error> {
|
||||
let s = serde_json::to_string(self)?;
|
||||
Ok(s.as_bytes().into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use json::{
|
||||
insert_vault_name_to_json_meta, remove_vault_name_from_json_meta, Aes128Ctr, Cipher,
|
||||
Crypto, Kdf, Pbkdf2, Prf, VaultKeyFile, Version,
|
||||
};
|
||||
use serde_json;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
lazy_static! {
|
||||
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_and_from_json() {
|
||||
let file = VaultKeyFile {
|
||||
id: "08d82c39-88e3-7a71-6abb-89c8f36c3ceb".into(),
|
||||
version: Version::V3,
|
||||
crypto: Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||
iv: "fecb968bbc8c7e608a89ebcfe53a41d0".into(),
|
||||
}),
|
||||
ciphertext: "4befe0a66d9a4b6fec8e39eb5c90ac5dafdeaab005fff1af665fd1f9af925c91".into(),
|
||||
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
||||
c: *ITERATIONS,
|
||||
dklen: 32,
|
||||
prf: Prf::HmacSha256,
|
||||
salt: "f17731e84ecac390546692dbd4ccf6a3a2720dc9652984978381e61c28a471b2".into(),
|
||||
}),
|
||||
mac: "7c7c3daafb24cf11eb3079dfb9064a11e92f309a0ee1dd676486bab119e686b7".into(),
|
||||
},
|
||||
metacrypto: Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||
iv: "9c353fb3f894fc05946843616c26bb3f".into(),
|
||||
}),
|
||||
ciphertext: "fef0d113d7576c1702daf380ad6f4c5408389e57991cae2a174facd74bd549338e1014850bddbab7eb486ff5f5c9c5532800c6a6d4db2be2212cd5cd3769244ab230e1f369e8382a9e6d7c0a".into(),
|
||||
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
||||
c: *ITERATIONS,
|
||||
dklen: 32,
|
||||
prf: Prf::HmacSha256,
|
||||
salt: "aca82865174a82249a198814b263f43a631f272cbf7ed329d0f0839d259c652a".into(),
|
||||
}),
|
||||
mac: "b7413946bfe459d2801268dc331c04b3a84d92be11ef4dd9a507f895e8d9b5bd".into(),
|
||||
}
|
||||
};
|
||||
|
||||
let serialized = serde_json::to_string(&file).unwrap();
|
||||
let deserialized = serde_json::from_str(&serialized).unwrap();
|
||||
|
||||
assert_eq!(file, deserialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vault_name_inserted_to_json_meta() {
|
||||
assert_eq!(
|
||||
insert_vault_name_to_json_meta(r#""#, "MyVault").unwrap(),
|
||||
r#"{"vault":"MyVault"}"#
|
||||
);
|
||||
assert_eq!(
|
||||
insert_vault_name_to_json_meta(r#"{"tags":["kalabala"]}"#, "MyVault").unwrap(),
|
||||
r#"{"tags":["kalabala"],"vault":"MyVault"}"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vault_name_not_inserted_to_json_meta() {
|
||||
assert!(insert_vault_name_to_json_meta(r#"///3533"#, "MyVault").is_err());
|
||||
assert!(insert_vault_name_to_json_meta(r#""string""#, "MyVault").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vault_name_removed_from_json_meta() {
|
||||
assert_eq!(
|
||||
remove_vault_name_from_json_meta(r#"{"vault":"MyVault"}"#).unwrap(),
|
||||
r#"{}"#
|
||||
);
|
||||
assert_eq!(
|
||||
remove_vault_name_from_json_meta(r#"{"tags":["kalabala"],"vault":"MyVault"}"#).unwrap(),
|
||||
r#"{"tags":["kalabala"]}"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vault_name_not_removed_from_json_meta() {
|
||||
assert!(remove_vault_name_from_json_meta(r#"///3533"#).is_err());
|
||||
assert!(remove_vault_name_from_json_meta(r#""string""#).is_err());
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::Error;
|
||||
use serde::{
|
||||
de::{Error as SerdeError, Visitor},
|
||||
Deserialize, Deserializer, Serialize, Serializer,
|
||||
};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Version {
|
||||
V3,
|
||||
}
|
||||
|
||||
impl Serialize for Version {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match *self {
|
||||
Version::V3 => serializer.serialize_u64(3),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for Version {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Version, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
deserializer.deserialize_any(VersionVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
struct VersionVisitor;
|
||||
|
||||
impl<'a> Visitor<'a> for VersionVisitor {
|
||||
type Value = Version;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "a valid key version identifier")
|
||||
}
|
||||
|
||||
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
|
||||
where
|
||||
E: SerdeError,
|
||||
{
|
||||
match value {
|
||||
3 => Ok(Version::V3),
|
||||
_ => Err(SerdeError::custom(Error::UnsupportedVersion)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crypto::{self, pbkdf2, Keccak256};
|
||||
use ethkey::{Address, KeyPair, Password, Secret};
|
||||
use json;
|
||||
use std::{fs, num::NonZeroU32, path::Path};
|
||||
use Error;
|
||||
|
||||
/// Pre-sale wallet.
|
||||
pub struct PresaleWallet {
|
||||
iv: [u8; 16],
|
||||
ciphertext: Vec<u8>,
|
||||
address: Address,
|
||||
}
|
||||
|
||||
impl From<json::PresaleWallet> for PresaleWallet {
|
||||
fn from(wallet: json::PresaleWallet) -> Self {
|
||||
let mut iv = [0u8; 16];
|
||||
iv.copy_from_slice(&wallet.encseed[..16]);
|
||||
|
||||
let mut ciphertext = vec![];
|
||||
ciphertext.extend_from_slice(&wallet.encseed[16..]);
|
||||
|
||||
PresaleWallet {
|
||||
iv: iv,
|
||||
ciphertext: ciphertext,
|
||||
address: Address::from(wallet.address),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PresaleWallet {
|
||||
/// Open a pre-sale wallet.
|
||||
pub fn open<P>(path: P) -> Result<Self, Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let file = fs::File::open(path)?;
|
||||
let presale =
|
||||
json::PresaleWallet::load(file).map_err(|e| Error::InvalidKeyFile(format!("{}", e)))?;
|
||||
Ok(PresaleWallet::from(presale))
|
||||
}
|
||||
|
||||
/// Decrypt the wallet.
|
||||
pub fn decrypt(&self, password: &Password) -> Result<KeyPair, Error> {
|
||||
let mut derived_key = [0u8; 32];
|
||||
let salt = pbkdf2::Salt(password.as_bytes());
|
||||
let sec = pbkdf2::Secret(password.as_bytes());
|
||||
let iter = NonZeroU32::new(2000).expect("2000 > 0; qed");
|
||||
pbkdf2::sha256(iter, salt, sec, &mut derived_key);
|
||||
|
||||
let mut key = vec![0; self.ciphertext.len()];
|
||||
let len =
|
||||
crypto::aes::decrypt_128_cbc(&derived_key[0..16], &self.iv, &self.ciphertext, &mut key)
|
||||
.map_err(|_| Error::InvalidPassword)?;
|
||||
let unpadded = &key[..len];
|
||||
|
||||
let secret = Secret::from_unsafe_slice(&unpadded.keccak256())?;
|
||||
if let Ok(kp) = KeyPair::from_secret(secret) {
|
||||
if kp.address() == self.address {
|
||||
return Ok(kp);
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::InvalidPassword)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::PresaleWallet;
|
||||
use json;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let json = r#"
|
||||
{
|
||||
"encseed": "137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066",
|
||||
"ethaddr": "ede84640d1a1d3e06902048e67aa7db8d52c2ce1",
|
||||
"email": "123@gmail.com",
|
||||
"btcaddr": "1JvqEc6WLhg6GnyrLBe2ztPAU28KRfuseH"
|
||||
} "#;
|
||||
|
||||
let wallet = json::PresaleWallet::load(json.as_bytes()).unwrap();
|
||||
let wallet = PresaleWallet::from(wallet);
|
||||
assert!(wallet.decrypt(&"123".into()).is_ok());
|
||||
assert!(wallet.decrypt(&"124".into()).is_err());
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use rand::{OsRng, Rng};
|
||||
|
||||
pub trait Random {
|
||||
fn random() -> Self
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
impl Random for [u8; 16] {
|
||||
fn random() -> Self {
|
||||
let mut result = [0u8; 16];
|
||||
let mut rng = OsRng::new().unwrap();
|
||||
rng.fill_bytes(&mut result);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl Random for [u8; 32] {
|
||||
fn random() -> Self {
|
||||
let mut result = [0u8; 32];
|
||||
let mut rng = OsRng::new().unwrap();
|
||||
rng.fill_bytes(&mut result);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a random string of given length.
|
||||
pub fn random_string(length: usize) -> String {
|
||||
let mut rng = OsRng::new().expect("Not able to operate without random source.");
|
||||
rng.gen_ascii_chars().take(length).collect()
|
||||
}
|
||||
@@ -1,268 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use ethereum_types::H256;
|
||||
use ethkey::{Address, Message, Password, Public, Secret, Signature};
|
||||
use json::{OpaqueKeyFile, Uuid};
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
hash::{Hash, Hasher},
|
||||
path::PathBuf,
|
||||
};
|
||||
use Error;
|
||||
use OpaqueSecret;
|
||||
|
||||
/// Key directory reference
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum SecretVaultRef {
|
||||
/// Reference to key in root directory
|
||||
Root,
|
||||
/// Referenc to key in specific vault
|
||||
Vault(String),
|
||||
}
|
||||
|
||||
/// Stored account reference
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Ord)]
|
||||
pub struct StoreAccountRef {
|
||||
/// Account address
|
||||
pub address: Address,
|
||||
/// Vault reference
|
||||
pub vault: SecretVaultRef,
|
||||
}
|
||||
|
||||
impl PartialOrd for StoreAccountRef {
|
||||
fn partial_cmp(&self, other: &StoreAccountRef) -> Option<Ordering> {
|
||||
Some(
|
||||
self.address
|
||||
.cmp(&other.address)
|
||||
.then_with(|| self.vault.cmp(&other.vault)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::borrow::Borrow<Address> for StoreAccountRef {
|
||||
fn borrow(&self) -> &Address {
|
||||
&self.address
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple Secret Store API
|
||||
pub trait SimpleSecretStore: Send + Sync {
|
||||
/// Inserts new accounts to the store (or vault) with given password.
|
||||
fn insert_account(
|
||||
&self,
|
||||
vault: SecretVaultRef,
|
||||
secret: Secret,
|
||||
password: &Password,
|
||||
) -> Result<StoreAccountRef, Error>;
|
||||
/// Inserts new derived account to the store (or vault) with given password.
|
||||
fn insert_derived(
|
||||
&self,
|
||||
vault: SecretVaultRef,
|
||||
account_ref: &StoreAccountRef,
|
||||
password: &Password,
|
||||
derivation: Derivation,
|
||||
) -> Result<StoreAccountRef, Error>;
|
||||
/// Changes accounts password.
|
||||
fn change_password(
|
||||
&self,
|
||||
account: &StoreAccountRef,
|
||||
old_password: &Password,
|
||||
new_password: &Password,
|
||||
) -> Result<(), Error>;
|
||||
/// Exports key details for account.
|
||||
fn export_account(
|
||||
&self,
|
||||
account: &StoreAccountRef,
|
||||
password: &Password,
|
||||
) -> Result<OpaqueKeyFile, Error>;
|
||||
/// Entirely removes account from the store and underlying storage.
|
||||
fn remove_account(&self, account: &StoreAccountRef, password: &Password) -> Result<(), Error>;
|
||||
/// Generates new derived account.
|
||||
fn generate_derived(
|
||||
&self,
|
||||
account_ref: &StoreAccountRef,
|
||||
password: &Password,
|
||||
derivation: Derivation,
|
||||
) -> Result<Address, Error>;
|
||||
/// Sign a message with given account.
|
||||
fn sign(
|
||||
&self,
|
||||
account: &StoreAccountRef,
|
||||
password: &Password,
|
||||
message: &Message,
|
||||
) -> Result<Signature, Error>;
|
||||
/// Sign a message with derived account.
|
||||
fn sign_derived(
|
||||
&self,
|
||||
account_ref: &StoreAccountRef,
|
||||
password: &Password,
|
||||
derivation: Derivation,
|
||||
message: &Message,
|
||||
) -> Result<Signature, Error>;
|
||||
/// Decrypt a messages with given account.
|
||||
fn decrypt(
|
||||
&self,
|
||||
account: &StoreAccountRef,
|
||||
password: &Password,
|
||||
shared_mac: &[u8],
|
||||
message: &[u8],
|
||||
) -> Result<Vec<u8>, Error>;
|
||||
/// Agree on shared key.
|
||||
fn agree(
|
||||
&self,
|
||||
account: &StoreAccountRef,
|
||||
password: &Password,
|
||||
other: &Public,
|
||||
) -> Result<Secret, Error>;
|
||||
|
||||
/// Returns all accounts in this secret store.
|
||||
fn accounts(&self) -> Result<Vec<StoreAccountRef>, Error>;
|
||||
/// Get reference to some account with given address.
|
||||
/// This method could be removed if we will guarantee that there is max(1) account for given address.
|
||||
fn account_ref(&self, address: &Address) -> Result<StoreAccountRef, Error>;
|
||||
|
||||
/// Create new vault with given password
|
||||
fn create_vault(&self, name: &str, password: &Password) -> Result<(), Error>;
|
||||
/// Open vault with given password
|
||||
fn open_vault(&self, name: &str, password: &Password) -> Result<(), Error>;
|
||||
/// Close vault
|
||||
fn close_vault(&self, name: &str) -> Result<(), Error>;
|
||||
/// List all vaults
|
||||
fn list_vaults(&self) -> Result<Vec<String>, Error>;
|
||||
/// List all currently opened vaults
|
||||
fn list_opened_vaults(&self) -> Result<Vec<String>, Error>;
|
||||
/// Change vault password
|
||||
fn change_vault_password(&self, name: &str, new_password: &Password) -> Result<(), Error>;
|
||||
/// Cnage account' vault
|
||||
fn change_account_vault(
|
||||
&self,
|
||||
vault: SecretVaultRef,
|
||||
account: StoreAccountRef,
|
||||
) -> Result<StoreAccountRef, Error>;
|
||||
/// Get vault metadata string.
|
||||
fn get_vault_meta(&self, name: &str) -> Result<String, Error>;
|
||||
/// Set vault metadata string.
|
||||
fn set_vault_meta(&self, name: &str, meta: &str) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
/// Secret Store API
|
||||
pub trait SecretStore: SimpleSecretStore {
|
||||
/// Returns a raw opaque Secret that can be later used to sign a message.
|
||||
fn raw_secret(
|
||||
&self,
|
||||
account: &StoreAccountRef,
|
||||
password: &Password,
|
||||
) -> Result<OpaqueSecret, Error>;
|
||||
|
||||
/// Signs a message with raw secret.
|
||||
fn sign_with_secret(
|
||||
&self,
|
||||
secret: &OpaqueSecret,
|
||||
message: &Message,
|
||||
) -> Result<Signature, Error> {
|
||||
Ok(::ethkey::sign(&secret.0, message)?)
|
||||
}
|
||||
|
||||
/// Imports presale wallet
|
||||
fn import_presale(
|
||||
&self,
|
||||
vault: SecretVaultRef,
|
||||
json: &[u8],
|
||||
password: &Password,
|
||||
) -> Result<StoreAccountRef, Error>;
|
||||
/// Imports existing JSON wallet
|
||||
fn import_wallet(
|
||||
&self,
|
||||
vault: SecretVaultRef,
|
||||
json: &[u8],
|
||||
password: &Password,
|
||||
gen_id: bool,
|
||||
) -> Result<StoreAccountRef, Error>;
|
||||
/// Copies account between stores and vaults.
|
||||
fn copy_account(
|
||||
&self,
|
||||
new_store: &dyn SimpleSecretStore,
|
||||
new_vault: SecretVaultRef,
|
||||
account: &StoreAccountRef,
|
||||
password: &Password,
|
||||
new_password: &Password,
|
||||
) -> Result<(), Error>;
|
||||
/// Checks if password matches given account.
|
||||
fn test_password(&self, account: &StoreAccountRef, password: &Password) -> Result<bool, Error>;
|
||||
|
||||
/// Returns a public key for given account.
|
||||
fn public(&self, account: &StoreAccountRef, password: &Password) -> Result<Public, Error>;
|
||||
|
||||
/// Returns uuid of an account.
|
||||
fn uuid(&self, account: &StoreAccountRef) -> Result<Uuid, Error>;
|
||||
/// Returns account's name.
|
||||
fn name(&self, account: &StoreAccountRef) -> Result<String, Error>;
|
||||
/// Returns account's metadata.
|
||||
fn meta(&self, account: &StoreAccountRef) -> Result<String, Error>;
|
||||
|
||||
/// Modifies account metadata.
|
||||
fn set_name(&self, account: &StoreAccountRef, name: String) -> Result<(), Error>;
|
||||
/// Modifies account name.
|
||||
fn set_meta(&self, account: &StoreAccountRef, meta: String) -> Result<(), Error>;
|
||||
|
||||
/// Returns local path of the store.
|
||||
fn local_path(&self) -> PathBuf;
|
||||
}
|
||||
|
||||
impl StoreAccountRef {
|
||||
/// Create reference to root account with given address
|
||||
pub fn root(address: Address) -> Self {
|
||||
StoreAccountRef::new(SecretVaultRef::Root, address)
|
||||
}
|
||||
|
||||
/// Create reference to vault account with given address
|
||||
pub fn vault(vault_name: &str, address: Address) -> Self {
|
||||
StoreAccountRef::new(SecretVaultRef::Vault(vault_name.to_owned()), address)
|
||||
}
|
||||
|
||||
/// Create new account reference
|
||||
pub fn new(vault_ref: SecretVaultRef, address: Address) -> Self {
|
||||
StoreAccountRef {
|
||||
vault: vault_ref,
|
||||
address: address,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for StoreAccountRef {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.address.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
/// Node in hierarchical derivation.
|
||||
pub struct IndexDerivation {
|
||||
/// Node is soft (allows proof of parent from parent node).
|
||||
pub soft: bool,
|
||||
/// Index sequence of the node.
|
||||
pub index: u32,
|
||||
}
|
||||
|
||||
/// Derivation scheme for keys
|
||||
pub enum Derivation {
|
||||
/// Hierarchical derivation
|
||||
Hierarchical(Vec<IndexDerivation>),
|
||||
/// Hash derivation, soft.
|
||||
SoftHash(H256),
|
||||
/// Hash derivation, hard.
|
||||
HardHash(H256),
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate ethstore;
|
||||
extern crate rand;
|
||||
|
||||
mod util;
|
||||
|
||||
use ethstore::{
|
||||
accounts_dir::RootDiskDirectory,
|
||||
ethkey::{verify_address, Generator, KeyPair, Random, Secret},
|
||||
EthStore, SecretVaultRef, SimpleSecretStore, StoreAccountRef,
|
||||
};
|
||||
use util::TransientDir;
|
||||
|
||||
#[test]
|
||||
fn secret_store_create() {
|
||||
let dir = TransientDir::create().unwrap();
|
||||
let _ = EthStore::open(Box::new(dir)).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn secret_store_open_not_existing() {
|
||||
let dir = TransientDir::open();
|
||||
let _ = EthStore::open(Box::new(dir)).unwrap();
|
||||
}
|
||||
|
||||
fn random_secret() -> Secret {
|
||||
Random.generate().unwrap().secret().clone()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_store_create_account() {
|
||||
let dir = TransientDir::create().unwrap();
|
||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||
assert_eq!(store.accounts().unwrap().len(), 0);
|
||||
assert!(store
|
||||
.insert_account(SecretVaultRef::Root, random_secret(), &"".into())
|
||||
.is_ok());
|
||||
assert_eq!(store.accounts().unwrap().len(), 1);
|
||||
assert!(store
|
||||
.insert_account(SecretVaultRef::Root, random_secret(), &"".into())
|
||||
.is_ok());
|
||||
assert_eq!(store.accounts().unwrap().len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_store_sign() {
|
||||
let dir = TransientDir::create().unwrap();
|
||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||
assert!(store
|
||||
.insert_account(SecretVaultRef::Root, random_secret(), &"".into())
|
||||
.is_ok());
|
||||
let accounts = store.accounts().unwrap();
|
||||
assert_eq!(accounts.len(), 1);
|
||||
assert!(store
|
||||
.sign(&accounts[0], &"".into(), &Default::default())
|
||||
.is_ok());
|
||||
assert!(store
|
||||
.sign(&accounts[0], &"1".into(), &Default::default())
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_store_change_password() {
|
||||
let dir = TransientDir::create().unwrap();
|
||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||
assert!(store
|
||||
.insert_account(SecretVaultRef::Root, random_secret(), &"".into())
|
||||
.is_ok());
|
||||
let accounts = store.accounts().unwrap();
|
||||
assert_eq!(accounts.len(), 1);
|
||||
assert!(store
|
||||
.sign(&accounts[0], &"".into(), &Default::default())
|
||||
.is_ok());
|
||||
assert!(store
|
||||
.change_password(&accounts[0], &"".into(), &"1".into())
|
||||
.is_ok());
|
||||
assert!(store
|
||||
.sign(&accounts[0], &"".into(), &Default::default())
|
||||
.is_err());
|
||||
assert!(store
|
||||
.sign(&accounts[0], &"1".into(), &Default::default())
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_store_remove_account() {
|
||||
let dir = TransientDir::create().unwrap();
|
||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||
assert!(store
|
||||
.insert_account(SecretVaultRef::Root, random_secret(), &"".into())
|
||||
.is_ok());
|
||||
let accounts = store.accounts().unwrap();
|
||||
assert_eq!(accounts.len(), 1);
|
||||
assert!(store.remove_account(&accounts[0], &"".into()).is_ok());
|
||||
assert_eq!(store.accounts().unwrap().len(), 0);
|
||||
assert!(store.remove_account(&accounts[0], &"".into()).is_err());
|
||||
}
|
||||
|
||||
fn test_path() -> &'static str {
|
||||
match ::std::fs::metadata("ethstore") {
|
||||
Ok(_) => "ethstore/tests/res/geth_keystore",
|
||||
Err(_) => "tests/res/geth_keystore",
|
||||
}
|
||||
}
|
||||
|
||||
fn pat_path() -> &'static str {
|
||||
match ::std::fs::metadata("ethstore") {
|
||||
Ok(_) => "ethstore/tests/res/pat",
|
||||
Err(_) => "tests/res/pat",
|
||||
}
|
||||
}
|
||||
|
||||
fn ciphertext_path() -> &'static str {
|
||||
match ::std::fs::metadata("ethstore") {
|
||||
Ok(_) => "ethstore/tests/res/ciphertext",
|
||||
Err(_) => "tests/res/ciphertext",
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_store_laod_geth_files() {
|
||||
let dir = RootDiskDirectory::at(test_path());
|
||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||
assert_eq!(
|
||||
store.accounts().unwrap(),
|
||||
vec![
|
||||
StoreAccountRef::root("3f49624084b67849c7b4e805c5988c21a430f9d9".into()),
|
||||
StoreAccountRef::root("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf".into()),
|
||||
StoreAccountRef::root("63121b431a52f8043c16fcf0d1df9cb7b5f66649".into()),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_store_load_pat_files() {
|
||||
let dir = RootDiskDirectory::at(pat_path());
|
||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||
assert_eq!(
|
||||
store.accounts().unwrap(),
|
||||
vec![
|
||||
StoreAccountRef::root("3f49624084b67849c7b4e805c5988c21a430f9d9".into()),
|
||||
StoreAccountRef::root("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf".into()),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decrypting_files_with_short_ciphertext() {
|
||||
// 31e9d1e6d844bd3a536800ef8d8be6a9975db509, 30
|
||||
let kp1 = KeyPair::from_secret(
|
||||
"000081c29e8142bb6a81bef5a92bda7a8328a5c85bb2f9542e76f9b0f94fc018"
|
||||
.parse()
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
// d1e64e5480bfaf733ba7d48712decb8227797a4e , 31
|
||||
let kp2 = KeyPair::from_secret(
|
||||
"00fa7b3db73dc7dfdf8c5fbdb796d741e4488628c41fc4febd9160a866ba0f35"
|
||||
.parse()
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
let dir = RootDiskDirectory::at(ciphertext_path());
|
||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||
let accounts = store.accounts().unwrap();
|
||||
assert_eq!(
|
||||
accounts,
|
||||
vec![
|
||||
StoreAccountRef::root("31e9d1e6d844bd3a536800ef8d8be6a9975db509".into()),
|
||||
StoreAccountRef::root("d1e64e5480bfaf733ba7d48712decb8227797a4e".into()),
|
||||
]
|
||||
);
|
||||
|
||||
let message = Default::default();
|
||||
|
||||
let s1 = store.sign(&accounts[0], &"foo".into(), &message).unwrap();
|
||||
let s2 = store.sign(&accounts[1], &"foo".into(), &message).unwrap();
|
||||
assert!(verify_address(&accounts[0].address, &s1, &message).unwrap());
|
||||
assert!(verify_address(&kp1.address(), &s1, &message).unwrap());
|
||||
assert!(verify_address(&kp2.address(), &s2, &message).unwrap());
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use ethstore::{
|
||||
accounts_dir::{KeyDirectory, RootDiskDirectory},
|
||||
Error, SafeAccount,
|
||||
};
|
||||
use rand::{OsRng, Rng};
|
||||
use std::{env, fs, path::PathBuf};
|
||||
|
||||
pub fn random_dir() -> PathBuf {
|
||||
let mut rng = OsRng::new().unwrap();
|
||||
let mut dir = env::temp_dir();
|
||||
dir.push(format!("{:x}-{:x}", rng.next_u64(), rng.next_u64()));
|
||||
dir
|
||||
}
|
||||
|
||||
pub struct TransientDir {
|
||||
dir: RootDiskDirectory,
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl TransientDir {
|
||||
pub fn create() -> Result<Self, Error> {
|
||||
let path = random_dir();
|
||||
let result = TransientDir {
|
||||
dir: RootDiskDirectory::create(&path)?,
|
||||
path: path,
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn open() -> Self {
|
||||
let path = random_dir();
|
||||
TransientDir {
|
||||
dir: RootDiskDirectory::at(&path),
|
||||
path: path,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TransientDir {
|
||||
fn drop(&mut self) {
|
||||
fs::remove_dir_all(&self.path).expect("Expected to remove temp dir");
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyDirectory for TransientDir {
|
||||
fn load(&self) -> Result<Vec<SafeAccount>, Error> {
|
||||
self.dir.load()
|
||||
}
|
||||
|
||||
fn update(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
|
||||
self.dir.update(account)
|
||||
}
|
||||
|
||||
fn insert(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
|
||||
self.dir.insert(account)
|
||||
}
|
||||
|
||||
fn remove(&self, account: &SafeAccount) -> Result<(), Error> {
|
||||
self.dir.remove(account)
|
||||
}
|
||||
|
||||
fn unique_repr(&self) -> Result<u64, Error> {
|
||||
self.dir.unique_repr()
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Account Metadata
|
||||
|
||||
use std::{collections::HashMap, time::Instant};
|
||||
|
||||
use ethkey::{Address, Password};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
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,46 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use ethstore::Error as SSError;
|
||||
|
||||
/// Signing error
|
||||
#[derive(Debug)]
|
||||
pub enum SignError {
|
||||
/// Account is not unlocked
|
||||
NotUnlocked,
|
||||
/// Account does not exist.
|
||||
NotFound,
|
||||
/// 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::SStore(ref e) => write!(f, "{}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SSError> for SignError {
|
||||
fn from(e: SSError) -> Self {
|
||||
SignError::SStore(e)
|
||||
}
|
||||
}
|
||||
@@ -1,841 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
//! Account management.
|
||||
|
||||
mod account_data;
|
||||
mod error;
|
||||
mod stores;
|
||||
|
||||
use self::{
|
||||
account_data::{AccountData, Unlock},
|
||||
stores::AddressBook,
|
||||
};
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use ethkey::{Address, Generator, Message, Password, Public, Random, Secret};
|
||||
use ethstore::{
|
||||
accounts_dir::MemoryDirectory, random_string, EthMultiStore, EthStore, OpaqueSecret,
|
||||
SecretStore, SecretVaultRef, SimpleSecretStore, StoreAccountRef,
|
||||
};
|
||||
use log::*;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
pub use ethkey::Signature;
|
||||
pub use ethstore::{Derivation, Error, IndexDerivation, KeyFile};
|
||||
|
||||
pub use self::{account_data::AccountMeta, error::SignError};
|
||||
|
||||
type AccountToken = Password;
|
||||
|
||||
/// Account management settings.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct AccountProviderSettings {
|
||||
/// Store raw account secret when unlocking the account permanently.
|
||||
pub unlock_keep_secret: bool,
|
||||
/// Disallowed accounts.
|
||||
pub blacklisted_accounts: Vec<Address>,
|
||||
}
|
||||
|
||||
/// Account management.
|
||||
/// Responsible for unlocking accounts.
|
||||
pub struct AccountProvider {
|
||||
/// For performance reasons some methods can re-use unlocked secrets.
|
||||
unlocked_secrets: RwLock<HashMap<StoreAccountRef, OpaqueSecret>>,
|
||||
/// Unlocked account data.
|
||||
unlocked: RwLock<HashMap<StoreAccountRef, AccountData>>,
|
||||
/// Address book.
|
||||
address_book: RwLock<AddressBook>,
|
||||
/// Accounts on disk
|
||||
sstore: Box<dyn SecretStore>,
|
||||
/// Accounts unlocked with rolling tokens
|
||||
transient_sstore: EthMultiStore,
|
||||
/// When unlocking account permanently we additionally keep a raw secret in memory
|
||||
/// to increase the performance of transaction signing.
|
||||
unlock_keep_secret: bool,
|
||||
/// Disallowed accounts.
|
||||
blacklisted_accounts: Vec<Address>,
|
||||
}
|
||||
|
||||
fn transient_sstore() -> EthMultiStore {
|
||||
EthMultiStore::open(Box::new(MemoryDirectory::default()))
|
||||
.expect("MemoryDirectory load always succeeds; qed")
|
||||
}
|
||||
|
||||
impl AccountProvider {
|
||||
/// Creates new account provider.
|
||||
pub fn new(sstore: Box<dyn SecretStore>, settings: AccountProviderSettings) -> Self {
|
||||
if let Ok(accounts) = sstore.accounts() {
|
||||
for account in accounts
|
||||
.into_iter()
|
||||
.filter(|a| settings.blacklisted_accounts.contains(&a.address))
|
||||
{
|
||||
warn!("Local Account {} has a blacklisted (known to be weak) address and will be ignored",
|
||||
account.address);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove blacklisted accounts from address book.
|
||||
let mut address_book = AddressBook::new(&sstore.local_path());
|
||||
for addr in &settings.blacklisted_accounts {
|
||||
address_book.remove(*addr);
|
||||
}
|
||||
|
||||
AccountProvider {
|
||||
unlocked_secrets: RwLock::new(HashMap::new()),
|
||||
unlocked: RwLock::new(HashMap::new()),
|
||||
address_book: RwLock::new(address_book),
|
||||
sstore: sstore,
|
||||
transient_sstore: transient_sstore(),
|
||||
unlock_keep_secret: settings.unlock_keep_secret,
|
||||
blacklisted_accounts: settings.blacklisted_accounts,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates not disk backed provider.
|
||||
pub fn transient_provider() -> Self {
|
||||
AccountProvider {
|
||||
unlocked_secrets: RwLock::new(HashMap::new()),
|
||||
unlocked: RwLock::new(HashMap::new()),
|
||||
address_book: RwLock::new(AddressBook::transient()),
|
||||
sstore: Box::new(
|
||||
EthStore::open(Box::new(MemoryDirectory::default()))
|
||||
.expect("MemoryDirectory load always succeeds; qed"),
|
||||
),
|
||||
transient_sstore: transient_sstore(),
|
||||
unlock_keep_secret: false,
|
||||
blacklisted_accounts: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates new random account.
|
||||
pub fn new_account(&self, password: &Password) -> Result<Address, Error> {
|
||||
self.new_account_and_public(password).map(|d| d.0)
|
||||
}
|
||||
|
||||
/// Creates new random account and returns address and public key
|
||||
pub fn new_account_and_public(&self, password: &Password) -> Result<(Address, Public), Error> {
|
||||
let acc = Random
|
||||
.generate()
|
||||
.expect("secp context has generation capabilities; qed");
|
||||
let public = acc.public().clone();
|
||||
let secret = acc.secret().clone();
|
||||
let account = self
|
||||
.sstore
|
||||
.insert_account(SecretVaultRef::Root, secret, password)?;
|
||||
Ok((account.address, public))
|
||||
}
|
||||
|
||||
/// Inserts new account into underlying store.
|
||||
/// Does not unlock account!
|
||||
pub fn insert_account(&self, secret: Secret, password: &Password) -> Result<Address, Error> {
|
||||
let account = self
|
||||
.sstore
|
||||
.insert_account(SecretVaultRef::Root, secret, password)?;
|
||||
if self.blacklisted_accounts.contains(&account.address) {
|
||||
self.sstore.remove_account(&account, password)?;
|
||||
return Err(Error::InvalidAccount.into());
|
||||
}
|
||||
Ok(account.address)
|
||||
}
|
||||
|
||||
/// Generates new derived account based on the existing one
|
||||
/// If password is not provided, account must be unlocked
|
||||
/// New account will be created with the same password (if save: true)
|
||||
pub fn derive_account(
|
||||
&self,
|
||||
address: &Address,
|
||||
password: Option<Password>,
|
||||
derivation: Derivation,
|
||||
save: bool,
|
||||
) -> Result<Address, SignError> {
|
||||
let account = self.sstore.account_ref(&address)?;
|
||||
let password = password
|
||||
.map(Ok)
|
||||
.unwrap_or_else(|| self.password(&account))?;
|
||||
Ok(if save {
|
||||
self.sstore
|
||||
.insert_derived(SecretVaultRef::Root, &account, &password, derivation)?
|
||||
.address
|
||||
} else {
|
||||
self.sstore
|
||||
.generate_derived(&account, &password, derivation)?
|
||||
})
|
||||
}
|
||||
|
||||
/// Import a new presale wallet.
|
||||
pub fn import_presale(
|
||||
&self,
|
||||
presale_json: &[u8],
|
||||
password: &Password,
|
||||
) -> Result<Address, Error> {
|
||||
let account = self
|
||||
.sstore
|
||||
.import_presale(SecretVaultRef::Root, presale_json, password)?;
|
||||
Ok(Address::from(account.address).into())
|
||||
}
|
||||
|
||||
/// Import a new wallet.
|
||||
pub fn import_wallet(
|
||||
&self,
|
||||
json: &[u8],
|
||||
password: &Password,
|
||||
gen_id: bool,
|
||||
) -> Result<Address, Error> {
|
||||
let account = self
|
||||
.sstore
|
||||
.import_wallet(SecretVaultRef::Root, json, password, gen_id)?;
|
||||
if self.blacklisted_accounts.contains(&account.address) {
|
||||
self.sstore.remove_account(&account, password)?;
|
||||
return Err(Error::InvalidAccount.into());
|
||||
}
|
||||
Ok(Address::from(account.address).into())
|
||||
}
|
||||
|
||||
/// Checks whether an account with a given address is present.
|
||||
pub fn has_account(&self, address: Address) -> bool {
|
||||
self.sstore.account_ref(&address).is_ok() && !self.blacklisted_accounts.contains(&address)
|
||||
}
|
||||
|
||||
/// Returns addresses of all accounts.
|
||||
pub fn accounts(&self) -> Result<Vec<Address>, Error> {
|
||||
let accounts = self.sstore.accounts()?;
|
||||
Ok(accounts
|
||||
.into_iter()
|
||||
.map(|a| a.address)
|
||||
.filter(|address| !self.blacklisted_accounts.contains(address))
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// Returns the address of default account.
|
||||
pub fn default_account(&self) -> Result<Address, Error> {
|
||||
Ok(self.accounts()?.first().cloned().unwrap_or_default())
|
||||
}
|
||||
|
||||
/// Returns each address along with metadata.
|
||||
pub fn addresses_info(&self) -> HashMap<Address, AccountMeta> {
|
||||
self.address_book.read().get()
|
||||
}
|
||||
|
||||
/// Returns each address along with metadata.
|
||||
pub fn set_address_name(&self, account: Address, name: String) {
|
||||
self.address_book.write().set_name(account, name)
|
||||
}
|
||||
|
||||
/// Returns each address along with metadata.
|
||||
pub fn set_address_meta(&self, account: Address, meta: String) {
|
||||
self.address_book.write().set_meta(account, meta)
|
||||
}
|
||||
|
||||
/// Removes and address from the address book
|
||||
pub fn remove_address(&self, addr: Address) {
|
||||
self.address_book.write().remove(addr)
|
||||
}
|
||||
|
||||
/// Returns each account along with name and meta.
|
||||
pub fn accounts_info(&self) -> Result<HashMap<Address, AccountMeta>, Error> {
|
||||
let r = self
|
||||
.sstore
|
||||
.accounts()?
|
||||
.into_iter()
|
||||
.filter(|a| !self.blacklisted_accounts.contains(&a.address))
|
||||
.map(|a| {
|
||||
(
|
||||
a.address.clone(),
|
||||
self.account_meta(a.address).ok().unwrap_or_default(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
/// Returns each account along with name and meta.
|
||||
pub fn account_meta(&self, address: Address) -> Result<AccountMeta, Error> {
|
||||
let account = self.sstore.account_ref(&address)?;
|
||||
Ok(AccountMeta {
|
||||
name: self.sstore.name(&account)?,
|
||||
meta: self.sstore.meta(&account)?,
|
||||
uuid: self.sstore.uuid(&account).ok().map(Into::into), // allowed to not have a Uuid
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns account public key.
|
||||
pub fn account_public(&self, address: Address, password: &Password) -> Result<Public, Error> {
|
||||
self.sstore
|
||||
.public(&self.sstore.account_ref(&address)?, password)
|
||||
}
|
||||
|
||||
/// Returns each account along with name and meta.
|
||||
pub fn set_account_name(&self, address: Address, name: String) -> Result<(), Error> {
|
||||
self.sstore
|
||||
.set_name(&self.sstore.account_ref(&address)?, name)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns each account along with name and meta.
|
||||
pub fn set_account_meta(&self, address: Address, meta: String) -> Result<(), Error> {
|
||||
self.sstore
|
||||
.set_meta(&self.sstore.account_ref(&address)?, meta)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns `true` if the password for `account` is `password`. `false` if not.
|
||||
pub fn test_password(&self, address: &Address, password: &Password) -> Result<bool, Error> {
|
||||
self.sstore
|
||||
.test_password(&self.sstore.account_ref(&address)?, password)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Permanently removes an account.
|
||||
pub fn kill_account(&self, address: &Address, password: &Password) -> Result<(), Error> {
|
||||
self.sstore
|
||||
.remove_account(&self.sstore.account_ref(&address)?, &password)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Changes the password of `account` from `password` to `new_password`. Fails if incorrect `password` given.
|
||||
pub fn change_password(
|
||||
&self,
|
||||
address: &Address,
|
||||
password: Password,
|
||||
new_password: Password,
|
||||
) -> Result<(), Error> {
|
||||
self.sstore
|
||||
.change_password(&self.sstore.account_ref(address)?, &password, &new_password)
|
||||
}
|
||||
|
||||
/// Exports an account for given address.
|
||||
pub fn export_account(&self, address: &Address, password: Password) -> Result<KeyFile, Error> {
|
||||
self.sstore
|
||||
.export_account(&self.sstore.account_ref(address)?, &password)
|
||||
}
|
||||
|
||||
/// Helper method used for unlocking accounts.
|
||||
fn unlock_account(
|
||||
&self,
|
||||
address: Address,
|
||||
password: Password,
|
||||
unlock: Unlock,
|
||||
) -> Result<(), Error> {
|
||||
let account = self.sstore.account_ref(&address)?;
|
||||
|
||||
// check if account is already unlocked permanently, if it is, do nothing
|
||||
let mut unlocked = self.unlocked.write();
|
||||
if let Some(data) = unlocked.get(&account) {
|
||||
if let Unlock::Perm = data.unlock {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
if self.unlock_keep_secret && unlock == Unlock::Perm {
|
||||
// verify password and get the secret
|
||||
let secret = self.sstore.raw_secret(&account, &password)?;
|
||||
self.unlocked_secrets
|
||||
.write()
|
||||
.insert(account.clone(), secret);
|
||||
} else {
|
||||
// verify password by signing dump message
|
||||
// result may be discarded
|
||||
let _ = self.sstore.sign(&account, &password, &Default::default())?;
|
||||
}
|
||||
|
||||
let data = AccountData {
|
||||
unlock: unlock,
|
||||
password: password,
|
||||
};
|
||||
|
||||
unlocked.insert(account, data);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn password(&self, account: &StoreAccountRef) -> Result<Password, SignError> {
|
||||
let mut unlocked = self.unlocked.write();
|
||||
let data = unlocked.get(account).ok_or(SignError::NotUnlocked)?.clone();
|
||||
if let Unlock::OneTime = data.unlock {
|
||||
unlocked
|
||||
.remove(account)
|
||||
.expect("data exists: so key must exist: qed");
|
||||
}
|
||||
if let Unlock::Timed(ref end) = data.unlock {
|
||||
if Instant::now() > *end {
|
||||
unlocked
|
||||
.remove(account)
|
||||
.expect("data exists: so key must exist: qed");
|
||||
return Err(SignError::NotUnlocked);
|
||||
}
|
||||
}
|
||||
Ok(data.password)
|
||||
}
|
||||
|
||||
/// Unlocks account permanently.
|
||||
pub fn unlock_account_permanently(
|
||||
&self,
|
||||
account: Address,
|
||||
password: Password,
|
||||
) -> Result<(), Error> {
|
||||
self.unlock_account(account, password, Unlock::Perm)
|
||||
}
|
||||
|
||||
/// Unlocks account temporarily (for one signing).
|
||||
pub fn unlock_account_temporarily(
|
||||
&self,
|
||||
account: Address,
|
||||
password: Password,
|
||||
) -> Result<(), Error> {
|
||||
self.unlock_account(account, password, Unlock::OneTime)
|
||||
}
|
||||
|
||||
/// Unlocks account temporarily with a timeout.
|
||||
pub fn unlock_account_timed(
|
||||
&self,
|
||||
account: Address,
|
||||
password: Password,
|
||||
duration: Duration,
|
||||
) -> Result<(), Error> {
|
||||
self.unlock_account(account, password, Unlock::Timed(Instant::now() + duration))
|
||||
}
|
||||
|
||||
/// Checks if given account is unlocked
|
||||
pub fn is_unlocked(&self, address: &Address) -> bool {
|
||||
let unlocked = self.unlocked.read();
|
||||
let unlocked_secrets = self.unlocked_secrets.read();
|
||||
self.sstore
|
||||
.account_ref(address)
|
||||
.map(|r| unlocked.get(&r).is_some() || unlocked_secrets.get(&r).is_some())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Checks if given account is unlocked permanently
|
||||
pub fn is_unlocked_permanently(&self, address: &Address) -> bool {
|
||||
let unlocked = self.unlocked.read();
|
||||
self.sstore
|
||||
.account_ref(address)
|
||||
.map(|r| {
|
||||
unlocked
|
||||
.get(&r)
|
||||
.map_or(false, |account| account.unlock == Unlock::Perm)
|
||||
})
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Signs the message. If password is not provided the account must be unlocked.
|
||||
pub fn sign(
|
||||
&self,
|
||||
address: Address,
|
||||
password: Option<Password>,
|
||||
message: Message,
|
||||
) -> Result<Signature, SignError> {
|
||||
let account = self.sstore.account_ref(&address)?;
|
||||
match self.unlocked_secrets.read().get(&account) {
|
||||
Some(secret) => Ok(self.sstore.sign_with_secret(&secret, &message)?),
|
||||
None => {
|
||||
let password = password
|
||||
.map(Ok)
|
||||
.unwrap_or_else(|| self.password(&account))?;
|
||||
Ok(self.sstore.sign(&account, &password, &message)?)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Signs message using the derived secret. If password is not provided the account must be unlocked.
|
||||
pub fn sign_derived(
|
||||
&self,
|
||||
address: &Address,
|
||||
password: Option<Password>,
|
||||
derivation: Derivation,
|
||||
message: Message,
|
||||
) -> Result<Signature, SignError> {
|
||||
let account = self.sstore.account_ref(address)?;
|
||||
let password = password
|
||||
.map(Ok)
|
||||
.unwrap_or_else(|| self.password(&account))?;
|
||||
Ok(self
|
||||
.sstore
|
||||
.sign_derived(&account, &password, derivation, &message)?)
|
||||
}
|
||||
|
||||
/// Signs given message with supplied token. Returns a token to use in next signing within this session.
|
||||
pub fn sign_with_token(
|
||||
&self,
|
||||
address: Address,
|
||||
token: AccountToken,
|
||||
message: Message,
|
||||
) -> Result<(Signature, AccountToken), SignError> {
|
||||
let account = self.sstore.account_ref(&address)?;
|
||||
let is_std_password = self.sstore.test_password(&account, &token)?;
|
||||
|
||||
let new_token = Password::from(random_string(16));
|
||||
let signature = if is_std_password {
|
||||
// Insert to transient store
|
||||
self.sstore.copy_account(
|
||||
&self.transient_sstore,
|
||||
SecretVaultRef::Root,
|
||||
&account,
|
||||
&token,
|
||||
&new_token,
|
||||
)?;
|
||||
// sign
|
||||
self.sstore.sign(&account, &token, &message)?
|
||||
} else {
|
||||
// check transient store
|
||||
self.transient_sstore
|
||||
.change_password(&account, &token, &new_token)?;
|
||||
// and sign
|
||||
self.transient_sstore.sign(&account, &new_token, &message)?
|
||||
};
|
||||
|
||||
Ok((signature, new_token))
|
||||
}
|
||||
|
||||
/// Decrypts a message with given token. Returns a token to use in next operation for this account.
|
||||
pub fn decrypt_with_token(
|
||||
&self,
|
||||
address: Address,
|
||||
token: AccountToken,
|
||||
shared_mac: &[u8],
|
||||
message: &[u8],
|
||||
) -> Result<(Vec<u8>, AccountToken), SignError> {
|
||||
let account = self.sstore.account_ref(&address)?;
|
||||
let is_std_password = self.sstore.test_password(&account, &token)?;
|
||||
|
||||
let new_token = Password::from(random_string(16));
|
||||
let message = if is_std_password {
|
||||
// Insert to transient store
|
||||
self.sstore.copy_account(
|
||||
&self.transient_sstore,
|
||||
SecretVaultRef::Root,
|
||||
&account,
|
||||
&token,
|
||||
&new_token,
|
||||
)?;
|
||||
// decrypt
|
||||
self.sstore.decrypt(&account, &token, shared_mac, message)?
|
||||
} else {
|
||||
// check transient store
|
||||
self.transient_sstore
|
||||
.change_password(&account, &token, &new_token)?;
|
||||
// and decrypt
|
||||
self.transient_sstore
|
||||
.decrypt(&account, &token, shared_mac, message)?
|
||||
};
|
||||
|
||||
Ok((message, new_token))
|
||||
}
|
||||
|
||||
/// Decrypts a message. If password is not provided the account must be unlocked.
|
||||
pub fn decrypt(
|
||||
&self,
|
||||
address: Address,
|
||||
password: Option<Password>,
|
||||
shared_mac: &[u8],
|
||||
message: &[u8],
|
||||
) -> Result<Vec<u8>, SignError> {
|
||||
let account = self.sstore.account_ref(&address)?;
|
||||
let password = password
|
||||
.map(Ok)
|
||||
.unwrap_or_else(|| self.password(&account))?;
|
||||
Ok(self
|
||||
.sstore
|
||||
.decrypt(&account, &password, shared_mac, message)?)
|
||||
}
|
||||
|
||||
/// Agree on shared key.
|
||||
pub fn agree(
|
||||
&self,
|
||||
address: Address,
|
||||
password: Option<Password>,
|
||||
other_public: &Public,
|
||||
) -> Result<Secret, SignError> {
|
||||
let account = self.sstore.account_ref(&address)?;
|
||||
let password = password
|
||||
.map(Ok)
|
||||
.unwrap_or_else(|| self.password(&account))?;
|
||||
Ok(self.sstore.agree(&account, &password, other_public)?)
|
||||
}
|
||||
|
||||
/// Create new vault.
|
||||
pub fn create_vault(&self, name: &str, password: &Password) -> Result<(), Error> {
|
||||
self.sstore.create_vault(name, password).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Open existing vault.
|
||||
pub fn open_vault(&self, name: &str, password: &Password) -> Result<(), Error> {
|
||||
self.sstore.open_vault(name, password).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Close previously opened vault.
|
||||
pub fn close_vault(&self, name: &str) -> Result<(), Error> {
|
||||
self.sstore.close_vault(name).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// List all vaults
|
||||
pub fn list_vaults(&self) -> Result<Vec<String>, Error> {
|
||||
self.sstore.list_vaults().map_err(Into::into)
|
||||
}
|
||||
|
||||
/// List all currently opened vaults
|
||||
pub fn list_opened_vaults(&self) -> Result<Vec<String>, Error> {
|
||||
self.sstore.list_opened_vaults().map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Change vault password.
|
||||
pub fn change_vault_password(&self, name: &str, new_password: &Password) -> Result<(), Error> {
|
||||
self.sstore
|
||||
.change_vault_password(name, new_password)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Change vault of the given address.
|
||||
pub fn change_vault(&self, address: Address, new_vault: &str) -> Result<(), Error> {
|
||||
let new_vault_ref = if new_vault.is_empty() {
|
||||
SecretVaultRef::Root
|
||||
} else {
|
||||
SecretVaultRef::Vault(new_vault.to_owned())
|
||||
};
|
||||
let old_account_ref = self.sstore.account_ref(&address)?;
|
||||
self.sstore
|
||||
.change_account_vault(new_vault_ref, old_account_ref)
|
||||
.map_err(Into::into)
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
/// Get vault metadata string.
|
||||
pub fn get_vault_meta(&self, name: &str) -> Result<String, Error> {
|
||||
self.sstore.get_vault_meta(name).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Set vault metadata string.
|
||||
pub fn set_vault_meta(&self, name: &str, meta: &str) -> Result<(), Error> {
|
||||
self.sstore.set_vault_meta(name, meta).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{AccountProvider, Unlock};
|
||||
use ethereum_types::H256;
|
||||
use ethkey::{Address, Generator, Random};
|
||||
use ethstore::{Derivation, StoreAccountRef};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[test]
|
||||
fn unlock_account_temp() {
|
||||
let kp = Random.generate().unwrap();
|
||||
let ap = AccountProvider::transient_provider();
|
||||
assert!(ap
|
||||
.insert_account(kp.secret().clone(), &"test".into())
|
||||
.is_ok());
|
||||
assert!(ap
|
||||
.unlock_account_temporarily(kp.address(), "test1".into())
|
||||
.is_err());
|
||||
assert!(ap
|
||||
.unlock_account_temporarily(kp.address(), "test".into())
|
||||
.is_ok());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derived_account_nosave() {
|
||||
let kp = Random.generate().unwrap();
|
||||
let ap = AccountProvider::transient_provider();
|
||||
assert!(ap
|
||||
.insert_account(kp.secret().clone(), &"base".into())
|
||||
.is_ok());
|
||||
assert!(ap
|
||||
.unlock_account_permanently(kp.address(), "base".into())
|
||||
.is_ok());
|
||||
|
||||
let derived_addr = ap
|
||||
.derive_account(
|
||||
&kp.address(),
|
||||
None,
|
||||
Derivation::SoftHash(H256::from(999)),
|
||||
false,
|
||||
)
|
||||
.expect("Derivation should not fail");
|
||||
|
||||
assert!(
|
||||
ap.unlock_account_permanently(derived_addr, "base".into())
|
||||
.is_err(),
|
||||
"There should be an error because account is not supposed to be saved"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derived_account_save() {
|
||||
let kp = Random.generate().unwrap();
|
||||
let ap = AccountProvider::transient_provider();
|
||||
assert!(ap
|
||||
.insert_account(kp.secret().clone(), &"base".into())
|
||||
.is_ok());
|
||||
assert!(ap
|
||||
.unlock_account_permanently(kp.address(), "base".into())
|
||||
.is_ok());
|
||||
|
||||
let derived_addr = ap
|
||||
.derive_account(
|
||||
&kp.address(),
|
||||
None,
|
||||
Derivation::SoftHash(H256::from(999)),
|
||||
true,
|
||||
)
|
||||
.expect("Derivation should not fail");
|
||||
|
||||
assert!(
|
||||
ap.unlock_account_permanently(derived_addr, "base_wrong".into())
|
||||
.is_err(),
|
||||
"There should be an error because password is invalid"
|
||||
);
|
||||
|
||||
assert!(
|
||||
ap.unlock_account_permanently(derived_addr, "base".into())
|
||||
.is_ok(),
|
||||
"Should be ok because account is saved and password is valid"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derived_account_sign() {
|
||||
let kp = Random.generate().unwrap();
|
||||
let ap = AccountProvider::transient_provider();
|
||||
assert!(ap
|
||||
.insert_account(kp.secret().clone(), &"base".into())
|
||||
.is_ok());
|
||||
assert!(ap
|
||||
.unlock_account_permanently(kp.address(), "base".into())
|
||||
.is_ok());
|
||||
|
||||
let derived_addr = ap
|
||||
.derive_account(
|
||||
&kp.address(),
|
||||
None,
|
||||
Derivation::SoftHash(H256::from(1999)),
|
||||
true,
|
||||
)
|
||||
.expect("Derivation should not fail");
|
||||
ap.unlock_account_permanently(derived_addr, "base".into())
|
||||
.expect("Should be ok because account is saved and password is valid");
|
||||
|
||||
let msg = Default::default();
|
||||
let signed_msg1 = ap
|
||||
.sign(derived_addr, None, msg)
|
||||
.expect("Signing with existing unlocked account should not fail");
|
||||
let signed_msg2 = ap
|
||||
.sign_derived(
|
||||
&kp.address(),
|
||||
None,
|
||||
Derivation::SoftHash(H256::from(1999)),
|
||||
msg,
|
||||
)
|
||||
.expect("Derived signing with existing unlocked account should not fail");
|
||||
|
||||
assert_eq!(signed_msg1, signed_msg2, "Signed messages should match");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unlock_account_perm() {
|
||||
let kp = Random.generate().unwrap();
|
||||
let ap = AccountProvider::transient_provider();
|
||||
assert!(ap
|
||||
.insert_account(kp.secret().clone(), &"test".into())
|
||||
.is_ok());
|
||||
assert!(ap
|
||||
.unlock_account_permanently(kp.address(), "test1".into())
|
||||
.is_err());
|
||||
assert!(ap
|
||||
.unlock_account_permanently(kp.address(), "test".into())
|
||||
.is_ok());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||
assert!(ap
|
||||
.unlock_account_temporarily(kp.address(), "test".into())
|
||||
.is_ok());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unlock_account_timer() {
|
||||
let kp = Random.generate().unwrap();
|
||||
let ap = AccountProvider::transient_provider();
|
||||
assert!(ap
|
||||
.insert_account(kp.secret().clone(), &"test".into())
|
||||
.is_ok());
|
||||
assert!(ap
|
||||
.unlock_account_timed(kp.address(), "test1".into(), Duration::from_secs(60))
|
||||
.is_err());
|
||||
assert!(ap
|
||||
.unlock_account_timed(kp.address(), "test".into(), Duration::from_secs(60))
|
||||
.is_ok());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||
ap.unlocked
|
||||
.write()
|
||||
.get_mut(&StoreAccountRef::root(kp.address()))
|
||||
.unwrap()
|
||||
.unlock = Unlock::Timed(Instant::now());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_sign_and_return_token() {
|
||||
// given
|
||||
let kp = Random.generate().unwrap();
|
||||
let ap = AccountProvider::transient_provider();
|
||||
assert!(ap
|
||||
.insert_account(kp.secret().clone(), &"test".into())
|
||||
.is_ok());
|
||||
|
||||
// when
|
||||
let (_signature, token) = ap
|
||||
.sign_with_token(kp.address(), "test".into(), Default::default())
|
||||
.unwrap();
|
||||
|
||||
// then
|
||||
ap.sign_with_token(kp.address(), token.clone(), Default::default())
|
||||
.expect("First usage of token should be correct.");
|
||||
assert!(
|
||||
ap.sign_with_token(kp.address(), token, Default::default())
|
||||
.is_err(),
|
||||
"Second usage of the same token should fail."
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_return_blacklisted_account() {
|
||||
// given
|
||||
let mut ap = AccountProvider::transient_provider();
|
||||
let acc = ap.new_account(&"test".into()).unwrap();
|
||||
ap.blacklisted_accounts = vec![acc];
|
||||
|
||||
// then
|
||||
assert_eq!(
|
||||
ap.accounts_info()
|
||||
.unwrap()
|
||||
.keys()
|
||||
.cloned()
|
||||
.collect::<Vec<Address>>(),
|
||||
vec![]
|
||||
);
|
||||
assert_eq!(ap.accounts().unwrap(), vec![]);
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Address Book Store
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fmt, fs, hash, ops,
|
||||
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 crate::account_data::AccountMeta;
|
||||
use std::collections::HashMap;
|
||||
use tempdir::TempDir;
|
||||
|
||||
#[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<_, _>>()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
[package]
|
||||
description = "Parity Ethereum Chain Specification"
|
||||
name = "chainspec"
|
||||
version = "0.1.0"
|
||||
authors = ["Marek Kotewicz <marek@parity.io>"]
|
||||
|
||||
@@ -1,51 +1,49 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate ethjson;
|
||||
extern crate serde_json;
|
||||
extern crate ethjson;
|
||||
|
||||
use std::{fs, env, process};
|
||||
use ethjson::spec::Spec;
|
||||
use std::{env, fs, process};
|
||||
|
||||
fn quit(s: &str) -> ! {
|
||||
println!("{}", s);
|
||||
process::exit(1);
|
||||
println!("{}", s);
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut args = env::args();
|
||||
if args.len() != 2 {
|
||||
quit(
|
||||
"You need to specify chainspec.json\n\
|
||||
let mut args = env::args();
|
||||
if args.len() != 2 {
|
||||
quit("You need to specify chainspec.json\n\
|
||||
\n\
|
||||
./chainspec <chainspec.json>",
|
||||
);
|
||||
}
|
||||
./chainspec <chainspec.json>");
|
||||
}
|
||||
|
||||
let path = args.nth(1).expect("args.len() == 2; qed");
|
||||
let file = match fs::File::open(&path) {
|
||||
Ok(file) => file,
|
||||
Err(_) => quit(&format!("{} could not be opened", path)),
|
||||
};
|
||||
let path = args.nth(1).expect("args.len() == 2; qed");
|
||||
let file = match fs::File::open(&path) {
|
||||
Ok(file) => file,
|
||||
Err(_) => quit(&format!("{} could not be opened", path)),
|
||||
};
|
||||
|
||||
let spec: Result<Spec, _> = serde_json::from_reader(file);
|
||||
let spec: Result<Spec, _> = serde_json::from_reader(file);
|
||||
|
||||
if let Err(err) = spec {
|
||||
quit(&format!("{} {}", path, err.to_string()));
|
||||
}
|
||||
if let Err(err) = spec {
|
||||
quit(&format!("{} {}", path, err.to_string()));
|
||||
}
|
||||
|
||||
println!("{} is valid", path);
|
||||
println!("{} is valid", path);
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
[package]
|
||||
description = "OpenEthereum CLI Signer Tool"
|
||||
homepage = "https://github.com/openethereum/openethereum"
|
||||
license = "GPL-3.0"
|
||||
name = "cli-signer"
|
||||
version = "1.4.0"
|
||||
authors = ["Parity <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
ethereum-types = "0.4"
|
||||
futures = "0.1"
|
||||
rpassword = "1.0"
|
||||
parity-rpc = { path = "../rpc" }
|
||||
parity-rpc-client = { path = "rpc-client" }
|
||||
@@ -1,21 +0,0 @@
|
||||
[package]
|
||||
description = "OpenEthereum RPC Client"
|
||||
homepage = "https://github.com/openethereum/openethereum"
|
||||
license = "GPL-3.0"
|
||||
name = "parity-rpc-client"
|
||||
version = "1.4.0"
|
||||
authors = ["Parity <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
ethereum-types = "0.4"
|
||||
futures = "0.1"
|
||||
log = "0.4"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
url = "2"
|
||||
matches = "0.1"
|
||||
parking_lot = "0.9"
|
||||
jsonrpc-core = "15.0.0"
|
||||
jsonrpc-ws-server = "15.0.0"
|
||||
parity-rpc = { path = "../../rpc" }
|
||||
keccak-hash = "0.1"
|
||||
@@ -1,327 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
fmt::{Debug, Error as FmtError, Formatter},
|
||||
io::{BufRead, BufReader},
|
||||
sync::{
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Arc,
|
||||
},
|
||||
thread, time,
|
||||
};
|
||||
|
||||
use hash::keccak;
|
||||
use parking_lot::Mutex;
|
||||
use std::{fs::File, path::PathBuf};
|
||||
use url::Url;
|
||||
|
||||
use ws::ws::{
|
||||
self, Error as WsError, ErrorKind as WsErrorKind, Handler, Handshake, Message, Request,
|
||||
Result as WsResult, Sender,
|
||||
};
|
||||
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde_json::{self as json, Error as JsonError, Value as JsonValue};
|
||||
|
||||
use futures::{done, oneshot, Canceled, Complete, Future};
|
||||
|
||||
use jsonrpc_core::{
|
||||
request::MethodCall,
|
||||
response::{Failure, Output, Success},
|
||||
Error as JsonRpcError, Id, Params, Version,
|
||||
};
|
||||
|
||||
use BoxFuture;
|
||||
|
||||
/// The actual websocket connection handler, passed into the
|
||||
/// event loop of ws-rs
|
||||
struct RpcHandler {
|
||||
pending: Pending,
|
||||
// Option is used here as temporary storage until connection
|
||||
// is setup and the values are moved into the new `Rpc`
|
||||
complete: Option<Complete<Result<Rpc, RpcError>>>,
|
||||
auth_code: String,
|
||||
out: Option<Sender>,
|
||||
}
|
||||
|
||||
impl RpcHandler {
|
||||
fn new(out: Sender, auth_code: String, complete: Complete<Result<Rpc, RpcError>>) -> Self {
|
||||
RpcHandler {
|
||||
out: Some(out),
|
||||
auth_code: auth_code,
|
||||
pending: Pending::new(),
|
||||
complete: Some(complete),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Handler for RpcHandler {
|
||||
fn build_request(&mut self, url: &Url) -> WsResult<Request> {
|
||||
match Request::from_url(url) {
|
||||
Ok(mut r) => {
|
||||
let timestamp = time::UNIX_EPOCH
|
||||
.elapsed()
|
||||
.map_err(|err| WsError::new(WsErrorKind::Internal, format!("{}", err)))?;
|
||||
let secs = timestamp.as_secs();
|
||||
let hashed = keccak(format!("{}:{}", self.auth_code, secs));
|
||||
let proto = format!("{:x}_{}", hashed, secs);
|
||||
r.add_protocol(&proto);
|
||||
Ok(r)
|
||||
}
|
||||
Err(e) => Err(WsError::new(WsErrorKind::Internal, format!("{}", e))),
|
||||
}
|
||||
}
|
||||
fn on_error(&mut self, err: WsError) {
|
||||
match self.complete.take() {
|
||||
Some(c) => match c.send(Err(RpcError::WsError(err))) {
|
||||
Ok(_) => {}
|
||||
Err(_) => warn!(target: "rpc-client", "Unable to notify about error."),
|
||||
},
|
||||
None => warn!(target: "rpc-client", "unexpected error: {}", err),
|
||||
}
|
||||
}
|
||||
fn on_open(&mut self, _: Handshake) -> WsResult<()> {
|
||||
match (self.complete.take(), self.out.take()) {
|
||||
(Some(c), Some(out)) => {
|
||||
let res = c.send(Ok(Rpc {
|
||||
out: out,
|
||||
counter: AtomicUsize::new(0),
|
||||
pending: self.pending.clone(),
|
||||
}));
|
||||
if let Err(_) = res {
|
||||
warn!(target: "rpc-client", "Unable to open a connection.")
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
_ => {
|
||||
let msg = format!("on_open called twice");
|
||||
Err(WsError::new(WsErrorKind::Internal, msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
fn on_message(&mut self, msg: Message) -> WsResult<()> {
|
||||
let ret: Result<JsonValue, JsonRpcError>;
|
||||
let response_id;
|
||||
let string = &msg.to_string();
|
||||
match json::from_str::<Output>(&string) {
|
||||
Ok(Output::Success(Success {
|
||||
result,
|
||||
id: Id::Num(id),
|
||||
..
|
||||
})) => {
|
||||
ret = Ok(result);
|
||||
response_id = id as usize;
|
||||
}
|
||||
Ok(Output::Failure(Failure {
|
||||
error,
|
||||
id: Id::Num(id),
|
||||
..
|
||||
})) => {
|
||||
ret = Err(error);
|
||||
response_id = id as usize;
|
||||
}
|
||||
Err(e) => {
|
||||
warn!(
|
||||
target: "rpc-client",
|
||||
"recieved invalid message: {}\n {:?}",
|
||||
string,
|
||||
e
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
_ => {
|
||||
warn!(
|
||||
target: "rpc-client",
|
||||
"recieved invalid message: {}",
|
||||
string
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
match self.pending.remove(response_id) {
|
||||
Some(c) => {
|
||||
if let Err(_) = c.send(ret.map_err(|err| RpcError::JsonRpc(err))) {
|
||||
warn!(target: "rpc-client", "Unable to send response.")
|
||||
}
|
||||
}
|
||||
None => warn!(
|
||||
target: "rpc-client",
|
||||
"warning: unexpected id: {}",
|
||||
response_id
|
||||
),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Keeping track of issued requests to be matched up with responses
|
||||
#[derive(Clone)]
|
||||
struct Pending(Arc<Mutex<BTreeMap<usize, Complete<Result<JsonValue, RpcError>>>>>);
|
||||
|
||||
impl Pending {
|
||||
fn new() -> Self {
|
||||
Pending(Arc::new(Mutex::new(BTreeMap::new())))
|
||||
}
|
||||
fn insert(&mut self, k: usize, v: Complete<Result<JsonValue, RpcError>>) {
|
||||
self.0.lock().insert(k, v);
|
||||
}
|
||||
fn remove(&mut self, k: usize) -> Option<Complete<Result<JsonValue, RpcError>>> {
|
||||
self.0.lock().remove(&k)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_authcode(path: &PathBuf) -> Result<String, RpcError> {
|
||||
if let Ok(fd) = File::open(path) {
|
||||
if let Some(Ok(line)) = BufReader::new(fd).lines().next() {
|
||||
let mut parts = line.split(';');
|
||||
let token = parts.next();
|
||||
|
||||
if let Some(code) = token {
|
||||
return Ok(code.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(RpcError::NoAuthCode)
|
||||
}
|
||||
|
||||
/// The handle to the connection
|
||||
pub struct Rpc {
|
||||
out: Sender,
|
||||
counter: AtomicUsize,
|
||||
pending: Pending,
|
||||
}
|
||||
|
||||
impl Rpc {
|
||||
/// Blocking, returns a new initialized connection or RpcError
|
||||
pub fn new(url: &str, authpath: &PathBuf) -> Result<Self, RpcError> {
|
||||
let rpc = Self::connect(url, authpath).map(|rpc| rpc).wait()?;
|
||||
rpc
|
||||
}
|
||||
|
||||
/// Non-blocking, returns a future
|
||||
pub fn connect(url: &str, authpath: &PathBuf) -> BoxFuture<Result<Self, RpcError>, Canceled> {
|
||||
let (c, p) = oneshot::<Result<Self, RpcError>>();
|
||||
match get_authcode(authpath) {
|
||||
Err(e) => return Box::new(done(Ok(Err(e)))),
|
||||
Ok(code) => {
|
||||
let url = String::from(url);
|
||||
// The ws::connect takes a FnMut closure, which means c cannot
|
||||
// be moved into it, since it's consumed on complete.
|
||||
// Therefore we wrap it in an option and pick it out once.
|
||||
let mut once = Some(c);
|
||||
thread::spawn(move || {
|
||||
let conn = ws::connect(url, |out| {
|
||||
// this will panic if the closure is called twice,
|
||||
// which it should never be.
|
||||
let c = once.take().expect("connection closure called only once");
|
||||
RpcHandler::new(out, code.clone(), c)
|
||||
});
|
||||
match conn {
|
||||
Err(err) => {
|
||||
// since ws::connect is only called once, it cannot
|
||||
// both fail and succeed.
|
||||
let c = once.take().expect("connection closure called only once");
|
||||
let _ = c.send(Err(RpcError::WsError(err)));
|
||||
}
|
||||
// c will complete on the `on_open` event in the Handler
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
Box::new(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Non-blocking, returns a future of the request response
|
||||
pub fn request<T>(
|
||||
&mut self,
|
||||
method: &'static str,
|
||||
params: Vec<JsonValue>,
|
||||
) -> BoxFuture<Result<T, RpcError>, Canceled>
|
||||
where
|
||||
T: DeserializeOwned + Send + Sized,
|
||||
{
|
||||
let (c, p) = oneshot::<Result<JsonValue, RpcError>>();
|
||||
|
||||
let id = self.counter.fetch_add(1, Ordering::Relaxed);
|
||||
self.pending.insert(id, c);
|
||||
|
||||
let request = MethodCall {
|
||||
jsonrpc: Some(Version::V2),
|
||||
method: method.to_owned(),
|
||||
params: Params::Array(params),
|
||||
id: Id::Num(id as u64),
|
||||
};
|
||||
|
||||
let serialized = json::to_string(&request).expect("request is serializable");
|
||||
let _ = self.out.send(serialized);
|
||||
|
||||
Box::new(p.map(|result| match result {
|
||||
Ok(json) => {
|
||||
let t: T = json::from_value(json)?;
|
||||
Ok(t)
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub enum RpcError {
|
||||
WrongVersion(String),
|
||||
ParseError(JsonError),
|
||||
MalformedResponse(String),
|
||||
JsonRpc(JsonRpcError),
|
||||
WsError(WsError),
|
||||
Canceled(Canceled),
|
||||
UnexpectedId,
|
||||
NoAuthCode,
|
||||
}
|
||||
|
||||
impl Debug for RpcError {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
|
||||
match *self {
|
||||
RpcError::WrongVersion(ref s) => write!(f, "Expected version 2.0, got {}", s),
|
||||
RpcError::ParseError(ref err) => write!(f, "ParseError: {}", err),
|
||||
RpcError::MalformedResponse(ref s) => write!(f, "Malformed response: {}", s),
|
||||
RpcError::JsonRpc(ref json) => write!(f, "JsonRpc error: {:?}", json),
|
||||
RpcError::WsError(ref s) => write!(f, "Websocket error: {}", s),
|
||||
RpcError::Canceled(ref s) => write!(f, "Futures error: {:?}", s),
|
||||
RpcError::UnexpectedId => write!(f, "Unexpected response id"),
|
||||
RpcError::NoAuthCode => write!(f, "No authcodes available"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<JsonError> for RpcError {
|
||||
fn from(err: JsonError) -> RpcError {
|
||||
RpcError::ParseError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WsError> for RpcError {
|
||||
fn from(err: WsError) -> RpcError {
|
||||
RpcError::WsError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Canceled> for RpcError {
|
||||
fn from(err: Canceled) -> RpcError {
|
||||
RpcError::Canceled(err)
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
pub mod client;
|
||||
pub mod signer_client;
|
||||
|
||||
extern crate ethereum_types;
|
||||
extern crate futures;
|
||||
extern crate jsonrpc_core;
|
||||
extern crate jsonrpc_ws_server as ws;
|
||||
extern crate keccak_hash as hash;
|
||||
extern crate parity_rpc as rpc;
|
||||
extern crate parking_lot;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
extern crate url;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate matches;
|
||||
|
||||
/// Boxed future response.
|
||||
pub type BoxFuture<T, E> = Box<dyn futures::Future<Item = T, Error = E> + Send>;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use client::{Rpc, RpcError};
|
||||
use futures::Future;
|
||||
use rpc;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[test]
|
||||
fn test_connection_refused() {
|
||||
let (_srv, port, mut authcodes) = rpc::tests::ws::serve();
|
||||
|
||||
let _ = authcodes.generate_new();
|
||||
authcodes.to_file(&authcodes.path).unwrap();
|
||||
|
||||
let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port - 1), &authcodes.path);
|
||||
|
||||
let _ = connect
|
||||
.map(|conn| {
|
||||
assert!(matches!(&conn, &Err(RpcError::WsError(_))));
|
||||
})
|
||||
.wait();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_authcode_fail() {
|
||||
let (_srv, port, _) = rpc::tests::ws::serve();
|
||||
let path = PathBuf::from("nonexist");
|
||||
|
||||
let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port), &path);
|
||||
|
||||
let _ = connect
|
||||
.map(|conn| {
|
||||
assert!(matches!(&conn, &Err(RpcError::NoAuthCode)));
|
||||
})
|
||||
.wait();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_authcode_correct() {
|
||||
let (_srv, port, mut authcodes) = rpc::tests::ws::serve();
|
||||
|
||||
let _ = authcodes.generate_new();
|
||||
authcodes.to_file(&authcodes.path).unwrap();
|
||||
|
||||
let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port), &authcodes.path);
|
||||
|
||||
let _ = connect.map(|conn| assert!(conn.is_ok())).wait();
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use client::{Rpc, RpcError};
|
||||
use ethereum_types::U256;
|
||||
use futures::Canceled;
|
||||
use rpc::signer::{ConfirmationRequest, TransactionCondition, TransactionModification};
|
||||
use serde;
|
||||
use serde_json::{to_value, Value as JsonValue};
|
||||
use std::path::PathBuf;
|
||||
use BoxFuture;
|
||||
|
||||
pub struct SignerRpc {
|
||||
rpc: Rpc,
|
||||
}
|
||||
|
||||
impl SignerRpc {
|
||||
pub fn new(url: &str, authfile: &PathBuf) -> Result<Self, RpcError> {
|
||||
Ok(SignerRpc {
|
||||
rpc: Rpc::new(&url, authfile)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn requests_to_confirm(
|
||||
&mut self,
|
||||
) -> BoxFuture<Result<Vec<ConfirmationRequest>, RpcError>, Canceled> {
|
||||
self.rpc.request("signer_requestsToConfirm", vec![])
|
||||
}
|
||||
|
||||
pub fn confirm_request(
|
||||
&mut self,
|
||||
id: U256,
|
||||
new_gas: Option<U256>,
|
||||
new_gas_price: Option<U256>,
|
||||
new_condition: Option<Option<TransactionCondition>>,
|
||||
pwd: &str,
|
||||
) -> BoxFuture<Result<U256, RpcError>, Canceled> {
|
||||
self.rpc.request(
|
||||
"signer_confirmRequest",
|
||||
vec![
|
||||
Self::to_value(&format!("{:#x}", id)),
|
||||
Self::to_value(&TransactionModification {
|
||||
sender: None,
|
||||
gas_price: new_gas_price,
|
||||
gas: new_gas,
|
||||
condition: new_condition,
|
||||
}),
|
||||
Self::to_value(&pwd),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn reject_request(&mut self, id: U256) -> BoxFuture<Result<bool, RpcError>, Canceled> {
|
||||
self.rpc.request(
|
||||
"signer_rejectRequest",
|
||||
vec![JsonValue::String(format!("{:#x}", id))],
|
||||
)
|
||||
}
|
||||
|
||||
fn to_value<T: serde::Serialize>(v: &T) -> JsonValue {
|
||||
to_value(v).expect("Our types are always serializable; qed")
|
||||
}
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate ethereum_types;
|
||||
extern crate futures;
|
||||
extern crate rpassword;
|
||||
|
||||
extern crate parity_rpc as rpc;
|
||||
extern crate parity_rpc_client as client;
|
||||
|
||||
use client::signer_client::SignerRpc;
|
||||
use ethereum_types::U256;
|
||||
use rpc::signer::ConfirmationRequest;
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{stdin, stdout, BufRead, BufReader, Write},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use futures::Future;
|
||||
|
||||
fn sign_interactive(signer: &mut SignerRpc, password: &str, request: ConfirmationRequest) {
|
||||
print!(
|
||||
"\n{}\nSign this transaction? (y)es/(N)o/(r)eject: ",
|
||||
request
|
||||
);
|
||||
let _ = stdout().flush();
|
||||
match BufReader::new(stdin()).lines().next() {
|
||||
Some(Ok(line)) => match line.to_lowercase().chars().nth(0) {
|
||||
Some('y') => match sign_transaction(signer, request.id, password) {
|
||||
Ok(s) | Err(s) => println!("{}", s),
|
||||
},
|
||||
Some('r') => match reject_transaction(signer, request.id) {
|
||||
Ok(s) | Err(s) => println!("{}", s),
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
_ => println!("Could not read from stdin"),
|
||||
}
|
||||
}
|
||||
|
||||
fn sign_transactions(signer: &mut SignerRpc, password: String) -> Result<String, String> {
|
||||
signer
|
||||
.requests_to_confirm()
|
||||
.map(|reqs| match reqs {
|
||||
Ok(ref reqs) if reqs.is_empty() => Ok("No transactions in signing queue".to_owned()),
|
||||
Ok(reqs) => {
|
||||
for r in reqs {
|
||||
sign_interactive(signer, &password, r)
|
||||
}
|
||||
Ok("".to_owned())
|
||||
}
|
||||
Err(err) => Err(format!("error: {:?}", err)),
|
||||
})
|
||||
.map_err(|err| format!("{:?}", err))
|
||||
.wait()?
|
||||
}
|
||||
|
||||
fn list_transactions(signer: &mut SignerRpc) -> Result<String, String> {
|
||||
signer
|
||||
.requests_to_confirm()
|
||||
.map(|reqs| match reqs {
|
||||
Ok(ref reqs) if reqs.is_empty() => Ok("No transactions in signing queue".to_owned()),
|
||||
Ok(ref reqs) => Ok(format!(
|
||||
"Transaction queue:\n{}",
|
||||
reqs.iter()
|
||||
.map(|r| format!("{}", r))
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n")
|
||||
)),
|
||||
Err(err) => Err(format!("error: {:?}", err)),
|
||||
})
|
||||
.map_err(|err| format!("{:?}", err))
|
||||
.wait()?
|
||||
}
|
||||
|
||||
fn sign_transaction(signer: &mut SignerRpc, id: U256, password: &str) -> Result<String, String> {
|
||||
signer
|
||||
.confirm_request(id, None, None, None, password)
|
||||
.map(|res| match res {
|
||||
Ok(u) => Ok(format!("Signed transaction id: {:#x}", u)),
|
||||
Err(e) => Err(format!("{:?}", e)),
|
||||
})
|
||||
.map_err(|err| format!("{:?}", err))
|
||||
.wait()?
|
||||
}
|
||||
|
||||
fn reject_transaction(signer: &mut SignerRpc, id: U256) -> Result<String, String> {
|
||||
signer
|
||||
.reject_request(id)
|
||||
.map(|res| match res {
|
||||
Ok(true) => Ok(format!("Rejected transaction id {:#x}", id)),
|
||||
Ok(false) => Err(format!("No such request")),
|
||||
Err(e) => Err(format!("{:?}", e)),
|
||||
})
|
||||
.map_err(|err| format!("{:?}", err))
|
||||
.wait()?
|
||||
}
|
||||
|
||||
// cmds
|
||||
|
||||
pub fn signer_list(signerport: u16, authfile: PathBuf) -> Result<String, String> {
|
||||
let addr = &format!("ws://127.0.0.1:{}", signerport);
|
||||
let mut signer = SignerRpc::new(addr, &authfile).map_err(|err| format!("{:?}", err))?;
|
||||
list_transactions(&mut signer)
|
||||
}
|
||||
|
||||
pub fn signer_reject(
|
||||
id: Option<usize>,
|
||||
signerport: u16,
|
||||
authfile: PathBuf,
|
||||
) -> Result<String, String> {
|
||||
let id = id.ok_or(format!("id required for signer reject"))?;
|
||||
let addr = &format!("ws://127.0.0.1:{}", signerport);
|
||||
let mut signer = SignerRpc::new(addr, &authfile).map_err(|err| format!("{:?}", err))?;
|
||||
reject_transaction(&mut signer, U256::from(id))
|
||||
}
|
||||
|
||||
pub fn signer_sign(
|
||||
id: Option<usize>,
|
||||
pwfile: Option<PathBuf>,
|
||||
signerport: u16,
|
||||
authfile: PathBuf,
|
||||
) -> Result<String, String> {
|
||||
let password;
|
||||
match pwfile {
|
||||
Some(pwfile) => match File::open(pwfile) {
|
||||
Ok(fd) => match BufReader::new(fd).lines().next() {
|
||||
Some(Ok(line)) => password = line,
|
||||
_ => return Err(format!("No password in file")),
|
||||
},
|
||||
Err(e) => return Err(format!("Could not open password file: {}", e)),
|
||||
},
|
||||
None => {
|
||||
password = match rpassword::prompt_password_stdout("Password: ") {
|
||||
Ok(p) => p,
|
||||
Err(e) => return Err(format!("{}", e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let addr = &format!("ws://127.0.0.1:{}", signerport);
|
||||
let mut signer = SignerRpc::new(addr, &authfile).map_err(|err| format!("{:?}", err))?;
|
||||
|
||||
match id {
|
||||
Some(id) => sign_transaction(&mut signer, U256::from(id), &password),
|
||||
None => sign_transactions(&mut signer, password),
|
||||
}
|
||||
}
|
||||
7
devtools/Cargo.toml
Normal file
7
devtools/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
description = "Ethcore development/test/build tools"
|
||||
homepage = "http://parity.io"
|
||||
license = "GPL-3.0"
|
||||
name = "ethcore-devtools"
|
||||
version = "1.12.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
132
devtools/src/http_client.rs
Normal file
132
devtools/src/http_client.rs
Normal file
@@ -0,0 +1,132 @@
|
||||
// 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::thread;
|
||||
use std::time::Duration;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::str::{self, Lines};
|
||||
use std::net::{TcpStream, SocketAddr};
|
||||
|
||||
pub struct Response {
|
||||
pub status: String,
|
||||
pub headers: Vec<String>,
|
||||
pub headers_raw: String,
|
||||
pub body: String,
|
||||
}
|
||||
|
||||
impl Response {
|
||||
pub fn assert_header(&self, header: &str, value: &str) {
|
||||
let header = format!("{}: {}", header, value);
|
||||
assert!(self.headers.iter().find(|h| *h == &header).is_some(), "Couldn't find header {} in {:?}", header, &self.headers)
|
||||
}
|
||||
|
||||
pub fn assert_status(&self, status: &str) {
|
||||
assert_eq!(self.status, status.to_owned(), "Got unexpected code. Body: {:?}", self.body);
|
||||
}
|
||||
|
||||
pub fn assert_security_headers_present(&self, port: Option<u16>) {
|
||||
assert_security_headers_present(&self.headers, port)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_block(lines: &mut Lines, all: bool) -> String {
|
||||
let mut block = String::new();
|
||||
loop {
|
||||
let line = lines.next();
|
||||
match line {
|
||||
None => break,
|
||||
Some("") if !all => break,
|
||||
Some(v) => {
|
||||
block.push_str(v);
|
||||
block.push_str("\n");
|
||||
},
|
||||
}
|
||||
}
|
||||
block
|
||||
}
|
||||
|
||||
fn connect(address: &SocketAddr) -> TcpStream {
|
||||
let mut retries = 0;
|
||||
let mut last_error = None;
|
||||
while retries < 10 {
|
||||
retries += 1;
|
||||
|
||||
let res = TcpStream::connect(address);
|
||||
match res {
|
||||
Ok(stream) => {
|
||||
return stream;
|
||||
},
|
||||
Err(e) => {
|
||||
last_error = Some(e);
|
||||
thread::sleep(Duration::from_millis(retries * 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
panic!("Unable to connect to the server. Last error: {:?}", last_error);
|
||||
}
|
||||
|
||||
pub fn request(address: &SocketAddr, request: &str) -> Response {
|
||||
let mut req = connect(address);
|
||||
req.set_read_timeout(Some(Duration::from_secs(2))).unwrap();
|
||||
req.write_all(request.as_bytes()).unwrap();
|
||||
|
||||
let mut response = Vec::new();
|
||||
loop {
|
||||
let mut chunk = [0; 32 *1024];
|
||||
match req.read(&mut chunk) {
|
||||
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => break,
|
||||
Err(err) => panic!("Unable to read response: {:?}", err),
|
||||
Ok(0) => break,
|
||||
Ok(read) => response.extend_from_slice(&chunk[..read]),
|
||||
}
|
||||
}
|
||||
|
||||
let response = String::from_utf8_lossy(&response).into_owned();
|
||||
let mut lines = response.lines();
|
||||
let status = lines.next().expect("Expected a response").to_owned();
|
||||
let headers_raw = read_block(&mut lines, false);
|
||||
let headers = headers_raw.split('\n').map(|v| v.to_owned()).collect();
|
||||
let body = read_block(&mut lines, true);
|
||||
|
||||
Response {
|
||||
status: status,
|
||||
headers: headers,
|
||||
headers_raw: headers_raw,
|
||||
body: body,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if all required security headers are present
|
||||
pub fn assert_security_headers_present(headers: &[String], port: Option<u16>) {
|
||||
if let None = port {
|
||||
assert!(
|
||||
headers.iter().find(|header| header.as_str() == "X-Frame-Options: SAMEORIGIN").is_some(),
|
||||
"X-Frame-Options: SAMEORIGIN missing: {:?}", headers
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
headers.iter().find(|header| header.as_str() == "X-XSS-Protection: 1; mode=block").is_some(),
|
||||
"X-XSS-Protection missing: {:?}", headers
|
||||
);
|
||||
assert!(
|
||||
headers.iter().find(|header| header.as_str() == "X-Content-Type-Options: nosniff").is_some(),
|
||||
"X-Content-Type-Options missing: {:?}", headers
|
||||
);
|
||||
assert!(
|
||||
headers.iter().find(|header| header.starts_with("Content-Security-Policy: ")).is_some(),
|
||||
"Content-Security-Policy missing: {:?}", headers
|
||||
)
|
||||
}
|
||||
@@ -1,22 +1,19 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Ethereum Transactions
|
||||
//! dev-tools
|
||||
|
||||
mod error;
|
||||
mod transaction;
|
||||
|
||||
pub use self::{error::Error, transaction::*};
|
||||
pub mod http_client;
|
||||
@@ -27,7 +27,7 @@ Parity 1.4.10 is a first stable release of 1.4.x series. It includes a few minor
|
||||
|
||||
- Gas_limit for blocks, mined by Parity will be divisible by 37 (#4154) [#4179](https://github.com/paritytech/parity/pull/4179)
|
||||
- gas_limit for new blocks will divide evenly by 13
|
||||
- increased GAS_LIMIT_DETERMINANT to 37
|
||||
- increased PARITY_GAS_LIMIT_DETERMINANT to 37
|
||||
- separate method for marking mined block
|
||||
- debug_asserts(gas_limit within protocol range)
|
||||
- round_block_gas_limit method is now static
|
||||
|
||||
@@ -421,7 +421,7 @@ Full changes:
|
||||
- Prevent duplicate incoming connections ([#4180](https://github.com/paritytech/parity/pull/4180))
|
||||
- Gas_limit for blocks, mined by Parity will be divisible by 37 ([#4154](https://github.com/paritytech/parity/pull/4154)) [#4176](https://github.com/paritytech/parity/pull/4176)
|
||||
- gas_limit for new blocks will divide evenly by 13
|
||||
- increased GAS_LIMIT_DETERMINANT to 37
|
||||
- increased PARITY_GAS_LIMIT_DETERMINANT to 37
|
||||
- separate method for marking mined block
|
||||
- debug_asserts(gas_limit within protocol range)
|
||||
- round_block_gas_limit method is now static
|
||||
|
||||
@@ -1,26 +1,3 @@
|
||||
Note: Parity Ethereum 2.0 reached End-of-Life on 2018-11-15 (EOL).
|
||||
|
||||
## Parity-Ethereum [v2.0.9](https://github.com/paritytech/parity-ethereum/releases/tag/v2.0.9) (2018-10-29)
|
||||
|
||||
Parity-Ethereum 2.0.9-stable is a bug-fix release to improve performance and stability.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Backports: parity stable 2.0.9 ([#9786](https://github.com/paritytech/parity-ethereum/pull/9786))
|
||||
- Version: bump parity stable to 2.0.9
|
||||
- Ethcore: bump ropsten forkblock checkpoint ([#9775](https://github.com/paritytech/parity-ethereum/pull/9775))
|
||||
- Ethcore: handle vm exception when estimating gas ([#9615](https://github.com/paritytech/parity-ethereum/pull/9615))
|
||||
- Update jsonrpc-core to a1b2bb742ce16d1168669ffb13ffe856e8131228 ([#9780](https://github.com/paritytech/parity-ethereum/pull/9780))
|
||||
- Removed "rustup" & added new runner tag ([#9731](https://github.com/paritytech/parity-ethereum/pull/9731))
|
||||
- Removed "rustup" & added new runner tag
|
||||
- Exchanged tag "rust-windows" with "windows"
|
||||
- Revert windows tag change
|
||||
- Allow zero chain id in EIP155 signing process ([#9792](https://github.com/paritytech/parity-ethereum/pull/9792))
|
||||
- Allow zero chain id in EIP155 signing process
|
||||
- Rename test
|
||||
- Fix test failure
|
||||
- Insert dev account before unlocking ([#9813](https://github.com/paritytech/parity-ethereum/pull/9813))
|
||||
|
||||
## Parity-Ethereum [v2.0.8](https://github.com/paritytech/parity-ethereum/releases/tag/v2.0.8) (2018-10-16)
|
||||
|
||||
Parity-Ethereum 2.0.8-stable is a release that fixes a consensus issue with the recent Constantinople release. Upgrading is mandatory whatever network you are connected to that plans enabling EIP-1283, e.g., Ropsten, Kovan, Ethereum.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,392 +0,0 @@
|
||||
Note: Parity Ethereum 2.2 reached End-of-Life on 2019-02-25 (EOL).
|
||||
|
||||
## Parity-Ethereum [v2.2.11](https://github.com/paritytech/parity-ethereum/releases/tag/v2.2.11) (2019-02-21)
|
||||
|
||||
Parity-Ethereum 2.2.11-stable is a maintenance release that fixes snap and docker installations.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Stable: snap: release untagged versions from branches to the candidate ([#10357](https://github.com/paritytech/parity-ethereum/pull/10357)) ([#10372](https://github.com/paritytech/parity-ethereum/pull/10372))
|
||||
- Snap: release untagged versions from branches to the candidate snap channel ([#10357](https://github.com/paritytech/parity-ethereum/pull/10357))
|
||||
- Snap: add the removable-media plug ([#10377](https://github.com/paritytech/parity-ethereum/pull/10377))
|
||||
- Exchanged old(azure) bootnodes with new(ovh) ones ([#10309](https://github.com/paritytech/parity-ethereum/pull/10309))
|
||||
- Stable Backports ([#10353](https://github.com/paritytech/parity-ethereum/pull/10353))
|
||||
- Version: bump stable to 2.2.11
|
||||
- Snap: prefix version and populate candidate channel ([#10343](https://github.com/paritytech/parity-ethereum/pull/10343))
|
||||
- Snap: populate candidate releases with beta snaps to avoid stale channel
|
||||
- Snap: prefix version with v*
|
||||
- No volumes are needed, just run -v volume:/path/in/the/container ([#10345](https://github.com/paritytech/parity-ethereum/pull/10345))
|
||||
|
||||
## Parity-Ethereum [v2.2.10](https://github.com/paritytech/parity-ethereum/releases/tag/v2.2.10) (2019-02-13)
|
||||
|
||||
Parity-Ethereum 2.2.10-stable is a security-relevant release. A bug in the JSONRPC-deserialization module can cause crashes of all versions of Parity Ethereum nodes if an attacker is able to submit a specially-crafted RPC to certain publicly available endpoints.
|
||||
|
||||
- https://www.parity.io/new-parity-ethereum-update-fixes-several-rpc-vulnerabilities/
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Additional error for invalid gas ([#10327](https://github.com/paritytech/parity-ethereum/pull/10327)) ([#10329](https://github.com/paritytech/parity-ethereum/pull/10329))
|
||||
- Backports for Stable 2.2.10 ([#10332](https://github.com/paritytech/parity-ethereum/pull/10332))
|
||||
- fix(docker-aarch64) : cross-compile config ([#9798](https://github.com/paritytech/parity-ethereum/pull/9798))
|
||||
- import rpc transactions sequentially ([#10051](https://github.com/paritytech/parity-ethereum/pull/10051))
|
||||
- fix(docker): fix not receives SIGINT ([#10059](https://github.com/paritytech/parity-ethereum/pull/10059))
|
||||
- snap: official image / test ([#10168](https://github.com/paritytech/parity-ethereum/pull/10168))
|
||||
- perform stripping during build ([#10208](https://github.com/paritytech/parity-ethereum/pull/10208))
|
||||
- Additional tests for uint/hash/bytes deserialization. ([#10279](https://github.com/paritytech/parity-ethereum/pull/10279))
|
||||
- Don't run the CPP example on CI ([#10285](https://github.com/paritytech/parity-ethereum/pull/10285))
|
||||
- CI optimizations ([#10297](https://github.com/paritytech/parity-ethereum/pull/10297))
|
||||
- fix publish job ([#10317](https://github.com/paritytech/parity-ethereum/pull/10317))
|
||||
- Add Statetest support for Constantinople Fix ([#10323](https://github.com/paritytech/parity-ethereum/pull/10323))
|
||||
- Add helper for Timestamp overflows ([#10330](https://github.com/paritytech/parity-ethereum/pull/10330))
|
||||
- Don't add discovery initiators to the node table ([#10305](https://github.com/paritytech/parity-ethereum/pull/10305))
|
||||
- change docker image based on debian instead of ubuntu due to the chan ([#10336](https://github.com/paritytech/parity-ethereum/pull/10336))
|
||||
- role back docker build image and docker deploy image to ubuntu:xenial based ([#10338](https://github.com/paritytech/parity-ethereum/pull/10338))
|
||||
|
||||
## Parity-Ethereum [v2.2.9](https://github.com/paritytech/parity-ethereum/releases/tag/v2.2.9) (2019-02-03)
|
||||
|
||||
Parity-Ethereum 2.2.9-stable is a security-relevant release. A bug in the JSONRPC-deserialization module can cause crashes of all versions of Parity Ethereum nodes if an attacker is able to submit a specially-crafted RPC to certain publicly available endpoints.
|
||||
|
||||
- https://www.parity.io/security-alert-parity-ethereum-03-02/
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Additional tests for uint deserialization. ([#10279](https://github.com/paritytech/parity-ethereum/pull/10279)) ([#10281](https://github.com/paritytech/parity-ethereum/pull/10281))
|
||||
- Version: bump stable to 2.2.9 ([#10282](https://github.com/paritytech/parity-ethereum/pull/10282))
|
||||
|
||||
## Parity-Ethereum [v2.2.8](https://github.com/paritytech/parity-ethereum/releases/tag/v2.2.8) (2019-02-01)
|
||||
|
||||
Parity-Ethereum 2.2.8-stable is a consensus-relevant release that enables _St. Petersfork_ on:
|
||||
|
||||
- Ethereum Block `7280000` (along with Constantinople)
|
||||
- Kovan Block `10255201`
|
||||
- Ropsten Block `4939394`
|
||||
- POA Sokol Block `7026400`
|
||||
|
||||
In addition to this, Constantinople is cancelled for the POA Core network. Upgrading is mandatory for clients on any of these chains.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Backports for stable 2.2.8 ([#10224](https://github.com/paritytech/parity-ethereum/pull/10224))
|
||||
- Update for Android cross-compilation. ([#10180](https://github.com/paritytech/parity-ethereum/pull/10180))
|
||||
- Cancel Constantinople HF on POA Core ([#10198](https://github.com/paritytech/parity-ethereum/pull/10198))
|
||||
- Add EIP-1283 disable transition ([#10214](https://github.com/paritytech/parity-ethereum/pull/10214))
|
||||
- Enable St-Peters-Fork ("Constantinople Fix") ([#10223](https://github.com/paritytech/parity-ethereum/pull/10223))
|
||||
- Stable: Macos heapsize force jemalloc ([#10234](https://github.com/paritytech/parity-ethereum/pull/10234)) ([#10258](https://github.com/paritytech/parity-ethereum/pull/10258))
|
||||
|
||||
## Parity-Ethereum [v2.2.7](https://github.com/paritytech/parity-ethereum/releases/tag/v2.2.7) (2019-01-15)
|
||||
|
||||
Parity-Ethereum 2.2.7-stable is a consensus-relevant security release that reverts Constantinople on the Ethereum network. Upgrading is mandatory for Ethereum, and strongly recommended for other networks.
|
||||
|
||||
- **Consensus** - Ethereum Network: Pull Constantinople protocol upgrade on Ethereum ([#10189](https://github.com/paritytech/parity-ethereum/pull/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](https://github.com/paritytech/parity-ethereum/pull/10167))
|
||||
- **Wasm** - Kovan Network: Update pwasm-utils to 0.6.1 ([#10134](https://github.com/paritytech/parity-ethereum/pull/10134))
|
||||
|
||||
_Note:_ This release marks Parity 2.2 as _stable_. All versions of Parity 2.1 now reached _end of life_.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Backports for stable 2.2.7 ([#10163](https://github.com/paritytech/parity-ethereum/pull/10163))
|
||||
- Version: bump stable to 2.2.7
|
||||
- Version: mark 2.2 track stable
|
||||
- Version: mark update critical on all networks
|
||||
- Handle the case for contract creation on an empty but exist account with storage items ([#10065](https://github.com/paritytech/parity-ethereum/pull/10065))
|
||||
- Fix _cannot recursively call into `Core`_ issue ([#10144](https://github.com/paritytech/parity-ethereum/pull/10144))
|
||||
- Snap: fix path in script ([#10157](https://github.com/paritytech/parity-ethereum/pull/10157))
|
||||
- Ping nodes from discovery ([#10167](https://github.com/paritytech/parity-ethereum/pull/10167))
|
||||
- Version: bump fork blocks for kovan and foundation, mark releases non critical
|
||||
- Pull constantinople on ethereum network ([#10189](https://github.com/paritytech/parity-ethereum/pull/10189))
|
||||
|
||||
## Parity-Ethereum [v2.2.6](https://github.com/paritytech/parity-ethereum/releases/tag/v2.2.6) (2019-01-10)
|
||||
|
||||
Parity-Ethereum 2.2.6-beta is a bugfix release that improves performance and stability.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Beta backports v2.2.6 ([#10113](https://github.com/paritytech/parity-ethereum/pull/10113))
|
||||
- Version: bump beta to v2.2.6
|
||||
- Fill transaction hash on ethGetLog of light client. ([#9938](https://github.com/paritytech/parity-ethereum/pull/9938))
|
||||
- Fix pubsub new_blocks notifications to include all blocks ([#9987](https://github.com/paritytech/parity-ethereum/pull/9987))
|
||||
- Finality: dont require chain head to be in the chain ([#10054](https://github.com/paritytech/parity-ethereum/pull/10054))
|
||||
- Handle the case for contract creation on an empty but exist account with storage items ([#10065](https://github.com/paritytech/parity-ethereum/pull/10065))
|
||||
- Autogen docs for the "Configuring Parity Ethereum" wiki page. ([#10067](https://github.com/paritytech/parity-ethereum/pull/10067))
|
||||
- HF in POA Sokol (2019-01-04) ([#10077](https://github.com/paritytech/parity-ethereum/pull/10077))
|
||||
- Add --locked when running cargo ([#10107](https://github.com/paritytech/parity-ethereum/pull/10107))
|
||||
- Ethcore: update hardcoded headers ([#10123](https://github.com/paritytech/parity-ethereum/pull/10123))
|
||||
- Identity fix ([#10128](https://github.com/paritytech/parity-ethereum/pull/10128))
|
||||
- Update pwasm-utils to 0.6.1 ([#10134](https://github.com/paritytech/parity-ethereum/pull/10134))
|
||||
- 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))
|
||||
- Version: mark upgrade critical on kovan
|
||||
|
||||
## Parity-Ethereum [v2.2.5](https://github.com/paritytech/parity-ethereum/releases/tag/v2.2.5) (2018-12-14)
|
||||
|
||||
Parity-Ethereum 2.2.5-beta is an important release that introduces Constantinople fork at block 7080000 on Mainnet.
|
||||
This release also contains a fix for chains using AuRa + EmptySteps. Read carefully if this applies to you.
|
||||
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 0** for any chain with `empty_steps`.
|
||||
If your network uses `empty_steps` you **must**:
|
||||
- plan a hard fork and change `strict_empty_steps_transition` to the desire fork block
|
||||
- update the clients of the whole network to 2.2.5-beta / 2.1.10-stable.
|
||||
If for some reason you don't want to do this please set`strict_empty_steps_transition` to `0xfffffffff` to disable it.
|
||||
|
||||
The full list of included changes:
|
||||
- Backports for beta 2.2.5 ([#10047](https://github.com/paritytech/parity-ethereum/pull/10047))
|
||||
- Bump beta to 2.2.5 ([#10047](https://github.com/paritytech/parity-ethereum/pull/10047))
|
||||
- Fix empty steps ([#9939](https://github.com/paritytech/parity-ethereum/pull/9939))
|
||||
- Prevent sending empty step message twice
|
||||
- Prevent sending empty step and then block in the same step
|
||||
- Don't accept double empty steps
|
||||
- Do basic validation of self-sealed blocks
|
||||
- Strict empty steps validation ([#10041](https://github.com/paritytech/parity-ethereum/pull/10041))
|
||||
- Enables strict verification of empty steps - there can be no duplicates and empty steps should be ordered inside the seal.
|
||||
- Note that authorities won't produce invalid seals after [#9939](https://github.com/paritytech/parity-ethereum/pull/9939), this PR just adds verification to the seal to prevent forging incorrect blocks and potentially causing consensus issues.
|
||||
- This features is enabled by default so any AuRa + EmptySteps chain should set strict_empty_steps_transition fork block number in their spec and upgrade to v2.2.5-beta or v2.1.10-stable.
|
||||
- ethcore: enable constantinople on ethereum ([#10031](https://github.com/paritytech/parity-ethereum/pull/10031))
|
||||
- ethcore: change blockreward to 2e18 for foundation after constantinople
|
||||
- ethcore: delay diff bomb by 2e6 blocks for foundation after constantinople
|
||||
- ethcore: enable eip-{145,1014,1052,1283} for foundation after constantinople
|
||||
- Change test miner max memory to malloc reports. ([#10024](https://github.com/paritytech/parity-ethereum/pull/10024))
|
||||
- Fix: test corpus_inaccessible panic ([#10019](https://github.com/paritytech/parity-ethereum/pull/10019))
|
||||
|
||||
## Parity-Ethereum [v2.2.2](https://github.com/paritytech/parity-ethereum/releases/tag/v2.2.2) (2018-11-29)
|
||||
|
||||
Parity-Ethereum 2.2.2-beta is an exciting release. Among others, it improves sync performance, peering stability, block propagation, and transaction propagation times. Also, a warp-sync no longer removes existing blocks from the database, but rather reuses locally available information to decrease sync times and reduces required bandwidth.
|
||||
|
||||
Before upgrading to 2.2.2, please also verify the validity of your chain specs. Parity Ethereum now denies unknown fields in the specification. To do this, use the chainspec tool:
|
||||
|
||||
```
|
||||
cargo build --release -p chainspec
|
||||
./target/release/chainspec /path/to/spec.json
|
||||
```
|
||||
|
||||
Last but not least, JSONRPC APIs which are not yet accepted as an EIP in the `eth`, `personal`, or `web3` namespace, are now considere experimental as their final specification might change in future. These APIs have to be manually enabled by explicitly running `--jsonrpc-experimental`.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Backports For beta 2.2.2 ([#9976](https://github.com/paritytech/parity-ethereum/pull/9976))
|
||||
- Version: bump beta to 2.2.2
|
||||
- Add experimental RPCs flag ([#9928](https://github.com/paritytech/parity-ethereum/pull/9928))
|
||||
- Keep existing blocks when restoring a Snapshot ([#8643](https://github.com/paritytech/parity-ethereum/pull/8643))
|
||||
- Rename db_restore => client
|
||||
- First step: make it compile!
|
||||
- Second step: working implementation!
|
||||
- Refactoring
|
||||
- Fix tests
|
||||
- Migrate ancient blocks interacting backward
|
||||
- Early return in block migration if snapshot is aborted
|
||||
- Remove RwLock getter (PR Grumble I)
|
||||
- Remove dependency on `Client`: only used Traits
|
||||
- Add test for recovering aborted snapshot recovery
|
||||
- Add test for migrating old blocks
|
||||
- Release RwLock earlier
|
||||
- Revert Cargo.lock
|
||||
- Update _update ancient block_ logic: set local in `commit`
|
||||
- Update typo in ethcore/src/snapshot/service.rs
|
||||
- Adjust requests costs for light client ([#9925](https://github.com/paritytech/parity-ethereum/pull/9925))
|
||||
- Pip Table Cost relative to average peers instead of max peers
|
||||
- Add tracing in PIP new_cost_table
|
||||
- Update stat peer_count
|
||||
- Use number of leeching peers for Light serve costs
|
||||
- Fix test::light_params_load_share_depends_on_max_peers (wrong type)
|
||||
- Remove (now) useless test
|
||||
- Remove `load_share` from LightParams.Config
|
||||
- Add LEECHER_COUNT_FACTOR
|
||||
- Pr Grumble: u64 to u32 for f64 casting
|
||||
- Prevent u32 overflow for avg_peer_count
|
||||
- Add tests for LightSync::Statistics
|
||||
- Fix empty steps ([#9939](https://github.com/paritytech/parity-ethereum/pull/9939))
|
||||
- Don't send empty step twice or empty step then block.
|
||||
- Perform basic validation of locally sealed blocks.
|
||||
- Don't include empty step twice.
|
||||
- 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 a deadlock ([#9952](https://github.com/paritytech/parity-ethereum/pull/9952))
|
||||
- Update informant:
|
||||
- Decimal in Mgas/s
|
||||
- Print every 5s (not randomly between 5s and 10s)
|
||||
- Fix dead-lock in `blockchain.rs`
|
||||
- Update locks ordering
|
||||
- Fix light client informant while syncing ([#9932](https://github.com/paritytech/parity-ethereum/pull/9932))
|
||||
- Add `is_idle` to LightSync to check importing status
|
||||
- Use SyncStateWrapper to make sure is_idle gets updates
|
||||
- Update is_major_import to use verified queue size as well
|
||||
- Add comment for `is_idle`
|
||||
- Add Debug to `SyncStateWrapper`
|
||||
- `fn get` -> `fn into_inner`
|
||||
- Ci: rearrange pipeline by logic ([#9970](https://github.com/paritytech/parity-ethereum/pull/9970))
|
||||
- Ci: rearrange pipeline by logic
|
||||
- Ci: rename docs script
|
||||
- Fix docker build ([#9971](https://github.com/paritytech/parity-ethereum/pull/9971))
|
||||
- Deny unknown fields for chainspec ([#9972](https://github.com/paritytech/parity-ethereum/pull/9972))
|
||||
- Add deny_unknown_fields to chainspec
|
||||
- Add tests and fix existing one
|
||||
- Remove serde_ignored dependency for chainspec
|
||||
- Fix rpc test eth chain spec
|
||||
- Fix starting_nonce_test spec
|
||||
- Improve block and transaction propagation ([#9954](https://github.com/paritytech/parity-ethereum/pull/9954))
|
||||
- Refactor sync to add priority tasks.
|
||||
- Send priority tasks notifications.
|
||||
- Propagate blocks, optimize transactions.
|
||||
- Implement transaction propagation. Use sync_channel.
|
||||
- Tone down info.
|
||||
- Prevent deadlock by not waiting forever for sync lock.
|
||||
- Fix lock order.
|
||||
- Don't use sync_channel to prevent deadlocks.
|
||||
- Fix tests.
|
||||
- Fix unstable peers and slowness in sync ([#9967](https://github.com/paritytech/parity-ethereum/pull/9967))
|
||||
- Don't sync all peers after each response
|
||||
- Update formating
|
||||
- Fix tests: add `continue_sync` to `Sync_step`
|
||||
- Update ethcore/sync/src/chain/mod.rs
|
||||
- Fix rpc middlewares
|
||||
- Fix Cargo.lock
|
||||
- Json: resolve merge in spec
|
||||
- Rpc: fix starting_nonce_test
|
||||
- Ci: allow nightl job to fail
|
||||
|
||||
## Parity-Ethereum [v2.2.1](https://github.com/paritytech/parity-ethereum/releases/tag/v2.2.1) (2018-11-15)
|
||||
|
||||
Parity-Ethereum 2.2.1-beta is the first v2.2 release, and might introduce features that break previous work flows, among others:
|
||||
|
||||
- Prevent zero network ID ([#9763](https://github.com/paritytech/parity-ethereum/pull/9763)) and drop support for Olympic testnet ([#9801](https://github.com/paritytech/parity-ethereum/pull/9801)): The Olympic test net is dead for years and never used a chain ID but network ID zero. Parity Ethereum is now preventing the network ID to be zero, thus Olympic support is dropped. Make sure to chose positive non-zero network IDs in future.
|
||||
- Multithreaded snapshot creation ([#9239](https://github.com/paritytech/parity-ethereum/pull/9239)): adds a CLI argument `--snapshot-threads` which specifies the number of threads. This helps improving the performance of full nodes that wish to provide warp-snapshots for the network. The gain in performance comes with a slight drawback in increased snapshot size.
|
||||
- Expose config max-round-blocks-to-import ([#9439](https://github.com/paritytech/parity-ethereum/pull/9439)): Parity Ethereum imports blocks in rounds. If at the end of any round, the queue is not empty, we consider it to be _importing_ and won't notify pubsub. On large re-orgs (10+ blocks), this is possible. The default `max_round_blocks_to_import` is increased to 12 and configurable via the `--max-round-blocks-to-import` CLI flag. With unstable network conditions, it is advised to increase the number. This shouldn't have any noticeable performance impact unless the number is set to really large.
|
||||
- Increase Gas-floor-target and Gas Cap ([#9564](https://github.com/paritytech/parity-ethereum/pull/9564)): the default values for gas floor target are `8_000_000` and gas cap `10_000_000`, similar to Geth 1.8.15+.
|
||||
- Produce portable binaries ([#9725](https://github.com/paritytech/parity-ethereum/pull/9725)): we now produce portable binaries, but it may incur some performance degradation. For ultimate performance it's now better to compile Parity Ethereum from source with `PORTABLE=OFF` environment variable.
|
||||
- RPC: `parity_allTransactionHashes` ([#9745](https://github.com/paritytech/parity-ethereum/pull/9745)): Get all pending transactions from the queue with the high performant `parity_allTransactionHashes` RPC method.
|
||||
- Support `eth_chainId` RPC method ([#9783](https://github.com/paritytech/parity-ethereum/pull/9783)): implements EIP-695 to get the chainID via RPC.
|
||||
- AuRa: finalize blocks ([#9692](https://github.com/paritytech/parity-ethereum/pull/9692)): The AuRa engine was updated to emit ancestry actions to finalize blocks. The full client stores block finality in the database, the engine builds finality from an ancestry of `ExtendedHeader`; `is_epoch_end` was updated to take a vec of recently finalized headers; `is_epoch_end_light` was added which maintains the previous interface and is used by the light client since the client itself doesn't track finality.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Backport to parity 2.2.1 beta ([#9905](https://github.com/paritytech/parity-ethereum/pull/9905))
|
||||
- Bump version to 2.2.1
|
||||
- Fix: Intermittent failing CI due to addr in use ([#9885](https://github.com/paritytech/parity-ethereum/pull/9885))
|
||||
- Fix Parity not closing on Ctrl-C ([#9886](https://github.com/paritytech/parity-ethereum/pull/9886))
|
||||
- Fix json tracer overflow ([#9873](https://github.com/paritytech/parity-ethereum/pull/9873))
|
||||
- Fix docker script ([#9854](https://github.com/paritytech/parity-ethereum/pull/9854))
|
||||
- Add hardcoded headers for light client ([#9907](https://github.com/paritytech/parity-ethereum/pull/9907))
|
||||
- Gitlab-ci: make android release build succeed ([#9743](https://github.com/paritytech/parity-ethereum/pull/9743))
|
||||
- Allow to seal work on latest block ([#9876](https://github.com/paritytech/parity-ethereum/pull/9876))
|
||||
- Remove rust-toolchain file ([#9906](https://github.com/paritytech/parity-ethereum/pull/9906))
|
||||
- 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-712 implementation ([#9631](https://github.com/paritytech/parity-ethereum/pull/9631))
|
||||
- Eip-191 implementation ([#9701](https://github.com/paritytech/parity-ethereum/pull/9701))
|
||||
- Simplify cargo audit ([#9918](https://github.com/paritytech/parity-ethereum/pull/9918))
|
||||
- Fix performance issue importing Kovan blocks ([#9914](https://github.com/paritytech/parity-ethereum/pull/9914))
|
||||
- Ci: nuke the gitlab caches ([#9855](https://github.com/paritytech/parity-ethereum/pull/9855))
|
||||
- Backports to parity beta 2.2.0 ([#9820](https://github.com/paritytech/parity-ethereum/pull/9820))
|
||||
- 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 ([#9814](https://github.com/paritytech/parity-ethereum/pull/9814))
|
||||
- Move state root verification before gas used ([#9841](https://github.com/paritytech/parity-ethereum/pull/9841))
|
||||
- Classic.json Bootnode Update ([#9828](https://github.com/paritytech/parity-ethereum/pull/9828))
|
||||
- Rpc: parity_allTransactionHashes ([#9745](https://github.com/paritytech/parity-ethereum/pull/9745))
|
||||
- Revert "prevent zero networkID ([#9763](https://github.com/paritytech/parity-ethereum/pull/9763))" ([#9815](https://github.com/paritytech/parity-ethereum/pull/9815))
|
||||
- Allow zero chain id in EIP155 signing process ([#9792](https://github.com/paritytech/parity-ethereum/pull/9792))
|
||||
- Add readiness check for docker container ([#9804](https://github.com/paritytech/parity-ethereum/pull/9804))
|
||||
- Insert dev account before unlocking ([#9813](https://github.com/paritytech/parity-ethereum/pull/9813))
|
||||
- Removed "rustup" & added new runner tag ([#9731](https://github.com/paritytech/parity-ethereum/pull/9731))
|
||||
- Expose config max-round-blocks-to-import ([#9439](https://github.com/paritytech/parity-ethereum/pull/9439))
|
||||
- Aura: finalize blocks ([#9692](https://github.com/paritytech/parity-ethereum/pull/9692))
|
||||
- Sync: retry different peer after empty subchain heads response ([#9753](https://github.com/paritytech/parity-ethereum/pull/9753))
|
||||
- Fix(light-rpc/parity) : Remove unused client ([#9802](https://github.com/paritytech/parity-ethereum/pull/9802))
|
||||
- Drops support for olympic testnet, closes [#9800](https://github.com/paritytech/parity-ethereum/issues/9800) ([#9801](https://github.com/paritytech/parity-ethereum/pull/9801))
|
||||
- Replace `tokio_core` with `tokio` (`ring` -> 0.13) ([#9657](https://github.com/paritytech/parity-ethereum/pull/9657))
|
||||
- Support eth_chainId RPC method ([#9783](https://github.com/paritytech/parity-ethereum/pull/9783))
|
||||
- Ethcore: bump ropsten forkblock checkpoint ([#9775](https://github.com/paritytech/parity-ethereum/pull/9775))
|
||||
- Docs: changelogs for 2.0.8 and 2.1.3 ([#9758](https://github.com/paritytech/parity-ethereum/pull/9758))
|
||||
- Prevent zero networkID ([#9763](https://github.com/paritytech/parity-ethereum/pull/9763))
|
||||
- Skip seal fields count check when --no-seal-check is used ([#9757](https://github.com/paritytech/parity-ethereum/pull/9757))
|
||||
- Aura: fix panic on extra_info with unsealed block ([#9755](https://github.com/paritytech/parity-ethereum/pull/9755))
|
||||
- Docs: update changelogs ([#9742](https://github.com/paritytech/parity-ethereum/pull/9742))
|
||||
- Removed extra assert in generation_session_is_removed_when_succeeded ([#9738](https://github.com/paritytech/parity-ethereum/pull/9738))
|
||||
- Make checkpoint_storage_at use plain loop instead of recursion ([#9734](https://github.com/paritytech/parity-ethereum/pull/9734))
|
||||
- Use signed 256-bit integer for sstore gas refund substate ([#9746](https://github.com/paritytech/parity-ethereum/pull/9746))
|
||||
- Heads ref not present for branches beta and stable ([#9741](https://github.com/paritytech/parity-ethereum/pull/9741))
|
||||
- Add Callisto support ([#9534](https://github.com/paritytech/parity-ethereum/pull/9534))
|
||||
- Add --force to cargo audit install script ([#9735](https://github.com/paritytech/parity-ethereum/pull/9735))
|
||||
- Remove unused expired value from Handshake ([#9732](https://github.com/paritytech/parity-ethereum/pull/9732))
|
||||
- Add hardcoded headers ([#9730](https://github.com/paritytech/parity-ethereum/pull/9730))
|
||||
- Produce portable binaries ([#9725](https://github.com/paritytech/parity-ethereum/pull/9725))
|
||||
- Gitlab ci: releasable_branches: change variables condition to schedule ([#9729](https://github.com/paritytech/parity-ethereum/pull/9729))
|
||||
- Update a few parity-common dependencies ([#9663](https://github.com/paritytech/parity-ethereum/pull/9663))
|
||||
- Hf in POA Core (2018-10-22) ([#9724](https://github.com/paritytech/parity-ethereum/pull/9724))
|
||||
- Schedule nightly builds ([#9717](https://github.com/paritytech/parity-ethereum/pull/9717))
|
||||
- Fix ancient blocks sync ([#9531](https://github.com/paritytech/parity-ethereum/pull/9531))
|
||||
- Ci: Skip docs job for nightly ([#9693](https://github.com/paritytech/parity-ethereum/pull/9693))
|
||||
- Fix (light/provider) : Make `read_only executions` read-only ([#9591](https://github.com/paritytech/parity-ethereum/pull/9591))
|
||||
- Ethcore: fix detection of major import ([#9552](https://github.com/paritytech/parity-ethereum/pull/9552))
|
||||
- Return 0 on error ([#9705](https://github.com/paritytech/parity-ethereum/pull/9705))
|
||||
- Ethcore: delay ropsten hardfork ([#9704](https://github.com/paritytech/parity-ethereum/pull/9704))
|
||||
- Make instantSeal engine backwards compatible, closes [#9696](https://github.com/paritytech/parity-ethereum/issues/9696) ([#9700](https://github.com/paritytech/parity-ethereum/pull/9700))
|
||||
- Implement CREATE2 gas changes and fix some potential overflowing ([#9694](https://github.com/paritytech/parity-ethereum/pull/9694))
|
||||
- Don't hash the init_code of CREATE. ([#9688](https://github.com/paritytech/parity-ethereum/pull/9688))
|
||||
- Ethcore: minor optimization of modexp by using LR exponentiation ([#9697](https://github.com/paritytech/parity-ethereum/pull/9697))
|
||||
- Removed redundant clone before each block import ([#9683](https://github.com/paritytech/parity-ethereum/pull/9683))
|
||||
- Add Foundation Bootnodes ([#9666](https://github.com/paritytech/parity-ethereum/pull/9666))
|
||||
- Docker: run as parity user ([#9689](https://github.com/paritytech/parity-ethereum/pull/9689))
|
||||
- Ethcore: mcip3 block reward contract ([#9605](https://github.com/paritytech/parity-ethereum/pull/9605))
|
||||
- Verify block syncing responses against requests ([#9670](https://github.com/paritytech/parity-ethereum/pull/9670))
|
||||
- Add a new RPC `parity_submitWorkDetail` similar `eth_submitWork` but return block hash ([#9404](https://github.com/paritytech/parity-ethereum/pull/9404))
|
||||
- Resumable EVM and heap-allocated callstack ([#9360](https://github.com/paritytech/parity-ethereum/pull/9360))
|
||||
- Update parity-wordlist library ([#9682](https://github.com/paritytech/parity-ethereum/pull/9682))
|
||||
- Ci: Remove unnecessary pipes ([#9681](https://github.com/paritytech/parity-ethereum/pull/9681))
|
||||
- Test.sh: use cargo --target for platforms other than linux, win or mac ([#9650](https://github.com/paritytech/parity-ethereum/pull/9650))
|
||||
- Ci: fix push script ([#9679](https://github.com/paritytech/parity-ethereum/pull/9679))
|
||||
- Hardfork the testnets ([#9562](https://github.com/paritytech/parity-ethereum/pull/9562))
|
||||
- Calculate sha3 instead of sha256 for push-release. ([#9673](https://github.com/paritytech/parity-ethereum/pull/9673))
|
||||
- Ethcore-io retries failed work steal ([#9651](https://github.com/paritytech/parity-ethereum/pull/9651))
|
||||
- Fix(light_fetch): avoid race with BlockNumber::Latest ([#9665](https://github.com/paritytech/parity-ethereum/pull/9665))
|
||||
- Test fix for windows cache name... ([#9658](https://github.com/paritytech/parity-ethereum/pull/9658))
|
||||
- Refactor(fetch) : light use only one `DNS` thread ([#9647](https://github.com/paritytech/parity-ethereum/pull/9647))
|
||||
- Ethereum libfuzzer integration small change ([#9547](https://github.com/paritytech/parity-ethereum/pull/9547))
|
||||
- Cli: remove reference to --no-ui in --unlock flag help ([#9616](https://github.com/paritytech/parity-ethereum/pull/9616))
|
||||
- Remove master from releasable branches ([#9655](https://github.com/paritytech/parity-ethereum/pull/9655))
|
||||
- Ethcore/VerificationQueue don't spawn up extra `worker-threads` when explictly specified not to ([#9620](https://github.com/paritytech/parity-ethereum/pull/9620))
|
||||
- Rpc: parity_getBlockReceipts ([#9527](https://github.com/paritytech/parity-ethereum/pull/9527))
|
||||
- Remove unused dependencies ([#9589](https://github.com/paritytech/parity-ethereum/pull/9589))
|
||||
- Ignore key_server_cluster randomly failing tests ([#9639](https://github.com/paritytech/parity-ethereum/pull/9639))
|
||||
- Ethcore: handle vm exception when estimating gas ([#9615](https://github.com/paritytech/parity-ethereum/pull/9615))
|
||||
- Fix bad-block reporting no reason ([#9638](https://github.com/paritytech/parity-ethereum/pull/9638))
|
||||
- Use static call and apparent value transfer for block reward contract code ([#9603](https://github.com/paritytech/parity-ethereum/pull/9603))
|
||||
- Hf in POA Sokol (2018-09-19) ([#9607](https://github.com/paritytech/parity-ethereum/pull/9607))
|
||||
- Bump smallvec to 0.6 in ethcore-light, ethstore and whisper ([#9588](https://github.com/paritytech/parity-ethereum/pull/9588))
|
||||
- Add constantinople conf to EvmTestClient. ([#9570](https://github.com/paritytech/parity-ethereum/pull/9570))
|
||||
- Fix(network): don't disconnect reserved peers ([#9608](https://github.com/paritytech/parity-ethereum/pull/9608))
|
||||
- Fix failing node-table tests on mac os, closes [#9632](https://github.com/paritytech/parity-ethereum/issues/9632) ([#9633](https://github.com/paritytech/parity-ethereum/pull/9633))
|
||||
- Update ropsten.json ([#9602](https://github.com/paritytech/parity-ethereum/pull/9602))
|
||||
- Simplify ethcore errors by removing BlockImportError ([#9593](https://github.com/paritytech/parity-ethereum/pull/9593))
|
||||
- Fix windows compilation, replaces [#9561](https://github.com/paritytech/parity-ethereum/issues/9561) ([#9621](https://github.com/paritytech/parity-ethereum/pull/9621))
|
||||
- Master: rpc-docs set github token ([#9610](https://github.com/paritytech/parity-ethereum/pull/9610))
|
||||
- Docs: add changelogs for 1.11.10, 1.11.11, 2.0.3, 2.0.4, 2.0.5, 2.0.6, 2.1.0, and 2.1.1 ([#9554](https://github.com/paritytech/parity-ethereum/pull/9554))
|
||||
- Docs(rpc): annotate tag with the provided message ([#9601](https://github.com/paritytech/parity-ethereum/pull/9601))
|
||||
- Ci: fix regex roll_eyes ([#9597](https://github.com/paritytech/parity-ethereum/pull/9597))
|
||||
- Remove snapcraft clean ([#9585](https://github.com/paritytech/parity-ethereum/pull/9585))
|
||||
- Add snapcraft package image (master) ([#9584](https://github.com/paritytech/parity-ethereum/pull/9584))
|
||||
- Docs(rpc): push the branch along with tags ([#9578](https://github.com/paritytech/parity-ethereum/pull/9578))
|
||||
- Fix typo for jsonrpc-threads flag ([#9574](https://github.com/paritytech/parity-ethereum/pull/9574))
|
||||
- Fix informant compile ([#9571](https://github.com/paritytech/parity-ethereum/pull/9571))
|
||||
- Added ropsten bootnodes ([#9569](https://github.com/paritytech/parity-ethereum/pull/9569))
|
||||
- Increase Gas-floor-target and Gas Cap ([#9564](https://github.com/paritytech/parity-ethereum/pull/9564))
|
||||
- While working on the platform tests make them non-breaking ([#9563](https://github.com/paritytech/parity-ethereum/pull/9563))
|
||||
- Improve P2P discovery ([#9526](https://github.com/paritytech/parity-ethereum/pull/9526))
|
||||
- Move dockerfile for android build container to scripts repo ([#9560](https://github.com/paritytech/parity-ethereum/pull/9560))
|
||||
- Simultaneous platform tests WIP ([#9557](https://github.com/paritytech/parity-ethereum/pull/9557))
|
||||
- Update ethabi-derive, serde, serde_json, serde_derive, syn && quote ([#9553](https://github.com/paritytech/parity-ethereum/pull/9553))
|
||||
- Ci: fix rpc docs generation 2 ([#9550](https://github.com/paritytech/parity-ethereum/pull/9550))
|
||||
- Ci: always run build pipelines for win, mac, linux, and android ([#9537](https://github.com/paritytech/parity-ethereum/pull/9537))
|
||||
- Multithreaded snapshot creation ([#9239](https://github.com/paritytech/parity-ethereum/pull/9239))
|
||||
- New ethabi ([#9511](https://github.com/paritytech/parity-ethereum/pull/9511))
|
||||
- Remove initial token for WS. ([#9545](https://github.com/paritytech/parity-ethereum/pull/9545))
|
||||
- Net_version caches network_id to avoid redundant aquire of sync readlock ([#9544](https://github.com/paritytech/parity-ethereum/pull/9544))
|
||||
- Correct before_script for nightly build versions ([#9543](https://github.com/paritytech/parity-ethereum/pull/9543))
|
||||
- Deps: bump kvdb-rocksdb to 0.1.4 ([#9539](https://github.com/paritytech/parity-ethereum/pull/9539))
|
||||
- State: test when contract creation fails, old storage values should re-appear ([#9532](https://github.com/paritytech/parity-ethereum/pull/9532))
|
||||
- Allow dropping light client RPC query with no results ([#9318](https://github.com/paritytech/parity-ethereum/pull/9318))
|
||||
- Bump master to 2.2.0 ([#9517](https://github.com/paritytech/parity-ethereum/pull/9517))
|
||||
- Enable all Constantinople hard fork changes in constantinople_test.json ([#9505](https://github.com/paritytech/parity-ethereum/pull/9505))
|
||||
- [Light] Validate `account balance` before importing transactions ([#9417](https://github.com/paritytech/parity-ethereum/pull/9417))
|
||||
- In create memory calculation is the same for create2 because the additional parameter was popped before. ([#9522](https://github.com/paritytech/parity-ethereum/pull/9522))
|
||||
- Update patricia trie to 0.2.2 ([#9525](https://github.com/paritytech/parity-ethereum/pull/9525))
|
||||
- Replace hardcoded JSON with serde json! macro ([#9489](https://github.com/paritytech/parity-ethereum/pull/9489))
|
||||
- Fix typo in version string ([#9516](https://github.com/paritytech/parity-ethereum/pull/9516))
|
||||
@@ -1,288 +0,0 @@
|
||||
## Parity-Ethereum [v2.3.8](https://github.com/paritytech/parity-ethereum/releases/tag/v2.3.8) (2019-03-22)
|
||||
|
||||
Parity-Ethereum 2.3.8-stable is a bugfix release that improves performance and stability. This patch release contains a critical bug fix where serving light clients previously led to client crashes. Upgrading is highly recommended.
|
||||
|
||||
The full list of included changes:
|
||||
- 2.3.8 stable backports ([#10507](https://github.com/paritytech/parity-ethereum/pull/10507))
|
||||
- Version: bump stable
|
||||
- Add additional request tests ([#10503](https://github.com/paritytech/parity-ethereum/pull/10503))
|
||||
|
||||
## Parity-Ethereum [v2.3.7](https://github.com/paritytech/parity-ethereum/releases/tag/v2.3.7) (2019-03-20)
|
||||
|
||||
Parity-Ethereum 2.3.7-stable is a bugfix release that improves performance and stability.
|
||||
|
||||
The full list of included changes:
|
||||
- 2.3.7 stable backports ([#10487](https://github.com/paritytech/parity-ethereum/pull/10487))
|
||||
- Version: bump stable
|
||||
- Сaching through docker volume ([#10477](https://github.com/paritytech/parity-ethereum/pull/10477))
|
||||
- fix win&mac build ([#10486](https://github.com/paritytech/parity-ethereum/pull/10486))
|
||||
- fix(extract `timestamp_checked_add` as lib) ([#10383](https://github.com/paritytech/parity-ethereum/pull/10383))
|
||||
|
||||
## Parity-Ethereum [v2.3.6](https://github.com/paritytech/parity-ethereum/releases/tag/v2.3.6) (2019-03-19)
|
||||
|
||||
Parity-Ethereum 2.3.6-stable is a bugfix release that improves performance and stability.
|
||||
|
||||
The full list of included changes:
|
||||
- 2.3.6 stable backports ([#10470](https://github.com/paritytech/parity-ethereum/pull/10470))
|
||||
- Version: bump stable
|
||||
- CI publish to aws ([#10446](https://github.com/paritytech/parity-ethereum/pull/10446))
|
||||
- Ensure static validator set changes are recognized ([#10467](https://github.com/paritytech/parity-ethereum/pull/10467))
|
||||
- CI aws git checkout ([#10451](https://github.com/paritytech/parity-ethereum/pull/10451))
|
||||
- Revert "CI aws git checkout ([#10451](https://github.com/paritytech/parity-ethereum/pull/10451))" ([#10456](https://github.com/paritytech/parity-ethereum/pull/10456))
|
||||
- Tests parallelized ([#10452](https://github.com/paritytech/parity-ethereum/pull/10452))
|
||||
|
||||
## Parity-Ethereum [v2.3.5](https://github.com/paritytech/parity-ethereum/releases/tag/v2.3.5) (2019-02-25)
|
||||
|
||||
Parity-Ethereum 2.3.5-stable is a bugfix release that improves performance and stability.
|
||||
|
||||
Note, all 2.2 releases and older are now unsupported and upgrading is recommended.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- More Backports for Stable 2.3.5 ([#10430](https://github.com/paritytech/parity-ethereum/pull/10430))
|
||||
- Revert some changes, could be buggy ([#10399](https://github.com/paritytech/parity-ethereum/pull/10399))
|
||||
- Ci: clean up gitlab-ci.yml leftovers from previous merge ([#10429](https://github.com/paritytech/parity-ethereum/pull/10429))
|
||||
- 10000 > 5000 ([#10422](https://github.com/paritytech/parity-ethereum/pull/10422))
|
||||
- Fix underflow in pip, closes [#10419](https://github.com/paritytech/parity-ethereum/pull/10419) ([#10423](https://github.com/paritytech/parity-ethereum/pull/10423))
|
||||
- Fix panic when logging directory does not exist, closes [#10420](https://github.com/paritytech/parity-ethereum/pull/10420) ([#10424](https://github.com/paritytech/parity-ethereum/pull/10424))
|
||||
- Update hardcoded headers for Foundation, Ropsten, Kovan and Classic ([#10417](https://github.com/paritytech/parity-ethereum/pull/10417))
|
||||
- Backports for Stable 2.3.5 ([#10414](https://github.com/paritytech/parity-ethereum/pull/10414))
|
||||
- No-git for publish jobs, empty artifacts dir ([#10393](https://github.com/paritytech/parity-ethereum/pull/10393))
|
||||
- Snap: reenable i386, arm64, armhf architecture publishing ([#10386](https://github.com/paritytech/parity-ethereum/pull/10386))
|
||||
- Tx pool: always accept local transactions ([#10375](https://github.com/paritytech/parity-ethereum/pull/10375))
|
||||
- Fix to_pod storage trie value decoding ([#10368)](https://github.com/paritytech/parity-ethereum/pull/10368))
|
||||
- Version: mark 2.3.5 as stable
|
||||
|
||||
## Parity-Ethereum [v2.3.4](https://github.com/paritytech/parity-ethereum/releases/tag/v2.3.4) (2019-02-21)
|
||||
|
||||
Parity-Ethereum 2.3.4-beta is a maintenance release that fixes snap and docker installations.
|
||||
|
||||
The full list of included changes:
|
||||
- Beta: snap: release untagged versions from branches to the candidate ([#10357](https://github.com/paritytech/parity-ethereum/pull/10357)) ([#10373](https://github.com/paritytech/parity-ethereum/pull/10373))
|
||||
- Snap: release untagged versions from branches to the candidate snap channel ([#10357](https://github.com/paritytech/parity-ethereum/pull/10357))
|
||||
- Snap: add the removable-media plug ([#10377](https://github.com/paritytech/parity-ethereum/pull/10377))
|
||||
- Exchanged old(azure) bootnodes with new(ovh) ones ([#10309](https://github.com/paritytech/parity-ethereum/pull/10309))
|
||||
- Beta Backports ([#10354](https://github.com/paritytech/parity-ethereum/pull/10354))
|
||||
- Version: bump beta to 2.3.4
|
||||
- Snap: prefix version and populate candidate channel ([#10343](https://github.com/paritytech/parity-ethereum/pull/10343))
|
||||
- Snap: populate candidate releases with beta snaps to avoid stale channel
|
||||
- Snap: prefix version with v*
|
||||
- No volumes are needed, just run -v volume:/path/in/the/container ([#10345](https://github.com/paritytech/parity-ethereum/pull/10345))
|
||||
|
||||
## Parity-Ethereum [v2.3.3](https://github.com/paritytech/parity-ethereum/releases/tag/v2.3.3) (2019-02-13)
|
||||
|
||||
Parity-Ethereum 2.3.3-beta is a security-relevant release. A bug in the JSONRPC-deserialization module can cause crashes of all versions of Parity Ethereum nodes if an attacker is able to submit a specially-crafted RPC to certain publicly available endpoints.
|
||||
|
||||
- https://www.parity.io/new-parity-ethereum-update-fixes-several-rpc-vulnerabilities/
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Additional error for invalid gas ([#10327](https://github.com/paritytech/parity-ethereum/pull/10327)) ([#10328](https://github.com/paritytech/parity-ethereum/pull/10328))
|
||||
- Backports for Beta 2.3.3 ([#10333](https://github.com/paritytech/parity-ethereum/pull/10333))
|
||||
- Properly handle check_epoch_end_signal errors ([#10015](https://github.com/paritytech/parity-ethereum/pull/10015))
|
||||
- import rpc transactions sequentially ([#10051](https://github.com/paritytech/parity-ethereum/pull/10051))
|
||||
- fix(docker): fix not receives SIGINT ([#10059](https://github.com/paritytech/parity-ethereum/pull/10059))
|
||||
- snap: official image / test ([#10168](https://github.com/paritytech/parity-ethereum/pull/10168))
|
||||
- Extract CallContract and RegistryInfo traits into their own crate ([#10178](https://github.com/paritytech/parity-ethereum/pull/10178))
|
||||
- perform stripping during build ([#10208](https://github.com/paritytech/parity-ethereum/pull/10208))
|
||||
- Remove CallContract and RegistryInfo re-exports from `ethcore/client` ([#10205](https://github.com/paritytech/parity-ethereum/pull/10205))
|
||||
- fixed: types::transaction::SignedTransaction; ([#10229](https://github.com/paritytech/parity-ethereum/pull/10229))
|
||||
- Additional tests for uint/hash/bytes deserialization. ([#10279](https://github.com/paritytech/parity-ethereum/pull/10279))
|
||||
- Fix Windows build ([#10284](https://github.com/paritytech/parity-ethereum/pull/10284))
|
||||
- Don't run the CPP example on CI ([#10285](https://github.com/paritytech/parity-ethereum/pull/10285))
|
||||
- CI optimizations ([#10297](https://github.com/paritytech/parity-ethereum/pull/10297))
|
||||
- fix publish job ([#10317](https://github.com/paritytech/parity-ethereum/pull/10317))
|
||||
- Add Statetest support for Constantinople Fix ([#10323](https://github.com/paritytech/parity-ethereum/pull/10323))
|
||||
- Add helper for Timestamp overflows ([#10330](https://github.com/paritytech/parity-ethereum/pull/10330))
|
||||
- Don't add discovery initiators to the node table ([#10305](https://github.com/paritytech/parity-ethereum/pull/10305))
|
||||
- change docker image based on debian instead of ubuntu due to the chan ([#10336](https://github.com/paritytech/parity-ethereum/pull/10336))
|
||||
- role back docker build image and docker deploy image to ubuntu:xenial based ([#10338](https://github.com/paritytech/parity-ethereum/pull/10338))
|
||||
|
||||
## Parity-Ethereum [v2.3.2](https://github.com/paritytech/parity-ethereum/releases/tag/v2.3.2) (2019-02-03)
|
||||
|
||||
Parity-Ethereum 2.3.2-stable is a security-relevant release. A bug in the JSONRPC-deserialization module can cause crashes of all versions of Parity Ethereum nodes if an attacker is able to submit a specially-crafted RPC to certain publicly available endpoints.
|
||||
|
||||
- https://www.parity.io/security-alert-parity-ethereum-03-02/
|
||||
|
||||
The full list of included changes:
|
||||
- Version: bump beta to 2.3.2 ([#10283](https://github.com/paritytech/parity-ethereum/pull/10283))
|
||||
- Additional tests for uint deserialization. ([#10279](https://github.com/paritytech/parity-ethereum/pull/10279)) ([#10280](https://github.com/paritytech/parity-ethereum/pull/10280))
|
||||
- Backport [#10285](https://github.com/paritytech/parity-ethereum/pull/10285) to beta ([#10286](https://github.com/paritytech/parity-ethereum/pull/10286))
|
||||
|
||||
## Parity-Ethereum [v2.3.1](https://github.com/paritytech/parity-ethereum/releases/tag/v2.3.1) (2019-02-01)
|
||||
|
||||
Parity-Ethereum 2.3.1-beta is a consensus-relevant release that enables _St. Petersfork_ on:
|
||||
|
||||
- Ethereum Block `7280000` (along with Constantinople)
|
||||
- Kovan Block `10255201`
|
||||
- Ropsten Block `4939394`
|
||||
- POA Sokol Block `7026400`
|
||||
|
||||
In addition to this, Constantinople is cancelled for the POA Core network. Upgrading is mandatory for clients on any of these chains.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Backports for beta 2.3.1 ([#10225](https://github.com/paritytech/parity-ethereum/pull/10225))
|
||||
- Fix _cannot recursively call into `Core`_ issue ([#10144](https://github.com/paritytech/parity-ethereum/pull/10144))
|
||||
- Update for Android cross-compilation. ([#10180](https://github.com/paritytech/parity-ethereum/pull/10180))
|
||||
- Fix _cannot recursively call into `Core`_ - Part 2 ([#10195](https://github.com/paritytech/parity-ethereum/pull/10195))
|
||||
- Cancel Constantinople HF on POA Core ([#10198](https://github.com/paritytech/parity-ethereum/pull/10198))
|
||||
- Add EIP-1283 disable transition ([#10214](https://github.com/paritytech/parity-ethereum/pull/10214))
|
||||
- Enable St-Peters-Fork ("Constantinople Fix") ([#10223](https://github.com/paritytech/parity-ethereum/pull/10223))
|
||||
- Beta: Macos heapsize force jemalloc ([#10234](https://github.com/paritytech/parity-ethereum/pull/10234)) ([#10259](https://github.com/paritytech/parity-ethereum/pull/10259))
|
||||
|
||||
## Parity-Ethereum [v2.3.0](https://github.com/paritytech/parity-ethereum/releases/tag/v2.3.0) (2019-01-16)
|
||||
|
||||
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.
|
||||
|
||||
- **Consensus** - Ethereum Network: Pull Constantinople protocol upgrade on Ethereum ([#10189](https://github.com/paritytech/parity-ethereum/pull/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](https://github.com/paritytech/parity-ethereum/pull/10167))
|
||||
- **Wasm** - Kovan Network: Update pwasm-utils to 0.6.1 ([#10134](https://github.com/paritytech/parity-ethereum/pull/10134))
|
||||
|
||||
Other notable changes:
|
||||
|
||||
- Existing blocks in the database are now kept when restoring a Snapshot. ([#8643](https://github.com/paritytech/parity-ethereum/pull/8643))
|
||||
- Block and transaction propagation is improved significantly. ([#9954](https://github.com/paritytech/parity-ethereum/pull/9954))
|
||||
- The ERC-191 Signed Data Standard is now supported by `personal_sign191`. ([#9701](https://github.com/paritytech/parity-ethereum/pull/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](https://github.com/paritytech/parity-ethereum/pull/9631))
|
||||
- Add support for ERC-1186 `eth_getProof` ([#9001](https://github.com/paritytech/parity-ethereum/pull/9001))
|
||||
- Add experimental RPCs flag to enable ERC-191, ERC-712, and ERC-1186 APIs via `--jsonrpc-experimental` ([#9928](https://github.com/paritytech/parity-ethereum/pull/9928))
|
||||
- Make `CALLCODE` to trace value to be the code address. ([#9881](https://github.com/paritytech/parity-ethereum/pull/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](https://github.com/paritytech/parity-ethereum/pull/9955))
|
||||
- Also, unknown fields in chain specs are now rejected. ([#9972](https://github.com/paritytech/parity-ethereum/pull/9972))
|
||||
- The Tendermint engine was removed from Parity Ethereum and is no longer available and maintained. ([#9980](https://github.com/paritytech/parity-ethereum/pull/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](https://github.com/paritytech/parity-ethereum/pull/10123))
|
||||
- Strict empty steps validation ([#10041](https://github.com/paritytech/parity-ethereum/pull/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_.
|
||||
|
||||
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))
|
||||
@@ -1,128 +0,0 @@
|
||||
## Parity-Ethereum [v2.4.9](https://github.com/paritytech/parity-ethereum/releases/tag/v2.4.9)
|
||||
|
||||
Parity Ethereum v2.4.9-stable is a security update which addresses servo/rust-smallvec#148
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* cargo update -p smallvec ([#10822](https://github.com/paritytech/parity-ethereum/pull/10822))
|
||||
|
||||
## Parity-Ethereum [v2.4.8](https://github.com/paritytech/parity-ethereum/releases/tag/v2.4.8)
|
||||
|
||||
Parity-Ethereum 2.4.8-stable is a bugfix release that improves performance and stability.
|
||||
|
||||
* Blockchain: fix reset chain
|
||||
* State tests: treat empty accounts the same as non-existant accounts (EIP 1052)
|
||||
* Aura: fix Timestamp Overflow
|
||||
* Networking: support discovery-only peers (geth bootnodes)
|
||||
* Snapshotting: fix unclean shutdown while snappshotting is under way
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* ethcore/res: activate atlantis classic hf on block 8772000 ([#10766](https://github.com/paritytech/parity-ethereum/pull/10766))
|
||||
* fix docker tags for publishing ([#10741](https://github.com/paritytech/parity-ethereum/pull/10741))
|
||||
* Reset blockchain properly ([#10669](https://github.com/paritytech/parity-ethereum/pull/10669))
|
||||
* adds rpc error message for --no-ancient-blocks ([#10608](https://github.com/paritytech/parity-ethereum/pull/10608))
|
||||
* Treat empty account the same as non-exist accounts in EIP-1052 ([#10775](https://github.com/paritytech/parity-ethereum/pull/10775))
|
||||
* fix: aura don't add `SystemTime::now()` ([#10720](https://github.com/paritytech/parity-ethereum/pull/10720))
|
||||
* DevP2p: Get node IP address and udp port from Socket, if not included in PING packet ([#10705](https://github.com/paritytech/parity-ethereum/pull/10705))
|
||||
* Revert "fix: aura don't add `SystemTime::now()` ([#10720](https://github.com/paritytech/parity-ethereum/pull/10720))"
|
||||
* Add a way to signal shutdown to snapshotting threads ([#10744](https://github.com/paritytech/parity-ethereum/pull/10744))
|
||||
|
||||
## Parity-Ethereum [v2.4.7](https://github.com/paritytech/parity-ethereum/releases/tag/v2.4.7)
|
||||
|
||||
Parity-Ethereum 2.4.7-stable is a bugfix release that improves performance and stability.
|
||||
|
||||
Among others, it enables the _Atlantis_ hardfork on **Morden** and **Kotti** Classic networks.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* [CI] allow cargo audit to fail ([#10676](https://github.com/paritytech/parity-ethereum/pull/10676))
|
||||
* new image ([#10673](https://github.com/paritytech/parity-ethereum/pull/10673))
|
||||
* Update publishing ([#10644](https://github.com/paritytech/parity-ethereum/pull/10644))
|
||||
* enable lto for release builds ([#10717](https://github.com/paritytech/parity-ethereum/pull/10717))
|
||||
* Use RUSTFLAGS to set the optimization level ([#10719](https://github.com/paritytech/parity-ethereum/pull/10719))
|
||||
* ethcore: enable ECIP-1054 for classic ([#10731](https://github.com/paritytech/parity-ethereum/pull/10731))
|
||||
|
||||
## Parity-Ethereum [v2.4.6](https://github.com/paritytech/parity-ethereum/releases/tag/v2.4.6)
|
||||
|
||||
Parity-Ethereum 2.4.6-stable is a bugfix release that improves performance and stability.
|
||||
|
||||
Among others, it enables the Petersburg hardfork on **Rinkeby** and **POA-Core** Network, as well as the **Kovan** Network community hardfork.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* ci: publish docs debug ([#10638](https://github.com/paritytech/parity-ethereum/pull/10638))
|
||||
|
||||
## Parity-Ethereum [v2.4.5](https://github.com/paritytech/parity-ethereum/releases/tag/v2.4.5)
|
||||
|
||||
Parity-Ethereum 2.4.5-stable is a bugfix release that improves performance and stability. This release improves memory optimizations around timestamp handling and stabilizes the 2.4 release branch.
|
||||
|
||||
As of today, Parity-Ethereum 2.3 reaches end of life and everyone is encouraged to upgrade.
|
||||
|
||||
## Parity-Ethereum [v2.4.4](https://github.com/paritytech/parity-ethereum/releases/tag/v2.4.4)
|
||||
|
||||
Parity-Ethereum 2.4.4-beta is a bugfix release that improves performance and stability. This patch release removes the dead chain configs for Easthub and Ethereum Social.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* fix(rpc-types): replace uint and hash with `ethereum_types v0.4` ([#10217](https://github.com/paritytech/parity-ethereum/pull/10217))
|
||||
* chore(bump ethereum-types) ([#10396](https://github.com/paritytech/parity-ethereum/pull/10396))
|
||||
* fix(light eth_gasPrice): ask network if not in cache ([#10535](https://github.com/paritytech/parity-ethereum/pull/10535))
|
||||
* fix(light account response): update `tx_queue` ([#10545](https://github.com/paritytech/parity-ethereum/pull/10545))
|
||||
* fix(bump dependencies) ([#10540](https://github.com/paritytech/parity-ethereum/pull/10540))
|
||||
* tx-pool: check transaction readiness before replacing ([#10526](https://github.com/paritytech/parity-ethereum/pull/10526))
|
||||
* fix #10390 ([#10391](https://github.com/paritytech/parity-ethereum/pull/10391))
|
||||
* private-tx: replace error_chain ([#10510](https://github.com/paritytech/parity-ethereum/pull/10510))
|
||||
|
||||
## Parity-Ethereum [v2.4.3](https://github.com/paritytech/parity-ethereum/releases/tag/v2.4.3)
|
||||
|
||||
Parity-Ethereum 2.4.3-beta is a bugfix release that improves performance and stability. This patch release contains a critical bug fix where serving light clients previously led to client crashes. Upgrading is highly recommended.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* Add additional request tests ([#10503](https://github.com/paritytech/parity-ethereum/pull/10503))
|
||||
|
||||
## Parity-Ethereum [v2.4.2](https://github.com/paritytech/parity-ethereum/releases/tag/v2.4.2)
|
||||
|
||||
Parity-Ethereum 2.4.2-beta is a bugfix release that improves performance and stability.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* Сaching through docker volume ([#10477](https://github.com/paritytech/parity-ethereum/pull/10477))
|
||||
* fix win&mac build ([#10486](https://github.com/paritytech/parity-ethereum/pull/10486))
|
||||
* fix(extract `timestamp_checked_add` as lib) ([#10383](https://github.com/paritytech/parity-ethereum/pull/10383))
|
||||
|
||||
## Parity-Ethereum [v2.4.1](https://github.com/paritytech/parity-ethereum/releases/tag/v2.4.1)
|
||||
|
||||
Parity-Ethereum 2.4.1-beta is a bugfix release that improves performance and stability.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* Implement parity_versionInfo & parity_setChain on LC; fix parity_setChain ([#10312](https://github.com/paritytech/parity-ethereum/pull/10312))
|
||||
* CI publish to aws ([#10446](https://github.com/paritytech/parity-ethereum/pull/10446))
|
||||
* CI aws git checkout ([#10451](https://github.com/paritytech/parity-ethereum/pull/10451))
|
||||
* Revert "CI aws git checkout ([#10451](https://github.com/paritytech/parity-ethereum/pull/10451))" (#10456)
|
||||
* Revert "CI aws git checkout ([#10451](https://github.com/paritytech/parity-ethereum/pull/10451))"
|
||||
* Tests parallelized ([#10452](https://github.com/paritytech/parity-ethereum/pull/10452))
|
||||
* Ensure static validator set changes are recognized ([#10467](https://github.com/paritytech/parity-ethereum/pull/10467))
|
||||
|
||||
## Parity-Ethereum [v2.4.0](https://github.com/paritytech/parity-ethereum/releases/tag/v2.4.0)
|
||||
|
||||
Parity-Ethereum 2.4.0-beta is our trifortnightly minor version release coming with a lot of new features as well as bugfixes and performance improvements.
|
||||
|
||||
Notable changes:
|
||||
- Account management is now deprecated ([#10213](https://github.com/paritytech/parity-ethereum/pull/10213))
|
||||
- Local accounts can now be specified via CLI ([#9960](https://github.com/paritytech/parity-ethereum/pull/9960))
|
||||
- Chains can now be reset to a particular block via CLI ([#9782](https://github.com/paritytech/parity-ethereum/pull/9782))
|
||||
- Ethash now additionally implements ProgPoW ([#9762](https://github.com/paritytech/parity-ethereum/pull/9762))
|
||||
- The `eip1283DisableTransition` flag was added to revert EIP-1283 ([#10214](https://github.com/paritytech/parity-ethereum/pull/10214))
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* revert some changes, could be buggy ([#10399](https://github.com/paritytech/parity-ethereum/pull/10399))
|
||||
* 10000 > 5000 ([#10422](https://github.com/paritytech/parity-ethereum/pull/10422))
|
||||
* fix panic when logging directory does not exist, closes #10420 ([#10424](https://github.com/paritytech/parity-ethereum/pull/10424))
|
||||
* fix underflow in pip, closes #10419 ([#10423](https://github.com/paritytech/parity-ethereum/pull/10423))
|
||||
* ci: clean up gitlab-ci.yml leftovers from previous merge ([#10429](https://github.com/paritytech/parity-ethereum/pull/10429))
|
||||
* Update hardcoded headers for Foundation, Ropsten, Kovan and Classic ([#10417](https://github.com/paritytech/parity-ethereum/pull/10417))
|
||||
|
||||
@@ -1,220 +0,0 @@
|
||||
## Parity-Ethereum [v2.5.13](https://github.com/openethereum/openethereum/releases/tag/v2.5.13)
|
||||
|
||||
Parity Ethereum v2.5.13-stable is a security release. Valid blocks with manipulated transactions (added/replaced) cause the client to stall.
|
||||
|
||||
The full list of included changes:
|
||||
* Make sure to not mark block header hash as invalid if only the body is wrong (#11356)
|
||||
|
||||
## Parity-Ethereum [v2.5.12](https://github.com/openethereum/openethereum/releases/tag/v2.5.12)
|
||||
|
||||
Parity Ethereum v2.5.12-stable is a patch release that adds Istanbul hardfork
|
||||
block numbers for POA and xDai networks, implements ECIP-1056 and implements
|
||||
EIP-2384/2387 - Muir Glacier.
|
||||
|
||||
The full list of included changes:
|
||||
* Enable EIP-2384 for ice age hard fork (#11281)
|
||||
* ethcore/res: activate agharta on classic 9573000 (#11331)
|
||||
* Istanbul HF in xDai (2019-12-12) (#11299)
|
||||
* Istanbul HF in POA Core (2019-12-19) (#11298)
|
||||
* Istanbul HF in POA Sokol (2019-12-05) (#11282)
|
||||
* Activate ecip-1061 on kotti and mordor (#11338)
|
||||
* Enable basic verification of local transactions (#11332)
|
||||
* Disallow EIP-86 style null signatures for transactions outside tests (#11335)
|
||||
|
||||
|
||||
## Parity-Ethereum [v2.5.11](https://github.com/openethereum/openethereum/releases/tag/v2.5.11)
|
||||
|
||||
Parity Ethereum v2.5.11-stable is an emergency patch release that adds the missing
|
||||
eip1344_transition for mainnet - Users are advised to update as soon as possible
|
||||
to prevent any issues with the imminent Istanbul hardfork
|
||||
|
||||
The full list of included changes:
|
||||
- [chainspec]: add `eip1344_transition` for istanbul (#11301)
|
||||
|
||||
## Parity-Ethereum [v2.5.10](https://github.com/openethereum/openethereum/releases/tag/2.5.10)
|
||||
|
||||
Parity Ethereum v2.5.10-stable is a patch release that adds block numbers for
|
||||
activating the Istanbul hardfork on mainnet, as well as a large number of
|
||||
various bugfixes, QoL changes, some code cleanup/refactoring and other
|
||||
miscellaneous changes.
|
||||
|
||||
This release removes legacy aliases for the mainnet. If you specify `--chain homestead`, `--chain frontier` or `--chain byzantium`, this will need to be changed to one of: `--chain eth`, `--chain ethereum`, `--chain foundation` or `--chain mainnet`.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* ropsten #6631425 foundation #8798209 (#11201)
|
||||
* [stable] builtin, istanbul and mordor testnet backports (#11234)
|
||||
* ethcore-builtin (#10850)
|
||||
* [builtin]: support `multiple prices and activations` in chain spec (#11039)
|
||||
* [chain specs]: activate `Istanbul` on mainnet (#11228)
|
||||
* ethcore/res: add mordor testnet configuration (#11200)
|
||||
* Update list of bootnodes for xDai chain (#11236)
|
||||
* ethcore: remove `test-helper feat` from build (#11047)
|
||||
* Secret store: fix Instant::now() related race in net_keep_alive (#11155) (#11159)
|
||||
* [stable]: backport #10691 and #10683 (#11143)
|
||||
* Fix compiler warning (that will become an error) (#10683)
|
||||
* Refactor Clique stepping (#10691)
|
||||
* Add Constantinople eips to the dev (instant_seal) config (#10809)
|
||||
* Add cargo-remote dir to .gitignore (?)
|
||||
* Insert explicit warning into the panic hook (#11225)
|
||||
* Fix docker centos build (#11226)
|
||||
* Update MIX bootnodes. (#11203)
|
||||
* Use provided usd-per-eth value if an endpoint is specified (#11209)
|
||||
* Add new line after writing block to hex file. (#10984)
|
||||
* Type annotation for next_key() matching of json filter options (#11192) (but no `FilterOption` in 2.5 so…)
|
||||
* Upgrade jsonrpc to latest (#11206)
|
||||
* [CI] check evmbin build (#11096)
|
||||
* Correct EIP-712 encoding (#11092)
|
||||
* [client]: Fix for incorrectly dropped consensus messages (#11086)
|
||||
* Fix block detail updating (#11015)
|
||||
* Switching sccache from local to Redis (#10971)
|
||||
* Made ecrecover implementation trait public (#11188)
|
||||
* [dependencies]: jsonrpc `14.0.1` (#11183)
|
||||
* [receipt]: add `sender` & `receiver` to `RichReceipts` (#11179)
|
||||
* [ethcore/builtin]: do not panic in blake2pricer on short input (#11180)
|
||||
* util Host: fix a double Read Lock bug in fn Host::session_readable() (#11175)
|
||||
* ethcore client: fix a double Read Lock bug in fn Client::logs() (#11172)
|
||||
* Change how RPCs eth_call and eth_estimateGas handle "Pending" (#11127)
|
||||
* Cleanup stratum a bit (#11161)
|
||||
* Upgrade to jsonrpc v14 (#11151)
|
||||
* SecretStore: expose restore_key_public in HTTP API (#10241)
|
||||
|
||||
## Parity-Ethereum [v2.5.9](https://github.com/openethereum/openethereum/releases/tag/v2.5.9)
|
||||
|
||||
Parity Ethereum v2.5.9-stable is a patch release that adds the block numbers for activating the Istanbul hardfork on test networks: Ropsten, Görli, Rinkeby and Kovan.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* ethcore/res: activate Istanbul on Ropsten, Görli, Rinkeby, Kovan (#11068)
|
||||
* [json-spec] make blake2 pricing spec more readable (#11034)
|
||||
|
||||
## Parity-Ethereum [v2.5.8](https://github.com/openethereum/openethereum/releases/tag/v2.5.8)
|
||||
|
||||
Parity Ethereum v2.5.8-stable is a patch release that improves security, stability and performance.
|
||||
|
||||
* The most noteworthy improvement in this release is incorporating all the EIPs required for the Istanbul hard fork.
|
||||
* This release also fixes certain security and performance issues, one of which was suspected to be consensus-threatening but turned out to be benign. Thanks to Martin Holst Swende and Felix Lange from the Ethereum Foundation for bringing the suspicious issue to our attention.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* add more tx tests (#11038)
|
||||
* Fix parallel transactions race-condition (#10995)
|
||||
* Add blake2_f precompile (#11017)
|
||||
* [trace] introduce trace failed to Ext (#11019)
|
||||
* Edit publish-onchain.sh to use https (#11016)
|
||||
* Fix deadlock in network-devp2p (#11013)
|
||||
* EIP 1108: Reduce alt_bn128 precompile gas costs (#11008)
|
||||
* xDai chain support and nodes list update (#10989)
|
||||
* EIP 2028: transaction gas lowered from 68 to 16 (#10987)
|
||||
* EIP-1344 Add CHAINID op-code (#10983)
|
||||
* manual publish jobs for releases, no changes for nightlies (#10977)
|
||||
* [blooms-db] Fix benchmarks (#10974)
|
||||
* Verify transaction against its block during import (#10954)
|
||||
* Better error message for rpc gas price errors (#10931)
|
||||
* tx-pool: accept local tx with higher gas price when pool full (#10901)
|
||||
* Fix fork choice (#10837)
|
||||
* Cleanup unused vm dependencies (#10787)
|
||||
* Fix compilation on recent nightlies (#10991)
|
||||
* Don't build rpc with ethcore test-helpers (#11048)
|
||||
* EIP 1884 Re-pricing of trie-size dependent operations (#10992)
|
||||
* Implement EIP-1283 reenable transition, EIP-1706 and EIP-2200 (#10191)
|
||||
|
||||
## Parity-Ethereum [v2.5.7](https://github.com/openethereum/openethereum/releases/tag/v2.5.7)
|
||||
|
||||
Parity Ethereum v2.5.7-stable is a bugfix release that fixes a potential DoS attack in the trace_call RPC method. This is a critical upgrade for anyone running Parity nodes with RPC exposed to the public internet (and highly recommended for anyone else). For details see this blog post.
|
||||
|
||||
## Parity-Ethereum [v2.5.6](https://github.com/openethereum/openethereum/releases/tag/v2.5.6)
|
||||
|
||||
Parity-Ethereum v2.5.6-stable is a bugfix release that improves stability.
|
||||
|
||||
* Allow specifying hostnames for node URLs
|
||||
* Fix a bug where archive nodes were losing peers
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* Kaspersky AV whitelisting (#10919)
|
||||
* Avast whitelist script (#10900)
|
||||
* Docker images renaming (#10863)
|
||||
* Remove excessive warning (#10831)
|
||||
* Allow --nat extip:your.host.here.org (#10830)
|
||||
* When updating the client or when called from RPC, sleep should mean sleep (#10814)
|
||||
* added new ropsten-bootnode and removed old one (#10794)
|
||||
* ethkey no longer uses byteorder (#10786)
|
||||
* Do not drop the peer with None difficulty (#10772)
|
||||
* docs: Update Readme with TOC, Contributor Guideline. Update Cargo package descriptions (#10652)
|
||||
|
||||
## Parity-Ethereum [v2.5.5](https://github.com/openethereum/openethereum/releases/tag/v2.5.5)
|
||||
|
||||
Parity-Ethereum v2.5.5-stable is a minor release that improves performance and stability.
|
||||
This release stabilises the 2.5 branch.
|
||||
|
||||
As of today, Parity-Ethereum 2.4 reaches end of life and everyone is
|
||||
encouraged to upgrade.
|
||||
|
||||
## Parity-Ethereum [v2.5.4](https://github.com/openethereum/openethereum/releases/tag/v2.5.4)
|
||||
|
||||
Parity Ethereum v2.5.4-beta is a security update that addresses servo/rust-smallvec#148
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* cargo update -p smallvec ([#10822](https://github.com/openethereum/openethereum/pull/10822))
|
||||
|
||||
## Parity-Ethereum [v2.5.3](https://github.com/openethereum/openethereum/releases/tag/v2.5.3)
|
||||
|
||||
Parity-Ethereum 2.5.3-beta is a bugfix release that improves performance and stability.
|
||||
|
||||
* EthereumClassic: activate the Atlantis Hardfork
|
||||
* Clique: fix time overflow
|
||||
* State tests: treat empty accounts the same as non-existant accounts (EIP 1052)
|
||||
* Networking: support discovery-only peers (geth bootnodes)
|
||||
* Snapshotting: fix unclean shutdown while snappshotting is under way
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* ethcore/res: activate atlantis classic hf on block 8772000 ([#10766](https://github.com/openethereum/openethereum/pull/10766))
|
||||
* fix docker tags for publishing ([#10741](https://github.com/openethereum/openethereum/pull/10741))
|
||||
* fix: aura don't add `SystemTime::now()` ([#10720](https://github.com/openethereum/openethereum/pull/10720))
|
||||
* Treat empty account the same as non-exist accounts in EIP-1052 ([#10775](https://github.com/openethereum/openethereum/pull/10775))
|
||||
* DevP2p: Get node IP address and udp port from Socket, if not included in PING packet ([#10705](https://github.com/openethereum/openethereum/pull/10705))
|
||||
* Add a way to signal shutdown to snapshotting threads ([#10744](https://github.com/openethereum/openethereum/pull/10744))
|
||||
|
||||
## Parity-Ethereum [v2.5.2](https://github.com/openethereum/openethereum/releases/tag/v2.5.2)
|
||||
|
||||
Parity-Ethereum 2.5.2-beta is a bugfix release that improves performance and stability.
|
||||
|
||||
Among others, it enables the _Atlantis_ hardfork on **Morden** and **Kotti** Classic networks.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* [CI] allow cargo audit to fail ([#10676](https://github.com/openethereum/openethereum/pull/10676))
|
||||
* Reset blockchain properly ([#10669](https://github.com/openethereum/openethereum/pull/10669))
|
||||
* new image ([#10673](https://github.com/openethereum/openethereum/pull/10673))
|
||||
* Update publishing ([#10644](https://github.com/openethereum/openethereum/pull/10644))
|
||||
* enable lto for release builds ([#10717](https://github.com/openethereum/openethereum/pull/10717))
|
||||
* Use RUSTFLAGS to set the optimization level ([#10719](https://github.com/openethereum/openethereum/pull/10719))
|
||||
* ethcore: enable ECIP-1054 for classic ([#10731](https://github.com/openethereum/openethereum/pull/10731))
|
||||
|
||||
## Parity-Ethereum [v2.5.1](https://github.com/openethereum/openethereum/releases/tag/v2.5.1)
|
||||
|
||||
Parity-Ethereum 2.5.1-beta is a bugfix release that improves performance and stability.
|
||||
|
||||
Among others, it enables the Petersburg hardfork on **Rinkeby** and **POA-Core** Network, as well as the **Kovan** Network community hardfork.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* ci: publish docs debug ([#10638](https://github.com/openethereum/openethereum/pull/10638))
|
||||
|
||||
## Parity-Ethereum [v2.5.0](https://github.com/openethereum/openethereum/releases/tag/v2.5.0)
|
||||
|
||||
Parity-Ethereum 2.5.0-beta is a minor release that improves performance and stabilizes the 2.5 branch by marking it as beta release.
|
||||
|
||||
- This release adds support for the Clique consensus engine ([#9981](https://github.com/openethereum/openethereum/pull/9981))
|
||||
- This enables Parity-Ethereum users to use the Görli, the Kotti Classic, and the legacy Rinkeby testnet. To get started try `parity --chain goerli`; note that light client support is currently not yet fully functional.
|
||||
- This release removes the dead chain configs for Easthub and Ethereum Social ([#10531](https://github.com/openethereum/openethereum/pull/10531))
|
||||
|
||||
As of today, Parity-Ethereum 2.3 reaches end of life and everyone is encouraged to upgrade.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* fix(light cull): poll light cull instead of timer ([#10559](https://github.com/openethereum/openethereum/pull/10559))
|
||||
|
||||
@@ -1,307 +0,0 @@
|
||||
## Parity-Ethereum [v2.6.8](https://github.com/openethereum/openethereum/releases/tag/v2.6.8)
|
||||
|
||||
Parity Ethereum v2.6.8-beta is a security release. Valid blocks with manipulated transactions (added/replaced) cause the client to stall.
|
||||
|
||||
The full list of included changes:
|
||||
* Make sure to not mark block header hash as invalid if only the body is wrong (#11356)
|
||||
|
||||
## Parity-Ethereum [v2.6.7](https://github.com/openethereum/openethereum/releases/tag/v2.6.7)
|
||||
|
||||
Parity Ethereum v2.6.7-beta is a patch release that adds Istanbul hardfork
|
||||
block numbers for POA and xDai networks, implements ECIP-1056 and implements
|
||||
EIP-2384/2387 - Muir Glacier.
|
||||
|
||||
The full list of included changes:
|
||||
* Enable EIP-2384 for ice age hard fork (#11281)
|
||||
* ethcore/res: activate agharta on classic 9573000 (#11331)
|
||||
* Istanbul HF in xDai (2019-12-12) (#11299)
|
||||
* Istanbul HF in POA Core (2019-12-19) (#11298)
|
||||
* Istanbul HF in POA Sokol (2019-12-05) (#11282)
|
||||
* Activate ecip-1061 on kotti and mordor (#11338)
|
||||
* Enable basic verification of local transactions (#11332)
|
||||
* Disallow EIP-86 style null signatures for transactions outside tests (#11335)
|
||||
* SecretStore database migration to v4 (#11322)
|
||||
|
||||
## Parity-Ethereum [v2.6.6](https://github.com/openethereum/openethereum/releases/tag/v2.6.6)
|
||||
|
||||
Parity Ethereum v2.6.6-beta is an emergency patch release that adds the missing
|
||||
eip1344_transition for mainnet - Users are advised to update as soon as possible
|
||||
to prevent any issues with the imminent Istanbul hardfork
|
||||
|
||||
The full list of included changes:
|
||||
* [chainspec]: add `eip1344_transition` for istanbul (#11301)
|
||||
|
||||
## Parity-Ethereum [v2.6.5](https://github.com/openethereum/openethereum/releases/tag/v2.6.5)
|
||||
|
||||
Parity Ethereum v2.6.5-beta is a patch release that adds block numbers for activating the Istanbul hardfork on mainnet, as well as a large number of various bugfixes, QoL changes, some code cleanup/refactoring and other miscellaneous changes.
|
||||
|
||||
This release removes legacy aliases for the mainnet. If you specify `--chain homestead`, `--chain frontier` or `--chain byzantium`, this will need to be changed to one of: `--chain eth`, `--chain ethereum`, `--chain foundation` or `--chain mainnet`.
|
||||
|
||||
This release includes important changes to how snapshots are produced. The size of the Ethereum account state means that producing a snapshot takes a long while; most nodes today are not able to finish before the relevant state is pruned. Starting with v2.6.5, pruning is paused while a snapshot is underway, hopefully fixing the current dearth of recent snapshots. The downside to this is that memory usage goes up while a snapshot is produced.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* [CI] check evmbin build (#11096)
|
||||
* Correct EIP-712 encoding (#11092)
|
||||
* [client]: Fix for incorrectly dropped consensus messages (#11082) (#11086)
|
||||
* Update hardcoded headers (foundation, classic, kovan, xdai, ewc, ...) (#11053)
|
||||
* Add cargo-remote dir to .gitignore (?)
|
||||
* Update light client headers: ropsten 6631425 foundation 8798209 (#11201)
|
||||
* Update list of bootnodes for xDai chain (#11236)
|
||||
* ethcore/res: add mordor testnet configuration (#11200)
|
||||
* [chain specs]: activate Istanbul on mainnet (#11228)
|
||||
* [builtin]: support multiple prices and activations in chain spec (#11039)
|
||||
* [receipt]: add sender & receiver to RichReceipts (#11179)
|
||||
* [ethcore/builtin]: do not panic in blake2pricer on short input (#11180)
|
||||
* Made ecrecover implementation trait public (#11188)
|
||||
* Fix docker centos build (#11226)
|
||||
* Update MIX bootnodes. (#11203)
|
||||
* Insert explicit warning into the panic hook (#11225)
|
||||
* Use provided usd-per-eth value if an endpoint is specified (#11209)
|
||||
* Cleanup stratum a bit (#11161)
|
||||
* Add Constantinople EIPs to the dev (instant_seal) config (#10809) (already backported)
|
||||
* util Host: fix a double Read Lock bug in fn Host::session_readable() (#11175)
|
||||
* ethcore client: fix a double Read Lock bug in fn Client::logs() (#11172)
|
||||
* Type annotation for next_key() matching of json filter options (#11192)
|
||||
* Upgrade jsonrpc to latest (#11206)
|
||||
* [dependencies]: jsonrpc 14.0.1 (#11183)
|
||||
* Upgrade to jsonrpc v14 (#11151)
|
||||
* Switching sccache from local to Redis (#10971)
|
||||
* Snapshot restoration overhaul (#11219)
|
||||
* Add new line after writing block to hex file. (#10984)
|
||||
* Pause pruning while snapshotting (#11178)
|
||||
* Change how RPCs eth_call and eth_estimateGas handle "Pending" (#11127)
|
||||
* Fix block detail updating (#11015)
|
||||
* Make InstantSeal Instant again #11186
|
||||
* Filter out some bad ropsten warp snapshots (#11247)
|
||||
* Allow default block parameter to be blockHash (#10932)
|
||||
|
||||
## Parity-Ethereum [v2.6.4](https://github.com/openethereum/openethereum/releases/tag/v2.6.4)
|
||||
|
||||
Parity Ethereum v2.6.4-stable is a patch release that adds the block numbers for activating the Istanbul hardfork on test networks: Ropsten, Görli, Rinkeby and Kovan.
|
||||
|
||||
A full list of included changes:
|
||||
|
||||
* ethcore/res: activate Istanbul on Ropsten, Görli, Rinkeby, Kovan (#11068)
|
||||
* cleanup json crate (#11027)
|
||||
* [json-spec] make blake2 pricing spec more readable (#11034)
|
||||
* Update JSON tests to d4f86ecf4aa7c (#11054)
|
||||
|
||||
## Parity-Ethereum [v2.6.3](https://github.com/openethereum/openethereum/releases/tag/v2.6.3)
|
||||
|
||||
Parity Ethereum v2.6.3-stable is a patch release that improves security, stability and performance.
|
||||
|
||||
* The most noteworthy improvement in this release is incorporating all the EIPs required for the Istanbul hard fork.
|
||||
* This release also fixes certain security and performance issues, one of which was suspected to be consensus-threatening but turned out to be benign. Thanks to Martin Holst Swende and Felix Lange from the Ethereum Foundation for bringing the suspicious issue to our attention.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* add more tx tests (#11038)
|
||||
* Fix parallel transactions race-condition (#10995)
|
||||
* Add blake2_f precompile (#11017)
|
||||
* [trace] introduce trace failed to Ext (#11019)
|
||||
* Edit publish-onchain.sh to use https (#11016)
|
||||
* Fix deadlock in network-devp2p (#11013)
|
||||
* EIP 1108: Reduce alt_bn128 precompile gas costs (#11008)
|
||||
* xDai chain support and nodes list update (#10989)
|
||||
* EIP 2028: transaction gas lowered from 68 to 16 (#10987)
|
||||
* EIP-1344 Add CHAINID op-code (#10983)
|
||||
* manual publish jobs for releases, no changes for nightlies (#10977)
|
||||
* [blooms-db] Fix benchmarks (#10974)
|
||||
* Verify transaction against its block during import (#10954)
|
||||
* Better error message for rpc gas price errors (#10931)
|
||||
* Fix fork choice (#10837)
|
||||
* Fix compilation on recent nightlies (#10991)
|
||||
* Don't build rpc with ethcore test-helpers (#11048)
|
||||
* EIP 1884 Re-pricing of trie-size dependent operations (#10992)
|
||||
* Implement EIP-1283 reenable transition, EIP-1706 and EIP-2200 (#10191)
|
||||
|
||||
## Parity-Ethereum [v2.6.2](https://github.com/openethereum/openethereum/releases/tag/v2.6.2)
|
||||
|
||||
Parity Ethereum v2.6.2-stable is a bugfix release that fixes a potential DoS attack in the trace_call RPC method. This is a critical upgrade for anyone running Parity nodes with RPC exposed to the public internet (and highly recommended for anyone else). For details see this blog post.
|
||||
|
||||
## Parity-Ethereum [v2.6.1](https://github.com/openethereum/openethereum/releases/tag/v2.6.1)
|
||||
|
||||
Parity-Ethereum 2.6.1-beta is a patch release that improves stability.
|
||||
|
||||
This release includes:
|
||||
* Allow specifying hostnames for node URLs
|
||||
* Fix a bug where archive nodes were losing peers
|
||||
* Add support for Energy Web Foundations new chains 'Volta' and 'EWC', and remove their deprecated 'Tobalaba' chain.
|
||||
|
||||
The full list of included changes:
|
||||
* Add support for Energy Web Foundation's new chains (#10957)
|
||||
* Kaspersky AV whitelisting (#10919)
|
||||
* Avast whitelist script (#10900)
|
||||
* Docker images renaming (#10863)
|
||||
* Remove excessive warning (#10831)
|
||||
* Allow --nat extip:your.host.here.org (#10830)
|
||||
* When updating the client or when called from RPC, sleep should mean sleep (#10814)
|
||||
* added new ropsten-bootnode and removed old one (#10794)
|
||||
* ethkey no longer uses byteorder (#10786)
|
||||
* docs: Update Readme with TOC, Contributor Guideline. Update Cargo package descriptions (#10652)
|
||||
|
||||
## Parity-Ethereum [v2.6.0](https://github.com/openethereum/openethereum/releases/tag/v2.6.0)
|
||||
|
||||
Parity-Ethereum 2.6.0-beta is a minor release that stabilizes the 2.6 branch by
|
||||
marking it as a beta release.
|
||||
|
||||
This release includes:
|
||||
* Major refactoring of the codebase
|
||||
* Many bugfixes
|
||||
* Significant improvements to logging, error and warning message clarity.
|
||||
* SecretStore: remove support of old database formats (#10757)
|
||||
* This is a potentially breaking change if you have not upgraded for
|
||||
quite some time.
|
||||
|
||||
As of today, Parity-Ethereum 2.4 reaches end of life, and everyone is
|
||||
encouraged to upgrade.
|
||||
|
||||
The full list of included changes:
|
||||
* update jsonrpc to 12.0 ([#10841](https://github.com/openethereum/openethereum/pull/10841))
|
||||
* Move more code into state-account ([#10840](https://github.com/openethereum/openethereum/pull/10840))
|
||||
* Extract AccountDB to account-db ([#10839](https://github.com/openethereum/openethereum/pull/10839))
|
||||
* Extricate PodAccount and state Account to own crates ([#10838](https://github.com/openethereum/openethereum/pull/10838))
|
||||
* Fix fork choice ([#10837](https://github.com/openethereum/openethereum/pull/10837))
|
||||
* tests: Relates to #10655: Test instructions for Readme ([#10835](https://github.com/openethereum/openethereum/pull/10835))
|
||||
* idiomatic changes to PodState ([#10834](https://github.com/openethereum/openethereum/pull/10834))
|
||||
* Break circular dependency between Client and Engine (part 1) ([#10833](https://github.com/openethereum/openethereum/pull/10833))
|
||||
* Remove excessive warning ([#10831](https://github.com/openethereum/openethereum/pull/10831))
|
||||
* Allow --nat extip:your.host.here.org ([#10830](https://github.com/openethereum/openethereum/pull/10830))
|
||||
* ethcore does not use byteorder ([#10829](https://github.com/openethereum/openethereum/pull/10829))
|
||||
* Fix typo in README.md ([#10828](https://github.com/openethereum/openethereum/pull/10828))
|
||||
* Update wordlist to v1.3 ([#10823](https://github.com/openethereum/openethereum/pull/10823))
|
||||
* bump `smallvec 0.6.10` to fix vulnerability ([#10822](https://github.com/openethereum/openethereum/pull/10822))
|
||||
* removed additional_params method ([#10818](https://github.com/openethereum/openethereum/pull/10818))
|
||||
* Improve logging when remote peer is unknown ([#10817](https://github.com/openethereum/openethereum/pull/10817))
|
||||
* replace memzero with zeroize crate ([#10816](https://github.com/openethereum/openethereum/pull/10816))
|
||||
* When updating the client or when called from RPC, sleep should mean sleep ([#10814](https://github.com/openethereum/openethereum/pull/10814))
|
||||
* Don't reimplement the logic from the Default impl ([#10813](https://github.com/openethereum/openethereum/pull/10813))
|
||||
* refactor: whisper: Add type aliases and update rustdocs in message.rs ([#10812](https://github.com/openethereum/openethereum/pull/10812))
|
||||
* test: whisper/cli `add invalid pool size test depending on processor` ([#10811](https://github.com/openethereum/openethereum/pull/10811))
|
||||
* Add Constantinople EIPs to the dev (instant_seal) config ([#10809](https://github.com/openethereum/openethereum/pull/10809))
|
||||
* fix spurious test failure ([#10808](https://github.com/openethereum/openethereum/pull/10808))
|
||||
* revert temp changes to .gitlab-ci.yml ([#10807](https://github.com/openethereum/openethereum/pull/10807))
|
||||
* removed redundant fmt::Display implementations ([#10806](https://github.com/openethereum/openethereum/pull/10806))
|
||||
* removed EthEngine alias ([#10805](https://github.com/openethereum/openethereum/pull/10805))
|
||||
* ethcore-bloom-journal updated to 2018 ([#10804](https://github.com/openethereum/openethereum/pull/10804))
|
||||
* Fix a few typos and unused warnings. ([#10803](https://github.com/openethereum/openethereum/pull/10803))
|
||||
* updated price-info to edition 2018 ([#10801](https://github.com/openethereum/openethereum/pull/10801))
|
||||
* updated parity-local-store to edition 2018 ([#10800](https://github.com/openethereum/openethereum/pull/10800))
|
||||
* updated project to ansi_term 0.11 ([#10799](https://github.com/openethereum/openethereum/pull/10799))
|
||||
* ethcore-light uses bincode 1.1 ([#10798](https://github.com/openethereum/openethereum/pull/10798))
|
||||
* ethcore-network-devp2p uses igd 0.9 ([#10797](https://github.com/openethereum/openethereum/pull/10797))
|
||||
* Better logging when backfilling ancient blocks fail ([#10796](https://github.com/openethereum/openethereum/pull/10796))
|
||||
* added new ropsten-bootnode and removed old one ([#10794](https://github.com/openethereum/openethereum/pull/10794))
|
||||
* Removed machine abstraction from ethcore ([#10791](https://github.com/openethereum/openethereum/pull/10791))
|
||||
* Removed redundant ethcore-service error type ([#10788](https://github.com/openethereum/openethereum/pull/10788))
|
||||
* Cleanup unused vm dependencies ([#10787](https://github.com/openethereum/openethereum/pull/10787))
|
||||
* ethkey no longer uses byteorder ([#10786](https://github.com/openethereum/openethereum/pull/10786))
|
||||
* Updated blooms-db to rust 2018 and removed redundant deps ([#10785](https://github.com/openethereum/openethereum/pull/10785))
|
||||
* Treat empty account the same as non-exist accounts in EIP-1052 ([#10775](https://github.com/openethereum/openethereum/pull/10775))
|
||||
* Do not drop the peer with None difficulty ([#10772](https://github.com/openethereum/openethereum/pull/10772))
|
||||
* EIP-1702: Generalized Account Versioning Scheme ([#10771](https://github.com/openethereum/openethereum/pull/10771))
|
||||
* Move Engine::register_client to be before other I/O handler registration ([#10767](https://github.com/openethereum/openethereum/pull/10767))
|
||||
* ethcore/res: activate atlantis classic hf on block 8772000 ([#10766](https://github.com/openethereum/openethereum/pull/10766))
|
||||
* Updated Bn128PairingImpl to use optimized batch pairing ([#10765](https://github.com/openethereum/openethereum/pull/10765))
|
||||
* Remove unused code ([#10762](https://github.com/openethereum/openethereum/pull/10762))
|
||||
* Initialize private tx logger only if private tx functionality is enabled ([#10758](https://github.com/openethereum/openethereum/pull/10758))
|
||||
* SecretStore: remove support of old database formats ([#10757](https://github.com/openethereum/openethereum/pull/10757))
|
||||
* Enable aesni ([#10756](https://github.com/openethereum/openethereum/pull/10756))
|
||||
* updater: fix static id hashes initialization ([#10755](https://github.com/openethereum/openethereum/pull/10755))
|
||||
* Use fewer threads for snapshotting ([#10752](https://github.com/openethereum/openethereum/pull/10752))
|
||||
* Die error_chain, die ([#10747](https://github.com/openethereum/openethereum/pull/10747))
|
||||
* Fix deprectation warnings on nightly ([#10746](https://github.com/openethereum/openethereum/pull/10746))
|
||||
* Improve logging and cleanup in miner around block sealing ([#10745](https://github.com/openethereum/openethereum/pull/10745))
|
||||
* Add a way to signal shutdown to snapshotting threads ([#10744](https://github.com/openethereum/openethereum/pull/10744))
|
||||
* fix docker tags for publishing ([#10741](https://github.com/openethereum/openethereum/pull/10741))
|
||||
* refactor: Fix indentation in ethjson ([#10740](https://github.com/openethereum/openethereum/pull/10740))
|
||||
* Log validator set changes in EpochManager ([#10734](https://github.com/openethereum/openethereum/pull/10734))
|
||||
* Print warnings when using dangerous settings for ValidatorSet ([#10733](https://github.com/openethereum/openethereum/pull/10733))
|
||||
* ethcore: enable ECIP-1054 for classic ([#10731](https://github.com/openethereum/openethereum/pull/10731))
|
||||
* Stop breaking out of loop if a non-canonical hash is found ([#10729](https://github.com/openethereum/openethereum/pull/10729))
|
||||
* Removed secret_store folder ([#10722](https://github.com/openethereum/openethereum/pull/10722))
|
||||
* Revert "enable lto for release builds (#10717)" ([#10721](https://github.com/openethereum/openethereum/pull/10721))
|
||||
* fix: aura don't add `SystemTime::now()` ([#10720](https://github.com/openethereum/openethereum/pull/10720))
|
||||
* Use RUSTFLAGS to set the optimization level ([#10719](https://github.com/openethereum/openethereum/pull/10719))
|
||||
* enable lto for release builds ([#10717](https://github.com/openethereum/openethereum/pull/10717))
|
||||
* [devp2p] Update to 2018 edition ([#10716](https://github.com/openethereum/openethereum/pull/10716))
|
||||
* [devp2p] Don't use `rust-crypto` ([#10714](https://github.com/openethereum/openethereum/pull/10714))
|
||||
* [devp2p] Fix warnings and re-org imports ([#10710](https://github.com/openethereum/openethereum/pull/10710))
|
||||
* DevP2p: Get node IP address and udp port from Socket, if not included in PING packet ([#10705](https://github.com/openethereum/openethereum/pull/10705))
|
||||
* introduce MissingParent Error, fixes #10699 ([#10700](https://github.com/openethereum/openethereum/pull/10700))
|
||||
* Refactor Clique stepping ([#10691](https://github.com/openethereum/openethereum/pull/10691))
|
||||
* add_sync_notifier in EthPubSubClient holds on to a Client for too long ([#10689](https://github.com/openethereum/openethereum/pull/10689))
|
||||
* Fix compiler warning (that will become an error) ([#10683](https://github.com/openethereum/openethereum/pull/10683))
|
||||
* Don't panic if extra_data is longer than VANITY_LENGTH ([#10682](https://github.com/openethereum/openethereum/pull/10682))
|
||||
* Remove annoying compiler warnings ([#10679](https://github.com/openethereum/openethereum/pull/10679))
|
||||
* Remove support for hardware wallets ([#10678](https://github.com/openethereum/openethereum/pull/10678))
|
||||
* [CI] allow cargo audit to fail ([#10676](https://github.com/openethereum/openethereum/pull/10676))
|
||||
* new image ([#10673](https://github.com/openethereum/openethereum/pull/10673))
|
||||
* Upgrade ethereum types ([#10670](https://github.com/openethereum/openethereum/pull/10670))
|
||||
* Reset blockchain properly ([#10669](https://github.com/openethereum/openethereum/pull/10669))
|
||||
* fix: Move PR template into .github/ folder ([#10663](https://github.com/openethereum/openethereum/pull/10663))
|
||||
* docs: evmbin - Update Rust docs ([#10658](https://github.com/openethereum/openethereum/pull/10658))
|
||||
* refactor: Related #9459 - evmbin: replace untyped json! macro with fully typed serde serialization using Rust structs ([#10657](https://github.com/openethereum/openethereum/pull/10657))
|
||||
* docs: Add PR template ([#10654](https://github.com/openethereum/openethereum/pull/10654))
|
||||
* docs: Add ProgPoW Rust docs to ethash module ([#10653](https://github.com/openethereum/openethereum/pull/10653))
|
||||
* docs: Update Readme with TOC, Contributor Guideline. Update Cargo package descriptions ([#10652](https://github.com/openethereum/openethereum/pull/10652))
|
||||
* Upgrade to parity-crypto 0.4 ([#10650](https://github.com/openethereum/openethereum/pull/10650))
|
||||
* fix(compilation warnings) ([#10649](https://github.com/openethereum/openethereum/pull/10649))
|
||||
* [whisper] Move needed aes_gcm crypto in-crate ([#10647](https://github.com/openethereum/openethereum/pull/10647))
|
||||
* Update publishing ([#10644](https://github.com/openethereum/openethereum/pull/10644))
|
||||
* ci: publish docs debug ([#10638](https://github.com/openethereum/openethereum/pull/10638))
|
||||
* Fix publish docs ([#10635](https://github.com/openethereum/openethereum/pull/10635))
|
||||
* Fix rinkeby petersburg fork ([#10632](https://github.com/openethereum/openethereum/pull/10632))
|
||||
* Update kovan.json to switch Kovan validator set to POA Consensus Contracts ([#10628](https://github.com/openethereum/openethereum/pull/10628))
|
||||
* [ethcore] remove error_chain ([#10616](https://github.com/openethereum/openethereum/pull/10616))
|
||||
* Remove unused import ([#10615](https://github.com/openethereum/openethereum/pull/10615))
|
||||
* Adds parity_getRawBlockByNumber, parity_submitRawBlock ([#10609](https://github.com/openethereum/openethereum/pull/10609))
|
||||
* adds rpc error message for --no-ancient-blocks ([#10608](https://github.com/openethereum/openethereum/pull/10608))
|
||||
* Constantinople HF on POA Core ([#10606](https://github.com/openethereum/openethereum/pull/10606))
|
||||
* Clique: zero-fill extradata when the supplied value is less than 32 bytes in length ([#10605](https://github.com/openethereum/openethereum/pull/10605))
|
||||
* evm: add some mulmod benches ([#10600](https://github.com/openethereum/openethereum/pull/10600))
|
||||
* sccache logs to stdout ([#10596](https://github.com/openethereum/openethereum/pull/10596))
|
||||
* update bootnodes ([#10595](https://github.com/openethereum/openethereum/pull/10595))
|
||||
* Merge `Notifier` and `TransactionsPoolNotifier` ([#10591](https://github.com/openethereum/openethereum/pull/10591))
|
||||
* fix(whisper): change expiry `unix_time + ttl + work` ([#10587](https://github.com/openethereum/openethereum/pull/10587))
|
||||
* fix(evmbin): make benches compile again ([#10586](https://github.com/openethereum/openethereum/pull/10586))
|
||||
* fix issue with compilation when 'slow-blocks' feature enabled ([#10585](https://github.com/openethereum/openethereum/pull/10585))
|
||||
* Allow CORS requests in Secret Store API ([#10584](https://github.com/openethereum/openethereum/pull/10584))
|
||||
* CI improvements ([#10579](https://github.com/openethereum/openethereum/pull/10579))
|
||||
* ethcore: improve timestamp handling ([#10574](https://github.com/openethereum/openethereum/pull/10574))
|
||||
* Update Issue Template to direct security issue to email ([#10562](https://github.com/openethereum/openethereum/pull/10562))
|
||||
* version: bump master to 2.6 ([#10560](https://github.com/openethereum/openethereum/pull/10560))
|
||||
* fix(light cull): poll light cull instead of timer ([#10559](https://github.com/openethereum/openethereum/pull/10559))
|
||||
* Watch transactions pool ([#10558](https://github.com/openethereum/openethereum/pull/10558))
|
||||
* Add SealingState; don't prepare block when not ready. ([#10529](https://github.com/openethereum/openethereum/pull/10529))
|
||||
* Explicitly enable or disable Stratum in config file (Issue 9785) ([#10521](https://github.com/openethereum/openethereum/pull/10521))
|
||||
* Add filtering capability to `parity_pendingTransactions` (issue 8269) ([#10506](https://github.com/openethereum/openethereum/pull/10506))
|
||||
* Remove calls to heapsize ([#10432](https://github.com/openethereum/openethereum/pull/10432))
|
||||
* RPC: Implements eth_subscribe("syncing") ([#10311](https://github.com/openethereum/openethereum/pull/10311))
|
||||
* SecretStore: non-blocking wait of session completion ([#10303](https://github.com/openethereum/openethereum/pull/10303))
|
||||
* Node table limiting and cache for node filter ([#10288](https://github.com/openethereum/openethereum/pull/10288))
|
||||
* SecretStore: expose restore_key_public in HTTP API ([#10241](https://github.com/openethereum/openethereum/pull/10241))
|
||||
* Trivial journal for private transactions ([#10056](https://github.com/openethereum/openethereum/pull/10056))
|
||||
|
||||
## Previous releases
|
||||
|
||||
- [CHANGELOG-2.5](docs/CHANGELOG-2.5.md) (_stable_)
|
||||
- [CHANGELOG-2.4](docs/CHANGELOG-2.4.md) (EOL: 2019-07-08)
|
||||
- [CHANGELOG-2.3](docs/CHANGELOG-2.3.md) (EOL: 2019-04-09)
|
||||
- [CHANGELOG-2.2](docs/CHANGELOG-2.2.md) (EOL: 2019-02-25)
|
||||
- [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)
|
||||
@@ -1,383 +0,0 @@
|
||||
## Parity-Ethereum [v2.7.2](https://github.com/openethereum/openethereum/releases/tag/v2.7.2)
|
||||
|
||||
Parity Ethereum v2.7.2-stable is a patch version release of parity-ethereum.
|
||||
Starting in the 2.7.x series of releases, parity-ethereum is switching to a single stable release
|
||||
track. As a result, any clients that currently receive updates from the beta
|
||||
track should switch to the stable track.
|
||||
Due to database format changes, upgrading from 2.5.x or 2.6.x is one-way only.
|
||||
|
||||
The full list of included changes:
|
||||
* [eth classic chainspec]: remove balance = 1 (#11458)
|
||||
* backwards compatible call_type creation_method (#11450 + #11455)
|
||||
* chore: remove unused dependencies (#11432)
|
||||
* Cargo.lock: new lockfile format (#11448)
|
||||
* rlp_derive: cleanup (#11446)
|
||||
* Avoid long state queries when serving GetNodeData requests (#11444)
|
||||
* update kvdb-rocksdb to 0.4 (#11442)
|
||||
* Remove dead bootnodes, add new geth bootnodes (#11441)
|
||||
* goerli: replace foundation bootnode (#11433)
|
||||
* fix: export hardcoded sync format (#11416)
|
||||
* verification: fix race same block + misc (#11400)
|
||||
* update classic testnet bootnodes (#11398)
|
||||
* gcc to clang (#11453)
|
||||
|
||||
## Parity-Ethereum [v2.7.1](https://github.com/openethereum/openethereum/releases/tag/v2.7.1)
|
||||
* Revert #11311 (#11427)
|
||||
|
||||
## Parity-Ethereum [v2.7.0](https://github.com/openethereum/openethereum/releases/tag/v2.7.0)
|
||||
|
||||
Parity Ethereum v2.7.0-stable is a minor version release of parity-ethereum. As
|
||||
of this release, parity-ethereum is switching to a single `stable` release
|
||||
track. As a result, any clients that currently receive updates from the `beta`
|
||||
track should switch to the `stable` track.
|
||||
|
||||
The full list of included changes from `v2.5-stable` to `v2.7-stable` (the
|
||||
`v2.6-beta` branch will already include some of these changes):
|
||||
|
||||
* miner: fix deprecation warning Error::description (#11380)
|
||||
* Fix Aztlan hard fork issues (#11347)
|
||||
* authority_round: Fix next_step_time_duration. (#11379)
|
||||
* Set the block gas limit to the value returned by a contract call (#10928)
|
||||
* [Trace] Distinguish between `create` and `create2` (#11311)
|
||||
* fix cargo audit (#11378)
|
||||
* Fix esoteric test config variable (#11292)
|
||||
* Rip out the C and Java bindings (#11346)
|
||||
* Encapsulate access to the client for secret store (#11232)
|
||||
* Forward-port #11356 (#11359)
|
||||
* Fix error message typo (#11363)
|
||||
* [util/migration]: remove needless `static` bounds (#11348)
|
||||
* Replace stale boot nodes with latest list (#11351)
|
||||
* Update to latest `kvdb-*`: no default column, DBValue is Vec (#11312)
|
||||
* we do not profit from incremental now (#11302)
|
||||
* update autoupdate fork blocks for nightly (#11308)
|
||||
* Add Nat PMP method to P2P module (#11210)
|
||||
* Add randomness contract support to AuthorityRound. (#10946)
|
||||
* ethcore/res: activate ecip-1061 on kotti and mordor (#11338)
|
||||
* tx-q: enable basic verification of local transactions (#11332)
|
||||
* remove null signatures (#11335)
|
||||
* ethcore/res: activate agharta on classic 9573000 (#11331)
|
||||
* [secretstore] migrate to version 4 (#11322)
|
||||
* Enable EIP-2384 for ice age hard fork (#11281)
|
||||
* Fix atomicity violation in network-devp2p (#11277)
|
||||
* Istanbul activation on xDai (#11299)
|
||||
* Istanbul activation on POA Core (#11298)
|
||||
* Adds support for ipc socket permissions (#11273)
|
||||
* Add check for deserialising hex values over U256 limit (#11309)
|
||||
* validate-chainspecs: check istanbul eips are in the foundation spec (#11305)
|
||||
* [chainspec]: add `eip1344_transition` for istanbul (#11301)
|
||||
* only add transactions to signing-queue if it is enabled (#11272)
|
||||
* Use upstream rocksdb (#11248)
|
||||
* Treat only blocks in queue as synced (#11264)
|
||||
* add support for evan.network chains (#11289)
|
||||
* Add benchmarks and tests for RlpNodeCodec decoding (#11287)
|
||||
* upgrade vergen to 3.0 (#11293)
|
||||
* interruptible test and build jobs (#11294)
|
||||
* Istanbul HF on POA Sokol (#11282)
|
||||
* [ethcore]: apply filter when `PendingSet::AlwaysQueue` in `ready_transactions_filtered` (#11227)
|
||||
* Update lib.rs (#11286)
|
||||
* Don't prune ancient state when instantiating a Client (#11270)
|
||||
* fixed verify_uncles error type (#11276)
|
||||
* ethcore: fix rlp deprecation warnings (#11280)
|
||||
* Upgrade trie-db to 0.16.0. (#11274)
|
||||
* Clarify what first_block `None` means (#11269)
|
||||
* removed redundant VMType enum with one variant (#11266)
|
||||
* Ensure jsonrpc threading settings are sane (#11267)
|
||||
* Return Ok(None) when the registrar contract returns empty slice (#11257)
|
||||
* Add a benchmark for snapshot::account::to_fat_rlps() (#11185)
|
||||
* Fix misc compile warnings (#11258)
|
||||
* simplify verification (#11249)
|
||||
* update ropsten forkCanonHash, forkBlock (#11247)
|
||||
* Make InstantSeal Instant again (#11186)
|
||||
* ropsten #6631425 foundation #8798209 (#11201)
|
||||
* Update list of bootnodes for xDai chain (#11236)
|
||||
* ethcore/res: add mordor testnet configuration (#11200)
|
||||
* [chain specs]: activate `Istanbul` on mainnet (#11228)
|
||||
* [builtin]: support `multiple prices and activations` in chain spec (#11039)
|
||||
* Insert explicit warning into the panic hook (#11225)
|
||||
* Snapshot restoration overhaul (#11219)
|
||||
* Fix docker centos build (#11226)
|
||||
* retry on gitlab system failures (#11222)
|
||||
* Update bootnodes. (#11203)
|
||||
* Use provided usd-per-eth value if an endpoint is specified (#11209)
|
||||
* Use a lock instead of atomics for snapshot Progress (#11197)
|
||||
* [informant]: `MillisecondDuration` -> `as_millis()` (#11211)
|
||||
* Step duration map configuration parameter ported from the POA Network fork (#10902)
|
||||
* Upgrade jsonrpc to latest (#11206)
|
||||
* [export hardcoded sync]: use debug for `H256` (#11204)
|
||||
* Pause pruning while snapshotting (#11178)
|
||||
* Type annotation for next_key() matching of json filter options (#11192)
|
||||
* Crypto primitives removed from ethkey (#11174)
|
||||
* Made ecrecover implementation trait public (#11188)
|
||||
* Remove unused macro_use. (#11191)
|
||||
* [dependencies]: jsonrpc `14.0.1` (#11183)
|
||||
* [receipt]: add `sender` & `receiver` to `RichReceipts` (#11179)
|
||||
* [dependencies] bump rand 0.7 (#11022)
|
||||
* [ethcore/builtin]: do not panic in blake2pricer on short input (#11180)
|
||||
* TxPermissions ver 3: gas price & data (#11170)
|
||||
* [ethash] chainspec validate `ecip1017EraRounds` non-zero (#11123)
|
||||
* util Host: fix a double Read Lock bug in fn Host::session_readable() (#11175)
|
||||
* ethcore client: fix a double Read Lock bug in fn Client::logs() (#11172)
|
||||
* Aura: Report malice on sibling blocks from the same validator (#11160)
|
||||
* Change how RPCs eth_call and eth_estimateGas handle "Pending" (#11127)
|
||||
* Cleanup stratum a bit (#11161)
|
||||
* [keccak-hasher]: rust2018 (#11163)
|
||||
* Upgrade to jsonrpc v14 (#11151)
|
||||
* Secret store: fix Instant::now() related race in net_keep_alive (#11155)
|
||||
* RPC method for clearing the engine signer (#10920)
|
||||
* Use TryFrom instead of From+panic for Builtin (#11140)
|
||||
* Fix sccache statistics (#11145)
|
||||
* Update ethereum types to 0.8.0 version (#11139)
|
||||
* [json]: add docs to `hardfork specification` (#11138)
|
||||
* ServiceTransactionChecker::refresh_cache: allow registrar unavailable (#11126)
|
||||
* Fix some random typos, formatting/whitespace (#11128)
|
||||
* Refactor parity_listStorageKeys with count parameter optional (#11124)
|
||||
* Make EIP712Domain Fields Optional (#11103)
|
||||
* EIP-712: bump version in prep for publishing (#11106)
|
||||
* move StateResult to `common-types` (#11121)
|
||||
* Deduplicate registrar contract & calling logic (#11110)
|
||||
* Refactor return type of `BlockChainClient::code` #7098 (#11102)
|
||||
* Switching sccache from local to Redis (#10971)
|
||||
* SIMD Implementation for EIP-152 (#11056)
|
||||
* Fix deprecated trait objects without an explicit `dyn` (#11112)
|
||||
* [spec] fix rinkeby spec (#11108)
|
||||
* Update to latest jsonrpc (#11111)
|
||||
* use images from our registry (#11105)
|
||||
* Correct EIP-712 encoding (#11092)
|
||||
* [CI] check evmbin build (#11096)
|
||||
* Update `kvdb`, `kvdb-rocksdb` and `h2` (#11091)
|
||||
* [client]: Fix for incorrectly dropped consensus messages (#11082) (#11086)
|
||||
* Update JSON tests to d4f86ecf4aa7c (#11054)
|
||||
* fix(network): typo (#11088)
|
||||
* [ethash] remove manual unrolling (#11069)
|
||||
* ethcore/res: activate Istanbul on Ropsten, Görli, Rinkeby, Kovan (#11068)
|
||||
* [sync]: rust 2018 (#11067)
|
||||
* [ethcore]: move client test types to test-helpers (#11062)
|
||||
* [sync]: remove unused dependencies or make dev (#11061)
|
||||
* [ethcore]: reduce re-exports (#11059)
|
||||
* [evmbin] fix time formatting (#11060)
|
||||
* Update hardcoded headers (foundation, classic, kovan, xdai, ewc, ...) (#11053)
|
||||
* cargo update -p eth-secp256k1 (#11052)
|
||||
* ethcore: remove `test-helper feat` from build (#11047)
|
||||
* Include test-helpers from ethjson (#11045)
|
||||
* [ethcore]: cleanup dependencies (#11043)
|
||||
* add more tx tests (#11038)
|
||||
* Fix parallel transactions race-condition (#10995)
|
||||
* [ethcore]: make it compile without `test-helpers` feature (#11036)
|
||||
* Benchmarks for block verification (#11035)
|
||||
* Move snapshot related traits to their proper place (#11012)
|
||||
* cleanup json crate (#11027)
|
||||
* [spec] add istanbul test spec (#11033)
|
||||
* [json-spec] make blake2 pricing spec more readable (#11034)
|
||||
* Add blake2_f precompile (#11017)
|
||||
* Add new line after writing block to hex file. (#10984)
|
||||
* fix: remove unused error-chain (#11028)
|
||||
* fix: remove needless use of itertools (#11029)
|
||||
* Convert `std::test` benchmarks to use Criterion (#10999)
|
||||
* Fix block detail updating (#11015)
|
||||
* [trace] introduce trace failed to Ext (#11019)
|
||||
* cli: update usage and version headers (#10924)
|
||||
* [private-tx] remove unused rand (#11024)
|
||||
* Extract snapshot to own crate (#11010)
|
||||
* Edit publish-onchain.sh to use https (#11016)
|
||||
* EIP 1108: Reduce alt_bn128 precompile gas costs (#11008)
|
||||
* Fix deadlock in `network-devp2p` (#11013)
|
||||
* Implement EIP-1283 reenable transition, EIP-1706 and EIP-2200 (#10191)
|
||||
* EIP 1884 Re-pricing of trie-size dependent operations (#10992)
|
||||
* xDai chain support and nodes list update (#10989)
|
||||
* [trace] check mem diff within range (#11002)
|
||||
* EIP-1344 Add CHAINID op-code (#10983)
|
||||
* Make ClientIoMessage generic over the Client (#10981)
|
||||
* bump spin to 0.5.2 (#10996)
|
||||
* fix compile warnings (#10993)
|
||||
* Fix compilation on recent nightlies (#10991)
|
||||
* [ipfs] Convert to edition 2018 (#10979)
|
||||
* Extract spec to own crate (#10978)
|
||||
* EIP 2028: transaction gas lowered from 68 to 16 (#10987)
|
||||
* Extract engines to own crates (#10966)
|
||||
* Configuration map of block reward contract addresses (#10875)
|
||||
* Add a 2/3 quorum option to Authority Round. (#10909)
|
||||
* Fix rlp decode for inline trie nodes. (#10980)
|
||||
* Private contract migration and offchain state sync (#10748)
|
||||
* manual publish jobs for releases, no changes for nightlies (#10977)
|
||||
* Extract the Engine trait (#10958)
|
||||
* Better error message for rpc gas price errors (#10931)
|
||||
* [.gitlab.yml] cargo check ethcore benches (#10965)
|
||||
* Verify transaction against its block during import (#10954)
|
||||
* [evmbin] fix compilation (#10976)
|
||||
* Update to latest trie version. (#10972)
|
||||
* [blooms-db] Fix benchmarks (#10974)
|
||||
* Fix ethcore/benches build. (#10964)
|
||||
* tx-pool: accept local tx with higher gas price when pool full (#10901)
|
||||
* Disable unsyncable expanse chain (#10926)
|
||||
* Extract Machine from ethcore (#10949)
|
||||
* removed redundant state_root function from spec, improve spec error types (#10955)
|
||||
* Add support for Energy Web Foundation's new chains (#10957)
|
||||
* [evmbin] add more tests to main.rs (#10956)
|
||||
* Fix compiler warnings in util/io and upgrade to edition 2018 Upgrade mio to latest (#10953)
|
||||
* unify loading spec && further spec cleanups (#10948)
|
||||
* refactor: Refactor evmbin CLI (#10742)
|
||||
* journaldb changes (#10929)
|
||||
* Allow default block parameter to be blockHash (#10932)
|
||||
* Enable sealing when engine is ready (#10938)
|
||||
* Fix some warnings and typos. (#10941)
|
||||
* Updated security@parity.io key (#10939)
|
||||
* Change the return type of step_inner function. (#10940)
|
||||
* get rid of hidden mutability of Spec (#10904)
|
||||
* simplify BlockReward::reward implementation (#10906)
|
||||
* Kaspersky AV whitelisting (#10919)
|
||||
* additional arithmetic EVM opcode benchmarks (#10916)
|
||||
* [Cargo.lock] cargo update -p crossbeam-epoch (#10921)
|
||||
* Fixes incorrect comment. (#10913)
|
||||
* Add file path to disk map write/read warnings (#10911)
|
||||
* remove verify_transaction_unordered from engine (#10891)
|
||||
* Avast whitelist script (#10900)
|
||||
* cleanup ethcore ethereum module (#10899)
|
||||
* Move more types out of ethcore (#10880)
|
||||
* return block nonce when engine is clique (#10892)
|
||||
* TransactionQueue::import accepts iterator (#10889)
|
||||
* rename is_pruned to is_prunable (#10888)
|
||||
* simplify create_address_scheme (#10890)
|
||||
* Move DatabaseExtras back to trace (#10868)
|
||||
* Update README.md and Changelogs (#10866)
|
||||
* whisper is no longer a part of parity-ethereum repo (#10855)
|
||||
* [ethash] remove mem::uninitialized (#10861)
|
||||
* Docker images renaming (#10863)
|
||||
* Move the substate module into ethcore/executive (#10867)
|
||||
* Run cargo fix on a few of the worst offenders (#10854)
|
||||
* removed redundant fork choice abstraction (#10849)
|
||||
* Extract state-db from ethcore (#10858)
|
||||
* Fix fork choice (#10837)
|
||||
* Move more code into state-account (#10840)
|
||||
* Remove compiler warning (#10865)
|
||||
* [ethash] use static_assertions crate (#10860)
|
||||
* EIP-1702: Generalized Account Versioning Scheme (#10771)
|
||||
* ethcore-builtin (#10850)
|
||||
* removed QueueError type (#10852)
|
||||
* removed unused macros (#10851)
|
||||
* bump crossbeam (#10848)
|
||||
* removed unused trait PrivateNotify and unused Error types (#10847)
|
||||
* make fn submit_seal more idiomatic (#10843)
|
||||
* update parking-lot to 0.8 (#10845)
|
||||
* Update version to 2.7.0 (#10846)
|
||||
* update jsonrpc to 12.0 (#10841)
|
||||
* Improve logging and cleanup in miner around block sealing (#10745)
|
||||
* Extract AccountDB to account-db (#10839)
|
||||
* test: Update Whisper test for invalid pool size (#10811)
|
||||
* Extricate PodAccount and state Account to own crates (#10838)
|
||||
* logs (#10817)
|
||||
* refactor: whisper: Add type aliases and update rustdocs in message.rs (#10812)
|
||||
* Break circular dependency between Client and Engine (part 1) (#10833)
|
||||
* tests: Relates to #10655: Test instructions for Readme (#10835)
|
||||
* refactor: Related #9459 - evmbin: replace untyped json! macro with fully typed serde serialization using Rust structs (#10657)
|
||||
* idiomatic changes to PodState (#10834)
|
||||
* Allow --nat extip:your.host.here.org (#10830)
|
||||
* When updating the client or when called from RPC, sleep should mean sleep (#10814)
|
||||
* Remove excessive warning (#10831)
|
||||
* Fix typo in README.md (#10828)
|
||||
* ethcore does not use byteorder (#10829)
|
||||
* Better logging when backfilling ancient blocks fail (#10796)
|
||||
* depends: Update wordlist to v1.3 (#10823)
|
||||
* cargo update -p smallvec (#10822)
|
||||
* replace memzero with zeroize crate (#10816)
|
||||
* Don't repeat the logic from Default impl (#10813)
|
||||
* removed additional_params method (#10818)
|
||||
* Add Constantinople eips to the dev (instant_seal) config (#10809)
|
||||
* removed redundant fmt::Display implementations (#10806)
|
||||
* revert changes to .gitlab-ci.yml (#10807)
|
||||
* Add filtering capability to `parity_pendingTransactions` (issue 8269) (#10506)
|
||||
* removed EthEngine alias (#10805)
|
||||
* wait a bit longer in should_check_status_of_request_when_its_resolved (#10808)
|
||||
* Do not drop the peer with None difficulty (#10772)
|
||||
* ethcore-bloom-journal updated to 2018 (#10804)
|
||||
* ethcore-light uses bincode 1.1 (#10798)
|
||||
* Fix a few typos and unused warnings. (#10803)
|
||||
* updated project to ansi_term 0.11 (#10799)
|
||||
* added new ropsten-bootnode and removed old one (#10794)
|
||||
* updated price-info to edition 2018 (#10801)
|
||||
* ethcore-network-devp2p uses igd 0.9 (#10797)
|
||||
* updated parity-local-store to edition 2018 and removed redundant Error type (#10800)
|
||||
* Cleanup unused vm dependencies (#10787)
|
||||
* Removed redundant ethcore-service error type (#10788)
|
||||
* Removed machine abstraction from ethcore (#10791)
|
||||
* Updated blooms-db to rust 2018 and removed redundant deps (#10785)
|
||||
* ethkey no longer uses byteorder (#10786)
|
||||
* Log validator set changes in EpochManager (#10734)
|
||||
* Treat empty account the same as non-exist accounts in EIP-1052 (#10775)
|
||||
* docs: Update Readme with TOC, Contributor Guideline. Update Cargo package descriptions (#10652)
|
||||
* Move Engine::register_client to be before other I/O handler registration (#10767)
|
||||
* Print warnings when using dangerous settings for ValidatorSet (#10733)
|
||||
* ethcore/res: activate atlantis classic hf on block 8772000 (#10766)
|
||||
* refactor: Fix indentation (#10740)
|
||||
* Updated Bn128PairingImpl to use optimized batch pairing (#10765)
|
||||
* fix: aura don't add `SystemTime::now()` (#10720)
|
||||
* Initialize private tx logger only if private tx functionality is enabled (#10758)
|
||||
* Remove unused code (#10762)
|
||||
* Remove calls to heapsize (#10432)
|
||||
* [devp2p] Update to 2018 edition (#10716)
|
||||
* Add a way to signal shutdown to snapshotting threads (#10744)
|
||||
* Enable aesni (#10756)
|
||||
* remove support of old SS db formats (#10757)
|
||||
* [devp2p] Don't use `rust-crypto` (#10714)
|
||||
* updater: fix static id hashes initialization (#10755)
|
||||
* Use fewer threads for snapshotting (#10752)
|
||||
* Die error_chain, die (#10747)
|
||||
* Fix deprectation warnings on nightly (#10746)
|
||||
* fix docker tags for publishing (#10741)
|
||||
* DevP2p: Get node IP address and udp port from Socket, if not included in PING packet (#10705)
|
||||
* ethcore: enable ECIP-1054 for classic (#10731)
|
||||
* Stop breaking out of loop if a non-canonical hash is found (#10729)
|
||||
* Refactor Clique stepping (#10691)
|
||||
* Use RUSTFLAGS to set the optimization level (#10719)
|
||||
* SecretStore: non-blocking wait of session completion (#10303)
|
||||
* removed secret_store folder (#10722)
|
||||
* SecretStore: expose restore_key_public in HTTP API (#10241)
|
||||
* Revert "enable lto for release builds (#10717)" (#10721)
|
||||
* enable lto for release builds (#10717)
|
||||
* Merge `Notifier` and `TransactionsPoolNotifier` (#10591)
|
||||
* [devp2p] Fix warnings and re-org imports (#10710)
|
||||
* Upgrade ethereum types (#10670)
|
||||
* introduce MissingParent Error, fixes #10699 (#10700)
|
||||
* Update publishing (#10644)
|
||||
* Upgrade to parity-crypto 0.4 (#10650)
|
||||
* new image (#10673)
|
||||
* Add SealingState; don't prepare block when not ready. (#10529)
|
||||
* Fix compiler warning (that will become an error) (#10683)
|
||||
* add_sync_notifier in EthPubSubClient holds on to a Client for too long (#10689)
|
||||
* Don't panic if extra_data is longer than VANITY_LENGTH (#10682)
|
||||
* docs: evmbin - Update Rust docs (#10658)
|
||||
* Remove annoying compiler warnings (#10679)
|
||||
* Reset blockchain properly (#10669)
|
||||
* Remove support for hardware wallets (#10678)
|
||||
* [CI] allow cargo audit to fail (#10676)
|
||||
* docs: Add ProgPoW Rust docs to ethash module (#10653)
|
||||
* fix: Move PR template into .github/ folder (#10663)
|
||||
* docs: Add PR template (#10654)
|
||||
* Trivial journal for private transactions (#10056)
|
||||
* fix(compilation warnings) (#10649)
|
||||
* [whisper] Move needed aes_gcm crypto in-crate (#10647)
|
||||
* Adds parity_getRawBlockByNumber, parity_submitRawBlock (#10609)
|
||||
* Fix rinkeby petersburg fork (#10632)
|
||||
* ci: publish docs debug (#10638)
|
||||
* Fix publish docs (#10635)
|
||||
* Update kovan.json to switch validator set to POA Consensus Contracts (#10628)
|
||||
* [ethcore] remove error_chain (#10616)
|
||||
* Remove unused import (#10615)
|
||||
* evm: add some mulmod benches (#10600)
|
||||
* Clique: zero-fill extradata when the supplied value is less than 32 bytes in length (#10605)
|
||||
* Constantinople HF on POA Core (#10606)
|
||||
* adds rpc error message for --no-ancient-blocks (#10608)
|
||||
* Allow CORS requests in Secret Store API (#10584)
|
||||
* update bootnodes (#10595)
|
||||
* sccache logs to stdout (#10596)
|
||||
* fix(whisper expiry): current time + work + ttl (#10587)
|
||||
* CI improvements (#10579)
|
||||
* Watch transactions pool (#10558)
|
||||
* fix(evmbin): make benches compile again (#10586)
|
||||
* fix issue with compilation when 'slow-blocks' feature enabled (#10585)
|
||||
* Reject crazy timestamps instead of truncating. (#10574)
|
||||
* Node table limiting and cache for node filter (#10288)
|
||||
* fix(light cull): poll light cull instead of timer (#10559)
|
||||
* Update Issue Template to direct security issue to email (#10562)
|
||||
* RPC: Implements eth_subscribe("syncing") (#10311)
|
||||
* Explicitly enable or disable Stratum in config file (Issue 9785) (#10521)
|
||||
* version: bump master to 2.6 (#10560)
|
||||
@@ -1,122 +0,0 @@
|
||||
## OpenEthereum [v3.0.1](https://github.com/openethereum/openethereum/releases/tag/v3.0.1)
|
||||
- Add missing forks to fork ID (#11747)
|
||||
|
||||
## OpenEthereum [v3.0.0](https://github.com/openethereum/openethereum/releases/tag/v3.0.0)
|
||||
|
||||
OpenEthereum v3.0.0 is the first release of OpenEthereum client as part of OpenEthereum project, divested from Parity Technologies.
|
||||
|
||||
This release marks the transition from Parity Technologies infrastructure and bootnodes to the one managed by OpenEthereum project.
|
||||
|
||||
Parity-Ethereum v2.7 users can continue using their existing data folders. Command-line interface has also stayed identical.
|
||||
Unless specified otherwise, OpenEthereum v3.0.0 will detect if the user's database in the old Parity-Ethereum default path,
|
||||
and only if it's not found will write to the new default location.
|
||||
|
||||
This release includes several major improvements to network and database stack:
|
||||
- Support for `eth/64` protocol and Node Discovery v4 `Ethereum Node Records` extension.
|
||||
- Accounts bloom is removed which should decrease the database size.
|
||||
|
||||
**Due to database changes this is a one-way upgrade. Please back up your database if you plan to continue using Parity-Ethereum v2.7.2.**
|
||||
|
||||
Note that this release drops support for IPFS and `eth/62` protocol. Additionally, it marks light client, private transactions and updater as deprecated features which may be removed in a future release.
|
||||
|
||||
The full list of included changes:
|
||||
- Add deprecation warnings (#11682)
|
||||
- Add Curl to Docker image (#11687)
|
||||
- v3 release version strings and track stable (#11680)
|
||||
- Fix sccache server errors (#11675)
|
||||
- Don't delete old db after migration (#11662)
|
||||
- rename inject to drain_transaction_overlay (#11657)
|
||||
- Drain the transaction overlay (#11654)
|
||||
- vergen library seems to depend not only on the .git folder content but also on the git binary (#11651)
|
||||
- New default paths (#11641)
|
||||
- Update EWF's chains with Istanbul transition block numbers (#11482)
|
||||
- add openethereum supplementary bootnodes, those are not active right now, but will be in case the network needs more power (#11650)
|
||||
- Remove Parity bootnodes (#11644)
|
||||
- Remove accounts bloom (#11589)
|
||||
- Deploy docker images on master (#11640)
|
||||
- Fix some compiler warnings (#11632)
|
||||
- Add support for non-fork side of Phoenix (#11627)
|
||||
- validate mainnet specs against all forks (#11625)
|
||||
- Fix ecrecover builtin (#11623)
|
||||
- Update .gitmodules (#11628)
|
||||
- ethcore/res: activate ecip-1088 phoenix on classic (#11598)
|
||||
- Upgrade parity-common deps to latest (#11620)
|
||||
- Fix Goerli syncing (#11604)
|
||||
- deps: switch to upstream ctrlc (#11617)
|
||||
- Deduplicating crate dependencies (part 3 of n) (#11614)
|
||||
- Deduplicating crate dependencies (part 2 of n, `slab`) (#11613)
|
||||
- Actually save ENR on creation and modification (#11602)
|
||||
- Activate POSDAO on xDai chain and update bootnodes (#11610)
|
||||
- Activate on-chain randomness in POA Core (#11609)
|
||||
- Deduplicating crate dependencies (part 1 of n) (#11606)
|
||||
- Update enodes for POA Sokol (#11611)
|
||||
- Remove .git folder from dogerignore file so vergen library can get build date and commit hash in the binary generatio vergen library can get build date and commit hash in the binary generation (#11608)
|
||||
- Reduced gas cost for static calls made to precompiles EIP2046/1352 (#11583)
|
||||
- `ethcore-bloom-journal` was renamed to `accounts-bloom` (#11605)
|
||||
- Use serde_json to export hardcoded sync (#11601)
|
||||
- Node Discovery v4 ENR Extension (EIP-868) (#11540)
|
||||
- Fix compile warnings (#11595)
|
||||
- Update version to 3.0.0-alpha.1 (#11592)
|
||||
- ethcore/res: bump canon fork hash for mordor and kotti testnets (#11584)
|
||||
- Update on push tags (#11590)
|
||||
- Replace deprecated tempdir dependency with tempfile (#11588)
|
||||
- Fix project name, links, rename the binaries (#11580)
|
||||
- Update Cargo.lock (#11573)
|
||||
- ci: workaround for the cache bug on Linux (#11568)
|
||||
- Increase the default pruning parameters (#11558)
|
||||
- Add Docker build and push to github actions (#11555)
|
||||
- Update README (#11578)
|
||||
- informant: display I/O stats (#11523)
|
||||
- [devp2p discovery]: remove `deprecated_echo_hash` (#11564)
|
||||
- [secretstore] create db_version file when database doesn't exist (#11570)
|
||||
- Remove Parity's Security Policy (#11565)
|
||||
- ethcore/res: enable ecip-1088 phoenix upgrade for kotti and mordor testnets (#11529)
|
||||
- Misc docs and renames …and one less clone (#11556)
|
||||
- [secretstore]: don't sign message with only zeroes (#11561)
|
||||
- [devp2p discovery]: cleanup (#11547)
|
||||
- Code cleanup in the sync module (#11552)
|
||||
- initial cleanup (#11542)
|
||||
- Warn if genesis constructor revert (#11550)
|
||||
- ethcore: cleanup after #11531 (#11546)
|
||||
- license update (#11543)
|
||||
- Less cloning when importing blocks (#11531)
|
||||
- Github Actions (#11528)
|
||||
- Fix Alpine Dockerfile (#11538)
|
||||
- Remove AuxiliaryData/AuxiliaryRequest (#11533)
|
||||
- [journaldb]: cleanup (#11534)
|
||||
- Remove references to parity-ethereum (#11525)
|
||||
- Drop IPFS support (#11532)
|
||||
- chain-supplier: fix warning reporting for GetNodeData request (#11530)
|
||||
- Faster kill_garbage (#11514)
|
||||
- [EngineSigner]: don't sign message with only zeroes (#11524)
|
||||
- fix compilation warnings (#11522)
|
||||
- [ethcore cleanup]: various unrelated fixes from `#11493` (#11507)
|
||||
- Add benchmark for transaction execution (#11509)
|
||||
- Add Smart Contract License v1.0
|
||||
- Misc fixes (#11510)
|
||||
- [dependencies]: unify `rustc-hex` (#11506)
|
||||
- Activate on-chain randomness in POA Sokol (#11505)
|
||||
- Grab bag of cleanup (#11504)
|
||||
- Implement eth/64 (EIP-2364) and drop support for eth/62 (#11472)
|
||||
- [dependencies]: remove `util/macros` (#11501)
|
||||
- OpenEthereum bootnodes are added (#11499)
|
||||
- [ci benches]: use `all-features` (#11496)
|
||||
- [verification]: make test-build compile standalone (#11495)
|
||||
- complete null-signatures removal (#11491)
|
||||
- Include the seal when populating the header for a new block (#11475)
|
||||
- fix compilation warnings (#11492)
|
||||
- cargo update -p cmake (#11490)
|
||||
- update to published rlp-derive (#11489)
|
||||
- Switch usage of Secret Store to the external lib (#11487)
|
||||
- Switch from the internal runtime lib to external one from crates.io (#11480)
|
||||
- Update params.rs (#11474)
|
||||
- weak_counts has been stabilized (#11476)
|
||||
- sync: remove broken eth_protocol_version method (#11473)
|
||||
- Use parity-crypto updated to use upstream rust-secp256k1 (#11406)
|
||||
- Cleanup some code in Aura (#11466)
|
||||
- upgrade some of the dependencies (#11467)
|
||||
- Some more release track changes to README.md (#11465)
|
||||
- Update simple one-line installer due to switching to a single stable release track (#11463)
|
||||
- update Dockerfile (#11461)
|
||||
- Implement EIP-2124 (#11456)
|
||||
- [eth classic chainspec]: remove `balance = 1` (#11459)
|
||||
@@ -1,5 +1,4 @@
|
||||
[package]
|
||||
description = "Parity Ethereum Ethash & ProgPoW Implementations"
|
||||
name = "ethash"
|
||||
version = "1.12.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
@@ -11,25 +10,11 @@ ethereum-types = "0.4"
|
||||
keccak-hash = "0.1"
|
||||
log = "0.4"
|
||||
memmap = "0.6"
|
||||
parking_lot = "0.7"
|
||||
parking_lot = "0.6"
|
||||
primal = "0.2.3"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.2"
|
||||
rustc-hex = "1.0"
|
||||
serde_json = "1.0"
|
||||
tempdir = "0.3"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
bench = []
|
||||
|
||||
[[bench]]
|
||||
name = "basic"
|
||||
harness = false
|
||||
required-features = ['bench']
|
||||
|
||||
[[bench]]
|
||||
name = "progpow"
|
||||
harness = false
|
||||
required-features = ['bench']
|
||||
benches = []
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#[macro_use]
|
||||
extern crate criterion;
|
||||
extern crate ethash;
|
||||
|
||||
use criterion::Criterion;
|
||||
use ethash::{NodeCacheBuilder, OptimizeFor};
|
||||
|
||||
const HASH: [u8; 32] = [
|
||||
0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, 0x5b, 0xf2, 0xbe, 0xe4, 0x0a, 0xb3, 0x35, 0x8a,
|
||||
0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f, 0x5e, 0x59, 0x5e, 0xab, 0x55, 0x94, 0x05, 0x52, 0x7d, 0x72,
|
||||
];
|
||||
const NONCE: u64 = 0xd7b3ac70a301a249;
|
||||
|
||||
criterion_group!(
|
||||
basic,
|
||||
bench_light_compute_memmap,
|
||||
bench_light_compute_memory,
|
||||
bench_light_new_round_trip_memmap,
|
||||
bench_light_new_round_trip_memory,
|
||||
bench_light_from_file_round_trip_memory,
|
||||
bench_light_from_file_round_trip_memmap
|
||||
);
|
||||
criterion_main!(basic);
|
||||
|
||||
fn bench_light_compute_memmap(b: &mut Criterion) {
|
||||
use std::env;
|
||||
|
||||
let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value());
|
||||
let light = builder.light(&env::temp_dir(), 486382);
|
||||
|
||||
b.bench_function("bench_light_compute_memmap", move |b| {
|
||||
b.iter(|| light.compute(&HASH, NONCE, u64::max_value()))
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_light_compute_memory(b: &mut Criterion) {
|
||||
use std::env;
|
||||
|
||||
let builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value());
|
||||
let light = builder.light(&env::temp_dir(), 486382);
|
||||
|
||||
b.bench_function("bench_light_compute_memmap", move |b| {
|
||||
b.iter(|| light.compute(&HASH, NONCE, u64::max_value()))
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_light_new_round_trip_memmap(b: &mut Criterion) {
|
||||
use std::env;
|
||||
|
||||
b.bench_function("bench_light_compute_memmap", move |b| {
|
||||
b.iter(|| {
|
||||
let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value());
|
||||
let light = builder.light(&env::temp_dir(), 486382);
|
||||
light.compute(&HASH, NONCE, u64::max_value());
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_light_new_round_trip_memory(b: &mut Criterion) {
|
||||
use std::env;
|
||||
|
||||
b.bench_function("bench_light_compute_memmap", move |b| {
|
||||
b.iter(|| {
|
||||
let builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value());
|
||||
let light = builder.light(&env::temp_dir(), 486382);
|
||||
light.compute(&HASH, NONCE, u64::max_value());
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_light_from_file_round_trip_memory(b: &mut Criterion) {
|
||||
use std::env;
|
||||
|
||||
let dir = env::temp_dir();
|
||||
let height = 486382;
|
||||
{
|
||||
let builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value());
|
||||
let mut dummy = builder.light(&dir, height);
|
||||
dummy.to_file().unwrap();
|
||||
}
|
||||
|
||||
b.bench_function("bench_light_compute_memmap", move |b| {
|
||||
b.iter(|| {
|
||||
let builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value());
|
||||
let light = builder.light_from_file(&dir, 486382).unwrap();
|
||||
light.compute(&HASH, NONCE, u64::max_value());
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_light_from_file_round_trip_memmap(b: &mut Criterion) {
|
||||
use std::env;
|
||||
|
||||
let dir = env::temp_dir();
|
||||
let height = 486382;
|
||||
|
||||
{
|
||||
let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value());
|
||||
let mut dummy = builder.light(&dir, height);
|
||||
dummy.to_file().unwrap();
|
||||
}
|
||||
|
||||
b.bench_function("bench_light_compute_memmap", move |b| {
|
||||
b.iter(|| {
|
||||
let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value());
|
||||
let light = builder.light_from_file(&dir, 486382).unwrap();
|
||||
light.compute(&HASH, NONCE, u64::max_value());
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
#[macro_use]
|
||||
extern crate criterion;
|
||||
extern crate ethash;
|
||||
extern crate rustc_hex;
|
||||
extern crate tempdir;
|
||||
|
||||
use criterion::Criterion;
|
||||
use ethash::progpow;
|
||||
|
||||
use ethash::{compute::light_compute, NodeCacheBuilder, OptimizeFor};
|
||||
use rustc_hex::FromHex;
|
||||
use tempdir::TempDir;
|
||||
|
||||
fn bench_hashimoto_light(c: &mut Criterion) {
|
||||
let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value());
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let light = builder.light(&tempdir.path(), 1);
|
||||
let h = FromHex::from_hex("c9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f")
|
||||
.unwrap();
|
||||
let mut hash = [0; 32];
|
||||
hash.copy_from_slice(&h);
|
||||
|
||||
c.bench_function("hashimoto_light", move |b| {
|
||||
b.iter(|| light_compute(&light, &hash, 0))
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_progpow_light(c: &mut Criterion) {
|
||||
let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value());
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let cache = builder.new_cache(tempdir.into_path(), 0);
|
||||
|
||||
let h = FromHex::from_hex("c9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f")
|
||||
.unwrap();
|
||||
let mut hash = [0; 32];
|
||||
hash.copy_from_slice(&h);
|
||||
|
||||
c.bench_function("progpow_light", move |b| {
|
||||
b.iter(|| {
|
||||
let c_dag = progpow::generate_cdag(cache.as_ref());
|
||||
progpow::progpow(hash, 0, 0, cache.as_ref(), &c_dag);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_progpow_optimal_light(c: &mut Criterion) {
|
||||
let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value());
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let cache = builder.new_cache(tempdir.into_path(), 0);
|
||||
let c_dag = progpow::generate_cdag(cache.as_ref());
|
||||
|
||||
let h = FromHex::from_hex("c9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f")
|
||||
.unwrap();
|
||||
let mut hash = [0; 32];
|
||||
hash.copy_from_slice(&h);
|
||||
|
||||
c.bench_function("progpow_optimal_light", move |b| {
|
||||
b.iter(|| {
|
||||
progpow::progpow(hash, 0, 0, cache.as_ref(), &c_dag);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_keccak_f800_long(c: &mut Criterion) {
|
||||
c.bench_function("keccak_f800_long(0, 0, 0)", |b| {
|
||||
b.iter(|| progpow::keccak_f800_long([0; 32], 0, [0; 8]))
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
bench_hashimoto_light,
|
||||
bench_progpow_light,
|
||||
bench_progpow_optimal_light,
|
||||
bench_keccak_f800_long,
|
||||
);
|
||||
criterion_main!(benches);
|
||||
@@ -1,86 +0,0 @@
|
||||
[
|
||||
[
|
||||
0,
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0000000000000000",
|
||||
"faeb1be51075b03a4ff44b335067951ead07a3b078539ace76fd56fc410557a3",
|
||||
"63155f732f2bf556967f906155b510c917e48e99685ead76ea83f4eca03ab12b"
|
||||
],
|
||||
[
|
||||
49,
|
||||
"63155f732f2bf556967f906155b510c917e48e99685ead76ea83f4eca03ab12b",
|
||||
"0000000006ff2c47",
|
||||
"c789c1180f890ec555ff42042913465481e8e6bc512cb981e1c1108dc3f2227d",
|
||||
"9e7248f20914913a73d80a70174c331b1d34f260535ac3631d770e656b5dd922"
|
||||
],
|
||||
[
|
||||
50,
|
||||
"9e7248f20914913a73d80a70174c331b1d34f260535ac3631d770e656b5dd922",
|
||||
"00000000076e482e",
|
||||
"c7340542c2a06b3a7dc7222635f7cd402abf8b528ae971ddac6bbe2b0c7cb518",
|
||||
"de37e1824c86d35d154cf65a88de6d9286aec4f7f10c3fc9f0fa1bcc2687188d"
|
||||
],
|
||||
[
|
||||
99,
|
||||
"de37e1824c86d35d154cf65a88de6d9286aec4f7f10c3fc9f0fa1bcc2687188d",
|
||||
"000000003917afab",
|
||||
"f5e60b2c5bfddd136167a30cbc3c8dbdbd15a512257dee7964e0bc6daa9f8ba7",
|
||||
"ac7b55e801511b77e11d52e9599206101550144525b5679f2dab19386f23dcce"
|
||||
],
|
||||
[
|
||||
29950,
|
||||
"ac7b55e801511b77e11d52e9599206101550144525b5679f2dab19386f23dcce",
|
||||
"005d409dbc23a62a",
|
||||
"07393d15805eb08ee6fc6cb3ad4ad1010533bd0ff92d6006850246829f18fd6e",
|
||||
"e43d7e0bdc8a4a3f6e291a5ed790b9fa1a0948a2b9e33c844888690847de19f5"
|
||||
],
|
||||
[
|
||||
29999,
|
||||
"e43d7e0bdc8a4a3f6e291a5ed790b9fa1a0948a2b9e33c844888690847de19f5",
|
||||
"005db5fa4c2a3d03",
|
||||
"7551bddf977491da2f6cfc1679299544b23483e8f8ee0931c4c16a796558a0b8",
|
||||
"d34519f72c97cae8892c277776259db3320820cb5279a299d0ef1e155e5c6454"
|
||||
],
|
||||
[
|
||||
30000,
|
||||
"d34519f72c97cae8892c277776259db3320820cb5279a299d0ef1e155e5c6454",
|
||||
"005db8607994ff30",
|
||||
"f1c2c7c32266af9635462e6ce1c98ebe4e7e3ecab7a38aaabfbf2e731e0fbff4",
|
||||
"8b6ce5da0b06d18db7bd8492d9e5717f8b53e7e098d9fef7886d58a6e913ef64"
|
||||
],
|
||||
[
|
||||
30049,
|
||||
"8b6ce5da0b06d18db7bd8492d9e5717f8b53e7e098d9fef7886d58a6e913ef64",
|
||||
"005e2e215a8ca2e7",
|
||||
"57fe6a9fbf920b4e91deeb66cb0efa971e08229d1a160330e08da54af0689add",
|
||||
"c2c46173481b9ced61123d2e293b42ede5a1b323210eb2a684df0874ffe09047"
|
||||
],
|
||||
[
|
||||
30050,
|
||||
"c2c46173481b9ced61123d2e293b42ede5a1b323210eb2a684df0874ffe09047",
|
||||
"005e30899481055e",
|
||||
"ba30c61cc5a2c74a5ecaf505965140a08f24a296d687e78720f0b48baf712f2d",
|
||||
"ea42197eb2ba79c63cb5e655b8b1f612c5f08aae1a49ff236795a3516d87bc71"
|
||||
],
|
||||
[
|
||||
30099,
|
||||
"ea42197eb2ba79c63cb5e655b8b1f612c5f08aae1a49ff236795a3516d87bc71",
|
||||
"005ea6aef136f88b",
|
||||
"cfd5e46048cd133d40f261fe8704e51d3f497fc14203ac6a9ef6a0841780b1cd",
|
||||
"49e15ba4bf501ce8fe8876101c808e24c69a859be15de554bf85dbc095491bd6"
|
||||
],
|
||||
[
|
||||
59950,
|
||||
"49e15ba4bf501ce8fe8876101c808e24c69a859be15de554bf85dbc095491bd6",
|
||||
"02ebe0503bd7b1da",
|
||||
"21511fbaa31fb9f5fc4998a754e97b3083a866f4de86fa7500a633346f56d773",
|
||||
"f5c50ba5c0d6210ddb16250ec3efda178de857b2b1703d8d5403bd0f848e19cf"
|
||||
],
|
||||
[
|
||||
59999,
|
||||
"f5c50ba5c0d6210ddb16250ec3efda178de857b2b1703d8d5403bd0f848e19cf",
|
||||
"02edb6275bd221e3",
|
||||
"653eda37d337e39d311d22be9bbd3458d3abee4e643bee4a7280a6d08106ef98",
|
||||
"341562d10d4afb706ec2c8d5537cb0c810de02b4ebb0a0eea5ae335af6fb2e88"
|
||||
]
|
||||
]
|
||||
@@ -1,313 +1,304 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use compute::Light;
|
||||
use either::Either;
|
||||
use keccak::{keccak_512, H256};
|
||||
use keccak::{H256, keccak_512};
|
||||
use memmap::MmapMut;
|
||||
use parking_lot::Mutex;
|
||||
use seed_compute::SeedHashCompute;
|
||||
|
||||
use shared::{epoch, get_cache_size, to_hex, Node, ETHASH_CACHE_ROUNDS, NODE_BYTES, NODE_DWORDS};
|
||||
use shared::{ETHASH_CACHE_ROUNDS, NODE_BYTES, NODE_DWORDS, Node, epoch, get_cache_size, to_hex};
|
||||
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
fs,
|
||||
io::{self, Read, Write},
|
||||
path::{Path, PathBuf},
|
||||
slice,
|
||||
sync::Arc,
|
||||
};
|
||||
use std::borrow::Cow;
|
||||
use std::fs;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::slice;
|
||||
use std::sync::Arc;
|
||||
|
||||
type Cache = Either<Vec<Node>, MmapMut>;
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
||||
pub enum OptimizeFor {
|
||||
Cpu,
|
||||
Memory,
|
||||
Cpu,
|
||||
Memory,
|
||||
}
|
||||
|
||||
impl Default for OptimizeFor {
|
||||
fn default() -> Self {
|
||||
OptimizeFor::Cpu
|
||||
}
|
||||
fn default() -> Self {
|
||||
OptimizeFor::Cpu
|
||||
}
|
||||
}
|
||||
|
||||
fn byte_size(cache: &Cache) -> usize {
|
||||
use self::Either::{Left, Right};
|
||||
use self::Either::{Left, Right};
|
||||
|
||||
match *cache {
|
||||
Left(ref vec) => vec.len() * NODE_BYTES,
|
||||
Right(ref mmap) => mmap.len(),
|
||||
}
|
||||
match *cache {
|
||||
Left(ref vec) => vec.len() * NODE_BYTES,
|
||||
Right(ref mmap) => mmap.len(),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_buffer(path: &Path, num_nodes: usize, ident: &H256, optimize_for: OptimizeFor) -> Cache {
|
||||
let memmap = match optimize_for {
|
||||
OptimizeFor::Cpu => None,
|
||||
OptimizeFor::Memory => make_memmapped_cache(path, num_nodes, ident).ok(),
|
||||
};
|
||||
let memmap = match optimize_for {
|
||||
OptimizeFor::Cpu => None,
|
||||
OptimizeFor::Memory => make_memmapped_cache(path, num_nodes, ident).ok(),
|
||||
};
|
||||
|
||||
memmap
|
||||
.map(Either::Right)
|
||||
.unwrap_or_else(|| Either::Left(make_memory_cache(num_nodes, ident)))
|
||||
memmap.map(Either::Right).unwrap_or_else(|| {
|
||||
Either::Left(make_memory_cache(num_nodes, ident))
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NodeCacheBuilder {
|
||||
// TODO: Remove this locking and just use an `Rc`?
|
||||
seedhash: Arc<Mutex<SeedHashCompute>>,
|
||||
optimize_for: OptimizeFor,
|
||||
progpow_transition: u64,
|
||||
// TODO: Remove this locking and just use an `Rc`?
|
||||
seedhash: Arc<Mutex<SeedHashCompute>>,
|
||||
optimize_for: OptimizeFor,
|
||||
}
|
||||
|
||||
// TODO: Abstract the "optimize for" logic
|
||||
pub struct NodeCache {
|
||||
builder: NodeCacheBuilder,
|
||||
cache_dir: Cow<'static, Path>,
|
||||
cache_path: PathBuf,
|
||||
epoch: u64,
|
||||
cache: Cache,
|
||||
builder: NodeCacheBuilder,
|
||||
cache_dir: Cow<'static, Path>,
|
||||
cache_path: PathBuf,
|
||||
epoch: u64,
|
||||
cache: Cache,
|
||||
}
|
||||
|
||||
impl NodeCacheBuilder {
|
||||
pub fn light(&self, cache_dir: &Path, block_number: u64) -> Light {
|
||||
Light::new_with_builder(self, cache_dir, block_number, self.progpow_transition)
|
||||
}
|
||||
pub fn light(&self, cache_dir: &Path, block_number: u64) -> Light {
|
||||
Light::new_with_builder(self, cache_dir, block_number)
|
||||
}
|
||||
|
||||
pub fn light_from_file(&self, cache_dir: &Path, block_number: u64) -> io::Result<Light> {
|
||||
Light::from_file_with_builder(self, cache_dir, block_number, self.progpow_transition)
|
||||
}
|
||||
pub fn light_from_file(&self, cache_dir: &Path, block_number: u64) -> io::Result<Light> {
|
||||
Light::from_file_with_builder(self, cache_dir, block_number)
|
||||
}
|
||||
|
||||
pub fn new<T: Into<Option<OptimizeFor>>>(optimize_for: T, progpow_transition: u64) -> Self {
|
||||
NodeCacheBuilder {
|
||||
seedhash: Arc::new(Mutex::new(SeedHashCompute::default())),
|
||||
optimize_for: optimize_for.into().unwrap_or_default(),
|
||||
progpow_transition,
|
||||
}
|
||||
}
|
||||
pub fn new<T: Into<Option<OptimizeFor>>>(optimize_for: T) -> Self {
|
||||
NodeCacheBuilder {
|
||||
seedhash: Arc::new(Mutex::new(SeedHashCompute::default())),
|
||||
optimize_for: optimize_for.into().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn block_number_to_ident(&self, block_number: u64) -> H256 {
|
||||
self.seedhash.lock().hash_block_number(block_number)
|
||||
}
|
||||
fn block_number_to_ident(&self, block_number: u64) -> H256 {
|
||||
self.seedhash.lock().hash_block_number(block_number)
|
||||
}
|
||||
|
||||
fn epoch_to_ident(&self, epoch: u64) -> H256 {
|
||||
self.seedhash.lock().hash_epoch(epoch)
|
||||
}
|
||||
fn epoch_to_ident(&self, epoch: u64) -> H256 {
|
||||
self.seedhash.lock().hash_epoch(epoch)
|
||||
}
|
||||
|
||||
pub fn from_file<P: Into<Cow<'static, Path>>>(
|
||||
&self,
|
||||
cache_dir: P,
|
||||
block_number: u64,
|
||||
) -> io::Result<NodeCache> {
|
||||
let cache_dir = cache_dir.into();
|
||||
let ident = self.block_number_to_ident(block_number);
|
||||
pub fn from_file<P: Into<Cow<'static, Path>>>(
|
||||
&self,
|
||||
cache_dir: P,
|
||||
block_number: u64,
|
||||
) -> io::Result<NodeCache> {
|
||||
let cache_dir = cache_dir.into();
|
||||
let ident = self.block_number_to_ident(block_number);
|
||||
|
||||
let path = cache_path(cache_dir.as_ref(), &ident);
|
||||
let path = cache_path(cache_dir.as_ref(), &ident);
|
||||
|
||||
let cache = cache_from_path(&path, self.optimize_for)?;
|
||||
let expected_cache_size = get_cache_size(block_number);
|
||||
let cache = cache_from_path(&path, self.optimize_for)?;
|
||||
let expected_cache_size = get_cache_size(block_number);
|
||||
|
||||
if byte_size(&cache) == expected_cache_size {
|
||||
Ok(NodeCache {
|
||||
builder: self.clone(),
|
||||
epoch: epoch(block_number),
|
||||
cache_dir: cache_dir,
|
||||
cache_path: path,
|
||||
cache: cache,
|
||||
})
|
||||
} else {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"Node cache is of incorrect size",
|
||||
))
|
||||
}
|
||||
}
|
||||
if byte_size(&cache) == expected_cache_size {
|
||||
Ok(NodeCache {
|
||||
builder: self.clone(),
|
||||
epoch: epoch(block_number),
|
||||
cache_dir: cache_dir,
|
||||
cache_path: path,
|
||||
cache: cache,
|
||||
})
|
||||
} else {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"Node cache is of incorrect size",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_cache<P: Into<Cow<'static, Path>>>(
|
||||
&self,
|
||||
cache_dir: P,
|
||||
block_number: u64,
|
||||
) -> NodeCache {
|
||||
let cache_dir = cache_dir.into();
|
||||
let ident = self.block_number_to_ident(block_number);
|
||||
pub fn new_cache<P: Into<Cow<'static, Path>>>(
|
||||
&self,
|
||||
cache_dir: P,
|
||||
block_number: u64,
|
||||
) -> NodeCache {
|
||||
let cache_dir = cache_dir.into();
|
||||
let ident = self.block_number_to_ident(block_number);
|
||||
|
||||
let cache_size = get_cache_size(block_number);
|
||||
let cache_size = get_cache_size(block_number);
|
||||
|
||||
// We use `debug_assert` since it is impossible for `get_cache_size` to return an unaligned
|
||||
// value with the current implementation. If the implementation changes, CI will catch it.
|
||||
debug_assert!(cache_size % NODE_BYTES == 0, "Unaligned cache size");
|
||||
let num_nodes = cache_size / NODE_BYTES;
|
||||
// We use `debug_assert` since it is impossible for `get_cache_size` to return an unaligned
|
||||
// value with the current implementation. If the implementation changes, CI will catch it.
|
||||
debug_assert!(cache_size % NODE_BYTES == 0, "Unaligned cache size");
|
||||
let num_nodes = cache_size / NODE_BYTES;
|
||||
|
||||
let path = cache_path(cache_dir.as_ref(), &ident);
|
||||
let nodes = new_buffer(&path, num_nodes, &ident, self.optimize_for);
|
||||
let path = cache_path(cache_dir.as_ref(), &ident);
|
||||
let nodes = new_buffer(&path, num_nodes, &ident, self.optimize_for);
|
||||
|
||||
NodeCache {
|
||||
builder: self.clone(),
|
||||
epoch: epoch(block_number),
|
||||
cache_dir: cache_dir.into(),
|
||||
cache_path: path,
|
||||
cache: nodes,
|
||||
}
|
||||
}
|
||||
NodeCache {
|
||||
builder: self.clone(),
|
||||
epoch: epoch(block_number),
|
||||
cache_dir: cache_dir.into(),
|
||||
cache_path: path,
|
||||
cache: nodes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeCache {
|
||||
pub fn cache_path(&self) -> &Path {
|
||||
&self.cache_path
|
||||
}
|
||||
pub fn cache_path(&self) -> &Path {
|
||||
&self.cache_path
|
||||
}
|
||||
|
||||
pub fn flush(&mut self) -> io::Result<()> {
|
||||
if let Some(last) = self
|
||||
.epoch
|
||||
.checked_sub(2)
|
||||
.map(|ep| cache_path(self.cache_dir.as_ref(), &self.builder.epoch_to_ident(ep)))
|
||||
{
|
||||
fs::remove_file(last).unwrap_or_else(|error| match error.kind() {
|
||||
io::ErrorKind::NotFound => (),
|
||||
_ => warn!("Error removing stale DAG cache: {:?}", error),
|
||||
});
|
||||
}
|
||||
pub fn flush(&mut self) -> io::Result<()> {
|
||||
if let Some(last) = self.epoch.checked_sub(2).map(|ep| {
|
||||
cache_path(self.cache_dir.as_ref(), &self.builder.epoch_to_ident(ep))
|
||||
})
|
||||
{
|
||||
fs::remove_file(last).unwrap_or_else(|error| match error.kind() {
|
||||
io::ErrorKind::NotFound => (),
|
||||
_ => warn!("Error removing stale DAG cache: {:?}", error),
|
||||
});
|
||||
}
|
||||
|
||||
consume_cache(&mut self.cache, &self.cache_path)
|
||||
}
|
||||
consume_cache(&mut self.cache, &self.cache_path)
|
||||
}
|
||||
}
|
||||
|
||||
fn make_memmapped_cache(path: &Path, num_nodes: usize, ident: &H256) -> io::Result<MmapMut> {
|
||||
use std::fs::OpenOptions;
|
||||
use std::fs::OpenOptions;
|
||||
|
||||
let file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(&path)?;
|
||||
file.set_len((num_nodes * NODE_BYTES) as _)?;
|
||||
let file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(&path)?;
|
||||
file.set_len((num_nodes * NODE_BYTES) as _)?;
|
||||
|
||||
let mut memmap = unsafe { MmapMut::map_mut(&file)? };
|
||||
let mut memmap = unsafe { MmapMut::map_mut(&file)? };
|
||||
|
||||
unsafe { initialize_memory(memmap.as_mut_ptr() as *mut Node, num_nodes, ident) };
|
||||
unsafe { initialize_memory(memmap.as_mut_ptr() as *mut Node, num_nodes, ident) };
|
||||
|
||||
Ok(memmap)
|
||||
Ok(memmap)
|
||||
}
|
||||
|
||||
fn make_memory_cache(num_nodes: usize, ident: &H256) -> Vec<Node> {
|
||||
let mut nodes: Vec<Node> = Vec::with_capacity(num_nodes);
|
||||
// Use uninit instead of unnecessarily writing `size_of::<Node>() * num_nodes` 0s
|
||||
unsafe {
|
||||
initialize_memory(nodes.as_mut_ptr(), num_nodes, ident);
|
||||
nodes.set_len(num_nodes);
|
||||
}
|
||||
let mut nodes: Vec<Node> = Vec::with_capacity(num_nodes);
|
||||
// Use uninit instead of unnecessarily writing `size_of::<Node>() * num_nodes` 0s
|
||||
unsafe {
|
||||
initialize_memory(nodes.as_mut_ptr(), num_nodes, ident);
|
||||
nodes.set_len(num_nodes);
|
||||
}
|
||||
|
||||
nodes
|
||||
nodes
|
||||
}
|
||||
|
||||
fn cache_path<'a, P: Into<Cow<'a, Path>>>(path: P, ident: &H256) -> PathBuf {
|
||||
let mut buf = path.into().into_owned();
|
||||
buf.push(to_hex(ident));
|
||||
buf
|
||||
let mut buf = path.into().into_owned();
|
||||
buf.push(to_hex(ident));
|
||||
buf
|
||||
}
|
||||
|
||||
fn consume_cache(cache: &mut Cache, path: &Path) -> io::Result<()> {
|
||||
use std::fs::OpenOptions;
|
||||
use std::fs::OpenOptions;
|
||||
|
||||
match *cache {
|
||||
Either::Left(ref mut vec) => {
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(&path)?;
|
||||
match *cache {
|
||||
Either::Left(ref mut vec) => {
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(&path)?;
|
||||
|
||||
let buf = unsafe {
|
||||
slice::from_raw_parts_mut(vec.as_mut_ptr() as *mut u8, vec.len() * NODE_BYTES)
|
||||
};
|
||||
let buf = unsafe {
|
||||
slice::from_raw_parts_mut(vec.as_mut_ptr() as *mut u8, vec.len() * NODE_BYTES)
|
||||
};
|
||||
|
||||
file.write_all(buf).map(|_| ())
|
||||
}
|
||||
Either::Right(ref mmap) => mmap.flush(),
|
||||
}
|
||||
file.write_all(buf).map(|_| ())
|
||||
}
|
||||
Either::Right(ref mmap) => {
|
||||
mmap.flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cache_from_path(path: &Path, optimize_for: OptimizeFor) -> io::Result<Cache> {
|
||||
let memmap = match optimize_for {
|
||||
OptimizeFor::Cpu => None,
|
||||
OptimizeFor::Memory => {
|
||||
let file = fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(path)?;
|
||||
unsafe { MmapMut::map_mut(&file).ok() }
|
||||
}
|
||||
};
|
||||
let memmap = match optimize_for {
|
||||
OptimizeFor::Cpu => None,
|
||||
OptimizeFor::Memory => {
|
||||
let file = fs::OpenOptions::new().read(true).write(true).create(true).open(path)?;
|
||||
unsafe { MmapMut::map_mut(&file).ok() }
|
||||
},
|
||||
};
|
||||
|
||||
memmap
|
||||
.map(Either::Right)
|
||||
.ok_or(())
|
||||
.or_else(|_| read_from_path(path).map(Either::Left))
|
||||
memmap.map(Either::Right).ok_or(()).or_else(|_| {
|
||||
read_from_path(path).map(Either::Left)
|
||||
})
|
||||
}
|
||||
|
||||
fn read_from_path(path: &Path) -> io::Result<Vec<Node>> {
|
||||
use std::{fs::File, mem};
|
||||
use std::fs::File;
|
||||
use std::mem;
|
||||
|
||||
let mut file = File::open(path)?;
|
||||
let mut file = File::open(path)?;
|
||||
|
||||
let mut nodes: Vec<u8> = Vec::with_capacity(
|
||||
file.metadata()
|
||||
.map(|m| m.len() as _)
|
||||
.unwrap_or(NODE_BYTES * 1_000_000),
|
||||
);
|
||||
file.read_to_end(&mut nodes)?;
|
||||
let mut nodes: Vec<u8> = Vec::with_capacity(file.metadata().map(|m| m.len() as _).unwrap_or(
|
||||
NODE_BYTES * 1_000_000,
|
||||
));
|
||||
file.read_to_end(&mut nodes)?;
|
||||
|
||||
nodes.shrink_to_fit();
|
||||
nodes.shrink_to_fit();
|
||||
|
||||
if nodes.len() % NODE_BYTES != 0 || nodes.capacity() % NODE_BYTES != 0 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"Node cache is not a multiple of node size",
|
||||
));
|
||||
}
|
||||
if nodes.len() % NODE_BYTES != 0 || nodes.capacity() % NODE_BYTES != 0 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"Node cache is not a multiple of node size",
|
||||
));
|
||||
}
|
||||
|
||||
let out: Vec<Node> = unsafe {
|
||||
Vec::from_raw_parts(
|
||||
nodes.as_mut_ptr() as *mut _,
|
||||
nodes.len() / NODE_BYTES,
|
||||
nodes.capacity() / NODE_BYTES,
|
||||
)
|
||||
};
|
||||
let out: Vec<Node> = unsafe {
|
||||
Vec::from_raw_parts(
|
||||
nodes.as_mut_ptr() as *mut _,
|
||||
nodes.len() / NODE_BYTES,
|
||||
nodes.capacity() / NODE_BYTES,
|
||||
)
|
||||
};
|
||||
|
||||
mem::forget(nodes);
|
||||
mem::forget(nodes);
|
||||
|
||||
Ok(out)
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
impl AsRef<[Node]> for NodeCache {
|
||||
fn as_ref(&self) -> &[Node] {
|
||||
match self.cache {
|
||||
Either::Left(ref vec) => vec,
|
||||
Either::Right(ref mmap) => unsafe {
|
||||
let bytes = mmap.as_ptr();
|
||||
// This isn't a safety issue, so we can keep this a debug lint. We don't care about
|
||||
// people manually messing with the files unless it can cause unsafety, but if we're
|
||||
// generating incorrect files then we want to catch that in CI.
|
||||
debug_assert_eq!(mmap.len() % NODE_BYTES, 0);
|
||||
slice::from_raw_parts(bytes as _, mmap.len() / NODE_BYTES)
|
||||
},
|
||||
}
|
||||
}
|
||||
fn as_ref(&self) -> &[Node] {
|
||||
match self.cache {
|
||||
Either::Left(ref vec) => vec,
|
||||
Either::Right(ref mmap) => unsafe {
|
||||
let bytes = mmap.as_ptr();
|
||||
// This isn't a safety issue, so we can keep this a debug lint. We don't care about
|
||||
// people manually messing with the files unless it can cause unsafety, but if we're
|
||||
// generating incorrect files then we want to catch that in CI.
|
||||
debug_assert_eq!(mmap.len() % NODE_BYTES, 0);
|
||||
slice::from_raw_parts(bytes as _, mmap.len() / NODE_BYTES)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This takes a raw pointer and a counter because `memory` may be uninitialized. `memory` _must_ be
|
||||
@@ -318,47 +309,47 @@ impl AsRef<[Node]> for NodeCache {
|
||||
// out. It counts as a read and causes all writes afterwards to be elided. Yes, really. I know, I
|
||||
// want to refactor this to use less `unsafe` as much as the next rustacean.
|
||||
unsafe fn initialize_memory(memory: *mut Node, num_nodes: usize, ident: &H256) {
|
||||
let dst = memory as *mut u8;
|
||||
let dst = memory as *mut u8;
|
||||
|
||||
debug_assert_eq!(ident.len(), 32);
|
||||
keccak_512::unchecked(dst, NODE_BYTES, ident.as_ptr(), ident.len());
|
||||
debug_assert_eq!(ident.len(), 32);
|
||||
keccak_512::unchecked(dst, NODE_BYTES, ident.as_ptr(), ident.len());
|
||||
|
||||
for i in 1..num_nodes {
|
||||
// We use raw pointers here, see above
|
||||
let dst = memory.offset(i as _) as *mut u8;
|
||||
let src = memory.offset(i as isize - 1) as *mut u8;
|
||||
for i in 1..num_nodes {
|
||||
// We use raw pointers here, see above
|
||||
let dst = memory.offset(i as _) as *mut u8;
|
||||
let src = memory.offset(i as isize - 1) as *mut u8;
|
||||
|
||||
keccak_512::unchecked(dst, NODE_BYTES, src, NODE_BYTES);
|
||||
}
|
||||
keccak_512::unchecked(dst, NODE_BYTES, src, NODE_BYTES);
|
||||
}
|
||||
|
||||
// Now this is initialized, we can treat it as a slice.
|
||||
let nodes: &mut [Node] = slice::from_raw_parts_mut(memory, num_nodes);
|
||||
// Now this is initialized, we can treat it as a slice.
|
||||
let nodes: &mut [Node] = slice::from_raw_parts_mut(memory, num_nodes);
|
||||
|
||||
// For `unroll!`, see below. If the literal in `unroll!` is not the same as the RHS here then
|
||||
// these have got out of sync! Don't let this happen!
|
||||
debug_assert_eq!(NODE_DWORDS, 8);
|
||||
// For `unroll!`, see below. If the literal in `unroll!` is not the same as the RHS here then
|
||||
// these have got out of sync! Don't let this happen!
|
||||
debug_assert_eq!(NODE_DWORDS, 8);
|
||||
|
||||
// This _should_ get unrolled by the compiler, since it's not using the loop variable.
|
||||
for _ in 0..ETHASH_CACHE_ROUNDS {
|
||||
for i in 0..num_nodes {
|
||||
let data_idx = (num_nodes - 1 + i) % num_nodes;
|
||||
let idx = nodes.get_unchecked_mut(i).as_words()[0] as usize % num_nodes;
|
||||
// This _should_ get unrolled by the compiler, since it's not using the loop variable.
|
||||
for _ in 0..ETHASH_CACHE_ROUNDS {
|
||||
for i in 0..num_nodes {
|
||||
let data_idx = (num_nodes - 1 + i) % num_nodes;
|
||||
let idx = nodes.get_unchecked_mut(i).as_words()[0] as usize % num_nodes;
|
||||
|
||||
let data = {
|
||||
let mut data: Node = nodes.get_unchecked(data_idx).clone();
|
||||
let rhs: &Node = nodes.get_unchecked(idx);
|
||||
let data = {
|
||||
let mut data: Node = nodes.get_unchecked(data_idx).clone();
|
||||
let rhs: &Node = nodes.get_unchecked(idx);
|
||||
|
||||
unroll! {
|
||||
for w in 0..8 {
|
||||
*data.as_dwords_mut().get_unchecked_mut(w) ^=
|
||||
*rhs.as_dwords().get_unchecked(w);
|
||||
}
|
||||
}
|
||||
unroll! {
|
||||
for w in 0..8 {
|
||||
*data.as_dwords_mut().get_unchecked_mut(w) ^=
|
||||
*rhs.as_dwords().get_unchecked(w);
|
||||
}
|
||||
}
|
||||
|
||||
data
|
||||
};
|
||||
data
|
||||
};
|
||||
|
||||
keccak_512::write(&data.bytes, &mut nodes.get_unchecked_mut(i).bytes);
|
||||
}
|
||||
}
|
||||
keccak_512::write(&data.bytes, &mut nodes.get_unchecked_mut(i).bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,132 +1,96 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Ethash implementation
|
||||
//! See https://github.com/ethereum/wiki/wiki/Ethash
|
||||
|
||||
// TODO: fix endianess for big endian
|
||||
|
||||
use keccak::{keccak_512, keccak_256, H256};
|
||||
use cache::{NodeCache, NodeCacheBuilder};
|
||||
use keccak::{keccak_256, keccak_512, H256};
|
||||
use progpow::{generate_cdag, keccak_f800_long, keccak_f800_short, progpow, CDag};
|
||||
use seed_compute::SeedHashCompute;
|
||||
use shared::*;
|
||||
use std::io;
|
||||
|
||||
use std::{mem, path::Path, ptr};
|
||||
use std::{mem, ptr};
|
||||
use std::path::Path;
|
||||
|
||||
const MIX_WORDS: usize = ETHASH_MIX_BYTES / 4;
|
||||
const MIX_NODES: usize = MIX_WORDS / NODE_WORDS;
|
||||
pub const FNV_PRIME: u32 = 0x01000193;
|
||||
const FNV_PRIME: u32 = 0x01000193;
|
||||
|
||||
/// Computation result
|
||||
pub struct ProofOfWork {
|
||||
/// Difficulty boundary
|
||||
pub value: H256,
|
||||
/// Mix
|
||||
pub mix_hash: H256,
|
||||
}
|
||||
|
||||
enum Algorithm {
|
||||
Hashimoto,
|
||||
Progpow(Box<CDag>),
|
||||
/// Difficulty boundary
|
||||
pub value: H256,
|
||||
/// Mix
|
||||
pub mix_hash: H256,
|
||||
}
|
||||
|
||||
pub struct Light {
|
||||
block_number: u64,
|
||||
cache: NodeCache,
|
||||
algorithm: Algorithm,
|
||||
block_number: u64,
|
||||
cache: NodeCache,
|
||||
}
|
||||
|
||||
/// Light cache structure
|
||||
impl Light {
|
||||
pub fn new_with_builder(
|
||||
builder: &NodeCacheBuilder,
|
||||
cache_dir: &Path,
|
||||
block_number: u64,
|
||||
progpow_transition: u64,
|
||||
) -> Self {
|
||||
let cache = builder.new_cache(cache_dir.to_path_buf(), block_number);
|
||||
pub fn new_with_builder(
|
||||
builder: &NodeCacheBuilder,
|
||||
cache_dir: &Path,
|
||||
block_number: u64,
|
||||
) -> Self {
|
||||
let cache = builder.new_cache(cache_dir.to_path_buf(), block_number);
|
||||
|
||||
let algorithm = if block_number >= progpow_transition {
|
||||
Algorithm::Progpow(Box::new(generate_cdag(cache.as_ref())))
|
||||
} else {
|
||||
Algorithm::Hashimoto
|
||||
};
|
||||
Light {
|
||||
block_number: block_number,
|
||||
cache: cache,
|
||||
}
|
||||
}
|
||||
|
||||
Light {
|
||||
block_number,
|
||||
cache,
|
||||
algorithm,
|
||||
}
|
||||
}
|
||||
/// Calculate the light boundary data
|
||||
/// `header_hash` - The header hash to pack into the mix
|
||||
/// `nonce` - The nonce to pack into the mix
|
||||
pub fn compute(&self, header_hash: &H256, nonce: u64) -> ProofOfWork {
|
||||
light_compute(self, header_hash, nonce)
|
||||
}
|
||||
|
||||
/// Calculate the light boundary data
|
||||
/// `header_hash` - The header hash to pack into the mix
|
||||
/// `nonce` - The nonce to pack into the mix
|
||||
pub fn compute(&self, header_hash: &H256, nonce: u64, block_number: u64) -> ProofOfWork {
|
||||
match self.algorithm {
|
||||
Algorithm::Progpow(ref c_dag) => {
|
||||
let (value, mix_hash) = progpow(
|
||||
*header_hash,
|
||||
nonce,
|
||||
block_number,
|
||||
self.cache.as_ref(),
|
||||
c_dag,
|
||||
);
|
||||
pub fn from_file_with_builder(
|
||||
builder: &NodeCacheBuilder,
|
||||
cache_dir: &Path,
|
||||
block_number: u64,
|
||||
) -> io::Result<Self> {
|
||||
let cache = builder.from_file(cache_dir.to_path_buf(), block_number)?;
|
||||
Ok(Light {
|
||||
block_number: block_number,
|
||||
cache: cache,
|
||||
})
|
||||
}
|
||||
|
||||
ProofOfWork { value, mix_hash }
|
||||
}
|
||||
Algorithm::Hashimoto => light_compute(self, header_hash, nonce),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_file_with_builder(
|
||||
builder: &NodeCacheBuilder,
|
||||
cache_dir: &Path,
|
||||
block_number: u64,
|
||||
progpow_transition: u64,
|
||||
) -> io::Result<Self> {
|
||||
let cache = builder.from_file(cache_dir.to_path_buf(), block_number)?;
|
||||
|
||||
let algorithm = if block_number >= progpow_transition {
|
||||
Algorithm::Progpow(Box::new(generate_cdag(cache.as_ref())))
|
||||
} else {
|
||||
Algorithm::Hashimoto
|
||||
};
|
||||
|
||||
Ok(Light {
|
||||
block_number,
|
||||
cache,
|
||||
algorithm,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_file(&mut self) -> io::Result<&Path> {
|
||||
self.cache.flush()?;
|
||||
Ok(self.cache.cache_path())
|
||||
}
|
||||
pub fn to_file(&mut self) -> io::Result<&Path> {
|
||||
self.cache.flush()?;
|
||||
Ok(self.cache.cache_path())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn slow_hash_block_number(block_number: u64) -> H256 {
|
||||
SeedHashCompute::resume_compute_seedhash([0u8; 32], 0, block_number / ETHASH_EPOCH_LENGTH)
|
||||
SeedHashCompute::resume_compute_seedhash([0u8; 32], 0, block_number / ETHASH_EPOCH_LENGTH)
|
||||
}
|
||||
|
||||
fn fnv_hash(x: u32, y: u32) -> u32 {
|
||||
return x.wrapping_mul(FNV_PRIME) ^ y;
|
||||
return x.wrapping_mul(FNV_PRIME) ^ y;
|
||||
}
|
||||
|
||||
/// Difficulty quick check for POW preverification
|
||||
@@ -135,38 +99,28 @@ fn fnv_hash(x: u32, y: u32) -> u32 {
|
||||
/// `nonce` The block's nonce
|
||||
/// `mix_hash` The mix digest hash
|
||||
/// Boundary recovered from mix hash
|
||||
pub fn quick_get_difficulty(
|
||||
header_hash: &H256,
|
||||
nonce: u64,
|
||||
mix_hash: &H256,
|
||||
progpow: bool,
|
||||
) -> H256 {
|
||||
unsafe {
|
||||
if progpow {
|
||||
let seed = keccak_f800_short(*header_hash, nonce, [0u32; 8]);
|
||||
keccak_f800_long(*header_hash, seed, mem::transmute(*mix_hash))
|
||||
} else {
|
||||
// This is safe - the `keccak_512` call below reads the first 40 bytes (which we explicitly set
|
||||
// with two `copy_nonoverlapping` calls) but writes the first 64, and then we explicitly write
|
||||
// the next 32 bytes before we read the whole thing with `keccak_256`.
|
||||
//
|
||||
// This cannot be elided by the compiler as it doesn't know the implementation of
|
||||
// `keccak_512`.
|
||||
let mut buf: [u8; 64 + 32] = ::mem::MaybeUninit::uninit().assume_init();
|
||||
pub fn quick_get_difficulty(header_hash: &H256, nonce: u64, mix_hash: &H256) -> H256 {
|
||||
unsafe {
|
||||
// This is safe - the `keccak_512` call below reads the first 40 bytes (which we explicitly set
|
||||
// with two `copy_nonoverlapping` calls) but writes the first 64, and then we explicitly write
|
||||
// the next 32 bytes before we read the whole thing with `keccak_256`.
|
||||
//
|
||||
// This cannot be elided by the compiler as it doesn't know the implementation of
|
||||
// `keccak_512`.
|
||||
let mut buf: [u8; 64 + 32] = mem::uninitialized();
|
||||
|
||||
ptr::copy_nonoverlapping(header_hash.as_ptr(), buf.as_mut_ptr(), 32);
|
||||
ptr::copy_nonoverlapping(&nonce as *const u64 as *const u8, buf[32..].as_mut_ptr(), 8);
|
||||
ptr::copy_nonoverlapping(header_hash.as_ptr(), buf.as_mut_ptr(), 32);
|
||||
ptr::copy_nonoverlapping(&nonce as *const u64 as *const u8, buf[32..].as_mut_ptr(), 8);
|
||||
|
||||
keccak_512::unchecked(buf.as_mut_ptr(), 64, buf.as_ptr(), 40);
|
||||
ptr::copy_nonoverlapping(mix_hash.as_ptr(), buf[64..].as_mut_ptr(), 32);
|
||||
keccak_512::unchecked(buf.as_mut_ptr(), 64, buf.as_ptr(), 40);
|
||||
ptr::copy_nonoverlapping(mix_hash.as_ptr(), buf[64..].as_mut_ptr(), 32);
|
||||
|
||||
// This is initialized in `keccak_256`
|
||||
let mut hash: [u8; 32] = ::mem::MaybeUninit::uninit().assume_init();
|
||||
keccak_256::unchecked(hash.as_mut_ptr(), hash.len(), buf.as_ptr(), buf.len());
|
||||
// This is initialized in `keccak_256`
|
||||
let mut hash: [u8; 32] = mem::uninitialized();
|
||||
keccak_256::unchecked(hash.as_mut_ptr(), hash.len(), buf.as_ptr(), buf.len());
|
||||
|
||||
hash
|
||||
}
|
||||
}
|
||||
hash
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the light client data
|
||||
@@ -174,310 +128,289 @@ pub fn quick_get_difficulty(
|
||||
/// `header_hash` - The header hash to pack into the mix
|
||||
/// `nonce` - The nonce to pack into the mix
|
||||
pub fn light_compute(light: &Light, header_hash: &H256, nonce: u64) -> ProofOfWork {
|
||||
let full_size = get_data_size(light.block_number);
|
||||
hash_compute(light, full_size, header_hash, nonce)
|
||||
let full_size = get_data_size(light.block_number);
|
||||
hash_compute(light, full_size, header_hash, nonce)
|
||||
}
|
||||
|
||||
fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64) -> ProofOfWork {
|
||||
macro_rules! make_const_array {
|
||||
($n:expr, $value:expr) => {{
|
||||
// We use explicit lifetimes to ensure that val's borrow is invalidated until the
|
||||
// transmuted val dies.
|
||||
unsafe fn make_const_array<T, U>(val: &mut [T]) -> &mut [U; $n] {
|
||||
use std::mem;
|
||||
macro_rules! make_const_array {
|
||||
($n:expr, $value:expr) => {{
|
||||
// We use explicit lifetimes to ensure that val's borrow is invalidated until the
|
||||
// transmuted val dies.
|
||||
unsafe fn make_const_array<T, U>(val: &mut [T]) -> &mut [U; $n] {
|
||||
use ::std::mem;
|
||||
|
||||
debug_assert_eq!(val.len() * mem::size_of::<T>(), $n * mem::size_of::<U>());
|
||||
&mut *(val.as_mut_ptr() as *mut [U; $n])
|
||||
}
|
||||
debug_assert_eq!(val.len() * mem::size_of::<T>(), $n * mem::size_of::<U>());
|
||||
&mut *(val.as_mut_ptr() as *mut [U; $n])
|
||||
}
|
||||
|
||||
make_const_array($value)
|
||||
}};
|
||||
}
|
||||
make_const_array($value)
|
||||
}}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct MixBuf {
|
||||
half_mix: Node,
|
||||
compress_bytes: [u8; MIX_WORDS],
|
||||
};
|
||||
#[repr(C)]
|
||||
struct MixBuf {
|
||||
half_mix: Node,
|
||||
compress_bytes: [u8; MIX_WORDS],
|
||||
};
|
||||
|
||||
if full_size % MIX_WORDS != 0 {
|
||||
panic!("Unaligned full size");
|
||||
}
|
||||
if full_size % MIX_WORDS != 0 {
|
||||
panic!("Unaligned full size");
|
||||
}
|
||||
|
||||
// You may be asking yourself: what in the name of Crypto Jesus is going on here? So: we need
|
||||
// `half_mix` and `compress_bytes` in a single array later down in the code (we hash them
|
||||
// together to create `value`) so that we can hash the full array. However, we do a bunch of
|
||||
// reading and writing to these variables first. We originally allocated two arrays and then
|
||||
// stuck them together with `ptr::copy_nonoverlapping` at the end, but this method is
|
||||
// _significantly_ faster - by my benchmarks, a consistent 3-5%. This is the most ridiculous
|
||||
// optimization I have ever done and I am so sorry. I can only chalk it up to cache locality
|
||||
// improvements, since I can't imagine that 3-5% of our runtime is taken up by catting two
|
||||
// arrays together.
|
||||
let mut buf: MixBuf = MixBuf {
|
||||
half_mix: unsafe {
|
||||
// Pack `header_hash` and `nonce` together
|
||||
// We explicitly write the first 40 bytes, leaving the last 24 as uninitialized. Then
|
||||
// `keccak_512` reads the first 40 bytes (4th parameter) and overwrites the entire array,
|
||||
// leaving it fully initialized.
|
||||
let mut out: [u8; NODE_BYTES] = ::mem::MaybeUninit::uninit().assume_init();
|
||||
// You may be asking yourself: what in the name of Crypto Jesus is going on here? So: we need
|
||||
// `half_mix` and `compress_bytes` in a single array later down in the code (we hash them
|
||||
// together to create `value`) so that we can hash the full array. However, we do a bunch of
|
||||
// reading and writing to these variables first. We originally allocated two arrays and then
|
||||
// stuck them together with `ptr::copy_nonoverlapping` at the end, but this method is
|
||||
// _significantly_ faster - by my benchmarks, a consistent 3-5%. This is the most ridiculous
|
||||
// optimization I have ever done and I am so sorry. I can only chalk it up to cache locality
|
||||
// improvements, since I can't imagine that 3-5% of our runtime is taken up by catting two
|
||||
// arrays together.
|
||||
let mut buf: MixBuf = MixBuf {
|
||||
half_mix: unsafe {
|
||||
// Pack `header_hash` and `nonce` together
|
||||
// We explicitly write the first 40 bytes, leaving the last 24 as uninitialized. Then
|
||||
// `keccak_512` reads the first 40 bytes (4th parameter) and overwrites the entire array,
|
||||
// leaving it fully initialized.
|
||||
let mut out: [u8; NODE_BYTES] = mem::uninitialized();
|
||||
|
||||
ptr::copy_nonoverlapping(header_hash.as_ptr(), out.as_mut_ptr(), header_hash.len());
|
||||
ptr::copy_nonoverlapping(
|
||||
&nonce as *const u64 as *const u8,
|
||||
out[header_hash.len()..].as_mut_ptr(),
|
||||
mem::size_of::<u64>(),
|
||||
);
|
||||
ptr::copy_nonoverlapping(header_hash.as_ptr(), out.as_mut_ptr(), header_hash.len());
|
||||
ptr::copy_nonoverlapping(
|
||||
&nonce as *const u64 as *const u8,
|
||||
out[header_hash.len()..].as_mut_ptr(),
|
||||
mem::size_of::<u64>(),
|
||||
);
|
||||
|
||||
// compute keccak-512 hash and replicate across mix
|
||||
keccak_512::unchecked(
|
||||
out.as_mut_ptr(),
|
||||
NODE_BYTES,
|
||||
out.as_ptr(),
|
||||
header_hash.len() + mem::size_of::<u64>(),
|
||||
);
|
||||
// compute keccak-512 hash and replicate across mix
|
||||
keccak_512::unchecked(
|
||||
out.as_mut_ptr(),
|
||||
NODE_BYTES,
|
||||
out.as_ptr(),
|
||||
header_hash.len() + mem::size_of::<u64>(),
|
||||
);
|
||||
|
||||
Node { bytes: out }
|
||||
},
|
||||
// This is fully initialized before being read, see `let mut compress = ...` below
|
||||
compress_bytes: unsafe { ::mem::MaybeUninit::uninit().assume_init() },
|
||||
};
|
||||
Node { bytes: out }
|
||||
},
|
||||
// This is fully initialized before being read, see `let mut compress = ...` below
|
||||
compress_bytes: unsafe { mem::uninitialized() },
|
||||
};
|
||||
|
||||
let mut mix: [_; MIX_NODES] = [buf.half_mix.clone(), buf.half_mix.clone()];
|
||||
let mut mix: [_; MIX_NODES] = [buf.half_mix.clone(), buf.half_mix.clone()];
|
||||
|
||||
let page_size = 4 * MIX_WORDS;
|
||||
let num_full_pages = (full_size / page_size) as u32;
|
||||
// deref once for better performance
|
||||
let cache: &[Node] = light.cache.as_ref();
|
||||
let first_val = buf.half_mix.as_words()[0];
|
||||
let page_size = 4 * MIX_WORDS;
|
||||
let num_full_pages = (full_size / page_size) as u32;
|
||||
// deref once for better performance
|
||||
let cache: &[Node] = light.cache.as_ref();
|
||||
let first_val = buf.half_mix.as_words()[0];
|
||||
|
||||
debug_assert_eq!(MIX_NODES, 2);
|
||||
debug_assert_eq!(NODE_WORDS, 16);
|
||||
debug_assert_eq!(MIX_NODES, 2);
|
||||
debug_assert_eq!(NODE_WORDS, 16);
|
||||
|
||||
for i in 0..ETHASH_ACCESSES as u32 {
|
||||
let index = {
|
||||
// This is trivially safe, but does not work on big-endian. The safety of this is
|
||||
// asserted in debug builds (see the definition of `make_const_array!`).
|
||||
let mix_words: &mut [u32; MIX_WORDS] =
|
||||
unsafe { make_const_array!(MIX_WORDS, &mut mix) };
|
||||
for i in 0..ETHASH_ACCESSES as u32 {
|
||||
let index = {
|
||||
// This is trivially safe, but does not work on big-endian. The safety of this is
|
||||
// asserted in debug builds (see the definition of `make_const_array!`).
|
||||
let mix_words: &mut [u32; MIX_WORDS] =
|
||||
unsafe { make_const_array!(MIX_WORDS, &mut mix) };
|
||||
|
||||
fnv_hash(first_val ^ i, mix_words[i as usize % MIX_WORDS]) % num_full_pages
|
||||
};
|
||||
fnv_hash(first_val ^ i, mix_words[i as usize % MIX_WORDS]) % num_full_pages
|
||||
};
|
||||
|
||||
unroll! {
|
||||
// MIX_NODES
|
||||
for n in 0..2 {
|
||||
let tmp_node = calculate_dag_item(
|
||||
index * MIX_NODES as u32 + n as u32,
|
||||
cache,
|
||||
);
|
||||
unroll! {
|
||||
// MIX_NODES
|
||||
for n in 0..2 {
|
||||
let tmp_node = calculate_dag_item(
|
||||
index * MIX_NODES as u32 + n as u32,
|
||||
cache,
|
||||
);
|
||||
|
||||
unroll! {
|
||||
// NODE_WORDS
|
||||
for w in 0..16 {
|
||||
mix[n].as_words_mut()[w] =
|
||||
fnv_hash(
|
||||
mix[n].as_words()[w],
|
||||
tmp_node.as_words()[w],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unroll! {
|
||||
// NODE_WORDS
|
||||
for w in 0..16 {
|
||||
mix[n].as_words_mut()[w] =
|
||||
fnv_hash(
|
||||
mix[n].as_words()[w],
|
||||
tmp_node.as_words()[w],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mix_words: [u32; MIX_WORDS] = unsafe { mem::transmute(mix) };
|
||||
let mix_words: [u32; MIX_WORDS] = unsafe { mem::transmute(mix) };
|
||||
|
||||
{
|
||||
// This is an uninitialized buffer to begin with, but we iterate precisely `compress.len()`
|
||||
// times and set each index, leaving the array fully initialized. THIS ONLY WORKS ON LITTLE-
|
||||
// ENDIAN MACHINES. See a future PR to make this and the rest of the code work correctly on
|
||||
// big-endian arches like mips.
|
||||
let compress: &mut [u32; MIX_WORDS / 4] =
|
||||
unsafe { make_const_array!(MIX_WORDS / 4, &mut buf.compress_bytes) };
|
||||
{
|
||||
// This is an uninitialized buffer to begin with, but we iterate precisely `compress.len()`
|
||||
// times and set each index, leaving the array fully initialized. THIS ONLY WORKS ON LITTLE-
|
||||
// ENDIAN MACHINES. See a future PR to make this and the rest of the code work correctly on
|
||||
// big-endian arches like mips.
|
||||
let compress: &mut [u32; MIX_WORDS / 4] =
|
||||
unsafe { make_const_array!(MIX_WORDS / 4, &mut buf.compress_bytes) };
|
||||
|
||||
// Compress mix
|
||||
debug_assert_eq!(MIX_WORDS / 4, 8);
|
||||
unroll! {
|
||||
for i in 0..8 {
|
||||
let w = i * 4;
|
||||
// Compress mix
|
||||
debug_assert_eq!(MIX_WORDS / 4, 8);
|
||||
unroll! {
|
||||
for i in 0..8 {
|
||||
let w = i * 4;
|
||||
|
||||
let mut reduction = mix_words[w + 0];
|
||||
reduction = reduction.wrapping_mul(FNV_PRIME) ^ mix_words[w + 1];
|
||||
reduction = reduction.wrapping_mul(FNV_PRIME) ^ mix_words[w + 2];
|
||||
reduction = reduction.wrapping_mul(FNV_PRIME) ^ mix_words[w + 3];
|
||||
compress[i] = reduction;
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut reduction = mix_words[w + 0];
|
||||
reduction = reduction.wrapping_mul(FNV_PRIME) ^ mix_words[w + 1];
|
||||
reduction = reduction.wrapping_mul(FNV_PRIME) ^ mix_words[w + 2];
|
||||
reduction = reduction.wrapping_mul(FNV_PRIME) ^ mix_words[w + 3];
|
||||
compress[i] = reduction;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mix_hash = buf.compress_bytes;
|
||||
let mix_hash = buf.compress_bytes;
|
||||
|
||||
let value: H256 = {
|
||||
// We can interpret the buffer as an array of `u8`s, since it's `repr(C)`.
|
||||
let read_ptr: *const u8 = &buf as *const MixBuf as *const u8;
|
||||
// We overwrite the second half since `keccak_256` has an internal buffer and so allows
|
||||
// overlapping arrays as input.
|
||||
let write_ptr: *mut u8 = &mut buf.compress_bytes as *mut [u8; 32] as *mut u8;
|
||||
unsafe {
|
||||
keccak_256::unchecked(
|
||||
write_ptr,
|
||||
buf.compress_bytes.len(),
|
||||
read_ptr,
|
||||
buf.half_mix.bytes.len() + buf.compress_bytes.len(),
|
||||
);
|
||||
}
|
||||
buf.compress_bytes
|
||||
};
|
||||
let value: H256 = {
|
||||
// We can interpret the buffer as an array of `u8`s, since it's `repr(C)`.
|
||||
let read_ptr: *const u8 = &buf as *const MixBuf as *const u8;
|
||||
// We overwrite the second half since `keccak_256` has an internal buffer and so allows
|
||||
// overlapping arrays as input.
|
||||
let write_ptr: *mut u8 = &mut buf.compress_bytes as *mut [u8; 32] as *mut u8;
|
||||
unsafe {
|
||||
keccak_256::unchecked(
|
||||
write_ptr,
|
||||
buf.compress_bytes.len(),
|
||||
read_ptr,
|
||||
buf.half_mix.bytes.len() + buf.compress_bytes.len(),
|
||||
);
|
||||
}
|
||||
buf.compress_bytes
|
||||
};
|
||||
|
||||
ProofOfWork {
|
||||
mix_hash: mix_hash,
|
||||
value: value,
|
||||
}
|
||||
ProofOfWork { mix_hash: mix_hash, value: value }
|
||||
}
|
||||
|
||||
// TODO: Use the `simd` crate
|
||||
pub fn calculate_dag_item(node_index: u32, cache: &[Node]) -> Node {
|
||||
let num_parent_nodes = cache.len();
|
||||
let mut ret = cache[node_index as usize % num_parent_nodes].clone();
|
||||
ret.as_words_mut()[0] ^= node_index;
|
||||
fn calculate_dag_item(node_index: u32, cache: &[Node]) -> Node {
|
||||
let num_parent_nodes = cache.len();
|
||||
let mut ret = cache[node_index as usize % num_parent_nodes].clone();
|
||||
ret.as_words_mut()[0] ^= node_index;
|
||||
|
||||
keccak_512::inplace(ret.as_bytes_mut());
|
||||
keccak_512::inplace(ret.as_bytes_mut());
|
||||
|
||||
debug_assert_eq!(NODE_WORDS, 16);
|
||||
for i in 0..ETHASH_DATASET_PARENTS as u32 {
|
||||
let parent_index = fnv_hash(node_index ^ i, ret.as_words()[i as usize % NODE_WORDS])
|
||||
% num_parent_nodes as u32;
|
||||
let parent = &cache[parent_index as usize];
|
||||
debug_assert_eq!(NODE_WORDS, 16);
|
||||
for i in 0..ETHASH_DATASET_PARENTS as u32 {
|
||||
let parent_index = fnv_hash(node_index ^ i, ret.as_words()[i as usize % NODE_WORDS]) %
|
||||
num_parent_nodes as u32;
|
||||
let parent = &cache[parent_index as usize];
|
||||
|
||||
unroll! {
|
||||
for w in 0..16 {
|
||||
ret.as_words_mut()[w] = fnv_hash(ret.as_words()[w], parent.as_words()[w]);
|
||||
}
|
||||
}
|
||||
}
|
||||
unroll! {
|
||||
for w in 0..16 {
|
||||
ret.as_words_mut()[w] = fnv_hash(ret.as_words()[w], parent.as_words()[w]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keccak_512::inplace(ret.as_bytes_mut());
|
||||
keccak_512::inplace(ret.as_bytes_mut());
|
||||
|
||||
ret
|
||||
ret
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::fs;
|
||||
use tempdir::TempDir;
|
||||
use super::*;
|
||||
use std::fs;
|
||||
use tempdir::TempDir;
|
||||
|
||||
#[test]
|
||||
fn test_get_cache_size() {
|
||||
// https://github.com/ethereum/wiki/wiki/Ethash/ef6b93f9596746a088ea95d01ca2778be43ae68f#data-sizes
|
||||
assert_eq!(16776896usize, get_cache_size(0));
|
||||
assert_eq!(16776896usize, get_cache_size(1));
|
||||
assert_eq!(16776896usize, get_cache_size(ETHASH_EPOCH_LENGTH - 1));
|
||||
assert_eq!(16907456usize, get_cache_size(ETHASH_EPOCH_LENGTH));
|
||||
assert_eq!(16907456usize, get_cache_size(ETHASH_EPOCH_LENGTH + 1));
|
||||
assert_eq!(284950208usize, get_cache_size(2046 * ETHASH_EPOCH_LENGTH));
|
||||
assert_eq!(285081536usize, get_cache_size(2047 * ETHASH_EPOCH_LENGTH));
|
||||
assert_eq!(
|
||||
285081536usize,
|
||||
get_cache_size(2048 * ETHASH_EPOCH_LENGTH - 1)
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_get_cache_size() {
|
||||
// https://github.com/ethereum/wiki/wiki/Ethash/ef6b93f9596746a088ea95d01ca2778be43ae68f#data-sizes
|
||||
assert_eq!(16776896usize, get_cache_size(0));
|
||||
assert_eq!(16776896usize, get_cache_size(1));
|
||||
assert_eq!(16776896usize, get_cache_size(ETHASH_EPOCH_LENGTH - 1));
|
||||
assert_eq!(16907456usize, get_cache_size(ETHASH_EPOCH_LENGTH));
|
||||
assert_eq!(16907456usize, get_cache_size(ETHASH_EPOCH_LENGTH + 1));
|
||||
assert_eq!(284950208usize, get_cache_size(2046 * ETHASH_EPOCH_LENGTH));
|
||||
assert_eq!(285081536usize, get_cache_size(2047 * ETHASH_EPOCH_LENGTH));
|
||||
assert_eq!(285081536usize, get_cache_size(2048 * ETHASH_EPOCH_LENGTH - 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_data_size() {
|
||||
// https://github.com/ethereum/wiki/wiki/Ethash/ef6b93f9596746a088ea95d01ca2778be43ae68f#data-sizes
|
||||
assert_eq!(1073739904usize, get_data_size(0));
|
||||
assert_eq!(1073739904usize, get_data_size(1));
|
||||
assert_eq!(1073739904usize, get_data_size(ETHASH_EPOCH_LENGTH - 1));
|
||||
assert_eq!(1082130304usize, get_data_size(ETHASH_EPOCH_LENGTH));
|
||||
assert_eq!(1082130304usize, get_data_size(ETHASH_EPOCH_LENGTH + 1));
|
||||
assert_eq!(18236833408usize, get_data_size(2046 * ETHASH_EPOCH_LENGTH));
|
||||
assert_eq!(18245220736usize, get_data_size(2047 * ETHASH_EPOCH_LENGTH));
|
||||
}
|
||||
#[test]
|
||||
fn test_get_data_size() {
|
||||
// https://github.com/ethereum/wiki/wiki/Ethash/ef6b93f9596746a088ea95d01ca2778be43ae68f#data-sizes
|
||||
assert_eq!(1073739904usize, get_data_size(0));
|
||||
assert_eq!(1073739904usize, get_data_size(1));
|
||||
assert_eq!(1073739904usize, get_data_size(ETHASH_EPOCH_LENGTH - 1));
|
||||
assert_eq!(1082130304usize, get_data_size(ETHASH_EPOCH_LENGTH));
|
||||
assert_eq!(1082130304usize, get_data_size(ETHASH_EPOCH_LENGTH + 1));
|
||||
assert_eq!(18236833408usize, get_data_size(2046 * ETHASH_EPOCH_LENGTH));
|
||||
assert_eq!(18245220736usize, get_data_size(2047 * ETHASH_EPOCH_LENGTH));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_difficulty_test() {
|
||||
let hash = [
|
||||
0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, 0x5b, 0xf2, 0xbe, 0xe4, 0x0a, 0xb3,
|
||||
0x35, 0x8a, 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f, 0x5e, 0x59, 0x5e, 0xab, 0x55, 0x94,
|
||||
0x05, 0x52, 0x7d, 0x72,
|
||||
];
|
||||
let mix_hash = [
|
||||
0x1f, 0xff, 0x04, 0xce, 0xc9, 0x41, 0x73, 0xfd, 0x59, 0x1e, 0x3d, 0x89, 0x60, 0xce,
|
||||
0x6b, 0xdf, 0x8b, 0x19, 0x71, 0x04, 0x8c, 0x71, 0xff, 0x93, 0x7b, 0xb2, 0xd3, 0x2a,
|
||||
0x64, 0x31, 0xab, 0x6d,
|
||||
];
|
||||
let nonce = 0xd7b3ac70a301a249;
|
||||
let boundary_good = [
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3e, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2,
|
||||
0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a,
|
||||
0xe9, 0x7e, 0x53, 0x84,
|
||||
];
|
||||
assert_eq!(
|
||||
quick_get_difficulty(&hash, nonce, &mix_hash, false)[..],
|
||||
boundary_good[..]
|
||||
);
|
||||
let boundary_bad = [
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3a, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2,
|
||||
0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a,
|
||||
0xe9, 0x7e, 0x53, 0x84,
|
||||
];
|
||||
assert!(quick_get_difficulty(&hash, nonce, &mix_hash, false)[..] != boundary_bad[..]);
|
||||
}
|
||||
#[test]
|
||||
fn test_difficulty_test() {
|
||||
let hash = [
|
||||
0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, 0x5b, 0xf2, 0xbe, 0xe4, 0x0a, 0xb3,
|
||||
0x35, 0x8a, 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f, 0x5e, 0x59, 0x5e, 0xab, 0x55, 0x94,
|
||||
0x05, 0x52, 0x7d, 0x72,
|
||||
];
|
||||
let mix_hash = [
|
||||
0x1f, 0xff, 0x04, 0xce, 0xc9, 0x41, 0x73, 0xfd, 0x59, 0x1e, 0x3d, 0x89, 0x60, 0xce,
|
||||
0x6b, 0xdf, 0x8b, 0x19, 0x71, 0x04, 0x8c, 0x71, 0xff, 0x93, 0x7b, 0xb2, 0xd3, 0x2a,
|
||||
0x64, 0x31, 0xab, 0x6d,
|
||||
];
|
||||
let nonce = 0xd7b3ac70a301a249;
|
||||
let boundary_good = [
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3e, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2,
|
||||
0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a,
|
||||
0xe9, 0x7e, 0x53, 0x84,
|
||||
];
|
||||
assert_eq!(quick_get_difficulty(&hash, nonce, &mix_hash)[..], boundary_good[..]);
|
||||
let boundary_bad = [
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3a, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2,
|
||||
0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a,
|
||||
0xe9, 0x7e, 0x53, 0x84,
|
||||
];
|
||||
assert!(quick_get_difficulty(&hash, nonce, &mix_hash)[..] != boundary_bad[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_light_compute() {
|
||||
let hash = [
|
||||
0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, 0x5b, 0xf2, 0xbe, 0xe4, 0x0a, 0xb3,
|
||||
0x35, 0x8a, 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f, 0x5e, 0x59, 0x5e, 0xab, 0x55, 0x94,
|
||||
0x05, 0x52, 0x7d, 0x72,
|
||||
];
|
||||
let mix_hash = [
|
||||
0x1f, 0xff, 0x04, 0xce, 0xc9, 0x41, 0x73, 0xfd, 0x59, 0x1e, 0x3d, 0x89, 0x60, 0xce,
|
||||
0x6b, 0xdf, 0x8b, 0x19, 0x71, 0x04, 0x8c, 0x71, 0xff, 0x93, 0x7b, 0xb2, 0xd3, 0x2a,
|
||||
0x64, 0x31, 0xab, 0x6d,
|
||||
];
|
||||
let boundary = [
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3e, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2,
|
||||
0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a,
|
||||
0xe9, 0x7e, 0x53, 0x84,
|
||||
];
|
||||
let nonce = 0xd7b3ac70a301a249;
|
||||
#[test]
|
||||
fn test_light_compute() {
|
||||
let hash = [
|
||||
0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, 0x5b, 0xf2, 0xbe, 0xe4, 0x0a, 0xb3,
|
||||
0x35, 0x8a, 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f, 0x5e, 0x59, 0x5e, 0xab, 0x55, 0x94,
|
||||
0x05, 0x52, 0x7d, 0x72,
|
||||
];
|
||||
let mix_hash = [
|
||||
0x1f, 0xff, 0x04, 0xce, 0xc9, 0x41, 0x73, 0xfd, 0x59, 0x1e, 0x3d, 0x89, 0x60, 0xce,
|
||||
0x6b, 0xdf, 0x8b, 0x19, 0x71, 0x04, 0x8c, 0x71, 0xff, 0x93, 0x7b, 0xb2, 0xd3, 0x2a,
|
||||
0x64, 0x31, 0xab, 0x6d,
|
||||
];
|
||||
let boundary = [
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3e, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2,
|
||||
0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a,
|
||||
0xe9, 0x7e, 0x53, 0x84,
|
||||
];
|
||||
let nonce = 0xd7b3ac70a301a249;
|
||||
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
// difficulty = 0x085657254bd9u64;
|
||||
let light = NodeCacheBuilder::new(None, u64::max_value()).light(tempdir.path(), 486382);
|
||||
let result = light_compute(&light, &hash, nonce);
|
||||
assert_eq!(result.mix_hash[..], mix_hash[..]);
|
||||
assert_eq!(result.value[..], boundary[..]);
|
||||
}
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
// difficulty = 0x085657254bd9u64;
|
||||
let light = NodeCacheBuilder::new(None).light(tempdir.path(), 486382);
|
||||
let result = light_compute(&light, &hash, nonce);
|
||||
assert_eq!(result.mix_hash[..], mix_hash[..]);
|
||||
assert_eq!(result.value[..], boundary[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_drop_old_data() {
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let builder = NodeCacheBuilder::new(None, u64::max_value());
|
||||
let first = builder
|
||||
.light(tempdir.path(), 0)
|
||||
.to_file()
|
||||
.unwrap()
|
||||
.to_owned();
|
||||
#[test]
|
||||
fn test_drop_old_data() {
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let builder = NodeCacheBuilder::new(None);
|
||||
let first = builder.light(tempdir.path(), 0).to_file().unwrap().to_owned();
|
||||
|
||||
let second = builder
|
||||
.light(tempdir.path(), ETHASH_EPOCH_LENGTH)
|
||||
.to_file()
|
||||
.unwrap()
|
||||
.to_owned();
|
||||
assert!(fs::metadata(&first).is_ok());
|
||||
let second = builder.light(tempdir.path(), ETHASH_EPOCH_LENGTH).to_file().unwrap().to_owned();
|
||||
assert!(fs::metadata(&first).is_ok());
|
||||
|
||||
let _ = builder
|
||||
.light(tempdir.path(), ETHASH_EPOCH_LENGTH * 2)
|
||||
.to_file();
|
||||
assert!(fs::metadata(&first).is_err());
|
||||
assert!(fs::metadata(&second).is_ok());
|
||||
let _ = builder.light(tempdir.path(), ETHASH_EPOCH_LENGTH * 2).to_file();
|
||||
assert!(fs::metadata(&first).is_err());
|
||||
assert!(fs::metadata(&second).is_ok());
|
||||
|
||||
let _ = builder
|
||||
.light(tempdir.path(), ETHASH_EPOCH_LENGTH * 3)
|
||||
.to_file();
|
||||
assert!(fs::metadata(&second).is_err());
|
||||
}
|
||||
let _ = builder.light(tempdir.path(), ETHASH_EPOCH_LENGTH * 3).to_file();
|
||||
assert!(fs::metadata(&second).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,66 +1,56 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate keccak_hash as hash;
|
||||
|
||||
pub type H256 = [u8; 32];
|
||||
|
||||
pub mod keccak_512 {
|
||||
use super::hash;
|
||||
use super::hash;
|
||||
|
||||
pub use self::hash::keccak_512_unchecked as unchecked;
|
||||
pub use self::hash::keccak_512_unchecked as unchecked;
|
||||
|
||||
pub fn write(input: &[u8], output: &mut [u8]) {
|
||||
hash::keccak_512(input, output);
|
||||
}
|
||||
pub fn write(input: &[u8], output: &mut [u8]) {
|
||||
hash::keccak_512(input, output);
|
||||
}
|
||||
|
||||
pub fn inplace(input: &mut [u8]) {
|
||||
// This is safe since `keccak_*` uses an internal buffer and copies the result to the output. This
|
||||
// means that we can reuse the input buffer for both input and output.
|
||||
unsafe {
|
||||
hash::keccak_512_unchecked(
|
||||
input.as_mut_ptr(),
|
||||
input.len(),
|
||||
input.as_ptr(),
|
||||
input.len(),
|
||||
);
|
||||
}
|
||||
}
|
||||
pub fn inplace(input: &mut [u8]) {
|
||||
// This is safe since `keccak_*` uses an internal buffer and copies the result to the output. This
|
||||
// means that we can reuse the input buffer for both input and output.
|
||||
unsafe {
|
||||
hash::keccak_512_unchecked(input.as_mut_ptr(), input.len(), input.as_ptr(), input.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod keccak_256 {
|
||||
use super::hash;
|
||||
use super::hash;
|
||||
|
||||
pub use self::hash::keccak_256_unchecked as unchecked;
|
||||
pub use self::hash::keccak_256_unchecked as unchecked;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn write(input: &[u8], output: &mut [u8]) {
|
||||
hash::keccak_256(input, output);
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn write(input: &[u8], output: &mut [u8]) {
|
||||
hash::keccak_256(input, output);
|
||||
}
|
||||
|
||||
pub fn inplace(input: &mut [u8]) {
|
||||
// This is safe since `keccak_*` uses an internal buffer and copies the result to the output. This
|
||||
// means that we can reuse the input buffer for both input and output.
|
||||
unsafe {
|
||||
hash::keccak_256_unchecked(
|
||||
input.as_mut_ptr(),
|
||||
input.len(),
|
||||
input.as_ptr(),
|
||||
input.len(),
|
||||
);
|
||||
}
|
||||
}
|
||||
pub fn inplace(input: &mut [u8]) {
|
||||
// This is safe since `keccak_*` uses an internal buffer and copies the result to the output. This
|
||||
// means that we can reuse the input buffer for both input and output.
|
||||
unsafe {
|
||||
hash::keccak_256_unchecked(input.as_mut_ptr(), input.len(), input.as_ptr(), input.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#![cfg_attr(feature = "benches", feature(test))]
|
||||
|
||||
extern crate either;
|
||||
extern crate ethereum_types;
|
||||
@@ -25,246 +27,289 @@ extern crate crunchy;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate rustc_hex;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate serde_json;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate tempdir;
|
||||
|
||||
#[cfg(feature = "bench")]
|
||||
pub mod compute;
|
||||
#[cfg(not(feature = "bench"))]
|
||||
mod compute;
|
||||
|
||||
mod seed_compute;
|
||||
mod cache;
|
||||
mod keccak;
|
||||
mod seed_compute;
|
||||
mod shared;
|
||||
|
||||
#[cfg(feature = "bench")]
|
||||
pub mod progpow;
|
||||
#[cfg(not(feature = "bench"))]
|
||||
mod progpow;
|
||||
|
||||
pub use cache::{NodeCacheBuilder, OptimizeFor};
|
||||
pub use compute::{ProofOfWork, quick_get_difficulty, slow_hash_block_number};
|
||||
use compute::Light;
|
||||
pub use compute::{quick_get_difficulty, slow_hash_block_number, ProofOfWork};
|
||||
use ethereum_types::{U256, U512};
|
||||
use keccak::H256;
|
||||
use parking_lot::Mutex;
|
||||
pub use seed_compute::SeedHashCompute;
|
||||
pub use shared::ETHASH_EPOCH_LENGTH;
|
||||
use std::{
|
||||
mem,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use std::mem;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
struct LightCache {
|
||||
recent_epoch: Option<u64>,
|
||||
recent: Option<Arc<Light>>,
|
||||
prev_epoch: Option<u64>,
|
||||
prev: Option<Arc<Light>>,
|
||||
recent_epoch: Option<u64>,
|
||||
recent: Option<Arc<Light>>,
|
||||
prev_epoch: Option<u64>,
|
||||
prev: Option<Arc<Light>>,
|
||||
}
|
||||
|
||||
/// Light/Full cache manager.
|
||||
pub struct EthashManager {
|
||||
nodecache_builder: NodeCacheBuilder,
|
||||
cache: Mutex<LightCache>,
|
||||
cache_dir: PathBuf,
|
||||
progpow_transition: u64,
|
||||
nodecache_builder: NodeCacheBuilder,
|
||||
cache: Mutex<LightCache>,
|
||||
cache_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl EthashManager {
|
||||
/// Create a new new instance of ethash manager
|
||||
pub fn new<T: Into<Option<OptimizeFor>>>(
|
||||
cache_dir: &Path,
|
||||
optimize_for: T,
|
||||
progpow_transition: u64,
|
||||
) -> EthashManager {
|
||||
EthashManager {
|
||||
cache_dir: cache_dir.to_path_buf(),
|
||||
nodecache_builder: NodeCacheBuilder::new(
|
||||
optimize_for.into().unwrap_or_default(),
|
||||
progpow_transition,
|
||||
),
|
||||
progpow_transition: progpow_transition,
|
||||
cache: Mutex::new(LightCache {
|
||||
recent_epoch: None,
|
||||
recent: None,
|
||||
prev_epoch: None,
|
||||
prev: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
/// Create a new new instance of ethash manager
|
||||
pub fn new<T: Into<Option<OptimizeFor>>>(cache_dir: &Path, optimize_for: T) -> EthashManager {
|
||||
EthashManager {
|
||||
cache_dir: cache_dir.to_path_buf(),
|
||||
nodecache_builder: NodeCacheBuilder::new(optimize_for.into().unwrap_or_default()),
|
||||
cache: Mutex::new(LightCache {
|
||||
recent_epoch: None,
|
||||
recent: None,
|
||||
prev_epoch: None,
|
||||
prev: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the light client data
|
||||
/// `block_number` - Block number to check
|
||||
/// `light` - The light client handler
|
||||
/// `header_hash` - The header hash to pack into the mix
|
||||
/// `nonce` - The nonce to pack into the mix
|
||||
pub fn compute_light(&self, block_number: u64, header_hash: &H256, nonce: u64) -> ProofOfWork {
|
||||
let epoch = block_number / ETHASH_EPOCH_LENGTH;
|
||||
let light = {
|
||||
let mut lights = self.cache.lock();
|
||||
let light = if block_number == self.progpow_transition {
|
||||
// we need to regenerate the cache to trigger algorithm change to progpow inside `Light`
|
||||
None
|
||||
} else {
|
||||
match lights.recent_epoch.clone() {
|
||||
Some(ref e) if *e == epoch => lights.recent.clone(),
|
||||
_ => match lights.prev_epoch.clone() {
|
||||
Some(e) if e == epoch => {
|
||||
// don't swap if recent is newer.
|
||||
if lights.recent_epoch > lights.prev_epoch {
|
||||
None
|
||||
} else {
|
||||
// swap
|
||||
let t = lights.prev_epoch;
|
||||
lights.prev_epoch = lights.recent_epoch;
|
||||
lights.recent_epoch = t;
|
||||
let t = lights.prev.clone();
|
||||
lights.prev = lights.recent.clone();
|
||||
lights.recent = t;
|
||||
lights.recent.clone()
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
match light {
|
||||
None => {
|
||||
let light = match self
|
||||
.nodecache_builder
|
||||
.light_from_file(&self.cache_dir, block_number)
|
||||
{
|
||||
Ok(light) => Arc::new(light),
|
||||
Err(e) => {
|
||||
debug!("Light cache file not found for {}:{}", block_number, e);
|
||||
let mut light =
|
||||
self.nodecache_builder.light(&self.cache_dir, block_number);
|
||||
if let Err(e) = light.to_file() {
|
||||
warn!("Light cache file write error: {}", e);
|
||||
}
|
||||
Arc::new(light)
|
||||
}
|
||||
};
|
||||
lights.prev_epoch = mem::replace(&mut lights.recent_epoch, Some(epoch));
|
||||
lights.prev = mem::replace(&mut lights.recent, Some(light.clone()));
|
||||
light
|
||||
}
|
||||
Some(light) => light,
|
||||
}
|
||||
};
|
||||
light.compute(header_hash, nonce, block_number)
|
||||
}
|
||||
/// Calculate the light client data
|
||||
/// `block_number` - Block number to check
|
||||
/// `light` - The light client handler
|
||||
/// `header_hash` - The header hash to pack into the mix
|
||||
/// `nonce` - The nonce to pack into the mix
|
||||
pub fn compute_light(&self, block_number: u64, header_hash: &H256, nonce: u64) -> ProofOfWork {
|
||||
let epoch = block_number / ETHASH_EPOCH_LENGTH;
|
||||
let light = {
|
||||
let mut lights = self.cache.lock();
|
||||
let light = match lights.recent_epoch.clone() {
|
||||
Some(ref e) if *e == epoch => lights.recent.clone(),
|
||||
_ => match lights.prev_epoch.clone() {
|
||||
Some(e) if e == epoch => {
|
||||
// don't swap if recent is newer.
|
||||
if lights.recent_epoch > lights.prev_epoch {
|
||||
None
|
||||
} else {
|
||||
// swap
|
||||
let t = lights.prev_epoch;
|
||||
lights.prev_epoch = lights.recent_epoch;
|
||||
lights.recent_epoch = t;
|
||||
let t = lights.prev.clone();
|
||||
lights.prev = lights.recent.clone();
|
||||
lights.recent = t;
|
||||
lights.recent.clone()
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
};
|
||||
match light {
|
||||
None => {
|
||||
let light = match self.nodecache_builder.light_from_file(
|
||||
&self.cache_dir,
|
||||
block_number,
|
||||
) {
|
||||
Ok(light) => Arc::new(light),
|
||||
Err(e) => {
|
||||
debug!("Light cache file not found for {}:{}", block_number, e);
|
||||
let mut light = self.nodecache_builder.light(
|
||||
&self.cache_dir,
|
||||
block_number,
|
||||
);
|
||||
if let Err(e) = light.to_file() {
|
||||
warn!("Light cache file write error: {}", e);
|
||||
}
|
||||
Arc::new(light)
|
||||
}
|
||||
};
|
||||
lights.prev_epoch = mem::replace(&mut lights.recent_epoch, Some(epoch));
|
||||
lights.prev = mem::replace(&mut lights.recent, Some(light.clone()));
|
||||
light
|
||||
}
|
||||
Some(light) => light,
|
||||
}
|
||||
};
|
||||
light.compute(header_hash, nonce)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert an Ethash boundary to its original difficulty. Basically just `f(x) = 2^256 / x`.
|
||||
pub fn boundary_to_difficulty(boundary: ðereum_types::H256) -> U256 {
|
||||
difficulty_to_boundary_aux(&**boundary)
|
||||
difficulty_to_boundary_aux(&**boundary)
|
||||
}
|
||||
|
||||
/// Convert an Ethash difficulty to the target boundary. Basically just `f(x) = 2^256 / x`.
|
||||
pub fn difficulty_to_boundary(difficulty: &U256) -> ethereum_types::H256 {
|
||||
difficulty_to_boundary_aux(difficulty).into()
|
||||
difficulty_to_boundary_aux(difficulty).into()
|
||||
}
|
||||
|
||||
fn difficulty_to_boundary_aux<T: Into<U512>>(difficulty: T) -> ethereum_types::U256 {
|
||||
let difficulty = difficulty.into();
|
||||
let difficulty = difficulty.into();
|
||||
|
||||
assert!(!difficulty.is_zero());
|
||||
assert!(!difficulty.is_zero());
|
||||
|
||||
if difficulty == U512::one() {
|
||||
U256::max_value()
|
||||
} else {
|
||||
// difficulty > 1, so result should never overflow 256 bits
|
||||
U256::from((U512::one() << 256) / difficulty)
|
||||
}
|
||||
if difficulty == U512::one() {
|
||||
U256::max_value()
|
||||
} else {
|
||||
// difficulty > 1, so result should never overflow 256 bits
|
||||
U256::from((U512::one() << 256) / difficulty)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lru() {
|
||||
use tempdir::TempDir;
|
||||
use tempdir::TempDir;
|
||||
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let ethash = EthashManager::new(tempdir.path(), None, u64::max_value());
|
||||
let hash = [0u8; 32];
|
||||
ethash.compute_light(1, &hash, 1);
|
||||
ethash.compute_light(50000, &hash, 1);
|
||||
assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 1);
|
||||
assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 0);
|
||||
ethash.compute_light(1, &hash, 1);
|
||||
assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 0);
|
||||
assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 1);
|
||||
ethash.compute_light(70000, &hash, 1);
|
||||
assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 2);
|
||||
assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 0);
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let ethash = EthashManager::new(tempdir.path(), None);
|
||||
let hash = [0u8; 32];
|
||||
ethash.compute_light(1, &hash, 1);
|
||||
ethash.compute_light(50000, &hash, 1);
|
||||
assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 1);
|
||||
assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 0);
|
||||
ethash.compute_light(1, &hash, 1);
|
||||
assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 0);
|
||||
assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 1);
|
||||
ethash.compute_light(70000, &hash, 1);
|
||||
assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 2);
|
||||
assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_difficulty_to_boundary() {
|
||||
use ethereum_types::H256;
|
||||
use std::str::FromStr;
|
||||
use ethereum_types::H256;
|
||||
use std::str::FromStr;
|
||||
|
||||
assert_eq!(
|
||||
difficulty_to_boundary(&U256::from(1)),
|
||||
H256::from(U256::max_value())
|
||||
);
|
||||
assert_eq!(
|
||||
difficulty_to_boundary(&U256::from(2)),
|
||||
H256::from_str("8000000000000000000000000000000000000000000000000000000000000000").unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
difficulty_to_boundary(&U256::from(4)),
|
||||
H256::from_str("4000000000000000000000000000000000000000000000000000000000000000").unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
difficulty_to_boundary(&U256::from(32)),
|
||||
H256::from_str("0800000000000000000000000000000000000000000000000000000000000000").unwrap()
|
||||
);
|
||||
assert_eq!(difficulty_to_boundary(&U256::from(1)), H256::from(U256::max_value()));
|
||||
assert_eq!(difficulty_to_boundary(&U256::from(2)), H256::from_str("8000000000000000000000000000000000000000000000000000000000000000").unwrap());
|
||||
assert_eq!(difficulty_to_boundary(&U256::from(4)), H256::from_str("4000000000000000000000000000000000000000000000000000000000000000").unwrap());
|
||||
assert_eq!(difficulty_to_boundary(&U256::from(32)), H256::from_str("0800000000000000000000000000000000000000000000000000000000000000").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_difficulty_to_boundary_regression() {
|
||||
use ethereum_types::H256;
|
||||
use ethereum_types::H256;
|
||||
|
||||
// the last bit was originally being truncated when performing the conversion
|
||||
// https://github.com/openethereum/openethereum/issues/8397
|
||||
for difficulty in 1..9 {
|
||||
assert_eq!(
|
||||
U256::from(difficulty),
|
||||
boundary_to_difficulty(&difficulty_to_boundary(&difficulty.into()))
|
||||
);
|
||||
assert_eq!(
|
||||
H256::from(difficulty),
|
||||
difficulty_to_boundary(&boundary_to_difficulty(&difficulty.into()))
|
||||
);
|
||||
assert_eq!(
|
||||
U256::from(difficulty),
|
||||
boundary_to_difficulty(&boundary_to_difficulty(&difficulty.into()).into())
|
||||
);
|
||||
assert_eq!(
|
||||
H256::from(difficulty),
|
||||
difficulty_to_boundary(&difficulty_to_boundary(&difficulty.into()).into())
|
||||
);
|
||||
}
|
||||
// the last bit was originally being truncated when performing the conversion
|
||||
// https://github.com/paritytech/parity-ethereum/issues/8397
|
||||
for difficulty in 1..9 {
|
||||
assert_eq!(U256::from(difficulty), boundary_to_difficulty(&difficulty_to_boundary(&difficulty.into())));
|
||||
assert_eq!(H256::from(difficulty), difficulty_to_boundary(&boundary_to_difficulty(&difficulty.into())));
|
||||
assert_eq!(U256::from(difficulty), boundary_to_difficulty(&boundary_to_difficulty(&difficulty.into()).into()));
|
||||
assert_eq!(H256::from(difficulty), difficulty_to_boundary(&difficulty_to_boundary(&difficulty.into()).into()));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_difficulty_to_boundary_panics_on_zero() {
|
||||
difficulty_to_boundary(&U256::from(0));
|
||||
difficulty_to_boundary(&U256::from(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_boundary_to_difficulty_panics_on_zero() {
|
||||
boundary_to_difficulty(ðereum_types::H256::from(0));
|
||||
boundary_to_difficulty(ðereum_types::H256::from(0));
|
||||
}
|
||||
|
||||
#[cfg(feature = "benches")]
|
||||
mod benchmarks {
|
||||
extern crate test;
|
||||
|
||||
use self::test::Bencher;
|
||||
use cache::{NodeCacheBuilder, OptimizeFor};
|
||||
use compute::{Light, light_compute};
|
||||
|
||||
const HASH: [u8; 32] = [0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, 0x5b, 0xf2, 0xbe,
|
||||
0xe4, 0x0a, 0xb3, 0x35, 0x8a, 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f,
|
||||
0x5e, 0x59, 0x5e, 0xab, 0x55, 0x94, 0x05, 0x52, 0x7d, 0x72];
|
||||
const NONCE: u64 = 0xd7b3ac70a301a249;
|
||||
|
||||
#[bench]
|
||||
fn bench_light_compute_memmap(b: &mut Bencher) {
|
||||
use std::env;
|
||||
|
||||
let builder = NodeCacheBuilder::new(OptimizeFor::Memory);
|
||||
let light = builder.light(&env::temp_dir(), 486382);
|
||||
|
||||
b.iter(|| light_compute(&light, &HASH, NONCE));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_light_compute_memory(b: &mut Bencher) {
|
||||
use std::env;
|
||||
|
||||
let builder = NodeCacheBuilder::new(OptimizeFor::Cpu);
|
||||
let light = builder.light(&env::temp_dir(), 486382);
|
||||
|
||||
b.iter(|| light_compute(&light, &HASH, NONCE));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
#[ignore]
|
||||
fn bench_light_new_round_trip_memmap(b: &mut Bencher) {
|
||||
use std::env;
|
||||
|
||||
b.iter(|| {
|
||||
let builder = NodeCacheBuilder::new(OptimizeFor::Memory);
|
||||
let light = builder.light(&env::temp_dir(), 486382);
|
||||
light_compute(&light, &HASH, NONCE);
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
#[ignore]
|
||||
fn bench_light_new_round_trip_memory(b: &mut Bencher) {
|
||||
use std::env;
|
||||
|
||||
b.iter(|| {
|
||||
let builder = NodeCacheBuilder::new(OptimizeFor::Cpu);
|
||||
let light = builder.light(&env::temp_dir(), 486382);
|
||||
light_compute(&light, &HASH, NONCE);
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_light_from_file_round_trip_memory(b: &mut Bencher) {
|
||||
use std::env;
|
||||
|
||||
let dir = env::temp_dir();
|
||||
let height = 486382;
|
||||
{
|
||||
let builder = NodeCacheBuilder::new(OptimizeFor::Cpu);
|
||||
let mut dummy = builder.light(&dir, height);
|
||||
dummy.to_file().unwrap();
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
let builder = NodeCacheBuilder::new(OptimizeFor::Cpu);
|
||||
let light = builder.light_from_file(&dir, 486382).unwrap();
|
||||
light_compute(&light, &HASH, NONCE);
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_light_from_file_round_trip_memmap(b: &mut Bencher) {
|
||||
use std::env;
|
||||
|
||||
let dir = env::temp_dir();
|
||||
let height = 486382;
|
||||
|
||||
{
|
||||
let builder = NodeCacheBuilder::new(OptimizeFor::Memory);
|
||||
let mut dummy = builder.light(&dir, height);
|
||||
dummy.to_file().unwrap();
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
let builder = NodeCacheBuilder::new(OptimizeFor::Memory);
|
||||
let light = builder.light_from_file(&dir, 486382).unwrap();
|
||||
light_compute(&light, &HASH, NONCE);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,582 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use compute::{calculate_dag_item, FNV_PRIME};
|
||||
use keccak::H256;
|
||||
use shared::{get_data_size, Node, ETHASH_ACCESSES, ETHASH_MIX_BYTES};
|
||||
|
||||
const PROGPOW_CACHE_BYTES: usize = 16 * 1024;
|
||||
const PROGPOW_CACHE_WORDS: usize = PROGPOW_CACHE_BYTES / 4;
|
||||
const PROGPOW_CNT_CACHE: usize = 12;
|
||||
const PROGPOW_CNT_MATH: usize = 20;
|
||||
const PROGPOW_CNT_DAG: usize = ETHASH_ACCESSES;
|
||||
const PROGPOW_DAG_LOADS: usize = 4;
|
||||
const PROGPOW_MIX_BYTES: usize = 2 * ETHASH_MIX_BYTES;
|
||||
const PROGPOW_PERIOD_LENGTH: usize = 50; // blocks per progpow epoch (N)
|
||||
const PROGPOW_LANES: usize = 16;
|
||||
const PROGPOW_REGS: usize = 32;
|
||||
|
||||
const FNV_HASH: u32 = 0x811c9dc5;
|
||||
|
||||
const KECCAKF_RNDC: [u32; 24] = [
|
||||
0x00000001, 0x00008082, 0x0000808a, 0x80008000, 0x0000808b, 0x80000001, 0x80008081, 0x00008009,
|
||||
0x0000008a, 0x00000088, 0x80008009, 0x8000000a, 0x8000808b, 0x0000008b, 0x00008089, 0x00008003,
|
||||
0x00008002, 0x00000080, 0x0000800a, 0x8000000a, 0x80008081, 0x00008080, 0x80000001, 0x80008008,
|
||||
];
|
||||
|
||||
const KECCAKF_ROTC: [u32; 24] = [
|
||||
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
|
||||
];
|
||||
|
||||
const KECCAKF_PILN: [usize; 24] = [
|
||||
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
|
||||
];
|
||||
|
||||
fn keccak_f800_round(st: &mut [u32; 25], r: usize) {
|
||||
// Theta
|
||||
let mut bc = [0u32; 5];
|
||||
for i in 0..bc.len() {
|
||||
bc[i] = st[i] ^ st[i + 5] ^ st[i + 10] ^ st[i + 15] ^ st[i + 20];
|
||||
}
|
||||
|
||||
for i in 0..bc.len() {
|
||||
let t = bc[(i + 4) % 5] ^ bc[(i + 1) % 5].rotate_left(1);
|
||||
for j in (0..st.len()).step_by(5) {
|
||||
st[j + i] ^= t;
|
||||
}
|
||||
}
|
||||
|
||||
// Rho Pi
|
||||
let mut t = st[1];
|
||||
|
||||
debug_assert_eq!(KECCAKF_ROTC.len(), 24);
|
||||
for i in 0..24 {
|
||||
let j = KECCAKF_PILN[i];
|
||||
bc[0] = st[j];
|
||||
st[j] = t.rotate_left(KECCAKF_ROTC[i]);
|
||||
t = bc[0];
|
||||
}
|
||||
|
||||
// Chi
|
||||
for j in (0..st.len()).step_by(5) {
|
||||
for i in 0..bc.len() {
|
||||
bc[i] = st[j + i];
|
||||
}
|
||||
for i in 0..bc.len() {
|
||||
st[j + i] ^= (!bc[(i + 1) % 5]) & bc[(i + 2) % 5];
|
||||
}
|
||||
}
|
||||
|
||||
// Iota
|
||||
debug_assert!(r < KECCAKF_RNDC.len());
|
||||
st[0] ^= KECCAKF_RNDC[r];
|
||||
}
|
||||
|
||||
fn keccak_f800(header_hash: H256, nonce: u64, result: [u32; 8], st: &mut [u32; 25]) {
|
||||
for i in 0..8 {
|
||||
st[i] = (header_hash[4 * i] as u32)
|
||||
+ ((header_hash[4 * i + 1] as u32) << 8)
|
||||
+ ((header_hash[4 * i + 2] as u32) << 16)
|
||||
+ ((header_hash[4 * i + 3] as u32) << 24);
|
||||
}
|
||||
|
||||
st[8] = nonce as u32;
|
||||
st[9] = (nonce >> 32) as u32;
|
||||
|
||||
for i in 0..8 {
|
||||
st[10 + i] = result[i];
|
||||
}
|
||||
|
||||
for r in 0..22 {
|
||||
keccak_f800_round(st, r);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn keccak_f800_short(header_hash: H256, nonce: u64, result: [u32; 8]) -> u64 {
|
||||
let mut st = [0u32; 25];
|
||||
keccak_f800(header_hash, nonce, result, &mut st);
|
||||
(st[0].swap_bytes() as u64) << 32 | st[1].swap_bytes() as u64
|
||||
}
|
||||
|
||||
pub fn keccak_f800_long(header_hash: H256, nonce: u64, result: [u32; 8]) -> H256 {
|
||||
let mut st = [0u32; 25];
|
||||
keccak_f800(header_hash, nonce, result, &mut st);
|
||||
|
||||
// NOTE: transmute from `[u32; 8]` to `[u8; 32]`
|
||||
unsafe { std::mem::transmute([st[0], st[1], st[2], st[3], st[4], st[5], st[6], st[7]]) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn fnv1a_hash(h: u32, d: u32) -> u32 {
|
||||
(h ^ d).wrapping_mul(FNV_PRIME)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Kiss99 {
|
||||
z: u32,
|
||||
w: u32,
|
||||
jsr: u32,
|
||||
jcong: u32,
|
||||
}
|
||||
|
||||
impl Kiss99 {
|
||||
fn new(z: u32, w: u32, jsr: u32, jcong: u32) -> Kiss99 {
|
||||
Kiss99 { z, w, jsr, jcong }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
self.z = 36969u32
|
||||
.wrapping_mul(self.z & 65535)
|
||||
.wrapping_add(self.z >> 16);
|
||||
self.w = 18000u32
|
||||
.wrapping_mul(self.w & 65535)
|
||||
.wrapping_add(self.w >> 16);
|
||||
let mwc = (self.z << 16).wrapping_add(self.w);
|
||||
self.jsr ^= self.jsr << 17;
|
||||
self.jsr ^= self.jsr >> 13;
|
||||
self.jsr ^= self.jsr << 5;
|
||||
self.jcong = 69069u32.wrapping_mul(self.jcong).wrapping_add(1234567);
|
||||
|
||||
(mwc ^ self.jcong).wrapping_add(self.jsr)
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_mix(seed: u64, lane_id: u32) -> [u32; PROGPOW_REGS] {
|
||||
// Use FNV to expand the per-warp seed to per-lane
|
||||
// Use KISS to expand the per-lane seed to fill mix
|
||||
let z = fnv1a_hash(FNV_HASH, seed as u32);
|
||||
let w = fnv1a_hash(z, (seed >> 32) as u32);
|
||||
let jsr = fnv1a_hash(w, lane_id);
|
||||
let jcong = fnv1a_hash(jsr, lane_id);
|
||||
|
||||
let mut rnd = Kiss99::new(z, w, jsr, jcong);
|
||||
|
||||
let mut mix = [0; PROGPOW_REGS];
|
||||
|
||||
debug_assert_eq!(PROGPOW_REGS, 32);
|
||||
for i in 0..32 {
|
||||
mix[i] = rnd.next_u32();
|
||||
}
|
||||
|
||||
mix
|
||||
}
|
||||
|
||||
// Merge new data from b into the value in a. Assuming A has high entropy only
|
||||
// do ops that retain entropy even if B is low entropy (IE don't do A&B)
|
||||
fn merge(a: u32, b: u32, r: u32) -> u32 {
|
||||
match r % 4 {
|
||||
0 => a.wrapping_mul(33).wrapping_add(b),
|
||||
1 => (a ^ b).wrapping_mul(33),
|
||||
2 => a.rotate_left(((r >> 16) % 31) + 1) ^ b,
|
||||
_ => a.rotate_right(((r >> 16) % 31) + 1) ^ b,
|
||||
}
|
||||
}
|
||||
|
||||
fn math(a: u32, b: u32, r: u32) -> u32 {
|
||||
match r % 11 {
|
||||
0 => a.wrapping_add(b),
|
||||
1 => a.wrapping_mul(b),
|
||||
2 => ((a as u64).wrapping_mul(b as u64) >> 32) as u32,
|
||||
3 => a.min(b),
|
||||
4 => a.rotate_left(b),
|
||||
5 => a.rotate_right(b),
|
||||
6 => a & b,
|
||||
7 => a | b,
|
||||
8 => a ^ b,
|
||||
9 => a.leading_zeros() + b.leading_zeros(),
|
||||
_ => a.count_ones() + b.count_ones(),
|
||||
}
|
||||
}
|
||||
|
||||
fn progpow_init(seed: u64) -> (Kiss99, [u32; PROGPOW_REGS], [u32; PROGPOW_REGS]) {
|
||||
let z = fnv1a_hash(FNV_HASH, seed as u32);
|
||||
let w = fnv1a_hash(z, (seed >> 32) as u32);
|
||||
let jsr = fnv1a_hash(w, seed as u32);
|
||||
let jcong = fnv1a_hash(jsr, (seed >> 32) as u32);
|
||||
|
||||
let mut rnd = Kiss99::new(z, w, jsr, jcong);
|
||||
|
||||
// Create a random sequence of mix destinations for merge() and mix sources
|
||||
// for cache reads guarantees every destination merged once and guarantees
|
||||
// no duplicate cache reads, which could be optimized away. Uses
|
||||
// Fisher-Yates shuffle.
|
||||
let mut mix_seq_dst = [0u32; PROGPOW_REGS];
|
||||
let mut mix_seq_cache = [0u32; PROGPOW_REGS];
|
||||
for i in 0..mix_seq_dst.len() {
|
||||
mix_seq_dst[i] = i as u32;
|
||||
mix_seq_cache[i] = i as u32;
|
||||
}
|
||||
|
||||
for i in (1..mix_seq_dst.len()).rev() {
|
||||
let j = rnd.next_u32() as usize % (i + 1);
|
||||
mix_seq_dst.swap(i, j);
|
||||
|
||||
let j = rnd.next_u32() as usize % (i + 1);
|
||||
mix_seq_cache.swap(i, j);
|
||||
}
|
||||
|
||||
(rnd, mix_seq_dst, mix_seq_cache)
|
||||
}
|
||||
|
||||
pub type CDag = [u32; PROGPOW_CACHE_WORDS];
|
||||
|
||||
fn progpow_loop(
|
||||
seed: u64,
|
||||
loop_: usize,
|
||||
mix: &mut [[u32; PROGPOW_REGS]; PROGPOW_LANES],
|
||||
cache: &[Node],
|
||||
c_dag: &CDag,
|
||||
data_size: usize,
|
||||
) {
|
||||
// All lanes share a base address for the global load. Global offset uses
|
||||
// mix[0] to guarantee it depends on the load result.
|
||||
let g_offset = mix[loop_ % PROGPOW_LANES][0] as usize
|
||||
% (64 * data_size / (PROGPOW_LANES * PROGPOW_DAG_LOADS));
|
||||
|
||||
// 256 bytes of dag data
|
||||
let mut dag_item = [0u32; 64];
|
||||
|
||||
// Fetch DAG nodes (64 bytes each)
|
||||
for l in 0..PROGPOW_DAG_LOADS {
|
||||
let index = g_offset * PROGPOW_LANES * PROGPOW_DAG_LOADS + l * 16;
|
||||
let node = calculate_dag_item(index as u32 / 16, cache);
|
||||
dag_item[l * 16..(l + 1) * 16].clone_from_slice(node.as_words());
|
||||
}
|
||||
|
||||
let (rnd, mix_seq_dst, mix_seq_cache) = progpow_init(seed);
|
||||
|
||||
// Lanes can execute in parallel and will be convergent
|
||||
for l in 0..mix.len() {
|
||||
let mut rnd = rnd.clone();
|
||||
|
||||
// Initialize the seed and mix destination sequence
|
||||
let mut mix_seq_dst_cnt = 0;
|
||||
let mut mix_seq_cache_cnt = 0;
|
||||
|
||||
let mut mix_dst = || {
|
||||
let res = mix_seq_dst[mix_seq_dst_cnt % PROGPOW_REGS] as usize;
|
||||
mix_seq_dst_cnt += 1;
|
||||
res
|
||||
};
|
||||
let mut mix_cache = || {
|
||||
let res = mix_seq_cache[mix_seq_cache_cnt % PROGPOW_REGS] as usize;
|
||||
mix_seq_cache_cnt += 1;
|
||||
res
|
||||
};
|
||||
|
||||
for i in 0..PROGPOW_CNT_CACHE.max(PROGPOW_CNT_MATH) {
|
||||
if i < PROGPOW_CNT_CACHE {
|
||||
// Cached memory access, lanes access random 32-bit locations
|
||||
// within the first portion of the DAG
|
||||
let offset = mix[l][mix_cache()] as usize % PROGPOW_CACHE_WORDS;
|
||||
let data = c_dag[offset];
|
||||
let dst = mix_dst();
|
||||
|
||||
mix[l][dst] = merge(mix[l][dst], data, rnd.next_u32());
|
||||
}
|
||||
|
||||
if i < PROGPOW_CNT_MATH {
|
||||
// Random math
|
||||
// Generate 2 unique sources
|
||||
let src_rnd = rnd.next_u32() % (PROGPOW_REGS * (PROGPOW_REGS - 1)) as u32;
|
||||
let src1 = src_rnd % PROGPOW_REGS as u32; // 0 <= src1 < PROGPOW_REGS
|
||||
let mut src2 = src_rnd / PROGPOW_REGS as u32; // 0 <= src2 < PROGPOW_REGS - 1
|
||||
if src2 >= src1 {
|
||||
src2 += 1; // src2 is now any reg other than src1
|
||||
}
|
||||
|
||||
let data = math(mix[l][src1 as usize], mix[l][src2 as usize], rnd.next_u32());
|
||||
let dst = mix_dst();
|
||||
|
||||
mix[l][dst] = merge(mix[l][dst], data, rnd.next_u32());
|
||||
}
|
||||
}
|
||||
|
||||
// Global load to sequential locations
|
||||
let mut data_g = [0u32; PROGPOW_DAG_LOADS];
|
||||
let index = ((l ^ loop_) % PROGPOW_LANES) * PROGPOW_DAG_LOADS;
|
||||
for i in 0..PROGPOW_DAG_LOADS {
|
||||
data_g[i] = dag_item[index + i];
|
||||
}
|
||||
|
||||
// Consume the global load data at the very end of the loop to allow
|
||||
// full latency hiding. Always merge into `mix[0]` to feed the offset
|
||||
// calculation.
|
||||
mix[l][0] = merge(mix[l][0], data_g[0], rnd.next_u32());
|
||||
for i in 1..PROGPOW_DAG_LOADS {
|
||||
let dst = mix_dst();
|
||||
mix[l][dst] = merge(mix[l][dst], data_g[i], rnd.next_u32());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn progpow(
|
||||
header_hash: H256,
|
||||
nonce: u64,
|
||||
block_number: u64,
|
||||
cache: &[Node],
|
||||
c_dag: &CDag,
|
||||
) -> (H256, H256) {
|
||||
let mut mix = [[0u32; PROGPOW_REGS]; PROGPOW_LANES];
|
||||
let mut lane_results = [0u32; PROGPOW_LANES];
|
||||
let mut result = [0u32; 8];
|
||||
|
||||
let data_size = get_data_size(block_number) / PROGPOW_MIX_BYTES;
|
||||
|
||||
// NOTE: This assert is required to aid the optimizer elide the non-zero
|
||||
// remainder check in `progpow_loop`.
|
||||
assert!(data_size > 0);
|
||||
|
||||
// Initialize mix for all lanes
|
||||
let seed = keccak_f800_short(header_hash, nonce, result);
|
||||
|
||||
for l in 0..mix.len() {
|
||||
mix[l] = fill_mix(seed, l as u32);
|
||||
}
|
||||
|
||||
// Execute the randomly generated inner loop
|
||||
let period = block_number / PROGPOW_PERIOD_LENGTH as u64;
|
||||
for i in 0..PROGPOW_CNT_DAG {
|
||||
progpow_loop(period, i, &mut mix, cache, c_dag, data_size);
|
||||
}
|
||||
|
||||
// Reduce mix data to a single per-lane result
|
||||
for l in 0..lane_results.len() {
|
||||
lane_results[l] = FNV_HASH;
|
||||
for i in 0..PROGPOW_REGS {
|
||||
lane_results[l] = fnv1a_hash(lane_results[l], mix[l][i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Reduce all lanes to a single 128-bit result
|
||||
result = [FNV_HASH; 8];
|
||||
for l in 0..PROGPOW_LANES {
|
||||
result[l % 8] = fnv1a_hash(result[l % 8], lane_results[l]);
|
||||
}
|
||||
|
||||
let digest = keccak_f800_long(header_hash, seed, result);
|
||||
|
||||
// NOTE: transmute from `[u32; 8]` to `[u8; 32]`
|
||||
let result = unsafe { ::std::mem::transmute(result) };
|
||||
|
||||
(digest, result)
|
||||
}
|
||||
|
||||
pub fn generate_cdag(cache: &[Node]) -> CDag {
|
||||
let mut c_dag = [0u32; PROGPOW_CACHE_WORDS];
|
||||
|
||||
for i in 0..PROGPOW_CACHE_WORDS / 16 {
|
||||
let node = calculate_dag_item(i as u32, cache);
|
||||
for j in 0..16 {
|
||||
c_dag[i * 16 + j] = node.as_words()[j];
|
||||
}
|
||||
}
|
||||
|
||||
c_dag
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use tempdir::TempDir;
|
||||
|
||||
use super::*;
|
||||
use cache::{NodeCacheBuilder, OptimizeFor};
|
||||
use keccak::H256;
|
||||
use rustc_hex::FromHex;
|
||||
use serde_json::{self, Value};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
fn h256(hex: &str) -> H256 {
|
||||
let bytes = FromHex::from_hex(hex).unwrap();
|
||||
let mut res = [0; 32];
|
||||
res.copy_from_slice(&bytes);
|
||||
res
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cdag() {
|
||||
let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value());
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let cache = builder.new_cache(tempdir.into_path(), 0);
|
||||
|
||||
let c_dag = generate_cdag(cache.as_ref());
|
||||
|
||||
let expected = vec![
|
||||
690150178u32,
|
||||
1181503948,
|
||||
2248155602,
|
||||
2118233073,
|
||||
2193871115,
|
||||
1791778428,
|
||||
1067701239,
|
||||
724807309,
|
||||
530799275,
|
||||
3480325829,
|
||||
3899029234,
|
||||
1998124059,
|
||||
2541974622,
|
||||
1100859971,
|
||||
1297211151,
|
||||
3268320000,
|
||||
2217813733,
|
||||
2690422980,
|
||||
3172863319,
|
||||
2651064309,
|
||||
];
|
||||
|
||||
assert_eq!(c_dag.iter().take(20).cloned().collect::<Vec<_>>(), expected,);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_random_merge() {
|
||||
let tests = [
|
||||
(1000000u32, 101u32, 33000101u32),
|
||||
(2000000, 102, 66003366),
|
||||
(3000000, 103, 6000103),
|
||||
(4000000, 104, 2000104),
|
||||
(1000000, 0, 33000000),
|
||||
(2000000, 0, 66000000),
|
||||
(3000000, 0, 6000000),
|
||||
(4000000, 0, 2000000),
|
||||
];
|
||||
|
||||
for (i, &(a, b, expected)) in tests.iter().enumerate() {
|
||||
assert_eq!(merge(a, b, i as u32), expected,);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_random_math() {
|
||||
let tests = [
|
||||
(20u32, 22u32, 42u32),
|
||||
(70000, 80000, 1305032704),
|
||||
(70000, 80000, 1),
|
||||
(1, 2, 1),
|
||||
(3, 10000, 196608),
|
||||
(3, 0, 3),
|
||||
(3, 6, 2),
|
||||
(3, 6, 7),
|
||||
(3, 6, 5),
|
||||
(0, 0xffffffff, 32),
|
||||
(3 << 13, 1 << 5, 3),
|
||||
(22, 20, 42),
|
||||
(80000, 70000, 1305032704),
|
||||
(80000, 70000, 1),
|
||||
(2, 1, 1),
|
||||
(10000, 3, 80000),
|
||||
(0, 3, 0),
|
||||
(6, 3, 2),
|
||||
(6, 3, 7),
|
||||
(6, 3, 5),
|
||||
(0, 0xffffffff, 32),
|
||||
(3 << 13, 1 << 5, 3),
|
||||
];
|
||||
|
||||
for (i, &(a, b, expected)) in tests.iter().enumerate() {
|
||||
assert_eq!(math(a, b, i as u32), expected,);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keccak_256() {
|
||||
let expected = "5dd431e5fbc604f499bfa0232f45f8f142d0ff5178f539e5a7800bf0643697af";
|
||||
assert_eq!(keccak_f800_long([0; 32], 0, [0; 8]), h256(expected),);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keccak_64() {
|
||||
let expected: u64 = 0x5dd431e5fbc604f4;
|
||||
assert_eq!(keccak_f800_short([0; 32], 0, [0; 8]), expected,);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_progpow_hash() {
|
||||
let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value());
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let cache = builder.new_cache(tempdir.into_path(), 0);
|
||||
let c_dag = generate_cdag(cache.as_ref());
|
||||
|
||||
let header_hash = [0; 32];
|
||||
|
||||
let (digest, result) = progpow(header_hash, 0, 0, cache.as_ref(), &c_dag);
|
||||
|
||||
let expected_digest =
|
||||
FromHex::from_hex("63155f732f2bf556967f906155b510c917e48e99685ead76ea83f4eca03ab12b")
|
||||
.unwrap();
|
||||
let expected_result =
|
||||
FromHex::from_hex("faeb1be51075b03a4ff44b335067951ead07a3b078539ace76fd56fc410557a3")
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(digest.to_vec(), expected_digest,);
|
||||
|
||||
assert_eq!(result.to_vec(), expected_result,);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_progpow_testvectors() {
|
||||
struct ProgpowTest {
|
||||
block_number: u64,
|
||||
header_hash: H256,
|
||||
nonce: u64,
|
||||
mix_hash: H256,
|
||||
final_hash: H256,
|
||||
}
|
||||
|
||||
let tests: Vec<VecDeque<Value>> =
|
||||
serde_json::from_slice(include_bytes!("../res/progpow_testvectors.json")).unwrap();
|
||||
|
||||
let tests: Vec<ProgpowTest> = tests
|
||||
.into_iter()
|
||||
.map(|mut test: VecDeque<Value>| {
|
||||
assert!(test.len() == 5);
|
||||
|
||||
let block_number: u64 = serde_json::from_value(test.pop_front().unwrap()).unwrap();
|
||||
let header_hash: String =
|
||||
serde_json::from_value(test.pop_front().unwrap()).unwrap();
|
||||
let nonce: String = serde_json::from_value(test.pop_front().unwrap()).unwrap();
|
||||
let mix_hash: String = serde_json::from_value(test.pop_front().unwrap()).unwrap();
|
||||
let final_hash: String = serde_json::from_value(test.pop_front().unwrap()).unwrap();
|
||||
|
||||
ProgpowTest {
|
||||
block_number,
|
||||
header_hash: h256(&header_hash),
|
||||
nonce: u64::from_str_radix(&nonce, 16).unwrap(),
|
||||
mix_hash: h256(&mix_hash),
|
||||
final_hash: h256(&final_hash),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
for test in tests {
|
||||
let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value());
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let cache = builder.new_cache(tempdir.path().to_owned(), test.block_number);
|
||||
let c_dag = generate_cdag(cache.as_ref());
|
||||
|
||||
let (digest, result) = progpow(
|
||||
test.header_hash,
|
||||
test.nonce,
|
||||
test.block_number,
|
||||
cache.as_ref(),
|
||||
&c_dag,
|
||||
);
|
||||
|
||||
assert_eq!(digest, test.final_hash);
|
||||
assert_eq!(result, test.mix_hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,110 +1,102 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// OpenEthereum 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.
|
||||
|
||||
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use keccak::{keccak_256, H256};
|
||||
use shared;
|
||||
use keccak::{keccak_256, H256};
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SeedHashCompute {
|
||||
prev_epoch: Cell<u64>,
|
||||
prev_seedhash: Cell<H256>,
|
||||
prev_epoch: Cell<u64>,
|
||||
prev_seedhash: Cell<H256>,
|
||||
}
|
||||
|
||||
impl SeedHashCompute {
|
||||
#[inline]
|
||||
fn reset_cache(&self) {
|
||||
self.prev_epoch.set(0);
|
||||
self.prev_seedhash.set([0u8; 32]);
|
||||
}
|
||||
#[inline]
|
||||
fn reset_cache(&self) {
|
||||
self.prev_epoch.set(0);
|
||||
self.prev_seedhash.set([0u8; 32]);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hash_block_number(&self, block_number: u64) -> H256 {
|
||||
self.hash_epoch(shared::epoch(block_number))
|
||||
}
|
||||
#[inline]
|
||||
pub fn hash_block_number(&self, block_number: u64) -> H256 {
|
||||
self.hash_epoch(shared::epoch(block_number))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hash_epoch(&self, epoch: u64) -> H256 {
|
||||
if epoch < self.prev_epoch.get() {
|
||||
// can't build on previous hash if requesting an older block
|
||||
self.reset_cache();
|
||||
}
|
||||
if epoch > self.prev_epoch.get() {
|
||||
let seed_hash = SeedHashCompute::resume_compute_seedhash(
|
||||
self.prev_seedhash.get(),
|
||||
self.prev_epoch.get(),
|
||||
epoch,
|
||||
);
|
||||
self.prev_seedhash.set(seed_hash);
|
||||
self.prev_epoch.set(epoch);
|
||||
}
|
||||
self.prev_seedhash.get()
|
||||
}
|
||||
#[inline]
|
||||
pub fn hash_epoch(&self, epoch: u64) -> H256 {
|
||||
if epoch < self.prev_epoch.get() {
|
||||
// can't build on previous hash if requesting an older block
|
||||
self.reset_cache();
|
||||
}
|
||||
if epoch > self.prev_epoch.get() {
|
||||
let seed_hash = SeedHashCompute::resume_compute_seedhash(
|
||||
self.prev_seedhash.get(),
|
||||
self.prev_epoch.get(),
|
||||
epoch,
|
||||
);
|
||||
self.prev_seedhash.set(seed_hash);
|
||||
self.prev_epoch.set(epoch);
|
||||
}
|
||||
self.prev_seedhash.get()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn resume_compute_seedhash(mut hash: H256, start_epoch: u64, end_epoch: u64) -> H256 {
|
||||
for _ in start_epoch..end_epoch {
|
||||
keccak_256::inplace(&mut hash);
|
||||
}
|
||||
hash
|
||||
}
|
||||
#[inline]
|
||||
pub fn resume_compute_seedhash(mut hash: H256, start_epoch: u64, end_epoch: u64) -> H256 {
|
||||
for _ in start_epoch..end_epoch {
|
||||
keccak_256::inplace(&mut hash);
|
||||
}
|
||||
hash
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::SeedHashCompute;
|
||||
use super::SeedHashCompute;
|
||||
|
||||
#[test]
|
||||
fn test_seed_compute_once() {
|
||||
let seed_compute = SeedHashCompute::default();
|
||||
let hash = [
|
||||
241, 175, 44, 134, 39, 121, 245, 239, 228, 236, 43, 160, 195, 152, 46, 7, 199, 5, 253,
|
||||
147, 241, 206, 98, 43, 3, 104, 17, 40, 192, 79, 106, 162,
|
||||
];
|
||||
assert_eq!(seed_compute.hash_block_number(486382), hash);
|
||||
}
|
||||
#[test]
|
||||
fn test_seed_compute_once() {
|
||||
let seed_compute = SeedHashCompute::default();
|
||||
let hash = [241, 175, 44, 134, 39, 121, 245, 239, 228, 236, 43, 160, 195, 152, 46, 7, 199, 5, 253, 147, 241, 206, 98, 43, 3, 104, 17, 40, 192, 79, 106, 162];
|
||||
assert_eq!(seed_compute.hash_block_number(486382), hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_seed_compute_zero() {
|
||||
let seed_compute = SeedHashCompute::default();
|
||||
assert_eq!(seed_compute.hash_block_number(0), [0u8; 32]);
|
||||
}
|
||||
#[test]
|
||||
fn test_seed_compute_zero() {
|
||||
let seed_compute = SeedHashCompute::default();
|
||||
assert_eq!(seed_compute.hash_block_number(0), [0u8; 32]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_seed_compute_after_older() {
|
||||
let seed_compute = SeedHashCompute::default();
|
||||
// calculating an older value first shouldn't affect the result
|
||||
let _ = seed_compute.hash_block_number(50000);
|
||||
let hash = [
|
||||
241, 175, 44, 134, 39, 121, 245, 239, 228, 236, 43, 160, 195, 152, 46, 7, 199, 5, 253,
|
||||
147, 241, 206, 98, 43, 3, 104, 17, 40, 192, 79, 106, 162,
|
||||
];
|
||||
assert_eq!(seed_compute.hash_block_number(486382), hash);
|
||||
}
|
||||
#[test]
|
||||
fn test_seed_compute_after_older() {
|
||||
let seed_compute = SeedHashCompute::default();
|
||||
// calculating an older value first shouldn't affect the result
|
||||
let _ = seed_compute.hash_block_number(50000);
|
||||
let hash = [241, 175, 44, 134, 39, 121, 245, 239, 228, 236, 43, 160, 195, 152, 46, 7, 199, 5, 253, 147, 241, 206, 98, 43, 3, 104, 17, 40, 192, 79, 106, 162];
|
||||
assert_eq!(seed_compute.hash_block_number(486382), hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_seed_compute_after_newer() {
|
||||
let seed_compute = SeedHashCompute::default();
|
||||
// calculating an newer value first shouldn't affect the result
|
||||
let _ = seed_compute.hash_block_number(972764);
|
||||
let hash = [241, 175, 44, 134, 39, 121, 245, 239, 228, 236, 43, 160, 195, 152, 46, 7, 199, 5, 253, 147, 241, 206, 98, 43, 3, 104, 17, 40, 192, 79, 106, 162];
|
||||
assert_eq!(seed_compute.hash_block_number(486382), hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_seed_compute_after_newer() {
|
||||
let seed_compute = SeedHashCompute::default();
|
||||
// calculating an newer value first shouldn't affect the result
|
||||
let _ = seed_compute.hash_block_number(972764);
|
||||
let hash = [
|
||||
241, 175, 44, 134, 39, 121, 245, 239, 228, 236, 43, 160, 195, 152, 46, 7, 199, 5, 253,
|
||||
147, 241, 206, 98, 43, 3, 104, 17, 40, 192, 79, 106, 162,
|
||||
];
|
||||
assert_eq!(seed_compute.hash_block_number(486382), hash);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user