Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94a784bfe9 | ||
|
|
2072341eca | ||
|
|
36d250a420 | ||
|
|
6f7548f596 | ||
|
|
1ccbe5cfd4 | ||
|
|
a8242ffef9 | ||
|
|
fb071acb09 |
@@ -1,3 +1,3 @@
|
||||
[target.x86_64-pc-windows-msvc]
|
||||
# Link the C runtime statically ; https://github.com/openethereum/parity-ethereum/issues/6643
|
||||
# Link the C runtime statically ; https://github.com/openethereum/openethereum/issues/6643
|
||||
rustflags = ["-Ctarget-feature=+crt-static"]
|
||||
|
||||
4
.github/CONTRIBUTING.md
vendored
4
.github/CONTRIBUTING.md
vendored
@@ -2,7 +2,7 @@
|
||||
|
||||
## Do you have a question?
|
||||
|
||||
Check out our [Beginner Introduction](https://openethereum.github.io/Beginner-Introduction), [Configuration](https://openethereum.github.io//Configuring-OpenEthereum), and [FAQ](https://openethereum.github.io/FAQ) articles on our [wiki](https://openethereum.github.io/)!
|
||||
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)!
|
||||
|
||||
See also frequently asked questions [tagged with `parity`](https://ethereum.stackexchange.com/questions/tagged/parity?sort=votes&pageSize=50) on Stack Exchange.
|
||||
|
||||
@@ -35,7 +35,7 @@ There are a few basic ground-rules for contributors (including the maintainer(s)
|
||||
* **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.
|
||||
* All code changed should be formated by running `cargo fmt -- --config=merge_imports=true`
|
||||
* Contributors should adhere to the [Parity Ethereum Style Guide](https://openethereum.github.io/wiki/Parity-Ethereum-Style-Guide).
|
||||
|
||||
### Recommendations
|
||||
|
||||
|
||||
4
.github/ISSUE_TEMPLATE.md
vendored
4
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,8 +1,6 @@
|
||||
For questions please use https://discord.io/openethereum, issues are for bugs and feature requests.
|
||||
|
||||
_Before filing a new issue, please **provide the following information**._
|
||||
|
||||
- **OpenEthereum version (>=3.1.0)**: 0.0.0
|
||||
- **OpenEthereum version**: 0.0.0
|
||||
- **Operating system**: Windows / MacOS / Linux
|
||||
- **Installation**: homebrew / one-line installer / built from source
|
||||
- **Fully synchronized**: no / yes
|
||||
|
||||
33
.github/workflows/build-test-windows.yml
vendored
33
.github/workflows/build-test-windows.yml
vendored
@@ -1,33 +0,0 @@
|
||||
name: Build and Test Suite on Windows
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
jobs:
|
||||
build-tests:
|
||||
name: Test and Build
|
||||
strategy:
|
||||
matrix:
|
||||
platform:
|
||||
- windows2019 # custom runner
|
||||
toolchain:
|
||||
- 1.52.1
|
||||
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: Build tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --locked --all --release --features "json-tests" --verbose --no-run
|
||||
71
.github/workflows/build-test.yml
vendored
71
.github/workflows/build-test.yml
vendored
@@ -5,17 +5,21 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
- 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:
|
||||
- 1.52.1
|
||||
- stable
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
@@ -28,13 +32,68 @@ jobs:
|
||||
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 }}
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
- 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
|
||||
|
||||
168
.github/workflows/build.yml
vendored
168
.github/workflows/build.yml
vendored
@@ -2,6 +2,8 @@ name: Build Release Suite
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- stable
|
||||
tags:
|
||||
- v*
|
||||
|
||||
@@ -9,18 +11,21 @@ on:
|
||||
env:
|
||||
AWS_REGION: "us-east-1"
|
||||
AWS_S3_ARTIFACTS_BUCKET: "openethereum-releases"
|
||||
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
|
||||
|
||||
|
||||
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:
|
||||
- 1.52.1
|
||||
- stable
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
@@ -31,32 +36,71 @@ jobs:
|
||||
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 == 'windows2019'
|
||||
# run: choco install llvm
|
||||
- name: Install LLVM for Windows
|
||||
if: matrix.platform == 'windows-latest'
|
||||
run: choco install llvm
|
||||
|
||||
# - name: Build OpenEthereum for Windows
|
||||
# if: matrix.platform == 'windows2019'
|
||||
# run: sh scripts/actions/build-windows.sh ${{matrix.platform}}
|
||||
- name: Sccache statistics
|
||||
run: sccache --show-stats
|
||||
|
||||
# - name: Upload Windows build
|
||||
# uses: actions/upload-artifact@v2
|
||||
# if: matrix.platform == 'windows2019'
|
||||
# with:
|
||||
# name: windows-artifacts
|
||||
# path: artifacts
|
||||
- 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 != 'windows2019'
|
||||
if: matrix.platform != 'windows-latest'
|
||||
run: sh scripts/actions/build-linux.sh ${{matrix.platform}}
|
||||
|
||||
- name: Upload Linux build
|
||||
@@ -73,23 +117,35 @@ jobs:
|
||||
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 "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_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 Windows artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: windows-artifacts
|
||||
path: windows-artifacts
|
||||
|
||||
- name: Download Linux artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
@@ -132,18 +188,18 @@ jobs:
|
||||
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 .
|
||||
- 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}')
|
||||
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
|
||||
@@ -162,11 +218,11 @@ jobs:
|
||||
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
|
||||
- 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
|
||||
@@ -191,8 +247,8 @@ jobs:
|
||||
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 }}
|
||||
windows-artifact: ${{ steps.create_zip_windows.outputs.WINDOWS_ARTIFACT }}
|
||||
windows-shasum: ${{ steps.create_zip_windows.outputs.WINDOWS_SHASUM }}
|
||||
|
||||
draft-release:
|
||||
name: Draft Release
|
||||
@@ -200,7 +256,7 @@ jobs:
|
||||
runs-on: ubuntu-16.04
|
||||
steps:
|
||||
- name: Set env
|
||||
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||
run: echo ::set-env name=RELEASE_VERSION::${GITHUB_REF#refs/*/}
|
||||
|
||||
# ==============================
|
||||
# Download artifacts
|
||||
@@ -216,10 +272,10 @@ jobs:
|
||||
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: Download artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: openethereum-windows-${{ env.RELEASE_VERSION }}.zip
|
||||
|
||||
- name: Display structure of downloaded files
|
||||
run: ls
|
||||
@@ -273,13 +329,13 @@ jobs:
|
||||
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
|
||||
- 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
|
||||
|
||||
46
.github/workflows/check.yml
vendored
46
.github/workflows/check.yml
vendored
@@ -5,22 +5,51 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
- 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 1.52.1 toolchain
|
||||
- name: Install stable toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.52.1
|
||||
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:
|
||||
@@ -30,12 +59,12 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
args: --locked --manifest-path crates/runtime/io/Cargo.toml --no-default-features --verbose
|
||||
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 crates/runtime/io/Cargo.toml --features "mio" --verbose
|
||||
args: --locked --manifest-path util/io/Cargo.toml --features "mio" --verbose
|
||||
- name: Run cargo check evmbin
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
@@ -48,3 +77,10 @@ jobs:
|
||||
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
|
||||
|
||||
29
.github/workflows/deploy-docker-nightly.yml
vendored
29
.github/workflows/deploy-docker-nightly.yml
vendored
@@ -1,29 +0,0 @@
|
||||
name: Docker Image Nightly Release
|
||||
|
||||
# Run "nightly" build on each commit to "dev" branch.
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
|
||||
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: 1.52.1
|
||||
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
|
||||
tags: "nightly"
|
||||
30
.github/workflows/deploy-docker-tag.yml
vendored
30
.github/workflows/deploy-docker-tag.yml
vendored
@@ -1,30 +0,0 @@
|
||||
name: Docker Image Tag and Latest Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
|
||||
jobs:
|
||||
deploy-docker:
|
||||
name: Build Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@master
|
||||
- name: Set env
|
||||
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||
- name: Install toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.52.1
|
||||
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
|
||||
tags: "latest,${{ env.RELEASE_VERSION }}"
|
||||
2
.github/workflows/deploy-docker.yml
vendored
2
.github/workflows/deploy-docker.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
- name: Install toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.52.1
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
override: true
|
||||
- name: Deploy to docker hub
|
||||
|
||||
2
.github/workflows/fmt.yml
vendored
2
.github/workflows/fmt.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: 1.52.1
|
||||
toolchain: stable
|
||||
override: true
|
||||
- run: rustup component add rustfmt
|
||||
- uses: actions-rs/cargo@v1
|
||||
|
||||
8
.gitmodules
vendored
8
.gitmodules
vendored
@@ -1,3 +1,7 @@
|
||||
[submodule "crates/ethcore/res/json_tests"]
|
||||
path = crates/ethcore/res/json_tests
|
||||
[submodule "ethcore/res/ethereum/tests"]
|
||||
path = ethcore/res/ethereum/tests
|
||||
url = https://github.com/ethereum/tests.git
|
||||
branch = develop
|
||||
[submodule "ethcore/res/wasm-tests"]
|
||||
path = ethcore/res/wasm-tests
|
||||
url = https://github.com/paritytech/wasm-tests
|
||||
|
||||
232
CHANGELOG.md
232
CHANGELOG.md
@@ -1,208 +1,34 @@
|
||||
## OpenEthereum v3.3.3
|
||||
## OpenEthereum v3.1RC1
|
||||
|
||||
Enhancements:
|
||||
* Implement eip-3607 (#593)
|
||||
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.
|
||||
|
||||
Bug fixes:
|
||||
* Add type field for legacy transactions in RPC calls (#580)
|
||||
* Makes eth_mining to return False if not is not allowed to seal (#581)
|
||||
* Made nodes data concatenate as RLP sequences instead of bytes (#598)
|
||||
Database migration utility currently in beta: https://github.com/openethereum/3.1-db-upgrade-tool
|
||||
|
||||
## OpenEthereum v3.3.2
|
||||
The full list of included changes from v2.5.13 to v3.1:
|
||||
|
||||
Enhancements:
|
||||
* London hardfork block: Sokol (24114400)
|
||||
- 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
|
||||
|
||||
Bug fixes:
|
||||
* Fix for maxPriorityFeePerGas overflow
|
||||
|
||||
## OpenEthereum v3.3.1
|
||||
|
||||
Enhancements:
|
||||
* Add eth_maxPriorityFeePerGas implementation (#570)
|
||||
* Add a bootnode for Kovan
|
||||
|
||||
Bug fixes:
|
||||
* Fix for modexp overflow in debug mode (#578)
|
||||
|
||||
## OpenEthereum v3.3.0
|
||||
|
||||
Enhancements:
|
||||
* Add `validateServiceTransactionsTransition` spec option to be able to enable additional checking of zero gas price transactions by block verifier
|
||||
|
||||
## OpenEthereum v3.3.0-rc.15
|
||||
|
||||
* Revert eip1559BaseFeeMinValue activation on xDai at London hardfork block
|
||||
|
||||
## OpenEthereum v3.3.0-rc.14
|
||||
|
||||
Enhancements:
|
||||
* Add eip1559BaseFeeMinValue and eip1559BaseFeeMinValueTransition spec options
|
||||
* Activate eip1559BaseFeeMinValue on xDai at London hardfork block (19040000), set it to 20 GWei
|
||||
* Activate eip1559BaseFeeMinValue on POA Core at block 24199500 (November 8, 2021), set it to 10 GWei
|
||||
* Delay difficulty bomb to June 2022 for Ethereum Mainnet (EIP-4345)
|
||||
|
||||
## OpenEthereum v3.3.0-rc.13
|
||||
|
||||
Enhancements:
|
||||
* London hardfork block: POA Core (24090200)
|
||||
|
||||
## OpenEthereum v3.3.0-rc.12
|
||||
|
||||
Enhancements:
|
||||
* London hardfork block: xDai (19040000)
|
||||
|
||||
## OpenEthereum v3.3.0-rc.11
|
||||
|
||||
Bug fixes:
|
||||
* Ignore GetNodeData requests only for non-AuRa chains
|
||||
|
||||
## OpenEthereum v3.3.0-rc.10
|
||||
|
||||
Enhancements:
|
||||
* Add eip1559FeeCollector and eip1559FeeCollectorTransition spec options
|
||||
|
||||
## OpenEthereum v3.3.0-rc.9
|
||||
|
||||
Bug fixes:
|
||||
* Add service transactions support for EIP-1559
|
||||
* Fix MinGasPrice config option for POSDAO and EIP-1559
|
||||
|
||||
Enhancements:
|
||||
* min_gas_price becomes min_effective_priority_fee
|
||||
* added version 4 for TxPermission contract
|
||||
|
||||
## OpenEthereum v3.3.0-rc.8
|
||||
|
||||
Bug fixes:
|
||||
* Ignore GetNodeData requests (#519)
|
||||
|
||||
## OpenEthereum v3.3.0-rc.7
|
||||
|
||||
Bug fixes:
|
||||
* GetPooledTransactions is sent in invalid form (wrong packet id)
|
||||
|
||||
## OpenEthereum v3.3.0-rc.6
|
||||
|
||||
Enhancements:
|
||||
* London hardfork block: kovan (26741100) (#502)
|
||||
|
||||
## OpenEthereum v3.3.0-rc.4
|
||||
|
||||
Enhancements:
|
||||
* London hardfork block: mainnet (12,965,000) (#475)
|
||||
* Support for eth/66 protocol version (#465)
|
||||
* Bump ethereum/tests to v9.0.3
|
||||
* Add eth_feeHistory
|
||||
|
||||
Bug fixes:
|
||||
* GetNodeData from eth63 is missing (#466)
|
||||
* Effective gas price not omitting (#477)
|
||||
* London support in openethereum-evm (#479)
|
||||
* gasPrice is required field for Transaction object (#481)
|
||||
|
||||
## OpenEthereum v3.3.0-rc.3
|
||||
|
||||
Bug fixes:
|
||||
* Add effective_gas_price to eth_getTransactionReceipt #445 (#450)
|
||||
* Update eth_gasPrice to support EIP-1559 #449 (#458)
|
||||
* eth_estimateGas returns "Requires higher than upper limit of X" after London Ropsten Hard Fork #459 (#460)
|
||||
|
||||
## OpenEthereum v3.3.0-rc.2
|
||||
|
||||
Enhancements:
|
||||
* EIP-1559: Fee market change for ETH 1.0 chain
|
||||
* EIP-3198: BASEFEE opcode
|
||||
* EIP-3529: Reduction in gas refunds
|
||||
* EIP-3541: Reject new contracts starting with the 0xEF byte
|
||||
* Delay difficulty bomb to December 2021 (EIP-3554)
|
||||
* London hardfork blocks: goerli (5,062,605), rinkeby (8,897,988), ropsten (10,499,401)
|
||||
* Add chainspecs for aleut and baikal
|
||||
* Bump ethereum/tests to v9.0.2
|
||||
|
||||
## OpenEthereum v3.2.6
|
||||
|
||||
Enhancement:
|
||||
* Berlin hardfork blocks: poacore (21,364,900), poasokol (21,050,600)
|
||||
|
||||
## OpenEthereum v3.2.5
|
||||
|
||||
Bug fixes:
|
||||
* Backport: Block sync stopped without any errors. #277 (#286)
|
||||
* Strict memory order (#306)
|
||||
|
||||
Enhancements:
|
||||
* Executable queue for ancient blocks inclusion (#208)
|
||||
* Backport AuRa commits for xdai (#330)
|
||||
* Add Nethermind to clients that accept service transactions (#324)
|
||||
* Implement the filter argument in parity_pendingTransactions (#295)
|
||||
* Ethereum-types and various libs upgraded (#315)
|
||||
* [evmbin] Omit storage output, now for std-json (#311)
|
||||
* Freeze pruning while creating snapshot (#205)
|
||||
* AuRa multi block reward (#290)
|
||||
* Improved metrics. DB read/write. prometheus prefix config (#240)
|
||||
* Send RLPx auth in EIP-8 format (#287)
|
||||
* rpc module reverted for RPC JSON api (#284)
|
||||
* Revert "Remove eth/63 protocol version (#252)"
|
||||
* Support for eth/65 protocol version (#366)
|
||||
* Berlin hardfork blocks: kovan (24,770,900), xdai (16,101,500)
|
||||
* Bump ethereum/tests to v8.0.3
|
||||
|
||||
devops:
|
||||
* Upgrade docker alpine to `v1.13.2`. for rust `v1.47`.
|
||||
* Send SIGTERM instead of SIGHUP to OE daemon (#317)
|
||||
|
||||
## OpenEthereum v3.2.4
|
||||
|
||||
* Fix for Typed transaction broadcast.
|
||||
|
||||
## OpenEthereum v3.2.3
|
||||
|
||||
* Hotfix for berlin consensus error.
|
||||
|
||||
## OpenEthereum v3.2.2-rc.1
|
||||
|
||||
Bug fixes:
|
||||
* Backport: Block sync stopped without any errors. #277 (#286)
|
||||
* Strict memory order (#306)
|
||||
|
||||
Enhancements:
|
||||
* Executable queue for ancient blocks inclusion (#208)
|
||||
* Backport AuRa commits for xdai (#330)
|
||||
* Add Nethermind to clients that accept service transactions (#324)
|
||||
* Implement the filter argument in parity_pendingTransactions (#295)
|
||||
* Ethereum-types and various libs upgraded (#315)
|
||||
* Bump ethereum/tests to v8.0.2
|
||||
* [evmbin] Omit storage output, now for std-json (#311)
|
||||
* Freeze pruning while creating snapshot (#205)
|
||||
* AuRa multi block reward (#290)
|
||||
* Improved metrics. DB read/write. prometheus prefix config (#240)
|
||||
* Send RLPx auth in EIP-8 format (#287)
|
||||
* rpc module reverted for RPC JSON api (#284)
|
||||
* Revert "Remove eth/63 protocol version (#252)"
|
||||
|
||||
devops:
|
||||
* Upgrade docker alpine to `v1.13.2`. for rust `v1.47`.
|
||||
* Send SIGTERM instead of SIGHUP to OE daemon (#317)
|
||||
|
||||
## OpenEthereum v3.2.1
|
||||
|
||||
Hot fix issue, related to initial sync:
|
||||
* Initial sync gets stuck. (#318)
|
||||
|
||||
## OpenEthereum v3.2.0
|
||||
|
||||
Bug fixes:
|
||||
* Update EWF's chains with Istanbul transition block numbers (#11482) (#254)
|
||||
* fix Supplied instant is later than self (#169)
|
||||
* ethcore/snapshot: fix double-lock in Service::feed_chunk (#289)
|
||||
|
||||
Enhancements:
|
||||
* Berlin hardfork blocks: mainnet (12,244,000), goerli (4,460,644), rinkeby (8,290,928) and ropsten (9,812,189)
|
||||
* yolo3x spec (#241)
|
||||
* EIP-2930 RPC support
|
||||
* Remove eth/63 protocol version (#252)
|
||||
* Snapshot manifest block added to prometheus (#232)
|
||||
* EIP-1898: Allow default block parameter to be blockHash
|
||||
* Change ProtocolId to U64
|
||||
* Update ethereum/tests
|
||||
|
||||
1260
Cargo.lock
generated
1260
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
93
Cargo.toml
93
Cargo.toml
@@ -2,7 +2,7 @@
|
||||
description = "OpenEthereum"
|
||||
name = "openethereum"
|
||||
# NOTE Make sure to update util/version/Cargo.toml as well
|
||||
version = "3.3.3"
|
||||
version = "3.1.0"
|
||||
license = "GPL-3.0"
|
||||
authors = [
|
||||
"OpenEthereum developers",
|
||||
@@ -10,7 +10,7 @@ authors = [
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
blooms-db = { path = "crates/db/blooms-db" }
|
||||
blooms-db = { path = "util/blooms-db" }
|
||||
log = "0.4"
|
||||
rustc-hex = "1.0"
|
||||
docopt = "1.0"
|
||||
@@ -22,7 +22,7 @@ number_prefix = "0.2"
|
||||
rpassword = "1.0"
|
||||
semver = "0.9"
|
||||
ansi_term = "0.10"
|
||||
parking_lot = "0.11.1"
|
||||
parking_lot = "0.7"
|
||||
regex = "1.0"
|
||||
atty = "0.2.8"
|
||||
toml = "0.4"
|
||||
@@ -35,43 +35,44 @@ fdlimit = "0.1"
|
||||
ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" }
|
||||
jsonrpc-core = "15.0.0"
|
||||
parity-bytes = "0.1"
|
||||
common-types = { path = "crates/ethcore/types" }
|
||||
ethcore = { path = "crates/ethcore", features = ["parity"] }
|
||||
ethcore-accounts = { path = "crates/accounts", optional = true }
|
||||
ethcore-blockchain = { path = "crates/ethcore/blockchain" }
|
||||
ethcore-call-contract = { path = "crates/vm/call-contract"}
|
||||
ethcore-db = { path = "crates/db/db" }
|
||||
ethcore-io = { path = "crates/runtime/io" }
|
||||
ethcore-logger = { path = "bin/oe/logger" }
|
||||
ethcore-miner = { path = "crates/concensus/miner" }
|
||||
ethcore-network = { path = "crates/net/network" }
|
||||
ethcore-service = { path = "crates/ethcore/service" }
|
||||
ethcore-sync = { path = "crates/ethcore/sync" }
|
||||
ethereum-types = "0.9.2"
|
||||
ethkey = { path = "crates/accounts/ethkey" }
|
||||
ethstore = { path = "crates/accounts/ethstore" }
|
||||
fetch = { path = "crates/net/fetch" }
|
||||
node-filter = { path = "crates/net/node-filter" }
|
||||
parity-crypto = { version = "0.6.2", features = [ "publickey" ] }
|
||||
rlp = { version = "0.4.6" }
|
||||
cli-signer= { path = "crates/util/cli-signer" }
|
||||
common-types = { path = "ethcore/types" }
|
||||
ethcore = { path = "ethcore", features = ["parity"] }
|
||||
ethcore-accounts = { path = "accounts", optional = true }
|
||||
ethcore-blockchain = { path = "ethcore/blockchain" }
|
||||
ethcore-call-contract = { path = "ethcore/call-contract"}
|
||||
ethcore-db = { path = "ethcore/db" }
|
||||
ethcore-io = { path = "util/io" }
|
||||
ethcore-logger = { path = "parity/logger" }
|
||||
ethcore-miner = { path = "miner" }
|
||||
ethcore-network = { path = "util/network" }
|
||||
ethcore-service = { path = "ethcore/service" }
|
||||
ethcore-sync = { path = "ethcore/sync" }
|
||||
ethereum-types = "0.4"
|
||||
ethkey = { path = "accounts/ethkey" }
|
||||
ethstore = { path = "accounts/ethstore" }
|
||||
fetch = { path = "util/fetch" }
|
||||
node-filter = { path = "ethcore/node-filter" }
|
||||
rlp = { version = "0.3.0", features = ["ethereum"] }
|
||||
cli-signer= { path = "cli-signer" }
|
||||
parity-daemonize = "0.3"
|
||||
parity-local-store = { path = "crates/concensus/miner/local-store" }
|
||||
parity-runtime = { path = "crates/runtime/runtime" }
|
||||
parity-rpc = { path = "crates/rpc" }
|
||||
parity-version = { path = "crates/util/version" }
|
||||
parity-local-store = { path = "miner/local-store" }
|
||||
parity-runtime = { path = "util/runtime" }
|
||||
parity-rpc = { path = "rpc" }
|
||||
parity-version = { path = "util/version" }
|
||||
parity-path = "0.1"
|
||||
dir = { path = "crates/util/dir" }
|
||||
panic_hook = { path = "crates/util/panic-hook" }
|
||||
keccak-hash = "0.5.0"
|
||||
migration-rocksdb = { path = "crates/db/migration-rocksdb" }
|
||||
dir = { path = "util/dir" }
|
||||
panic_hook = { path = "util/panic-hook" }
|
||||
keccak-hash = "0.1"
|
||||
migration-rocksdb = { path = "util/migration-rocksdb" }
|
||||
kvdb = "0.1"
|
||||
kvdb-rocksdb = "0.1.3"
|
||||
journaldb = { path = "crates/db/journaldb" }
|
||||
stats = { path = "crates/util/stats" }
|
||||
journaldb = { path = "util/journaldb" }
|
||||
stats = { path = "util/stats" }
|
||||
prometheus = "0.9.0"
|
||||
|
||||
# ethcore-secretstore = { path = "crates/util/secret-store", optional = true }
|
||||
ethcore-secretstore = { path = "secret-store", optional = true }
|
||||
|
||||
registrar = { path = "util/registrar" }
|
||||
|
||||
[build-dependencies]
|
||||
rustc_version = "0.2"
|
||||
@@ -80,7 +81,7 @@ rustc_version = "0.2"
|
||||
pretty_assertions = "0.1"
|
||||
ipnetwork = "0.12.6"
|
||||
tempdir = "0.3"
|
||||
fake-fetch = { path = "crates/net/fake-fetch" }
|
||||
fake-fetch = { path = "util/fake-fetch" }
|
||||
lazy_static = "1.2.0"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
@@ -96,6 +97,7 @@ 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"]
|
||||
final = ["parity-version/final"]
|
||||
deadlock_detection = ["parking_lot/deadlock_detection"]
|
||||
# to create a memory profile (requires nightly rust), use e.g.
|
||||
@@ -107,10 +109,10 @@ deadlock_detection = ["parking_lot/deadlock_detection"]
|
||||
memory_profiling = []
|
||||
|
||||
[lib]
|
||||
path = "bin/oe/lib.rs"
|
||||
path = "parity/lib.rs"
|
||||
|
||||
[[bin]]
|
||||
path = "bin/oe/main.rs"
|
||||
path = "parity/main.rs"
|
||||
name = "openethereum"
|
||||
|
||||
[profile.test]
|
||||
@@ -126,8 +128,17 @@ lto = true
|
||||
# in the dependency tree in any other way
|
||||
# (i.e. pretty much only standalone CLI tools)
|
||||
members = [
|
||||
"bin/ethkey",
|
||||
"bin/ethstore",
|
||||
"bin/evmbin",
|
||||
"bin/chainspec"
|
||||
"accounts/ethkey/cli",
|
||||
"accounts/ethstore/cli",
|
||||
"chainspec",
|
||||
"ethcore/wasm/run",
|
||||
"evmbin",
|
||||
"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" }
|
||||
|
||||
12
README.md
12
README.md
@@ -46,7 +46,7 @@ OpenEthereum's goal is to be the fastest, lightest, and most secure Ethereum cli
|
||||
|
||||
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.
|
||||
|
||||
If you run into problems while using OpenEthereum, check out the [old wiki for documentation](https://openethereum.github.io/), 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 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!
|
||||
|
||||
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.
|
||||
|
||||
@@ -135,7 +135,7 @@ To start OpenEthereum as a regular user using `systemd` init:
|
||||
1. Copy `./scripts/openethereum.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 wiki](https://openethereum.github.io/Configuring-OpenEthereum) for details.
|
||||
3. To configure OpenEthereum, see [our old wiki](https://openethereum.github.io/wiki/Configuring-OpenEthereum) for details.
|
||||
|
||||
## 4. Testing <a id="chapter-004"></a>
|
||||
|
||||
@@ -157,7 +157,7 @@ You can show your logs in the test output by passing `--nocapture` (i.e. `cargo
|
||||
|
||||
## 5. Documentation <a id="chapter-005"></a>
|
||||
|
||||
Be sure to [check out our wiki](https://openethereum.github.io/) for more information.
|
||||
Be sure to [check out our old wiki](https://openethereum.github.io/wiki/) for more information.
|
||||
|
||||
### Viewing documentation for OpenEthereum packages
|
||||
|
||||
@@ -297,9 +297,9 @@ Caching, Importing Blocks, and Block Information
|
||||
|
||||
In addition to the OpenEthereum client, there are additional tools in this repository available:
|
||||
|
||||
- [evmbin](./bin/evmbin) - OpenEthereum EVM Implementation.
|
||||
- [ethstore](./crates/accounts/ethstore) - OpenEthereum Key Management.
|
||||
- [ethkey](./crates/accounts/ethkey) - OpenEthereum Keys Generator.
|
||||
- [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)
|
||||
|
||||
@@ -12,12 +12,11 @@ common-types = { path = "../ethcore/types" }
|
||||
ethkey = { path = "ethkey" }
|
||||
ethstore = { path = "ethstore" }
|
||||
log = "0.4"
|
||||
parity-crypto = { version = "0.6.2", features = [ "publickey" ] }
|
||||
parking_lot = "0.11.1"
|
||||
parking_lot = "0.7"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
ethereum-types = "0.9.2"
|
||||
ethereum-types = "0.4"
|
||||
tempdir = "0.3"
|
||||
@@ -6,15 +6,15 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
edit-distance = "2.0"
|
||||
parity-crypto = { version = "0.6.2", features = ["publickey"] }
|
||||
eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1", rev = "9791e79f21a5309dcb6e0bd254b1ef88fca2f1f4" }
|
||||
ethereum-types = "0.9.2"
|
||||
parity-crypto = "0.3.0"
|
||||
eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1", rev = "ccc06e7480148b723eb44ac56cf4d20eec380b6f" }
|
||||
ethereum-types = "0.4"
|
||||
lazy_static = "1.0"
|
||||
log = "0.4"
|
||||
memzero = { path = "../../../crates/util/memzero" }
|
||||
memzero = { path = "../../util/memzero" }
|
||||
parity-wordlist = "1.3"
|
||||
quick-error = "1.2.2"
|
||||
rand = "0.7.3"
|
||||
rand = "0.4"
|
||||
rustc-hex = "1.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
@@ -7,9 +7,8 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
[dependencies]
|
||||
docopt = "1.0"
|
||||
env_logger = "0.5"
|
||||
ethkey = { path = "../../crates/accounts/ethkey" }
|
||||
panic_hook = { path = "../../crates/util/panic-hook" }
|
||||
parity-crypto = { version = "0.6.2", features = [ "publickey" ] }
|
||||
ethkey = { path = "../" }
|
||||
panic_hook = { path = "../../../util/panic-hook" }
|
||||
parity-wordlist="1.3"
|
||||
rustc-hex = "1.0"
|
||||
serde = "1.0"
|
||||
@@ -18,7 +18,6 @@ extern crate docopt;
|
||||
extern crate env_logger;
|
||||
extern crate ethkey;
|
||||
extern crate panic_hook;
|
||||
extern crate parity_crypto as crypto;
|
||||
extern crate parity_wordlist;
|
||||
extern crate rustc_hex;
|
||||
extern crate serde;
|
||||
@@ -29,11 +28,11 @@ extern crate serde_derive;
|
||||
|
||||
use std::{env, fmt, io, num::ParseIntError, process, sync};
|
||||
|
||||
use crypto::publickey::{
|
||||
sign, verify_address, verify_public, Error as EthkeyError, Generator, KeyPair, Random,
|
||||
};
|
||||
use docopt::Docopt;
|
||||
use ethkey::{brain_recover, Brain, BrainPrefix, Prefix};
|
||||
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#"
|
||||
@@ -203,13 +202,15 @@ where
|
||||
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();
|
||||
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::InvalidSecretKey)?;
|
||||
.map_err(|_| EthkeyError::InvalidSecret)?;
|
||||
(KeyPair::from_secret(secret)?, None)
|
||||
};
|
||||
Ok(display(result, display_mode))
|
||||
@@ -222,7 +223,7 @@ where
|
||||
let phrase = format!("recovery phrase: {}", brain.phrase());
|
||||
(keypair, Some(phrase))
|
||||
} else {
|
||||
(Random.generate(), None)
|
||||
(Random.generate()?, None)
|
||||
}
|
||||
} else if args.cmd_prefix {
|
||||
let prefix = args.arg_prefix.from_hex()?;
|
||||
@@ -253,7 +254,7 @@ where
|
||||
let secret = args
|
||||
.arg_secret
|
||||
.parse()
|
||||
.map_err(|_| EthkeyError::InvalidSecretKey)?;
|
||||
.map_err(|_| EthkeyError::InvalidSecret)?;
|
||||
let message = args
|
||||
.arg_message
|
||||
.parse()
|
||||
@@ -273,7 +274,7 @@ where
|
||||
let public = args
|
||||
.arg_public
|
||||
.parse()
|
||||
.map_err(|_| EthkeyError::InvalidPublicKey)?;
|
||||
.map_err(|_| EthkeyError::InvalidPublic)?;
|
||||
verify_public(&public, &signature, &message)?
|
||||
} else if args.cmd_address {
|
||||
let address = args
|
||||
@@ -300,7 +301,7 @@ where
|
||||
while let Some(phrase) = it.next() {
|
||||
i += 1;
|
||||
|
||||
let keypair = Brain::new(phrase.clone()).generate();
|
||||
let keypair = Brain::new(phrase.clone()).generate().unwrap();
|
||||
if keypair.address() == address {
|
||||
return Ok(Some((phrase, keypair)));
|
||||
}
|
||||
@@ -14,10 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use parity_crypto::{
|
||||
publickey::{KeyPair, Secret},
|
||||
Keccak256,
|
||||
};
|
||||
use super::{Generator, KeyPair, Secret};
|
||||
use keccak::Keccak256;
|
||||
use parity_wordlist;
|
||||
|
||||
/// Simple brainwallet.
|
||||
@@ -31,8 +29,12 @@ impl Brain {
|
||||
pub fn validate_phrase(phrase: &str, expected_words: usize) -> Result<(), ::WordlistError> {
|
||||
parity_wordlist::validate_phrase(phrase, expected_words)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate(&mut self) -> KeyPair {
|
||||
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();
|
||||
|
||||
@@ -43,10 +45,12 @@ impl Brain {
|
||||
match i > 16384 {
|
||||
false => i += 1,
|
||||
true => {
|
||||
if let Ok(pair) = Secret::import_key(&secret).and_then(KeyPair::from_secret) {
|
||||
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 pair;
|
||||
return Ok(pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,12 +62,13 @@ impl Brain {
|
||||
#[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();
|
||||
let second_keypair = Brain::new(words.clone()).generate();
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,7 @@
|
||||
// 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;
|
||||
use parity_crypto::publickey::{Error, KeyPair};
|
||||
use super::{Brain, Error, Generator, KeyPair};
|
||||
use parity_wordlist as wordlist;
|
||||
|
||||
/// Tries to find brain-seed keypair with address starting with given prefix.
|
||||
@@ -39,12 +38,16 @@ impl BrainPrefix {
|
||||
pub fn phrase(&self) -> &str {
|
||||
&self.last_phrase
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate(&mut self) -> Result<KeyPair, Error> {
|
||||
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();
|
||||
if keypair.address().as_ref().starts_with(&self.prefix) {
|
||||
let keypair = Brain::new(phrase.clone()).generate().unwrap();
|
||||
if keypair.address().starts_with(&self.prefix) {
|
||||
self.last_phrase = phrase;
|
||||
return Ok(keypair);
|
||||
}
|
||||
@@ -57,6 +60,7 @@ impl BrainPrefix {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use BrainPrefix;
|
||||
use Generator;
|
||||
|
||||
#[test]
|
||||
fn prefix_generator() {
|
||||
@@ -64,6 +68,6 @@ mod tests {
|
||||
let keypair = BrainPrefix::new(prefix.clone(), usize::max_value(), 12)
|
||||
.generate()
|
||||
.unwrap();
|
||||
assert!(keypair.address().as_bytes().starts_with(&prefix));
|
||||
assert!(keypair.address().starts_with(&prefix));
|
||||
}
|
||||
}
|
||||
@@ -17,10 +17,9 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use edit_distance::edit_distance;
|
||||
use parity_crypto::publickey::Address;
|
||||
use parity_wordlist;
|
||||
|
||||
use super::Brain;
|
||||
use super::{Address, Brain, Generator};
|
||||
|
||||
/// Tries to find a phrase for address, given the number
|
||||
/// of expected words and a partial phrase.
|
||||
@@ -33,7 +32,9 @@ pub fn brain_recover(
|
||||
) -> Option<String> {
|
||||
let it = PhrasesIterator::from_known_phrase(known_phrase, expected_words);
|
||||
for phrase in it {
|
||||
let keypair = Brain::new(phrase.clone()).generate();
|
||||
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);
|
||||
202
accounts/ethkey/src/crypto.rs
Normal file
202
accounts/ethkey/src/crypto.rs
Normal file
@@ -0,0 +1,202 @@
|
||||
// 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[..]);
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@
|
||||
// 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::Error as CryptoError;
|
||||
use std::{error, fmt};
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -54,7 +53,7 @@ impl fmt::Display for Error {
|
||||
|
||||
impl error::Error for Error {
|
||||
fn description(&self) -> &str {
|
||||
format!("{:?}", &self)
|
||||
"Crypto error"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,12 +63,6 @@ impl Into<String> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CryptoError> for Error {
|
||||
fn from(e: CryptoError) -> Error {
|
||||
Error::Custom(e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<::secp256k1::Error> for Error {
|
||||
fn from(e: ::secp256k1::Error) -> Error {
|
||||
match e {
|
||||
589
accounts/ethkey/src/extended.rs
Normal file
589
accounts/ethkey/src/extended.rs
Normal file
@@ -0,0 +1,589 @@
|
||||
// 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"),
|
||||
);
|
||||
}
|
||||
}
|
||||
33
accounts/ethkey/src/keccak.rs
Normal file
33
accounts/ethkey/src/keccak.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
// 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
|
||||
}
|
||||
}
|
||||
120
accounts/ethkey/src/keypair.rs
Normal file
120
accounts/ethkey/src/keypair.rs
Normal file
@@ -0,0 +1,120 @@
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
89
accounts/ethkey/src/lib.rs
Normal file
89
accounts/ethkey/src/lib.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
// 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)]
|
||||
|
||||
extern crate edit_distance;
|
||||
extern crate ethereum_types;
|
||||
extern crate memzero;
|
||||
extern crate parity_crypto;
|
||||
extern crate parity_wordlist;
|
||||
#[macro_use]
|
||||
extern crate quick_error;
|
||||
extern crate rand;
|
||||
extern crate rustc_hex;
|
||||
extern crate secp256k1;
|
||||
extern crate serde;
|
||||
extern crate tiny_keccak;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
mod brain;
|
||||
mod brain_prefix;
|
||||
mod error;
|
||||
mod extended;
|
||||
mod keccak;
|
||||
mod keypair;
|
||||
mod password;
|
||||
mod prefix;
|
||||
mod random;
|
||||
mod secret;
|
||||
mod signature;
|
||||
|
||||
pub mod brain_recover;
|
||||
pub mod crypto;
|
||||
pub mod math;
|
||||
|
||||
pub use self::{
|
||||
brain::Brain,
|
||||
brain_prefix::BrainPrefix,
|
||||
error::Error,
|
||||
extended::{Derivation, DerivationError, ExtendedKeyPair, ExtendedPublic, ExtendedSecret},
|
||||
keypair::{public_to_address, KeyPair},
|
||||
math::public_is_valid,
|
||||
parity_wordlist::Error as WordlistError,
|
||||
password::Password,
|
||||
prefix::Prefix,
|
||||
random::Random,
|
||||
secret::Secret,
|
||||
signature::{recover, sign, verify_address, verify_public, Signature},
|
||||
};
|
||||
|
||||
use ethereum_types::H256;
|
||||
|
||||
pub use ethereum_types::{Address, Public};
|
||||
pub type Message = H256;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref SECP256K1: secp256k1::Secp256k1 = secp256k1::Secp256k1::new();
|
||||
}
|
||||
|
||||
/// Uninstantiatable error type for infallible generators.
|
||||
#[derive(Debug)]
|
||||
pub enum Void {}
|
||||
|
||||
/// Generates new keypair.
|
||||
pub trait Generator {
|
||||
type Error;
|
||||
|
||||
/// Should be called to generate new keypair.
|
||||
fn generate(&mut self) -> Result<KeyPair, Self::Error>;
|
||||
}
|
||||
134
accounts/ethkey/src/math.rs
Normal file
134
accounts/ethkey/src/math.rs
Normal file
@@ -0,0 +1,134 @@
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use parity_crypto::publickey::{Error, Generator, KeyPair, Random};
|
||||
use super::{Error, Generator, KeyPair, Random};
|
||||
|
||||
/// Tries to find keypair with address starting with given prefix.
|
||||
pub struct Prefix {
|
||||
@@ -24,13 +24,20 @@ pub struct Prefix {
|
||||
|
||||
impl Prefix {
|
||||
pub fn new(prefix: Vec<u8>, iterations: usize) -> Self {
|
||||
Prefix { prefix, iterations }
|
||||
Prefix {
|
||||
prefix: prefix,
|
||||
iterations: iterations,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate(&mut self) -> Result<KeyPair, Error> {
|
||||
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().as_ref().starts_with(&self.prefix) {
|
||||
let keypair = Random.generate()?;
|
||||
if keypair.address().starts_with(&self.prefix) {
|
||||
return Ok(keypair);
|
||||
}
|
||||
}
|
||||
@@ -41,6 +48,7 @@ impl Prefix {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use Generator;
|
||||
use Prefix;
|
||||
|
||||
#[test]
|
||||
@@ -49,6 +57,6 @@ mod tests {
|
||||
let keypair = Prefix::new(prefix.clone(), usize::max_value())
|
||||
.generate()
|
||||
.unwrap();
|
||||
assert!(keypair.address().as_bytes().starts_with(&prefix));
|
||||
assert!(keypair.address().starts_with(&prefix));
|
||||
}
|
||||
}
|
||||
45
accounts/ethkey/src/random.rs
Normal file
45
accounts/ethkey/src/random.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
// 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))
|
||||
}
|
||||
}
|
||||
322
accounts/ethkey/src/secret.rs
Normal file
322
accounts/ethkey/src/secret.rs
Normal file
@@ -0,0 +1,322 @@
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
325
accounts/ethkey/src/signature.rs
Normal file
325
accounts/ethkey/src/signature.rs
Normal file
@@ -0,0 +1,325 @@
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
@@ -7,17 +7,19 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
libc = "0.2"
|
||||
rand = "0.7.3"
|
||||
rand = "0.4"
|
||||
ethkey = { path = "../ethkey" }
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
serde_derive = "1.0"
|
||||
rustc-hex = "1.0"
|
||||
tiny-keccak = "1.4"
|
||||
time = "0.1.34"
|
||||
itertools = "0.5"
|
||||
parking_lot = "0.11.1"
|
||||
parity-crypto = { version = "0.6.2", features = [ "publickey"] }
|
||||
ethereum-types = "0.9.2"
|
||||
parking_lot = "0.7"
|
||||
parity-crypto = "0.3.0"
|
||||
ethereum-types = "0.4"
|
||||
dir = { path = "../../util/dir" }
|
||||
smallvec = "0.6"
|
||||
parity-wordlist = "1.3"
|
||||
tempdir = "0.3"
|
||||
@@ -11,10 +11,10 @@ num_cpus = "1.6"
|
||||
rustc-hex = "1.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
parking_lot = "0.11.1"
|
||||
ethstore = { path = "../../crates/accounts/ethstore" }
|
||||
dir = { path = '../../crates/util/dir' }
|
||||
panic_hook = { path = "../../crates/util/panic-hook" }
|
||||
parking_lot = "0.7"
|
||||
ethstore = { path = "../" }
|
||||
dir = { path = '../../../util/dir' }
|
||||
panic_hook = { path = "../../../util/panic-hook" }
|
||||
|
||||
[[bin]]
|
||||
name = "ethstore"
|
||||
@@ -15,8 +15,8 @@
|
||||
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use account::{Aes128Ctr, Cipher, Kdf, Pbkdf2, Prf};
|
||||
use crypto::{self, publickey::Secret, Keccak256};
|
||||
use ethkey::Password;
|
||||
use crypto::{self, Keccak256};
|
||||
use ethkey::{Password, Secret};
|
||||
use json;
|
||||
use random::Random;
|
||||
use smallvec::SmallVec;
|
||||
@@ -79,7 +79,7 @@ impl Crypto {
|
||||
password: &Password,
|
||||
iterations: NonZeroU32,
|
||||
) -> Result<Self, crypto::Error> {
|
||||
Crypto::with_plain(secret.as_bytes(), password, iterations)
|
||||
Crypto::with_plain(&*secret, password, iterations)
|
||||
}
|
||||
|
||||
/// Encrypt custom plain data
|
||||
@@ -94,7 +94,7 @@ impl Crypto {
|
||||
// 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.get());
|
||||
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
|
||||
@@ -127,7 +127,7 @@ impl Crypto {
|
||||
}
|
||||
|
||||
let secret = self.do_decrypt(password, 32)?;
|
||||
Ok(Secret::import_key(&secret)?)
|
||||
Ok(Secret::from_unsafe_slice(&secret)?)
|
||||
}
|
||||
|
||||
/// Try to decrypt and return result as is
|
||||
@@ -139,7 +139,7 @@ impl Crypto {
|
||||
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.get())
|
||||
crypto::derive_key_iterations(password.as_bytes(), ¶ms.salt, params.c)
|
||||
}
|
||||
Kdf::Scrypt(ref params) => crypto::scrypt::derive_key(
|
||||
password.as_bytes(),
|
||||
@@ -179,7 +179,7 @@ impl Crypto {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Crypto, Error, NonZeroU32};
|
||||
use crypto::publickey::{Generator, Random};
|
||||
use ethkey::{Generator, Random};
|
||||
|
||||
lazy_static! {
|
||||
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed");
|
||||
@@ -187,7 +187,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn crypto_with_secret_create() {
|
||||
let keypair = Random.generate();
|
||||
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();
|
||||
@@ -196,7 +196,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn crypto_with_secret_invalid_password() {
|
||||
let keypair = Random.generate();
|
||||
let keypair = Random.generate().unwrap();
|
||||
let crypto =
|
||||
Crypto::with_secret(keypair.secret(), &"this is sparta".into(), *ITERATIONS).unwrap();
|
||||
assert_matches!(
|
||||
@@ -16,11 +16,10 @@
|
||||
|
||||
use super::crypto::Crypto;
|
||||
use account::Version;
|
||||
use crypto::{
|
||||
self,
|
||||
publickey::{ecdh::agree, sign, Address, KeyPair, Message, Public, Secret, Signature},
|
||||
use crypto;
|
||||
use ethkey::{
|
||||
self, crypto::ecdh::agree, sign, Address, KeyPair, Message, Password, Public, Secret, Signature,
|
||||
};
|
||||
use ethkey::Password;
|
||||
use json;
|
||||
use std::num::NonZeroU32;
|
||||
use Error;
|
||||
@@ -194,7 +193,7 @@ impl SafeAccount {
|
||||
message: &[u8],
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
let secret = self.crypto.secret(password)?;
|
||||
crypto::publickey::ecies::decrypt(&secret, shared_mac, message).map_err(From::from)
|
||||
ethkey::crypto::ecies::decrypt(&secret, shared_mac, message).map_err(From::from)
|
||||
}
|
||||
|
||||
/// Agree on shared key.
|
||||
@@ -238,7 +237,7 @@ impl SafeAccount {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{NonZeroU32, SafeAccount};
|
||||
use crypto::publickey::{verify_public, Generator, Random};
|
||||
use ethkey::{verify_public, Generator, Message, Random};
|
||||
|
||||
lazy_static! {
|
||||
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed");
|
||||
@@ -246,9 +245,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn sign_and_verify_public() {
|
||||
let keypair = Random.generate();
|
||||
let keypair = Random.generate().unwrap();
|
||||
let password = "hello world".into();
|
||||
let message = [1u8; 32].into();
|
||||
let message = Message::default();
|
||||
let account = SafeAccount::create(
|
||||
&keypair,
|
||||
[0u8; 16],
|
||||
@@ -263,10 +262,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn change_password() {
|
||||
let keypair = Random.generate();
|
||||
let keypair = Random.generate().unwrap();
|
||||
let first_password = "hello world".into();
|
||||
let sec_password = "this is sparta".into();
|
||||
let message = [1u8; 32].into();
|
||||
let message = Message::default();
|
||||
let account = SafeAccount::create(
|
||||
&keypair,
|
||||
[0u8; 16],
|
||||
@@ -420,7 +420,7 @@ mod test {
|
||||
use self::tempdir::TempDir;
|
||||
use super::{KeyDirectory, RootDiskDirectory, VaultKey};
|
||||
use account::SafeAccount;
|
||||
use crypto::publickey::{Generator, Random};
|
||||
use ethkey::{Generator, Random};
|
||||
use std::{env, fs, num::NonZeroU32};
|
||||
|
||||
lazy_static! {
|
||||
@@ -432,7 +432,7 @@ mod test {
|
||||
// given
|
||||
let mut dir = env::temp_dir();
|
||||
dir.push("ethstore_should_create_new_account");
|
||||
let keypair = Random.generate();
|
||||
let keypair = Random.generate().unwrap();
|
||||
let password = "hello world".into();
|
||||
let directory = RootDiskDirectory::create(dir.clone()).unwrap();
|
||||
|
||||
@@ -463,7 +463,7 @@ mod test {
|
||||
// given
|
||||
let mut dir = env::temp_dir();
|
||||
dir.push("ethstore_should_handle_duplicate_filenames");
|
||||
let keypair = Random.generate();
|
||||
let keypair = Random.generate().unwrap();
|
||||
let password = "hello world".into();
|
||||
let directory = RootDiskDirectory::create(dir.clone()).unwrap();
|
||||
|
||||
@@ -582,7 +582,7 @@ mod test {
|
||||
.expect("Files hash should be calculated ok");
|
||||
assert_eq!(hash, 15130871412783076140);
|
||||
|
||||
let keypair = Random.generate();
|
||||
let keypair = Random.generate().unwrap();
|
||||
let password = "test pass".into();
|
||||
let account = SafeAccount::create(
|
||||
&keypair,
|
||||
@@ -14,7 +14,7 @@
|
||||
// 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::publickey::Address;
|
||||
use ethkey::Address;
|
||||
use itertools;
|
||||
use parking_lot::RwLock;
|
||||
use std::collections::HashMap;
|
||||
@@ -70,7 +70,7 @@ impl KeyDirectory for MemoryDirectory {
|
||||
let mut val = 0u64;
|
||||
let accounts = self.accounts.read();
|
||||
for acc in accounts.keys() {
|
||||
val = val ^ acc.to_low_u64_be()
|
||||
val = val ^ acc.low_u64()
|
||||
}
|
||||
Ok(val)
|
||||
}
|
||||
@@ -14,7 +14,8 @@
|
||||
// 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::publickey::DerivationError;
|
||||
use crypto::{self, Error as EthCryptoError};
|
||||
use ethkey::{self, DerivationError, Error as EthKeyError};
|
||||
use std::{fmt, io::Error as IoError};
|
||||
|
||||
/// Account-related errors.
|
||||
@@ -44,10 +45,12 @@ pub enum Error {
|
||||
VaultNotFound,
|
||||
/// Account creation failed.
|
||||
CreationFailed,
|
||||
/// `crypto::publickey::Error`
|
||||
EthCrypto(crypto::Error),
|
||||
/// `EthKey` error
|
||||
EthKey(EthKeyError),
|
||||
/// `ethkey::crypto::Error`
|
||||
EthKeyCrypto(ethkey::crypto::Error),
|
||||
/// `EthCrypto` error
|
||||
EthCryptoPublicKey(crypto::publickey::Error),
|
||||
EthCrypto(EthCryptoError),
|
||||
/// Derivation error
|
||||
Derivation(DerivationError),
|
||||
/// Custom error
|
||||
@@ -69,8 +72,9 @@ impl fmt::Display for Error {
|
||||
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::EthCryptoPublicKey(ref err) => err.to_string(),
|
||||
Error::Derivation(ref err) => format!("Derivation error: {:?}", err),
|
||||
Error::Custom(ref s) => s.clone(),
|
||||
};
|
||||
@@ -85,14 +89,20 @@ impl From<IoError> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crypto::publickey::Error> for Error {
|
||||
fn from(err: crypto::publickey::Error) -> Self {
|
||||
Error::EthCryptoPublicKey(err)
|
||||
impl From<EthKeyError> for Error {
|
||||
fn from(err: EthKeyError) -> Self {
|
||||
Error::EthKey(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crypto::Error> for Error {
|
||||
fn from(err: crypto::Error) -> Self {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
//! ethkey reexport to make documentation look pretty.
|
||||
pub use _ethkey::*;
|
||||
pub use crypto::publickey::Address;
|
||||
use json;
|
||||
|
||||
impl Into<json::H160> for Address {
|
||||
@@ -24,10 +24,9 @@ use std::{
|
||||
|
||||
use account::SafeAccount;
|
||||
use accounts_dir::{KeyDirectory, SetKeyError, VaultKey, VaultKeyDirectory};
|
||||
use crypto::publickey::{
|
||||
self, Address, ExtendedKeyPair, KeyPair, Message, Public, Secret, Signature,
|
||||
use ethkey::{
|
||||
self, Address, ExtendedKeyPair, KeyPair, Message, Password, Public, Secret, Signature,
|
||||
};
|
||||
use ethkey::Password;
|
||||
use json::{self, OpaqueKeyFile, Uuid};
|
||||
use presale::PresaleWallet;
|
||||
use random::Random;
|
||||
@@ -555,17 +554,17 @@ impl EthMultiStore {
|
||||
Derivation::Hierarchical(path) => {
|
||||
for path_item in path {
|
||||
extended = extended.derive(if path_item.soft {
|
||||
publickey::Derivation::Soft(path_item.index)
|
||||
ethkey::Derivation::Soft(path_item.index)
|
||||
} else {
|
||||
publickey::Derivation::Hard(path_item.index)
|
||||
ethkey::Derivation::Hard(path_item.index)
|
||||
})?;
|
||||
}
|
||||
}
|
||||
Derivation::SoftHash(h256) => {
|
||||
extended = extended.derive(publickey::Derivation::Soft(h256))?;
|
||||
extended = extended.derive(ethkey::Derivation::Soft(h256))?;
|
||||
}
|
||||
Derivation::HardHash(h256) => {
|
||||
extended = extended.derive(publickey::Derivation::Hard(h256))?;
|
||||
extended = extended.derive(ethkey::Derivation::Hard(h256))?;
|
||||
}
|
||||
}
|
||||
Ok(extended)
|
||||
@@ -616,7 +615,7 @@ impl SimpleSecretStore for EthMultiStore {
|
||||
let accounts = self.get_matching(&account_ref, password)?;
|
||||
for account in accounts {
|
||||
let extended = self.generate(account.crypto.secret(password)?, derivation)?;
|
||||
return Ok(publickey::public_to_address(extended.public().public()));
|
||||
return Ok(ethkey::public_to_address(extended.public().public()));
|
||||
}
|
||||
Err(Error::InvalidPassword)
|
||||
}
|
||||
@@ -632,7 +631,7 @@ impl SimpleSecretStore for EthMultiStore {
|
||||
for account in accounts {
|
||||
let extended = self.generate(account.crypto.secret(password)?, derivation)?;
|
||||
let secret = extended.secret().as_raw();
|
||||
return Ok(publickey::sign(&secret, message)?);
|
||||
return Ok(ethkey::sign(&secret, message)?);
|
||||
}
|
||||
Err(Error::InvalidPassword)
|
||||
}
|
||||
@@ -899,14 +898,14 @@ mod tests {
|
||||
use self::tempdir::TempDir;
|
||||
use super::{EthMultiStore, EthStore};
|
||||
use accounts_dir::{KeyDirectory, MemoryDirectory, RootDiskDirectory};
|
||||
use crypto::publickey::{Generator, KeyPair, Random};
|
||||
use ethereum_types::H256;
|
||||
use ethkey::{Generator, KeyPair, Random};
|
||||
use secret_store::{
|
||||
Derivation, SecretStore, SecretVaultRef, SimpleSecretStore, StoreAccountRef,
|
||||
};
|
||||
|
||||
fn keypair() -> KeyPair {
|
||||
Random.generate()
|
||||
Random.generate().unwrap()
|
||||
}
|
||||
|
||||
fn store() -> EthStore {
|
||||
@@ -1052,7 +1051,6 @@ mod tests {
|
||||
let passwd2 = "xzy".into();
|
||||
let multi_store = multi_store();
|
||||
let keypair = keypair();
|
||||
let message = [1u8; 32].into();
|
||||
let address = store
|
||||
.insert_account(SecretVaultRef::Root, keypair.secret().clone(), &passwd1)
|
||||
.unwrap();
|
||||
@@ -1075,7 +1073,9 @@ mod tests {
|
||||
"First password should work for store."
|
||||
);
|
||||
assert!(
|
||||
multi_store.sign(&address, &passwd2, &message).is_ok(),
|
||||
multi_store
|
||||
.sign(&address, &passwd2, &Default::default())
|
||||
.is_ok(),
|
||||
"Second password should work for second store."
|
||||
);
|
||||
assert_eq!(multi_store.accounts().unwrap().len(), 1);
|
||||
@@ -1462,7 +1462,7 @@ mod tests {
|
||||
SecretVaultRef::Root,
|
||||
&address,
|
||||
&"test".into(),
|
||||
Derivation::HardHash(H256::zero()),
|
||||
Derivation::HardHash(H256::from(0)),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -1470,10 +1470,11 @@ mod tests {
|
||||
let accounts = store.accounts().unwrap();
|
||||
assert_eq!(accounts.len(), 2);
|
||||
|
||||
let message = [1u8; 32].into();
|
||||
// and we can sign with the derived contract
|
||||
assert!(
|
||||
store.sign(&derived, &"test".into(), &message).is_ok(),
|
||||
store
|
||||
.sign(&derived, &"test".into(), &Default::default())
|
||||
.is_ok(),
|
||||
"Second password should work for second store."
|
||||
);
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
use std::{collections::HashSet, fs, path::Path};
|
||||
|
||||
use accounts_dir::{DiskKeyFileManager, KeyDirectory, KeyFileManager};
|
||||
use crypto::publickey::Address;
|
||||
use ethkey::Address;
|
||||
use Error;
|
||||
|
||||
/// Import an account from a file.
|
||||
@@ -159,7 +159,10 @@ impl<'a> Visitor<'a> for CryptoVisitor {
|
||||
(Some(_), None) => return Err(V::Error::missing_field("cipherparams")),
|
||||
};
|
||||
|
||||
let ciphertext = ciphertext.ok_or_else(|| V::Error::missing_field("ciphertext"))?;
|
||||
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),
|
||||
@@ -169,7 +172,10 @@ impl<'a> Visitor<'a> for CryptoVisitor {
|
||||
(Some(_), None) => return Err(V::Error::missing_field("kdfparams")),
|
||||
};
|
||||
|
||||
let mac = mac.ok_or_else(|| V::Error::missing_field("mac"))?;
|
||||
let mac = match mac {
|
||||
Some(mac) => mac,
|
||||
None => return Err(V::Error::missing_field("mac")),
|
||||
};
|
||||
|
||||
let result = Crypto {
|
||||
cipher: cipher,
|
||||
@@ -169,11 +169,20 @@ impl<'a> Visitor<'a> for KeyFileVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
let id = id.ok_or_else(|| V::Error::missing_field("id"))?;
|
||||
let id = match id {
|
||||
Some(id) => id,
|
||||
None => return Err(V::Error::missing_field("id")),
|
||||
};
|
||||
|
||||
let version = version.ok_or_else(|| V::Error::missing_field("version"))?;
|
||||
let version = match version {
|
||||
Some(version) => version,
|
||||
None => return Err(V::Error::missing_field("version")),
|
||||
};
|
||||
|
||||
let crypto = crypto.ok_or_else(|| V::Error::missing_field("crypto"))?;
|
||||
let crypto = match crypto {
|
||||
Some(crypto) => crypto,
|
||||
None => return Err(V::Error::missing_field("crypto")),
|
||||
};
|
||||
|
||||
let result = KeyFile {
|
||||
id: id,
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
extern crate dir;
|
||||
extern crate itertools;
|
||||
extern crate libc;
|
||||
extern crate parking_lot;
|
||||
@@ -28,6 +29,7 @@ extern crate serde_json;
|
||||
extern crate smallvec;
|
||||
extern crate tempdir;
|
||||
extern crate time;
|
||||
extern crate tiny_keccak;
|
||||
|
||||
extern crate ethereum_types;
|
||||
extern crate ethkey as _ethkey;
|
||||
@@ -74,4 +76,4 @@ pub use self::{
|
||||
};
|
||||
|
||||
/// An opaque wrapper for secret.
|
||||
pub struct OpaqueSecret(crypto::publickey::Secret);
|
||||
pub struct OpaqueSecret(::ethkey::Secret);
|
||||
@@ -14,12 +14,8 @@
|
||||
// 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,
|
||||
publickey::{Address, KeyPair, Secret},
|
||||
Keccak256,
|
||||
};
|
||||
use ethkey::Password;
|
||||
use crypto::{self, pbkdf2, Keccak256};
|
||||
use ethkey::{Address, KeyPair, Password, Secret};
|
||||
use json;
|
||||
use std::{fs, num::NonZeroU32, path::Path};
|
||||
use Error;
|
||||
@@ -65,7 +61,7 @@ impl PresaleWallet {
|
||||
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.get(), salt, sec, &mut derived_key);
|
||||
pbkdf2::sha256(iter, salt, sec, &mut derived_key);
|
||||
|
||||
let mut key = vec![0; self.ciphertext.len()];
|
||||
let len =
|
||||
@@ -73,7 +69,7 @@ impl PresaleWallet {
|
||||
.map_err(|_| Error::InvalidPassword)?;
|
||||
let unpadded = &key[..len];
|
||||
|
||||
let secret = Secret::import_key(&unpadded.keccak256())?;
|
||||
let secret = Secret::from_unsafe_slice(&unpadded.keccak256())?;
|
||||
if let Ok(kp) = KeyPair::from_secret(secret) {
|
||||
if kp.address() == self.address {
|
||||
return Ok(kp);
|
||||
@@ -14,7 +14,7 @@
|
||||
// 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::{distributions::Alphanumeric, rngs::OsRng, Rng, RngCore};
|
||||
use rand::{OsRng, Rng};
|
||||
|
||||
pub trait Random {
|
||||
fn random() -> Self
|
||||
@@ -25,7 +25,7 @@ pub trait Random {
|
||||
impl Random for [u8; 16] {
|
||||
fn random() -> Self {
|
||||
let mut result = [0u8; 16];
|
||||
let mut rng = OsRng;
|
||||
let mut rng = OsRng::new().unwrap();
|
||||
rng.fill_bytes(&mut result);
|
||||
result
|
||||
}
|
||||
@@ -34,7 +34,7 @@ impl Random for [u8; 16] {
|
||||
impl Random for [u8; 32] {
|
||||
fn random() -> Self {
|
||||
let mut result = [0u8; 32];
|
||||
let mut rng = OsRng;
|
||||
let mut rng = OsRng::new().unwrap();
|
||||
rng.fill_bytes(&mut result);
|
||||
result
|
||||
}
|
||||
@@ -42,6 +42,6 @@ impl Random for [u8; 32] {
|
||||
|
||||
/// Generate a random string of given length.
|
||||
pub fn random_string(length: usize) -> String {
|
||||
let rng = OsRng;
|
||||
rng.sample_iter(&Alphanumeric).take(length).collect()
|
||||
let mut rng = OsRng::new().expect("Not able to operate without random source.");
|
||||
rng.gen_ascii_chars().take(length).collect()
|
||||
}
|
||||
@@ -14,9 +14,8 @@
|
||||
// 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::publickey::{Address, Message, Public, Secret, Signature};
|
||||
use ethereum_types::H256;
|
||||
use ethkey::Password;
|
||||
use ethkey::{Address, Message, Password, Public, Secret, Signature};
|
||||
use json::{OpaqueKeyFile, Uuid};
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
@@ -175,7 +174,7 @@ pub trait SecretStore: SimpleSecretStore {
|
||||
secret: &OpaqueSecret,
|
||||
message: &Message,
|
||||
) -> Result<Signature, Error> {
|
||||
Ok(crypto::publickey::sign(&secret.0, message)?)
|
||||
Ok(::ethkey::sign(&secret.0, message)?)
|
||||
}
|
||||
|
||||
/// Imports presale wallet
|
||||
@@ -14,19 +14,15 @@
|
||||
// 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 ethstore;
|
||||
extern crate parity_crypto as crypto;
|
||||
extern crate rand;
|
||||
|
||||
mod util;
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use crypto::publickey::{verify_address, Generator, KeyPair, Random, Secret};
|
||||
use ethereum_types::H160;
|
||||
use ethstore::{
|
||||
accounts_dir::RootDiskDirectory, EthStore, SecretVaultRef, SimpleSecretStore, StoreAccountRef,
|
||||
accounts_dir::RootDiskDirectory,
|
||||
ethkey::{verify_address, Generator, KeyPair, Random, Secret},
|
||||
EthStore, SecretVaultRef, SimpleSecretStore, StoreAccountRef,
|
||||
};
|
||||
use util::TransientDir;
|
||||
|
||||
@@ -44,7 +40,7 @@ fn secret_store_open_not_existing() {
|
||||
}
|
||||
|
||||
fn random_secret() -> Secret {
|
||||
Random.generate().secret().clone()
|
||||
Random.generate().unwrap().secret().clone()
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -70,10 +66,13 @@ fn secret_store_sign() {
|
||||
.insert_account(SecretVaultRef::Root, random_secret(), &"".into())
|
||||
.is_ok());
|
||||
let accounts = store.accounts().unwrap();
|
||||
let message = [1u8; 32].into();
|
||||
assert_eq!(accounts.len(), 1);
|
||||
assert!(store.sign(&accounts[0], &"".into(), &message).is_ok());
|
||||
assert!(store.sign(&accounts[0], &"1".into(), &message).is_err());
|
||||
assert!(store
|
||||
.sign(&accounts[0], &"".into(), &Default::default())
|
||||
.is_ok());
|
||||
assert!(store
|
||||
.sign(&accounts[0], &"1".into(), &Default::default())
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -84,14 +83,19 @@ fn secret_store_change_password() {
|
||||
.insert_account(SecretVaultRef::Root, random_secret(), &"".into())
|
||||
.is_ok());
|
||||
let accounts = store.accounts().unwrap();
|
||||
let message = [1u8; 32].into();
|
||||
assert_eq!(accounts.len(), 1);
|
||||
assert!(store.sign(&accounts[0], &"".into(), &message).is_ok());
|
||||
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(), &message).is_err());
|
||||
assert!(store.sign(&accounts[0], &"1".into(), &message).is_ok());
|
||||
assert!(store
|
||||
.sign(&accounts[0], &"".into(), &Default::default())
|
||||
.is_err());
|
||||
assert!(store
|
||||
.sign(&accounts[0], &"1".into(), &Default::default())
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -136,15 +140,9 @@ fn secret_store_laod_geth_files() {
|
||||
assert_eq!(
|
||||
store.accounts().unwrap(),
|
||||
vec![
|
||||
StoreAccountRef::root(
|
||||
H160::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap()
|
||||
),
|
||||
StoreAccountRef::root(
|
||||
H160::from_str("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf").unwrap()
|
||||
),
|
||||
StoreAccountRef::root(
|
||||
H160::from_str("63121b431a52f8043c16fcf0d1df9cb7b5f66649").unwrap()
|
||||
),
|
||||
StoreAccountRef::root("3f49624084b67849c7b4e805c5988c21a430f9d9".into()),
|
||||
StoreAccountRef::root("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf".into()),
|
||||
StoreAccountRef::root("63121b431a52f8043c16fcf0d1df9cb7b5f66649".into()),
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -156,12 +154,8 @@ fn secret_store_load_pat_files() {
|
||||
assert_eq!(
|
||||
store.accounts().unwrap(),
|
||||
vec![
|
||||
StoreAccountRef::root(
|
||||
H160::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap()
|
||||
),
|
||||
StoreAccountRef::root(
|
||||
H160::from_str("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf").unwrap()
|
||||
),
|
||||
StoreAccountRef::root("3f49624084b67849c7b4e805c5988c21a430f9d9".into()),
|
||||
StoreAccountRef::root("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf".into()),
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -188,16 +182,12 @@ fn test_decrypting_files_with_short_ciphertext() {
|
||||
assert_eq!(
|
||||
accounts,
|
||||
vec![
|
||||
StoreAccountRef::root(
|
||||
H160::from_str("31e9d1e6d844bd3a536800ef8d8be6a9975db509").unwrap()
|
||||
),
|
||||
StoreAccountRef::root(
|
||||
H160::from_str("d1e64e5480bfaf733ba7d48712decb8227797a4e").unwrap()
|
||||
),
|
||||
StoreAccountRef::root("31e9d1e6d844bd3a536800ef8d8be6a9975db509".into()),
|
||||
StoreAccountRef::root("d1e64e5480bfaf733ba7d48712decb8227797a4e".into()),
|
||||
]
|
||||
);
|
||||
|
||||
let message = [1u8; 32].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();
|
||||
@@ -18,11 +18,11 @@ use ethstore::{
|
||||
accounts_dir::{KeyDirectory, RootDiskDirectory},
|
||||
Error, SafeAccount,
|
||||
};
|
||||
use rand::{rngs::OsRng, RngCore};
|
||||
use rand::{OsRng, Rng};
|
||||
use std::{env, fs, path::PathBuf};
|
||||
|
||||
pub fn random_dir() -> PathBuf {
|
||||
let mut rng = OsRng;
|
||||
let mut rng = OsRng::new().unwrap();
|
||||
let mut dir = env::temp_dir();
|
||||
dir.push(format!("{:x}-{:x}", rng.next_u64(), rng.next_u64()));
|
||||
dir
|
||||
@@ -18,8 +18,7 @@
|
||||
|
||||
use std::{collections::HashMap, time::Instant};
|
||||
|
||||
use crypto::publickey::Address;
|
||||
use ethkey::Password;
|
||||
use ethkey::{Address, Password};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use serde_json;
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
|
||||
//! Account management.
|
||||
|
||||
extern crate parity_crypto as crypto;
|
||||
|
||||
mod account_data;
|
||||
mod error;
|
||||
mod stores;
|
||||
@@ -34,8 +32,7 @@ use std::{
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use crypto::publickey::{Address, Generator, Message, Public, Random, Secret};
|
||||
use ethkey::Password;
|
||||
use ethkey::{Address, Generator, Message, Password, Public, Random, Secret};
|
||||
use ethstore::{
|
||||
accounts_dir::MemoryDirectory, random_string, EthMultiStore, EthStore, OpaqueSecret,
|
||||
SecretStore, SecretVaultRef, SimpleSecretStore, StoreAccountRef,
|
||||
@@ -43,7 +40,7 @@ use ethstore::{
|
||||
use log::*;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
pub use crypto::publickey::Signature;
|
||||
pub use ethkey::Signature;
|
||||
pub use ethstore::{Derivation, Error, IndexDerivation, KeyFile};
|
||||
|
||||
pub use self::{account_data::AccountMeta, error::SignError};
|
||||
@@ -137,7 +134,9 @@ impl AccountProvider {
|
||||
|
||||
/// 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();
|
||||
let acc = Random
|
||||
.generate()
|
||||
.expect("secp context has generation capabilities; qed");
|
||||
let public = acc.public().clone();
|
||||
let secret = acc.secret().clone();
|
||||
let account = self
|
||||
@@ -356,8 +355,7 @@ impl AccountProvider {
|
||||
} else {
|
||||
// verify password by signing dump message
|
||||
// result may be discarded
|
||||
let dummy_msg = [1u8; 32].into();
|
||||
let _ = self.sstore.sign(&account, &password, &dummy_msg)?;
|
||||
let _ = self.sstore.sign(&account, &password, &Default::default())?;
|
||||
}
|
||||
|
||||
let data = AccountData {
|
||||
@@ -634,16 +632,15 @@ impl AccountProvider {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{AccountProvider, Unlock};
|
||||
use crypto::publickey::{Address, Generator, Random};
|
||||
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();
|
||||
let kp = Random.generate().unwrap();
|
||||
let ap = AccountProvider::transient_provider();
|
||||
let dummy_msg = [1u8; 32].into();
|
||||
assert!(ap
|
||||
.insert_account(kp.secret().clone(), &"test".into())
|
||||
.is_ok());
|
||||
@@ -653,13 +650,13 @@ mod tests {
|
||||
assert!(ap
|
||||
.unlock_account_temporarily(kp.address(), "test".into())
|
||||
.is_ok());
|
||||
assert!(ap.sign(kp.address(), None, dummy_msg).is_ok());
|
||||
assert!(ap.sign(kp.address(), None, dummy_msg).is_err());
|
||||
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();
|
||||
let kp = Random.generate().unwrap();
|
||||
let ap = AccountProvider::transient_provider();
|
||||
assert!(ap
|
||||
.insert_account(kp.secret().clone(), &"base".into())
|
||||
@@ -672,7 +669,7 @@ mod tests {
|
||||
.derive_account(
|
||||
&kp.address(),
|
||||
None,
|
||||
Derivation::SoftHash(H256::from_low_u64_be(999)),
|
||||
Derivation::SoftHash(H256::from(999)),
|
||||
false,
|
||||
)
|
||||
.expect("Derivation should not fail");
|
||||
@@ -686,7 +683,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn derived_account_save() {
|
||||
let kp = Random.generate();
|
||||
let kp = Random.generate().unwrap();
|
||||
let ap = AccountProvider::transient_provider();
|
||||
assert!(ap
|
||||
.insert_account(kp.secret().clone(), &"base".into())
|
||||
@@ -699,7 +696,7 @@ mod tests {
|
||||
.derive_account(
|
||||
&kp.address(),
|
||||
None,
|
||||
Derivation::SoftHash(H256::from_low_u64_be(999)),
|
||||
Derivation::SoftHash(H256::from(999)),
|
||||
true,
|
||||
)
|
||||
.expect("Derivation should not fail");
|
||||
@@ -719,7 +716,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn derived_account_sign() {
|
||||
let kp = Random.generate();
|
||||
let kp = Random.generate().unwrap();
|
||||
let ap = AccountProvider::transient_provider();
|
||||
assert!(ap
|
||||
.insert_account(kp.secret().clone(), &"base".into())
|
||||
@@ -732,14 +729,14 @@ mod tests {
|
||||
.derive_account(
|
||||
&kp.address(),
|
||||
None,
|
||||
Derivation::SoftHash(H256::from_low_u64_be(1999)),
|
||||
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 = [2u8; 32].into();
|
||||
let msg = Default::default();
|
||||
let signed_msg1 = ap
|
||||
.sign(derived_addr, None, msg)
|
||||
.expect("Signing with existing unlocked account should not fail");
|
||||
@@ -747,7 +744,7 @@ mod tests {
|
||||
.sign_derived(
|
||||
&kp.address(),
|
||||
None,
|
||||
Derivation::SoftHash(H256::from_low_u64_be(1999)),
|
||||
Derivation::SoftHash(H256::from(1999)),
|
||||
msg,
|
||||
)
|
||||
.expect("Derived signing with existing unlocked account should not fail");
|
||||
@@ -757,9 +754,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn unlock_account_perm() {
|
||||
let kp = Random.generate();
|
||||
let kp = Random.generate().unwrap();
|
||||
let ap = AccountProvider::transient_provider();
|
||||
let dummy_msg = [1u8; 32].into();
|
||||
assert!(ap
|
||||
.insert_account(kp.secret().clone(), &"test".into())
|
||||
.is_ok());
|
||||
@@ -769,20 +765,19 @@ mod tests {
|
||||
assert!(ap
|
||||
.unlock_account_permanently(kp.address(), "test".into())
|
||||
.is_ok());
|
||||
assert!(ap.sign(kp.address(), None, dummy_msg).is_ok());
|
||||
assert!(ap.sign(kp.address(), None, dummy_msg).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, dummy_msg).is_ok());
|
||||
assert!(ap.sign(kp.address(), None, dummy_msg).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();
|
||||
let kp = Random.generate().unwrap();
|
||||
let ap = AccountProvider::transient_provider();
|
||||
let dummy_msg = [1u8; 32].into();
|
||||
assert!(ap
|
||||
.insert_account(kp.secret().clone(), &"test".into())
|
||||
.is_ok());
|
||||
@@ -792,35 +787,35 @@ mod tests {
|
||||
assert!(ap
|
||||
.unlock_account_timed(kp.address(), "test".into(), Duration::from_secs(60))
|
||||
.is_ok());
|
||||
assert!(ap.sign(kp.address(), None, dummy_msg).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, dummy_msg).is_err());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_sign_and_return_token() {
|
||||
// given
|
||||
let kp = Random.generate();
|
||||
let kp = Random.generate().unwrap();
|
||||
let ap = AccountProvider::transient_provider();
|
||||
let dummy_msg = [1u8; 32].into();
|
||||
assert!(ap
|
||||
.insert_account(kp.secret().clone(), &"test".into())
|
||||
.is_ok());
|
||||
|
||||
// when
|
||||
let (_signature, token) = ap
|
||||
.sign_with_token(kp.address(), "test".into(), dummy_msg)
|
||||
.sign_with_token(kp.address(), "test".into(), Default::default())
|
||||
.unwrap();
|
||||
|
||||
// then
|
||||
ap.sign_with_token(kp.address(), token.clone(), dummy_msg)
|
||||
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, dummy_msg).is_err(),
|
||||
ap.sign_with_token(kp.address(), token, Default::default())
|
||||
.is_err(),
|
||||
"Second usage of the same token should fail."
|
||||
);
|
||||
}
|
||||
@@ -22,7 +22,7 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use crypto::publickey::Address;
|
||||
use ethkey::Address;
|
||||
use log::{trace, warn};
|
||||
|
||||
use crate::AccountMeta;
|
||||
@@ -172,7 +172,6 @@ impl<K: hash::Hash + Eq, V> DiskMap<K, V> {
|
||||
mod tests {
|
||||
use super::AddressBook;
|
||||
use crate::account_data::AccountMeta;
|
||||
use ethereum_types::H160;
|
||||
use std::collections::HashMap;
|
||||
use tempdir::TempDir;
|
||||
|
||||
@@ -180,8 +179,8 @@ mod tests {
|
||||
fn should_save_and_reload_address_book() {
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let mut b = AddressBook::new(tempdir.path());
|
||||
b.set_name(H160::from_low_u64_be(1), "One".to_owned());
|
||||
b.set_meta(H160::from_low_u64_be(1), "{1:1}".to_owned());
|
||||
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(),
|
||||
@@ -194,7 +193,7 @@ mod tests {
|
||||
}
|
||||
)]
|
||||
.into_iter()
|
||||
.map(|(a, b)| (H160::from_low_u64_be(a), b))
|
||||
.map(|(a, b)| (a.into(), b))
|
||||
.collect::<HashMap<_, _>>()
|
||||
);
|
||||
}
|
||||
@@ -204,10 +203,10 @@ mod tests {
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let mut b = AddressBook::new(tempdir.path());
|
||||
|
||||
b.set_name(H160::from_low_u64_be(1), "One".to_owned());
|
||||
b.set_name(H160::from_low_u64_be(2), "Two".to_owned());
|
||||
b.set_name(H160::from_low_u64_be(3), "Three".to_owned());
|
||||
b.remove(H160::from_low_u64_be(2));
|
||||
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!(
|
||||
@@ -231,7 +230,7 @@ mod tests {
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|(a, b)| (H160::from_low_u64_be(a), b))
|
||||
.map(|(a, b)| (a.into(), b))
|
||||
.collect::<HashMap<_, _>>()
|
||||
);
|
||||
}
|
||||
@@ -5,5 +5,5 @@ version = "0.1.0"
|
||||
authors = ["Marek Kotewicz <marek@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
ethjson = { path = "../../crates/ethjson" }
|
||||
ethjson = { path = "../json" }
|
||||
serde_json = "1.0"
|
||||
@@ -7,8 +7,8 @@ version = "1.4.0"
|
||||
authors = ["Parity <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
ethereum-types = "0.9.2"
|
||||
ethereum-types = "0.4"
|
||||
futures = "0.1"
|
||||
rpassword = "1.0"
|
||||
parity-rpc = { path = "../../rpc" }
|
||||
parity-rpc = { path = "../rpc" }
|
||||
parity-rpc-client = { path = "rpc-client" }
|
||||
@@ -7,7 +7,7 @@ version = "1.4.0"
|
||||
authors = ["Parity <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
ethereum-types = "0.9.2"
|
||||
ethereum-types = "0.4"
|
||||
futures = "0.1"
|
||||
log = "0.4"
|
||||
serde = "1.0"
|
||||
@@ -17,5 +17,5 @@ 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.5.0"
|
||||
parity-rpc = { path = "../../rpc" }
|
||||
keccak-hash = "0.1"
|
||||
@@ -259,7 +259,7 @@ impl Rpc {
|
||||
{
|
||||
let (c, p) = oneshot::<Result<JsonValue, RpcError>>();
|
||||
|
||||
let id = self.counter.fetch_add(1, Ordering::SeqCst);
|
||||
let id = self.counter.fetch_add(1, Ordering::Relaxed);
|
||||
self.pending.insert(id, c);
|
||||
|
||||
let request = MethodCall {
|
||||
@@ -1,35 +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 keccak_hash as hash;
|
||||
|
||||
pub type H256 = [u8; 32];
|
||||
|
||||
pub mod keccak_512 {
|
||||
use super::hash;
|
||||
|
||||
pub use self::hash::{
|
||||
keccak512 as inplace, keccak512_range as inplace_range, keccak_512 as write,
|
||||
};
|
||||
}
|
||||
|
||||
pub mod keccak_256 {
|
||||
use super::hash;
|
||||
|
||||
pub use self::hash::{
|
||||
keccak256 as inplace, keccak256_range as inplace_range, keccak_256 as write,
|
||||
};
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user