Compare commits
201 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70389b2888 | ||
|
|
8f63f6d32a | ||
|
|
f02f99cd3f | ||
|
|
bbbd6f01fc | ||
|
|
ddbce9f014 | ||
|
|
423c5e87da | ||
|
|
a15d2882d0 | ||
|
|
6d5809757f | ||
|
|
e179b0e0c2 | ||
|
|
37eb0fb04e | ||
|
|
32b0a80a18 | ||
|
|
9df9937922 | ||
|
|
eae66568a9 | ||
|
|
ece348d173 | ||
|
|
cc0c54a45c | ||
|
|
3f139116b2 | ||
|
|
771e3998a9 | ||
|
|
03fbde9723 | ||
|
|
3303aa73ff | ||
|
|
8367be6992 | ||
|
|
e6237ed05e | ||
|
|
0abdcedc03 | ||
|
|
59a735e2c4 | ||
|
|
bf24355480 | ||
|
|
4ed15931af | ||
|
|
c5553c1f21 | ||
|
|
33d633eed1 | ||
|
|
a3a0de096d | ||
|
|
aed708ffd8 | ||
|
|
66d3d7ec11 | ||
|
|
8503aeb2df | ||
|
|
a7ebc1dd25 | ||
|
|
584d89f964 | ||
|
|
4aeaab3a2f | ||
|
|
9750a31336 | ||
|
|
404005cc93 | ||
|
|
9b5bd37dc5 | ||
|
|
f96e5bc0d5 | ||
|
|
444c9c4375 | ||
|
|
2a2c24855c | ||
|
|
712302391c | ||
|
|
3023d76a46 | ||
|
|
f60d74b231 | ||
|
|
3e192ab486 | ||
|
|
fe313677b3 | ||
|
|
20f3f356e5 | ||
|
|
b9141b830b | ||
|
|
a938385692 | ||
|
|
c9ac0392d8 | ||
|
|
02dfc8632e | ||
|
|
ac2a62707a | ||
|
|
6f78523bf0 | ||
|
|
5dbbf9db13 | ||
|
|
0d6ffa2a97 | ||
|
|
3afb6370f9 | ||
|
|
dad4bfecdf | ||
|
|
902b44c94f | ||
|
|
54bf237e4f | ||
|
|
f835ce8253 | ||
|
|
4d04668c48 | ||
|
|
b4d95089d4 | ||
|
|
cca30f964e | ||
|
|
f11cc80561 | ||
|
|
4f71fd5d4d | ||
|
|
0340466f5f | ||
|
|
8847819b31 | ||
|
|
ca91fd5e76 | ||
|
|
df2619a37d | ||
|
|
4840371635 | ||
|
|
68e4156143 | ||
|
|
83f812b176 | ||
|
|
36c5525e3f | ||
|
|
78b5c12cf9 | ||
|
|
0564e7c168 | ||
|
|
7a4e91d26d | ||
|
|
ec26827f58 | ||
|
|
01b71bf9ba | ||
|
|
42040438ed | ||
|
|
799828b51e | ||
|
|
f78e8c3a38 | ||
|
|
4f242996d1 | ||
|
|
daecf5db3e | ||
|
|
9a09f52679 | ||
|
|
841a996fa6 | ||
|
|
ff99ccaf82 | ||
|
|
60f2aadb8e | ||
|
|
c143b1b783 | ||
|
|
2377d46c59 | ||
|
|
10d361aec5 | ||
|
|
bf4e4df4c8 | ||
|
|
3b766f01f2 | ||
|
|
2aa3864d67 | ||
|
|
04082d1514 | ||
|
|
85fa07728c | ||
|
|
a36ac996a0 | ||
|
|
6e666ecac7 | ||
|
|
08dc83205c | ||
|
|
bff142828d | ||
|
|
6d1ae888ca | ||
|
|
d5cc2c9e67 | ||
|
|
048dba7ef3 | ||
|
|
280c5e8a99 | ||
|
|
1257ad2993 | ||
|
|
5c72e60b14 | ||
|
|
aa6b6e477c | ||
|
|
245ef854dd | ||
|
|
c2cffd1e06 | ||
|
|
ddab958e61 | ||
|
|
a10160bd56 | ||
|
|
49be60e984 | ||
|
|
2b8a151b82 | ||
|
|
9bcc6c9e69 | ||
|
|
0fc605face | ||
|
|
8f1618687c | ||
|
|
cb246c26c0 | ||
|
|
086e45671b | ||
|
|
f3ad643b69 | ||
|
|
c5a4993e39 | ||
|
|
32cc0bddf6 | ||
|
|
d87286c5c3 | ||
|
|
ac3acdef71 | ||
|
|
dffa9bcef6 | ||
|
|
9ce75a173e | ||
|
|
755faf37e3 | ||
|
|
de21fef868 | ||
|
|
39aa02ed26 | ||
|
|
7a0dfec115 | ||
|
|
1fa019d92a | ||
|
|
7105ed3f61 | ||
|
|
2f411c93fd | ||
|
|
808f959a94 | ||
|
|
2aae862330 | ||
|
|
b18a7bde9a | ||
|
|
a0cdf5c420 | ||
|
|
b3dd72ab0a | ||
|
|
4d984ead4f | ||
|
|
fbc85e31c2 | ||
|
|
177d26a25e | ||
|
|
1f363b22fc | ||
|
|
5aed421fa3 | ||
|
|
f4df9c8dfd | ||
|
|
144385e67d | ||
|
|
9b6abb5861 | ||
|
|
6c81f1fb74 | ||
|
|
7371beb26c | ||
|
|
7acdc80b27 | ||
|
|
3847f8a9fc | ||
|
|
56e977b425 | ||
|
|
2f8e6be2ea | ||
|
|
180debb260 | ||
|
|
b79c7f05c0 | ||
|
|
d17c629b71 | ||
|
|
4be4a1c151 | ||
|
|
b0d7d48391 | ||
|
|
164cd268ad | ||
|
|
3a16ef19c1 | ||
|
|
74ea9cc74c | ||
|
|
2e88183df4 | ||
|
|
fca8eb5810 | ||
|
|
df4b326cb3 | ||
|
|
4db397ecb1 | ||
|
|
e767fd88f6 | ||
|
|
fae7039fda | ||
|
|
69d6f3ed3e | ||
|
|
4292e75dca | ||
|
|
2b4640c94c | ||
|
|
42a6954ec9 | ||
|
|
114bab66c4 | ||
|
|
13d0d6d006 | ||
|
|
c15e78cd95 | ||
|
|
24993b8d4e | ||
|
|
69d89583ca | ||
|
|
3c8fb60cbd | ||
|
|
9f8a131fa4 | ||
|
|
31f4a214a9 | ||
|
|
81f8f939b9 | ||
|
|
28e4847b59 | ||
|
|
30ecff3959 | ||
|
|
8add773b12 | ||
|
|
c20e761051 | ||
|
|
35be0e9943 | ||
|
|
3bf0a00ba4 | ||
|
|
570d2c21d3 | ||
|
|
b9fc5bdda0 | ||
|
|
1964ed7eb3 | ||
|
|
60bcab3e48 | ||
|
|
ebb7699da2 | ||
|
|
78821bb935 | ||
|
|
e8a21116f7 | ||
|
|
9f81b07224 | ||
|
|
8eeda2b1a3 | ||
|
|
0087568c16 | ||
|
|
75a11ba9be | ||
|
|
4ac9807bc1 | ||
|
|
1c26ffa208 | ||
|
|
f8b18d5d7b | ||
|
|
b327e79b42 | ||
|
|
8e2f35856a | ||
|
|
a029551b0b | ||
|
|
320581a3e4 | ||
|
|
f77b4119da |
@@ -1,27 +0,0 @@
|
||||
# NOTE: if you make changes here, remember to also update:
|
||||
# scripts/test-linux.sh
|
||||
# scripts/build-linux.sh
|
||||
# scripts/build-windows.sh
|
||||
|
||||
# Using 'cfg` is broken, see https://github.com/rust-lang/cargo/issues/6858
|
||||
#[target.'cfg(target_arch = "x86_64")']
|
||||
#rustflags = ["-Ctarget-feature=+aes,+sse2,+ssse3"]
|
||||
|
||||
# …so instead we list all target triples (Tier 1 64-bit platforms)
|
||||
[target.x86_64-unknown-linux-gnu]
|
||||
# Enables the aes-ni instructions for RustCrypto dependency.
|
||||
rustflags = ["-Ctarget-feature=+aes,+sse2,+ssse3"]
|
||||
|
||||
[target.x86_64-pc-windows-gnu]
|
||||
# Enables the aes-ni instructions for RustCrypto dependency.
|
||||
rustflags = ["-Ctarget-feature=+aes,+sse2,+ssse3"]
|
||||
|
||||
[target.x86_64-pc-windows-msvc]
|
||||
# Enables the aes-ni instructions for RustCrypto dependency.
|
||||
# Link the C runtime statically ; https://github.com/paritytech/parity-ethereum/issues/6643
|
||||
rustflags = ["-Ctarget-feature=+aes,+sse2,+ssse3", "-Ctarget-feature=+crt-static"]
|
||||
|
||||
[target.x86_64-apple-darwin]
|
||||
# Enables the aes-ni instructions for RustCrypto dependency.
|
||||
rustflags = ["-Ctarget-feature=+aes,+sse2,+ssse3"]
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
target
|
||||
|
||||
*.swp
|
||||
*.swo
|
||||
*.swn
|
||||
*.DS_Store
|
||||
|
||||
# Visual Studio Code stuff
|
||||
.vscode
|
||||
|
||||
# GitEye stuff
|
||||
.project
|
||||
|
||||
# idea ide
|
||||
.idea
|
||||
|
||||
# git stuff
|
||||
.git
|
||||
|
||||
ethcore/res/ethereum/tests
|
||||
@@ -9,8 +9,3 @@ trim_trailing_whitespace=true
|
||||
max_line_length=120
|
||||
insert_final_newline=true
|
||||
|
||||
[*.{yml,sh}]
|
||||
indent_style=space
|
||||
indent_size=2
|
||||
tab_width=8
|
||||
end_of_line=lf
|
||||
|
||||
84
.github/CODE_OF_CONDUCT.md
vendored
84
.github/CODE_OF_CONDUCT.md
vendored
@@ -1,84 +0,0 @@
|
||||
# Code of Conduct
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
A primary goal of Parity is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof).
|
||||
|
||||
This code of conduct outlines our expectations for all those who participate in our community, as well as the consequences for unacceptable behavior.
|
||||
|
||||
We invite all those who participate in Parity to help us create safe and positive experiences for everyone.
|
||||
|
||||
## 2. Open Source Citizenship
|
||||
|
||||
A supplemental goal of this Code of Conduct is to increase open source citizenship by encouraging participants to recognize and strengthen the relationships between our actions and their effects on our community.
|
||||
|
||||
Communities mirror the societies in which they exist and positive action is essential to counteract the many forms of inequality and abuses of power that exist in society.
|
||||
|
||||
If you see someone who is making an extra effort to ensure our community is welcoming, friendly, and encourages all participants to contribute to the fullest extent, we want to know.
|
||||
|
||||
## 3. Expected Behavior
|
||||
|
||||
The following behaviors are expected and requested of all community members:
|
||||
|
||||
* Participate in an authentic and active way. In doing so, you contribute to the health and longevity of this community.
|
||||
* Exercise consideration and respect in your speech and actions.
|
||||
* Attempt collaboration before conflict.
|
||||
* Refrain from demeaning, discriminatory, or harassing behavior and speech.
|
||||
* Be mindful of your surroundings and of your fellow participants. Alert community leaders if you notice a dangerous situation, someone in distress, or violations of this Code of Conduct, even if they seem inconsequential.
|
||||
* Remember that community event venues may be shared with members of the public; please be respectful to all patrons of these locations.
|
||||
|
||||
## 4. Unacceptable Behavior
|
||||
|
||||
The following behaviors are considered harassment and are unacceptable within our community:
|
||||
|
||||
* Violence, threats of violence or violent language directed against another person.
|
||||
* Sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory jokes and language.
|
||||
* Posting or displaying sexually explicit or violent material.
|
||||
* Posting or threatening to post other people’s personally identifying information ("doxing").
|
||||
* Personal insults, particularly those related to gender, sexual orientation, race, religion, or disability.
|
||||
* Inappropriate photography or recording.
|
||||
* Inappropriate physical contact. You should have someone’s consent before touching them.
|
||||
* Unwelcome sexual attention. This includes, sexualized comments or jokes; inappropriate touching, groping, and unwelcomed sexual advances.
|
||||
* Deliberate intimidation, stalking or following (online or in person).
|
||||
* Advocating for, or encouraging, any of the above behavior.
|
||||
* Sustained disruption of community events, including talks and presentations.
|
||||
|
||||
## 5. Consequences of Unacceptable Behavior
|
||||
|
||||
Unacceptable behavior from any community member, including sponsors and those with decision-making authority, will not be tolerated.
|
||||
|
||||
Anyone asked to stop unacceptable behavior is expected to comply immediately.
|
||||
|
||||
If a community member engages in unacceptable behavior, the community organizers may take any action they deem appropriate, up to and including a temporary ban or permanent expulsion from the community without warning (and without refund in the case of a paid event).
|
||||
|
||||
## 6. Reporting Guidelines
|
||||
|
||||
If you are subject to or witness unacceptable behavior, or have any other concerns, please notify a community organizer as soon as possible. community@parity.io.
|
||||
|
||||
Link to reporting guidelines: [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
|
||||
Link to security policy: [SECURITY.md](../SECURITY.md)
|
||||
|
||||
Additionally, community organizers are available to help community members engage with local law enforcement or to otherwise help those experiencing unacceptable behavior feel safe. In the context of in-person events, organizers will also provide escorts as desired by the person experiencing distress.
|
||||
|
||||
## 7. Addressing Grievances
|
||||
|
||||
If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify Parity Technologies with a concise description of your grievance. Your grievance will be handled in accordance with our existing governing policies.
|
||||
|
||||
## 8. Scope
|
||||
|
||||
We expect all community participants (contributors, paid or otherwise; sponsors; and other guests) to abide by this Code of Conduct in all community venues–online and in-person–as well as in all one-on-one communications pertaining to community business.
|
||||
|
||||
This code of conduct and its related procedures also applies to unacceptable behavior occurring outside the scope of community activities when such behavior has the potential to adversely affect the safety and well-being of community members.
|
||||
|
||||
## 9. Contact info
|
||||
|
||||
You can contact Parity via Email: community@parity.io
|
||||
|
||||
## 10. License and attribution
|
||||
|
||||
This Code of Conduct is distributed under a [Creative Commons Attribution-ShareAlike license](http://creativecommons.org/licenses/by-sa/3.0/).
|
||||
|
||||
Portions of text derived from the [Django Code of Conduct](https://www.djangoproject.com/conduct/) and the [Geek Feminism Anti-Harassment Policy](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Policy).
|
||||
|
||||
Retrieved on November 22, 2016 from [http://citizencodeofconduct.org/](http://citizencodeofconduct.org/)
|
||||
68
.github/CONTRIBUTING.md
vendored
68
.github/CONTRIBUTING.md
vendored
@@ -1,68 +0,0 @@
|
||||
# Contributing Guidelines
|
||||
|
||||
## Do you have a question?
|
||||
|
||||
Check out our [Basic Usage](https://wiki.parity.io/Basic-Usage), [Configuration](https://wiki.parity.io/Configuring-Parity-Ethereum), and [FAQ](https://wiki.parity.io/FAQ) articles on our [wiki](https://wiki.parity.io/)!
|
||||
|
||||
See also frequently asked questions [tagged with `parity`](https://ethereum.stackexchange.com/questions/tagged/parity?sort=votes&pageSize=50) on Stack Exchange.
|
||||
|
||||
## Report bugs!
|
||||
|
||||
Do **not** open an issue on Github if you think your discovered bug could be a **security-relevant vulnerability**. Please, read our [security policy](../SECURITY.md) instead.
|
||||
|
||||
Otherwise, just create a [new issue](https://github.com/paritytech/parity-ethereum/issues/new) in our repository and state:
|
||||
|
||||
- What's your Parity Ethereum version?
|
||||
- What's your operating system and version?
|
||||
- How did you install Parity Ethereum?
|
||||
- Is your node fully synchronized?
|
||||
- Did you try turning it off and on again?
|
||||
|
||||
Also, try to include **steps to reproduce** the issue and expand on the **actual versus expected behavior**.
|
||||
|
||||
## Contribute!
|
||||
|
||||
If you would like to contribute to Parity Ethereum, please **fork it**, fix bugs or implement features, and [propose a pull request](https://github.com/paritytech/parity-ethereum/compare).
|
||||
|
||||
### Labels & Milestones
|
||||
|
||||
We use [labels](https://github.com/paritytech/parity-ethereum/labels) to manage PRs and issues and communicate the state of a PR. Please familiarize yourself with them. Furthermore we are organizing issues in [milestones](https://github.com/paritytech/parity-ethereum/milestones). Best way to get started is to a pick a ticket from the current milestone tagged [`easy`](https://github.com/paritytech/parity-ethereum/labels/Q2-easy%20%F0%9F%92%83) and get going, or [`mentor`](https://github.com/paritytech/parity-ethereum/labels/Q1-mentor%20%F0%9F%95%BA) and get in contact with the mentor offering their support on that larger task.
|
||||
|
||||
### Rules
|
||||
|
||||
There are a few basic ground-rules for contributors (including the maintainer(s) of the project):
|
||||
|
||||
* **No pushing directly to the master branch**.
|
||||
* **All modifications** must be made in a **pull-request** to solicit feedback from other contributors.
|
||||
* Pull-requests cannot be merged before CI runs green and two reviewers have given their approval.
|
||||
* Contributors should adhere to the [Parity Ethereum Style Guide](https://wiki.parity.io/Parity-Ethereum-Style-Guide).
|
||||
|
||||
### Recommendations
|
||||
|
||||
* **Non-master branch names** *should* be prefixed with a short name moniker, followed by the associated Github Issue ID (if any), and a brief description of the task using the format `<GITHUB_USERNAME>-<ISSUE_ID>-<BRIEF_DESCRIPTION>` (e.g. `gavin-123-readme`). The name moniker helps people to inquiry about their unfinished work, and the GitHub Issue ID helps your future self and other developers (particularly those who are onboarding) find out about and understand the original scope of the task, and where it fits into Parity Ethereum [Projects](https://github.com/paritytech/parity-ethereum/projects).
|
||||
* **Remove stale branches periodically**
|
||||
|
||||
### Preparing Pull Requests
|
||||
|
||||
* If your PR does not alter any logic (e.g. comments, dependencies, docs), then it may be tagged [`insubstantial`](https://github.com/paritytech/parity-ethereum/pulls?q=is%3Aopen+is%3Apr+label%3A%22A2-insubstantial+%F0%9F%91%B6%22).
|
||||
|
||||
* Once a PR is ready for review please add the [`pleasereview`](https://github.com/paritytech/parity-ethereum/pulls?utf8=%E2%9C%93&q=is%3Aopen+is%3Apr+label%3A%22A0-pleasereview+%F0%9F%A4%93%22+) label.
|
||||
|
||||
### Reviewing Pull Requests*:
|
||||
|
||||
* At least two reviewers are required to review PRs (even for PRs tagged [`insubstantial`](https://github.com/paritytech/parity-ethereum/pulls?q=is%3Aopen+is%3Apr+label%3A%22A2-insubstantial+%F0%9F%91%B6%22)).
|
||||
|
||||
When doing a review, make sure to look for any:
|
||||
|
||||
* Buggy behavior.
|
||||
* Undue maintenance burden.
|
||||
* Breaking with house coding style.
|
||||
* Pessimization (i.e. reduction of speed as measured in the projects benchmarks).
|
||||
* Breaking changes should be carefuly reviewed and tagged as such so they end up in the [changelog](../CHANGELOG.md).
|
||||
* Uselessness (i.e. it does not strictly add a feature or fix a known issue).
|
||||
|
||||
## License.
|
||||
|
||||
By contributing to Parity Ethereum, you agree that your contributions will be licensed under the [GPLv3 License](../LICENSE).
|
||||
|
||||
Each contributor has to sign our Contributor License Agreement. The purpose of the CLA is to ensure that the guardian of a project's outputs has the necessary ownership or grants of rights over all contributions to allow them to distribute under the chosen license. You can read and sign our full Contributor License Agreement at [cla.parity.io](https://cla.parity.io) before submitting a pull request.
|
||||
15
.github/ISSUE_TEMPLATE.md
vendored
15
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,15 +0,0 @@
|
||||
_Before filing a new issue, please **provide the following information**._
|
||||
|
||||
_If you think that your issue is an exploitable security vulnerability, please mail your bugreport to security@parity.io instead; your submission might be eligible for our Bug Bounty._
|
||||
_You can find mode info on the reporting process in [SECURITY.md](https://github.com/paritytech/parity-ethereum/blob/master/SECURITY.md)_
|
||||
|
||||
|
||||
- **Parity Ethereum version**: 0.0.0
|
||||
- **Operating system**: Windows / MacOS / Linux
|
||||
- **Installation**: homebrew / one-line installer / built from source
|
||||
- **Fully synchronized**: no / yes
|
||||
- **Network**: ethereum / ropsten / goerli / ...
|
||||
- **Restarted**: no / yes
|
||||
|
||||
_Your issue description goes here below. Try to include **actual** vs. **expected behavior** and **steps to reproduce** the issue._
|
||||
|
||||
21
.github/PULL_REQUEST_TEMPLATE.md
vendored
21
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,21 +0,0 @@
|
||||
Thank you for your Pull Request!
|
||||
|
||||
Before you submitting, please check that:
|
||||
|
||||
- [ ] You added a brief description of the PR, e.g.:
|
||||
- What does it do?
|
||||
- What important points reviewers should know?
|
||||
- Is there something left for follow-up PRs?
|
||||
- [ ] You labeled the PR with appropriate labels if you have permissions to do so.
|
||||
- [ ] You mentioned a related issue if this PR related to it, e.g. `Fixes #228` or `Related #1337`.
|
||||
- [ ] You asked any particular reviewers to review. If you aren't sure, start with GH suggestions.
|
||||
- [ ] Your PR adheres [the style guide](https://wiki.parity.io/Coding-guide)
|
||||
- In particular, mind the maximal line length.
|
||||
- There is no commented code checked in unless necessary.
|
||||
- Any panickers have a proof or removed.
|
||||
- [ ] You updated any rustdocs which may have changed
|
||||
|
||||
After you've read this notice feel free to remove it.
|
||||
Thank you!
|
||||
|
||||
✄ -----------------------------------------------------------------------------
|
||||
13
.gitignore
vendored
13
.gitignore
vendored
@@ -15,18 +15,10 @@
|
||||
|
||||
# vim stuff
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# mac stuff
|
||||
.DS_Store
|
||||
|
||||
# npm stuff
|
||||
npm-debug.log
|
||||
node_modules
|
||||
|
||||
# js build artifacts
|
||||
.git-release.log
|
||||
|
||||
# gdb files
|
||||
.gdb_history
|
||||
|
||||
@@ -38,8 +30,3 @@ node_modules
|
||||
|
||||
# Build artifacts
|
||||
out/
|
||||
parity-clib-examples/cpp/build/
|
||||
|
||||
.vscode
|
||||
rls/
|
||||
/parity.*
|
||||
|
||||
356
.gitlab-ci.yml
356
.gitlab-ci.yml
@@ -1,356 +0,0 @@
|
||||
stages:
|
||||
- test
|
||||
- build
|
||||
- publish
|
||||
- optional
|
||||
|
||||
image: ${REGISTRY}/parity-ci-linux:latest
|
||||
|
||||
variables:
|
||||
GIT_STRATEGY: fetch
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
GIT_DEPTH: 3
|
||||
CI_SERVER_NAME: "GitLab CI"
|
||||
CARGO_HOME: "/ci-cache/${CI_PROJECT_NAME}/cargo/${CI_JOB_NAME}"
|
||||
CARGO_TARGET: x86_64-unknown-linux-gnu
|
||||
CARGO_INCREMENTAL: 0
|
||||
REGISTRY: registry.parity.io/parity/infrastructure/scripts
|
||||
|
||||
.releaseable_branches: # list of git refs for building GitLab artifacts (think "pre-release binaries")
|
||||
only: &releaseable_branches
|
||||
- stable
|
||||
- tags
|
||||
- schedules
|
||||
|
||||
.collect_artifacts: &collect_artifacts
|
||||
artifacts:
|
||||
name: "${CI_JOB_NAME}_${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}"
|
||||
when: on_success
|
||||
expire_in: 1 mos
|
||||
paths:
|
||||
- artifacts/
|
||||
- tools/
|
||||
|
||||
.docker-cache-status: &docker-cache-status
|
||||
dependencies: []
|
||||
interruptible: true
|
||||
before_script:
|
||||
- rustup show
|
||||
- cargo --version
|
||||
retry:
|
||||
max: 2
|
||||
when:
|
||||
- runner_system_failure
|
||||
- unknown_failure
|
||||
- api_failure
|
||||
tags:
|
||||
- linux-docker
|
||||
|
||||
.build-on-linux: &build-on-linux
|
||||
stage: build
|
||||
<<: *docker-cache-status
|
||||
<<: *collect_artifacts
|
||||
script:
|
||||
- scripts/gitlab/build-linux.sh
|
||||
after_script:
|
||||
- mkdir -p tools
|
||||
- cp -r scripts/docker/hub/* ./tools
|
||||
- cp scripts/gitlab/publish-snap.sh ./tools
|
||||
- cp scripts/gitlab/publish-onchain.sh ./tools
|
||||
- cp scripts/gitlab/safe-curl.sh ./tools
|
||||
- echo v"$(sed -r -n '1,/^version/s/^version\s*=\s*"([^"]+)".*$/\1/p' Cargo.toml)" |
|
||||
tee ./tools/VERSION
|
||||
- echo "$(sed -r -n '1,/^track/s/^track\s*=\s*"([^"]+)".*$/\1/p' ./util/version/Cargo.toml)" |
|
||||
tee ./tools/TRACK
|
||||
|
||||
|
||||
cargo-check 0 3:
|
||||
stage: test
|
||||
<<: *docker-cache-status
|
||||
script:
|
||||
- time cargo check --target $CARGO_TARGET --locked --no-default-features --verbose --color=always
|
||||
- sccache --show-stats
|
||||
|
||||
cargo-check 1 3:
|
||||
stage: test
|
||||
<<: *docker-cache-status
|
||||
script:
|
||||
- time cargo check --target $CARGO_TARGET --locked --manifest-path util/io/Cargo.toml --no-default-features --verbose --color=always
|
||||
- sccache --show-stats
|
||||
|
||||
cargo-check 2 3:
|
||||
stage: test
|
||||
<<: *docker-cache-status
|
||||
script:
|
||||
- time cargo check --target $CARGO_TARGET --locked --manifest-path util/io/Cargo.toml --features "mio" --verbose --color=always
|
||||
- sccache --show-stats
|
||||
|
||||
cargo-check-evmbin:
|
||||
stage: test
|
||||
<<: *docker-cache-status
|
||||
script:
|
||||
- time cargo check -p evmbin --target $CARGO_TARGET --locked --verbose --color=always
|
||||
- sccache --show-stats
|
||||
|
||||
cargo-check-benches:
|
||||
stage: test
|
||||
<<: *docker-cache-status
|
||||
script:
|
||||
- time cargo check --all --benches --target $CARGO_TARGET --locked --verbose --color=always
|
||||
- sccache --show-stats
|
||||
|
||||
cargo-audit:
|
||||
stage: test
|
||||
<<: *docker-cache-status
|
||||
script:
|
||||
- cargo audit
|
||||
allow_failure: true # failed cargo audit shouldn't prevent a PR from being merged
|
||||
|
||||
validate-chainspecs:
|
||||
stage: test
|
||||
<<: *docker-cache-status
|
||||
script:
|
||||
- ./scripts/gitlab/validate-chainspecs.sh
|
||||
|
||||
test-linux:
|
||||
stage: build
|
||||
<<: *docker-cache-status
|
||||
script:
|
||||
- ./scripts/gitlab/test-linux.sh stable
|
||||
|
||||
test-linux-beta:
|
||||
stage: build
|
||||
only: *releaseable_branches
|
||||
<<: *docker-cache-status
|
||||
script:
|
||||
- ./scripts/gitlab/test-linux.sh beta
|
||||
|
||||
test-linux-nightly:
|
||||
stage: build
|
||||
only: *releaseable_branches
|
||||
<<: *docker-cache-status
|
||||
script:
|
||||
- ./scripts/gitlab/test-linux.sh nightly
|
||||
allow_failure: true
|
||||
|
||||
build-linux:
|
||||
<<: *build-on-linux
|
||||
only: *releaseable_branches
|
||||
|
||||
build-linux-i386:
|
||||
<<: *build-on-linux
|
||||
only: *releaseable_branches
|
||||
image: ${REGISTRY}/parity-ci-i386:latest
|
||||
variables:
|
||||
CARGO_TARGET: i686-unknown-linux-gnu
|
||||
|
||||
build-linux-arm64:
|
||||
<<: *build-on-linux
|
||||
only: *releaseable_branches
|
||||
image: ${REGISTRY}/parity-ci-arm64:latest
|
||||
variables:
|
||||
CARGO_TARGET: aarch64-unknown-linux-gnu
|
||||
|
||||
build-linux-armhf:
|
||||
<<: *build-on-linux
|
||||
only: *releaseable_branches
|
||||
image: ${REGISTRY}/parity-ci-armhf:latest
|
||||
variables:
|
||||
CARGO_TARGET: armv7-unknown-linux-gnueabihf
|
||||
|
||||
build-darwin:
|
||||
stage: build
|
||||
<<: *collect_artifacts
|
||||
only: *releaseable_branches
|
||||
variables:
|
||||
CARGO_TARGET: x86_64-apple-darwin
|
||||
CARGO_HOME: "${CI_PROJECT_DIR}/.cargo"
|
||||
script:
|
||||
- scripts/gitlab/build-linux.sh
|
||||
tags:
|
||||
- rust-osx
|
||||
|
||||
build-windows:
|
||||
stage: build
|
||||
<<: *collect_artifacts
|
||||
only: *releaseable_branches
|
||||
variables:
|
||||
CARGO_TARGET: x86_64-pc-windows-msvc
|
||||
CARGO_HOME: "C:/ci-cache/parity-ethereum/cargo/$CI_JOB_NAME"
|
||||
GIT_SUBMODULE_STRATEGY: none
|
||||
script:
|
||||
- sh scripts/gitlab/build-windows.sh
|
||||
tags:
|
||||
- rust-windows
|
||||
|
||||
publish-docker:
|
||||
stage: publish
|
||||
only: *releaseable_branches
|
||||
except:
|
||||
- nightly
|
||||
when: manual
|
||||
dependencies:
|
||||
- build-linux
|
||||
environment:
|
||||
name: parity-build
|
||||
cache: {}
|
||||
image: docker:stable
|
||||
services:
|
||||
- docker:dind
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
DOCKER_HOST: tcp://localhost:2375
|
||||
DOCKER_DRIVER: overlay2
|
||||
GIT_STRATEGY: none
|
||||
# DOCKERFILE: tools/Dockerfile
|
||||
# CONTAINER_IMAGE: parity/parity
|
||||
script:
|
||||
- ./tools/publish-docker.sh
|
||||
tags:
|
||||
- kubernetes-parity-build
|
||||
|
||||
publish-snap-nightly: &publish-snap
|
||||
stage: publish
|
||||
only:
|
||||
- nightly
|
||||
image: snapcore/snapcraft
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
BUILD_ARCH: amd64
|
||||
cache: {}
|
||||
dependencies:
|
||||
- build-linux
|
||||
tags:
|
||||
- linux-docker
|
||||
script:
|
||||
- ./tools/publish-snap.sh
|
||||
|
||||
publish-snap-manually:
|
||||
<<: *publish-snap
|
||||
only: *releaseable_branches
|
||||
when: manual
|
||||
|
||||
publish-snap-i386-nightly: &publish-snap-i386
|
||||
<<: *publish-snap
|
||||
variables:
|
||||
BUILD_ARCH: i386
|
||||
CARGO_TARGET: i686-unknown-linux-gnu
|
||||
dependencies:
|
||||
- build-linux-i386
|
||||
|
||||
publish-snap-i386-manually:
|
||||
<<: *publish-snap-i386
|
||||
only: *releaseable_branches
|
||||
when: manual
|
||||
|
||||
publish-snap-arm64-nightly: &publish-snap-arm64
|
||||
<<: *publish-snap
|
||||
variables:
|
||||
BUILD_ARCH: arm64
|
||||
CARGO_TARGET: aarch64-unknown-linux-gnu
|
||||
dependencies:
|
||||
- build-linux-arm64
|
||||
|
||||
publish-snap-arm64-manually:
|
||||
<<: *publish-snap-arm64
|
||||
only: *releaseable_branches
|
||||
when: manual
|
||||
|
||||
publish-snap-armhf-nightly: &publish-snap-armhf
|
||||
<<: *publish-snap
|
||||
variables:
|
||||
BUILD_ARCH: armhf
|
||||
CARGO_TARGET: armv7-unknown-linux-gnueabihf
|
||||
dependencies:
|
||||
- build-linux-armhf
|
||||
|
||||
publish-snap-armhf-manually:
|
||||
<<: *publish-snap-armhf
|
||||
only: *releaseable_branches
|
||||
when: manual
|
||||
|
||||
publish-onchain-nightly: &publish-onchain
|
||||
stage: publish
|
||||
only:
|
||||
- nightly
|
||||
cache: {}
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
dependencies:
|
||||
- build-linux
|
||||
- build-darwin
|
||||
- build-windows
|
||||
script:
|
||||
- ./tools/publish-onchain.sh
|
||||
tags:
|
||||
- linux-docker
|
||||
|
||||
publish-onchain-manually:
|
||||
<<: *publish-onchain
|
||||
only: *releaseable_branches
|
||||
when: manual
|
||||
|
||||
publish-release-awss3-nightly: &publish-release-awss3
|
||||
image: ${REGISTRY}/awscli:latest
|
||||
stage: publish
|
||||
only:
|
||||
- nightly
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
cache: {}
|
||||
dependencies:
|
||||
- build-linux
|
||||
- build-darwin
|
||||
- build-windows
|
||||
script:
|
||||
- echo "__________Push binaries to AWS S3____________"
|
||||
- case "${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}" in
|
||||
(stable|nightly)
|
||||
export BUCKET=releases.parity.io/ethereum;
|
||||
;;
|
||||
(*)
|
||||
export BUCKET=builds-parity;
|
||||
;;
|
||||
esac
|
||||
- aws s3 sync ./artifacts s3://${BUCKET}/${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}/
|
||||
- echo "__________Read from S3____________"
|
||||
- aws s3 ls s3://${BUCKET}/${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}} --recursive --human-readable --summarize
|
||||
tags:
|
||||
- linux-docker
|
||||
|
||||
publish-release-awss3-manually:
|
||||
<<: *publish-release-awss3
|
||||
only: *releaseable_branches
|
||||
when: manual
|
||||
|
||||
publish-docs:
|
||||
stage: publish
|
||||
image: ${REGISTRY}/parity-ci-docs:latest
|
||||
only:
|
||||
- tags
|
||||
except:
|
||||
- nightly
|
||||
when: manual
|
||||
cache: {}
|
||||
dependencies: []
|
||||
script:
|
||||
- scripts/gitlab/publish-docs.sh
|
||||
tags:
|
||||
- linux-docker
|
||||
allow_failure: true
|
||||
|
||||
publish-av-whitelist:
|
||||
stage: publish
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
only: *releaseable_branches
|
||||
except:
|
||||
- nightly
|
||||
when: manual
|
||||
cache: {}
|
||||
dependencies:
|
||||
- build-windows
|
||||
script:
|
||||
- scripts/gitlab/publish-av-whitelists.sh
|
||||
tags:
|
||||
- linux-docker
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -2,6 +2,3 @@
|
||||
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
|
||||
|
||||
103
.travis.yml
Normal file
103
.travis.yml
Normal file
@@ -0,0 +1,103 @@
|
||||
sudo: required
|
||||
dist: trusty
|
||||
language: rust
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- /^beta-.*$/
|
||||
- /^stable-.*$/
|
||||
- /^beta$/
|
||||
- /^stable$/
|
||||
git:
|
||||
depth: 3
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- rust: nightly
|
||||
include:
|
||||
- rust: stable
|
||||
env: FEATURES="--features travis-beta" RUN_TESTS="true"
|
||||
# - rust: beta
|
||||
# env: FEATURES="--features travis-beta" RUN_TESTS="true"
|
||||
- rust: stable
|
||||
env: FEATURES="--features travis-beta" RUN_BUILD="true"
|
||||
- rust: beta
|
||||
env: FEATURES="--features travis-beta" RUN_BUILD="true"
|
||||
- rust: stable
|
||||
env: FEATURES="--features travis-beta" RUN_COVERAGE="true"
|
||||
# - rust: nightly
|
||||
# env: FEATURES="--features travis-nightly" RUN_BENCHES="true"
|
||||
- rust: nightly
|
||||
env: FEATURES="--features travis-nightly" RUN_TESTS="true"
|
||||
env:
|
||||
global:
|
||||
# GH_TOKEN
|
||||
- secure: bumJASbZSU8bxJ0EyPUJmu16AiV9EXOpyOj86Jlq/Ty9CfwGqsSXt96uDyE+OUJf34RUFQMsw0nk37/zC4lcn6kqk2wpuH3N/o85Zo/cVZY/NusBWLQqtT5VbYWsV+u2Ua4Tmmsw8yVYQhYwU2ZOejNpflL+Cs9XGgORp1L+/gMRMC2y5Se6ZhwnKPQlRJ8LGsG1dzjQULxzADIt3/zuspNBS8a2urJwlHfGMkvHDoUWCviP/GXoSqw3TZR7FmKyxE19I8n9+iSvm9+oZZquvcgfUxMHn8Gq/b44UbPvjtFOg2yam4xdWXF/RyWCHdc/R9EHorSABeCbefIsm+zcUF3/YQxwpSxM4IZEeH2rTiC7dcrsKw3XsO16xFQz5YI5Bay+CT/wTdMmJd7DdYz7Dyf+pOvcM9WOf/zorxYWSBOMYy0uzbusU2iyIghQ82s7E/Ahg+WARtPgkuTLSB5aL1oCTBKHqQscMr7lo5Ti6RpWLxEdTQMBznc+bMr+6dEtkEcG9zqc6cE9XX+ox3wTU6+HVMfQ1ltCntJ4UKcw3A6INEbw9wgocQa812CIASQ2fE+SCAbz6JxBjIAlFUnD1lUB7S8PdMPwn9plfQgKQ2A5YZqg6FnBdf0rQXIJYxQWKHXj/rBHSUCT0tHACDlzTA+EwWggvkP5AGIxRxm8jhw=
|
||||
- CVER=5
|
||||
- NUM_JOBS=1
|
||||
# - TARGETS="-p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethminer"
|
||||
- TARGETS="-p ethcore-util"
|
||||
- ARCHIVE_SUFFIX="-${TRAVIS_OS_NAME}-${TRAVIS_TAG}"
|
||||
- KCOV_FEATURES=""
|
||||
- KCOV_CMD="./kcov-master/tmp/usr/local/bin/kcov --exclude-pattern /usr/,/.cargo,/root/.multirust,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests target/kcov"
|
||||
- RUN_TESTS="false"
|
||||
- RUN_COVERAGE="false"
|
||||
- RUN_BUILD="false"
|
||||
- RUN_BENCHES="false"
|
||||
cache:
|
||||
apt: true
|
||||
directories:
|
||||
- $TRAVIS_BUILD_DIR/target
|
||||
- $HOME/.cargo
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libcurl4-openssl-dev
|
||||
- libelf-dev
|
||||
- libdw-dev
|
||||
|
||||
script:
|
||||
- echo "$TRAVIS_BUILD_DIR/target"
|
||||
- ls "$TRAVIS_BUILD_DIR/target"
|
||||
- if [ "$RUN_TESTS" = "true" ]; then cargo test --release --verbose ${FEATURES} ${TARGETS}; fi
|
||||
- if [ "$RUN_BENCHES" = "true" ]; then cargo bench --no-run ${FEATURES} ${TARGETS}; fi
|
||||
- if [ "$RUN_BUILD" = "true" ]; then cargo build --release --verbose ${FEATURES}; fi
|
||||
- if [ "$RUN_BUILD" = "true" ]; then tar cvzf parity${ARCHIVE_SUFFIX}.tar.gz -C target/release parity; fi
|
||||
|
||||
after_success: |
|
||||
[ "$RUN_COVERAGE" = "true" ] &&
|
||||
wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz &&
|
||||
tar xzf master.tar.gz && mkdir kcov-master/build && cd kcov-master/build && cmake .. && make && make install DESTDIR=../tmp && cd ../.. &&
|
||||
cargo test --no-run ${KCOV_FEATURES} ${TARGETS} &&
|
||||
$KCOV_CMD target/debug/deps/ethcore_util-* &&
|
||||
$KCOV_CMD target/debug/deps/ethash-* &&
|
||||
$KCOV_CMD target/debug/deps/ethcore-* &&
|
||||
$KCOV_CMD target/debug/deps/ethsync-* &&
|
||||
$KCOV_CMD target/debug/deps/ethcore_rpc-* &&
|
||||
$KCOV_CMD target/debug/deps/ethminer-* &&
|
||||
$KCOV_CMD target/debug/parity-* &&
|
||||
[ $TRAVIS_BRANCH = master ] &&
|
||||
[ $TRAVIS_PULL_REQUEST = false ] &&
|
||||
[ $TRAVIS_RUST_VERSION = stable ] &&
|
||||
cargo doc --no-deps --verbose ${KCOV_FEATURES} ${TARGETS} &&
|
||||
echo '<meta http-equiv=refresh content=0;url=ethcore/index.html>' > target/doc/index.html &&
|
||||
pip install --user ghp-import &&
|
||||
/home/travis/.local/bin/ghp-import -n target/doc &&
|
||||
git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages
|
||||
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: "t+oGT/4lsy7IScw5s86Dpntl5Nyck4qG6nhHwMScc6FYzwLldgwgJaafL8Ej+HG+b7nFLriN+Snoa4YQ5o74X5ZlSWubVREOYQlL/fq7vcPB0DwAZ0Jufq1QW2R1M+3SwwF1eAwTv2W3G7A2K7dxjCVvENcy/gdxnZ36NeUPsqaCC9UcI2Yc7+4jyQwvx6ZfBvQeu+HbKENA0eUNs2ZQOID/1IPy0LJBvSyxAQYsysXdjTzGdNu4+Iba20E8uWYe4fAbgz+gwGarXg1L6D6gKyMlWkViqWjvXWBuDJJqMQZ3rw41AwZOoh3mKd2Lc0l6l4oZcEqPuob0yKTNjz1tuJy9xKTC2F2bDzsvUgk1IRfMK5ukXXXS09ZCZWuA9/GtnsqJ1xGTiwX+DhQzpVBHaBiseSNlYE1YN/3jNyGY+iSts1qut+1BwE7swmcTLsAPoAy8Ue+f7ErNoCg1lm71vq7VO2DLn7x2NqHyHUEuJ+7olDHSdE84G7d9otDRu/+TfMOw7GXwTaha6yJRInuNsnj4CFMLNVvYACzCC2idB7f7nUZoSFi9jf18S9fCMPVmazMrFj4g95HWrVHkjpV5zRTeUdTWw6DJl6pC9HFqORHdCvLv4Rc4dm5r3CmOcAQ0ZuiccV2oKzw4/Wic96daae8M5f5KSQ/WTr+h0wXZKp0="
|
||||
skip_cleanup: true
|
||||
file: parity${ARCHIVE_SUFFIX}.tar.gz
|
||||
on:
|
||||
tags: true
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://hooks.slack.com/services/${SLACK_WEBHOOK}
|
||||
on_success: always
|
||||
on_failure: always
|
||||
on_start: never
|
||||
397
CHANGELOG.md
397
CHANGELOG.md
@@ -1,397 +0,0 @@
|
||||
## Parity-Ethereum [v2.7.1](https://github.com/paritytech/parity-ethereum/releases/tag/v2.7.1)
|
||||
Parity Ethereum v2.7.2-stable is a patch version release of parity-ethereum.
|
||||
Starting in the 2.7.x series of releases, parity-ethereum is switching to a single `stable` release
|
||||
track. As a result, any clients that currently receive updates from the `beta`
|
||||
track should switch to the `stable` track.
|
||||
Due to database format changes, upgrading from 2.5.x or 2.6.x is one-way only.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
* backwards compatible call_type creation_method (#11450 + #11455)
|
||||
* chore: remove unused dependencies (#11432)
|
||||
* Cargo.lock: new lockfile format (#11448)
|
||||
* rlp_derive: cleanup (#11446)
|
||||
* Avoid long state queries when serving GetNodeData requests (#11444)
|
||||
* update kvdb-rocksdb to 0.4 (#11442)
|
||||
* Remove dead bootnodes, add new geth bootnodes (#11441)
|
||||
* goerli: replace foundation bootnode (#11433)
|
||||
* fix: export hardcoded sync format (#11416)
|
||||
* verification: fix race same block + misc (#11400)
|
||||
* update classic testnet bootnodes (#11398)
|
||||
* gcc to clang (#11453)
|
||||
|
||||
## Parity-Ethereum [v2.7.1](https://github.com/paritytech/parity-ethereum/releases/tag/v2.7.1)
|
||||
Parity Ethereum v2.7.1-stable is a patch version release of parity-ethereum.
|
||||
Starting in the 2.7.x series of releases, parity-ethereum is switching to a single `stable` release
|
||||
track. As a result, any clients that currently receive updates from the `beta`
|
||||
track should switch to the `stable` track.
|
||||
Due to database format changes, upgrading from 2.5.x or 2.6.x is one-way only.
|
||||
|
||||
The full list of included changes from `v2.7.0` to `v2.7.1`:
|
||||
|
||||
* Revert "Distinguish between `create` and `create2` (#11311)" (#11427)
|
||||
|
||||
## Parity-Ethereum [v2.7.0](https://github.com/paritytech/parity-ethereum/releases/tag/v2.7.0)
|
||||
|
||||
Parity Ethereum v2.7.0-stable is a minor version release of parity-ethereum. As
|
||||
of this release, parity-ethereum is switching to a single `stable` release
|
||||
track. As a result, any clients that currently receive updates from the `beta`
|
||||
track should switch to the `stable` track.
|
||||
|
||||
The full list of included changes from `v2.5-stable` to `v2.7-stable` (the
|
||||
`v2.6-beta` branch will already include some of these changes):
|
||||
|
||||
* Update POA bootnodes (#11411)
|
||||
* Update ProgPoW to 0.9.3 (#11407)
|
||||
* Add EtherCore support (#11402)
|
||||
* json-tests: Fix compile error (#11384)
|
||||
* ethcore/res: fix ethereum classic chainspec blake2_f activation block num (#11391)
|
||||
* Switching to stable-track (#11377)
|
||||
* Update copyright notice 2020 (#11386)
|
||||
* miner: fix deprecation warning Error::description (#11380)
|
||||
* Fix Aztlan hard fork issues (#11347)
|
||||
* authority_round: Fix next_step_time_duration. (#11379)
|
||||
* Set the block gas limit to the value returned by a contract call (#10928)
|
||||
* [Trace] Distinguish between `create` and `create2` (#11311)
|
||||
* fix cargo audit (#11378)
|
||||
* Fix esoteric test config variable (#11292)
|
||||
* Rip out the C and Java bindings (#11346)
|
||||
* Encapsulate access to the client for secret store (#11232)
|
||||
* Forward-port #11356 (#11359)
|
||||
* Fix error message typo (#11363)
|
||||
* [util/migration]: remove needless `static` bounds (#11348)
|
||||
* Replace stale boot nodes with latest list (#11351)
|
||||
* Update to latest `kvdb-*`: no default column, DBValue is Vec (#11312)
|
||||
* we do not profit from incremental now (#11302)
|
||||
* update autoupdate fork blocks for nightly (#11308)
|
||||
* Add Nat PMP method to P2P module (#11210)
|
||||
* Add randomness contract support to AuthorityRound. (#10946)
|
||||
* ethcore/res: activate ecip-1061 on kotti and mordor (#11338)
|
||||
* tx-q: enable basic verification of local transactions (#11332)
|
||||
* remove null signatures (#11335)
|
||||
* ethcore/res: activate agharta on classic 9573000 (#11331)
|
||||
* [secretstore] migrate to version 4 (#11322)
|
||||
* Enable EIP-2384 for ice age hard fork (#11281)
|
||||
* Fix atomicity violation in network-devp2p (#11277)
|
||||
* Istanbul activation on xDai (#11299)
|
||||
* Istanbul activation on POA Core (#11298)
|
||||
* Adds support for ipc socket permissions (#11273)
|
||||
* Add check for deserialising hex values over U256 limit (#11309)
|
||||
* validate-chainspecs: check istanbul eips are in the foundation spec (#11305)
|
||||
* [chainspec]: add `eip1344_transition` for istanbul (#11301)
|
||||
* only add transactions to signing-queue if it is enabled (#11272)
|
||||
* Use upstream rocksdb (#11248)
|
||||
* Treat only blocks in queue as synced (#11264)
|
||||
* add support for evan.network chains (#11289)
|
||||
* Add benchmarks and tests for RlpNodeCodec decoding (#11287)
|
||||
* upgrade vergen to 3.0 (#11293)
|
||||
* interruptible test and build jobs (#11294)
|
||||
* Istanbul HF on POA Sokol (#11282)
|
||||
* [ethcore]: apply filter when `PendingSet::AlwaysQueue` in `ready_transactions_filtered` (#11227)
|
||||
* Update lib.rs (#11286)
|
||||
* Don't prune ancient state when instantiating a Client (#11270)
|
||||
* fixed verify_uncles error type (#11276)
|
||||
* ethcore: fix rlp deprecation warnings (#11280)
|
||||
* Upgrade trie-db to 0.16.0. (#11274)
|
||||
* Clarify what first_block `None` means (#11269)
|
||||
* removed redundant VMType enum with one variant (#11266)
|
||||
* Ensure jsonrpc threading settings are sane (#11267)
|
||||
* Return Ok(None) when the registrar contract returns empty slice (#11257)
|
||||
* Add a benchmark for snapshot::account::to_fat_rlps() (#11185)
|
||||
* Fix misc compile warnings (#11258)
|
||||
* simplify verification (#11249)
|
||||
* update ropsten forkCanonHash, forkBlock (#11247)
|
||||
* Make InstantSeal Instant again (#11186)
|
||||
* ropsten #6631425 foundation #8798209 (#11201)
|
||||
* Update list of bootnodes for xDai chain (#11236)
|
||||
* ethcore/res: add mordor testnet configuration (#11200)
|
||||
* [chain specs]: activate `Istanbul` on mainnet (#11228)
|
||||
* [builtin]: support `multiple prices and activations` in chain spec (#11039)
|
||||
* Insert explicit warning into the panic hook (#11225)
|
||||
* Snapshot restoration overhaul (#11219)
|
||||
* Fix docker centos build (#11226)
|
||||
* retry on gitlab system failures (#11222)
|
||||
* Update bootnodes. (#11203)
|
||||
* Use provided usd-per-eth value if an endpoint is specified (#11209)
|
||||
* Use a lock instead of atomics for snapshot Progress (#11197)
|
||||
* [informant]: `MillisecondDuration` -> `as_millis()` (#11211)
|
||||
* Step duration map configuration parameter ported from the POA Network fork (#10902)
|
||||
* Upgrade jsonrpc to latest (#11206)
|
||||
* [export hardcoded sync]: use debug for `H256` (#11204)
|
||||
* Pause pruning while snapshotting (#11178)
|
||||
* Type annotation for next_key() matching of json filter options (#11192)
|
||||
* Crypto primitives removed from ethkey (#11174)
|
||||
* Made ecrecover implementation trait public (#11188)
|
||||
* Remove unused macro_use. (#11191)
|
||||
* [dependencies]: jsonrpc `14.0.1` (#11183)
|
||||
* [receipt]: add `sender` & `receiver` to `RichReceipts` (#11179)
|
||||
* [dependencies] bump rand 0.7 (#11022)
|
||||
* [ethcore/builtin]: do not panic in blake2pricer on short input (#11180)
|
||||
* TxPermissions ver 3: gas price & data (#11170)
|
||||
* [ethash] chainspec validate `ecip1017EraRounds` non-zero (#11123)
|
||||
* util Host: fix a double Read Lock bug in fn Host::session_readable() (#11175)
|
||||
* ethcore client: fix a double Read Lock bug in fn Client::logs() (#11172)
|
||||
* Aura: Report malice on sibling blocks from the same validator (#11160)
|
||||
* Change how RPCs eth_call and eth_estimateGas handle "Pending" (#11127)
|
||||
* Cleanup stratum a bit (#11161)
|
||||
* [keccak-hasher]: rust2018 (#11163)
|
||||
* Upgrade to jsonrpc v14 (#11151)
|
||||
* Secret store: fix Instant::now() related race in net_keep_alive (#11155)
|
||||
* RPC method for clearing the engine signer (#10920)
|
||||
* Use TryFrom instead of From+panic for Builtin (#11140)
|
||||
* Fix sccache statistics (#11145)
|
||||
* Update ethereum types to 0.8.0 version (#11139)
|
||||
* [json]: add docs to `hardfork specification` (#11138)
|
||||
* ServiceTransactionChecker::refresh_cache: allow registrar unavailable (#11126)
|
||||
* Fix some random typos, formatting/whitespace (#11128)
|
||||
* Refactor parity_listStorageKeys with count parameter optional (#11124)
|
||||
* Make EIP712Domain Fields Optional (#11103)
|
||||
* EIP-712: bump version in prep for publishing (#11106)
|
||||
* move StateResult to `common-types` (#11121)
|
||||
* Deduplicate registrar contract & calling logic (#11110)
|
||||
* Refactor return type of `BlockChainClient::code` #7098 (#11102)
|
||||
* Switching sccache from local to Redis (#10971)
|
||||
* SIMD Implementation for EIP-152 (#11056)
|
||||
* Fix deprecated trait objects without an explicit `dyn` (#11112)
|
||||
* [spec] fix rinkeby spec (#11108)
|
||||
* Update to latest jsonrpc (#11111)
|
||||
* use images from our registry (#11105)
|
||||
* Correct EIP-712 encoding (#11092)
|
||||
* [CI] check evmbin build (#11096)
|
||||
* Update `kvdb`, `kvdb-rocksdb` and `h2` (#11091)
|
||||
* [client]: Fix for incorrectly dropped consensus messages (#11082) (#11086)
|
||||
* Update JSON tests to d4f86ecf4aa7c (#11054)
|
||||
* fix(network): typo (#11088)
|
||||
* [ethash] remove manual unrolling (#11069)
|
||||
* ethcore/res: activate Istanbul on Ropsten, Görli, Rinkeby, Kovan (#11068)
|
||||
* [sync]: rust 2018 (#11067)
|
||||
* [ethcore]: move client test types to test-helpers (#11062)
|
||||
* [sync]: remove unused dependencies or make dev (#11061)
|
||||
* [ethcore]: reduce re-exports (#11059)
|
||||
* [evmbin] fix time formatting (#11060)
|
||||
* Update hardcoded headers (foundation, classic, kovan, xdai, ewc, ...) (#11053)
|
||||
* cargo update -p eth-secp256k1 (#11052)
|
||||
* ethcore: remove `test-helper feat` from build (#11047)
|
||||
* Include test-helpers from ethjson (#11045)
|
||||
* [ethcore]: cleanup dependencies (#11043)
|
||||
* add more tx tests (#11038)
|
||||
* Fix parallel transactions race-condition (#10995)
|
||||
* [ethcore]: make it compile without `test-helpers` feature (#11036)
|
||||
* Benchmarks for block verification (#11035)
|
||||
* Move snapshot related traits to their proper place (#11012)
|
||||
* cleanup json crate (#11027)
|
||||
* [spec] add istanbul test spec (#11033)
|
||||
* [json-spec] make blake2 pricing spec more readable (#11034)
|
||||
* Add blake2_f precompile (#11017)
|
||||
* Add new line after writing block to hex file. (#10984)
|
||||
* fix: remove unused error-chain (#11028)
|
||||
* fix: remove needless use of itertools (#11029)
|
||||
* Convert `std::test` benchmarks to use Criterion (#10999)
|
||||
* Fix block detail updating (#11015)
|
||||
* [trace] introduce trace failed to Ext (#11019)
|
||||
* cli: update usage and version headers (#10924)
|
||||
* [private-tx] remove unused rand (#11024)
|
||||
* Extract snapshot to own crate (#11010)
|
||||
* Edit publish-onchain.sh to use https (#11016)
|
||||
* EIP 1108: Reduce alt_bn128 precompile gas costs (#11008)
|
||||
* Fix deadlock in `network-devp2p` (#11013)
|
||||
* Implement EIP-1283 reenable transition, EIP-1706 and EIP-2200 (#10191)
|
||||
* EIP 1884 Re-pricing of trie-size dependent operations (#10992)
|
||||
* xDai chain support and nodes list update (#10989)
|
||||
* [trace] check mem diff within range (#11002)
|
||||
* EIP-1344 Add CHAINID op-code (#10983)
|
||||
* Make ClientIoMessage generic over the Client (#10981)
|
||||
* bump spin to 0.5.2 (#10996)
|
||||
* fix compile warnings (#10993)
|
||||
* Fix compilation on recent nightlies (#10991)
|
||||
* [ipfs] Convert to edition 2018 (#10979)
|
||||
* Extract spec to own crate (#10978)
|
||||
* EIP 2028: transaction gas lowered from 68 to 16 (#10987)
|
||||
* Extract engines to own crates (#10966)
|
||||
* Configuration map of block reward contract addresses (#10875)
|
||||
* Add a 2/3 quorum option to Authority Round. (#10909)
|
||||
* Fix rlp decode for inline trie nodes. (#10980)
|
||||
* Private contract migration and offchain state sync (#10748)
|
||||
* manual publish jobs for releases, no changes for nightlies (#10977)
|
||||
* Extract the Engine trait (#10958)
|
||||
* Better error message for rpc gas price errors (#10931)
|
||||
* [.gitlab.yml] cargo check ethcore benches (#10965)
|
||||
* Verify transaction against its block during import (#10954)
|
||||
* [evmbin] fix compilation (#10976)
|
||||
* Update to latest trie version. (#10972)
|
||||
* [blooms-db] Fix benchmarks (#10974)
|
||||
* Fix ethcore/benches build. (#10964)
|
||||
* tx-pool: accept local tx with higher gas price when pool full (#10901)
|
||||
* Disable unsyncable expanse chain (#10926)
|
||||
* Extract Machine from ethcore (#10949)
|
||||
* removed redundant state_root function from spec, improve spec error types (#10955)
|
||||
* Add support for Energy Web Foundation's new chains (#10957)
|
||||
* [evmbin] add more tests to main.rs (#10956)
|
||||
* Fix compiler warnings in util/io and upgrade to edition 2018 Upgrade mio to latest (#10953)
|
||||
* unify loading spec && further spec cleanups (#10948)
|
||||
* refactor: Refactor evmbin CLI (#10742)
|
||||
* journaldb changes (#10929)
|
||||
* Allow default block parameter to be blockHash (#10932)
|
||||
* Enable sealing when engine is ready (#10938)
|
||||
* Fix some warnings and typos. (#10941)
|
||||
* Updated security@parity.io key (#10939)
|
||||
* Change the return type of step_inner function. (#10940)
|
||||
* get rid of hidden mutability of Spec (#10904)
|
||||
* simplify BlockReward::reward implementation (#10906)
|
||||
* Kaspersky AV whitelisting (#10919)
|
||||
* additional arithmetic EVM opcode benchmarks (#10916)
|
||||
* [Cargo.lock] cargo update -p crossbeam-epoch (#10921)
|
||||
* Fixes incorrect comment. (#10913)
|
||||
* Add file path to disk map write/read warnings (#10911)
|
||||
* remove verify_transaction_unordered from engine (#10891)
|
||||
* Avast whitelist script (#10900)
|
||||
* cleanup ethcore ethereum module (#10899)
|
||||
* Move more types out of ethcore (#10880)
|
||||
* return block nonce when engine is clique (#10892)
|
||||
* TransactionQueue::import accepts iterator (#10889)
|
||||
* rename is_pruned to is_prunable (#10888)
|
||||
* simplify create_address_scheme (#10890)
|
||||
* Move DatabaseExtras back to trace (#10868)
|
||||
* Update README.md and Changelogs (#10866)
|
||||
* whisper is no longer a part of parity-ethereum repo (#10855)
|
||||
* [ethash] remove mem::uninitialized (#10861)
|
||||
* Docker images renaming (#10863)
|
||||
* Move the substate module into ethcore/executive (#10867)
|
||||
* Run cargo fix on a few of the worst offenders (#10854)
|
||||
* removed redundant fork choice abstraction (#10849)
|
||||
* Extract state-db from ethcore (#10858)
|
||||
* Fix fork choice (#10837)
|
||||
* Move more code into state-account (#10840)
|
||||
* Remove compiler warning (#10865)
|
||||
* [ethash] use static_assertions crate (#10860)
|
||||
* EIP-1702: Generalized Account Versioning Scheme (#10771)
|
||||
* ethcore-builtin (#10850)
|
||||
* removed QueueError type (#10852)
|
||||
* removed unused macros (#10851)
|
||||
* bump crossbeam (#10848)
|
||||
* removed unused trait PrivateNotify and unused Error types (#10847)
|
||||
* make fn submit_seal more idiomatic (#10843)
|
||||
* update parking-lot to 0.8 (#10845)
|
||||
* Update version to 2.7.0 (#10846)
|
||||
* update jsonrpc to 12.0 (#10841)
|
||||
* Improve logging and cleanup in miner around block sealing (#10745)
|
||||
* Extract AccountDB to account-db (#10839)
|
||||
* test: Update Whisper test for invalid pool size (#10811)
|
||||
* Extricate PodAccount and state Account to own crates (#10838)
|
||||
* logs (#10817)
|
||||
* refactor: whisper: Add type aliases and update rustdocs in message.rs (#10812)
|
||||
* Break circular dependency between Client and Engine (part 1) (#10833)
|
||||
* tests: Relates to #10655: Test instructions for Readme (#10835)
|
||||
* refactor: Related #9459 - evmbin: replace untyped json! macro with fully typed serde serialization using Rust structs (#10657)
|
||||
* idiomatic changes to PodState (#10834)
|
||||
* Allow --nat extip:your.host.here.org (#10830)
|
||||
* When updating the client or when called from RPC, sleep should mean sleep (#10814)
|
||||
* Remove excessive warning (#10831)
|
||||
* Fix typo in README.md (#10828)
|
||||
* ethcore does not use byteorder (#10829)
|
||||
* Better logging when backfilling ancient blocks fail (#10796)
|
||||
* depends: Update wordlist to v1.3 (#10823)
|
||||
* cargo update -p smallvec (#10822)
|
||||
* replace memzero with zeroize crate (#10816)
|
||||
* Don't repeat the logic from Default impl (#10813)
|
||||
* removed additional_params method (#10818)
|
||||
* Add Constantinople eips to the dev (instant_seal) config (#10809)
|
||||
* removed redundant fmt::Display implementations (#10806)
|
||||
* revert changes to .gitlab-ci.yml (#10807)
|
||||
* Add filtering capability to `parity_pendingTransactions` (issue 8269) (#10506)
|
||||
* removed EthEngine alias (#10805)
|
||||
* wait a bit longer in should_check_status_of_request_when_its_resolved (#10808)
|
||||
* Do not drop the peer with None difficulty (#10772)
|
||||
* ethcore-bloom-journal updated to 2018 (#10804)
|
||||
* ethcore-light uses bincode 1.1 (#10798)
|
||||
* Fix a few typos and unused warnings. (#10803)
|
||||
* updated project to ansi_term 0.11 (#10799)
|
||||
* added new ropsten-bootnode and removed old one (#10794)
|
||||
* updated price-info to edition 2018 (#10801)
|
||||
* ethcore-network-devp2p uses igd 0.9 (#10797)
|
||||
* updated parity-local-store to edition 2018 and removed redundant Error type (#10800)
|
||||
* Cleanup unused vm dependencies (#10787)
|
||||
* Removed redundant ethcore-service error type (#10788)
|
||||
* Removed machine abstraction from ethcore (#10791)
|
||||
* Updated blooms-db to rust 2018 and removed redundant deps (#10785)
|
||||
* ethkey no longer uses byteorder (#10786)
|
||||
* Log validator set changes in EpochManager (#10734)
|
||||
* Treat empty account the same as non-exist accounts in EIP-1052 (#10775)
|
||||
* docs: Update Readme with TOC, Contributor Guideline. Update Cargo package descriptions (#10652)
|
||||
* Move Engine::register_client to be before other I/O handler registration (#10767)
|
||||
* Print warnings when using dangerous settings for ValidatorSet (#10733)
|
||||
* ethcore/res: activate atlantis classic hf on block 8772000 (#10766)
|
||||
* refactor: Fix indentation (#10740)
|
||||
* Updated Bn128PairingImpl to use optimized batch pairing (#10765)
|
||||
* fix: aura don't add `SystemTime::now()` (#10720)
|
||||
* Initialize private tx logger only if private tx functionality is enabled (#10758)
|
||||
* Remove unused code (#10762)
|
||||
* Remove calls to heapsize (#10432)
|
||||
* [devp2p] Update to 2018 edition (#10716)
|
||||
* Add a way to signal shutdown to snapshotting threads (#10744)
|
||||
* Enable aesni (#10756)
|
||||
* remove support of old SS db formats (#10757)
|
||||
* [devp2p] Don't use `rust-crypto` (#10714)
|
||||
* updater: fix static id hashes initialization (#10755)
|
||||
* Use fewer threads for snapshotting (#10752)
|
||||
* Die error_chain, die (#10747)
|
||||
* Fix deprectation warnings on nightly (#10746)
|
||||
* fix docker tags for publishing (#10741)
|
||||
* DevP2p: Get node IP address and udp port from Socket, if not included in PING packet (#10705)
|
||||
* ethcore: enable ECIP-1054 for classic (#10731)
|
||||
* Stop breaking out of loop if a non-canonical hash is found (#10729)
|
||||
* Refactor Clique stepping (#10691)
|
||||
* Use RUSTFLAGS to set the optimization level (#10719)
|
||||
* SecretStore: non-blocking wait of session completion (#10303)
|
||||
* removed secret_store folder (#10722)
|
||||
* SecretStore: expose restore_key_public in HTTP API (#10241)
|
||||
* Revert "enable lto for release builds (#10717)" (#10721)
|
||||
* enable lto for release builds (#10717)
|
||||
* Merge `Notifier` and `TransactionsPoolNotifier` (#10591)
|
||||
* [devp2p] Fix warnings and re-org imports (#10710)
|
||||
* Upgrade ethereum types (#10670)
|
||||
* introduce MissingParent Error, fixes #10699 (#10700)
|
||||
* Update publishing (#10644)
|
||||
* Upgrade to parity-crypto 0.4 (#10650)
|
||||
* new image (#10673)
|
||||
* Add SealingState; don't prepare block when not ready. (#10529)
|
||||
* Fix compiler warning (that will become an error) (#10683)
|
||||
* add_sync_notifier in EthPubSubClient holds on to a Client for too long (#10689)
|
||||
* Don't panic if extra_data is longer than VANITY_LENGTH (#10682)
|
||||
* docs: evmbin - Update Rust docs (#10658)
|
||||
* Remove annoying compiler warnings (#10679)
|
||||
* Reset blockchain properly (#10669)
|
||||
* Remove support for hardware wallets (#10678)
|
||||
* [CI] allow cargo audit to fail (#10676)
|
||||
* docs: Add ProgPoW Rust docs to ethash module (#10653)
|
||||
* fix: Move PR template into .github/ folder (#10663)
|
||||
* docs: Add PR template (#10654)
|
||||
* Trivial journal for private transactions (#10056)
|
||||
* fix(compilation warnings) (#10649)
|
||||
* [whisper] Move needed aes_gcm crypto in-crate (#10647)
|
||||
* Adds parity_getRawBlockByNumber, parity_submitRawBlock (#10609)
|
||||
* Fix rinkeby petersburg fork (#10632)
|
||||
* ci: publish docs debug (#10638)
|
||||
* Fix publish docs (#10635)
|
||||
* Update kovan.json to switch validator set to POA Consensus Contracts (#10628)
|
||||
* [ethcore] remove error_chain (#10616)
|
||||
* Remove unused import (#10615)
|
||||
* evm: add some mulmod benches (#10600)
|
||||
* Clique: zero-fill extradata when the supplied value is less than 32 bytes in length (#10605)
|
||||
* Constantinople HF on POA Core (#10606)
|
||||
* adds rpc error message for --no-ancient-blocks (#10608)
|
||||
* Allow CORS requests in Secret Store API (#10584)
|
||||
* update bootnodes (#10595)
|
||||
* sccache logs to stdout (#10596)
|
||||
* fix(whisper expiry): current time + work + ttl (#10587)
|
||||
* CI improvements (#10579)
|
||||
* Watch transactions pool (#10558)
|
||||
* fix(evmbin): make benches compile again (#10586)
|
||||
* fix issue with compilation when 'slow-blocks' feature enabled (#10585)
|
||||
* Reject crazy timestamps instead of truncating. (#10574)
|
||||
* Node table limiting and cache for node filter (#10288)
|
||||
* fix(light cull): poll light cull instead of timer (#10559)
|
||||
* Update Issue Template to direct security issue to email (#10562)
|
||||
* RPC: Implements eth_subscribe("syncing") (#10311)
|
||||
* Explicitly enable or disable Stratum in config file (Issue 9785) (#10521)
|
||||
* version: bump master to 2.6 (#10560)
|
||||
5975
Cargo.lock
generated
5975
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
155
Cargo.toml
155
Cargo.toml
@@ -1,121 +1,40 @@
|
||||
[package]
|
||||
description = "Parity Ethereum client"
|
||||
name = "parity-ethereum"
|
||||
# NOTE Make sure to update util/version/Cargo.toml as well
|
||||
version = "2.7.2"
|
||||
description = "Ethcore client."
|
||||
name = "parity"
|
||||
version = "1.0.2"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
ansi_term = "0.11"
|
||||
atty = "0.2.8"
|
||||
blooms-db = { path = "util/blooms-db" }
|
||||
clap = "2"
|
||||
cli-signer= { path = "cli-signer" }
|
||||
client-traits = { path = "ethcore/client-traits" }
|
||||
common-types = { path = "ethcore/types" }
|
||||
ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" }
|
||||
dir = { path = "util/dir" }
|
||||
docopt = "1.0"
|
||||
engine = { path = "ethcore/engine" }
|
||||
ethabi = { version = "9.0.1", optional = true }
|
||||
ethcore = { path = "ethcore", features = ["parity"] }
|
||||
ethcore-accounts = { path = "accounts", optional = true }
|
||||
ethcore-blockchain = { path = "ethcore/blockchain" }
|
||||
ethcore-call-contract = { path = "ethcore/call-contract", optional = true }
|
||||
ethcore-db = { path = "ethcore/db" }
|
||||
ethcore-io = { path = "util/io" }
|
||||
ethcore-light = { path = "ethcore/light" }
|
||||
ethcore-logger = { path = "parity/logger" }
|
||||
ethcore-miner = { path = "miner" }
|
||||
ethcore-network = { path = "util/network" }
|
||||
ethcore-private-tx = { path = "ethcore/private-tx" }
|
||||
ethcore-secretstore = { path = "secret-store", optional = true }
|
||||
ethcore-service = { path = "ethcore/service" }
|
||||
ethcore-sync = { path = "ethcore/sync" }
|
||||
ethereum-types = "0.8.0"
|
||||
ethkey = { path = "accounts/ethkey" }
|
||||
ethstore = { path = "accounts/ethstore" }
|
||||
fdlimit = "0.1"
|
||||
futures = "0.1"
|
||||
journaldb = { path = "util/journaldb" }
|
||||
jsonrpc-core = "14.0.3"
|
||||
keccak-hash = "0.4.0"
|
||||
kvdb = "0.3.1"
|
||||
kvdb-rocksdb = "0.4.1"
|
||||
log = "0.4"
|
||||
migration-rocksdb = { path = "util/migration-rocksdb" }
|
||||
node-filter = { path = "ethcore/node-filter" }
|
||||
num_cpus = "1.2"
|
||||
number_prefix = "0.2"
|
||||
panic_hook = { path = "util/panic-hook" }
|
||||
parity-bytes = "0.1"
|
||||
parity-crypto = { version = "0.4.2", features = ["publickey"] }
|
||||
parity-daemonize = "0.3"
|
||||
parity-hash-fetch = { path = "updater/hash-fetch" }
|
||||
parity-ipfs-api = { path = "ipfs" }
|
||||
parity-local-store = { path = "miner/local-store" }
|
||||
parity-path = "0.1"
|
||||
parity-rpc = { path = "rpc" }
|
||||
parity-runtime = { path = "util/runtime" }
|
||||
parity-updater = { path = "updater" }
|
||||
parity-util-mem = { version = "0.3.0", features = ["jemalloc-global"] }
|
||||
parity-version = { path = "util/version" }
|
||||
parking_lot = "0.9"
|
||||
regex = "1.0"
|
||||
registrar = { path = "util/registrar" }
|
||||
rlp = "0.4.0"
|
||||
rpassword = "1.0"
|
||||
rustc-hex = "1.0"
|
||||
semver = "0.9"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
snapshot = { path = "ethcore/snapshot" }
|
||||
spec = { path = "ethcore/spec" }
|
||||
term_size = "0.3"
|
||||
textwrap = "0.9"
|
||||
toml = "0.4"
|
||||
verification = { path = "ethcore/verification" }
|
||||
authors = ["Ethcore <admin@ethcore.io>"]
|
||||
build = "build.rs"
|
||||
|
||||
[build-dependencies]
|
||||
rustc_version = "0.2"
|
||||
rustc_version = "0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "0.1"
|
||||
ipnetwork = "0.12.6"
|
||||
tempdir = "0.3"
|
||||
fake-fetch = { path = "util/fake-fetch" }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3.4", features = ["winsock2", "winuser", "shellapi"] }
|
||||
[dependencies]
|
||||
log = "0.3"
|
||||
env_logger = "0.3"
|
||||
rustc-serialize = "0.3"
|
||||
docopt = "0.6"
|
||||
time = "0.1"
|
||||
ctrlc = { git = "https://github.com/tomusdrw/rust-ctrlc.git" }
|
||||
fdlimit = { path = "util/fdlimit" }
|
||||
daemonize = "0.2"
|
||||
num_cpus = "0.2"
|
||||
number_prefix = "0.2"
|
||||
rpassword = "0.1"
|
||||
clippy = { version = "0.0.54", optional = true }
|
||||
ethcore = { path = "ethcore" }
|
||||
ethcore-util = { path = "util" }
|
||||
ethsync = { path = "sync" }
|
||||
ethminer = { path = "miner" }
|
||||
ethcore-devtools = { path = "devtools" }
|
||||
ethcore-rpc = { path = "rpc", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["accounts"]
|
||||
accounts = ["ethcore-accounts", "parity-rpc/accounts"]
|
||||
miner-debug = ["ethcore/miner-debug"]
|
||||
json-tests = ["ethcore/json-tests"]
|
||||
test-heavy = ["ethcore/test-heavy"]
|
||||
evm-debug = ["ethcore/evm-debug"]
|
||||
evm-debug-tests = ["ethcore/evm-debug-tests"]
|
||||
slow-blocks = ["ethcore/slow-blocks"]
|
||||
secretstore = ["ethcore-secretstore", "accounts", "ethabi", "ethcore-call-contract"]
|
||||
final = ["parity-version/final"]
|
||||
deadlock_detection = ["parking_lot/deadlock_detection"]
|
||||
# to create a memory profile (requires nightly rust), use e.g.
|
||||
# `heaptrack /path/to/parity <parity params>`,
|
||||
# to visualize a memory profile, use `heaptrack_gui`
|
||||
# or
|
||||
# `valgrind --tool=massif /path/to/parity <parity params>`
|
||||
# and `massif-visualizer` for visualization
|
||||
memory_profiling = []
|
||||
# hardcode version number 1.3.7 of parity to force an update
|
||||
# in order to manually test that parity fall-over to the local version
|
||||
# in case of invalid or deprecated command line arguments are entered
|
||||
test-updater = ["parity-updater/test-updater"]
|
||||
|
||||
[lib]
|
||||
path = "parity/lib.rs"
|
||||
default = ["rpc"]
|
||||
rpc = ["ethcore-rpc"]
|
||||
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "ethminer/dev"]
|
||||
travis-beta = ["ethcore/json-tests"]
|
||||
travis-nightly = ["ethcore/json-tests", "dev"]
|
||||
|
||||
[[bin]]
|
||||
path = "parity/main.rs"
|
||||
@@ -123,16 +42,4 @@ name = "parity"
|
||||
|
||||
[profile.release]
|
||||
debug = false
|
||||
lto = true
|
||||
|
||||
[workspace]
|
||||
# This should only list projects that are not
|
||||
# in the dependency tree in any other way
|
||||
# (i.e. pretty much only standalone CLI tools)
|
||||
members = [
|
||||
"accounts/ethkey/cli",
|
||||
"accounts/ethstore/cli",
|
||||
"chainspec",
|
||||
"ethcore/wasm/run",
|
||||
"evmbin",
|
||||
]
|
||||
lto = false
|
||||
|
||||
399
README.md
399
README.md
@@ -1,387 +1,40 @@
|
||||

|
||||
# ethcore
|
||||
|
||||
<h2 align="center">The Fastest and most Advanced Ethereum Client.</h2>
|
||||
[![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url] [![Join the chat at https://gitter.im/trogdoro/xiki][gitter-image]][gitter-url] [![GPLv3][license-image]][license-url]
|
||||
|
||||
<p align="center"><strong><a href="https://github.com/paritytech/parity-ethereum/releases/latest">» Download the latest release «</a></strong></p>
|
||||
[travis-image]: https://travis-ci.org/ethcore/parity.svg?branch=master
|
||||
[travis-url]: https://travis-ci.org/ethcore/parity
|
||||
[coveralls-image]: https://coveralls.io/repos/github/ethcore/parity/badge.svg?branch=master
|
||||
[coveralls-url]: https://coveralls.io/github/ethcore/parity?branch=master
|
||||
[gitter-image]: https://badges.gitter.im/Join%20Chat.svg
|
||||
[gitter-url]: https://gitter.im/ethcore/parity?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
||||
[license-image]: https://img.shields.io/badge/license-GPL%20v3-green.svg
|
||||
[license-url]: http://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
<p align="center"><a href="https://gitlab.parity.io/parity/parity-ethereum/commits/master" target="_blank"><img src="https://gitlab.parity.io/parity/parity-ethereum/badges/master/build.svg" /></a>
|
||||
<a href="https://www.gnu.org/licenses/gpl-3.0.en.html" target="_blank"><img src="https://img.shields.io/badge/license-GPL%20v3-green.svg" /></a></p>
|
||||
[Documentation](http://ethcore.github.io/parity/ethcore/index.html)
|
||||
|
||||
## Table of Contents
|
||||
### Building from source
|
||||
|
||||
1. [Description](#chapter-001)
|
||||
2. [Technical Overview](#chapter-002)
|
||||
3. [Building](#chapter-003)<br>
|
||||
3.1 [Building Dependencies](#chapter-0031)<br>
|
||||
3.2 [Building from Source Code](#chapter-0032)<br>
|
||||
3.3 [Simple One-Line Installer for Mac and Linux](#chapter-0033)<br>
|
||||
3.4 [Starting Parity Ethereum](#chapter-0034)
|
||||
4. [Testing](#chapter-004)
|
||||
5. [Documentation](#chapter-005)
|
||||
6. [Toolchain](#chapter-006)
|
||||
7. [Community](#chapter-007)
|
||||
8. [Contributing](#chapter-008)
|
||||
9. [License](#chapter-009)
|
||||
|
||||
|
||||
## 1. Description <a id="chapter-001"></a>
|
||||
|
||||
**Built for mission-critical use**: Miners, service providers, and exchanges need fast synchronisation and maximum uptime. Parity Ethereum provides the core infrastructure essential for speedy and reliable services.
|
||||
|
||||
- Clean, modular codebase for easy customisation
|
||||
- Advanced CLI-based client
|
||||
- Minimal memory and storage footprint
|
||||
- Synchronise in hours, not days with Warp Sync
|
||||
- Modular for light integration into your service or product
|
||||
|
||||
## 2. Technical Overview <a id="chapter-002"></a>
|
||||
|
||||
Parity Ethereum's goal is to be the fastest, lightest, and most secure Ethereum client. We are developing Parity Ethereum using the sophisticated and cutting-edge **Rust programming language**. Parity Ethereum is licensed under the GPLv3 and can be used for all your Ethereum needs.
|
||||
|
||||
By default, Parity Ethereum runs a JSON-RPC HTTP server on port `:8545` and a Web-Sockets server on port `:8546`. This is fully configurable and supports a number of APIs.
|
||||
|
||||
If you run into problems while using Parity Ethereum, check out the [wiki for documentation](https://wiki.parity.io/), feel free to [file an issue in this repository](https://github.com/paritytech/parity-ethereum/issues/new), or hop on our [Gitter](https://gitter.im/paritytech/parity) or [Riot](https://riot.im/app/#/group/+parity:matrix.parity.io) chat room to ask a question. We are glad to help! **For security-critical issues**, please refer to the security policy outlined in [SECURITY.md](SECURITY.md).
|
||||
|
||||
Parity Ethereum's current beta-release is 2.6. You can download it at [the releases page](https://github.com/paritytech/parity-ethereum/releases) or follow the instructions below to build from source. Please, mind the [CHANGELOG.md](CHANGELOG.md) for a list of all changes between different versions.
|
||||
|
||||
## 3. Building <a id="chapter-003"></a>
|
||||
|
||||
### 3.1 Build Dependencies <a id="chapter-0031"></a>
|
||||
|
||||
Parity Ethereum requires **latest stable Rust version** to build.
|
||||
|
||||
We recommend installing Rust through [rustup](https://www.rustup.rs/). If you don't already have `rustup`, you can install it like this:
|
||||
First (if you don't already have it) get multirust:
|
||||
|
||||
- Linux:
|
||||
```bash
|
||||
$ curl https://sh.rustup.rs -sSf | sh
|
||||
```
|
||||
```bash
|
||||
curl -sf https://raw.githubusercontent.com/brson/multirust/master/quick-install.sh | sudo sh -s -- --yes
|
||||
```
|
||||
|
||||
Parity Ethereum also requires `gcc`, `g++`, `pkg-config`, `file`, `make`, and `cmake` packages to be installed.
|
||||
- OSX with Homebrew:
|
||||
```bash
|
||||
brew update && brew install multirust
|
||||
multirust default stable
|
||||
```
|
||||
|
||||
- OSX:
|
||||
```bash
|
||||
$ curl https://sh.rustup.rs -sSf | sh
|
||||
```
|
||||
|
||||
`clang` is required. It comes with Xcode command line tools or can be installed with homebrew.
|
||||
|
||||
- Windows:
|
||||
Make sure you have Visual Studio 2015 with C++ support installed. Next, download and run the `rustup` installer from
|
||||
https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe, start "VS2015 x64 Native Tools Command Prompt", and use the following command to install and set up the `msvc` toolchain:
|
||||
```bash
|
||||
$ rustup default stable-x86_64-pc-windows-msvc
|
||||
```
|
||||
|
||||
Once you have `rustup` installed, then you need to install:
|
||||
* [Perl](https://www.perl.org)
|
||||
* [Yasm](https://yasm.tortall.net)
|
||||
|
||||
Make sure that these binaries are in your `PATH`. After that, you should be able to build Parity Ethereum from source.
|
||||
|
||||
### 3.2 Build from Source Code <a id="chapter-0032"></a>
|
||||
Then, download and build Parity:
|
||||
|
||||
```bash
|
||||
# download Parity Ethereum code
|
||||
$ git clone https://github.com/paritytech/parity-ethereum
|
||||
$ cd parity-ethereum
|
||||
# download Parity code
|
||||
git clone https://github.com/ethcore/parity
|
||||
cd parity
|
||||
|
||||
# build in release mode
|
||||
$ cargo build --release --features final
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
This produces an executable in the `./target/release` subdirectory.
|
||||
|
||||
Note: if cargo fails to parse manifest try:
|
||||
|
||||
```bash
|
||||
$ ~/.cargo/bin/cargo build --release
|
||||
```
|
||||
|
||||
Note, when compiling a crate and you receive errors, it's in most cases your outdated version of Rust, or some of your crates have to be recompiled. Cleaning the repository will most likely solve the issue if you are on the latest stable version of Rust, try:
|
||||
|
||||
```bash
|
||||
$ cargo clean
|
||||
```
|
||||
|
||||
This always compiles the latest nightly builds. If you want to build stable or beta, do a
|
||||
|
||||
```bash
|
||||
$ git checkout stable
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```bash
|
||||
$ git checkout beta
|
||||
```
|
||||
|
||||
### 3.3 Simple One-Line Installer for Mac and Linux <a id="chapter-0033"></a>
|
||||
|
||||
```bash
|
||||
bash <(curl https://get.parity.io -L)
|
||||
```
|
||||
|
||||
The one-line installer always defaults to the latest beta release. To install a stable release, run:
|
||||
|
||||
```bash
|
||||
bash <(curl https://get.parity.io -L) -r stable
|
||||
```
|
||||
|
||||
### 3.4 Starting Parity Ethereum <a id="chapter-0034"></a>
|
||||
|
||||
#### Manually
|
||||
|
||||
To start Parity Ethereum manually, just run
|
||||
|
||||
```bash
|
||||
$ ./target/release/parity
|
||||
```
|
||||
|
||||
so Parity Ethereum begins syncing the Ethereum blockchain.
|
||||
|
||||
#### Using `systemd` service file
|
||||
|
||||
To start Parity Ethereum as a regular user using `systemd` init:
|
||||
|
||||
1. Copy `./scripts/parity.service` to your
|
||||
`systemd` user directory (usually `~/.config/systemd/user`).
|
||||
2. Copy release to bin folder, write `sudo install ./target/release/parity /usr/bin/parity`
|
||||
3. To configure Parity Ethereum, write a `/etc/parity/config.toml` config file, see [Configuring Parity Ethereum](https://paritytech.github.io/wiki/Configuring-Parity) for details.
|
||||
|
||||
## 4. Testing <a id="chapter-004"></a>
|
||||
|
||||
Download the required test files: `git submodule update --init --recursive`. You can run tests with the following commands:
|
||||
|
||||
* **All** packages
|
||||
```
|
||||
cargo test --all
|
||||
```
|
||||
|
||||
* Specific package
|
||||
```
|
||||
cargo test --package <spec>
|
||||
```
|
||||
|
||||
Replace `<spec>` with one of the packages from the [package list](#package-list) (e.g. `cargo test --package evmbin`).
|
||||
|
||||
You can show your logs in the test output by passing `--nocapture` (i.e. `cargo test --package evmbin -- --nocapture`)
|
||||
|
||||
## 5. Documentation <a id="chapter-005"></a>
|
||||
|
||||
Official website: https://parity.io
|
||||
|
||||
Be sure to [check out our wiki](https://wiki.parity.io) for more information.
|
||||
|
||||
### Viewing documentation for Parity Ethereum packages
|
||||
|
||||
You can generate documentation for Parity Ethereum Rust packages that automatically opens in your web browser using [rustdoc with Cargo](https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html#using-rustdoc-with-cargo) (of the The Rustdoc Book), by running the the following commands:
|
||||
|
||||
* **All** packages
|
||||
```
|
||||
cargo doc --document-private-items --open
|
||||
```
|
||||
|
||||
* Specific package
|
||||
```
|
||||
cargo doc --package <spec> -- --document-private-items --open
|
||||
```
|
||||
|
||||
Use`--document-private-items` to also view private documentation and `--no-deps` to exclude building documentation for dependencies.
|
||||
|
||||
Replacing `<spec>` with one of the following from the details section below (i.e. `cargo doc --package parity-ethereum --open`):
|
||||
|
||||
<a id="package-list"></a>
|
||||
**Package List**
|
||||
<details><p>
|
||||
|
||||
* Parity Ethereum (EthCore) Client Application
|
||||
```bash
|
||||
parity-ethereum
|
||||
```
|
||||
* Parity Ethereum Account Management, Key Management Tool, and Keys Generator
|
||||
```bash
|
||||
ethcore-accounts, ethkey-cli, ethstore, ethstore-cli
|
||||
```
|
||||
* Parity Chain Specification
|
||||
```bash
|
||||
chainspec
|
||||
```
|
||||
* Parity CLI Signer Tool & RPC Client
|
||||
```bash
|
||||
cli-signer parity-rpc-client
|
||||
```
|
||||
* Parity Ethereum Ethash & ProgPoW Implementations
|
||||
```bash
|
||||
ethash
|
||||
```
|
||||
* Parity (EthCore) Library
|
||||
```bash
|
||||
ethcore
|
||||
```
|
||||
* Parity Ethereum Blockchain Database, Test Generator, Configuration,
|
||||
Caching, Importing Blocks, and Block Information
|
||||
```bash
|
||||
ethcore-blockchain
|
||||
```
|
||||
* Parity Ethereum (EthCore) Contract Calls and Blockchain Service & Registry Information
|
||||
```bash
|
||||
ethcore-call-contract
|
||||
```
|
||||
* Parity Ethereum (EthCore) Database Access & Utilities, Database Cache Manager
|
||||
```bash
|
||||
ethcore-db
|
||||
```
|
||||
* Parity Ethereum Virtual Machine (EVM) Rust Implementation
|
||||
```bash
|
||||
evm
|
||||
```
|
||||
* Parity Ethereum (EthCore) Light Client Implementation
|
||||
```bash
|
||||
ethcore-light
|
||||
```
|
||||
* Parity Smart Contract based Node Filter, Manage Permissions of Network Connections
|
||||
```bash
|
||||
node-filter
|
||||
```
|
||||
* Parity Private Transactions
|
||||
```bash
|
||||
ethcore-private-tx
|
||||
```
|
||||
* Parity Ethereum (EthCore) Client & Network Service Creation & Registration with the I/O Subsystem
|
||||
```bash
|
||||
ethcore-service
|
||||
```
|
||||
* Parity Ethereum (EthCore) Blockchain Synchronization
|
||||
```bash
|
||||
ethcore-sync
|
||||
```
|
||||
* Parity Ethereum Common Types
|
||||
```bash
|
||||
common-types
|
||||
```
|
||||
* Parity Ethereum Virtual Machines (VM) Support Library
|
||||
```bash
|
||||
vm
|
||||
```
|
||||
* Parity Ethereum WASM Interpreter
|
||||
```bash
|
||||
wasm
|
||||
```
|
||||
* Parity Ethereum WASM Test Runner
|
||||
```bash
|
||||
pwasm-run-test
|
||||
```
|
||||
* Parity EVM Implementation
|
||||
```bash
|
||||
evmbin
|
||||
```
|
||||
* Parity Ethereum IPFS-compatible API
|
||||
```bash
|
||||
parity-ipfs-api
|
||||
```
|
||||
* Parity Ethereum JSON Deserialization
|
||||
```bash
|
||||
ethjson
|
||||
```
|
||||
* Parity Ethereum State Machine Generalization for Consensus Engines
|
||||
```bash
|
||||
parity-machine
|
||||
```
|
||||
* Parity Ethereum (EthCore) Miner Interface
|
||||
```bash
|
||||
ethcore-miner parity-local-store price-info ethcore-stratum using_queue
|
||||
```
|
||||
* Parity Ethereum (EthCore) Logger Implementation
|
||||
```bash
|
||||
ethcore-logger
|
||||
```
|
||||
* C bindings library for the Parity Ethereum client
|
||||
```bash
|
||||
parity-clib
|
||||
```
|
||||
* Parity Ethereum JSON-RPC Servers
|
||||
```bash
|
||||
parity-rpc
|
||||
```
|
||||
* Parity Ethereum (EthCore) Secret Store
|
||||
```bash
|
||||
ethcore-secretstore
|
||||
```
|
||||
* Parity Updater Service
|
||||
```bash
|
||||
parity-updater parity-hash-fetch
|
||||
```
|
||||
* Parity Core Libraries (Parity Util)
|
||||
```bash
|
||||
ethcore-bloom-journal blooms-db dir eip-712 fake-fetch fastmap fetch ethcore-io
|
||||
journaldb keccak-hasher len-caching-lock macros memory-cache memzero
|
||||
migration-rocksdb ethcore-network ethcore-network-devp2p panic_hook
|
||||
patricia-trie-ethereum registrar rlp_compress rlp_derive parity-runtime stats
|
||||
time-utils triehash-ethereum unexpected parity-version
|
||||
```
|
||||
|
||||
</p></details>
|
||||
|
||||
### Contributing to documentation for Parity Ethereum packages
|
||||
|
||||
[Document source code](https://doc.rust-lang.org/1.9.0/book/documentation.html) for Parity Ethereum packages by annotating the source code with documentation comments.
|
||||
|
||||
Example (generic documentation comment):
|
||||
```markdown
|
||||
/// Summary
|
||||
///
|
||||
/// Description
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Summary of Example 1
|
||||
///
|
||||
/// ```rust
|
||||
/// // insert example 1 code here for use with documentation as tests
|
||||
/// ```
|
||||
///
|
||||
```
|
||||
|
||||
## 6. Toolchain <a id="chapter-006"></a>
|
||||
|
||||
In addition to the Parity Ethereum client, there are additional tools in this repository available:
|
||||
|
||||
- [evmbin](./evmbin) - Parity Ethereum EVM Implementation.
|
||||
- [ethstore](./accounts/ethstore) - Parity Ethereum Key Management.
|
||||
- [ethkey](./accounts/ethkey) - Parity Ethereum Keys Generator.
|
||||
|
||||
The following tool is available in a separate repository:
|
||||
- [ethabi](https://github.com/paritytech/ethabi) - Parity Ethereum Encoding of Function Calls. [Docs here](https://crates.io/crates/ethabi)
|
||||
- [whisper](https://github.com/paritytech/whisper) - Parity Ethereum Whisper-v2 PoC Implementation.
|
||||
|
||||
## 7. Community <a id="chapter-007"></a>
|
||||
|
||||
### Join the chat!
|
||||
|
||||
Questions? Get in touch with us on Gitter:
|
||||
[](https://gitter.im/paritytech/parity)
|
||||
[](https://gitter.im/paritytech/parity.js)
|
||||
[](https://gitter.im/paritytech/parity/miners)
|
||||
[](https://gitter.im/paritytech/parity-poa)
|
||||
|
||||
Alternatively, join our community on Matrix:
|
||||
[](https://riot.im/app/#/group/+parity:matrix.parity.io)
|
||||
|
||||
## 8. Contributing <a id="chapter-008"></a>
|
||||
|
||||
An introduction has been provided in the ["So You Want to be a Core Developer" presentation slides by Hernando Castano](http://tiny.cc/contrib-to-parity-eth). Additional guidelines are provided in [CONTRIBUTING](./.github/CONTRIBUTING.md).
|
||||
|
||||
### Contributor Code of Conduct
|
||||
|
||||
[CODE_OF_CONDUCT](./.github/CODE_OF_CONDUCT.md)
|
||||
|
||||
## 9. License <a id="chapter-009"></a>
|
||||
|
||||
[LICENSE](./LICENSE)
|
||||
|
||||
101
SECURITY.md
101
SECURITY.md
@@ -1,101 +0,0 @@
|
||||
# Security Policy
|
||||
|
||||
Parity Technologies is committed to resolving security vulnerabilities in our software quickly and carefully. We take the necessary steps to minimize risk, provide timely information, and deliver vulnerability fixes and mitigations required to address security issues.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Security vulnerabilities in Parity software should be reported by email to security@parity.io. If you think your report might be eligible for the Parity Bug Bounty Program, your email should be send to bugbounty@parity.io.
|
||||
|
||||
Your report should include the following:
|
||||
|
||||
- your name
|
||||
- description of the vulnerability
|
||||
- attack scenario (if any)
|
||||
- components
|
||||
- reproduction
|
||||
- other details
|
||||
|
||||
Try to include as much information in your report as you can, including a description of the vulnerability, its potential impact, and steps for reproducing it. Be sure to use a descriptive subject line.
|
||||
|
||||
You'll receive a response to your email within two business days indicating the next steps in handling your report. We encourage finders to use encrypted communication channels to protect the confidentiality of vulnerability reports. You can encrypt your report using our public key. This key is [on MIT's key server](https://pgp.mit.edu/pks/lookup?op=get&search=0x5D0F03018D07DE73) server and reproduced below.
|
||||
|
||||
After the initial reply to your report, our team will endeavor to keep you informed of the progress being made towards a fix. These updates will be sent at least every five business days.
|
||||
|
||||
Thank you for taking the time to responsibly disclose any vulnerabilities you find.
|
||||
|
||||
## Responsible Investigation and Reporting
|
||||
|
||||
Responsible investigation and reporting includes, but isn't limited to, the following:
|
||||
|
||||
- Don't violate the privacy of other users, destroy data, etc.
|
||||
- Don’t defraud or harm Parity Technologies Ltd or its users during your research; you should make a good faith effort to not interrupt or degrade our services.
|
||||
- Don't target our physical security measures, or attempt to use social engineering, spam, distributed denial of service (DDOS) attacks, etc.
|
||||
- Initially report the bug only to us and not to anyone else.
|
||||
- Give us a reasonable amount of time to fix the bug before disclosing it to anyone else, and give us adequate written warning before disclosing it to anyone else.
|
||||
- In general, please investigate and report bugs in a way that makes a reasonable, good faith effort not to be disruptive or harmful to us or our users. Otherwise your actions might be interpreted as an attack rather than an effort to be helpful.
|
||||
|
||||
## Bug Bounty Program
|
||||
|
||||
Our Bug Bounty Program allows us to recognise and reward members of the Parity community for helping us find and address significant bugs, in accordance with the terms of the Parity Bug Bounty Program. A detailed description on eligibility, rewards, legal information and terms & conditions for contributors can be found on [our website](https://paritytech.io/bug-bounty.html).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Plaintext PGP Key
|
||||
|
||||
```
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQINBF0vHwQBEADKui4qAo4bzdzRhMm+uhUpYGf8jjjmET3zJ8kKQIpp6JTsV+HJ
|
||||
6m1We0QYeMRXoOYH1xVHBf2zNCuHS0nSQdUCQA7SHWsPB05STa2hvlR7fSdQnCCp
|
||||
gnLOJWXvvedlRDIAhvqI6cwLdUlXgVSKEwrwmrpiBhh4NxI3qX+LyIa+Ovkchu2S
|
||||
d/YCnE4GqojSGRfJYiGwe2N+sF7OfaoKhQuTrtdDExHrMU4cWnTXW2wyxTr4xkj9
|
||||
jS2WeLVZWflvkDHT8JD9N6jNxBVEF/Qvjk83zI0kCOzkhek8x+YUgfLq3/rHOYbX
|
||||
3pW21ccHYPacHjHWvKE+xRebjeEhJ4KxKHfCVjQcxybwDBqDka1AniZt4CQ7UORf
|
||||
MU/ue2oSZ9nNg0uMdb/0AbQPZ04OlMcYPAPWzFL08nVPox9wT9uqlL6JtcOeC90h
|
||||
oOeDmfgwmjMmdwWTRgt9qQjcbgXzVvuAzIGbzj1X3MdLspWdHs/d2+US4nji1TkN
|
||||
oYIW7vE+xkd3aB+NZunIlm9Rwd/0mSgDg+DaNa5KceOLhq0/qKgcXC/RRU29I8II
|
||||
tusRoR/oesGJGYTjh4k6PJkG+nvDPsoQrwYT44bhnniS1xYkxWYXF99JFI7LgMdD
|
||||
e1SgKeIDVpvm873k82E6arp5655Wod1XOjaXBggCwFp84eKcEZEN+1qEWwARAQAB
|
||||
tClQYXJpdHkgU2VjdXJpdHkgVGVhbSA8c2VjdXJpdHlAcGFyaXR5LmlvPokCVAQT
|
||||
AQoAPhYhBJ1LK264+XFW0ZZpqf8IEtSRuWeYBQJdLx8EAhsDBQkDwmcABQsJCAcC
|
||||
BhUKCQgLAgQWAgMBAh4BAheAAAoJEP8IEtSRuWeYL84QAI6NwnwS561DWYYRAd4y
|
||||
ocGPr3CnwFSt1GjkSkRy3B+tMhzexBg1y7EbLRUefIrO4LwOlywtRk8tTRGgEI4i
|
||||
5xRLHbOkeolfgCFSpOj5d8cMKCt5HEIv18hsv6dkrzlSYA5NLX/GRBEh3F/0sGny
|
||||
vCXapfxa1cx72sU7631JBK7t2Tf+MfwxdfyFZ9TI9WdtP5AfVjgTkIVkEDFcZPTc
|
||||
n3CYXqTYFIBCNUD8LP4iTi3xUt7pTGJQQoFT8l15nJCgzRYQ+tXpoTRlf+/LtXmw
|
||||
6iidPV87E06jHdK9666rBouIabAtx7i0/4kwo+bSZ8DiSKRUaehiHGd212HSEmdF
|
||||
jxquWE4pEzoUowYznhSIfR+WWIqRBHxEYarP4m98Hi+VXZ7Fw1ytzO8+BAKnLXnj
|
||||
2W2+T9qJks5gqVEoaWNnqpvya6JA11QZvZ0w7Om2carDc2ILNm2Xx9J0mRUye8P0
|
||||
KxcgqJuKNGFtugebQAsXagkxOKsdKna1PlDlxEfTf6AgI3ST8qSiMAwaaIMB/REF
|
||||
VKUapGoslQX4tOCjibI2pzEgE//D8NAaSVu2A9+BUcFERdZRxsI7fydIXNeZ2R46
|
||||
N2qfW+DP3YR/14QgdRxDItEavUoE1vByRXwIufKAkVemOZzIoFXKFsDeXwqTVW5i
|
||||
6CXu6OddZ3QHDiT9TEbRny4QuQINBF0vKCwBEACnP5J7LEGbpxNBrPvGdxZUo0YA
|
||||
U8RgeKDRPxJTvMo27V1IPZGaKRCRq8LBfg/eHhqZhQ7SLJBjBljd8kuT5dHDBTRe
|
||||
jE1UIOhmnlSlrEJjAmpVO08irlGpq1o+8mGcvkBsR0poCVjeNeSnwYfRnR+c3GK5
|
||||
Er6/JRqfN4mJvnEC9/Pbm6C7ql6YLKxC3yqzF97JL5brbbuozrW7nixY/yAI8619
|
||||
VlBIMP7PAUbGcnSQyuV5b/Wr2Sgr6NJclnNSLjh2U9/Du6w/0tDGlMBts8HjRnWJ
|
||||
BXbkTdQKCTaqgK68kTKSiN1/x+lynxHC2AavMpH/08Kopg2ZCzJowMKIgcB+4Z/I
|
||||
DJKZWHWKumhaZMGXcWgzgcByog9IpamuROEZFJNEUAFf7YIncEckPSif4looiOdS
|
||||
VurKZGvYXXaGSsZbGgHxI5CWu7ZxMdLBLvtOcCYmRQrG+g/h+PGU5BT0bNAfNTkm
|
||||
V3/n1B/TWbpWRmB3AwT2emQivXHkaubGI0VivhaO43AuI9JWoqiMqFtxbuTeoxwD
|
||||
xlu2Dzcp0v+AR4T5cIG9D5/+yiPc25aIY7cIKxuNFHIDL4td5fwSGC7vU6998PIG
|
||||
2Y48TGBnw7zpEfDfMayqAeBjX0YU6PTNsvS5O6bP3j4ojTOUYD7Z8QdCvgISDID3
|
||||
WMGAdmSwmCRvsQ/OJwARAQABiQI8BBgBCgAmFiEEnUsrbrj5cVbRlmmp/wgS1JG5
|
||||
Z5gFAl0vKCwCGwwFCQB2pwAACgkQ/wgS1JG5Z5hdbw//ZqR+JcWm59NUIHjauETJ
|
||||
sYDYhcAfa3txTacRn5uPz/TQiTd7wZ82+G8Et0ZnpEHy6eWyBqHpG0hiPhFBzxjY
|
||||
nhjHl8jJeyo2mQIVJhzkL58BHBZk8WM2TlaU7VxZ6TYOmP2y3qf6FD6mCcrQ4Fml
|
||||
E9f0lyVUoI/5Zs9oF0izRk8vkwaY3UvLM7XEY6nM8GnFG8kaiZMYmx26Zo7Uz31G
|
||||
7EGGZFsrVDXfNhSJyz79Gyn+Lx9jOTdoR0sH/THYIIosE83awMGE6jKeuDYTbVWu
|
||||
+ZtHQef+pRteki3wvNLJK+kC1y3BtHqDJS9Lqx0s8SCiVozlC+fZfC9hCtU7bXJK
|
||||
0UJZ4qjSvj6whzfaNgOZAqJpmwgOnd8W/3YJk1DwUeX98FcU38MR23SOkx2EDdDE
|
||||
77Kdu62vTs/tLmOTuyKBvYPaHaYulYjQTxurG+o8vhHtaL87ARvuq+83dj+nO5z3
|
||||
5O9vkcVJYWjOEnJe7ZvCTxeLJehpCmHIbyUuDx5P24MWVbyXOxIlxNxTqlub5GlW
|
||||
rQF6Qsa/0k9TRk7Htbct6fAA0/VahJS0g096MrTH8AxBXDNE8lIoNeGikVlaxK9Z
|
||||
S+aannlWYIJymZ4FygIPPaRlzhAoXBuJd8OaR5giC7dS1xquxKOiQEXTGsLeGFaI
|
||||
BZYiIhW7GG4ozvKDqyNm4eg=
|
||||
=yKcB
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
```
|
||||
@@ -1,22 +0,0 @@
|
||||
[package]
|
||||
description = "Parity Ethereum Account Management"
|
||||
homepage = "http://parity.io"
|
||||
license = "GPL-3.0"
|
||||
name = "ethcore-accounts"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
ethkey = { path = "ethkey" }
|
||||
ethstore = { path = "ethstore" }
|
||||
log = "0.4"
|
||||
parity-crypto = { version = "0.4.2", features = ["publickey"] }
|
||||
parking_lot = "0.9"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
ethereum-types = "0.8.0"
|
||||
tempdir = "0.3"
|
||||
2
accounts/ethkey/.gitignore
vendored
2
accounts/ethkey/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
target
|
||||
*.swp
|
||||
@@ -1,23 +0,0 @@
|
||||
sudo: false
|
||||
language: rust
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
matrix:
|
||||
fast_finish: false
|
||||
include:
|
||||
- rust: stable
|
||||
- rust: beta
|
||||
- rust: nightly
|
||||
after_success: |
|
||||
[ $TRAVIS_BRANCH = master ] &&
|
||||
[ $TRAVIS_PULL_REQUEST = false ] &&
|
||||
[ $TRAVIS_RUST_VERSION = stable ] &&
|
||||
cargo doc --no-deps --verbose &&
|
||||
echo '<meta http-equiv=refresh content=0;url=ethkey/index.html>' > target/doc/index.html &&
|
||||
pip install --user ghp-import &&
|
||||
/home/travis/.local/bin/ghp-import -n target/doc &&
|
||||
git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages
|
||||
env:
|
||||
global:
|
||||
- secure: LBkFAH5fAhzHRP7kYQZnOCavOuPS2vEDv49KGfTsY/7MmW0De4c0sz0a3/0IdFqqIMLYLV2uMO86S0p6FBaq6/GIdobLsGZZ3cFReYFI+vb8sylYF/+D/aQ/UOjpEOD8HP6G3YmV5buSyL8uiPlmYbqwBAe4z6ELEbh/16gRuIqQLYQtpYPxMCD3tZzSux81b45K2khETZ7E+ap3LUG3rFTXxjEgx9leIZlVY+Qk4U5D9gFnJnjmxDPyIqzn2dORnw5jcpp3eSUEvSvSgjz4TAVg7Gw789jDl2dyr26U1wp1E5bB9AqZVYOb4l8vcQ6QiHrCvu7Wgl32O6XYkwMjDaDUB68bm5MTsUrwDWgKGx4xeurIBil5doHFlCGZ98RrzPxdgoCd6hCI459dA8jEwdXAfOkZ80RycZlryHCwn68x3dlnJoqVyg8viYo6H6G0GdH/dIhuwbnLDdWZsODehN8eJEy9KKQ4tPp+PjBcgKm1Wz5MzKFSIwfFInic7hjTVXGozHSvgvXJE0BI2bPbjVNCdZa5kGAAUAhBNXyTn7PbC7hYbmwAalzaOIjoYcdQLmUEz2J2gSOK8xW2gMU0Z2I+IylA0oh8xB/r2Q5sqLHT3LPLdzoETsyzaQjWFcFdXdsbbcG59DnFC9s2Jq7KqeODp6EJG4cw0ofKpBuDRes=
|
||||
@@ -1,13 +0,0 @@
|
||||
[package]
|
||||
description = "Parity Ethereum Keys Generator"
|
||||
name = "ethkey"
|
||||
version = "0.4.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
edit-distance = "2.0"
|
||||
log = "0.4"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
parity-crypto = { version = "0.4.2", features = ["publickey"] }
|
||||
parity-wordlist = "1.3"
|
||||
@@ -1,221 +0,0 @@
|
||||
## ethkey-cli
|
||||
|
||||
Parity Ethereum keys generator.
|
||||
|
||||
### Usage
|
||||
|
||||
```
|
||||
Parity Ethereum Keys Generator.
|
||||
Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
|
||||
Usage:
|
||||
ethkey info <secret-or-phrase> [options]
|
||||
ethkey generate random [options]
|
||||
ethkey generate prefix <prefix> [options]
|
||||
ethkey sign <secret> <message>
|
||||
ethkey verify public <public> <signature> <message>
|
||||
ethkey verify address <address> <signature> <message>
|
||||
ethkey recover <address> <known-phrase>
|
||||
ethkey [-h | --help]
|
||||
|
||||
Options:
|
||||
-h, --help Display this message and exit.
|
||||
-s, --secret Display only the secret key.
|
||||
-p, --public Display only the public key.
|
||||
-a, --address Display only the address.
|
||||
-b, --brain Use parity brain wallet algorithm. Not recommended.
|
||||
|
||||
Commands:
|
||||
info Display public key and address of the secret.
|
||||
generate random Generates new random Ethereum key.
|
||||
generate prefix Random generation, but address must start with a prefix ("vanity address").
|
||||
sign Sign message using a secret key.
|
||||
verify Verify signer of the signature by public key or address.
|
||||
recover Try to find brain phrase matching given address from partial phrase.
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
#### `info <secret>`
|
||||
*Display info about private key.*
|
||||
|
||||
- `<secret>` - ethereum secret, 32 bytes long
|
||||
|
||||
```
|
||||
ethkey info 17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55
|
||||
```
|
||||
|
||||
```
|
||||
secret: 17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55
|
||||
public: 689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124
|
||||
address: 26d1ec50b4e62c1d1a40d16e7cacc6a6580757d5
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
|
||||
#### `info --brain <phrase>`
|
||||
*Display info about private key generate from brain wallet recovery phrase.*
|
||||
|
||||
- `<phrase>` - Parity recovery phrase, 12 words
|
||||
|
||||
```
|
||||
ethkey info --brain "this is sparta"
|
||||
```
|
||||
|
||||
```
|
||||
The recover phrase was not generated by Parity: The word 'this' does not come from the dictionary.
|
||||
|
||||
secret: aa22b54c0cb43ee30a014afe5ef3664b1cde299feabca46cd3167a85a57c39f2
|
||||
public: c4c5398da6843632c123f543d714d2d2277716c11ff612b2a2f23c6bda4d6f0327c31cd58c55a9572c3cc141dade0c32747a13b7ef34c241b26c84adbb28fcf4
|
||||
address: 006e27b6a72e1f34c626762f3c4761547aff1421
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
#### `generate random`
|
||||
*Generate new keypair randomly.*
|
||||
|
||||
```
|
||||
ethkey generate random
|
||||
```
|
||||
|
||||
```
|
||||
secret: 7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5
|
||||
public: 35f222d88b80151857a2877826d940104887376a94c1cbd2c8c7c192eb701df88a18a4ecb8b05b1466c5b3706042027b5e079fe3a3683e66d822b0e047aa3418
|
||||
address: a8fa5dd30a87bb9e3288d604eb74949c515ab66e
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
#### `generate random --brain`
|
||||
*Generate new keypair with recovery phrase randomly.*
|
||||
|
||||
```
|
||||
ethkey generate random --brain
|
||||
```
|
||||
|
||||
```
|
||||
recovery phrase: thwarting scandal creamer nuzzle asparagus blast crouch trusting anytime elixir frenzied octagon
|
||||
secret: 001ce488d50d2f7579dc190c4655f32918d505cee3de63bddc7101bc91c0c2f0
|
||||
public: 4e19a5fdae82596e1485c69b687c9cc52b5078e5b0668ef3ce8543cd90e712cb00df822489bc1f1dcb3623538a54476c7b3def44e1a51dc174e86448b63f42d0
|
||||
address: 00cf3711cbd3a1512570639280758118ba0b2bcb
|
||||
```
|
||||
|
||||
|
||||
--
|
||||
|
||||
#### `generate prefix <prefix>`
|
||||
*Generate new keypair randomly with address starting with prefix.*
|
||||
|
||||
- `<prefix>` - desired address prefix, 0 - 32 bytes long.
|
||||
|
||||
```
|
||||
ethkey generate prefix ff
|
||||
```
|
||||
|
||||
```
|
||||
secret: 2075b1d9c124ea673de7273758ed6de14802a9da8a73ceb74533d7c312ff6acd
|
||||
public: 48dbce4508566a05509980a5dd1335599fcdac6f9858ba67018cecb9f09b8c4066dc4c18ae2722112fd4d9ac36d626793fffffb26071dfeb0c2300df994bd173
|
||||
address: fff7e25dff2aa60f61f9d98130c8646a01f31649
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
#### `generate prefix --brain <prefix>`
|
||||
*Generate new keypair with recovery phrase randomly with address starting with prefix.*
|
||||
|
||||
- `<prefix>` - desired address prefix, 0 - 32 bytes long.
|
||||
|
||||
```
|
||||
ethkey generate prefix --brain 00cf
|
||||
```
|
||||
|
||||
```
|
||||
recovery phrase: thwarting scandal creamer nuzzle asparagus blast crouch trusting anytime elixir frenzied octagon
|
||||
secret: 001ce488d50d2f7579dc190c4655f32918d505cee3de63bddc7101bc91c0c2f0
|
||||
public: 4e19a5fdae82596e1485c69b687c9cc52b5078e5b0668ef3ce8543cd90e712cb00df822489bc1f1dcb3623538a54476c7b3def44e1a51dc174e86448b63f42d0
|
||||
address: 00cf3711cbd3a1512570639280758118ba0b2bcb
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
#### `sign <secret> <message>`
|
||||
*Sign a message with a secret.*
|
||||
|
||||
- `<secret>` - ethereum secret, 32 bytes long
|
||||
- `<message>` - message to sign, 32 bytes long
|
||||
|
||||
```
|
||||
ethkey sign 17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55 bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987
|
||||
```
|
||||
|
||||
```
|
||||
c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
#### `verify public <public> <signature> <message>`
|
||||
*Verify the signature.*
|
||||
|
||||
- `<public>` - ethereum public, 64 bytes long
|
||||
- `<signature>` - message signature, 65 bytes long
|
||||
- `<message>` - message, 32 bytes long
|
||||
|
||||
```
|
||||
ethkey verify public 689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124 c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200 bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987
|
||||
```
|
||||
|
||||
```
|
||||
true
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
#### `verify address <address> <signature> <message>`
|
||||
*Verify the signature.*
|
||||
|
||||
- `<address>` - ethereum address, 20 bytes long
|
||||
- `<signature>` - message signature, 65 bytes long
|
||||
- `<message>` - message, 32 bytes long
|
||||
|
||||
```
|
||||
ethkey verify address 689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124 c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200 bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987
|
||||
```
|
||||
|
||||
```
|
||||
true
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
#### `recover <address> <known-phrase>`
|
||||
*Try to recover an account given expected address and partial (too short or with invalid words) recovery phrase.*
|
||||
|
||||
- `<address>` - ethereum address, 20 bytes long
|
||||
- `<known-phrase>` - known phrase, can be in a form of `thwarting * creamer`
|
||||
|
||||
```
|
||||
RUST_LOG="info" ethkey recover "00cf3711cbd3a1512570639280758118ba0b2bcb" "thwarting scandal creamer nuzzle asparagus blast crouch trusting anytime elixir frenzied octag"
|
||||
```
|
||||
|
||||
```
|
||||
INFO:ethkey::brain_recover: Invalid word 'octag', looking for potential substitutions.
|
||||
INFO:ethkey::brain_recover: Closest words: ["ocean", "octagon", "octane", "outage", "tag", "acting", "acts", "aorta", "cage", "chug"]
|
||||
INFO:ethkey::brain_recover: Starting to test 7776 possible combinations.
|
||||
|
||||
thwarting scandal creamer nuzzle asparagus blast crouch trusting anytime elixir frenzied octagon
|
||||
secret: 001ce488d50d2f7579dc190c4655f32918d505cee3de63bddc7101bc91c0c2f0
|
||||
public: 4e19a5fdae82596e1485c69b687c9cc52b5078e5b0668ef3ce8543cd90e712cb00df822489bc1f1dcb3623538a54476c7b3def44e1a51dc174e86448b63f42d0
|
||||
address: 00cf3711cbd3a1512570639280758118ba0b2bcb
|
||||
```
|
||||
|
||||
## Parity Ethereum toolchain
|
||||
_This project is a part of the Parity Ethereum toolchain._
|
||||
|
||||
- [evmbin](https://github.com/paritytech/parity-ethereum/blob/master/evmbin/) - EVM implementation for Parity Ethereum.
|
||||
- [ethabi](https://github.com/paritytech/ethabi) - Parity Ethereum function calls encoding.
|
||||
- [ethstore](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethstore) - Parity Ethereum key management.
|
||||
- [ethkey](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethkey) - Parity Ethereum keys generator.
|
||||
- [whisper](https://github.com/paritytech/whisper) - Implementation of Whisper-v2 PoC.
|
||||
@@ -1,22 +0,0 @@
|
||||
[package]
|
||||
description = "Parity Ethereum Keys Generator CLI"
|
||||
name = "ethkey-cli"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
docopt = "1.0"
|
||||
env_logger = "0.5"
|
||||
ethkey = { path = "../" }
|
||||
panic_hook = { path = "../../../util/panic-hook" }
|
||||
parity-crypto = { version = "0.4.2", features = ["publickey"] }
|
||||
parity-wordlist="1.2"
|
||||
rustc-hex = "1.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
threadpool = "1.7"
|
||||
|
||||
[[bin]]
|
||||
name = "ethkey"
|
||||
path = "src/main.rs"
|
||||
doc = false
|
||||
@@ -1,453 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate docopt;
|
||||
extern crate env_logger;
|
||||
extern crate ethkey;
|
||||
extern crate panic_hook;
|
||||
extern crate parity_wordlist;
|
||||
extern crate parity_crypto;
|
||||
extern crate rustc_hex;
|
||||
extern crate serde;
|
||||
extern crate threadpool;
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
use std::num::ParseIntError;
|
||||
use std::{env, fmt, process, io, sync};
|
||||
|
||||
use docopt::Docopt;
|
||||
use ethkey::{Brain, BrainPrefix, Prefix, brain_recover};
|
||||
use parity_crypto::publickey::{KeyPair, Random, Error as EthkeyError, Generator, sign, verify_public, verify_address};
|
||||
use rustc_hex::{FromHex, FromHexError};
|
||||
|
||||
const USAGE: &'static str = r#"
|
||||
Parity Ethereum keys generator.
|
||||
Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
|
||||
Usage:
|
||||
ethkey info <secret-or-phrase> [options]
|
||||
ethkey generate random [options]
|
||||
ethkey generate prefix <prefix> [options]
|
||||
ethkey sign <secret> <message>
|
||||
ethkey verify public <public> <signature> <message>
|
||||
ethkey verify address <address> <signature> <message>
|
||||
ethkey recover <address> <known-phrase>
|
||||
ethkey [-h | --help]
|
||||
|
||||
Options:
|
||||
-h, --help Display this message and exit.
|
||||
-s, --secret Display only the secret key.
|
||||
-p, --public Display only the public key.
|
||||
-a, --address Display only the address.
|
||||
-b, --brain Use parity brain wallet algorithm. Not recommended.
|
||||
|
||||
Commands:
|
||||
info Display public key and address of the secret.
|
||||
generate random Generates new random Ethereum key.
|
||||
generate prefix Random generation, but address must start with a prefix ("vanity address").
|
||||
sign Sign message using a secret key.
|
||||
verify Verify signer of the signature by public key or address.
|
||||
recover Try to find brain phrase matching given address from partial phrase.
|
||||
"#;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Args {
|
||||
cmd_info: bool,
|
||||
cmd_generate: bool,
|
||||
cmd_random: bool,
|
||||
cmd_prefix: bool,
|
||||
cmd_sign: bool,
|
||||
cmd_verify: bool,
|
||||
cmd_public: bool,
|
||||
cmd_address: bool,
|
||||
cmd_recover: bool,
|
||||
arg_prefix: String,
|
||||
arg_secret: String,
|
||||
arg_secret_or_phrase: String,
|
||||
arg_known_phrase: String,
|
||||
arg_message: String,
|
||||
arg_public: String,
|
||||
arg_address: String,
|
||||
arg_signature: String,
|
||||
flag_secret: bool,
|
||||
flag_public: bool,
|
||||
flag_address: bool,
|
||||
flag_brain: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Error {
|
||||
Ethkey(EthkeyError),
|
||||
FromHex(FromHexError),
|
||||
ParseInt(ParseIntError),
|
||||
Docopt(docopt::Error),
|
||||
Io(io::Error),
|
||||
}
|
||||
|
||||
impl From<EthkeyError> for Error {
|
||||
fn from(err: EthkeyError) -> Self {
|
||||
Error::Ethkey(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FromHexError> for Error {
|
||||
fn from(err: FromHexError) -> Self {
|
||||
Error::FromHex(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseIntError> for Error {
|
||||
fn from(err: ParseIntError) -> Self {
|
||||
Error::ParseInt(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<docopt::Error> for Error {
|
||||
fn from(err: docopt::Error) -> Self {
|
||||
Error::Docopt(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(err: io::Error) -> Self {
|
||||
Error::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
Error::Ethkey(ref e) => write!(f, "{}", e),
|
||||
Error::FromHex(ref e) => write!(f, "{}", e),
|
||||
Error::ParseInt(ref e) => write!(f, "{}", e),
|
||||
Error::Docopt(ref e) => write!(f, "{}", e),
|
||||
Error::Io(ref e) => write!(f, "{}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum DisplayMode {
|
||||
KeyPair,
|
||||
Secret,
|
||||
Public,
|
||||
Address,
|
||||
}
|
||||
|
||||
impl DisplayMode {
|
||||
fn new(args: &Args) -> Self {
|
||||
if args.flag_secret {
|
||||
DisplayMode::Secret
|
||||
} else if args.flag_public {
|
||||
DisplayMode::Public
|
||||
} else if args.flag_address {
|
||||
DisplayMode::Address
|
||||
} else {
|
||||
DisplayMode::KeyPair
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
panic_hook::set_abort();
|
||||
env_logger::try_init().expect("Logger initialized only once.");
|
||||
|
||||
match execute(env::args()) {
|
||||
Ok(ok) => println!("{}", ok),
|
||||
Err(Error::Docopt(ref e)) => e.exit(),
|
||||
Err(err) => {
|
||||
eprintln!("{}", err);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn display(result: (KeyPair, Option<String>), mode: DisplayMode) -> String {
|
||||
let keypair = result.0;
|
||||
match mode {
|
||||
DisplayMode::KeyPair => match result.1 {
|
||||
Some(extra_data) => format!("{}\n{}", extra_data, keypair),
|
||||
None => format!("{}", keypair)
|
||||
},
|
||||
DisplayMode::Secret => format!("{:x}", keypair.secret()),
|
||||
DisplayMode::Public => format!("{:x}", keypair.public()),
|
||||
DisplayMode::Address => format!("{:x}", keypair.address()),
|
||||
}
|
||||
}
|
||||
|
||||
fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item=S>, S: AsRef<str> {
|
||||
let args: Args = Docopt::new(USAGE)
|
||||
.and_then(|d| d.argv(command).deserialize())?;
|
||||
|
||||
return if args.cmd_info {
|
||||
let display_mode = DisplayMode::new(&args);
|
||||
|
||||
let result = if args.flag_brain {
|
||||
let phrase = args.arg_secret_or_phrase;
|
||||
let phrase_info = validate_phrase(&phrase);
|
||||
let keypair = Brain::new(phrase).generate().expect("Brain wallet generator is infallible; qed");
|
||||
(keypair, Some(phrase_info))
|
||||
} else {
|
||||
let secret = args.arg_secret_or_phrase.parse().map_err(|_| EthkeyError::InvalidSecretKey)?;
|
||||
(KeyPair::from_secret(secret)?, None)
|
||||
};
|
||||
Ok(display(result, display_mode))
|
||||
} else if args.cmd_generate {
|
||||
let display_mode = DisplayMode::new(&args);
|
||||
let result = if args.cmd_random {
|
||||
if args.flag_brain {
|
||||
let mut brain = BrainPrefix::new(vec![0], usize::max_value(), BRAIN_WORDS);
|
||||
let keypair = brain.generate()?;
|
||||
let phrase = format!("recovery phrase: {}", brain.phrase());
|
||||
(keypair, Some(phrase))
|
||||
} else {
|
||||
(Random.generate()?, None)
|
||||
}
|
||||
} else if args.cmd_prefix {
|
||||
let prefix = args.arg_prefix.from_hex()?;
|
||||
let brain = args.flag_brain;
|
||||
in_threads(move || {
|
||||
let iterations = 1024;
|
||||
let prefix = prefix.clone();
|
||||
move || {
|
||||
let prefix = prefix.clone();
|
||||
let res = if brain {
|
||||
let mut brain = BrainPrefix::new(prefix, iterations, BRAIN_WORDS);
|
||||
let result = brain.generate();
|
||||
let phrase = format!("recovery phrase: {}", brain.phrase());
|
||||
result.map(|keypair| (keypair, Some(phrase)))
|
||||
} else {
|
||||
let result = Prefix::new(prefix, iterations).generate();
|
||||
result.map(|res| (res, None))
|
||||
};
|
||||
|
||||
Ok(res.map(Some).unwrap_or(None))
|
||||
}
|
||||
})?
|
||||
} else {
|
||||
return Ok(format!("{}", USAGE))
|
||||
};
|
||||
Ok(display(result, display_mode))
|
||||
} else if args.cmd_sign {
|
||||
let secret = args.arg_secret.parse().map_err(|_| EthkeyError::InvalidSecretKey)?;
|
||||
let message = args.arg_message.parse().map_err(|_| EthkeyError::InvalidMessage)?;
|
||||
let signature = sign(&secret, &message)?;
|
||||
Ok(format!("{}", signature))
|
||||
} else if args.cmd_verify {
|
||||
let signature = args.arg_signature.parse().map_err(|_| EthkeyError::InvalidSignature)?;
|
||||
let message = args.arg_message.parse().map_err(|_| EthkeyError::InvalidMessage)?;
|
||||
let ok = if args.cmd_public {
|
||||
let public = args.arg_public.parse().map_err(|_| EthkeyError::InvalidPublicKey)?;
|
||||
verify_public(&public, &signature, &message)?
|
||||
} else if args.cmd_address {
|
||||
let address = args.arg_address.parse().map_err(|_| EthkeyError::InvalidAddress)?;
|
||||
verify_address(&address, &signature, &message)?
|
||||
} else {
|
||||
return Ok(format!("{}", USAGE))
|
||||
};
|
||||
Ok(format!("{}", ok))
|
||||
} else if args.cmd_recover {
|
||||
let display_mode = DisplayMode::new(&args);
|
||||
let known_phrase = args.arg_known_phrase;
|
||||
let address = args.arg_address.parse().map_err(|_| EthkeyError::InvalidAddress)?;
|
||||
let (phrase, keypair) = in_threads(move || {
|
||||
let mut it = brain_recover::PhrasesIterator::from_known_phrase(&known_phrase, BRAIN_WORDS);
|
||||
move || {
|
||||
let mut i = 0;
|
||||
while let Some(phrase) = it.next() {
|
||||
i += 1;
|
||||
|
||||
let keypair = Brain::new(phrase.clone()).generate().unwrap();
|
||||
if keypair.address() == address {
|
||||
return Ok(Some((phrase, keypair)))
|
||||
}
|
||||
|
||||
if i >= 1024 {
|
||||
return Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
Err(EthkeyError::Custom("Couldn't find any results.".into()))
|
||||
}
|
||||
})?;
|
||||
Ok(display((keypair, Some(phrase)), display_mode))
|
||||
} else {
|
||||
Ok(format!("{}", USAGE))
|
||||
}
|
||||
}
|
||||
|
||||
const BRAIN_WORDS: usize = 12;
|
||||
|
||||
fn validate_phrase(phrase: &str) -> String {
|
||||
match Brain::validate_phrase(phrase, BRAIN_WORDS) {
|
||||
Ok(()) => format!("The recovery phrase looks correct.\n"),
|
||||
Err(err) => format!("The recover phrase was not generated by Parity: {}", err)
|
||||
}
|
||||
}
|
||||
|
||||
fn in_threads<F, X, O>(prepare: F) -> Result<O, EthkeyError> where
|
||||
O: Send + 'static,
|
||||
X: Send + 'static,
|
||||
F: Fn() -> X,
|
||||
X: FnMut() -> Result<Option<O>, EthkeyError>,
|
||||
{
|
||||
let pool = threadpool::Builder::new().build();
|
||||
|
||||
let (tx, rx) = sync::mpsc::sync_channel(1);
|
||||
let is_done = sync::Arc::new(sync::atomic::AtomicBool::default());
|
||||
|
||||
for _ in 0..pool.max_count() {
|
||||
let is_done = is_done.clone();
|
||||
let tx = tx.clone();
|
||||
let mut task = prepare();
|
||||
pool.execute(move || {
|
||||
loop {
|
||||
if is_done.load(sync::atomic::Ordering::SeqCst) {
|
||||
return;
|
||||
}
|
||||
|
||||
let res = match task() {
|
||||
Ok(None) => continue,
|
||||
Ok(Some(v)) => Ok(v),
|
||||
Err(err) => Err(err),
|
||||
};
|
||||
|
||||
// We are interested only in the first response.
|
||||
let _ = tx.send(res);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if let Ok(solution) = rx.recv() {
|
||||
is_done.store(true, sync::atomic::Ordering::SeqCst);
|
||||
return solution;
|
||||
}
|
||||
|
||||
Err(EthkeyError::Custom("No results found.".into()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::execute;
|
||||
|
||||
#[test]
|
||||
fn info() {
|
||||
let command = vec!["ethkey", "info", "17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55"]
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let expected =
|
||||
"secret: 17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55
|
||||
public: 689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124
|
||||
address: 26d1ec50b4e62c1d1a40d16e7cacc6a6580757d5".to_owned();
|
||||
assert_eq!(execute(command).unwrap(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn brain() {
|
||||
let command = vec!["ethkey", "info", "--brain", "this is sparta"]
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let expected =
|
||||
"The recover phrase was not generated by Parity: The word 'this' does not come from the dictionary.
|
||||
|
||||
secret: aa22b54c0cb43ee30a014afe5ef3664b1cde299feabca46cd3167a85a57c39f2
|
||||
public: c4c5398da6843632c123f543d714d2d2277716c11ff612b2a2f23c6bda4d6f0327c31cd58c55a9572c3cc141dade0c32747a13b7ef34c241b26c84adbb28fcf4
|
||||
address: 006e27b6a72e1f34c626762f3c4761547aff1421".to_owned();
|
||||
assert_eq!(execute(command).unwrap(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret() {
|
||||
let command = vec!["ethkey", "info", "--brain", "this is sparta", "--secret"]
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let expected = "aa22b54c0cb43ee30a014afe5ef3664b1cde299feabca46cd3167a85a57c39f2".to_owned();
|
||||
assert_eq!(execute(command).unwrap(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public() {
|
||||
let command = vec!["ethkey", "info", "--brain", "this is sparta", "--public"]
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let expected = "c4c5398da6843632c123f543d714d2d2277716c11ff612b2a2f23c6bda4d6f0327c31cd58c55a9572c3cc141dade0c32747a13b7ef34c241b26c84adbb28fcf4".to_owned();
|
||||
assert_eq!(execute(command).unwrap(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn address() {
|
||||
let command = vec!["ethkey", "info", "-b", "this is sparta", "--address"]
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let expected = "006e27b6a72e1f34c626762f3c4761547aff1421".to_owned();
|
||||
assert_eq!(execute(command).unwrap(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sign() {
|
||||
let command = vec!["ethkey", "sign", "17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987"]
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let expected = "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200".to_owned();
|
||||
assert_eq!(execute(command).unwrap(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_valid_public() {
|
||||
let command = vec!["ethkey", "verify", "public", "689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987"]
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let expected = "true".to_owned();
|
||||
assert_eq!(execute(command).unwrap(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_valid_address() {
|
||||
let command = vec!["ethkey", "verify", "address", "26d1ec50b4e62c1d1a40d16e7cacc6a6580757d5", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987"]
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let expected = "true".to_owned();
|
||||
assert_eq!(execute(command).unwrap(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_invalid() {
|
||||
let command = vec!["ethkey", "verify", "public", "689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec986"]
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let expected = "false".to_owned();
|
||||
assert_eq!(execute(command).unwrap(), expected);
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::convert::Infallible;
|
||||
use parity_crypto::publickey::{KeyPair, Generator, Secret};
|
||||
use parity_crypto::Keccak256;
|
||||
use parity_wordlist;
|
||||
|
||||
/// Simple brainwallet.
|
||||
pub struct Brain(String);
|
||||
|
||||
impl Brain {
|
||||
pub fn new(s: String) -> Self {
|
||||
Brain(s)
|
||||
}
|
||||
|
||||
pub fn validate_phrase(phrase: &str, expected_words: usize) -> Result<(), ::WordlistError> {
|
||||
parity_wordlist::validate_phrase(phrase, expected_words)
|
||||
}
|
||||
}
|
||||
|
||||
impl Generator for Brain {
|
||||
type Error = Infallible;
|
||||
|
||||
fn generate(&mut self) -> Result<KeyPair, Self::Error> {
|
||||
let seed = self.0.clone();
|
||||
let mut secret = seed.into_bytes().keccak256();
|
||||
|
||||
let mut i = 0;
|
||||
loop {
|
||||
secret = secret.keccak256();
|
||||
|
||||
match i > 16384 {
|
||||
false => i += 1,
|
||||
true => {
|
||||
if let Ok(pair) = Secret::import_key(&secret)
|
||||
.and_then(KeyPair::from_secret)
|
||||
{
|
||||
if pair.address()[0] == 0 {
|
||||
trace!("Testing: {}, got: {:?}", self.0, pair.address());
|
||||
return Ok(pair)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use Brain;
|
||||
use parity_crypto::publickey::Generator;
|
||||
|
||||
#[test]
|
||||
fn test_brain() {
|
||||
let words = "this is sparta!".to_owned();
|
||||
let first_keypair = Brain::new(words.clone()).generate().unwrap();
|
||||
let second_keypair = Brain::new(words.clone()).generate().unwrap();
|
||||
assert_eq!(first_keypair.secret(), second_keypair.secret());
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::Brain;
|
||||
use parity_crypto::publickey::{Generator, KeyPair, Error};
|
||||
use parity_wordlist as wordlist;
|
||||
|
||||
/// Tries to find brain-seed keypair with address starting with given prefix.
|
||||
pub struct BrainPrefix {
|
||||
prefix: Vec<u8>,
|
||||
iterations: usize,
|
||||
no_of_words: usize,
|
||||
last_phrase: String,
|
||||
}
|
||||
|
||||
impl BrainPrefix {
|
||||
pub fn new(prefix: Vec<u8>, iterations: usize, no_of_words: usize) -> Self {
|
||||
BrainPrefix {
|
||||
prefix,
|
||||
iterations,
|
||||
no_of_words,
|
||||
last_phrase: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn phrase(&self) -> &str {
|
||||
&self.last_phrase
|
||||
}
|
||||
}
|
||||
|
||||
impl Generator for BrainPrefix {
|
||||
type Error = Error;
|
||||
|
||||
fn generate(&mut self) -> Result<KeyPair, Error> {
|
||||
for _ in 0..self.iterations {
|
||||
let phrase = wordlist::random_phrase(self.no_of_words);
|
||||
let keypair = Brain::new(phrase.clone()).generate().unwrap();
|
||||
if keypair.address().as_ref().starts_with(&self.prefix) {
|
||||
self.last_phrase = phrase;
|
||||
return Ok(keypair)
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::Custom("Could not find keypair".into()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use BrainPrefix;
|
||||
use parity_crypto::publickey::Generator;
|
||||
|
||||
#[test]
|
||||
fn prefix_generator() {
|
||||
let prefix = vec![0x00u8];
|
||||
let keypair = BrainPrefix::new(prefix.clone(), usize::max_value(), 12).generate().unwrap();
|
||||
assert!(keypair.address().as_bytes().starts_with(&prefix));
|
||||
}
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use edit_distance::edit_distance;
|
||||
use parity_wordlist;
|
||||
|
||||
use super::Brain;
|
||||
use parity_crypto::publickey::{Address, Generator};
|
||||
|
||||
/// Tries to find a phrase for address, given the number
|
||||
/// of expected words and a partial phrase.
|
||||
///
|
||||
/// Returns `None` if phrase couldn't be found.
|
||||
pub fn brain_recover(
|
||||
address: &Address,
|
||||
known_phrase: &str,
|
||||
expected_words: usize,
|
||||
) -> Option<String> {
|
||||
let it = PhrasesIterator::from_known_phrase(known_phrase, expected_words);
|
||||
for phrase in it {
|
||||
let keypair = Brain::new(phrase.clone()).generate().expect("Brain wallets are infallible; qed");
|
||||
trace!("Testing: {}, got: {:?}", phrase, keypair.address());
|
||||
if &keypair.address() == address {
|
||||
return Some(phrase);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn generate_substitutions(word: &str) -> Vec<&'static str> {
|
||||
let mut words = parity_wordlist::WORDS.iter().cloned()
|
||||
.map(|w| (edit_distance(w, word), w))
|
||||
.collect::<Vec<_>>();
|
||||
words.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
|
||||
words.into_iter()
|
||||
.map(|pair| pair.1)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Iterator over possible
|
||||
pub struct PhrasesIterator {
|
||||
words: Vec<Vec<&'static str>>,
|
||||
combinations: u64,
|
||||
indexes: Vec<usize>,
|
||||
has_next: bool,
|
||||
}
|
||||
|
||||
impl PhrasesIterator {
|
||||
pub fn from_known_phrase(known_phrase: &str, expected_words: usize) -> Self {
|
||||
let known_words = parity_wordlist::WORDS.iter().cloned().collect::<HashSet<_>>();
|
||||
let mut words = known_phrase.split(' ')
|
||||
.map(|word| match known_words.get(word) {
|
||||
None => {
|
||||
info!("Invalid word '{}', looking for potential substitutions.", word);
|
||||
let substitutions = generate_substitutions(word);
|
||||
info!("Closest words: {:?}", &substitutions[..10]);
|
||||
substitutions
|
||||
},
|
||||
Some(word) => vec![*word],
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// add missing words
|
||||
if words.len() < expected_words {
|
||||
let to_add = expected_words - words.len();
|
||||
info!("Number of words is insuficcient adding {} more.", to_add);
|
||||
for _ in 0..to_add {
|
||||
words.push(parity_wordlist::WORDS.iter().cloned().collect());
|
||||
}
|
||||
}
|
||||
|
||||
// start searching
|
||||
PhrasesIterator::new(words)
|
||||
}
|
||||
|
||||
pub fn new(words: Vec<Vec<&'static str>>) -> Self {
|
||||
let combinations = words.iter().fold(1u64, |acc, x| acc * x.len() as u64);
|
||||
let indexes = words.iter().map(|_| 0).collect();
|
||||
info!("Starting to test {} possible combinations.", combinations);
|
||||
|
||||
PhrasesIterator {
|
||||
words,
|
||||
combinations,
|
||||
indexes,
|
||||
has_next: combinations > 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn combinations(&self) -> u64 {
|
||||
self.combinations
|
||||
}
|
||||
|
||||
fn current(&self) -> String {
|
||||
let mut s = self.words[0][self.indexes[0]].to_owned();
|
||||
for i in 1..self.indexes.len() {
|
||||
s.push(' ');
|
||||
s.push_str(self.words[i][self.indexes[i]]);
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
fn next_index(&mut self) -> bool {
|
||||
let mut pos = self.indexes.len();
|
||||
while pos > 0 {
|
||||
pos -= 1;
|
||||
self.indexes[pos] += 1;
|
||||
if self.indexes[pos] >= self.words[pos].len() {
|
||||
self.indexes[pos] = 0;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for PhrasesIterator {
|
||||
type Item = String;
|
||||
|
||||
fn next(&mut self) -> Option<String> {
|
||||
if !self.has_next {
|
||||
return None;
|
||||
}
|
||||
|
||||
let phrase = self.current();
|
||||
self.has_next = self.next_index();
|
||||
Some(phrase)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::PhrasesIterator;
|
||||
|
||||
#[test]
|
||||
fn should_generate_possible_combinations() {
|
||||
let mut it = PhrasesIterator::new(vec![
|
||||
vec!["1", "2", "3"],
|
||||
vec!["test"],
|
||||
vec!["a", "b", "c"],
|
||||
]);
|
||||
|
||||
assert_eq!(it.combinations(), 9);
|
||||
assert_eq!(it.next(), Some("1 test a".to_owned()));
|
||||
assert_eq!(it.next(), Some("1 test b".to_owned()));
|
||||
assert_eq!(it.next(), Some("1 test c".to_owned()));
|
||||
assert_eq!(it.next(), Some("2 test a".to_owned()));
|
||||
assert_eq!(it.next(), Some("2 test b".to_owned()));
|
||||
assert_eq!(it.next(), Some("2 test c".to_owned()));
|
||||
assert_eq!(it.next(), Some("3 test a".to_owned()));
|
||||
assert_eq!(it.next(), Some("3 test b".to_owned()));
|
||||
assert_eq!(it.next(), Some("3 test c".to_owned()));
|
||||
assert_eq!(it.next(), None);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// #![warn(missing_docs)]
|
||||
|
||||
extern crate edit_distance;
|
||||
extern crate parity_crypto;
|
||||
extern crate parity_wordlist;
|
||||
extern crate serde;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
mod brain;
|
||||
mod brain_prefix;
|
||||
mod password;
|
||||
mod prefix;
|
||||
|
||||
pub mod brain_recover;
|
||||
|
||||
pub use self::parity_wordlist::Error as WordlistError;
|
||||
pub use self::brain::Brain;
|
||||
pub use self::brain_prefix::BrainPrefix;
|
||||
pub use self::password::Password;
|
||||
pub use self::prefix::Prefix;
|
||||
@@ -1,59 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{fmt, ptr};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Password(String);
|
||||
|
||||
impl fmt::Debug for Password {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Password(******)")
|
||||
}
|
||||
}
|
||||
|
||||
impl Password {
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
self.0.as_bytes()
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.0.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
// Custom drop impl to zero out memory.
|
||||
impl Drop for Password {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
for byte_ref in self.0.as_mut_vec() {
|
||||
ptr::write_volatile(byte_ref, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Password {
|
||||
fn from(s: String) -> Password {
|
||||
Password(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for Password {
|
||||
fn from(s: &'a str) -> Password {
|
||||
Password::from(String::from(s))
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use parity_crypto::publickey::{Random, Generator, KeyPair, Error};
|
||||
|
||||
/// Tries to find keypair with address starting with given prefix.
|
||||
pub struct Prefix {
|
||||
prefix: Vec<u8>,
|
||||
iterations: usize,
|
||||
}
|
||||
|
||||
impl Prefix {
|
||||
pub fn new(prefix: Vec<u8>, iterations: usize) -> Self {
|
||||
Prefix {
|
||||
prefix: prefix,
|
||||
iterations: iterations,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Generator for Prefix {
|
||||
type Error = Error;
|
||||
|
||||
fn generate(&mut self) -> Result<KeyPair, Error> {
|
||||
for _ in 0..self.iterations {
|
||||
let keypair = Random.generate()?;
|
||||
if keypair.address().as_ref().starts_with(&self.prefix) {
|
||||
return Ok(keypair)
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::Custom("Could not find keypair".into()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use Prefix;
|
||||
use parity_crypto::publickey::Generator;
|
||||
|
||||
#[test]
|
||||
fn prefix_generator() {
|
||||
let prefix = vec![0xffu8];
|
||||
let keypair = Prefix::new(prefix.clone(), usize::max_value()).generate().unwrap();
|
||||
assert!(keypair.address().as_bytes().starts_with(&prefix));
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
root = true
|
||||
[*]
|
||||
indent_style=tab
|
||||
indent_size=tab
|
||||
tab_width=4
|
||||
end_of_line=lf
|
||||
charset=utf-8
|
||||
trim_trailing_whitespace=true
|
||||
max_line_length=120
|
||||
insert_final_newline=true
|
||||
|
||||
2
accounts/ethstore/.gitignore
vendored
2
accounts/ethstore/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
target
|
||||
*.swp
|
||||
@@ -1,23 +0,0 @@
|
||||
sudo: false
|
||||
language: rust
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
matrix:
|
||||
fast_finish: false
|
||||
include:
|
||||
- rust: stable
|
||||
- rust: beta
|
||||
- rust: nightly
|
||||
after_success: |
|
||||
[ $TRAVIS_BRANCH = master ] &&
|
||||
[ $TRAVIS_PULL_REQUEST = false ] &&
|
||||
[ $TRAVIS_RUST_VERSION = stable ] &&
|
||||
cargo doc --no-deps --verbose &&
|
||||
echo '<meta http-equiv=refresh content=0;url=ethkey/index.html>' > target/doc/index.html &&
|
||||
pip install --user ghp-import &&
|
||||
/home/travis/.local/bin/ghp-import -n target/doc &&
|
||||
git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages
|
||||
env:
|
||||
global:
|
||||
- secure: C4l7WR0jS84WNmd3MvmpPXQz4wRh4CLDS6bP3BqSHXadz8FPKejtMJZscLYAk5kIkDcVsTAYb88RsEFRrYOA4wkS6vhZBtryYRaJ68MlkyEU/77SYwm86rkQINIDw65O73dUD5LbWWCUoYkenGu26u/UnfayHfJBAyKw5IHkVKf6eDqu7E8ojKSEOXbWgBHjq6uixI8IESb15UjIE0AQ1Od+6cqhsz/caPhTMT3CJGjoCoVGWChwWSQZ+Ppb+xB83C/1h58UVwE9sZEyIPKwVP6socnHPmtR+VEUI6a7YIsOk6ZadKLtyy4523w4HqHNx1/dYjmsknbGpkF4D0DRp5L3D4t4J6URCkJIHfSRrBF5l2QbLMMuSf+KWMWuFOrOF5DBryobRKAVmIL5AjfvFsxtBNzYLPyVBs0ntbPuN5WeUPhadam00za9Z1ZvOUJxfNfyy9R67u6FdD9xkw2m/9hO7KJLDeZ4TSCRFrzfl/7WQprfjCwhZ+reKPgHH0Ufy1/Kh/WEuEBfZDa+z3mWWHlslqH2uBPH3+pvhzdVQGLB/5GZdJNeg/nJYJDCqHyWUKxkw+OMSvI0J8W0GiHV4TuY9V3p+rYjU2Zj69u3/xO/IvKrFtB9xdeJMrLiFQ2cD5vgzQOLCKo80f53NitUjdVSoWrY/NcYopBU4VHZMlk=
|
||||
@@ -1,30 +0,0 @@
|
||||
[package]
|
||||
description = "Parity Ethereum Key Management"
|
||||
name = "ethstore"
|
||||
version = "0.2.1"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
libc = "0.2"
|
||||
rand = "0.7"
|
||||
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.9"
|
||||
parity-crypto = { version = "0.4.2", features = ["publickey"] }
|
||||
ethereum-types = "0.8.0"
|
||||
dir = { path = "../../util/dir" }
|
||||
smallvec = "0.6"
|
||||
parity-wordlist = "1.0"
|
||||
tempdir = "0.3"
|
||||
|
||||
[dev-dependencies]
|
||||
matches = "0.1"
|
||||
|
||||
[lib]
|
||||
@@ -1,340 +0,0 @@
|
||||
## ethstore-cli
|
||||
|
||||
Parity Ethereum key management.
|
||||
|
||||
### Usage
|
||||
|
||||
```
|
||||
Parity Ethereum key management tool.
|
||||
Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
|
||||
Usage:
|
||||
ethstore insert <secret> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore change-pwd <address> <old-pwd> <new-pwd> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore list [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore import [--src DIR] [--dir DIR]
|
||||
ethstore import-wallet <path> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore find-wallet-pass <path> <password>
|
||||
ethstore remove <address> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore sign <address> <password> <message> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore public <address> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore list-vaults [--dir DIR]
|
||||
ethstore create-vault <vault> <password> [--dir DIR]
|
||||
ethstore change-vault-pwd <vault> <old-pwd> <new-pwd> [--dir DIR]
|
||||
ethstore move-to-vault <address> <vault> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore move-from-vault <address> <vault> <password> [--dir DIR]
|
||||
ethstore [-h | --help]
|
||||
|
||||
Options:
|
||||
-h, --help Display this message and exit.
|
||||
--dir DIR Specify the secret store directory. It may be either
|
||||
parity, parity-(chain), geth, geth-test
|
||||
or a path [default: parity].
|
||||
--vault VAULT Specify vault to use in this operation.
|
||||
--vault-pwd VAULTPWD Specify vault password to use in this operation. Please note
|
||||
that this option is required when vault option is set.
|
||||
Otherwise it is ignored.
|
||||
--src DIR Specify import source. It may be either
|
||||
parity, parity-(chain), geth, geth-test
|
||||
or a path [default: geth].
|
||||
|
||||
Commands:
|
||||
insert Save account with password.
|
||||
change-pwd Change password.
|
||||
list List accounts.
|
||||
import Import accounts from src.
|
||||
import-wallet Import presale wallet.
|
||||
find-wallet-pass Tries to open a wallet with list of passwords given.
|
||||
remove Remove account.
|
||||
sign Sign message.
|
||||
public Displays public key for an address.
|
||||
list-vaults List vaults.
|
||||
create-vault Create new vault.
|
||||
change-vault-pwd Change vault password.
|
||||
move-to-vault Move account to vault from another vault/root directory.
|
||||
move-from-vault Move account to root directory from given vault.
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
#### `insert <secret> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]`
|
||||
*Encrypt secret with a password and save it in secret store.*
|
||||
|
||||
- `<secret>` - ethereum secret, 32 bytes long
|
||||
- `<password>` - account password, file path
|
||||
- `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity
|
||||
- `[--vault VAULT]` - vault to use in this operation
|
||||
- `[--vault-pwd VAULTPWD]` - vault password to use in this operation, file path
|
||||
|
||||
```
|
||||
ethstore insert 7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5 password.txt
|
||||
```
|
||||
|
||||
```
|
||||
a8fa5dd30a87bb9e3288d604eb74949c515ab66e
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
```
|
||||
ethstore insert `ethkey generate random -s` "this is sparta"
|
||||
```
|
||||
|
||||
```
|
||||
24edfff680d536a5f6fe862d36df6f8f6f40f115
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
#### `change-pwd <address> <old-pwd> <new-pwd> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]`
|
||||
*Change account password.*
|
||||
|
||||
- `<address>` - ethereum address, 20 bytes long
|
||||
- `<old-pwd>` - old account password, file path
|
||||
- `<new-pwd>` - new account password, file path
|
||||
- `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity
|
||||
- `[--vault VAULT]` - vault to use in this operation
|
||||
- `[--vault-pwd VAULTPWD]` - vault password to use in this operation, file path
|
||||
|
||||
```
|
||||
ethstore change-pwd a8fa5dd30a87bb9e3288d604eb74949c515ab66e old_pwd.txt new_pwd.txt
|
||||
```
|
||||
|
||||
```
|
||||
true
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
#### `list [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]`
|
||||
*List secret store accounts.*
|
||||
|
||||
- `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity
|
||||
- `[--vault VAULT]` - vault to use in this operation
|
||||
- `[--vault-pwd VAULTPWD]` - vault password to use in this operation, file path
|
||||
|
||||
```
|
||||
ethstore list
|
||||
```
|
||||
|
||||
```
|
||||
0: 24edfff680d536a5f6fe862d36df6f8f6f40f115
|
||||
1: 6edddfc6349aff20bc6467ccf276c5b52487f7a8
|
||||
2: e6a3d25a7cb7cd21cb720df5b5e8afd154af1bbb
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
#### `import [--src DIR] [--dir DIR]`
|
||||
*Import accounts from src.*
|
||||
|
||||
- `[--src DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: geth
|
||||
- `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity
|
||||
|
||||
```
|
||||
ethstore import
|
||||
```
|
||||
|
||||
```
|
||||
0: e6a3d25a7cb7cd21cb720df5b5e8afd154af1bbb
|
||||
1: 6edddfc6349aff20bc6467ccf276c5b52487f7a8
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
#### `import-wallet <path> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]`
|
||||
*Import account from presale wallet.*
|
||||
|
||||
- `<path>` - presale wallet path
|
||||
- `<password>` - account password, file path
|
||||
- `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity
|
||||
- `[--vault VAULT]` - vault to use in this operation
|
||||
- `[--vault-pwd VAULTPWD]` - vault password to use in this operation, file path
|
||||
|
||||
```
|
||||
ethstore import-wallet ethwallet.json password.txt
|
||||
```
|
||||
|
||||
```
|
||||
e6a3d25a7cb7cd21cb720df5b5e8afd154af1bbb
|
||||
```
|
||||
|
||||
|
||||
--
|
||||
|
||||
#### `find-wallet-pass <path> <password>`
|
||||
Try to open presale wallet given a list of passwords from a file.
|
||||
The list of passwords can be generated using e.g. [Phildo/brutedist](https://github.com/Phildo/brutedist).
|
||||
|
||||
- `<path>` - presale wallet path
|
||||
- `<password>` - possible passwords, file path
|
||||
|
||||
```
|
||||
ethstore find-wallet-pass ethwallet.json passwords.txt
|
||||
```
|
||||
|
||||
```
|
||||
Found password: test
|
||||
```
|
||||
|
||||
|
||||
--
|
||||
|
||||
#### `remove <address> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]`
|
||||
*Remove account from secret store.*
|
||||
|
||||
- `<address>` - ethereum address, 20 bytes long
|
||||
- `<password>` - account password, file path
|
||||
- `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity
|
||||
- `[--vault VAULT]` - vault to use in this operation
|
||||
- `[--vault-pwd VAULTPWD]` - vault password to use in this operation, file path
|
||||
|
||||
```
|
||||
ethstore remove a8fa5dd30a87bb9e3288d604eb74949c515ab66e password.txt
|
||||
```
|
||||
|
||||
```
|
||||
true
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
#### `sign <address> <password> <message> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]`
|
||||
*Sign message with account's secret.*
|
||||
|
||||
- `<address>` - ethereum address, 20 bytes long
|
||||
- `<password>` - account password, file path
|
||||
- `<message>` - message to sign, 32 bytes long
|
||||
- `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity
|
||||
- `[--vault VAULT]` - vault to use in this operation
|
||||
- `[--vault-pwd VAULTPWD]` - vault password to use in this operation, file path
|
||||
|
||||
```
|
||||
ethstore sign 24edfff680d536a5f6fe862d36df6f8f6f40f115 password.txt 7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5
|
||||
```
|
||||
|
||||
```
|
||||
c6649f9555232d90ff716d7e552a744c5af771574425a74860e12f763479eb1b708c1f3a7dc0a0a7f7a81e0a0ca88c6deacf469222bb3d9c5bf0847f98bae54901
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
#### `public <address> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]`
|
||||
*Displays public key for an address.*
|
||||
|
||||
- `<address>` - ethereum address, 20 bytes long
|
||||
- `<password>` - account password, file path
|
||||
- `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity
|
||||
- `[--vault VAULT]` - vault to use in this operation
|
||||
- `[--vault-pwd VAULTPWD]` - vault password to use in this operation, file path
|
||||
|
||||
```
|
||||
ethstore public 00e63fdb87ceb815ec96ae185b8f7381a0b4a5ea account_password.txt --vault vault_name --vault-pwd vault_password.txt
|
||||
```
|
||||
|
||||
```
|
||||
0x84161d8c05a996a534efbec50f24485cfcc07458efaef749a1b22156d7836c903eeb39bf2df74676e702eacc4cfdde069e5fd86692b5ef6ef81ba906e9e77d82
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
#### `list-vaults [--dir DIR]`
|
||||
*List vaults.*
|
||||
|
||||
- `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity
|
||||
|
||||
```
|
||||
ethstore list-vaults
|
||||
```
|
||||
|
||||
```
|
||||
vault1
|
||||
vault2
|
||||
vault3
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
#### `create-vault <vault> <password> [--dir DIR]`
|
||||
*Create new vault.*
|
||||
|
||||
- `<vault>` - name of new vault. This can only contain letters, digits, whitespaces, dashes and underscores
|
||||
- `<password>` - vault password, file path
|
||||
- `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity
|
||||
|
||||
```
|
||||
ethstore create-vault vault3 vault3_password.txt
|
||||
```
|
||||
|
||||
```
|
||||
OK
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
#### `change-vault-pwd <vault> <old-pwd> <new-pwd> [--dir DIR]`
|
||||
*Change vault password.*
|
||||
|
||||
- `<vault>` - name of existing vault
|
||||
- `<old-pwd>` - old vault password, file path
|
||||
- `<new-pwd>` - new vault password, file path
|
||||
- `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity
|
||||
|
||||
```
|
||||
ethstore change-vault-pwd vault3 vault3_password.txt new_vault3_password.txt
|
||||
```
|
||||
|
||||
```
|
||||
OK
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
#### `move-to-vault <address> <vault> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]`
|
||||
*Move account to vault from another vault/root directory.*
|
||||
|
||||
- `<address>` - ethereum address, 20 bytes long
|
||||
- `<vault>` - name of existing vault to move account to
|
||||
- `<password>` - password of existing `<vault>` to move account to, file path
|
||||
- `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity
|
||||
- `[--vault VAULT]` - current vault of the `<address>` argument, if set
|
||||
- `[--vault-pwd VAULTPWD]` - password for the current vault of the `<address>` argument, if any. file path
|
||||
|
||||
|
||||
```
|
||||
ethstore move-to-vault 00e63fdb87ceb815ec96ae185b8f7381a0b4a5ea vault3 vault3_password.txt
|
||||
ethstore move-to-vault 00e63fdb87ceb815ec96ae185b8f7381a0b4a5ea vault1 vault1_password.txt --vault vault3 --vault-pwd vault3_password.txt
|
||||
```
|
||||
|
||||
```
|
||||
OK
|
||||
OK
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
#### `move-from-vault <address> <vault> <password> [--dir DIR]`
|
||||
*Move account to root directory from given vault.*
|
||||
|
||||
- `<address>` - ethereum address, 20 bytes long
|
||||
- `<vault>` - name of existing vault to move account to
|
||||
- `<password>` - password of existing `<vault>` to move account to, file path
|
||||
- `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity
|
||||
|
||||
|
||||
```
|
||||
ethstore move-from-vault 00e63fdb87ceb815ec96ae185b8f7381a0b4a5ea vault1 vault1_password.txt
|
||||
```
|
||||
|
||||
```
|
||||
OK
|
||||
```
|
||||
|
||||
## Parity Ethereum toolchain
|
||||
_This project is a part of the Parity Ethereum toolchain._
|
||||
|
||||
- [evmbin](https://github.com/paritytech/parity-ethereum/blob/master/evmbin/) - EVM implementation for Parity Ethereum.
|
||||
- [ethabi](https://github.com/paritytech/ethabi) - Parity Ethereum function calls encoding.
|
||||
- [ethstore](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethstore) - Parity Ethereum key management.
|
||||
- [ethkey](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethkey) - Parity Ethereum keys generator.
|
||||
- [whisper](https://github.com/paritytech/whisper) - Implementation of Whisper-v2 PoC.
|
||||
@@ -1,27 +0,0 @@
|
||||
[package]
|
||||
description = "Parity Ethereum Key Management CLI"
|
||||
name = "ethstore-cli"
|
||||
version = "0.1.1"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
docopt = "1.0"
|
||||
env_logger = "0.5"
|
||||
num_cpus = "1.6"
|
||||
rustc-hex = "1.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
parking_lot = "0.9"
|
||||
ethstore = { path = "../" }
|
||||
ethkey = { path = "../../ethkey" }
|
||||
parity-crypto = { version = "0.4.2", features = ["publickey"] }
|
||||
dir = { path = '../../../util/dir' }
|
||||
panic_hook = { path = "../../../util/panic-hook" }
|
||||
|
||||
[[bin]]
|
||||
name = "ethstore"
|
||||
path = "src/main.rs"
|
||||
doc = false
|
||||
|
||||
[dev-dependencies]
|
||||
tempdir = "0.3"
|
||||
@@ -1,67 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{cmp, thread};
|
||||
use std::sync::Arc;
|
||||
use std::collections::VecDeque;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use ethstore::{PresaleWallet, Error};
|
||||
use ethkey::Password;
|
||||
use num_cpus;
|
||||
|
||||
pub fn run(passwords: VecDeque<Password>, wallet_path: &str) -> Result<(), Error> {
|
||||
let passwords = Arc::new(Mutex::new(passwords));
|
||||
|
||||
let mut handles = Vec::new();
|
||||
|
||||
for _ in 0..num_cpus::get() {
|
||||
let passwords = passwords.clone();
|
||||
let wallet = PresaleWallet::open(&wallet_path)?;
|
||||
handles.push(thread::spawn(move || {
|
||||
look_for_password(passwords, wallet);
|
||||
}));
|
||||
}
|
||||
|
||||
for handle in handles {
|
||||
handle.join().map_err(|err| Error::Custom(format!("Error finishing thread: {:?}", err)))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn look_for_password(passwords: Arc<Mutex<VecDeque<Password>>>, wallet: PresaleWallet) {
|
||||
let mut counter = 0;
|
||||
while !passwords.lock().is_empty() {
|
||||
let package = {
|
||||
let mut passwords = passwords.lock();
|
||||
let len = passwords.len();
|
||||
passwords.split_off(cmp::min(len, 32))
|
||||
};
|
||||
for pass in package {
|
||||
counter += 1;
|
||||
match wallet.decrypt(&pass) {
|
||||
Ok(_) => {
|
||||
println!("Found password: {}", pass.as_str());
|
||||
passwords.lock().clear();
|
||||
return;
|
||||
},
|
||||
_ if counter % 100 == 0 => print!("."),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,320 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate dir;
|
||||
extern crate docopt;
|
||||
extern crate ethstore;
|
||||
extern crate ethkey;
|
||||
extern crate num_cpus;
|
||||
extern crate panic_hook;
|
||||
extern crate parking_lot;
|
||||
extern crate parity_crypto;
|
||||
extern crate rustc_hex;
|
||||
extern crate serde;
|
||||
|
||||
extern crate env_logger;
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::io::Read;
|
||||
use std::{env, process, fs, fmt};
|
||||
|
||||
use docopt::Docopt;
|
||||
use ethstore::accounts_dir::{KeyDirectory, RootDiskDirectory};
|
||||
use ethkey::Password;
|
||||
use parity_crypto::publickey::Address;
|
||||
use ethstore::{EthStore, SimpleSecretStore, SecretStore, import_accounts, PresaleWallet, SecretVaultRef, StoreAccountRef};
|
||||
|
||||
mod crack;
|
||||
|
||||
pub const USAGE: &'static str = r#"
|
||||
Parity Ethereum key management tool.
|
||||
Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
|
||||
Usage:
|
||||
ethstore insert <secret> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore change-pwd <address> <old-pwd> <new-pwd> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore list [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore import [<password>] [--src DIR] [--dir DIR]
|
||||
ethstore import-wallet <path> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore find-wallet-pass <path> <password>
|
||||
ethstore remove <address> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore sign <address> <password> <message> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore public <address> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore list-vaults [--dir DIR]
|
||||
ethstore create-vault <vault> <password> [--dir DIR]
|
||||
ethstore change-vault-pwd <vault> <old-pwd> <new-pwd> [--dir DIR]
|
||||
ethstore move-to-vault <address> <vault> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||
ethstore move-from-vault <address> <vault> <password> [--dir DIR]
|
||||
ethstore [-h | --help]
|
||||
|
||||
Options:
|
||||
-h, --help Display this message and exit.
|
||||
--dir DIR Specify the secret store directory. It may be either
|
||||
parity, parity-(chain), geth, geth-test
|
||||
or a path [default: parity].
|
||||
--vault VAULT Specify vault to use in this operation.
|
||||
--vault-pwd VAULTPWD Specify vault password to use in this operation. Please note
|
||||
that this option is required when vault option is set.
|
||||
Otherwise it is ignored.
|
||||
--src DIR Specify import source. It may be either
|
||||
parity, parity-(chain), geth, geth-test
|
||||
or a path [default: geth].
|
||||
|
||||
Commands:
|
||||
insert Save account with password.
|
||||
change-pwd Change password.
|
||||
list List accounts.
|
||||
import Import accounts from src.
|
||||
import-wallet Import presale wallet.
|
||||
find-wallet-pass Tries to open a wallet with list of passwords given.
|
||||
remove Remove account.
|
||||
sign Sign message.
|
||||
public Displays public key for an address.
|
||||
list-vaults List vaults.
|
||||
create-vault Create new vault.
|
||||
change-vault-pwd Change vault password.
|
||||
move-to-vault Move account to vault from another vault/root directory.
|
||||
move-from-vault Move account to root directory from given vault.
|
||||
"#;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Args {
|
||||
cmd_insert: bool,
|
||||
cmd_change_pwd: bool,
|
||||
cmd_list: bool,
|
||||
cmd_import: bool,
|
||||
cmd_import_wallet: bool,
|
||||
cmd_find_wallet_pass: bool,
|
||||
cmd_remove: bool,
|
||||
cmd_sign: bool,
|
||||
cmd_public: bool,
|
||||
cmd_list_vaults: bool,
|
||||
cmd_create_vault: bool,
|
||||
cmd_change_vault_pwd: bool,
|
||||
cmd_move_to_vault: bool,
|
||||
cmd_move_from_vault: bool,
|
||||
arg_secret: String,
|
||||
arg_password: String,
|
||||
arg_old_pwd: String,
|
||||
arg_new_pwd: String,
|
||||
arg_address: String,
|
||||
arg_message: String,
|
||||
arg_path: String,
|
||||
arg_vault: String,
|
||||
flag_src: String,
|
||||
flag_dir: String,
|
||||
flag_vault: String,
|
||||
flag_vault_pwd: String,
|
||||
}
|
||||
|
||||
enum Error {
|
||||
Ethstore(ethstore::Error),
|
||||
Docopt(docopt::Error),
|
||||
}
|
||||
|
||||
impl From<ethstore::Error> for Error {
|
||||
fn from(err: ethstore::Error) -> Self {
|
||||
Error::Ethstore(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<docopt::Error> for Error {
|
||||
fn from(err: docopt::Error) -> Self {
|
||||
Error::Docopt(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Error::Ethstore(ref err) => fmt::Display::fmt(err, f),
|
||||
Error::Docopt(ref err) => fmt::Display::fmt(err, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
panic_hook::set_abort();
|
||||
if env::var("RUST_LOG").is_err() {
|
||||
env::set_var("RUST_LOG", "warn")
|
||||
}
|
||||
env_logger::try_init().expect("Logger initialized only once.");
|
||||
|
||||
match execute(env::args()) {
|
||||
Ok(result) => println!("{}", result),
|
||||
Err(Error::Docopt(ref e)) => e.exit(),
|
||||
Err(err) => {
|
||||
eprintln!("{}", err);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn key_dir(location: &str, password: Option<Password>) -> Result<Box<dyn KeyDirectory>, Error> {
|
||||
let dir: RootDiskDirectory = match location {
|
||||
"geth" => RootDiskDirectory::create(dir::geth(false))?,
|
||||
"geth-test" => RootDiskDirectory::create(dir::geth(true))?,
|
||||
path if path.starts_with("parity") => {
|
||||
let chain = path.split('-').nth(1).unwrap_or("ethereum");
|
||||
let path = dir::parity(chain);
|
||||
RootDiskDirectory::create(path)?
|
||||
},
|
||||
path => RootDiskDirectory::create(path)?,
|
||||
};
|
||||
|
||||
Ok(Box::new(dir.with_password(password)))
|
||||
}
|
||||
|
||||
fn open_args_vault(store: &EthStore, args: &Args) -> Result<SecretVaultRef, Error> {
|
||||
if args.flag_vault.is_empty() {
|
||||
return Ok(SecretVaultRef::Root);
|
||||
}
|
||||
|
||||
let vault_pwd = load_password(&args.flag_vault_pwd)?;
|
||||
store.open_vault(&args.flag_vault, &vault_pwd)?;
|
||||
Ok(SecretVaultRef::Vault(args.flag_vault.clone()))
|
||||
}
|
||||
|
||||
fn open_args_vault_account(store: &EthStore, address: Address, args: &Args) -> Result<StoreAccountRef, Error> {
|
||||
match open_args_vault(store, args)? {
|
||||
SecretVaultRef::Root => Ok(StoreAccountRef::root(address)),
|
||||
SecretVaultRef::Vault(name) => Ok(StoreAccountRef::vault(&name, address)),
|
||||
}
|
||||
}
|
||||
|
||||
fn format_accounts(accounts: &[Address]) -> String {
|
||||
accounts.iter()
|
||||
.enumerate()
|
||||
.map(|(i, a)| format!("{:2}: 0x{:x}", i, a))
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
fn format_vaults(vaults: &[String]) -> String {
|
||||
vaults.join("\n")
|
||||
}
|
||||
|
||||
fn load_password(path: &str) -> Result<Password, Error> {
|
||||
let mut file = fs::File::open(path).map_err(|e| ethstore::Error::Custom(format!("Error opening password file '{}': {}", path, e)))?;
|
||||
let mut password = String::new();
|
||||
file.read_to_string(&mut password).map_err(|e| ethstore::Error::Custom(format!("Error reading password file '{}': {}", path, e)))?;
|
||||
// drop EOF
|
||||
let _ = password.pop();
|
||||
Ok(password.into())
|
||||
}
|
||||
|
||||
fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item=S>, S: AsRef<str> {
|
||||
let args: Args = Docopt::new(USAGE)
|
||||
.and_then(|d| d.argv(command).deserialize())?;
|
||||
|
||||
let store = EthStore::open(key_dir(&args.flag_dir, None)?)?;
|
||||
|
||||
return if args.cmd_insert {
|
||||
let secret = args.arg_secret.parse().map_err(|_| ethstore::Error::InvalidSecret)?;
|
||||
let password = load_password(&args.arg_password)?;
|
||||
let vault_ref = open_args_vault(&store, &args)?;
|
||||
let account_ref = store.insert_account(vault_ref, secret, &password)?;
|
||||
Ok(format!("0x{:x}", account_ref.address))
|
||||
} else if args.cmd_change_pwd {
|
||||
let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?;
|
||||
let old_pwd = load_password(&args.arg_old_pwd)?;
|
||||
let new_pwd = load_password(&args.arg_new_pwd)?;
|
||||
let account_ref = open_args_vault_account(&store, address, &args)?;
|
||||
let ok = store.change_password(&account_ref, &old_pwd, &new_pwd).is_ok();
|
||||
Ok(format!("{}", ok))
|
||||
} else if args.cmd_list {
|
||||
let vault_ref = open_args_vault(&store, &args)?;
|
||||
let accounts = store.accounts()?;
|
||||
let accounts: Vec<_> = accounts
|
||||
.into_iter()
|
||||
.filter(|a| &a.vault == &vault_ref)
|
||||
.map(|a| a.address)
|
||||
.collect();
|
||||
Ok(format_accounts(&accounts))
|
||||
} else if args.cmd_import {
|
||||
let password = match args.arg_password.as_ref() {
|
||||
"" => None,
|
||||
_ => Some(load_password(&args.arg_password)?)
|
||||
};
|
||||
let src = key_dir(&args.flag_src, password)?;
|
||||
let dst = key_dir(&args.flag_dir, None)?;
|
||||
|
||||
let accounts = import_accounts(&*src, &*dst)?;
|
||||
Ok(format_accounts(&accounts))
|
||||
} else if args.cmd_import_wallet {
|
||||
let wallet = PresaleWallet::open(&args.arg_path)?;
|
||||
let password = load_password(&args.arg_password)?;
|
||||
let kp = wallet.decrypt(&password)?;
|
||||
let vault_ref = open_args_vault(&store, &args)?;
|
||||
let account_ref = store.insert_account(vault_ref, kp.secret().clone(), &password)?;
|
||||
Ok(format!("0x{:x}", account_ref.address))
|
||||
} else if args.cmd_find_wallet_pass {
|
||||
let passwords = load_password(&args.arg_password)?;
|
||||
let passwords = passwords.as_str().lines().map(|line| str::to_owned(line).into()).collect::<VecDeque<_>>();
|
||||
crack::run(passwords, &args.arg_path)?;
|
||||
Ok(format!("Password not found."))
|
||||
} else if args.cmd_remove {
|
||||
let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?;
|
||||
let password = load_password(&args.arg_password)?;
|
||||
let account_ref = open_args_vault_account(&store, address, &args)?;
|
||||
let ok = store.remove_account(&account_ref, &password).is_ok();
|
||||
Ok(format!("{}", ok))
|
||||
} else if args.cmd_sign {
|
||||
let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?;
|
||||
let message = args.arg_message.parse().map_err(|_| ethstore::Error::InvalidMessage)?;
|
||||
let password = load_password(&args.arg_password)?;
|
||||
let account_ref = open_args_vault_account(&store, address, &args)?;
|
||||
let signature = store.sign(&account_ref, &password, &message)?;
|
||||
Ok(format!("0x{}", signature))
|
||||
} else if args.cmd_public {
|
||||
let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?;
|
||||
let password = load_password(&args.arg_password)?;
|
||||
let account_ref = open_args_vault_account(&store, address, &args)?;
|
||||
let public = store.public(&account_ref, &password)?;
|
||||
Ok(format!("0x{:x}", public))
|
||||
} else if args.cmd_list_vaults {
|
||||
let vaults = store.list_vaults()?;
|
||||
Ok(format_vaults(&vaults))
|
||||
} else if args.cmd_create_vault {
|
||||
let password = load_password(&args.arg_password)?;
|
||||
store.create_vault(&args.arg_vault, &password)?;
|
||||
Ok("OK".to_owned())
|
||||
} else if args.cmd_change_vault_pwd {
|
||||
let old_pwd = load_password(&args.arg_old_pwd)?;
|
||||
let new_pwd = load_password(&args.arg_new_pwd)?;
|
||||
store.open_vault(&args.arg_vault, &old_pwd)?;
|
||||
store.change_vault_password(&args.arg_vault, &new_pwd)?;
|
||||
Ok("OK".to_owned())
|
||||
} else if args.cmd_move_to_vault {
|
||||
let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?;
|
||||
let password = load_password(&args.arg_password)?;
|
||||
let account_ref = open_args_vault_account(&store, address, &args)?;
|
||||
store.open_vault(&args.arg_vault, &password)?;
|
||||
store.change_account_vault(SecretVaultRef::Vault(args.arg_vault), account_ref)?;
|
||||
Ok("OK".to_owned())
|
||||
} else if args.cmd_move_from_vault {
|
||||
let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?;
|
||||
let password = load_password(&args.arg_password)?;
|
||||
store.open_vault(&args.arg_vault, &password)?;
|
||||
store.change_account_vault(SecretVaultRef::Root, StoreAccountRef::vault(&args.arg_vault, address))?;
|
||||
Ok("OK".to_owned())
|
||||
} else {
|
||||
Ok(format!("{}", USAGE))
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate tempdir;
|
||||
use std::process::Command;
|
||||
use tempdir::TempDir;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
fn run(args: &[&str]) -> String {
|
||||
let output = Command::new("cargo")
|
||||
.args(&["run", "--"])
|
||||
.args(args)
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success());
|
||||
String::from_utf8(output.stdout).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli_cmd() {
|
||||
Command::new("cargo")
|
||||
.arg("build")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
let dir = TempDir::new("test-vault").unwrap();
|
||||
|
||||
let mut passwd = File::create(dir.path().join("test-password")).unwrap();
|
||||
writeln!(passwd, "password").unwrap();
|
||||
|
||||
let mut passwd2 = File::create(dir.path().join("test-vault-addr")).unwrap();
|
||||
writeln!(passwd2, "password2").unwrap();
|
||||
|
||||
let test_password_buf = dir.path().join("test-password");
|
||||
let test_password: &str = test_password_buf.to_str().unwrap();
|
||||
let dir_str: &str = dir.path().to_str().unwrap();
|
||||
let test_vault_addr_buf = dir.path().join("test-vault-addr");
|
||||
let test_vault_addr = test_vault_addr_buf.to_str().unwrap();
|
||||
|
||||
run(&["create-vault", "test-vault", test_password, "--dir", dir_str]);
|
||||
|
||||
let output = run(&["insert", "7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5",
|
||||
test_vault_addr,
|
||||
"--dir", dir_str,
|
||||
"--vault", "test-vault",
|
||||
"--vault-pwd", test_password]);
|
||||
let address = output.trim();
|
||||
|
||||
let output = run(&["list",
|
||||
"--dir", dir_str,
|
||||
"--vault", "test-vault",
|
||||
"--vault-pwd", test_password]);
|
||||
assert_eq!(output, " 0: 0xa8fa5dd30a87bb9e3288d604eb74949c515ab66e\n");
|
||||
|
||||
let output = run(&["sign", &address[2..],
|
||||
test_vault_addr,
|
||||
"7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5",
|
||||
"--dir", dir_str,
|
||||
"--vault", "test-vault",
|
||||
"--vault-pwd", test_password]);
|
||||
assert_eq!(output, "0x54ab6e5cf0c5cb40043fdca5d15d611a3a94285414a076dafecc8dc9c04183f413296a3defff61092c0bb478dc9887ec01070e1275234211208fb8f4be4a9b0101\n");
|
||||
|
||||
let output = run(&["public", &address[2..], test_vault_addr,
|
||||
"--dir", dir_str,
|
||||
"--vault", "test-vault",
|
||||
"--vault-pwd", test_password]);
|
||||
assert_eq!(output, "0x35f222d88b80151857a2877826d940104887376a94c1cbd2c8c7c192eb701df88a18a4ecb8b05b1466c5b3706042027b5e079fe3a3683e66d822b0e047aa3418\n");
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use json;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Aes128Ctr {
|
||||
pub iv: [u8; 16],
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Cipher {
|
||||
Aes128Ctr(Aes128Ctr),
|
||||
}
|
||||
|
||||
impl From<json::Aes128Ctr> for Aes128Ctr {
|
||||
fn from(json: json::Aes128Ctr) -> Self {
|
||||
Aes128Ctr {
|
||||
iv: json.iv.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<json::Aes128Ctr> for Aes128Ctr {
|
||||
fn into(self) -> json::Aes128Ctr {
|
||||
json::Aes128Ctr {
|
||||
iv: From::from(self.iv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<json::Cipher> for Cipher {
|
||||
fn from(json: json::Cipher) -> Self {
|
||||
match json {
|
||||
json::Cipher::Aes128Ctr(params) => Cipher::Aes128Ctr(From::from(params)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<json::Cipher> for Cipher {
|
||||
fn into(self) -> json::Cipher {
|
||||
match self {
|
||||
Cipher::Aes128Ctr(params) => json::Cipher::Aes128Ctr(params.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,207 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::str;
|
||||
use crypto::publickey::Secret;
|
||||
use ethkey::Password;
|
||||
use {json, Error, crypto};
|
||||
use crypto::Keccak256;
|
||||
use random::Random;
|
||||
use smallvec::SmallVec;
|
||||
use account::{Cipher, Kdf, Aes128Ctr, Pbkdf2, Prf};
|
||||
|
||||
/// Encrypted data
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Crypto {
|
||||
/// Encryption parameters
|
||||
pub cipher: Cipher,
|
||||
/// Encrypted data buffer
|
||||
pub ciphertext: Vec<u8>,
|
||||
/// Key derivation function parameters
|
||||
pub kdf: Kdf,
|
||||
/// Message authentication code
|
||||
pub mac: [u8; 32],
|
||||
}
|
||||
|
||||
impl From<json::Crypto> for Crypto {
|
||||
fn from(json: json::Crypto) -> Self {
|
||||
Crypto {
|
||||
cipher: json.cipher.into(),
|
||||
ciphertext: json.ciphertext.into(),
|
||||
kdf: json.kdf.into(),
|
||||
mac: json.mac.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Crypto> for json::Crypto {
|
||||
fn from(c: Crypto) -> Self {
|
||||
json::Crypto {
|
||||
cipher: c.cipher.into(),
|
||||
ciphertext: c.ciphertext.into(),
|
||||
kdf: c.kdf.into(),
|
||||
mac: c.mac.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl str::FromStr for Crypto {
|
||||
type Err = <json::Crypto as str::FromStr>::Err;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
s.parse::<json::Crypto>().map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Crypto> for String {
|
||||
fn from(c: Crypto) -> Self {
|
||||
json::Crypto::from(c).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Crypto {
|
||||
/// Encrypt account secret
|
||||
pub fn with_secret(secret: &Secret, password: &Password, iterations: u32) -> Result<Self, crypto::Error> {
|
||||
Crypto::with_plain(secret.as_ref(), password, iterations)
|
||||
}
|
||||
|
||||
/// Encrypt custom plain data
|
||||
pub fn with_plain(plain: &[u8], password: &Password, iterations: u32) -> Result<Self, crypto::Error> {
|
||||
let salt: [u8; 32] = Random::random();
|
||||
let iv: [u8; 16] = Random::random();
|
||||
|
||||
// two parts of derived key
|
||||
// DK = [ DK[0..15] DK[16..31] ] = [derived_left_bits, derived_right_bits]
|
||||
let (derived_left_bits, derived_right_bits) =
|
||||
crypto::derive_key_iterations(password.as_bytes(), &salt, iterations);
|
||||
|
||||
// preallocated (on-stack in case of `Secret`) buffer to hold cipher
|
||||
// length = length(plain) as we are using CTR-approach
|
||||
let plain_len = plain.len();
|
||||
let mut ciphertext: SmallVec<[u8; 32]> = SmallVec::from_vec(vec![0; plain_len]);
|
||||
|
||||
// aes-128-ctr with initial vector of iv
|
||||
crypto::aes::encrypt_128_ctr(&derived_left_bits, &iv, plain, &mut *ciphertext)?;
|
||||
|
||||
// KECCAK(DK[16..31] ++ <ciphertext>), where DK[16..31] - derived_right_bits
|
||||
let mac = crypto::derive_mac(&derived_right_bits, &*ciphertext).keccak256();
|
||||
|
||||
Ok(Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||
iv: iv,
|
||||
}),
|
||||
ciphertext: ciphertext.into_vec(),
|
||||
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
||||
dklen: crypto::KEY_LENGTH as u32,
|
||||
salt: salt.to_vec(),
|
||||
c: iterations,
|
||||
prf: Prf::HmacSha256,
|
||||
}),
|
||||
mac: mac,
|
||||
})
|
||||
}
|
||||
|
||||
/// Try to decrypt and convert result to account secret
|
||||
pub fn secret(&self, password: &Password) -> Result<Secret, Error> {
|
||||
if self.ciphertext.len() > 32 {
|
||||
return Err(Error::InvalidSecret);
|
||||
}
|
||||
|
||||
let secret = self.do_decrypt(password, 32)?;
|
||||
Ok(Secret::import_key(&secret)?)
|
||||
}
|
||||
|
||||
/// Try to decrypt and return result as is
|
||||
pub fn decrypt(&self, password: &Password) -> Result<Vec<u8>, Error> {
|
||||
let expected_len = self.ciphertext.len();
|
||||
self.do_decrypt(password, expected_len)
|
||||
}
|
||||
|
||||
fn do_decrypt(&self, password: &Password, expected_len: usize) -> Result<Vec<u8>, Error> {
|
||||
let (derived_left_bits, derived_right_bits) = match self.kdf {
|
||||
Kdf::Pbkdf2(ref params) => crypto::derive_key_iterations(password.as_bytes(), ¶ms.salt, params.c),
|
||||
Kdf::Scrypt(ref params) => crypto::scrypt::derive_key(password.as_bytes(), ¶ms.salt, params.n, params.p, params.r)?,
|
||||
};
|
||||
|
||||
let mac = crypto::derive_mac(&derived_right_bits, &self.ciphertext).keccak256();
|
||||
|
||||
if !crypto::is_equal(&mac, &self.mac) {
|
||||
return Err(Error::InvalidPassword)
|
||||
}
|
||||
|
||||
let mut plain: SmallVec<[u8; 32]> = SmallVec::from_vec(vec![0; expected_len]);
|
||||
|
||||
match self.cipher {
|
||||
Cipher::Aes128Ctr(ref params) => {
|
||||
// checker by callers
|
||||
debug_assert!(expected_len >= self.ciphertext.len());
|
||||
|
||||
let from = expected_len - self.ciphertext.len();
|
||||
crypto::aes::decrypt_128_ctr(&derived_left_bits, ¶ms.iv, &self.ciphertext, &mut plain[from..])?;
|
||||
Ok(plain.into_iter().collect())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crypto::publickey::{Generator, Random};
|
||||
use super::{Crypto, Error};
|
||||
|
||||
#[test]
|
||||
fn crypto_with_secret_create() {
|
||||
let keypair = Random.generate().unwrap();
|
||||
let passwd = "this is sparta".into();
|
||||
let crypto = Crypto::with_secret(keypair.secret(), &passwd, 10240).unwrap();
|
||||
let secret = crypto.secret(&passwd).unwrap();
|
||||
assert_eq!(keypair.secret(), &secret);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crypto_with_secret_invalid_password() {
|
||||
let keypair = Random.generate().unwrap();
|
||||
let crypto = Crypto::with_secret(keypair.secret(), &"this is sparta".into(), 10240).unwrap();
|
||||
assert_matches!(crypto.secret(&"this is sparta!".into()), Err(Error::InvalidPassword))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crypto_with_null_plain_data() {
|
||||
let original_data = b"";
|
||||
let passwd = "this is sparta".into();
|
||||
let crypto = Crypto::with_plain(&original_data[..], &passwd, 10240).unwrap();
|
||||
let decrypted_data = crypto.decrypt(&passwd).unwrap();
|
||||
assert_eq!(original_data[..], *decrypted_data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crypto_with_tiny_plain_data() {
|
||||
let original_data = b"{}";
|
||||
let passwd = "this is sparta".into();
|
||||
let crypto = Crypto::with_plain(&original_data[..], &passwd, 10240).unwrap();
|
||||
let decrypted_data = crypto.decrypt(&passwd).unwrap();
|
||||
assert_eq!(original_data[..], *decrypted_data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crypto_with_huge_plain_data() {
|
||||
let original_data: Vec<_> = (1..65536).map(|i| (i % 256) as u8).collect();
|
||||
let passwd = "this is sparta".into();
|
||||
let crypto = Crypto::with_plain(&original_data, &passwd, 10240).unwrap();
|
||||
let decrypted_data = crypto.decrypt(&passwd).unwrap();
|
||||
assert_eq!(&original_data, &decrypted_data);
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use json;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Prf {
|
||||
HmacSha256,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Pbkdf2 {
|
||||
pub c: u32,
|
||||
pub dklen: u32,
|
||||
pub prf: Prf,
|
||||
pub salt: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Scrypt {
|
||||
pub dklen: u32,
|
||||
pub p: u32,
|
||||
pub n: u32,
|
||||
pub r: u32,
|
||||
pub salt: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Kdf {
|
||||
Pbkdf2(Pbkdf2),
|
||||
Scrypt(Scrypt),
|
||||
}
|
||||
|
||||
impl From<json::Prf> for Prf {
|
||||
fn from(json: json::Prf) -> Self {
|
||||
match json {
|
||||
json::Prf::HmacSha256 => Prf::HmacSha256,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<json::Prf> for Prf {
|
||||
fn into(self) -> json::Prf {
|
||||
match self {
|
||||
Prf::HmacSha256 => json::Prf::HmacSha256,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<json::Pbkdf2> for Pbkdf2 {
|
||||
fn from(json: json::Pbkdf2) -> Self {
|
||||
Pbkdf2 {
|
||||
c: json.c,
|
||||
dklen: json.dklen,
|
||||
prf: From::from(json.prf),
|
||||
salt: json.salt.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<json::Pbkdf2> for Pbkdf2 {
|
||||
fn into(self) -> json::Pbkdf2 {
|
||||
json::Pbkdf2 {
|
||||
c: self.c,
|
||||
dklen: self.dklen,
|
||||
prf: self.prf.into(),
|
||||
salt: From::from(self.salt),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<json::Scrypt> for Scrypt {
|
||||
fn from(json: json::Scrypt) -> Self {
|
||||
Scrypt {
|
||||
dklen: json.dklen,
|
||||
p: json.p,
|
||||
n: json.n,
|
||||
r: json.r,
|
||||
salt: json.salt.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<json::Scrypt> for Scrypt {
|
||||
fn into(self) -> json::Scrypt {
|
||||
json::Scrypt {
|
||||
dklen: self.dklen,
|
||||
p: self.p,
|
||||
n: self.n,
|
||||
r: self.r,
|
||||
salt: From::from(self.salt),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<json::Kdf> for Kdf {
|
||||
fn from(json: json::Kdf) -> Self {
|
||||
match json {
|
||||
json::Kdf::Pbkdf2(params) => Kdf::Pbkdf2(From::from(params)),
|
||||
json::Kdf::Scrypt(params) => Kdf::Scrypt(From::from(params)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<json::Kdf> for Kdf {
|
||||
fn into(self) -> json::Kdf {
|
||||
match self {
|
||||
Kdf::Pbkdf2(params) => json::Kdf::Pbkdf2(params.into()),
|
||||
Kdf::Scrypt(params) => json::Kdf::Scrypt(params.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
mod cipher;
|
||||
mod crypto;
|
||||
mod kdf;
|
||||
mod safe_account;
|
||||
mod version;
|
||||
|
||||
pub use self::cipher::{Cipher, Aes128Ctr};
|
||||
pub use self::crypto::Crypto;
|
||||
pub use self::kdf::{Kdf, Pbkdf2, Scrypt, Prf};
|
||||
pub use self::safe_account::SafeAccount;
|
||||
pub use self::version::Version;
|
||||
@@ -1,230 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crypto::publickey::{KeyPair, sign, Address, Signature, Message, Public, Secret};
|
||||
use ethkey::Password;
|
||||
use crypto::publickey::ecdh::agree;
|
||||
use {json, Error};
|
||||
use account::Version;
|
||||
use crypto;
|
||||
use super::crypto::Crypto;
|
||||
|
||||
/// Account representation.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct SafeAccount {
|
||||
/// Account ID
|
||||
pub id: [u8; 16],
|
||||
/// Account version
|
||||
pub version: Version,
|
||||
/// Account address
|
||||
pub address: Address,
|
||||
/// Account private key derivation definition.
|
||||
pub crypto: Crypto,
|
||||
/// Account filename
|
||||
pub filename: Option<String>,
|
||||
/// Account name
|
||||
pub name: String,
|
||||
/// Account metadata
|
||||
pub meta: String,
|
||||
}
|
||||
|
||||
impl Into<json::KeyFile> for SafeAccount {
|
||||
fn into(self) -> json::KeyFile {
|
||||
json::KeyFile {
|
||||
id: From::from(self.id),
|
||||
version: self.version.into(),
|
||||
address: Some(self.address.into()),
|
||||
crypto: self.crypto.into(),
|
||||
name: Some(self.name.into()),
|
||||
meta: Some(self.meta.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SafeAccount {
|
||||
/// Create a new account
|
||||
pub fn create(
|
||||
keypair: &KeyPair,
|
||||
id: [u8; 16],
|
||||
password: &Password,
|
||||
iterations: u32,
|
||||
name: String,
|
||||
meta: String
|
||||
) -> Result<Self, crypto::Error> {
|
||||
Ok(SafeAccount {
|
||||
id: id,
|
||||
version: Version::V3,
|
||||
crypto: Crypto::with_secret(keypair.secret(), password, iterations)?,
|
||||
address: keypair.address(),
|
||||
filename: None,
|
||||
name: name,
|
||||
meta: meta,
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a new `SafeAccount` from the given `json`; if it was read from a
|
||||
/// file, the `filename` should be `Some` name. If it is as yet anonymous, then it
|
||||
/// can be left `None`.
|
||||
/// In case `password` is provided, we will attempt to read the secret from the keyfile
|
||||
/// and derive the address from it instead of reading it directly.
|
||||
/// Providing password is required for `json::KeyFile`s with no address.
|
||||
pub fn from_file(json: json::KeyFile, filename: Option<String>, password: &Option<Password>) -> Result<Self, Error> {
|
||||
let crypto = Crypto::from(json.crypto);
|
||||
let address = match (password, &json.address) {
|
||||
(None, Some(json_address)) => json_address.into(),
|
||||
(None, None) => Err(Error::Custom(
|
||||
"This keystore does not contain address. You need to provide password to import it".into()))?,
|
||||
(Some(password), json_address) => {
|
||||
let derived_address = KeyPair::from_secret(
|
||||
crypto.secret(&password).map_err(|_| Error::InvalidPassword)?
|
||||
)?.address();
|
||||
|
||||
match json_address {
|
||||
Some(json_address) => {
|
||||
let json_address = json_address.into();
|
||||
if derived_address != json_address {
|
||||
warn!("Detected address mismatch when opening an account. Derived: {:?}, in json got: {:?}",
|
||||
derived_address, json_address);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
derived_address
|
||||
}
|
||||
};
|
||||
|
||||
Ok(SafeAccount {
|
||||
id: json.id.into(),
|
||||
version: json.version.into(),
|
||||
address,
|
||||
crypto,
|
||||
filename,
|
||||
name: json.name.unwrap_or(String::new()),
|
||||
meta: json.meta.unwrap_or("{}".to_owned()),
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a new `SafeAccount` from the given vault `json`; if it was read from a
|
||||
/// file, the `filename` should be `Some` name. If it is as yet anonymous, then it
|
||||
/// can be left `None`.
|
||||
pub fn from_vault_file(password: &Password, json: json::VaultKeyFile, filename: Option<String>) -> Result<Self, Error> {
|
||||
let meta_crypto: Crypto = json.metacrypto.into();
|
||||
let meta_plain = meta_crypto.decrypt(password)?;
|
||||
let meta_plain = json::VaultKeyMeta::load(&meta_plain).map_err(|e| Error::Custom(format!("{:?}", e)))?;
|
||||
|
||||
SafeAccount::from_file(json::KeyFile {
|
||||
id: json.id,
|
||||
version: json.version,
|
||||
crypto: json.crypto,
|
||||
address: Some(meta_plain.address),
|
||||
name: meta_plain.name,
|
||||
meta: meta_plain.meta,
|
||||
}, filename, &None)
|
||||
}
|
||||
|
||||
/// Create a new `VaultKeyFile` from the given `self`
|
||||
pub fn into_vault_file(self, iterations: u32, password: &Password) -> Result<json::VaultKeyFile, Error> {
|
||||
let meta_plain = json::VaultKeyMeta {
|
||||
address: self.address.into(),
|
||||
name: Some(self.name),
|
||||
meta: Some(self.meta),
|
||||
};
|
||||
let meta_plain = meta_plain.write().map_err(|e| Error::Custom(format!("{:?}", e)))?;
|
||||
let meta_crypto = Crypto::with_plain(&meta_plain, password, iterations)?;
|
||||
|
||||
Ok(json::VaultKeyFile {
|
||||
id: self.id.into(),
|
||||
version: self.version.into(),
|
||||
crypto: self.crypto.into(),
|
||||
metacrypto: meta_crypto.into(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Sign a message.
|
||||
pub fn sign(&self, password: &Password, message: &Message) -> Result<Signature, Error> {
|
||||
let secret = self.crypto.secret(password)?;
|
||||
sign(&secret, message).map_err(From::from)
|
||||
}
|
||||
|
||||
/// Decrypt a message.
|
||||
pub fn decrypt(&self, password: &Password, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
let secret = self.crypto.secret(password)?;
|
||||
crypto::publickey::ecies::decrypt(&secret, shared_mac, message).map_err(From::from)
|
||||
}
|
||||
|
||||
/// Agree on shared key.
|
||||
pub fn agree(&self, password: &Password, other: &Public) -> Result<Secret, Error> {
|
||||
let secret = self.crypto.secret(password)?;
|
||||
agree(&secret, other).map_err(From::from)
|
||||
}
|
||||
|
||||
/// Derive public key.
|
||||
pub fn public(&self, password: &Password) -> Result<Public, Error> {
|
||||
let secret = self.crypto.secret(password)?;
|
||||
Ok(KeyPair::from_secret(secret)?.public().clone())
|
||||
}
|
||||
|
||||
/// Change account's password.
|
||||
pub fn change_password(&self, old_password: &Password, new_password: &Password, iterations: u32) -> Result<Self, Error> {
|
||||
let secret = self.crypto.secret(old_password)?;
|
||||
let result = SafeAccount {
|
||||
id: self.id.clone(),
|
||||
version: self.version.clone(),
|
||||
crypto: Crypto::with_secret(&secret, new_password, iterations)?,
|
||||
address: self.address.clone(),
|
||||
filename: self.filename.clone(),
|
||||
name: self.name.clone(),
|
||||
meta: self.meta.clone(),
|
||||
};
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Check if password matches the account.
|
||||
pub fn check_password(&self, password: &Password) -> bool {
|
||||
self.crypto.secret(password).is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crypto::publickey::{Generator, Random, verify_public, Message};
|
||||
use super::SafeAccount;
|
||||
|
||||
#[test]
|
||||
fn sign_and_verify_public() {
|
||||
let keypair = Random.generate().unwrap();
|
||||
let password = "hello world".into();
|
||||
let message = Message::default();
|
||||
let account = SafeAccount::create(&keypair, [0u8; 16], &password, 10240, "Test".to_owned(), "{}".to_owned());
|
||||
let signature = account.unwrap().sign(&password, &message).unwrap();
|
||||
assert!(verify_public(keypair.public(), &signature, &message).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_password() {
|
||||
let keypair = Random.generate().unwrap();
|
||||
let first_password = "hello world".into();
|
||||
let sec_password = "this is sparta".into();
|
||||
let i = 10240;
|
||||
let message = Message::default();
|
||||
let account = SafeAccount::create(&keypair, [0u8; 16], &first_password, i, "Test".to_owned(), "{}".to_owned()).unwrap();
|
||||
let new_account = account.change_password(&first_password, &sec_password, i).unwrap();
|
||||
assert!(account.sign(&first_password, &message).is_ok());
|
||||
assert!(account.sign(&sec_password, &message).is_err());
|
||||
assert!(new_account.sign(&first_password, &message).is_err());
|
||||
assert!(new_account.sign(&sec_password, &message).is_ok());
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use json;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Version {
|
||||
V3,
|
||||
}
|
||||
|
||||
impl From<json::Version> for Version {
|
||||
fn from(json: json::Version) -> Self {
|
||||
match json {
|
||||
json::Version::V3 => Version::V3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<json::Version> for Version {
|
||||
fn into(self) -> json::Version {
|
||||
match self {
|
||||
Version::V3 => json::Version::V3,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,484 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{fs, io};
|
||||
use std::io::Write;
|
||||
use std::path::{PathBuf, Path};
|
||||
use std::collections::HashMap;
|
||||
use time;
|
||||
use {json, SafeAccount, Error};
|
||||
use json::Uuid;
|
||||
use super::{KeyDirectory, VaultKeyDirectory, VaultKeyDirectoryProvider, VaultKey};
|
||||
use super::vault::{VAULT_FILE_NAME, VaultDiskDirectory};
|
||||
use ethkey::Password;
|
||||
|
||||
const IGNORED_FILES: &'static [&'static str] = &[
|
||||
"thumbs.db",
|
||||
"address_book.json",
|
||||
"dapps_policy.json",
|
||||
"dapps_accounts.json",
|
||||
"dapps_history.json",
|
||||
"vault.json",
|
||||
];
|
||||
|
||||
/// Find a unique filename that does not exist using four-letter random suffix.
|
||||
pub fn find_unique_filename_using_random_suffix(parent_path: &Path, original_filename: &str) -> io::Result<String> {
|
||||
let mut path = parent_path.join(original_filename);
|
||||
let mut deduped_filename = original_filename.to_string();
|
||||
|
||||
if path.exists() {
|
||||
const MAX_RETRIES: usize = 500;
|
||||
let mut retries = 0;
|
||||
|
||||
while path.exists() {
|
||||
if retries >= MAX_RETRIES {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "Exceeded maximum retries when deduplicating filename."));
|
||||
}
|
||||
|
||||
let suffix = ::random::random_string(4);
|
||||
deduped_filename = format!("{}-{}", original_filename, suffix);
|
||||
path.set_file_name(&deduped_filename);
|
||||
retries += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(deduped_filename)
|
||||
}
|
||||
|
||||
/// Create a new file and restrict permissions to owner only. It errors if the file already exists.
|
||||
#[cfg(unix)]
|
||||
pub fn create_new_file_with_permissions_to_owner(file_path: &Path) -> io::Result<fs::File> {
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
|
||||
fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.mode((libc::S_IWUSR | libc::S_IRUSR) as u32)
|
||||
.open(file_path)
|
||||
}
|
||||
|
||||
/// Create a new file and restrict permissions to owner only. It errors if the file already exists.
|
||||
#[cfg(not(unix))]
|
||||
pub fn create_new_file_with_permissions_to_owner(file_path: &Path) -> io::Result<fs::File> {
|
||||
fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(file_path)
|
||||
}
|
||||
|
||||
/// Create a new file and restrict permissions to owner only. It replaces the existing file if it already exists.
|
||||
#[cfg(unix)]
|
||||
pub fn replace_file_with_permissions_to_owner(file_path: &Path) -> io::Result<fs::File> {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let file = fs::File::create(file_path)?;
|
||||
let mut permissions = file.metadata()?.permissions();
|
||||
permissions.set_mode((libc::S_IWUSR | libc::S_IRUSR) as u32);
|
||||
file.set_permissions(permissions)?;
|
||||
|
||||
Ok(file)
|
||||
}
|
||||
|
||||
/// Create a new file and restrict permissions to owner only. It replaces the existing file if it already exists.
|
||||
#[cfg(not(unix))]
|
||||
pub fn replace_file_with_permissions_to_owner(file_path: &Path) -> io::Result<fs::File> {
|
||||
fs::File::create(file_path)
|
||||
}
|
||||
|
||||
/// Root keys directory implementation
|
||||
pub type RootDiskDirectory = DiskDirectory<DiskKeyFileManager>;
|
||||
|
||||
/// Disk directory key file manager
|
||||
pub trait KeyFileManager: Send + Sync {
|
||||
/// Read `SafeAccount` from given key file stream
|
||||
fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error> where T: io::Read;
|
||||
|
||||
/// Write `SafeAccount` to given key file stream
|
||||
fn write<T>(&self, account: SafeAccount, writer: &mut T) -> Result<(), Error> where T: io::Write;
|
||||
}
|
||||
|
||||
/// Disk-based keys directory implementation
|
||||
pub struct DiskDirectory<T> where T: KeyFileManager {
|
||||
path: PathBuf,
|
||||
key_manager: T,
|
||||
}
|
||||
|
||||
/// Keys file manager for root keys directory
|
||||
#[derive(Default)]
|
||||
pub struct DiskKeyFileManager {
|
||||
password: Option<Password>,
|
||||
}
|
||||
|
||||
impl RootDiskDirectory {
|
||||
pub fn create<P>(path: P) -> Result<Self, Error> where P: AsRef<Path> {
|
||||
fs::create_dir_all(&path)?;
|
||||
Ok(Self::at(path))
|
||||
}
|
||||
|
||||
/// allows to read keyfiles with given password (needed for keyfiles w/o address)
|
||||
pub fn with_password(&self, password: Option<Password>) -> Self {
|
||||
DiskDirectory::new(&self.path, DiskKeyFileManager { password })
|
||||
}
|
||||
|
||||
pub fn at<P>(path: P) -> Self where P: AsRef<Path> {
|
||||
DiskDirectory::new(path, DiskKeyFileManager::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DiskDirectory<T> where T: KeyFileManager {
|
||||
/// Create new disk directory instance
|
||||
pub fn new<P>(path: P, key_manager: T) -> Self where P: AsRef<Path> {
|
||||
DiskDirectory {
|
||||
path: path.as_ref().to_path_buf(),
|
||||
key_manager: key_manager,
|
||||
}
|
||||
}
|
||||
|
||||
fn files(&self) -> Result<Vec<PathBuf>, Error> {
|
||||
Ok(fs::read_dir(&self.path)?
|
||||
.flat_map(Result::ok)
|
||||
.filter(|entry| {
|
||||
let metadata = entry.metadata().ok();
|
||||
let file_name = entry.file_name();
|
||||
let name = file_name.to_string_lossy();
|
||||
// filter directories
|
||||
metadata.map_or(false, |m| !m.is_dir()) &&
|
||||
// hidden files
|
||||
!name.starts_with(".") &&
|
||||
// other ignored files
|
||||
!IGNORED_FILES.contains(&&*name)
|
||||
})
|
||||
.map(|entry| entry.path())
|
||||
.collect::<Vec<PathBuf>>()
|
||||
)
|
||||
}
|
||||
|
||||
pub fn files_hash(&self) -> Result<u64, Error> {
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::Hasher;
|
||||
|
||||
let mut hasher = DefaultHasher::new();
|
||||
let files = self.files()?;
|
||||
for file in files {
|
||||
hasher.write(file.to_str().unwrap_or("").as_bytes())
|
||||
}
|
||||
|
||||
Ok(hasher.finish())
|
||||
}
|
||||
|
||||
fn last_modification_date(&self) -> Result<u64, Error> {
|
||||
use std::time::{Duration, UNIX_EPOCH};
|
||||
let duration = fs::metadata(&self.path)?.modified()?.duration_since(UNIX_EPOCH).unwrap_or(Duration::default());
|
||||
let timestamp = duration.as_secs() ^ (duration.subsec_nanos() as u64);
|
||||
Ok(timestamp)
|
||||
}
|
||||
|
||||
/// all accounts found in keys directory
|
||||
fn files_content(&self) -> Result<HashMap<PathBuf, SafeAccount>, Error> {
|
||||
// it's not done using one iterator cause
|
||||
// there is an issue with rustc and it takes tooo much time to compile
|
||||
let paths = self.files()?;
|
||||
Ok(paths
|
||||
.into_iter()
|
||||
.filter_map(|path| {
|
||||
let filename = Some(path.file_name().and_then(|n| n.to_str()).expect("Keys have valid UTF8 names only.").to_owned());
|
||||
fs::File::open(path.clone())
|
||||
.map_err(Into::into)
|
||||
.and_then(|file| self.key_manager.read(filename, file))
|
||||
.map_err(|err| {
|
||||
warn!("Invalid key file: {:?} ({})", path, err);
|
||||
err
|
||||
})
|
||||
.map(|account| (path, account))
|
||||
.ok()
|
||||
})
|
||||
.collect()
|
||||
)
|
||||
}
|
||||
|
||||
/// insert account with given filename. if the filename is a duplicate of any stored account and dedup is set to
|
||||
/// true, a random suffix is appended to the filename.
|
||||
pub fn insert_with_filename(&self, account: SafeAccount, mut filename: String, dedup: bool) -> Result<SafeAccount, Error> {
|
||||
if dedup {
|
||||
filename = find_unique_filename_using_random_suffix(&self.path, &filename)?;
|
||||
}
|
||||
|
||||
// path to keyfile
|
||||
let keyfile_path = self.path.join(filename.as_str());
|
||||
|
||||
// update account filename
|
||||
let original_account = account.clone();
|
||||
let mut account = account;
|
||||
account.filename = Some(filename);
|
||||
|
||||
{
|
||||
// save the file
|
||||
let mut file = if dedup {
|
||||
create_new_file_with_permissions_to_owner(&keyfile_path)?
|
||||
} else {
|
||||
replace_file_with_permissions_to_owner(&keyfile_path)?
|
||||
};
|
||||
|
||||
// write key content
|
||||
self.key_manager.write(original_account, &mut file).map_err(|e| Error::Custom(format!("{:?}", e)))?;
|
||||
|
||||
file.flush()?;
|
||||
file.sync_all()?;
|
||||
}
|
||||
|
||||
Ok(account)
|
||||
}
|
||||
|
||||
/// Get key file manager referece
|
||||
pub fn key_manager(&self) -> &T {
|
||||
&self.key_manager
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> KeyDirectory for DiskDirectory<T> where T: KeyFileManager {
|
||||
fn load(&self) -> Result<Vec<SafeAccount>, Error> {
|
||||
let accounts = self.files_content()?
|
||||
.into_iter()
|
||||
.map(|(_, account)| account)
|
||||
.collect();
|
||||
Ok(accounts)
|
||||
}
|
||||
|
||||
fn update(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
|
||||
// Disk store handles updates correctly iff filename is the same
|
||||
let filename = account_filename(&account);
|
||||
self.insert_with_filename(account, filename, false)
|
||||
}
|
||||
|
||||
fn insert(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
|
||||
let filename = account_filename(&account);
|
||||
self.insert_with_filename(account, filename, true)
|
||||
}
|
||||
|
||||
fn remove(&self, account: &SafeAccount) -> Result<(), Error> {
|
||||
// enumerate all entries in keystore
|
||||
// and find entry with given address
|
||||
let to_remove = self.files_content()?
|
||||
.into_iter()
|
||||
.find(|&(_, ref acc)| acc.id == account.id && acc.address == account.address);
|
||||
|
||||
// remove it
|
||||
match to_remove {
|
||||
None => Err(Error::InvalidAccount),
|
||||
Some((path, _)) => fs::remove_file(path).map_err(From::from)
|
||||
}
|
||||
}
|
||||
|
||||
fn path(&self) -> Option<&PathBuf> { Some(&self.path) }
|
||||
|
||||
fn as_vault_provider(&self) -> Option<&dyn VaultKeyDirectoryProvider> {
|
||||
Some(self)
|
||||
}
|
||||
|
||||
fn unique_repr(&self) -> Result<u64, Error> {
|
||||
self.last_modification_date()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> VaultKeyDirectoryProvider for DiskDirectory<T> where T: KeyFileManager {
|
||||
fn create(&self, name: &str, key: VaultKey) -> Result<Box<dyn VaultKeyDirectory>, Error> {
|
||||
let vault_dir = VaultDiskDirectory::create(&self.path, name, key)?;
|
||||
Ok(Box::new(vault_dir))
|
||||
}
|
||||
|
||||
fn open(&self, name: &str, key: VaultKey) -> Result<Box<dyn VaultKeyDirectory>, Error> {
|
||||
let vault_dir = VaultDiskDirectory::at(&self.path, name, key)?;
|
||||
Ok(Box::new(vault_dir))
|
||||
}
|
||||
|
||||
fn list_vaults(&self) -> Result<Vec<String>, Error> {
|
||||
Ok(fs::read_dir(&self.path)?
|
||||
.filter_map(|e| e.ok().map(|e| e.path()))
|
||||
.filter_map(|path| {
|
||||
let mut vault_file_path = path.clone();
|
||||
vault_file_path.push(VAULT_FILE_NAME);
|
||||
if vault_file_path.is_file() {
|
||||
path.file_name().and_then(|f| f.to_str()).map(|f| f.to_owned())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn vault_meta(&self, name: &str) -> Result<String, Error> {
|
||||
VaultDiskDirectory::meta_at(&self.path, name)
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyFileManager for DiskKeyFileManager {
|
||||
fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error> where T: io::Read {
|
||||
let key_file = json::KeyFile::load(reader).map_err(|e| Error::Custom(format!("{:?}", e)))?;
|
||||
SafeAccount::from_file(key_file, filename, &self.password)
|
||||
}
|
||||
|
||||
fn write<T>(&self, mut account: SafeAccount, writer: &mut T) -> Result<(), Error> where T: io::Write {
|
||||
// when account is moved back to root directory from vault
|
||||
// => remove vault field from meta
|
||||
account.meta = json::remove_vault_name_from_json_meta(&account.meta)
|
||||
.map_err(|err| Error::Custom(format!("{:?}", err)))?;
|
||||
|
||||
let key_file: json::KeyFile = account.into();
|
||||
key_file.write(writer).map_err(|e| Error::Custom(format!("{:?}", e)))
|
||||
}
|
||||
}
|
||||
|
||||
fn account_filename(account: &SafeAccount) -> String {
|
||||
// build file path
|
||||
account.filename.clone().unwrap_or_else(|| {
|
||||
let timestamp = time::strftime("%Y-%m-%dT%H-%M-%S", &time::now_utc()).expect("Time-format string is valid.");
|
||||
format!("UTC--{}Z--{}", timestamp, Uuid::from(account.id))
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
extern crate tempdir;
|
||||
|
||||
use std::{env, fs};
|
||||
use super::{KeyDirectory, RootDiskDirectory, VaultKey};
|
||||
use account::SafeAccount;
|
||||
use crypto::publickey::{Random, Generator};
|
||||
use self::tempdir::TempDir;
|
||||
|
||||
#[test]
|
||||
fn should_create_new_account() {
|
||||
// given
|
||||
let mut dir = env::temp_dir();
|
||||
dir.push("ethstore_should_create_new_account");
|
||||
let keypair = Random.generate().unwrap();
|
||||
let password = "hello world".into();
|
||||
let directory = RootDiskDirectory::create(dir.clone()).unwrap();
|
||||
|
||||
// when
|
||||
let account = SafeAccount::create(&keypair, [0u8; 16], &password, 1024, "Test".to_owned(), "{}".to_owned());
|
||||
let res = directory.insert(account.unwrap());
|
||||
|
||||
// then
|
||||
assert!(res.is_ok(), "Should save account succesfuly.");
|
||||
assert!(res.unwrap().filename.is_some(), "Filename has been assigned.");
|
||||
|
||||
// cleanup
|
||||
let _ = fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_handle_duplicate_filenames() {
|
||||
// given
|
||||
let mut dir = env::temp_dir();
|
||||
dir.push("ethstore_should_handle_duplicate_filenames");
|
||||
let keypair = Random.generate().unwrap();
|
||||
let password = "hello world".into();
|
||||
let directory = RootDiskDirectory::create(dir.clone()).unwrap();
|
||||
|
||||
// when
|
||||
let account = SafeAccount::create(&keypair, [0u8; 16], &password, 1024, "Test".to_owned(), "{}".to_owned()).unwrap();
|
||||
let filename = "test".to_string();
|
||||
let dedup = true;
|
||||
|
||||
directory.insert_with_filename(account.clone(), "foo".to_string(), dedup).unwrap();
|
||||
let file1 = directory.insert_with_filename(account.clone(), filename.clone(), dedup).unwrap().filename.unwrap();
|
||||
let file2 = directory.insert_with_filename(account.clone(), filename.clone(), dedup).unwrap().filename.unwrap();
|
||||
let file3 = directory.insert_with_filename(account.clone(), filename.clone(), dedup).unwrap().filename.unwrap();
|
||||
|
||||
// then
|
||||
// the first file should have the original names
|
||||
assert_eq!(file1, filename);
|
||||
|
||||
// the following duplicate files should have a suffix appended
|
||||
assert!(file2 != file3);
|
||||
assert_eq!(file2.len(), filename.len() + 5);
|
||||
assert_eq!(file3.len(), filename.len() + 5);
|
||||
|
||||
// cleanup
|
||||
let _ = fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_manage_vaults() {
|
||||
// given
|
||||
let mut dir = env::temp_dir();
|
||||
dir.push("should_create_new_vault");
|
||||
let directory = RootDiskDirectory::create(dir.clone()).unwrap();
|
||||
let vault_name = "vault";
|
||||
let password = "password".into();
|
||||
|
||||
// then
|
||||
assert!(directory.as_vault_provider().is_some());
|
||||
|
||||
// and when
|
||||
let before_root_items_count = fs::read_dir(&dir).unwrap().count();
|
||||
let vault = directory.as_vault_provider().unwrap().create(vault_name, VaultKey::new(&password, 1024));
|
||||
|
||||
// then
|
||||
assert!(vault.is_ok());
|
||||
let after_root_items_count = fs::read_dir(&dir).unwrap().count();
|
||||
assert!(after_root_items_count > before_root_items_count);
|
||||
|
||||
// and when
|
||||
let vault = directory.as_vault_provider().unwrap().open(vault_name, VaultKey::new(&password, 1024));
|
||||
|
||||
// then
|
||||
assert!(vault.is_ok());
|
||||
let after_root_items_count2 = fs::read_dir(&dir).unwrap().count();
|
||||
assert!(after_root_items_count == after_root_items_count2);
|
||||
|
||||
// cleanup
|
||||
let _ = fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_list_vaults() {
|
||||
// given
|
||||
let temp_path = TempDir::new("").unwrap();
|
||||
let directory = RootDiskDirectory::create(&temp_path).unwrap();
|
||||
let vault_provider = directory.as_vault_provider().unwrap();
|
||||
vault_provider.create("vault1", VaultKey::new(&"password1".into(), 1)).unwrap();
|
||||
vault_provider.create("vault2", VaultKey::new(&"password2".into(), 1)).unwrap();
|
||||
|
||||
// then
|
||||
let vaults = vault_provider.list_vaults().unwrap();
|
||||
assert_eq!(vaults.len(), 2);
|
||||
assert!(vaults.iter().any(|v| &*v == "vault1"));
|
||||
assert!(vaults.iter().any(|v| &*v == "vault2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hash_of_files() {
|
||||
let temp_path = TempDir::new("").unwrap();
|
||||
let directory = RootDiskDirectory::create(&temp_path).unwrap();
|
||||
|
||||
let hash = directory.files_hash().expect("Files hash should be calculated ok");
|
||||
assert_eq!(
|
||||
hash,
|
||||
15130871412783076140
|
||||
);
|
||||
|
||||
let keypair = Random.generate().unwrap();
|
||||
let password = "test pass".into();
|
||||
let account = SafeAccount::create(&keypair, [0u8; 16], &password, 1024, "Test".to_owned(), "{}".to_owned());
|
||||
directory.insert(account.unwrap()).expect("Account should be inserted ok");
|
||||
|
||||
let new_hash = directory.files_hash().expect("New files hash should be calculated ok");
|
||||
|
||||
assert!(new_hash != hash, "hash of the file list should change once directory content changed");
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use parking_lot::RwLock;
|
||||
use itertools;
|
||||
use crypto::publickey::Address;
|
||||
|
||||
use {SafeAccount, Error};
|
||||
use super::KeyDirectory;
|
||||
|
||||
/// Accounts in-memory storage.
|
||||
#[derive(Default)]
|
||||
pub struct MemoryDirectory {
|
||||
accounts: RwLock<HashMap<Address, Vec<SafeAccount>>>,
|
||||
}
|
||||
|
||||
impl KeyDirectory for MemoryDirectory {
|
||||
fn load(&self) -> Result<Vec<SafeAccount>, Error> {
|
||||
Ok(itertools::Itertools::flatten(self.accounts.read().values().cloned()).collect())
|
||||
}
|
||||
|
||||
fn update(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
|
||||
let mut lock = self.accounts.write();
|
||||
let accounts = lock.entry(account.address.clone()).or_insert_with(Vec::new);
|
||||
// If the filename is the same we just need to replace the entry
|
||||
accounts.retain(|acc| acc.filename != account.filename);
|
||||
accounts.push(account.clone());
|
||||
Ok(account)
|
||||
}
|
||||
|
||||
fn insert(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
|
||||
let mut lock = self.accounts.write();
|
||||
let accounts = lock.entry(account.address.clone()).or_insert_with(Vec::new);
|
||||
accounts.push(account.clone());
|
||||
Ok(account)
|
||||
}
|
||||
|
||||
fn remove(&self, account: &SafeAccount) -> Result<(), Error> {
|
||||
let mut accounts = self.accounts.write();
|
||||
let is_empty = if let Some(accounts) = accounts.get_mut(&account.address) {
|
||||
if let Some(position) = accounts.iter().position(|acc| acc == account) {
|
||||
accounts.remove(position);
|
||||
}
|
||||
accounts.is_empty()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if is_empty {
|
||||
accounts.remove(&account.address);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unique_repr(&self) -> Result<u64, Error> {
|
||||
let mut val = 0u64;
|
||||
let accounts = self.accounts.read();
|
||||
for acc in accounts.keys() { val = val ^ acc.to_low_u64_be() }
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Accounts Directory
|
||||
|
||||
use ethkey::Password;
|
||||
use std::path::{PathBuf};
|
||||
use {SafeAccount, Error};
|
||||
|
||||
mod disk;
|
||||
mod memory;
|
||||
mod vault;
|
||||
|
||||
/// `VaultKeyDirectory::set_key` error
|
||||
#[derive(Debug)]
|
||||
pub enum SetKeyError {
|
||||
/// Error is fatal and directory is probably in inconsistent state
|
||||
Fatal(Error),
|
||||
/// Error is non fatal, directory is reverted to pre-operation state
|
||||
NonFatalOld(Error),
|
||||
/// Error is non fatal, directory is consistent with new key
|
||||
NonFatalNew(Error),
|
||||
}
|
||||
|
||||
/// Vault key
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct VaultKey {
|
||||
/// Vault password
|
||||
pub password: Password,
|
||||
/// Number of iterations to produce a derived key from password
|
||||
pub iterations: u32,
|
||||
}
|
||||
|
||||
/// Keys directory
|
||||
pub trait KeyDirectory: Send + Sync {
|
||||
/// Read keys from directory
|
||||
fn load(&self) -> Result<Vec<SafeAccount>, Error>;
|
||||
/// Insert new key to directory
|
||||
fn insert(&self, account: SafeAccount) -> Result<SafeAccount, Error>;
|
||||
/// Update key in the directory
|
||||
fn update(&self, account: SafeAccount) -> Result<SafeAccount, Error>;
|
||||
/// Remove key from directory
|
||||
fn remove(&self, account: &SafeAccount) -> Result<(), Error>;
|
||||
/// Get directory filesystem path, if available
|
||||
fn path(&self) -> Option<&PathBuf> { None }
|
||||
/// Return vault provider, if available
|
||||
fn as_vault_provider(&self) -> Option<&dyn VaultKeyDirectoryProvider> { None }
|
||||
/// Unique representation of directory account collection
|
||||
fn unique_repr(&self) -> Result<u64, Error>;
|
||||
}
|
||||
|
||||
/// Vaults provider
|
||||
pub trait VaultKeyDirectoryProvider {
|
||||
/// Create new vault with given key
|
||||
fn create(&self, name: &str, key: VaultKey) -> Result<Box<dyn VaultKeyDirectory>, Error>;
|
||||
/// Open existing vault with given key
|
||||
fn open(&self, name: &str, key: VaultKey) -> Result<Box<dyn VaultKeyDirectory>, Error>;
|
||||
/// List all vaults
|
||||
fn list_vaults(&self) -> Result<Vec<String>, Error>;
|
||||
/// Get vault meta
|
||||
fn vault_meta(&self, name: &str) -> Result<String, Error>;
|
||||
}
|
||||
|
||||
/// Vault directory
|
||||
pub trait VaultKeyDirectory: KeyDirectory {
|
||||
/// Cast to `KeyDirectory`
|
||||
fn as_key_directory(&self) -> &dyn KeyDirectory;
|
||||
/// Vault name
|
||||
fn name(&self) -> &str;
|
||||
/// Get vault key
|
||||
fn key(&self) -> VaultKey;
|
||||
/// Set new key for vault
|
||||
fn set_key(&self, key: VaultKey) -> Result<(), SetKeyError>;
|
||||
/// Get vault meta
|
||||
fn meta(&self) -> String;
|
||||
/// Set vault meta
|
||||
fn set_meta(&self, meta: &str) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
pub use self::disk::{RootDiskDirectory, DiskKeyFileManager, KeyFileManager};
|
||||
pub use self::memory::MemoryDirectory;
|
||||
pub use self::vault::VaultDiskDirectory;
|
||||
|
||||
impl VaultKey {
|
||||
/// Create new vault key
|
||||
pub fn new(password: &Password, iterations: u32) -> Self {
|
||||
VaultKey {
|
||||
password: password.clone(),
|
||||
iterations: iterations,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,443 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{fs, io};
|
||||
use std::path::{PathBuf, Path};
|
||||
use parking_lot::Mutex;
|
||||
use {json, SafeAccount, Error};
|
||||
use crypto::Keccak256;
|
||||
use super::super::account::Crypto;
|
||||
use super::{KeyDirectory, VaultKeyDirectory, VaultKey, SetKeyError};
|
||||
use super::disk::{self, DiskDirectory, KeyFileManager};
|
||||
|
||||
/// Name of vault metadata file
|
||||
pub const VAULT_FILE_NAME: &'static str = "vault.json";
|
||||
/// Name of temporary vault metadata file
|
||||
pub const VAULT_TEMP_FILE_NAME: &'static str = "vault_temp.json";
|
||||
|
||||
/// Vault directory implementation
|
||||
pub type VaultDiskDirectory = DiskDirectory<VaultKeyFileManager>;
|
||||
|
||||
/// Vault key file manager
|
||||
pub struct VaultKeyFileManager {
|
||||
name: String,
|
||||
key: VaultKey,
|
||||
meta: Mutex<String>,
|
||||
}
|
||||
|
||||
impl VaultDiskDirectory {
|
||||
/// Create new vault directory with given key
|
||||
pub fn create<P>(root: P, name: &str, key: VaultKey) -> Result<Self, Error> where P: AsRef<Path> {
|
||||
// check that vault directory does not exists
|
||||
let vault_dir_path = make_vault_dir_path(root, name, true)?;
|
||||
if vault_dir_path.exists() {
|
||||
return Err(Error::CreationFailed);
|
||||
}
|
||||
|
||||
// create vault && vault file
|
||||
let vault_meta = "{}";
|
||||
fs::create_dir_all(&vault_dir_path)?;
|
||||
if let Err(err) = create_vault_file(&vault_dir_path, &key, vault_meta) {
|
||||
let _ = fs::remove_dir_all(&vault_dir_path); // can't do anything with this
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
Ok(DiskDirectory::new(vault_dir_path, VaultKeyFileManager::new(name, key, vault_meta)))
|
||||
}
|
||||
|
||||
/// Open existing vault directory with given key
|
||||
pub fn at<P>(root: P, name: &str, key: VaultKey) -> Result<Self, Error> where P: AsRef<Path> {
|
||||
// check that vault directory exists
|
||||
let vault_dir_path = make_vault_dir_path(root, name, true)?;
|
||||
if !vault_dir_path.is_dir() {
|
||||
return Err(Error::CreationFailed);
|
||||
}
|
||||
|
||||
// check that passed key matches vault file
|
||||
let meta = read_vault_file(&vault_dir_path, Some(&key))?;
|
||||
|
||||
Ok(DiskDirectory::new(vault_dir_path, VaultKeyFileManager::new(name, key, &meta)))
|
||||
}
|
||||
|
||||
/// Read vault meta without actually opening the vault
|
||||
pub fn meta_at<P>(root: P, name: &str) -> Result<String, Error> where P: AsRef<Path> {
|
||||
// check that vault directory exists
|
||||
let vault_dir_path = make_vault_dir_path(root, name, true)?;
|
||||
if !vault_dir_path.is_dir() {
|
||||
return Err(Error::VaultNotFound);
|
||||
}
|
||||
|
||||
// check that passed key matches vault file
|
||||
read_vault_file(&vault_dir_path, None)
|
||||
}
|
||||
|
||||
fn create_temp_vault(&self, key: VaultKey) -> Result<VaultDiskDirectory, Error> {
|
||||
let original_path = self.path().expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed");
|
||||
let mut path: PathBuf = original_path.clone();
|
||||
let name = self.name();
|
||||
|
||||
path.push(name); // to jump to the next level
|
||||
|
||||
let mut index = 0;
|
||||
loop {
|
||||
let name = format!("{}_temp_{}", name, index);
|
||||
path.set_file_name(&name);
|
||||
if !path.exists() {
|
||||
return VaultDiskDirectory::create(original_path, &name, key);
|
||||
}
|
||||
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_to_vault(&self, vault: &VaultDiskDirectory) -> Result<(), Error> {
|
||||
for account in self.load()? {
|
||||
let filename = account.filename.clone().expect("self is instance of DiskDirectory; DiskDirectory fills filename in load; qed");
|
||||
vault.insert_with_filename(account, filename, true)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn delete(&self) -> Result<(), Error> {
|
||||
let path = self.path().expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed");
|
||||
fs::remove_dir_all(path).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl VaultKeyDirectory for VaultDiskDirectory {
|
||||
fn as_key_directory(&self) -> &dyn KeyDirectory {
|
||||
self
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.key_manager().name
|
||||
}
|
||||
|
||||
fn key(&self) -> VaultKey {
|
||||
self.key_manager().key.clone()
|
||||
}
|
||||
|
||||
fn set_key(&self, new_key: VaultKey) -> Result<(), SetKeyError> {
|
||||
let temp_vault = VaultDiskDirectory::create_temp_vault(self, new_key.clone()).map_err(|err| SetKeyError::NonFatalOld(err))?;
|
||||
let mut source_path = temp_vault.path().expect("temp_vault is instance of DiskDirectory; DiskDirectory always returns path; qed").clone();
|
||||
let mut target_path = self.path().expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed").clone();
|
||||
|
||||
// preserve meta
|
||||
temp_vault.set_meta(&self.meta()).map_err(SetKeyError::NonFatalOld)?;
|
||||
|
||||
// jump to next fs level
|
||||
source_path.push("next");
|
||||
target_path.push("next");
|
||||
|
||||
let temp_accounts = self.copy_to_vault(&temp_vault)
|
||||
.and_then(|_| temp_vault.load())
|
||||
.map_err(|err| {
|
||||
// ignore error, as we already processing error
|
||||
let _ = temp_vault.delete();
|
||||
SetKeyError::NonFatalOld(err)
|
||||
})?;
|
||||
|
||||
// we can't just delete temp vault until all files moved, because
|
||||
// original vault content has already been partially replaced
|
||||
// => when error or crash happens here, we can't do anything
|
||||
for temp_account in temp_accounts {
|
||||
let filename = temp_account.filename.expect("self is instance of DiskDirectory; DiskDirectory fills filename in load; qed");
|
||||
source_path.set_file_name(&filename);
|
||||
target_path.set_file_name(&filename);
|
||||
fs::rename(&source_path, &target_path).map_err(|err| SetKeyError::Fatal(err.into()))?;
|
||||
}
|
||||
source_path.set_file_name(VAULT_FILE_NAME);
|
||||
target_path.set_file_name(VAULT_FILE_NAME);
|
||||
fs::rename(source_path, target_path).map_err(|err| SetKeyError::Fatal(err.into()))?;
|
||||
|
||||
temp_vault.delete().map_err(|err| SetKeyError::NonFatalNew(err))
|
||||
}
|
||||
|
||||
fn meta(&self) -> String {
|
||||
self.key_manager().meta.lock().clone()
|
||||
}
|
||||
|
||||
fn set_meta(&self, meta: &str) -> Result<(), Error> {
|
||||
let key_manager = self.key_manager();
|
||||
let vault_path = self.path().expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed");
|
||||
create_vault_file(vault_path, &key_manager.key, meta)?;
|
||||
*key_manager.meta.lock() = meta.to_owned();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl VaultKeyFileManager {
|
||||
pub fn new(name: &str, key: VaultKey, meta: &str) -> Self {
|
||||
VaultKeyFileManager {
|
||||
name: name.into(),
|
||||
key: key,
|
||||
meta: Mutex::new(meta.to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyFileManager for VaultKeyFileManager {
|
||||
fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error> where T: io::Read {
|
||||
let vault_file = json::VaultKeyFile::load(reader).map_err(|e| Error::Custom(format!("{:?}", e)))?;
|
||||
let mut safe_account = SafeAccount::from_vault_file(&self.key.password, vault_file, filename.clone())?;
|
||||
|
||||
safe_account.meta = json::insert_vault_name_to_json_meta(&safe_account.meta, &self.name)
|
||||
.map_err(|err| Error::Custom(format!("{:?}", err)))?;
|
||||
Ok(safe_account)
|
||||
}
|
||||
|
||||
fn write<T>(&self, mut account: SafeAccount, writer: &mut T) -> Result<(), Error> where T: io::Write {
|
||||
account.meta = json::remove_vault_name_from_json_meta(&account.meta)
|
||||
.map_err(|err| Error::Custom(format!("{:?}", err)))?;
|
||||
|
||||
let vault_file: json::VaultKeyFile = account.into_vault_file(self.key.iterations, &self.key.password)?;
|
||||
vault_file.write(writer).map_err(|e| Error::Custom(format!("{:?}", e)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes path to vault directory, checking that vault name is appropriate
|
||||
fn make_vault_dir_path<P>(root: P, name: &str, check_name: bool) -> Result<PathBuf, Error> where P: AsRef<Path> {
|
||||
// check vault name
|
||||
if check_name && !check_vault_name(name) {
|
||||
return Err(Error::InvalidVaultName);
|
||||
}
|
||||
|
||||
let mut vault_dir_path: PathBuf = root.as_ref().into();
|
||||
vault_dir_path.push(name);
|
||||
Ok(vault_dir_path)
|
||||
}
|
||||
|
||||
/// Every vault must have unique name => we rely on filesystem to check this
|
||||
/// => vault name must not contain any fs-special characters to avoid directory traversal
|
||||
/// => we only allow alphanumeric + separator characters in vault name.
|
||||
fn check_vault_name(name: &str) -> bool {
|
||||
!name.is_empty()
|
||||
&& name.chars()
|
||||
.all(|c| c.is_alphanumeric()
|
||||
|| c.is_whitespace()
|
||||
|| c == '-' || c == '_')
|
||||
}
|
||||
|
||||
/// Vault can be empty, but still must be pluggable => we store vault password in separate file
|
||||
fn create_vault_file<P>(vault_dir_path: P, key: &VaultKey, meta: &str) -> Result<(), Error> where P: AsRef<Path> {
|
||||
let password_hash = key.password.as_bytes().keccak256();
|
||||
let crypto = Crypto::with_plain(&password_hash, &key.password, key.iterations)?;
|
||||
|
||||
let vault_file_path = vault_dir_path.as_ref().join(VAULT_FILE_NAME);
|
||||
let temp_vault_file_name = disk::find_unique_filename_using_random_suffix(vault_dir_path.as_ref(), &VAULT_TEMP_FILE_NAME)?;
|
||||
let temp_vault_file_path = vault_dir_path.as_ref().join(&temp_vault_file_name);
|
||||
|
||||
// this method is used to rewrite existing vault file
|
||||
// => write to temporary file first, then rename temporary file to vault file
|
||||
let mut vault_file = disk::create_new_file_with_permissions_to_owner(&temp_vault_file_path)?;
|
||||
let vault_file_contents = json::VaultFile {
|
||||
crypto: crypto.into(),
|
||||
meta: Some(meta.to_owned()),
|
||||
};
|
||||
vault_file_contents.write(&mut vault_file).map_err(|e| Error::Custom(format!("{:?}", e)))?;
|
||||
drop(vault_file);
|
||||
fs::rename(&temp_vault_file_path, &vault_file_path)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// When vault is opened => we must check that password matches && read metadata
|
||||
fn read_vault_file<P>(vault_dir_path: P, key: Option<&VaultKey>) -> Result<String, Error> where P: AsRef<Path> {
|
||||
let mut vault_file_path: PathBuf = vault_dir_path.as_ref().into();
|
||||
vault_file_path.push(VAULT_FILE_NAME);
|
||||
|
||||
let vault_file = fs::File::open(vault_file_path)?;
|
||||
let vault_file_contents = json::VaultFile::load(vault_file).map_err(|e| Error::Custom(format!("{:?}", e)))?;
|
||||
let vault_file_meta = vault_file_contents.meta.unwrap_or("{}".to_owned());
|
||||
let vault_file_crypto: Crypto = vault_file_contents.crypto.into();
|
||||
|
||||
if let Some(key) = key {
|
||||
let password_bytes = vault_file_crypto.decrypt(&key.password)?;
|
||||
let password_hash = key.password.as_bytes().keccak256();
|
||||
if password_hash != password_bytes.as_slice() {
|
||||
return Err(Error::InvalidPassword);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(vault_file_meta)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
extern crate tempdir;
|
||||
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use super::VaultKey;
|
||||
use super::{VAULT_FILE_NAME, check_vault_name, make_vault_dir_path, create_vault_file, read_vault_file, VaultDiskDirectory};
|
||||
use self::tempdir::TempDir;
|
||||
|
||||
#[test]
|
||||
fn check_vault_name_succeeds() {
|
||||
assert!(check_vault_name("vault"));
|
||||
assert!(check_vault_name("vault with spaces"));
|
||||
assert!(check_vault_name("vault with tabs"));
|
||||
assert!(check_vault_name("vault_with_underscores"));
|
||||
assert!(check_vault_name("vault-with-dashes"));
|
||||
assert!(check_vault_name("vault-with-digits-123"));
|
||||
assert!(check_vault_name("vault中文名字"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_vault_name_fails() {
|
||||
assert!(!check_vault_name(""));
|
||||
assert!(!check_vault_name("."));
|
||||
assert!(!check_vault_name("*"));
|
||||
assert!(!check_vault_name("../.bash_history"));
|
||||
assert!(!check_vault_name("/etc/passwd"));
|
||||
assert!(!check_vault_name("c:\\windows"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn make_vault_dir_path_succeeds() {
|
||||
use std::path::Path;
|
||||
|
||||
assert_eq!(&make_vault_dir_path("/home/user/parity", "vault", true).unwrap(), &Path::new("/home/user/parity/vault"));
|
||||
assert_eq!(&make_vault_dir_path("/home/user/parity", "*bad-name*", false).unwrap(), &Path::new("/home/user/parity/*bad-name*"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn make_vault_dir_path_fails() {
|
||||
assert!(make_vault_dir_path("/home/user/parity", "*bad-name*", true).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_vault_file_succeeds() {
|
||||
// given
|
||||
let temp_path = TempDir::new("").unwrap();
|
||||
let key = VaultKey::new(&"password".into(), 1024);
|
||||
let mut vault_dir: PathBuf = temp_path.path().into();
|
||||
vault_dir.push("vault");
|
||||
fs::create_dir_all(&vault_dir).unwrap();
|
||||
|
||||
// when
|
||||
let result = create_vault_file(&vault_dir, &key, "{}");
|
||||
|
||||
// then
|
||||
assert!(result.is_ok());
|
||||
let mut vault_file_path = vault_dir.clone();
|
||||
vault_file_path.push(VAULT_FILE_NAME);
|
||||
assert!(vault_file_path.exists() && vault_file_path.is_file());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_vault_file_succeeds() {
|
||||
// given
|
||||
let temp_path = TempDir::new("").unwrap();
|
||||
let key = VaultKey::new(&"password".into(), 1024);
|
||||
let vault_file_contents = r#"{"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"758696c8dc6378ab9b25bb42790da2f5"},"ciphertext":"54eb50683717d41caaeb12ea969f2c159daada5907383f26f327606a37dc7168","kdf":"pbkdf2","kdfparams":{"c":1024,"dklen":32,"prf":"hmac-sha256","salt":"3c320fa566a1a7963ac8df68a19548d27c8f40bf92ef87c84594dcd5bbc402b6"},"mac":"9e5c2314c2a0781962db85611417c614bd6756666b6b1e93840f5b6ed895f003"}}"#;
|
||||
let dir: PathBuf = temp_path.path().into();
|
||||
let mut vault_file_path: PathBuf = dir.clone();
|
||||
vault_file_path.push(VAULT_FILE_NAME);
|
||||
{
|
||||
let mut vault_file = fs::File::create(vault_file_path).unwrap();
|
||||
vault_file.write_all(vault_file_contents.as_bytes()).unwrap();
|
||||
}
|
||||
|
||||
// when
|
||||
let result = read_vault_file(&dir, Some(&key));
|
||||
|
||||
// then
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_vault_file_fails() {
|
||||
// given
|
||||
let temp_path = TempDir::new("").unwrap();
|
||||
let key = VaultKey::new(&"password1".into(), 1024);
|
||||
let dir: PathBuf = temp_path.path().into();
|
||||
let mut vault_file_path: PathBuf = dir.clone();
|
||||
vault_file_path.push(VAULT_FILE_NAME);
|
||||
|
||||
// when
|
||||
let result = read_vault_file(&dir, Some(&key));
|
||||
|
||||
// then
|
||||
assert!(result.is_err());
|
||||
|
||||
// and when given
|
||||
let vault_file_contents = r#"{"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"0155e3690be19fbfbecabcd440aa284b"},"ciphertext":"4d6938a1f49b7782","kdf":"pbkdf2","kdfparams":{"c":1024,"dklen":32,"prf":"hmac-sha256","salt":"b6a9338a7ccd39288a86dba73bfecd9101b4f3db9c9830e7c76afdbd4f6872e5"},"mac":"16381463ea11c6eb2239a9f339c2e780516d29d234ce30ac5f166f9080b5a262"}}"#;
|
||||
{
|
||||
let mut vault_file = fs::File::create(vault_file_path).unwrap();
|
||||
vault_file.write_all(vault_file_contents.as_bytes()).unwrap();
|
||||
}
|
||||
|
||||
// when
|
||||
let result = read_vault_file(&dir, Some(&key));
|
||||
|
||||
// then
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vault_directory_can_be_created() {
|
||||
// given
|
||||
let temp_path = TempDir::new("").unwrap();
|
||||
let key = VaultKey::new(&"password".into(), 1024);
|
||||
let dir: PathBuf = temp_path.path().into();
|
||||
|
||||
// when
|
||||
let vault = VaultDiskDirectory::create(&dir, "vault", key.clone());
|
||||
|
||||
// then
|
||||
assert!(vault.is_ok());
|
||||
|
||||
// and when
|
||||
let vault = VaultDiskDirectory::at(&dir, "vault", key);
|
||||
|
||||
// then
|
||||
assert!(vault.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vault_directory_cannot_be_created_if_already_exists() {
|
||||
// given
|
||||
let temp_path = TempDir::new("").unwrap();
|
||||
let key = VaultKey::new(&"password".into(), 1024);
|
||||
let dir: PathBuf = temp_path.path().into();
|
||||
let mut vault_dir = dir.clone();
|
||||
vault_dir.push("vault");
|
||||
fs::create_dir_all(&vault_dir).unwrap();
|
||||
|
||||
// when
|
||||
let vault = VaultDiskDirectory::create(&dir, "vault", key);
|
||||
|
||||
// then
|
||||
assert!(vault.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vault_directory_cannot_be_opened_if_not_exists() {
|
||||
// given
|
||||
let temp_path = TempDir::new("").unwrap();
|
||||
let key = VaultKey::new(&"password".into(), 1024);
|
||||
let dir: PathBuf = temp_path.path().into();
|
||||
|
||||
// when
|
||||
let vault = VaultDiskDirectory::at(&dir, "vault", key);
|
||||
|
||||
// then
|
||||
assert!(vault.is_err());
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::fmt;
|
||||
use std::io::Error as IoError;
|
||||
use crypto::{self, Error as EthCryptoError};
|
||||
use crypto::publickey::{Error as EthPublicKeyCryptoError, DerivationError};
|
||||
|
||||
/// Account-related errors.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// IO error
|
||||
Io(IoError),
|
||||
/// Invalid Password
|
||||
InvalidPassword,
|
||||
/// Account's secret is invalid.
|
||||
InvalidSecret,
|
||||
/// Invalid Vault Crypto meta.
|
||||
InvalidCryptoMeta,
|
||||
/// Invalid Account.
|
||||
InvalidAccount,
|
||||
/// Invalid Message.
|
||||
InvalidMessage,
|
||||
/// Invalid Key File
|
||||
InvalidKeyFile(String),
|
||||
/// Vaults are not supported.
|
||||
VaultsAreNotSupported,
|
||||
/// Unsupported vault
|
||||
UnsupportedVault,
|
||||
/// Invalid vault name
|
||||
InvalidVaultName,
|
||||
/// Vault not found
|
||||
VaultNotFound,
|
||||
/// Account creation failed.
|
||||
CreationFailed,
|
||||
/// `EthCrypto` error
|
||||
EthCrypto(EthCryptoError),
|
||||
/// `EthPublicKeyCryptoError` error
|
||||
EthPublicKeyCrypto(EthPublicKeyCryptoError),
|
||||
/// Derivation error
|
||||
Derivation(DerivationError),
|
||||
/// Custom error
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
let s = match *self {
|
||||
Error::Io(ref err) => err.to_string(),
|
||||
Error::InvalidPassword => "Invalid password".into(),
|
||||
Error::InvalidSecret => "Invalid secret".into(),
|
||||
Error::InvalidCryptoMeta => "Invalid crypted metadata".into(),
|
||||
Error::InvalidAccount => "Invalid account".into(),
|
||||
Error::InvalidMessage => "Invalid message".into(),
|
||||
Error::InvalidKeyFile(ref reason) => format!("Invalid key file: {}", reason),
|
||||
Error::VaultsAreNotSupported => "Vaults are not supported".into(),
|
||||
Error::UnsupportedVault => "Vault is not supported for this operation".into(),
|
||||
Error::InvalidVaultName => "Invalid vault name".into(),
|
||||
Error::VaultNotFound => "Vault not found".into(),
|
||||
Error::CreationFailed => "Account creation failed".into(),
|
||||
Error::EthCrypto(ref err) => err.to_string(),
|
||||
Error::EthPublicKeyCrypto(ref err) => err.to_string(),
|
||||
Error::Derivation(ref err) => format!("Derivation error: {:?}", err),
|
||||
Error::Custom(ref s) => s.clone(),
|
||||
};
|
||||
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IoError> for Error {
|
||||
fn from(err: IoError) -> Self {
|
||||
Error::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EthPublicKeyCryptoError> for Error {
|
||||
fn from(err: EthPublicKeyCryptoError) -> Self {
|
||||
Error::EthPublicKeyCrypto(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EthCryptoError> for Error {
|
||||
fn from(err: EthCryptoError) -> Self {
|
||||
Error::EthCrypto(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crypto::error::ScryptError> for Error {
|
||||
fn from(err: crypto::error::ScryptError) -> Self {
|
||||
Error::EthCrypto(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crypto::error::SymmError> for Error {
|
||||
fn from(err: crypto::error::SymmError) -> Self {
|
||||
Error::EthCrypto(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DerivationError> for Error {
|
||||
fn from(err: DerivationError) -> Self {
|
||||
Error::Derivation(err)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,80 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::path::Path;
|
||||
use std::fs;
|
||||
|
||||
use crypto::publickey::Address;
|
||||
use accounts_dir::{KeyDirectory, RootDiskDirectory, DiskKeyFileManager, KeyFileManager};
|
||||
use dir;
|
||||
use Error;
|
||||
|
||||
/// Import an account from a file.
|
||||
pub fn import_account(path: &Path, dst: &dyn KeyDirectory) -> Result<Address, Error> {
|
||||
let key_manager = DiskKeyFileManager::default();
|
||||
let existing_accounts = dst.load()?.into_iter().map(|a| a.address).collect::<HashSet<_>>();
|
||||
let filename = path.file_name().and_then(|n| n.to_str()).map(|f| f.to_owned());
|
||||
let account = fs::File::open(&path)
|
||||
.map_err(Into::into)
|
||||
.and_then(|file| key_manager.read(filename, file))?;
|
||||
|
||||
let address = account.address.clone();
|
||||
if !existing_accounts.contains(&address) {
|
||||
dst.insert(account)?;
|
||||
}
|
||||
Ok(address)
|
||||
}
|
||||
|
||||
/// Import all accounts from one directory to the other.
|
||||
pub fn import_accounts(src: &dyn KeyDirectory, dst: &dyn KeyDirectory) -> Result<Vec<Address>, Error> {
|
||||
let accounts = src.load()?;
|
||||
let existing_accounts = dst.load()?.into_iter()
|
||||
.map(|a| a.address)
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
accounts.into_iter()
|
||||
.filter(|a| !existing_accounts.contains(&a.address))
|
||||
.map(|a| {
|
||||
let address = a.address.clone();
|
||||
dst.insert(a)?;
|
||||
Ok(address)
|
||||
}).collect()
|
||||
}
|
||||
|
||||
/// Provide a `HashSet` of all accounts available for import from the Geth keystore.
|
||||
pub fn read_geth_accounts(testnet: bool) -> Vec<Address> {
|
||||
RootDiskDirectory::at(dir::geth(testnet))
|
||||
.load()
|
||||
.map(|d| d.into_iter().map(|a| a.address).collect())
|
||||
.unwrap_or_else(|_| Vec::new())
|
||||
}
|
||||
|
||||
/// Import specific `desired` accounts from the Geth keystore into `dst`.
|
||||
pub fn import_geth_accounts(dst: &dyn KeyDirectory, desired: HashSet<Address>, testnet: bool) -> Result<Vec<Address>, Error> {
|
||||
let src = RootDiskDirectory::at(dir::geth(testnet));
|
||||
let accounts = src.load()?;
|
||||
let existing_accounts = dst.load()?.into_iter().map(|a| a.address).collect::<HashSet<_>>();
|
||||
|
||||
accounts.into_iter()
|
||||
.filter(|a| !existing_accounts.contains(&a.address))
|
||||
.filter(|a| desired.contains(&a.address))
|
||||
.map(|a| {
|
||||
let address = a.address.clone();
|
||||
dst.insert(a)?;
|
||||
Ok(address)
|
||||
}).collect()
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{ops, str};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde::de::Error;
|
||||
use rustc_hex::{ToHex, FromHex, FromHexError};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Bytes(Vec<u8>);
|
||||
|
||||
impl ops::Deref for Bytes {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for Bytes {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where D: Deserializer<'a>
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
let data = s.from_hex().map_err(|e| Error::custom(format!("Invalid hex value {}", e)))?;
|
||||
Ok(Bytes(data))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Bytes {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: Serializer {
|
||||
serializer.serialize_str(&self.0.to_hex())
|
||||
}
|
||||
}
|
||||
|
||||
impl str::FromStr for Bytes {
|
||||
type Err = FromHexError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
s.from_hex().map(Bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static str> for Bytes {
|
||||
fn from(s: &'static str) -> Self {
|
||||
s.parse().expect(&format!("invalid string literal for {}: '{}'", stringify!(Self), s))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for Bytes {
|
||||
fn from(v: Vec<u8>) -> Self {
|
||||
Bytes(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Bytes> for Vec<u8> {
|
||||
fn from(b: Bytes) -> Self {
|
||||
b.0
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::fmt;
|
||||
use serde::{Serialize, Serializer, Deserialize, Deserializer};
|
||||
use serde::de::{Visitor, Error as SerdeError};
|
||||
use super::{Error, H128};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum CipherSer {
|
||||
Aes128Ctr,
|
||||
}
|
||||
|
||||
impl Serialize for CipherSer {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: Serializer {
|
||||
match *self {
|
||||
CipherSer::Aes128Ctr => serializer.serialize_str("aes-128-ctr"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for CipherSer {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where D: Deserializer<'a> {
|
||||
deserializer.deserialize_any(CipherSerVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
struct CipherSerVisitor;
|
||||
|
||||
impl<'a> Visitor<'a> for CipherSerVisitor {
|
||||
type Value = CipherSer;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "a valid cipher identifier")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: SerdeError {
|
||||
match value {
|
||||
"aes-128-ctr" => Ok(CipherSer::Aes128Ctr),
|
||||
_ => Err(SerdeError::custom(Error::UnsupportedCipher))
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_string<E>(self, value: String) -> Result<Self::Value, E> where E: SerdeError {
|
||||
self.visit_str(value.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Aes128Ctr {
|
||||
pub iv: H128,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum CipherSerParams {
|
||||
Aes128Ctr(Aes128Ctr),
|
||||
}
|
||||
|
||||
impl Serialize for CipherSerParams {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: Serializer {
|
||||
match *self {
|
||||
CipherSerParams::Aes128Ctr(ref params) => params.serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for CipherSerParams {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where D: Deserializer<'a> {
|
||||
Aes128Ctr::deserialize(deserializer)
|
||||
.map(CipherSerParams::Aes128Ctr)
|
||||
.map_err(|_| Error::InvalidCipherParams)
|
||||
.map_err(SerdeError::custom)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Cipher {
|
||||
Aes128Ctr(Aes128Ctr),
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{fmt, str};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde::ser::SerializeStruct;
|
||||
use serde::de::{Visitor, MapAccess, Error};
|
||||
use serde_json;
|
||||
use super::{Cipher, CipherSer, CipherSerParams, Kdf, KdfSer, KdfSerParams, H256, Bytes};
|
||||
|
||||
pub type CipherText = Bytes;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Crypto {
|
||||
pub cipher: Cipher,
|
||||
pub ciphertext: CipherText,
|
||||
pub kdf: Kdf,
|
||||
pub mac: H256,
|
||||
}
|
||||
|
||||
impl str::FromStr for Crypto {
|
||||
type Err = serde_json::error::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
serde_json::from_str(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Crypto> for String {
|
||||
fn from(c: Crypto) -> Self {
|
||||
serde_json::to_string(&c).expect("Serialization cannot fail, because all crypto keys are strings")
|
||||
}
|
||||
}
|
||||
|
||||
enum CryptoField {
|
||||
Cipher,
|
||||
CipherParams,
|
||||
CipherText,
|
||||
Kdf,
|
||||
KdfParams,
|
||||
Mac,
|
||||
Version,
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for CryptoField {
|
||||
fn deserialize<D>(deserializer: D) -> Result<CryptoField, D::Error>
|
||||
where D: Deserializer<'a>
|
||||
{
|
||||
deserializer.deserialize_any(CryptoFieldVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
struct CryptoFieldVisitor;
|
||||
|
||||
impl<'a> Visitor<'a> for CryptoFieldVisitor {
|
||||
type Value = CryptoField;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "a valid crypto struct description")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where E: Error
|
||||
{
|
||||
match value {
|
||||
"cipher" => Ok(CryptoField::Cipher),
|
||||
"cipherparams" => Ok(CryptoField::CipherParams),
|
||||
"ciphertext" => Ok(CryptoField::CipherText),
|
||||
"kdf" => Ok(CryptoField::Kdf),
|
||||
"kdfparams" => Ok(CryptoField::KdfParams),
|
||||
"mac" => Ok(CryptoField::Mac),
|
||||
"version" => Ok(CryptoField::Version),
|
||||
_ => Err(Error::custom(format!("Unknown field: '{}'", value))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for Crypto {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Crypto, D::Error>
|
||||
where D: Deserializer<'a>
|
||||
{
|
||||
static FIELDS: &'static [&'static str] = &["id", "version", "crypto", "Crypto", "address"];
|
||||
deserializer.deserialize_struct("Crypto", FIELDS, CryptoVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
struct CryptoVisitor;
|
||||
|
||||
impl<'a> Visitor<'a> for CryptoVisitor {
|
||||
type Value = Crypto;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "a valid vault crypto object")
|
||||
}
|
||||
|
||||
fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
|
||||
where V: MapAccess<'a>
|
||||
{
|
||||
let mut cipher = None;
|
||||
let mut cipherparams = None;
|
||||
let mut ciphertext = None;
|
||||
let mut kdf = None;
|
||||
let mut kdfparams = None;
|
||||
let mut mac = None;
|
||||
|
||||
loop {
|
||||
match visitor.next_key()? {
|
||||
Some(CryptoField::Cipher) => { cipher = Some(visitor.next_value()?); }
|
||||
Some(CryptoField::CipherParams) => { cipherparams = Some(visitor.next_value()?); }
|
||||
Some(CryptoField::CipherText) => { ciphertext = Some(visitor.next_value()?); }
|
||||
Some(CryptoField::Kdf) => { kdf = Some(visitor.next_value()?); }
|
||||
Some(CryptoField::KdfParams) => { kdfparams = Some(visitor.next_value()?); }
|
||||
Some(CryptoField::Mac) => { mac = Some(visitor.next_value()?); }
|
||||
// skip not required version field (it appears in pyethereum generated keystores)
|
||||
Some(CryptoField::Version) => { visitor.next_value().unwrap_or(()) }
|
||||
None => { break; }
|
||||
}
|
||||
}
|
||||
|
||||
let cipher = match (cipher, cipherparams) {
|
||||
(Some(CipherSer::Aes128Ctr), Some(CipherSerParams::Aes128Ctr(params))) => Cipher::Aes128Ctr(params),
|
||||
(None, _) => return Err(V::Error::missing_field("cipher")),
|
||||
(Some(_), None) => return Err(V::Error::missing_field("cipherparams")),
|
||||
};
|
||||
|
||||
let ciphertext = match ciphertext {
|
||||
Some(ciphertext) => ciphertext,
|
||||
None => return Err(V::Error::missing_field("ciphertext")),
|
||||
};
|
||||
|
||||
let kdf = match (kdf, kdfparams) {
|
||||
(Some(KdfSer::Pbkdf2), Some(KdfSerParams::Pbkdf2(params))) => Kdf::Pbkdf2(params),
|
||||
(Some(KdfSer::Scrypt), Some(KdfSerParams::Scrypt(params))) => Kdf::Scrypt(params),
|
||||
(Some(_), Some(_)) => return Err(V::Error::custom("Invalid cipherparams")),
|
||||
(None, _) => return Err(V::Error::missing_field("kdf")),
|
||||
(Some(_), None) => return Err(V::Error::missing_field("kdfparams")),
|
||||
};
|
||||
|
||||
let mac = match mac {
|
||||
Some(mac) => mac,
|
||||
None => return Err(V::Error::missing_field("mac")),
|
||||
};
|
||||
|
||||
let result = Crypto {
|
||||
cipher: cipher,
|
||||
ciphertext: ciphertext,
|
||||
kdf: kdf,
|
||||
mac: mac,
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Crypto {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: Serializer
|
||||
{
|
||||
let mut crypto = serializer.serialize_struct("Crypto", 6)?;
|
||||
match self.cipher {
|
||||
Cipher::Aes128Ctr(ref params) => {
|
||||
crypto.serialize_field("cipher", &CipherSer::Aes128Ctr)?;
|
||||
crypto.serialize_field("cipherparams", params)?;
|
||||
},
|
||||
}
|
||||
crypto.serialize_field("ciphertext", &self.ciphertext)?;
|
||||
match self.kdf {
|
||||
Kdf::Pbkdf2(ref params) => {
|
||||
crypto.serialize_field("kdf", &KdfSer::Pbkdf2)?;
|
||||
crypto.serialize_field("kdfparams", params)?;
|
||||
},
|
||||
Kdf::Scrypt(ref params) => {
|
||||
crypto.serialize_field("kdf", &KdfSer::Scrypt)?;
|
||||
crypto.serialize_field("kdfparams", params)?;
|
||||
},
|
||||
}
|
||||
|
||||
crypto.serialize_field("mac", &self.mac)?;
|
||||
crypto.end()
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
UnsupportedCipher,
|
||||
InvalidCipherParams,
|
||||
UnsupportedKdf,
|
||||
InvalidUuid,
|
||||
UnsupportedVersion,
|
||||
InvalidCiphertext,
|
||||
InvalidH256,
|
||||
InvalidPrf,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
Error::InvalidUuid => write!(f, "Invalid Uuid"),
|
||||
Error::UnsupportedVersion => write!(f, "Unsupported version"),
|
||||
Error::UnsupportedKdf => write!(f, "Unsupported kdf"),
|
||||
Error::InvalidCiphertext => write!(f, "Invalid ciphertext"),
|
||||
Error::UnsupportedCipher => write!(f, "Unsupported cipher"),
|
||||
Error::InvalidCipherParams => write!(f, "Invalid cipher params"),
|
||||
Error::InvalidH256 => write!(f, "Invalid hash"),
|
||||
Error::InvalidPrf => write!(f, "Invalid prf"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<String> for Error {
|
||||
fn into(self) -> String {
|
||||
format!("{}", self)
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{ops, fmt, str};
|
||||
use rustc_hex::{FromHex, ToHex};
|
||||
use serde::{Serialize, Serializer, Deserialize, Deserializer};
|
||||
use serde::de::{Visitor, Error as SerdeError};
|
||||
use super::Error;
|
||||
|
||||
macro_rules! impl_hash {
|
||||
($name: ident, $size: expr) => {
|
||||
pub struct $name([u8; $size]);
|
||||
|
||||
impl fmt::Debug for $name {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
let self_ref: &[u8] = &self.0;
|
||||
write!(f, "{:?}", self_ref)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for $name {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
let self_ref: &[u8] = &self.0;
|
||||
let other_ref: &[u8] = &other.0;
|
||||
self_ref == other_ref
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for $name {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for $name {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: Serializer {
|
||||
serializer.serialize_str(&self.0.to_hex())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for $name {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where D: Deserializer<'a> {
|
||||
struct HashVisitor;
|
||||
|
||||
impl<'b> Visitor<'b> for HashVisitor {
|
||||
type Value = $name;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "a hex-encoded {}", stringify!($name))
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: SerdeError {
|
||||
value.parse().map_err(SerdeError::custom)
|
||||
}
|
||||
|
||||
fn visit_string<E>(self, value: String) -> Result<Self::Value, E> where E: SerdeError {
|
||||
self.visit_str(value.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(HashVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl str::FromStr for $name {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
||||
match value.from_hex() {
|
||||
Ok(ref hex) if hex.len() == $size => {
|
||||
let mut hash = [0u8; $size];
|
||||
hash.clone_from_slice(hex);
|
||||
Ok($name(hash))
|
||||
}
|
||||
_ => Err(Error::InvalidH256),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static str> for $name {
|
||||
fn from(s: &'static str) -> Self {
|
||||
s.parse().expect(&format!("invalid string literal for {}: '{}'", stringify!($name), s))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; $size]> for $name {
|
||||
fn from(bytes: [u8; $size]) -> Self {
|
||||
$name(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<[u8; $size]> for $name {
|
||||
fn into(self) -> [u8; $size] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_hash!(H128, 16);
|
||||
impl_hash!(H160, 20);
|
||||
impl_hash!(H256, 32);
|
||||
@@ -1,153 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Universaly unique identifier.
|
||||
use std::{fmt, str};
|
||||
use rustc_hex::{ToHex, FromHex};
|
||||
use serde::{Deserialize, Serialize, Deserializer, Serializer};
|
||||
use serde::de::{Visitor, Error as SerdeError};
|
||||
use super::Error;
|
||||
|
||||
/// Universaly unique identifier.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Uuid([u8; 16]);
|
||||
|
||||
impl From<[u8; 16]> for Uuid {
|
||||
fn from(uuid: [u8; 16]) -> Self {
|
||||
Uuid(uuid)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<String> for &'a Uuid {
|
||||
fn into(self) -> String {
|
||||
let d1 = &self.0[0..4];
|
||||
let d2 = &self.0[4..6];
|
||||
let d3 = &self.0[6..8];
|
||||
let d4 = &self.0[8..10];
|
||||
let d5 = &self.0[10..16];
|
||||
[d1, d2, d3, d4, d5].iter().map(|d| d.to_hex()).collect::<Vec<String>>().join("-")
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<String> for Uuid {
|
||||
fn into(self) -> String {
|
||||
Into::into(&self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<[u8; 16]> for Uuid {
|
||||
fn into(self) -> [u8; 16] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Uuid {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
let s: String = (self as &Uuid).into();
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_into(from: &str, into: &mut [u8]) -> Result<(), Error> {
|
||||
let from = from.from_hex().map_err(|_| Error::InvalidUuid)?;
|
||||
|
||||
if from.len() != into.len() {
|
||||
return Err(Error::InvalidUuid);
|
||||
}
|
||||
|
||||
into.copy_from_slice(&from);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl str::FromStr for Uuid {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let parts: Vec<&str> = s.split("-").collect();
|
||||
|
||||
if parts.len() != 5 {
|
||||
return Err(Error::InvalidUuid);
|
||||
}
|
||||
|
||||
let mut uuid = [0u8; 16];
|
||||
|
||||
copy_into(parts[0], &mut uuid[0..4])?;
|
||||
copy_into(parts[1], &mut uuid[4..6])?;
|
||||
copy_into(parts[2], &mut uuid[6..8])?;
|
||||
copy_into(parts[3], &mut uuid[8..10])?;
|
||||
copy_into(parts[4], &mut uuid[10..16])?;
|
||||
|
||||
Ok(Uuid(uuid))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'static str> for Uuid {
|
||||
fn from(s: &'static str) -> Self {
|
||||
s.parse().expect(&format!("invalid string literal for {}: '{}'", stringify!(Self), s))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Uuid {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: Serializer {
|
||||
let s: String = self.into();
|
||||
serializer.serialize_str(&s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for Uuid {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where D: Deserializer<'a> {
|
||||
deserializer.deserialize_any(UuidVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
struct UuidVisitor;
|
||||
|
||||
impl<'a> Visitor<'a> for UuidVisitor {
|
||||
type Value = Uuid;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "a valid hex-encoded UUID")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: SerdeError {
|
||||
value.parse().map_err(SerdeError::custom)
|
||||
}
|
||||
|
||||
fn visit_string<E>(self, value: String) -> Result<Self::Value, E> where E: SerdeError {
|
||||
self.visit_str(value.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Uuid;
|
||||
|
||||
#[test]
|
||||
fn uuid_from_str() {
|
||||
let uuid: Uuid = "3198bc9c-6672-5ab3-d995-4942343ae5b6".into();
|
||||
assert_eq!(uuid, Uuid::from([0x31, 0x98, 0xbc, 0x9c, 0x66, 0x72, 0x5a, 0xb3, 0xd9, 0x95, 0x49, 0x42, 0x34, 0x3a, 0xe5, 0xb6]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uuid_from_and_to_str() {
|
||||
let from = "3198bc9c-6672-5ab3-d995-4942343ae5b6";
|
||||
let uuid: Uuid = from.into();
|
||||
let to: String = uuid.into();
|
||||
assert_eq!(from, &to);
|
||||
}
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::fmt;
|
||||
use serde::{Serialize, Serializer, Deserialize, Deserializer};
|
||||
use serde::de::{Visitor, Error as SerdeError};
|
||||
use super::{Error, Bytes};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum KdfSer {
|
||||
Pbkdf2,
|
||||
Scrypt,
|
||||
}
|
||||
|
||||
impl Serialize for KdfSer {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: Serializer {
|
||||
match *self {
|
||||
KdfSer::Pbkdf2 => serializer.serialize_str("pbkdf2"),
|
||||
KdfSer::Scrypt => serializer.serialize_str("scrypt"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for KdfSer {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where D: Deserializer<'a> {
|
||||
deserializer.deserialize_any(KdfSerVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
struct KdfSerVisitor;
|
||||
|
||||
impl<'a> Visitor<'a> for KdfSerVisitor {
|
||||
type Value = KdfSer;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "a kdf algorithm identifier")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: SerdeError {
|
||||
match value {
|
||||
"pbkdf2" => Ok(KdfSer::Pbkdf2),
|
||||
"scrypt" => Ok(KdfSer::Scrypt),
|
||||
_ => Err(SerdeError::custom(Error::UnsupportedKdf))
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_string<E>(self, value: String) -> Result<Self::Value, E> where E: SerdeError {
|
||||
self.visit_str(value.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Prf {
|
||||
HmacSha256,
|
||||
}
|
||||
|
||||
impl Serialize for Prf {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: Serializer {
|
||||
match *self {
|
||||
Prf::HmacSha256 => serializer.serialize_str("hmac-sha256"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for Prf {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where D: Deserializer<'a> {
|
||||
deserializer.deserialize_any(PrfVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
struct PrfVisitor;
|
||||
|
||||
impl<'a> Visitor<'a> for PrfVisitor {
|
||||
type Value = Prf;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "a prf algorithm identifier")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: SerdeError {
|
||||
match value {
|
||||
"hmac-sha256" => Ok(Prf::HmacSha256),
|
||||
_ => Err(SerdeError::custom(Error::InvalidPrf)),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_string<E>(self, value: String) -> Result<Self::Value, E> where E: SerdeError {
|
||||
self.visit_str(value.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Pbkdf2 {
|
||||
pub c: u32,
|
||||
pub dklen: u32,
|
||||
pub prf: Prf,
|
||||
pub salt: Bytes,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Scrypt {
|
||||
pub dklen: u32,
|
||||
pub p: u32,
|
||||
pub n: u32,
|
||||
pub r: u32,
|
||||
pub salt: Bytes,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum KdfSerParams {
|
||||
Pbkdf2(Pbkdf2),
|
||||
Scrypt(Scrypt),
|
||||
}
|
||||
|
||||
impl Serialize for KdfSerParams {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: Serializer {
|
||||
match *self {
|
||||
KdfSerParams::Pbkdf2(ref params) => params.serialize(serializer),
|
||||
KdfSerParams::Scrypt(ref params) => params.serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for KdfSerParams {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where D: Deserializer<'a> {
|
||||
use serde_json::{Value, from_value};
|
||||
|
||||
let v: Value = Deserialize::deserialize(deserializer)?;
|
||||
|
||||
from_value(v.clone()).map(KdfSerParams::Pbkdf2)
|
||||
.or_else(|_| from_value(v).map(KdfSerParams::Scrypt))
|
||||
.map_err(|_| D::Error::custom("Invalid KDF algorithm"))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Kdf {
|
||||
Pbkdf2(Pbkdf2),
|
||||
Scrypt(Scrypt),
|
||||
}
|
||||
@@ -1,324 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::fmt;
|
||||
use std::io::{Read, Write};
|
||||
use serde::{Serialize, Serializer, Deserialize, Deserializer};
|
||||
use serde::de::{Error, Visitor, MapAccess, DeserializeOwned};
|
||||
use serde_json;
|
||||
use super::{Uuid, Version, Crypto, H160};
|
||||
|
||||
/// Public opaque type representing serializable `KeyFile`.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct OpaqueKeyFile {
|
||||
key_file: KeyFile
|
||||
}
|
||||
|
||||
impl Serialize for OpaqueKeyFile {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where
|
||||
S: Serializer,
|
||||
{
|
||||
self.key_file.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for OpaqueKeyFile where T: Into<KeyFile> {
|
||||
fn from(val: T) -> Self {
|
||||
OpaqueKeyFile { key_file: val.into() }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize)]
|
||||
pub struct KeyFile {
|
||||
pub id: Uuid,
|
||||
pub version: Version,
|
||||
pub crypto: Crypto,
|
||||
pub address: Option<H160>,
|
||||
pub name: Option<String>,
|
||||
pub meta: Option<String>,
|
||||
}
|
||||
|
||||
enum KeyFileField {
|
||||
Id,
|
||||
Version,
|
||||
Crypto,
|
||||
Address,
|
||||
Name,
|
||||
Meta,
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for KeyFileField {
|
||||
fn deserialize<D>(deserializer: D) -> Result<KeyFileField, D::Error>
|
||||
where D: Deserializer<'a>
|
||||
{
|
||||
deserializer.deserialize_any(KeyFileFieldVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
struct KeyFileFieldVisitor;
|
||||
|
||||
impl<'a> Visitor<'a> for KeyFileFieldVisitor {
|
||||
type Value = KeyFileField;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "a valid key file field")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where E: Error
|
||||
{
|
||||
match value {
|
||||
"id" => Ok(KeyFileField::Id),
|
||||
"version" => Ok(KeyFileField::Version),
|
||||
"crypto" => Ok(KeyFileField::Crypto),
|
||||
"Crypto" => Ok(KeyFileField::Crypto),
|
||||
"address" => Ok(KeyFileField::Address),
|
||||
"name" => Ok(KeyFileField::Name),
|
||||
"meta" => Ok(KeyFileField::Meta),
|
||||
_ => Err(Error::custom(format!("Unknown field: '{}'", value))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for KeyFile {
|
||||
fn deserialize<D>(deserializer: D) -> Result<KeyFile, D::Error>
|
||||
where D: Deserializer<'a>
|
||||
{
|
||||
static FIELDS: &'static [&'static str] = &["id", "version", "crypto", "Crypto", "address"];
|
||||
deserializer.deserialize_struct("KeyFile", FIELDS, KeyFileVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
fn none_if_empty<'a, T>(v: Option<serde_json::Value>) -> Option<T> where
|
||||
T: DeserializeOwned
|
||||
{
|
||||
v.and_then(|v| if v.is_null() {
|
||||
None
|
||||
} else {
|
||||
serde_json::from_value(v).ok()
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
struct KeyFileVisitor;
|
||||
impl<'a> Visitor<'a> for KeyFileVisitor {
|
||||
type Value = KeyFile;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "a valid key object")
|
||||
}
|
||||
|
||||
fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
|
||||
where V: MapAccess<'a>
|
||||
{
|
||||
let mut id = None;
|
||||
let mut version = None;
|
||||
let mut crypto = None;
|
||||
let mut address = None;
|
||||
let mut name = None;
|
||||
let mut meta = None;
|
||||
|
||||
loop {
|
||||
match visitor.next_key()? {
|
||||
Some(KeyFileField::Id) => { id = Some(visitor.next_value()?); }
|
||||
Some(KeyFileField::Version) => { version = Some(visitor.next_value()?); }
|
||||
Some(KeyFileField::Crypto) => { crypto = Some(visitor.next_value()?); }
|
||||
Some(KeyFileField::Address) => { address = Some(visitor.next_value()?); }
|
||||
Some(KeyFileField::Name) => { name = none_if_empty(visitor.next_value().ok()) }
|
||||
Some(KeyFileField::Meta) => { meta = none_if_empty(visitor.next_value().ok()) }
|
||||
None => { break; }
|
||||
}
|
||||
}
|
||||
|
||||
let id = match id {
|
||||
Some(id) => id,
|
||||
None => return Err(V::Error::missing_field("id")),
|
||||
};
|
||||
|
||||
let version = match version {
|
||||
Some(version) => version,
|
||||
None => return Err(V::Error::missing_field("version")),
|
||||
};
|
||||
|
||||
let crypto = match crypto {
|
||||
Some(crypto) => crypto,
|
||||
None => return Err(V::Error::missing_field("crypto")),
|
||||
};
|
||||
|
||||
let result = KeyFile {
|
||||
id: id,
|
||||
version: version,
|
||||
crypto: crypto,
|
||||
address: address,
|
||||
name: name,
|
||||
meta: meta,
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyFile {
|
||||
pub fn load<R>(reader: R) -> Result<Self, serde_json::Error> where R: Read {
|
||||
serde_json::from_reader(reader)
|
||||
}
|
||||
|
||||
pub fn write<W>(&self, writer: &mut W) -> Result<(), serde_json::Error> where W: Write {
|
||||
serde_json::to_writer(writer, self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
use serde_json;
|
||||
use json::{KeyFile, Uuid, Version, Crypto, Cipher, Aes128Ctr, Kdf, Scrypt};
|
||||
|
||||
#[test]
|
||||
fn basic_keyfile() {
|
||||
let json = r#"
|
||||
{
|
||||
"address": "6edddfc6349aff20bc6467ccf276c5b52487f7a8",
|
||||
"crypto": {
|
||||
"cipher": "aes-128-ctr",
|
||||
"ciphertext": "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc",
|
||||
"cipherparams": {
|
||||
"iv": "b5a7ec855ec9e2c405371356855fec83"
|
||||
},
|
||||
"kdf": "scrypt",
|
||||
"kdfparams": {
|
||||
"dklen": 32,
|
||||
"n": 262144,
|
||||
"p": 1,
|
||||
"r": 8,
|
||||
"salt": "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209"
|
||||
},
|
||||
"mac": "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f"
|
||||
},
|
||||
"id": "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73",
|
||||
"version": 3,
|
||||
"name": "Test",
|
||||
"meta": "{}"
|
||||
}"#;
|
||||
|
||||
let expected = KeyFile {
|
||||
id: Uuid::from_str("8777d9f6-7860-4b9b-88b7-0b57ee6b3a73").unwrap(),
|
||||
version: Version::V3,
|
||||
address: Some("6edddfc6349aff20bc6467ccf276c5b52487f7a8".into()),
|
||||
crypto: Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||
iv: "b5a7ec855ec9e2c405371356855fec83".into(),
|
||||
}),
|
||||
ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc".into(),
|
||||
kdf: Kdf::Scrypt(Scrypt {
|
||||
n: 262144,
|
||||
dklen: 32,
|
||||
p: 1,
|
||||
r: 8,
|
||||
salt: "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209".into(),
|
||||
}),
|
||||
mac: "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f".into(),
|
||||
},
|
||||
name: Some("Test".to_owned()),
|
||||
meta: Some("{}".to_owned()),
|
||||
};
|
||||
|
||||
let keyfile: KeyFile = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(keyfile, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn capital_crypto_keyfile() {
|
||||
let json = r#"
|
||||
{
|
||||
"address": "6edddfc6349aff20bc6467ccf276c5b52487f7a8",
|
||||
"Crypto": {
|
||||
"cipher": "aes-128-ctr",
|
||||
"ciphertext": "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc",
|
||||
"cipherparams": {
|
||||
"iv": "b5a7ec855ec9e2c405371356855fec83"
|
||||
},
|
||||
"kdf": "scrypt",
|
||||
"kdfparams": {
|
||||
"dklen": 32,
|
||||
"n": 262144,
|
||||
"p": 1,
|
||||
"r": 8,
|
||||
"salt": "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209"
|
||||
},
|
||||
"mac": "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f"
|
||||
},
|
||||
"id": "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73",
|
||||
"version": 3
|
||||
}"#;
|
||||
|
||||
let expected = KeyFile {
|
||||
id: "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73".into(),
|
||||
version: Version::V3,
|
||||
address: Some("6edddfc6349aff20bc6467ccf276c5b52487f7a8".into()),
|
||||
crypto: Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||
iv: "b5a7ec855ec9e2c405371356855fec83".into(),
|
||||
}),
|
||||
ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc".into(),
|
||||
kdf: Kdf::Scrypt(Scrypt {
|
||||
n: 262144,
|
||||
dklen: 32,
|
||||
p: 1,
|
||||
r: 8,
|
||||
salt: "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209".into(),
|
||||
}),
|
||||
mac: "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f".into(),
|
||||
},
|
||||
name: None,
|
||||
meta: None,
|
||||
};
|
||||
|
||||
let keyfile: KeyFile = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(keyfile, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_and_from_json() {
|
||||
let file = KeyFile {
|
||||
id: "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73".into(),
|
||||
version: Version::V3,
|
||||
address: Some("6edddfc6349aff20bc6467ccf276c5b52487f7a8".into()),
|
||||
crypto: Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||
iv: "b5a7ec855ec9e2c405371356855fec83".into(),
|
||||
}),
|
||||
ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc".into(),
|
||||
kdf: Kdf::Scrypt(Scrypt {
|
||||
n: 262144,
|
||||
dklen: 32,
|
||||
p: 1,
|
||||
r: 8,
|
||||
salt: "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209".into(),
|
||||
}),
|
||||
mac: "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f".into(),
|
||||
},
|
||||
name: Some("Test".to_owned()),
|
||||
meta: None,
|
||||
};
|
||||
|
||||
let serialized = serde_json::to_string(&file).unwrap();
|
||||
println!("{}", serialized);
|
||||
let deserialized = serde_json::from_str(&serialized).unwrap();
|
||||
|
||||
assert_eq!(file, deserialized);
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Contract interface specification.
|
||||
|
||||
mod bytes;
|
||||
mod cipher;
|
||||
mod crypto;
|
||||
mod error;
|
||||
mod hash;
|
||||
mod id;
|
||||
mod kdf;
|
||||
mod key_file;
|
||||
mod presale;
|
||||
mod vault_file;
|
||||
mod vault_key_file;
|
||||
mod version;
|
||||
|
||||
pub use self::bytes::Bytes;
|
||||
pub use self::cipher::{Cipher, CipherSer, CipherSerParams, Aes128Ctr};
|
||||
pub use self::crypto::{Crypto, CipherText};
|
||||
pub use self::error::Error;
|
||||
pub use self::hash::{H128, H160, H256};
|
||||
pub use self::id::Uuid;
|
||||
pub use self::kdf::{Kdf, KdfSer, Prf, Pbkdf2, Scrypt, KdfSerParams};
|
||||
pub use self::key_file::{KeyFile, OpaqueKeyFile};
|
||||
pub use self::presale::{PresaleWallet, Encseed};
|
||||
pub use self::vault_file::VaultFile;
|
||||
pub use self::vault_key_file::{VaultKeyFile, VaultKeyMeta, insert_vault_name_to_json_meta, remove_vault_name_from_json_meta};
|
||||
pub use self::version::Version;
|
||||
@@ -1,80 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::io::Read;
|
||||
use serde_json;
|
||||
use super::{H160, Bytes};
|
||||
|
||||
pub type Encseed = Bytes;
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub struct PresaleWallet {
|
||||
pub encseed: Encseed,
|
||||
#[serde(rename = "ethaddr")]
|
||||
pub address: H160,
|
||||
}
|
||||
|
||||
impl PresaleWallet {
|
||||
pub fn load<R>(reader: R) -> Result<Self, serde_json::Error> where R: Read {
|
||||
serde_json::from_reader(reader)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
use serde_json;
|
||||
use json::{PresaleWallet, H160};
|
||||
|
||||
#[test]
|
||||
fn presale_wallet() {
|
||||
let json = r#"
|
||||
{
|
||||
"encseed": "137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066",
|
||||
"ethaddr": "ede84640d1a1d3e06902048e67aa7db8d52c2ce1",
|
||||
"email": "123@gmail.com",
|
||||
"btcaddr": "1JvqEc6WLhg6GnyrLBe2ztPAU28KRfuseH"
|
||||
} "#;
|
||||
|
||||
let expected = PresaleWallet {
|
||||
encseed: "137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066".into(),
|
||||
address: H160::from_str("ede84640d1a1d3e06902048e67aa7db8d52c2ce1").unwrap(),
|
||||
};
|
||||
|
||||
let wallet: PresaleWallet = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(expected, wallet);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn long_presale_wallet() {
|
||||
let json = r#"
|
||||
{
|
||||
"encseed":
|
||||
"137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0d",
|
||||
"ethaddr": "ede84640d1a1d3e06902048e67aa7db8d52c2ce1",
|
||||
"email": "123@gmail.com",
|
||||
"btcaddr": "1JvqEc6WLhg6GnyrLBe2ztPAU28KRfuseH"
|
||||
} "#;
|
||||
|
||||
let expected = PresaleWallet {
|
||||
encseed: "137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0dcf0d".into(),
|
||||
address: H160::from_str("ede84640d1a1d3e06902048e67aa7db8d52c2ce1").unwrap(),
|
||||
};
|
||||
|
||||
let wallet: PresaleWallet = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(expected, wallet);
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::io::{Read, Write};
|
||||
use serde_json;
|
||||
use super::Crypto;
|
||||
|
||||
/// Vault meta file
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct VaultFile {
|
||||
/// Vault password, encrypted with vault password
|
||||
pub crypto: Crypto,
|
||||
/// Vault metadata string
|
||||
pub meta: Option<String>,
|
||||
}
|
||||
|
||||
impl VaultFile {
|
||||
pub fn load<R>(reader: R) -> Result<Self, serde_json::Error> where R: Read {
|
||||
serde_json::from_reader(reader)
|
||||
}
|
||||
|
||||
pub fn write<W>(&self, writer: &mut W) -> Result<(), serde_json::Error> where W: Write {
|
||||
serde_json::to_writer(writer, self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use serde_json;
|
||||
use json::{VaultFile, Crypto, Cipher, Aes128Ctr, Kdf, Pbkdf2, Prf};
|
||||
|
||||
#[test]
|
||||
fn to_and_from_json() {
|
||||
let file = VaultFile {
|
||||
crypto: Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||
iv: "0155e3690be19fbfbecabcd440aa284b".into(),
|
||||
}),
|
||||
ciphertext: "4d6938a1f49b7782".into(),
|
||||
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
||||
c: 1024,
|
||||
dklen: 32,
|
||||
prf: Prf::HmacSha256,
|
||||
salt: "b6a9338a7ccd39288a86dba73bfecd9101b4f3db9c9830e7c76afdbd4f6872e5".into(),
|
||||
}),
|
||||
mac: "16381463ea11c6eb2239a9f339c2e780516d29d234ce30ac5f166f9080b5a262".into(),
|
||||
},
|
||||
meta: Some("{}".into()),
|
||||
};
|
||||
|
||||
let serialized = serde_json::to_string(&file).unwrap();
|
||||
let deserialized = serde_json::from_str(&serialized).unwrap();
|
||||
|
||||
assert_eq!(file, deserialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_and_from_json_no_meta() {
|
||||
let file = VaultFile {
|
||||
crypto: Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||
iv: "0155e3690be19fbfbecabcd440aa284b".into(),
|
||||
}),
|
||||
ciphertext: "4d6938a1f49b7782".into(),
|
||||
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
||||
c: 1024,
|
||||
dklen: 32,
|
||||
prf: Prf::HmacSha256,
|
||||
salt: "b6a9338a7ccd39288a86dba73bfecd9101b4f3db9c9830e7c76afdbd4f6872e5".into(),
|
||||
}),
|
||||
mac: "16381463ea11c6eb2239a9f339c2e780516d29d234ce30ac5f166f9080b5a262".into(),
|
||||
},
|
||||
meta: None,
|
||||
};
|
||||
|
||||
let serialized = serde_json::to_string(&file).unwrap();
|
||||
let deserialized = serde_json::from_str(&serialized).unwrap();
|
||||
|
||||
assert_eq!(file, deserialized);
|
||||
}
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::io::{Read, Write};
|
||||
use serde::de::Error;
|
||||
use serde_json;
|
||||
use serde_json::value::Value;
|
||||
use serde_json::error;
|
||||
use super::{Uuid, Version, Crypto, H160};
|
||||
|
||||
/// Meta key name for vault field
|
||||
const VAULT_NAME_META_KEY: &'static str = "vault";
|
||||
|
||||
/// Key file as stored in vaults
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct VaultKeyFile {
|
||||
/// Key id
|
||||
pub id: Uuid,
|
||||
/// Key version
|
||||
pub version: Version,
|
||||
/// Secret, encrypted with account password
|
||||
pub crypto: Crypto,
|
||||
/// Serialized `VaultKeyMeta`, encrypted with vault password
|
||||
pub metacrypto: Crypto,
|
||||
}
|
||||
|
||||
/// Data, stored in `VaultKeyFile::metacrypto`
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct VaultKeyMeta {
|
||||
/// Key address
|
||||
pub address: H160,
|
||||
/// Key name
|
||||
pub name: Option<String>,
|
||||
/// Key metadata
|
||||
pub meta: Option<String>,
|
||||
}
|
||||
|
||||
/// Insert vault name to the JSON meta field
|
||||
pub fn insert_vault_name_to_json_meta(meta: &str, vault_name: &str) -> Result<String, error::Error> {
|
||||
let mut meta = if meta.is_empty() {
|
||||
Value::Object(serde_json::Map::new())
|
||||
} else {
|
||||
serde_json::from_str(meta)?
|
||||
};
|
||||
|
||||
if let Some(meta_obj) = meta.as_object_mut() {
|
||||
meta_obj.insert(VAULT_NAME_META_KEY.to_owned(), Value::String(vault_name.to_owned()));
|
||||
serde_json::to_string(meta_obj)
|
||||
} else {
|
||||
Err(error::Error::custom("Meta is expected to be a serialized JSON object"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove vault name from the JSON meta field
|
||||
pub fn remove_vault_name_from_json_meta(meta: &str) -> Result<String, error::Error> {
|
||||
let mut meta = if meta.is_empty() {
|
||||
Value::Object(serde_json::Map::new())
|
||||
} else {
|
||||
serde_json::from_str(meta)?
|
||||
};
|
||||
|
||||
if let Some(meta_obj) = meta.as_object_mut() {
|
||||
meta_obj.remove(VAULT_NAME_META_KEY);
|
||||
serde_json::to_string(meta_obj)
|
||||
} else {
|
||||
Err(error::Error::custom("Meta is expected to be a serialized JSON object"))
|
||||
}
|
||||
}
|
||||
|
||||
impl VaultKeyFile {
|
||||
pub fn load<R>(reader: R) -> Result<Self, serde_json::Error> where R: Read {
|
||||
serde_json::from_reader(reader)
|
||||
}
|
||||
|
||||
pub fn write<W>(&self, writer: &mut W) -> Result<(), serde_json::Error> where W: Write {
|
||||
serde_json::to_writer(writer, self)
|
||||
}
|
||||
}
|
||||
|
||||
impl VaultKeyMeta {
|
||||
pub fn load(bytes: &[u8]) -> Result<Self, serde_json::Error> {
|
||||
serde_json::from_slice(&bytes)
|
||||
}
|
||||
|
||||
pub fn write(&self) -> Result<Vec<u8>, serde_json::Error> {
|
||||
let s = serde_json::to_string(self)?;
|
||||
Ok(s.as_bytes().into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use serde_json;
|
||||
use json::{VaultKeyFile, Version, Crypto, Cipher, Aes128Ctr, Kdf, Pbkdf2, Prf,
|
||||
insert_vault_name_to_json_meta, remove_vault_name_from_json_meta};
|
||||
|
||||
#[test]
|
||||
fn to_and_from_json() {
|
||||
let file = VaultKeyFile {
|
||||
id: "08d82c39-88e3-7a71-6abb-89c8f36c3ceb".into(),
|
||||
version: Version::V3,
|
||||
crypto: Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||
iv: "fecb968bbc8c7e608a89ebcfe53a41d0".into(),
|
||||
}),
|
||||
ciphertext: "4befe0a66d9a4b6fec8e39eb5c90ac5dafdeaab005fff1af665fd1f9af925c91".into(),
|
||||
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
||||
c: 10240,
|
||||
dklen: 32,
|
||||
prf: Prf::HmacSha256,
|
||||
salt: "f17731e84ecac390546692dbd4ccf6a3a2720dc9652984978381e61c28a471b2".into(),
|
||||
}),
|
||||
mac: "7c7c3daafb24cf11eb3079dfb9064a11e92f309a0ee1dd676486bab119e686b7".into(),
|
||||
},
|
||||
metacrypto: Crypto {
|
||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||
iv: "9c353fb3f894fc05946843616c26bb3f".into(),
|
||||
}),
|
||||
ciphertext: "fef0d113d7576c1702daf380ad6f4c5408389e57991cae2a174facd74bd549338e1014850bddbab7eb486ff5f5c9c5532800c6a6d4db2be2212cd5cd3769244ab230e1f369e8382a9e6d7c0a".into(),
|
||||
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
||||
c: 10240,
|
||||
dklen: 32,
|
||||
prf: Prf::HmacSha256,
|
||||
salt: "aca82865174a82249a198814b263f43a631f272cbf7ed329d0f0839d259c652a".into(),
|
||||
}),
|
||||
mac: "b7413946bfe459d2801268dc331c04b3a84d92be11ef4dd9a507f895e8d9b5bd".into(),
|
||||
}
|
||||
};
|
||||
|
||||
let serialized = serde_json::to_string(&file).unwrap();
|
||||
let deserialized = serde_json::from_str(&serialized).unwrap();
|
||||
|
||||
assert_eq!(file, deserialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vault_name_inserted_to_json_meta() {
|
||||
assert_eq!(insert_vault_name_to_json_meta(r#""#, "MyVault").unwrap(), r#"{"vault":"MyVault"}"#);
|
||||
assert_eq!(insert_vault_name_to_json_meta(r#"{"tags":["kalabala"]}"#, "MyVault").unwrap(), r#"{"tags":["kalabala"],"vault":"MyVault"}"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vault_name_not_inserted_to_json_meta() {
|
||||
assert!(insert_vault_name_to_json_meta(r#"///3533"#, "MyVault").is_err());
|
||||
assert!(insert_vault_name_to_json_meta(r#""string""#, "MyVault").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vault_name_removed_from_json_meta() {
|
||||
assert_eq!(remove_vault_name_from_json_meta(r#"{"vault":"MyVault"}"#).unwrap(), r#"{}"#);
|
||||
assert_eq!(remove_vault_name_from_json_meta(r#"{"tags":["kalabala"],"vault":"MyVault"}"#).unwrap(), r#"{"tags":["kalabala"]}"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vault_name_not_removed_from_json_meta() {
|
||||
assert!(remove_vault_name_from_json_meta(r#"///3533"#).is_err());
|
||||
assert!(remove_vault_name_from_json_meta(r#""string""#).is_err());
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::fmt;
|
||||
use serde::{Serialize, Serializer, Deserialize, Deserializer};
|
||||
use serde::de::{Error as SerdeError, Visitor};
|
||||
use super::Error;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Version {
|
||||
V3,
|
||||
}
|
||||
|
||||
impl Serialize for Version {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: Serializer {
|
||||
match *self {
|
||||
Version::V3 => serializer.serialize_u64(3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deserialize<'a> for Version {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Version, D::Error>
|
||||
where D: Deserializer<'a> {
|
||||
deserializer.deserialize_any(VersionVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
struct VersionVisitor;
|
||||
|
||||
impl<'a> Visitor<'a> for VersionVisitor {
|
||||
type Value = Version;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "a valid key version identifier")
|
||||
}
|
||||
|
||||
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> where E: SerdeError {
|
||||
match value {
|
||||
3 => Ok(Version::V3),
|
||||
_ => Err(SerdeError::custom(Error::UnsupportedVersion))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Ethereum key-management.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
extern crate dir;
|
||||
extern crate itertools;
|
||||
extern crate libc;
|
||||
extern crate parking_lot;
|
||||
extern crate rand;
|
||||
extern crate rustc_hex;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
extern crate smallvec;
|
||||
extern crate time;
|
||||
extern crate tiny_keccak;
|
||||
extern crate tempdir;
|
||||
|
||||
extern crate parity_crypto as crypto;
|
||||
extern crate ethereum_types;
|
||||
extern crate ethkey as ethkey;
|
||||
extern crate parity_wordlist;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate matches;
|
||||
|
||||
pub mod accounts_dir;
|
||||
|
||||
mod account;
|
||||
mod json;
|
||||
|
||||
mod error;
|
||||
mod ethstore;
|
||||
mod import;
|
||||
mod presale;
|
||||
mod random;
|
||||
mod secret_store;
|
||||
|
||||
pub use self::account::{SafeAccount, Crypto};
|
||||
pub use self::error::Error;
|
||||
pub use self::ethstore::{EthStore, EthMultiStore};
|
||||
pub use self::import::{import_account, import_accounts, read_geth_accounts};
|
||||
pub use self::json::OpaqueKeyFile as KeyFile;
|
||||
pub use self::presale::PresaleWallet;
|
||||
pub use self::secret_store::{
|
||||
SecretVaultRef, StoreAccountRef, SimpleSecretStore, SecretStore,
|
||||
Derivation, IndexDerivation,
|
||||
};
|
||||
pub use self::random::random_string;
|
||||
pub use self::parity_wordlist::random_phrase;
|
||||
|
||||
/// An opaque wrapper for secret.
|
||||
pub struct OpaqueSecret(crypto::publickey::Secret);
|
||||
|
||||
// Additional converters for Address
|
||||
use crypto::publickey::Address;
|
||||
|
||||
impl Into<json::H160> for Address {
|
||||
fn into(self) -> json::H160 {
|
||||
let a: [u8; 20] = self.into();
|
||||
From::from(a)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<json::H160> for Address {
|
||||
fn from(json: json::H160) -> Self {
|
||||
let a: [u8; 20] = json.into();
|
||||
From::from(a)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a json::H160> for Address {
|
||||
fn from(json: &'a json::H160) -> Self {
|
||||
let mut a = [0u8; 20];
|
||||
a.copy_from_slice(json);
|
||||
From::from(a)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use json;
|
||||
use crypto::publickey::{Address, Secret, KeyPair};
|
||||
use ethkey::Password;
|
||||
use crypto::{Keccak256, pbkdf2};
|
||||
use {crypto, Error};
|
||||
|
||||
/// Pre-sale wallet.
|
||||
pub struct PresaleWallet {
|
||||
iv: [u8; 16],
|
||||
ciphertext: Vec<u8>,
|
||||
address: Address,
|
||||
}
|
||||
|
||||
impl From<json::PresaleWallet> for PresaleWallet {
|
||||
fn from(wallet: json::PresaleWallet) -> Self {
|
||||
let mut iv = [0u8; 16];
|
||||
iv.copy_from_slice(&wallet.encseed[..16]);
|
||||
|
||||
let mut ciphertext = vec![];
|
||||
ciphertext.extend_from_slice(&wallet.encseed[16..]);
|
||||
|
||||
PresaleWallet {
|
||||
iv: iv,
|
||||
ciphertext: ciphertext,
|
||||
address: Address::from(wallet.address),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PresaleWallet {
|
||||
/// Open a pre-sale wallet.
|
||||
pub fn open<P>(path: P) -> Result<Self, Error> where P: AsRef<Path> {
|
||||
let file = fs::File::open(path)?;
|
||||
let presale = json::PresaleWallet::load(file)
|
||||
.map_err(|e| Error::InvalidKeyFile(format!("{}", e)))?;
|
||||
Ok(PresaleWallet::from(presale))
|
||||
}
|
||||
|
||||
/// Decrypt the wallet.
|
||||
pub fn decrypt(&self, password: &Password) -> Result<KeyPair, Error> {
|
||||
let mut derived_key = [0u8; 32];
|
||||
let salt = pbkdf2::Salt(password.as_bytes());
|
||||
let sec = pbkdf2::Secret(password.as_bytes());
|
||||
pbkdf2::sha256(2000, salt, sec, &mut derived_key);
|
||||
|
||||
let mut key = vec![0; self.ciphertext.len()];
|
||||
let len = crypto::aes::decrypt_128_cbc(&derived_key[0..16], &self.iv, &self.ciphertext, &mut key)
|
||||
.map_err(|_| Error::InvalidPassword)?;
|
||||
let unpadded = &key[..len];
|
||||
|
||||
let secret = Secret::import_key(&unpadded.keccak256())?;
|
||||
if let Ok(kp) = KeyPair::from_secret(secret) {
|
||||
if kp.address() == self.address {
|
||||
return Ok(kp)
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::InvalidPassword)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::PresaleWallet;
|
||||
use json;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let json = r#"
|
||||
{
|
||||
"encseed": "137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066",
|
||||
"ethaddr": "ede84640d1a1d3e06902048e67aa7db8d52c2ce1",
|
||||
"email": "123@gmail.com",
|
||||
"btcaddr": "1JvqEc6WLhg6GnyrLBe2ztPAU28KRfuseH"
|
||||
} "#;
|
||||
|
||||
let wallet = json::PresaleWallet::load(json.as_bytes()).unwrap();
|
||||
let wallet = PresaleWallet::from(wallet);
|
||||
assert!(wallet.decrypt(&"123".into()).is_ok());
|
||||
assert!(wallet.decrypt(&"124".into()).is_err());
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use rand::{Rng, RngCore, rngs::OsRng, distributions::Alphanumeric};
|
||||
|
||||
pub trait Random {
|
||||
fn random() -> Self where Self: Sized;
|
||||
}
|
||||
|
||||
impl Random for [u8; 16] {
|
||||
fn random() -> Self {
|
||||
let mut result = [0u8; 16];
|
||||
let mut rng = OsRng;
|
||||
rng.fill_bytes(&mut result);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl Random for [u8; 32] {
|
||||
fn random() -> Self {
|
||||
let mut result = [0u8; 32];
|
||||
let mut rng = OsRng;
|
||||
rng.fill_bytes(&mut result);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a random string of given length.
|
||||
pub fn random_string(length: usize) -> String {
|
||||
let rng = OsRng;
|
||||
rng.sample_iter(&Alphanumeric).take(length).collect()
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::path::PathBuf;
|
||||
use std::cmp::Ordering;
|
||||
use crypto::publickey::{Address, Message, Signature, Secret, Public};
|
||||
use ethkey::Password;
|
||||
use Error;
|
||||
use json::{Uuid, OpaqueKeyFile};
|
||||
use ethereum_types::H256;
|
||||
use OpaqueSecret;
|
||||
|
||||
/// Key directory reference
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum SecretVaultRef {
|
||||
/// Reference to key in root directory
|
||||
Root,
|
||||
/// Referenc to key in specific vault
|
||||
Vault(String),
|
||||
}
|
||||
|
||||
/// Stored account reference
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Ord)]
|
||||
pub struct StoreAccountRef {
|
||||
/// Account address
|
||||
pub address: Address,
|
||||
/// Vault reference
|
||||
pub vault: SecretVaultRef,
|
||||
}
|
||||
|
||||
impl PartialOrd for StoreAccountRef {
|
||||
fn partial_cmp(&self, other: &StoreAccountRef) -> Option<Ordering> {
|
||||
Some(self.address.cmp(&other.address).then_with(|| self.vault.cmp(&other.vault)))
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::borrow::Borrow<Address> for StoreAccountRef {
|
||||
fn borrow(&self) -> &Address {
|
||||
&self.address
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple Secret Store API
|
||||
pub trait SimpleSecretStore: Send + Sync {
|
||||
/// Inserts new accounts to the store (or vault) with given password.
|
||||
fn insert_account(&self, vault: SecretVaultRef, secret: Secret, password: &Password) -> Result<StoreAccountRef, Error>;
|
||||
/// Inserts new derived account to the store (or vault) with given password.
|
||||
fn insert_derived(&self, vault: SecretVaultRef, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation) -> Result<StoreAccountRef, Error>;
|
||||
/// Changes accounts password.
|
||||
fn change_password(&self, account: &StoreAccountRef, old_password: &Password, new_password: &Password) -> Result<(), Error>;
|
||||
/// Exports key details for account.
|
||||
fn export_account(&self, account: &StoreAccountRef, password: &Password) -> Result<OpaqueKeyFile, Error>;
|
||||
/// Entirely removes account from the store and underlying storage.
|
||||
fn remove_account(&self, account: &StoreAccountRef, password: &Password) -> Result<(), Error>;
|
||||
/// Generates new derived account.
|
||||
fn generate_derived(&self, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation) -> Result<Address, Error>;
|
||||
/// Sign a message with given account.
|
||||
fn sign(&self, account: &StoreAccountRef, password: &Password, message: &Message) -> Result<Signature, Error>;
|
||||
/// Sign a message with derived account.
|
||||
fn sign_derived(&self, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation, message: &Message) -> Result<Signature, Error>;
|
||||
/// Decrypt a messages with given account.
|
||||
fn decrypt(&self, account: &StoreAccountRef, password: &Password, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error>;
|
||||
/// Agree on shared key.
|
||||
fn agree(&self, account: &StoreAccountRef, password: &Password, other: &Public) -> Result<Secret, Error>;
|
||||
|
||||
/// Returns all accounts in this secret store.
|
||||
fn accounts(&self) -> Result<Vec<StoreAccountRef>, Error>;
|
||||
/// Get reference to some account with given address.
|
||||
/// This method could be removed if we will guarantee that there is max(1) account for given address.
|
||||
fn account_ref(&self, address: &Address) -> Result<StoreAccountRef, Error>;
|
||||
|
||||
/// Create new vault with given password
|
||||
fn create_vault(&self, name: &str, password: &Password) -> Result<(), Error>;
|
||||
/// Open vault with given password
|
||||
fn open_vault(&self, name: &str, password: &Password) -> Result<(), Error>;
|
||||
/// Close vault
|
||||
fn close_vault(&self, name: &str) -> Result<(), Error>;
|
||||
/// List all vaults
|
||||
fn list_vaults(&self) -> Result<Vec<String>, Error>;
|
||||
/// List all currently opened vaults
|
||||
fn list_opened_vaults(&self) -> Result<Vec<String>, Error>;
|
||||
/// Change vault password
|
||||
fn change_vault_password(&self, name: &str, new_password: &Password) -> Result<(), Error>;
|
||||
/// Cnage account' vault
|
||||
fn change_account_vault(&self, vault: SecretVaultRef, account: StoreAccountRef) -> Result<StoreAccountRef, Error>;
|
||||
/// Get vault metadata string.
|
||||
fn get_vault_meta(&self, name: &str) -> Result<String, Error>;
|
||||
/// Set vault metadata string.
|
||||
fn set_vault_meta(&self, name: &str, meta: &str) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
/// Secret Store API
|
||||
pub trait SecretStore: SimpleSecretStore {
|
||||
|
||||
/// Returns a raw opaque Secret that can be later used to sign a message.
|
||||
fn raw_secret(&self, account: &StoreAccountRef, password: &Password) -> Result<OpaqueSecret, Error>;
|
||||
|
||||
/// Signs a message with raw secret.
|
||||
fn sign_with_secret(&self, secret: &OpaqueSecret, message: &Message) -> Result<Signature, Error> {
|
||||
Ok(crypto::publickey::sign(&secret.0, message)?)
|
||||
}
|
||||
|
||||
/// Imports presale wallet
|
||||
fn import_presale(&self, vault: SecretVaultRef, json: &[u8], password: &Password) -> Result<StoreAccountRef, Error>;
|
||||
/// Imports existing JSON wallet
|
||||
fn import_wallet(&self, vault: SecretVaultRef, json: &[u8], password: &Password, gen_id: bool) -> Result<StoreAccountRef, Error>;
|
||||
/// Copies account between stores and vaults.
|
||||
fn copy_account(&self, new_store: &dyn SimpleSecretStore, new_vault: SecretVaultRef, account: &StoreAccountRef, password: &Password, new_password: &Password) -> Result<(), Error>;
|
||||
/// Checks if password matches given account.
|
||||
fn test_password(&self, account: &StoreAccountRef, password: &Password) -> Result<bool, Error>;
|
||||
|
||||
/// Returns a public key for given account.
|
||||
fn public(&self, account: &StoreAccountRef, password: &Password) -> Result<Public, Error>;
|
||||
|
||||
/// Returns uuid of an account.
|
||||
fn uuid(&self, account: &StoreAccountRef) -> Result<Uuid, Error>;
|
||||
/// Returns account's name.
|
||||
fn name(&self, account: &StoreAccountRef) -> Result<String, Error>;
|
||||
/// Returns account's metadata.
|
||||
fn meta(&self, account: &StoreAccountRef) -> Result<String, Error>;
|
||||
|
||||
/// Modifies account metadata.
|
||||
fn set_name(&self, account: &StoreAccountRef, name: String) -> Result<(), Error>;
|
||||
/// Modifies account name.
|
||||
fn set_meta(&self, account: &StoreAccountRef, meta: String) -> Result<(), Error>;
|
||||
|
||||
/// Returns local path of the store.
|
||||
fn local_path(&self) -> PathBuf;
|
||||
/// Lists all found geth accounts.
|
||||
fn list_geth_accounts(&self, testnet: bool) -> Vec<Address>;
|
||||
/// Imports geth accounts to the store/vault.
|
||||
fn import_geth_accounts(&self, vault: SecretVaultRef, desired: Vec<Address>, testnet: bool) -> Result<Vec<StoreAccountRef>, Error>;
|
||||
}
|
||||
|
||||
impl StoreAccountRef {
|
||||
/// Create reference to root account with given address
|
||||
pub fn root(address: Address) -> Self {
|
||||
StoreAccountRef::new(SecretVaultRef::Root, address)
|
||||
}
|
||||
|
||||
/// Create reference to vault account with given address
|
||||
pub fn vault(vault_name: &str, address: Address) -> Self {
|
||||
StoreAccountRef::new(SecretVaultRef::Vault(vault_name.to_owned()), address)
|
||||
}
|
||||
|
||||
/// Create new account reference
|
||||
pub fn new(vault_ref: SecretVaultRef, address: Address) -> Self {
|
||||
StoreAccountRef {
|
||||
vault: vault_ref,
|
||||
address: address,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for StoreAccountRef {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.address.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
/// Node in hierarchical derivation.
|
||||
pub struct IndexDerivation {
|
||||
/// Node is soft (allows proof of parent from parent node).
|
||||
pub soft: bool,
|
||||
/// Index sequence of the node.
|
||||
pub index: u32,
|
||||
}
|
||||
|
||||
/// Derivation scheme for keys
|
||||
pub enum Derivation {
|
||||
/// Hierarchical derivation
|
||||
Hierarchical(Vec<IndexDerivation>),
|
||||
/// Hash derivation, soft.
|
||||
SoftHash(H256),
|
||||
/// Hash derivation, hard.
|
||||
HardHash(H256),
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate rand;
|
||||
extern crate ethstore;
|
||||
extern crate ethereum_types;
|
||||
extern crate parity_crypto;
|
||||
|
||||
mod util;
|
||||
|
||||
use ethstore::{EthStore, SimpleSecretStore, SecretVaultRef, StoreAccountRef};
|
||||
use parity_crypto::publickey::{Random, Generator, Secret, KeyPair, verify_address};
|
||||
use ethstore::accounts_dir::RootDiskDirectory;
|
||||
use util::TransientDir;
|
||||
use ethereum_types::Address;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn secret_store_create() {
|
||||
let dir = TransientDir::create().unwrap();
|
||||
let _ = EthStore::open(Box::new(dir)).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn secret_store_open_not_existing() {
|
||||
let dir = TransientDir::open();
|
||||
let _ = EthStore::open(Box::new(dir)).unwrap();
|
||||
}
|
||||
|
||||
fn random_secret() -> Secret {
|
||||
Random.generate().unwrap().secret().clone()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_store_create_account() {
|
||||
let dir = TransientDir::create().unwrap();
|
||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||
assert_eq!(store.accounts().unwrap().len(), 0);
|
||||
assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok());
|
||||
assert_eq!(store.accounts().unwrap().len(), 1);
|
||||
assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok());
|
||||
assert_eq!(store.accounts().unwrap().len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_store_sign() {
|
||||
let dir = TransientDir::create().unwrap();
|
||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||
assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok());
|
||||
let accounts = store.accounts().unwrap();
|
||||
assert_eq!(accounts.len(), 1);
|
||||
assert!(store.sign(&accounts[0], &"".into(), &Default::default()).is_ok());
|
||||
assert!(store.sign(&accounts[0], &"1".into(), &Default::default()).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_store_change_password() {
|
||||
let dir = TransientDir::create().unwrap();
|
||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||
assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok());
|
||||
let accounts = store.accounts().unwrap();
|
||||
assert_eq!(accounts.len(), 1);
|
||||
assert!(store.sign(&accounts[0], &"".into(), &Default::default()).is_ok());
|
||||
assert!(store.change_password(&accounts[0], &"".into(), &"1".into()).is_ok());
|
||||
assert!(store.sign(&accounts[0], &"".into(), &Default::default()).is_err());
|
||||
assert!(store.sign(&accounts[0], &"1".into(), &Default::default()).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_store_remove_account() {
|
||||
let dir = TransientDir::create().unwrap();
|
||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||
assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok());
|
||||
let accounts = store.accounts().unwrap();
|
||||
assert_eq!(accounts.len(), 1);
|
||||
assert!(store.remove_account(&accounts[0], &"".into()).is_ok());
|
||||
assert_eq!(store.accounts().unwrap().len(), 0);
|
||||
assert!(store.remove_account(&accounts[0], &"".into()).is_err());
|
||||
}
|
||||
|
||||
fn test_path() -> &'static str {
|
||||
match ::std::fs::metadata("ethstore") {
|
||||
Ok(_) => "ethstore/tests/res/geth_keystore",
|
||||
Err(_) => "tests/res/geth_keystore",
|
||||
}
|
||||
}
|
||||
|
||||
fn pat_path() -> &'static str {
|
||||
match ::std::fs::metadata("ethstore") {
|
||||
Ok(_) => "ethstore/tests/res/pat",
|
||||
Err(_) => "tests/res/pat",
|
||||
}
|
||||
}
|
||||
|
||||
fn ciphertext_path() -> &'static str {
|
||||
match ::std::fs::metadata("ethstore") {
|
||||
Ok(_) => "ethstore/tests/res/ciphertext",
|
||||
Err(_) => "tests/res/ciphertext",
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_store_laod_geth_files() {
|
||||
let dir = RootDiskDirectory::at(test_path());
|
||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||
assert_eq!(store.accounts().unwrap(), vec![
|
||||
StoreAccountRef::root(Address::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap()),
|
||||
StoreAccountRef::root(Address::from_str("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf").unwrap()),
|
||||
StoreAccountRef::root(Address::from_str("63121b431a52f8043c16fcf0d1df9cb7b5f66649").unwrap()),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_store_load_pat_files() {
|
||||
let dir = RootDiskDirectory::at(pat_path());
|
||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||
assert_eq!(store.accounts().unwrap(), vec![
|
||||
StoreAccountRef::root(Address::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap()),
|
||||
StoreAccountRef::root(Address::from_str("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf").unwrap()),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decrypting_files_with_short_ciphertext() {
|
||||
// 31e9d1e6d844bd3a536800ef8d8be6a9975db509, 30
|
||||
let kp1 = KeyPair::from_secret("000081c29e8142bb6a81bef5a92bda7a8328a5c85bb2f9542e76f9b0f94fc018".parse().unwrap()).unwrap();
|
||||
// d1e64e5480bfaf733ba7d48712decb8227797a4e , 31
|
||||
let kp2 = KeyPair::from_secret("00fa7b3db73dc7dfdf8c5fbdb796d741e4488628c41fc4febd9160a866ba0f35".parse().unwrap()).unwrap();
|
||||
let dir = RootDiskDirectory::at(ciphertext_path());
|
||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||
let accounts = store.accounts().unwrap();
|
||||
assert_eq!(accounts, vec![
|
||||
StoreAccountRef::root(Address::from_str("31e9d1e6d844bd3a536800ef8d8be6a9975db509").unwrap()),
|
||||
StoreAccountRef::root(Address::from_str("d1e64e5480bfaf733ba7d48712decb8227797a4e").unwrap()),
|
||||
]);
|
||||
|
||||
let message = Default::default();
|
||||
|
||||
let s1 = store.sign(&accounts[0], &"foo".into(), &message).unwrap();
|
||||
let s2 = store.sign(&accounts[1], &"foo".into(), &message).unwrap();
|
||||
assert!(verify_address(&accounts[0].address, &s1, &message).unwrap());
|
||||
assert!(verify_address(&kp1.address(), &s1, &message).unwrap());
|
||||
assert!(verify_address(&kp2.address(), &s2, &message).unwrap());
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"address" : "31e9d1e6d844bd3a536800ef8d8be6a9975db509",
|
||||
"crypto" : {
|
||||
"cipher" : "aes-128-ctr",
|
||||
"cipherparams" : {
|
||||
"iv" : "3ca92af36ad7c2cd92454c59cea5ef00"
|
||||
},
|
||||
"ciphertext" : "108b7d34f3442fc26ab1ab90ca91476ba6bfa8c00975a49ef9051dc675aa",
|
||||
"kdf" : "scrypt",
|
||||
"kdfparams" : {
|
||||
"dklen" : 32,
|
||||
"n" : 2,
|
||||
"r" : 8,
|
||||
"p" : 1,
|
||||
"salt" : "d0769e608fb86cda848065642a9c6fa046845c928175662b8e356c77f914cd3b"
|
||||
},
|
||||
"mac" : "75d0e6759f7b3cefa319c3be41680ab6beea7d8328653474bd06706d4cc67420"
|
||||
},
|
||||
"id" : "a37e1559-5955-450d-8075-7b8931b392b2",
|
||||
"version" : 3
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"address" : "d1e64e5480bfaf733ba7d48712decb8227797a4e",
|
||||
"crypto" : {
|
||||
"cipher" : "aes-128-ctr",
|
||||
"cipherparams" : {
|
||||
"iv" : "e0c41130a323adc1446fc82f724bca2f"
|
||||
},
|
||||
"ciphertext" : "9517cd5bdbe69076f9bf5057248c6c050141e970efa36ce53692d5d59a3984",
|
||||
"kdf" : "scrypt",
|
||||
"kdfparams" : {
|
||||
"dklen" : 32,
|
||||
"n" : 2,
|
||||
"r" : 8,
|
||||
"p" : 1,
|
||||
"salt" : "711f816911c92d649fb4c84b047915679933555030b3552c1212609b38208c63"
|
||||
},
|
||||
"mac" : "d5e116151c6aa71470e67a7d42c9620c75c4d23229847dcc127794f0732b0db5"
|
||||
},
|
||||
"id" : "fecfc4ce-e956-48fd-953b-30f8b52ed66c",
|
||||
"version" : 3
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"address": "3f49624084b67849c7b4e805c5988c21a430f9d9",
|
||||
"Crypto": {
|
||||
"cipher": "aes-128-ctr",
|
||||
"ciphertext": "9f27e3dd4fc73e7103ed61e5493662189a3eb52223ae49e3d1deacc04c889eae",
|
||||
"cipherparams": {
|
||||
"iv": "457494bf05f2618c397dc74dbb5181c0"
|
||||
},
|
||||
"kdf": "scrypt",
|
||||
"kdfparams": {
|
||||
"dklen": 32,
|
||||
"n": 262144,
|
||||
"p": 1,
|
||||
"r": 8,
|
||||
"salt": "db14edb18c41ee7f5ec4397df89c3a2ae4d0af60884c52bb54ce490574f8df33"
|
||||
},
|
||||
"mac": "572d24532438d31fdf513c744a3ff26c933ffda5744ee42bc71661cbe3f2112e"
|
||||
},
|
||||
"id": "62a0ad73-556d-496a-8e1c-0783d30d3ace",
|
||||
"version": 3
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"address": "5ba4dcf897e97c2bdf8315b9ef26c13c085988cf",
|
||||
"Crypto": {
|
||||
"cipher": "aes-128-ctr",
|
||||
"ciphertext": "d4a08ec930163778273920f6ad1d49b71836337be6fd9863993ac700a612fddd",
|
||||
"cipherparams": {
|
||||
"iv": "89ce5ec129fc27cd5bcbeb8c92bdad50"
|
||||
},
|
||||
"kdf": "scrypt",
|
||||
"kdfparams": {
|
||||
"dklen": 32,
|
||||
"n": 262144,
|
||||
"p": 1,
|
||||
"r": 8,
|
||||
"salt": "612ab108dc37e69ee8af37a7b24bf7f2234086d7bbf945bacdeccce331f7f84a"
|
||||
},
|
||||
"mac": "4152caa7444e06784223d735cea80cd2690b4c587ad8db3d5529442227b25695"
|
||||
},
|
||||
"id": "35086353-fb12-4029-b56b-033cd61ce35b",
|
||||
"version": 3
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::{env, fs};
|
||||
use rand::{RngCore, rngs::OsRng};
|
||||
use ethstore::accounts_dir::{KeyDirectory, RootDiskDirectory};
|
||||
use ethstore::{Error, SafeAccount};
|
||||
|
||||
pub fn random_dir() -> PathBuf {
|
||||
let mut rng = OsRng;
|
||||
let mut dir = env::temp_dir();
|
||||
dir.push(format!("{:x}-{:x}", rng.next_u64(), rng.next_u64()));
|
||||
dir
|
||||
}
|
||||
|
||||
pub struct TransientDir {
|
||||
dir: RootDiskDirectory,
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl TransientDir {
|
||||
pub fn create() -> Result<Self, Error> {
|
||||
let path = random_dir();
|
||||
let result = TransientDir {
|
||||
dir: RootDiskDirectory::create(&path)?,
|
||||
path: path,
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn open() -> Self {
|
||||
let path = random_dir();
|
||||
TransientDir {
|
||||
dir: RootDiskDirectory::at(&path),
|
||||
path: path,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TransientDir {
|
||||
fn drop(&mut self) {
|
||||
fs::remove_dir_all(&self.path).expect("Expected to remove temp dir");
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyDirectory for TransientDir {
|
||||
fn load(&self) -> Result<Vec<SafeAccount>, Error> {
|
||||
self.dir.load()
|
||||
}
|
||||
|
||||
fn update(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
|
||||
self.dir.update(account)
|
||||
}
|
||||
|
||||
fn insert(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
|
||||
self.dir.insert(account)
|
||||
}
|
||||
|
||||
fn remove(&self, account: &SafeAccount) -> Result<(), Error> {
|
||||
self.dir.remove(account)
|
||||
}
|
||||
|
||||
fn unique_repr(&self) -> Result<u64, Error> {
|
||||
self.dir.unique_repr()
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Account Metadata
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use parity_crypto::publickey::Address;
|
||||
use ethkey::Password;
|
||||
use serde_derive::{Serialize, Deserialize};
|
||||
use serde_json;
|
||||
|
||||
/// Type of unlock.
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum Unlock {
|
||||
/// If account is unlocked temporarily, it should be locked after first usage.
|
||||
OneTime,
|
||||
/// Account unlocked permanently can always sign message.
|
||||
/// Use with caution.
|
||||
Perm,
|
||||
/// Account unlocked with a timeout
|
||||
Timed(Instant),
|
||||
}
|
||||
|
||||
/// Data associated with account.
|
||||
#[derive(Clone)]
|
||||
pub struct AccountData {
|
||||
pub unlock: Unlock,
|
||||
pub password: Password,
|
||||
}
|
||||
|
||||
/// Collected account metadata
|
||||
#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct AccountMeta {
|
||||
/// The name of the account.
|
||||
pub name: String,
|
||||
/// The rest of the metadata of the account.
|
||||
pub meta: String,
|
||||
/// The 128-bit Uuid of the account, if it has one (brain-wallets don't).
|
||||
pub uuid: Option<String>,
|
||||
}
|
||||
|
||||
impl AccountMeta {
|
||||
/// Read a hash map of Address -> AccountMeta
|
||||
pub fn read<R>(reader: R) -> Result<HashMap<Address, Self>, serde_json::Error> where
|
||||
R: ::std::io::Read,
|
||||
{
|
||||
serde_json::from_reader(reader)
|
||||
}
|
||||
|
||||
/// Write a hash map of Address -> AccountMeta
|
||||
pub fn write<W>(m: &HashMap<Address, Self>, writer: &mut W) -> Result<(), serde_json::Error> where
|
||||
W: ::std::io::Write,
|
||||
{
|
||||
serde_json::to_writer(writer, m)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,643 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
//! Account management.
|
||||
|
||||
mod account_data;
|
||||
mod error;
|
||||
mod stores;
|
||||
|
||||
use self::account_data::{Unlock, AccountData};
|
||||
use self::stores::AddressBook;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::time::{Instant, Duration};
|
||||
|
||||
use ethkey::Password;
|
||||
use parity_crypto::publickey::{Address, Message, Public, Secret, Random, Generator, Signature};
|
||||
use ethstore::accounts_dir::MemoryDirectory;
|
||||
use ethstore::{
|
||||
SimpleSecretStore, SecretStore, EthStore, EthMultiStore,
|
||||
random_string, SecretVaultRef, StoreAccountRef, OpaqueSecret,
|
||||
};
|
||||
use log::warn;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
pub use ethstore::{Derivation, IndexDerivation, KeyFile, Error};
|
||||
|
||||
pub use self::account_data::AccountMeta;
|
||||
pub use self::error::SignError;
|
||||
|
||||
type AccountToken = Password;
|
||||
|
||||
/// Account management settings.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct AccountProviderSettings {
|
||||
/// Store raw account secret when unlocking the account permanently.
|
||||
pub unlock_keep_secret: bool,
|
||||
/// Disallowed accounts.
|
||||
pub blacklisted_accounts: Vec<Address>,
|
||||
}
|
||||
|
||||
/// Account management.
|
||||
/// Responsible for unlocking accounts.
|
||||
pub struct AccountProvider {
|
||||
/// For performance reasons some methods can re-use unlocked secrets.
|
||||
unlocked_secrets: RwLock<HashMap<StoreAccountRef, OpaqueSecret>>,
|
||||
/// Unlocked account data.
|
||||
unlocked: RwLock<HashMap<StoreAccountRef, AccountData>>,
|
||||
/// Address book.
|
||||
address_book: RwLock<AddressBook>,
|
||||
/// Accounts on disk
|
||||
sstore: Box<dyn SecretStore>,
|
||||
/// Accounts unlocked with rolling tokens
|
||||
transient_sstore: EthMultiStore,
|
||||
/// When unlocking account permanently we additionally keep a raw secret in memory
|
||||
/// to increase the performance of transaction signing.
|
||||
unlock_keep_secret: bool,
|
||||
/// Disallowed accounts.
|
||||
blacklisted_accounts: Vec<Address>,
|
||||
}
|
||||
|
||||
fn transient_sstore() -> EthMultiStore {
|
||||
EthMultiStore::open(Box::new(MemoryDirectory::default())).expect("MemoryDirectory load always succeeds; qed")
|
||||
}
|
||||
|
||||
impl AccountProvider {
|
||||
/// Creates new account provider.
|
||||
pub fn new(sstore: Box<dyn SecretStore>, settings: AccountProviderSettings) -> Self {
|
||||
if let Ok(accounts) = sstore.accounts() {
|
||||
for account in accounts.into_iter().filter(|a| settings.blacklisted_accounts.contains(&a.address)) {
|
||||
warn!("Local Account {} has a blacklisted (known to be weak) address and will be ignored",
|
||||
account.address);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove blacklisted accounts from address book.
|
||||
let mut address_book = AddressBook::new(&sstore.local_path());
|
||||
for addr in &settings.blacklisted_accounts {
|
||||
address_book.remove(*addr);
|
||||
}
|
||||
|
||||
AccountProvider {
|
||||
unlocked_secrets: RwLock::new(HashMap::new()),
|
||||
unlocked: RwLock::new(HashMap::new()),
|
||||
address_book: RwLock::new(address_book),
|
||||
sstore,
|
||||
transient_sstore: transient_sstore(),
|
||||
unlock_keep_secret: settings.unlock_keep_secret,
|
||||
blacklisted_accounts: settings.blacklisted_accounts,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates not disk backed provider.
|
||||
pub fn transient_provider() -> Self {
|
||||
AccountProvider {
|
||||
unlocked_secrets: RwLock::new(HashMap::new()),
|
||||
unlocked: RwLock::new(HashMap::new()),
|
||||
address_book: RwLock::new(AddressBook::transient()),
|
||||
sstore: Box::new(EthStore::open(Box::new(MemoryDirectory::default())).expect("MemoryDirectory load always succeeds; qed")),
|
||||
transient_sstore: transient_sstore(),
|
||||
unlock_keep_secret: false,
|
||||
blacklisted_accounts: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates new random account.
|
||||
pub fn new_account(&self, password: &Password) -> Result<Address, Error> {
|
||||
self.new_account_and_public(password).map(|d| d.0)
|
||||
}
|
||||
|
||||
/// Creates new random account and returns address and public key
|
||||
pub fn new_account_and_public(&self, password: &Password) -> Result<(Address, Public), Error> {
|
||||
let acc = Random.generate().expect("secp context has generation capabilities; qed");
|
||||
let public = acc.public().clone();
|
||||
let secret = acc.secret().clone();
|
||||
let account = self.sstore.insert_account(SecretVaultRef::Root, secret, password)?;
|
||||
Ok((account.address, public))
|
||||
}
|
||||
|
||||
/// Inserts new account into underlying store.
|
||||
/// Does not unlock account!
|
||||
pub fn insert_account(&self, secret: Secret, password: &Password) -> Result<Address, Error> {
|
||||
let account = self.sstore.insert_account(SecretVaultRef::Root, secret, password)?;
|
||||
if self.blacklisted_accounts.contains(&account.address) {
|
||||
self.sstore.remove_account(&account, password)?;
|
||||
return Err(Error::InvalidAccount.into());
|
||||
}
|
||||
Ok(account.address)
|
||||
}
|
||||
|
||||
/// Generates new derived account based on the existing one
|
||||
/// If password is not provided, account must be unlocked
|
||||
/// New account will be created with the same password (if save: true)
|
||||
pub fn derive_account(&self, address: &Address, password: Option<Password>, derivation: Derivation, save: bool)
|
||||
-> Result<Address, SignError>
|
||||
{
|
||||
let account = self.sstore.account_ref(&address)?;
|
||||
let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?;
|
||||
Ok(
|
||||
if save { self.sstore.insert_derived(SecretVaultRef::Root, &account, &password, derivation)?.address }
|
||||
else { self.sstore.generate_derived(&account, &password, derivation)? }
|
||||
)
|
||||
}
|
||||
|
||||
/// Import a new presale wallet.
|
||||
pub fn import_presale(&self, presale_json: &[u8], password: &Password) -> Result<Address, Error> {
|
||||
let account = self.sstore.import_presale(SecretVaultRef::Root, presale_json, password)?;
|
||||
Ok(Address::from(account.address).into())
|
||||
}
|
||||
|
||||
/// Import a new wallet.
|
||||
pub fn import_wallet(&self, json: &[u8], password: &Password, gen_id: bool) -> Result<Address, Error> {
|
||||
let account = self.sstore.import_wallet(SecretVaultRef::Root, json, password, gen_id)?;
|
||||
if self.blacklisted_accounts.contains(&account.address) {
|
||||
self.sstore.remove_account(&account, password)?;
|
||||
return Err(Error::InvalidAccount.into());
|
||||
}
|
||||
Ok(Address::from(account.address).into())
|
||||
}
|
||||
|
||||
/// Checks whether an account with a given address is present.
|
||||
pub fn has_account(&self, address: Address) -> bool {
|
||||
self.sstore.account_ref(&address).is_ok() && !self.blacklisted_accounts.contains(&address)
|
||||
}
|
||||
|
||||
/// Returns addresses of all accounts.
|
||||
pub fn accounts(&self) -> Result<Vec<Address>, Error> {
|
||||
let accounts = self.sstore.accounts()?;
|
||||
Ok(accounts
|
||||
.into_iter()
|
||||
.map(|a| a.address)
|
||||
.filter(|address| !self.blacklisted_accounts.contains(address))
|
||||
.collect()
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the address of default account.
|
||||
pub fn default_account(&self) -> Result<Address, Error> {
|
||||
Ok(self.accounts()?.first().cloned().unwrap_or_default())
|
||||
}
|
||||
|
||||
/// Returns each address along with metadata.
|
||||
pub fn addresses_info(&self) -> HashMap<Address, AccountMeta> {
|
||||
self.address_book.read().get()
|
||||
}
|
||||
|
||||
/// Returns each address along with metadata.
|
||||
pub fn set_address_name(&self, account: Address, name: String) {
|
||||
self.address_book.write().set_name(account, name)
|
||||
}
|
||||
|
||||
/// Returns each address along with metadata.
|
||||
pub fn set_address_meta(&self, account: Address, meta: String) {
|
||||
self.address_book.write().set_meta(account, meta)
|
||||
}
|
||||
|
||||
/// Removes and address from the address book
|
||||
pub fn remove_address(&self, addr: Address) {
|
||||
self.address_book.write().remove(addr)
|
||||
}
|
||||
|
||||
/// Returns each account along with name and meta.
|
||||
pub fn accounts_info(&self) -> Result<HashMap<Address, AccountMeta>, Error> {
|
||||
let r = self.sstore.accounts()?
|
||||
.into_iter()
|
||||
.filter(|a| !self.blacklisted_accounts.contains(&a.address))
|
||||
.map(|a| (a.address.clone(), self.account_meta(a.address).ok().unwrap_or_default()))
|
||||
.collect();
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
/// Returns each account along with name and meta.
|
||||
pub fn account_meta(&self, address: Address) -> Result<AccountMeta, Error> {
|
||||
let account = self.sstore.account_ref(&address)?;
|
||||
Ok(AccountMeta {
|
||||
name: self.sstore.name(&account)?,
|
||||
meta: self.sstore.meta(&account)?,
|
||||
uuid: self.sstore.uuid(&account).ok().map(Into::into), // allowed to not have a Uuid
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns account public key.
|
||||
pub fn account_public(&self, address: Address, password: &Password) -> Result<Public, Error> {
|
||||
self.sstore.public(&self.sstore.account_ref(&address)?, password)
|
||||
}
|
||||
|
||||
/// Returns each account along with name and meta.
|
||||
pub fn set_account_name(&self, address: Address, name: String) -> Result<(), Error> {
|
||||
self.sstore.set_name(&self.sstore.account_ref(&address)?, name)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns each account along with name and meta.
|
||||
pub fn set_account_meta(&self, address: Address, meta: String) -> Result<(), Error> {
|
||||
self.sstore.set_meta(&self.sstore.account_ref(&address)?, meta)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns `true` if the password for `account` is `password`. `false` if not.
|
||||
pub fn test_password(&self, address: &Address, password: &Password) -> Result<bool, Error> {
|
||||
self.sstore.test_password(&self.sstore.account_ref(&address)?, password)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Permanently removes an account.
|
||||
pub fn kill_account(&self, address: &Address, password: &Password) -> Result<(), Error> {
|
||||
self.sstore.remove_account(&self.sstore.account_ref(&address)?, &password)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Changes the password of `account` from `password` to `new_password`. Fails if incorrect `password` given.
|
||||
pub fn change_password(&self, address: &Address, password: Password, new_password: Password) -> Result<(), Error> {
|
||||
self.sstore.change_password(&self.sstore.account_ref(address)?, &password, &new_password)
|
||||
}
|
||||
|
||||
/// Exports an account for given address.
|
||||
pub fn export_account(&self, address: &Address, password: Password) -> Result<KeyFile, Error> {
|
||||
self.sstore.export_account(&self.sstore.account_ref(address)?, &password)
|
||||
}
|
||||
|
||||
/// Helper method used for unlocking accounts.
|
||||
fn unlock_account(&self, address: Address, password: Password, unlock: Unlock) -> Result<(), Error> {
|
||||
let account = self.sstore.account_ref(&address)?;
|
||||
|
||||
// check if account is already unlocked permanently, if it is, do nothing
|
||||
let mut unlocked = self.unlocked.write();
|
||||
if let Some(data) = unlocked.get(&account) {
|
||||
if let Unlock::Perm = data.unlock {
|
||||
return Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
if self.unlock_keep_secret && unlock == Unlock::Perm {
|
||||
// verify password and get the secret
|
||||
let secret = self.sstore.raw_secret(&account, &password)?;
|
||||
self.unlocked_secrets.write().insert(account.clone(), secret);
|
||||
} else {
|
||||
// verify password by signing dump message
|
||||
// result may be discarded
|
||||
let _ = self.sstore.sign(&account, &password, &Default::default())?;
|
||||
}
|
||||
|
||||
let data = AccountData { unlock, password };
|
||||
|
||||
unlocked.insert(account, data);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn password(&self, account: &StoreAccountRef) -> Result<Password, SignError> {
|
||||
let mut unlocked = self.unlocked.write();
|
||||
let data = unlocked.get(account).ok_or(SignError::NotUnlocked)?.clone();
|
||||
if let Unlock::OneTime = data.unlock {
|
||||
unlocked.remove(account).expect("data exists: so key must exist: qed");
|
||||
}
|
||||
if let Unlock::Timed(ref end) = data.unlock {
|
||||
if Instant::now() > *end {
|
||||
unlocked.remove(account).expect("data exists: so key must exist: qed");
|
||||
return Err(SignError::NotUnlocked);
|
||||
}
|
||||
}
|
||||
Ok(data.password)
|
||||
}
|
||||
|
||||
/// Unlocks account permanently.
|
||||
pub fn unlock_account_permanently(&self, account: Address, password: Password) -> Result<(), Error> {
|
||||
self.unlock_account(account, password, Unlock::Perm)
|
||||
}
|
||||
|
||||
/// Unlocks account temporarily (for one signing).
|
||||
pub fn unlock_account_temporarily(&self, account: Address, password: Password) -> Result<(), Error> {
|
||||
self.unlock_account(account, password, Unlock::OneTime)
|
||||
}
|
||||
|
||||
/// Unlocks account temporarily with a timeout.
|
||||
pub fn unlock_account_timed(&self, account: Address, password: Password, duration: Duration) -> Result<(), Error> {
|
||||
self.unlock_account(account, password, Unlock::Timed(Instant::now() + duration))
|
||||
}
|
||||
|
||||
/// Checks if given account is unlocked
|
||||
pub fn is_unlocked(&self, address: &Address) -> bool {
|
||||
let unlocked = self.unlocked.read();
|
||||
let unlocked_secrets = self.unlocked_secrets.read();
|
||||
self.sstore.account_ref(address)
|
||||
.map(|r| unlocked.get(&r).is_some() || unlocked_secrets.get(&r).is_some())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Checks if given account is unlocked permanently
|
||||
pub fn is_unlocked_permanently(&self, address: &Address) -> bool {
|
||||
let unlocked = self.unlocked.read();
|
||||
self.sstore.account_ref(address)
|
||||
.map(|r| unlocked.get(&r).map_or(false, |account| account.unlock == Unlock::Perm))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Signs the message. If password is not provided the account must be unlocked.
|
||||
pub fn sign(&self, address: Address, password: Option<Password>, message: Message) -> Result<Signature, SignError> {
|
||||
let account = self.sstore.account_ref(&address)?;
|
||||
match self.unlocked_secrets.read().get(&account) {
|
||||
Some(secret) => {
|
||||
Ok(self.sstore.sign_with_secret(&secret, &message)?)
|
||||
},
|
||||
None => {
|
||||
let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?;
|
||||
Ok(self.sstore.sign(&account, &password, &message)?)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Signs message using the derived secret. If password is not provided the account must be unlocked.
|
||||
pub fn sign_derived(&self, address: &Address, password: Option<Password>, derivation: Derivation, message: Message)
|
||||
-> Result<Signature, SignError>
|
||||
{
|
||||
let account = self.sstore.account_ref(address)?;
|
||||
let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?;
|
||||
Ok(self.sstore.sign_derived(&account, &password, derivation, &message)?)
|
||||
}
|
||||
|
||||
/// Signs given message with supplied token. Returns a token to use in next signing within this session.
|
||||
pub fn sign_with_token(&self, address: Address, token: AccountToken, message: Message) -> Result<(Signature, AccountToken), SignError> {
|
||||
let account = self.sstore.account_ref(&address)?;
|
||||
let is_std_password = self.sstore.test_password(&account, &token)?;
|
||||
|
||||
let new_token = Password::from(random_string(16));
|
||||
let signature = if is_std_password {
|
||||
// Insert to transient store
|
||||
self.sstore.copy_account(&self.transient_sstore, SecretVaultRef::Root, &account, &token, &new_token)?;
|
||||
// sign
|
||||
self.sstore.sign(&account, &token, &message)?
|
||||
} else {
|
||||
// check transient store
|
||||
self.transient_sstore.change_password(&account, &token, &new_token)?;
|
||||
// and sign
|
||||
self.transient_sstore.sign(&account, &new_token, &message)?
|
||||
};
|
||||
|
||||
Ok((signature, new_token))
|
||||
}
|
||||
|
||||
/// Decrypts a message with given token. Returns a token to use in next operation for this account.
|
||||
pub fn decrypt_with_token(&self, address: Address, token: AccountToken, shared_mac: &[u8], message: &[u8])
|
||||
-> Result<(Vec<u8>, AccountToken), SignError>
|
||||
{
|
||||
let account = self.sstore.account_ref(&address)?;
|
||||
let is_std_password = self.sstore.test_password(&account, &token)?;
|
||||
|
||||
let new_token = Password::from(random_string(16));
|
||||
let message = if is_std_password {
|
||||
// Insert to transient store
|
||||
self.sstore.copy_account(&self.transient_sstore, SecretVaultRef::Root, &account, &token, &new_token)?;
|
||||
// decrypt
|
||||
self.sstore.decrypt(&account, &token, shared_mac, message)?
|
||||
} else {
|
||||
// check transient store
|
||||
self.transient_sstore.change_password(&account, &token, &new_token)?;
|
||||
// and decrypt
|
||||
self.transient_sstore.decrypt(&account, &token, shared_mac, message)?
|
||||
};
|
||||
|
||||
Ok((message, new_token))
|
||||
}
|
||||
|
||||
/// Decrypts a message. If password is not provided the account must be unlocked.
|
||||
pub fn decrypt(&self, address: Address, password: Option<Password>, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, SignError> {
|
||||
let account = self.sstore.account_ref(&address)?;
|
||||
let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?;
|
||||
Ok(self.sstore.decrypt(&account, &password, shared_mac, message)?)
|
||||
}
|
||||
|
||||
/// Agree on shared key.
|
||||
pub fn agree(&self, address: Address, password: Option<Password>, other_public: &Public) -> Result<Secret, SignError> {
|
||||
let account = self.sstore.account_ref(&address)?;
|
||||
let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?;
|
||||
Ok(self.sstore.agree(&account, &password, other_public)?)
|
||||
}
|
||||
|
||||
/// Returns the underlying `SecretStore` reference if one exists.
|
||||
pub fn list_geth_accounts(&self, testnet: bool) -> Vec<Address> {
|
||||
self.sstore.list_geth_accounts(testnet).into_iter().map(|a| Address::from(a).into()).collect()
|
||||
}
|
||||
|
||||
/// Returns the underlying `SecretStore` reference if one exists.
|
||||
pub fn import_geth_accounts(&self, desired: Vec<Address>, testnet: bool) -> Result<Vec<Address>, Error> {
|
||||
self.sstore.import_geth_accounts(SecretVaultRef::Root, desired, testnet)
|
||||
.map(|a| a.into_iter().map(|a| a.address).collect())
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Create new vault.
|
||||
pub fn create_vault(&self, name: &str, password: &Password) -> Result<(), Error> {
|
||||
self.sstore.create_vault(name, password)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Open existing vault.
|
||||
pub fn open_vault(&self, name: &str, password: &Password) -> Result<(), Error> {
|
||||
self.sstore.open_vault(name, password)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Close previously opened vault.
|
||||
pub fn close_vault(&self, name: &str) -> Result<(), Error> {
|
||||
self.sstore.close_vault(name)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// List all vaults
|
||||
pub fn list_vaults(&self) -> Result<Vec<String>, Error> {
|
||||
self.sstore.list_vaults()
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// List all currently opened vaults
|
||||
pub fn list_opened_vaults(&self) -> Result<Vec<String>, Error> {
|
||||
self.sstore.list_opened_vaults()
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Change vault password.
|
||||
pub fn change_vault_password(&self, name: &str, new_password: &Password) -> Result<(), Error> {
|
||||
self.sstore.change_vault_password(name, new_password)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Change vault of the given address.
|
||||
pub fn change_vault(&self, address: Address, new_vault: &str) -> Result<(), Error> {
|
||||
let new_vault_ref = if new_vault.is_empty() { SecretVaultRef::Root } else { SecretVaultRef::Vault(new_vault.to_owned()) };
|
||||
let old_account_ref = self.sstore.account_ref(&address)?;
|
||||
self.sstore.change_account_vault(new_vault_ref, old_account_ref)
|
||||
.map_err(Into::into)
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
/// Get vault metadata string.
|
||||
pub fn get_vault_meta(&self, name: &str) -> Result<String, Error> {
|
||||
self.sstore.get_vault_meta(name)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Set vault metadata string.
|
||||
pub fn set_vault_meta(&self, name: &str, meta: &str) -> Result<(), Error> {
|
||||
self.sstore.set_vault_meta(name, meta)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{AccountProvider, Unlock};
|
||||
use std::time::{Duration, Instant};
|
||||
use parity_crypto::publickey::{Generator, Random, Address};
|
||||
use ethstore::{StoreAccountRef, Derivation};
|
||||
use ethereum_types::H256;
|
||||
|
||||
#[test]
|
||||
fn unlock_account_temp() {
|
||||
let kp = Random.generate().unwrap();
|
||||
let ap = AccountProvider::transient_provider();
|
||||
assert!(ap.insert_account(kp.secret().clone(), &"test".into()).is_ok());
|
||||
assert!(ap.unlock_account_temporarily(kp.address(), "test1".into()).is_err());
|
||||
assert!(ap.unlock_account_temporarily(kp.address(), "test".into()).is_ok());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derived_account_nosave() {
|
||||
let kp = Random.generate().unwrap();
|
||||
let ap = AccountProvider::transient_provider();
|
||||
assert!(ap.insert_account(kp.secret().clone(), &"base".into()).is_ok());
|
||||
assert!(ap.unlock_account_permanently(kp.address(), "base".into()).is_ok());
|
||||
|
||||
let derived_addr = ap.derive_account(
|
||||
&kp.address(),
|
||||
None,
|
||||
Derivation::SoftHash(H256::from_low_u64_be(999)),
|
||||
false,
|
||||
).expect("Derivation should not fail");
|
||||
|
||||
assert!(ap.unlock_account_permanently(derived_addr, "base".into()).is_err(),
|
||||
"There should be an error because account is not supposed to be saved");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derived_account_save() {
|
||||
let kp = Random.generate().unwrap();
|
||||
let ap = AccountProvider::transient_provider();
|
||||
assert!(ap.insert_account(kp.secret().clone(), &"base".into()).is_ok());
|
||||
assert!(ap.unlock_account_permanently(kp.address(), "base".into()).is_ok());
|
||||
|
||||
let derived_addr = ap.derive_account(
|
||||
&kp.address(),
|
||||
None,
|
||||
Derivation::SoftHash(H256::from_low_u64_be(999)),
|
||||
true,
|
||||
).expect("Derivation should not fail");
|
||||
|
||||
assert!(ap.unlock_account_permanently(derived_addr, "base_wrong".into()).is_err(),
|
||||
"There should be an error because password is invalid");
|
||||
|
||||
assert!(ap.unlock_account_permanently(derived_addr, "base".into()).is_ok(),
|
||||
"Should be ok because account is saved and password is valid");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derived_account_sign() {
|
||||
let kp = Random.generate().unwrap();
|
||||
let ap = AccountProvider::transient_provider();
|
||||
assert!(ap.insert_account(kp.secret().clone(), &"base".into()).is_ok());
|
||||
assert!(ap.unlock_account_permanently(kp.address(), "base".into()).is_ok());
|
||||
|
||||
let derived_addr = ap.derive_account(
|
||||
&kp.address(),
|
||||
None,
|
||||
Derivation::SoftHash(H256::from_low_u64_be(1999)),
|
||||
true,
|
||||
).expect("Derivation should not fail");
|
||||
ap.unlock_account_permanently(derived_addr, "base".into())
|
||||
.expect("Should be ok because account is saved and password is valid");
|
||||
|
||||
let msg = Default::default();
|
||||
let signed_msg1 = ap.sign(derived_addr, None, msg)
|
||||
.expect("Signing with existing unlocked account should not fail");
|
||||
let signed_msg2 = ap.sign_derived(
|
||||
&kp.address(),
|
||||
None,
|
||||
Derivation::SoftHash(H256::from_low_u64_be(1999)),
|
||||
msg,
|
||||
).expect("Derived signing with existing unlocked account should not fail");
|
||||
|
||||
assert_eq!(signed_msg1, signed_msg2,
|
||||
"Signed messages should match");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unlock_account_perm() {
|
||||
let kp = Random.generate().unwrap();
|
||||
let ap = AccountProvider::transient_provider();
|
||||
assert!(ap.insert_account(kp.secret().clone(), &"test".into()).is_ok());
|
||||
assert!(ap.unlock_account_permanently(kp.address(), "test1".into()).is_err());
|
||||
assert!(ap.unlock_account_permanently(kp.address(), "test".into()).is_ok());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||
assert!(ap.unlock_account_temporarily(kp.address(), "test".into()).is_ok());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unlock_account_timer() {
|
||||
let kp = Random.generate().unwrap();
|
||||
let ap = AccountProvider::transient_provider();
|
||||
assert!(ap.insert_account(kp.secret().clone(), &"test".into()).is_ok());
|
||||
assert!(ap.unlock_account_timed(kp.address(), "test1".into(), Duration::from_secs(60)).is_err());
|
||||
assert!(ap.unlock_account_timed(kp.address(), "test".into(), Duration::from_secs(60)).is_ok());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||
ap.unlocked.write().get_mut(&StoreAccountRef::root(kp.address())).unwrap().unlock = Unlock::Timed(Instant::now());
|
||||
assert!(ap.sign(kp.address(), None, Default::default()).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_sign_and_return_token() {
|
||||
// given
|
||||
let kp = Random.generate().unwrap();
|
||||
let ap = AccountProvider::transient_provider();
|
||||
assert!(ap.insert_account(kp.secret().clone(), &"test".into()).is_ok());
|
||||
|
||||
// when
|
||||
let (_signature, token) = ap.sign_with_token(kp.address(), "test".into(), Default::default()).unwrap();
|
||||
|
||||
// then
|
||||
ap.sign_with_token(kp.address(), token.clone(), Default::default())
|
||||
.expect("First usage of token should be correct.");
|
||||
assert!(ap.sign_with_token(kp.address(), token, Default::default()).is_err(), "Second usage of the same token should fail.");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_return_blacklisted_account() {
|
||||
// given
|
||||
let mut ap = AccountProvider::transient_provider();
|
||||
let acc = ap.new_account(&"test".into()).unwrap();
|
||||
ap.blacklisted_accounts = vec![acc];
|
||||
|
||||
// then
|
||||
assert_eq!(ap.accounts_info().unwrap().keys().cloned().collect::<Vec<Address>>(), vec![]);
|
||||
assert_eq!(ap.accounts().unwrap(), vec![]);
|
||||
}
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Address Book Store
|
||||
|
||||
use std::{fs, fmt, hash, ops};
|
||||
use std::collections::HashMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use parity_crypto::publickey::Address;
|
||||
use log::{trace, warn};
|
||||
|
||||
use crate::AccountMeta;
|
||||
|
||||
/// Disk-backed map from Address to String. Uses JSON.
|
||||
pub struct AddressBook {
|
||||
cache: DiskMap<Address, AccountMeta>,
|
||||
}
|
||||
|
||||
impl AddressBook {
|
||||
/// Creates new address book at given directory.
|
||||
pub fn new(path: &Path) -> Self {
|
||||
let mut r = AddressBook {
|
||||
cache: DiskMap::new(path, "address_book.json")
|
||||
};
|
||||
r.cache.revert(AccountMeta::read);
|
||||
r
|
||||
}
|
||||
|
||||
/// Creates transient address book (no changes are saved to disk).
|
||||
pub fn transient() -> Self {
|
||||
AddressBook {
|
||||
cache: DiskMap::transient()
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the address book.
|
||||
pub fn get(&self) -> HashMap<Address, AccountMeta> {
|
||||
self.cache.clone()
|
||||
}
|
||||
|
||||
fn save(&self) {
|
||||
self.cache.save(AccountMeta::write)
|
||||
}
|
||||
|
||||
/// Sets new name for given address.
|
||||
pub fn set_name(&mut self, a: Address, name: String) {
|
||||
{
|
||||
let x = self.cache.entry(a)
|
||||
.or_insert_with(|| AccountMeta {name: Default::default(), meta: "{}".to_owned(), uuid: None});
|
||||
x.name = name;
|
||||
}
|
||||
self.save();
|
||||
}
|
||||
|
||||
/// Sets new meta for given address.
|
||||
pub fn set_meta(&mut self, a: Address, meta: String) {
|
||||
{
|
||||
let x = self.cache.entry(a)
|
||||
.or_insert_with(|| AccountMeta {name: "Anonymous".to_owned(), meta: Default::default(), uuid: None});
|
||||
x.meta = meta;
|
||||
}
|
||||
self.save();
|
||||
}
|
||||
|
||||
/// Removes an entry
|
||||
pub fn remove(&mut self, a: Address) {
|
||||
self.cache.remove(&a);
|
||||
self.save();
|
||||
}
|
||||
}
|
||||
|
||||
/// Disk-serializable HashMap
|
||||
#[derive(Debug)]
|
||||
struct DiskMap<K: hash::Hash + Eq, V> {
|
||||
path: PathBuf,
|
||||
cache: HashMap<K, V>,
|
||||
transient: bool,
|
||||
}
|
||||
|
||||
impl<K: hash::Hash + Eq, V> ops::Deref for DiskMap<K, V> {
|
||||
type Target = HashMap<K, V>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.cache
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: hash::Hash + Eq, V> ops::DerefMut for DiskMap<K, V> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.cache
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: hash::Hash + Eq, V> DiskMap<K, V> {
|
||||
pub fn new(path: &Path, file_name: &str) -> Self {
|
||||
let mut path = path.to_owned();
|
||||
path.push(file_name);
|
||||
trace!(target: "diskmap", "path={:?}", path);
|
||||
DiskMap {
|
||||
path: path,
|
||||
cache: HashMap::new(),
|
||||
transient: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn transient() -> Self {
|
||||
let mut map = DiskMap::new(&PathBuf::new(), "diskmap.json".into());
|
||||
map.transient = true;
|
||||
map
|
||||
}
|
||||
|
||||
fn revert<F, E>(&mut self, read: F) where
|
||||
F: Fn(fs::File) -> Result<HashMap<K, V>, E>,
|
||||
E: fmt::Display,
|
||||
{
|
||||
if self.transient { return; }
|
||||
trace!(target: "diskmap", "revert {:?}", self.path);
|
||||
let _ = fs::File::open(self.path.clone())
|
||||
.map_err(|e| trace!(target: "diskmap", "Couldn't open disk map: {}", e))
|
||||
.and_then(|f| read(f).map_err(|e| warn!(target: "diskmap", "Couldn't read disk map at: {:?} {}", self.path, e)))
|
||||
.and_then(|m| {
|
||||
self.cache = m;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
fn save<F, E>(&self, write: F) where
|
||||
F: Fn(&HashMap<K, V>, &mut fs::File) -> Result<(), E>,
|
||||
E: fmt::Display,
|
||||
{
|
||||
if self.transient { return; }
|
||||
trace!(target: "diskmap", "save {:?}", self.path);
|
||||
let _ = fs::File::create(self.path.clone())
|
||||
.map_err(|e| warn!(target: "diskmap", "Couldn't open disk map for writing at: {:?} {}", self.path, e))
|
||||
.and_then(|mut f| {
|
||||
write(&self.cache, &mut f).map_err(|e| warn!(target: "diskmap", "Couldn't write to disk map at: {:?} {}", self.path, e))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{AddressBook, Address};
|
||||
use std::collections::HashMap;
|
||||
use tempdir::TempDir;
|
||||
use crate::account_data::AccountMeta;
|
||||
|
||||
#[test]
|
||||
fn should_save_and_reload_address_book() {
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let mut b = AddressBook::new(tempdir.path());
|
||||
b.set_name(Address::from_low_u64_be(1), "One".to_owned());
|
||||
b.set_meta(Address::from_low_u64_be(1), "{1:1}".to_owned());
|
||||
let b = AddressBook::new(tempdir.path());
|
||||
assert_eq!(b.get(), vec![
|
||||
(Address::from_low_u64_be(1), AccountMeta {name: "One".to_owned(), meta: "{1:1}".to_owned(), uuid: None})
|
||||
].into_iter().collect::<HashMap<_, _>>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_remove_address() {
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let mut b = AddressBook::new(tempdir.path());
|
||||
|
||||
b.set_name(Address::from_low_u64_be(1), "One".to_owned());
|
||||
b.set_name(Address::from_low_u64_be(2), "Two".to_owned());
|
||||
b.set_name(Address::from_low_u64_be(3), "Three".to_owned());
|
||||
b.remove(Address::from_low_u64_be(2).into());
|
||||
|
||||
let b = AddressBook::new(tempdir.path());
|
||||
assert_eq!(b.get(), vec![
|
||||
(Address::from_low_u64_be(1), AccountMeta{name: "One".to_owned(), meta: "{}".to_owned(), uuid: None}),
|
||||
(Address::from_low_u64_be(3), AccountMeta{name: "Three".to_owned(), meta: "{}".to_owned(), uuid: None}),
|
||||
].into_iter().collect::<HashMap<_, _>>());
|
||||
}
|
||||
}
|
||||
6
add_license.sh
Executable file
6
add_license.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
for f in $(find . -name '*.rs'); do
|
||||
cat license_header $f > $f.new
|
||||
mv $f.new $f
|
||||
done
|
||||
25
build.rs
Normal file
25
build.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate rustc_version;
|
||||
|
||||
use rustc_version::{version_meta, Channel};
|
||||
|
||||
fn main() {
|
||||
if let Channel::Nightly = version_meta().channel {
|
||||
println!("cargo:rustc-cfg=nightly");
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
[package]
|
||||
description = "Parity Ethereum Chain Specification"
|
||||
name = "chainspec"
|
||||
version = "0.1.0"
|
||||
authors = ["Marek Kotewicz <marek@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
ethjson = { path = "../json" }
|
||||
serde_json = "1.0"
|
||||
@@ -1,49 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate serde_json;
|
||||
extern crate ethjson;
|
||||
|
||||
use std::{fs, env, process};
|
||||
use ethjson::spec::Spec;
|
||||
|
||||
fn quit(s: &str) -> ! {
|
||||
println!("{}", s);
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut args = env::args();
|
||||
if args.len() != 2 {
|
||||
quit("You need to specify chainspec.json\n\
|
||||
\n\
|
||||
./chainspec <chainspec.json>");
|
||||
}
|
||||
|
||||
let path = args.nth(1).expect("args.len() == 2; qed");
|
||||
let file = match fs::File::open(&path) {
|
||||
Ok(file) => file,
|
||||
Err(_) => quit(&format!("{} could not be opened", path)),
|
||||
};
|
||||
|
||||
let spec: Result<Spec, _> = serde_json::from_reader(file);
|
||||
|
||||
if let Err(err) = spec {
|
||||
quit(&format!("{} {}", path, err.to_string()));
|
||||
}
|
||||
|
||||
println!("{} is valid", path);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
[package]
|
||||
description = "Parity Ethereum CLI Signer Tool"
|
||||
homepage = "http://parity.io"
|
||||
license = "GPL-3.0"
|
||||
name = "cli-signer"
|
||||
version = "1.4.0"
|
||||
authors = ["Parity <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
ethereum-types = "0.8.0"
|
||||
futures = "0.1"
|
||||
rpassword = "1.0"
|
||||
parity-rpc = { path = "../rpc" }
|
||||
parity-rpc-client = { path = "rpc-client" }
|
||||
@@ -1,21 +0,0 @@
|
||||
[package]
|
||||
description = "Parity Ethereum RPC Client"
|
||||
homepage = "http://parity.io"
|
||||
license = "GPL-3.0"
|
||||
name = "parity-rpc-client"
|
||||
version = "1.4.0"
|
||||
authors = ["Parity <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
ethereum-types = "0.8.0"
|
||||
futures = "0.1"
|
||||
log = "0.4"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
url = "2.1.0"
|
||||
matches = "0.1"
|
||||
parking_lot = "0.9"
|
||||
jsonrpc-core = "14.0.3"
|
||||
jsonrpc-ws-server = "14.0.3"
|
||||
parity-rpc = { path = "../../rpc" }
|
||||
keccak-hash = "0.4.0"
|
||||
@@ -1,347 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::fmt::{Debug, Formatter, Error as FmtError};
|
||||
use std::io::{BufReader, BufRead};
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::collections::BTreeMap;
|
||||
use std::thread;
|
||||
use std::time;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use hash::keccak;
|
||||
use parking_lot::Mutex;
|
||||
use url::Url;
|
||||
use std::fs::File;
|
||||
|
||||
use ws::ws::{
|
||||
self,
|
||||
Request,
|
||||
Handler,
|
||||
Sender,
|
||||
Handshake,
|
||||
Error as WsError,
|
||||
ErrorKind as WsErrorKind,
|
||||
Message,
|
||||
Result as WsResult,
|
||||
};
|
||||
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde_json::{
|
||||
self as json,
|
||||
Value as JsonValue,
|
||||
Error as JsonError,
|
||||
};
|
||||
|
||||
use futures::{Canceled, Complete, Future, oneshot, done};
|
||||
|
||||
use jsonrpc_core::{Id, Version, Params, Error as JsonRpcError};
|
||||
use jsonrpc_core::request::MethodCall;
|
||||
use jsonrpc_core::response::{Output, Success, Failure};
|
||||
|
||||
use BoxFuture;
|
||||
|
||||
/// The actual websocket connection handler, passed into the
|
||||
/// event loop of ws-rs
|
||||
struct RpcHandler {
|
||||
pending: Pending,
|
||||
// Option is used here as temporary storage until connection
|
||||
// is setup and the values are moved into the new `Rpc`
|
||||
complete: Option<Complete<Result<Rpc, RpcError>>>,
|
||||
auth_code: String,
|
||||
out: Option<Sender>,
|
||||
}
|
||||
|
||||
impl RpcHandler {
|
||||
fn new(
|
||||
out: Sender,
|
||||
auth_code: String,
|
||||
complete: Complete<Result<Rpc, RpcError>>
|
||||
) -> Self {
|
||||
RpcHandler {
|
||||
out: Some(out),
|
||||
auth_code: auth_code,
|
||||
pending: Pending::new(),
|
||||
complete: Some(complete),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Handler for RpcHandler {
|
||||
fn build_request(&mut self, url: &Url) -> WsResult<Request> {
|
||||
match Request::from_url(url) {
|
||||
Ok(mut r) => {
|
||||
let timestamp = time::UNIX_EPOCH.elapsed().map_err(|err| {
|
||||
WsError::new(WsErrorKind::Internal, format!("{}", err))
|
||||
})?;
|
||||
let secs = timestamp.as_secs();
|
||||
let hashed = keccak(format!("{}:{}", self.auth_code, secs));
|
||||
let proto = format!("{:x}_{}", hashed, secs);
|
||||
r.add_protocol(&proto);
|
||||
Ok(r)
|
||||
},
|
||||
Err(e) =>
|
||||
Err(WsError::new(WsErrorKind::Internal, format!("{}", e))),
|
||||
}
|
||||
}
|
||||
fn on_error(&mut self, err: WsError) {
|
||||
match self.complete.take() {
|
||||
Some(c) => match c.send(Err(RpcError::WsError(err))) {
|
||||
Ok(_) => {},
|
||||
Err(_) => warn!(target: "rpc-client", "Unable to notify about error."),
|
||||
},
|
||||
None => warn!(target: "rpc-client", "unexpected error: {}", err),
|
||||
}
|
||||
}
|
||||
fn on_open(&mut self, _: Handshake) -> WsResult<()> {
|
||||
match (self.complete.take(), self.out.take()) {
|
||||
(Some(c), Some(out)) => {
|
||||
let res = c.send(Ok(Rpc {
|
||||
out: out,
|
||||
counter: AtomicUsize::new(0),
|
||||
pending: self.pending.clone(),
|
||||
}));
|
||||
if let Err(_) = res {
|
||||
warn!(target: "rpc-client", "Unable to open a connection.")
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
_ => {
|
||||
let msg = format!("on_open called twice");
|
||||
Err(WsError::new(WsErrorKind::Internal, msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
fn on_message(&mut self, msg: Message) -> WsResult<()> {
|
||||
let ret: Result<JsonValue, JsonRpcError>;
|
||||
let response_id;
|
||||
let string = &msg.to_string();
|
||||
match json::from_str::<Output>(&string) {
|
||||
Ok(Output::Success(Success { result, id: Id::Num(id), .. })) =>
|
||||
{
|
||||
ret = Ok(result);
|
||||
response_id = id as usize;
|
||||
}
|
||||
Ok(Output::Failure(Failure { error, id: Id::Num(id), .. })) => {
|
||||
ret = Err(error);
|
||||
response_id = id as usize;
|
||||
}
|
||||
Err(e) => {
|
||||
warn!(
|
||||
target: "rpc-client",
|
||||
"recieved invalid message: {}\n {:?}",
|
||||
string,
|
||||
e
|
||||
);
|
||||
return Ok(())
|
||||
},
|
||||
_ => {
|
||||
warn!(
|
||||
target: "rpc-client",
|
||||
"recieved invalid message: {}",
|
||||
string
|
||||
);
|
||||
return Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
match self.pending.remove(response_id) {
|
||||
Some(c) => if let Err(_) = c.send(ret.map_err(|err| RpcError::JsonRpc(err))) {
|
||||
warn!(target: "rpc-client", "Unable to send response.")
|
||||
},
|
||||
None => warn!(
|
||||
target: "rpc-client",
|
||||
"warning: unexpected id: {}",
|
||||
response_id
|
||||
),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Keeping track of issued requests to be matched up with responses
|
||||
#[derive(Clone)]
|
||||
struct Pending(
|
||||
Arc<Mutex<BTreeMap<usize, Complete<Result<JsonValue, RpcError>>>>>
|
||||
);
|
||||
|
||||
impl Pending {
|
||||
fn new() -> Self {
|
||||
Pending(Arc::new(Mutex::new(BTreeMap::new())))
|
||||
}
|
||||
fn insert(&mut self, k: usize, v: Complete<Result<JsonValue, RpcError>>) {
|
||||
self.0.lock().insert(k, v);
|
||||
}
|
||||
fn remove(
|
||||
&mut self,
|
||||
k: usize
|
||||
) -> Option<Complete<Result<JsonValue, RpcError>>> {
|
||||
self.0.lock().remove(&k)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_authcode(path: &PathBuf) -> Result<String, RpcError> {
|
||||
if let Ok(fd) = File::open(path) {
|
||||
if let Some(Ok(line)) = BufReader::new(fd).lines().next() {
|
||||
let mut parts = line.split(';');
|
||||
let token = parts.next();
|
||||
|
||||
if let Some(code) = token {
|
||||
return Ok(code.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(RpcError::NoAuthCode)
|
||||
}
|
||||
|
||||
/// The handle to the connection
|
||||
pub struct Rpc {
|
||||
out: Sender,
|
||||
counter: AtomicUsize,
|
||||
pending: Pending,
|
||||
}
|
||||
|
||||
impl Rpc {
|
||||
/// Blocking, returns a new initialized connection or RpcError
|
||||
pub fn new(url: &str, authpath: &PathBuf) -> Result<Self, RpcError> {
|
||||
let rpc = Self::connect(url, authpath).map(|rpc| rpc).wait()?;
|
||||
rpc
|
||||
}
|
||||
|
||||
/// Non-blocking, returns a future
|
||||
pub fn connect(
|
||||
url: &str, authpath: &PathBuf
|
||||
) -> BoxFuture<Result<Self, RpcError>, Canceled> {
|
||||
let (c, p) = oneshot::<Result<Self, RpcError>>();
|
||||
match get_authcode(authpath) {
|
||||
Err(e) => return Box::new(done(Ok(Err(e)))),
|
||||
Ok(code) => {
|
||||
let url = String::from(url);
|
||||
// The ws::connect takes a FnMut closure, which means c cannot
|
||||
// be moved into it, since it's consumed on complete.
|
||||
// Therefore we wrap it in an option and pick it out once.
|
||||
let mut once = Some(c);
|
||||
thread::spawn(move || {
|
||||
let conn = ws::connect(url, |out| {
|
||||
// this will panic if the closure is called twice,
|
||||
// which it should never be.
|
||||
let c = once.take()
|
||||
.expect("connection closure called only once");
|
||||
RpcHandler::new(out, code.clone(), c)
|
||||
});
|
||||
match conn {
|
||||
Err(err) => {
|
||||
// since ws::connect is only called once, it cannot
|
||||
// both fail and succeed.
|
||||
let c = once.take()
|
||||
.expect("connection closure called only once");
|
||||
let _ = c.send(Err(RpcError::WsError(err)));
|
||||
},
|
||||
// c will complete on the `on_open` event in the Handler
|
||||
_ => ()
|
||||
}
|
||||
});
|
||||
Box::new(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Non-blocking, returns a future of the request response
|
||||
pub fn request<T>(
|
||||
&mut self, method: &'static str, params: Vec<JsonValue>
|
||||
) -> BoxFuture<Result<T, RpcError>, Canceled>
|
||||
where T: DeserializeOwned + Send + Sized {
|
||||
|
||||
let (c, p) = oneshot::<Result<JsonValue, RpcError>>();
|
||||
|
||||
let id = self.counter.fetch_add(1, Ordering::Relaxed);
|
||||
self.pending.insert(id, c);
|
||||
|
||||
let request = MethodCall {
|
||||
jsonrpc: Some(Version::V2),
|
||||
method: method.to_owned(),
|
||||
params: Params::Array(params),
|
||||
id: Id::Num(id as u64),
|
||||
};
|
||||
|
||||
let serialized = json::to_string(&request)
|
||||
.expect("request is serializable");
|
||||
let _ = self.out.send(serialized);
|
||||
|
||||
Box::new(p.map(|result| {
|
||||
match result {
|
||||
Ok(json) => {
|
||||
let t: T = json::from_value(json)?;
|
||||
Ok(t)
|
||||
},
|
||||
Err(err) => Err(err)
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub enum RpcError {
|
||||
WrongVersion(String),
|
||||
ParseError(JsonError),
|
||||
MalformedResponse(String),
|
||||
JsonRpc(JsonRpcError),
|
||||
WsError(WsError),
|
||||
Canceled(Canceled),
|
||||
UnexpectedId,
|
||||
NoAuthCode,
|
||||
}
|
||||
|
||||
impl Debug for RpcError {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
|
||||
match *self {
|
||||
RpcError::WrongVersion(ref s)
|
||||
=> write!(f, "Expected version 2.0, got {}", s),
|
||||
RpcError::ParseError(ref err)
|
||||
=> write!(f, "ParseError: {}", err),
|
||||
RpcError::MalformedResponse(ref s)
|
||||
=> write!(f, "Malformed response: {}", s),
|
||||
RpcError::JsonRpc(ref json)
|
||||
=> write!(f, "JsonRpc error: {:?}", json),
|
||||
RpcError::WsError(ref s)
|
||||
=> write!(f, "Websocket error: {}", s),
|
||||
RpcError::Canceled(ref s)
|
||||
=> write!(f, "Futures error: {:?}", s),
|
||||
RpcError::UnexpectedId
|
||||
=> write!(f, "Unexpected response id"),
|
||||
RpcError::NoAuthCode
|
||||
=> write!(f, "No authcodes available"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<JsonError> for RpcError {
|
||||
fn from(err: JsonError) -> RpcError {
|
||||
RpcError::ParseError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WsError> for RpcError {
|
||||
fn from(err: WsError) -> RpcError {
|
||||
RpcError::WsError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Canceled> for RpcError {
|
||||
fn from(err: Canceled) -> RpcError {
|
||||
RpcError::Canceled(err)
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
pub mod client;
|
||||
pub mod signer_client;
|
||||
|
||||
extern crate ethereum_types;
|
||||
extern crate futures;
|
||||
extern crate jsonrpc_core;
|
||||
extern crate jsonrpc_ws_server as ws;
|
||||
extern crate parity_rpc as rpc;
|
||||
extern crate parking_lot;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
extern crate url;
|
||||
extern crate keccak_hash as hash;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate matches;
|
||||
|
||||
/// Boxed future response.
|
||||
pub type BoxFuture<T, E> = Box<dyn futures::Future<Item=T, Error=E> + Send>;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use futures::Future;
|
||||
use std::path::PathBuf;
|
||||
use client::{Rpc, RpcError};
|
||||
use rpc;
|
||||
|
||||
#[test]
|
||||
fn test_connection_refused() {
|
||||
let (_srv, port, mut authcodes) = rpc::tests::ws::serve();
|
||||
|
||||
let _ = authcodes.generate_new();
|
||||
authcodes.to_file(&authcodes.path).unwrap();
|
||||
|
||||
let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port - 1),
|
||||
&authcodes.path);
|
||||
|
||||
let _ = connect.map(|conn| {
|
||||
assert!(matches!(&conn, &Err(RpcError::WsError(_))));
|
||||
}).wait();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_authcode_fail() {
|
||||
let (_srv, port, _) = rpc::tests::ws::serve();
|
||||
let path = PathBuf::from("nonexist");
|
||||
|
||||
let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port), &path);
|
||||
|
||||
let _ = connect.map(|conn| {
|
||||
assert!(matches!(&conn, &Err(RpcError::NoAuthCode)));
|
||||
}).wait();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_authcode_correct() {
|
||||
let (_srv, port, mut authcodes) = rpc::tests::ws::serve();
|
||||
|
||||
let _ = authcodes.generate_new();
|
||||
authcodes.to_file(&authcodes.path).unwrap();
|
||||
|
||||
let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port),
|
||||
&authcodes.path);
|
||||
|
||||
let _ = connect.map(|conn| {
|
||||
assert!(conn.is_ok())
|
||||
}).wait();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use client::{Rpc, RpcError};
|
||||
use ethereum_types::U256;
|
||||
use rpc::signer::{ConfirmationRequest, TransactionModification, TransactionCondition};
|
||||
use serde;
|
||||
use serde_json::{Value as JsonValue, to_value};
|
||||
use std::path::PathBuf;
|
||||
use futures::{Canceled};
|
||||
use {BoxFuture};
|
||||
|
||||
pub struct SignerRpc {
|
||||
rpc: Rpc,
|
||||
}
|
||||
|
||||
impl SignerRpc {
|
||||
pub fn new(url: &str, authfile: &PathBuf) -> Result<Self, RpcError> {
|
||||
Ok(SignerRpc { rpc: Rpc::new(&url, authfile)? })
|
||||
}
|
||||
|
||||
pub fn requests_to_confirm(&mut self) -> BoxFuture<Result<Vec<ConfirmationRequest>, RpcError>, Canceled> {
|
||||
self.rpc.request("signer_requestsToConfirm", vec![])
|
||||
}
|
||||
|
||||
pub fn confirm_request(
|
||||
&mut self,
|
||||
id: U256,
|
||||
new_gas: Option<U256>,
|
||||
new_gas_price: Option<U256>,
|
||||
new_condition: Option<Option<TransactionCondition>>,
|
||||
pwd: &str
|
||||
) -> BoxFuture<Result<U256, RpcError>, Canceled> {
|
||||
self.rpc.request("signer_confirmRequest", vec![
|
||||
Self::to_value(&format!("{:#x}", id)),
|
||||
Self::to_value(&TransactionModification { sender: None, gas_price: new_gas_price, gas: new_gas, condition: new_condition }),
|
||||
Self::to_value(&pwd),
|
||||
])
|
||||
}
|
||||
|
||||
pub fn reject_request(&mut self, id: U256) -> BoxFuture<Result<bool, RpcError>, Canceled> {
|
||||
self.rpc.request("signer_rejectRequest", vec![
|
||||
JsonValue::String(format!("{:#x}", id))
|
||||
])
|
||||
}
|
||||
|
||||
fn to_value<T: serde::Serialize>(v: &T) -> JsonValue {
|
||||
to_value(v).expect("Our types are always serializable; qed")
|
||||
}
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate ethereum_types;
|
||||
extern crate futures;
|
||||
extern crate rpassword;
|
||||
|
||||
extern crate parity_rpc as rpc;
|
||||
extern crate parity_rpc_client as client;
|
||||
|
||||
use ethereum_types::U256;
|
||||
use rpc::signer::ConfirmationRequest;
|
||||
use client::signer_client::SignerRpc;
|
||||
use std::io::{Write, BufRead, BufReader, stdout, stdin};
|
||||
use std::path::PathBuf;
|
||||
use std::fs::File;
|
||||
|
||||
use futures::Future;
|
||||
|
||||
fn sign_interactive(
|
||||
signer: &mut SignerRpc,
|
||||
password: &str,
|
||||
request: ConfirmationRequest
|
||||
) {
|
||||
print!("\n{}\nSign this transaction? (y)es/(N)o/(r)eject: ", request);
|
||||
let _ = stdout().flush();
|
||||
match BufReader::new(stdin()).lines().next() {
|
||||
Some(Ok(line)) => {
|
||||
match line.to_lowercase().chars().nth(0) {
|
||||
Some('y') => {
|
||||
match sign_transaction(signer, request.id, password) {
|
||||
Ok(s) | Err(s) => println!("{}", s),
|
||||
}
|
||||
}
|
||||
Some('r') => {
|
||||
match reject_transaction(signer, request.id) {
|
||||
Ok(s) | Err(s) => println!("{}", s),
|
||||
}
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
_ => println!("Could not read from stdin")
|
||||
}
|
||||
}
|
||||
|
||||
fn sign_transactions(
|
||||
signer: &mut SignerRpc,
|
||||
password: String
|
||||
) -> Result<String, String> {
|
||||
signer.requests_to_confirm().map(|reqs| {
|
||||
match reqs {
|
||||
Ok(ref reqs) if reqs.is_empty() => {
|
||||
Ok("No transactions in signing queue".to_owned())
|
||||
}
|
||||
Ok(reqs) => {
|
||||
for r in reqs {
|
||||
sign_interactive(signer, &password, r)
|
||||
}
|
||||
Ok("".to_owned())
|
||||
}
|
||||
Err(err) => {
|
||||
Err(format!("error: {:?}", err))
|
||||
}
|
||||
}
|
||||
}).map_err(|err| {
|
||||
format!("{:?}", err)
|
||||
}).wait()?
|
||||
}
|
||||
|
||||
fn list_transactions(signer: &mut SignerRpc) -> Result<String, String> {
|
||||
signer.requests_to_confirm().map(|reqs| {
|
||||
match reqs {
|
||||
Ok(ref reqs) if reqs.is_empty() => {
|
||||
Ok("No transactions in signing queue".to_owned())
|
||||
}
|
||||
Ok(ref reqs) => {
|
||||
Ok(format!("Transaction queue:\n{}", reqs
|
||||
.iter()
|
||||
.map(|r| format!("{}", r))
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n")))
|
||||
}
|
||||
Err(err) => {
|
||||
Err(format!("error: {:?}", err))
|
||||
}
|
||||
}
|
||||
}).map_err(|err| {
|
||||
format!("{:?}", err)
|
||||
}).wait()?
|
||||
}
|
||||
|
||||
fn sign_transaction(
|
||||
signer: &mut SignerRpc, id: U256, password: &str
|
||||
) -> Result<String, String> {
|
||||
signer.confirm_request(id, None, None, None, password).map(|res| {
|
||||
match res {
|
||||
Ok(u) => Ok(format!("Signed transaction id: {:#x}", u)),
|
||||
Err(e) => Err(format!("{:?}", e)),
|
||||
}
|
||||
}).map_err(|err| {
|
||||
format!("{:?}", err)
|
||||
}).wait()?
|
||||
}
|
||||
|
||||
fn reject_transaction(
|
||||
signer: &mut SignerRpc, id: U256) -> Result<String, String>
|
||||
{
|
||||
signer.reject_request(id).map(|res| {
|
||||
match res {
|
||||
Ok(true) => Ok(format!("Rejected transaction id {:#x}", id)),
|
||||
Ok(false) => Err(format!("No such request")),
|
||||
Err(e) => Err(format!("{:?}", e)),
|
||||
}
|
||||
}).map_err(|err| {
|
||||
format!("{:?}", err)
|
||||
}).wait()?
|
||||
}
|
||||
|
||||
// cmds
|
||||
|
||||
pub fn signer_list(
|
||||
signerport: u16, authfile: PathBuf
|
||||
) -> Result<String, String> {
|
||||
let addr = &format!("ws://127.0.0.1:{}", signerport);
|
||||
let mut signer = SignerRpc::new(addr, &authfile).map_err(|err| {
|
||||
format!("{:?}", err)
|
||||
})?;
|
||||
list_transactions(&mut signer)
|
||||
}
|
||||
|
||||
pub fn signer_reject(
|
||||
id: Option<usize>, signerport: u16, authfile: PathBuf
|
||||
) -> Result<String, String> {
|
||||
let id = id.ok_or(format!("id required for signer reject"))?;
|
||||
let addr = &format!("ws://127.0.0.1:{}", signerport);
|
||||
let mut signer = SignerRpc::new(addr, &authfile).map_err(|err| {
|
||||
format!("{:?}", err)
|
||||
})?;
|
||||
reject_transaction(&mut signer, U256::from(id))
|
||||
}
|
||||
|
||||
pub fn signer_sign(
|
||||
id: Option<usize>,
|
||||
pwfile: Option<PathBuf>,
|
||||
signerport: u16,
|
||||
authfile: PathBuf
|
||||
) -> Result<String, String> {
|
||||
let password;
|
||||
match pwfile {
|
||||
Some(pwfile) => {
|
||||
match File::open(pwfile) {
|
||||
Ok(fd) => {
|
||||
match BufReader::new(fd).lines().next() {
|
||||
Some(Ok(line)) => password = line,
|
||||
_ => return Err(format!("No password in file"))
|
||||
}
|
||||
},
|
||||
Err(e) =>
|
||||
return Err(format!("Could not open password file: {}", e))
|
||||
}
|
||||
}
|
||||
None => {
|
||||
password = match rpassword::prompt_password_stdout("Password: ") {
|
||||
Ok(p) => p,
|
||||
Err(e) => return Err(format!("{}", e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let addr = &format!("ws://127.0.0.1:{}", signerport);
|
||||
let mut signer = SignerRpc::new(addr, &authfile).map_err(|err| {
|
||||
format!("{:?}", err)
|
||||
})?;
|
||||
|
||||
match id {
|
||||
Some(id) => {
|
||||
sign_transaction(&mut signer, U256::from(id), &password)
|
||||
},
|
||||
None => {
|
||||
sign_transactions(&mut signer, password)
|
||||
}
|
||||
}
|
||||
}
|
||||
37
cov.sh
Executable file
37
cov.sh
Executable file
@@ -0,0 +1,37 @@
|
||||
#!/bin/sh
|
||||
# Installing KCOV under ubuntu
|
||||
# https://users.rust-lang.org/t/tutorial-how-to-collect-test-coverages-for-rust-project/650#
|
||||
### Install deps
|
||||
# $ sudo apt-get install libcurl4-openssl-dev libelf-dev libdw-dev cmake gcc binutils-dev libiberty-dev
|
||||
#
|
||||
### Compile kcov
|
||||
# $ wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz && tar xf master.tar.gz
|
||||
# $ cd kcov-master && mkdir build && cd build
|
||||
# $ cmake .. && make && sudo make install
|
||||
|
||||
### Running coverage
|
||||
if ! type kcov > /dev/null; then
|
||||
echo "Install kcov first (details inside this file). Aborting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cargo test \
|
||||
-p ethash \
|
||||
-p ethcore-util \
|
||||
-p ethcore \
|
||||
-p ethsync \
|
||||
-p ethcore-rpc \
|
||||
-p parity \
|
||||
-p ethminer \
|
||||
--no-run || exit $?
|
||||
rm -rf target/coverage
|
||||
mkdir -p target/coverage
|
||||
|
||||
EXCLUDE="~/.multirust,rocksdb,secp256k1,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests"
|
||||
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore-*
|
||||
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethash-*
|
||||
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_util-*
|
||||
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethsync-*
|
||||
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_rpc-*
|
||||
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethminer-*
|
||||
xdg-open target/coverage/index.html
|
||||
16
devtools/Cargo.toml
Normal file
16
devtools/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
description = "Ethcore development/test/build tools"
|
||||
homepage = "http://ethcore.io"
|
||||
license = "GPL-3.0"
|
||||
name = "ethcore-devtools"
|
||||
version = "1.0.0"
|
||||
authors = ["Ethcore <admin@ethcore.io>"]
|
||||
|
||||
[dependencies]
|
||||
rand = "0.3"
|
||||
|
||||
[features]
|
||||
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
test = true
|
||||
1
devtools/README.md
Normal file
1
devtools/README.md
Normal file
@@ -0,0 +1 @@
|
||||
# ethcore dev tools
|
||||
@@ -1,23 +1,24 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
pub mod account;
|
||||
pub mod state;
|
||||
//! dev-tools
|
||||
|
||||
pub use {
|
||||
account::PodAccount,
|
||||
state::PodState,
|
||||
};
|
||||
|
||||
extern crate rand;
|
||||
|
||||
pub mod random_path;
|
||||
|
||||
pub use random_path::*;
|
||||
89
devtools/src/random_path.rs
Normal file
89
devtools/src/random_path.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Random path
|
||||
|
||||
use std::path::*;
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use rand::random;
|
||||
|
||||
pub struct RandomTempPath {
|
||||
path: PathBuf
|
||||
}
|
||||
|
||||
pub fn random_filename() -> String {
|
||||
(0..8).map(|_| ((random::<f32>() * 26.0) as u8 + 97) as char).collect()
|
||||
}
|
||||
|
||||
impl RandomTempPath {
|
||||
pub fn new() -> RandomTempPath {
|
||||
let mut dir = env::temp_dir();
|
||||
dir.push(random_filename());
|
||||
RandomTempPath {
|
||||
path: dir.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_dir() -> RandomTempPath {
|
||||
let mut dir = env::temp_dir();
|
||||
dir.push(random_filename());
|
||||
fs::create_dir_all(dir.as_path()).unwrap();
|
||||
RandomTempPath {
|
||||
path: dir.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_path(&self) -> &PathBuf {
|
||||
&self.path
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.path.to_str().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RandomTempPath {
|
||||
fn drop(&mut self) {
|
||||
if let Err(e) = fs::remove_dir_all(self.as_path()) {
|
||||
panic!("failed to remove temp directory, probably something failed to destroyed ({})", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn creates_dir() {
|
||||
let temp = RandomTempPath::create_dir();
|
||||
assert!(fs::metadata(temp.as_path()).unwrap().is_dir());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn destroys_dir() {
|
||||
let path_buf = {
|
||||
let temp = RandomTempPath::create_dir();
|
||||
assert!(fs::metadata(temp.as_path()).unwrap().is_dir());
|
||||
let path_buf = temp.as_path().to_path_buf();
|
||||
path_buf
|
||||
};
|
||||
|
||||
assert!(fs::metadata(&path_buf).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn provides_random() {
|
||||
let temp = RandomTempPath::create_dir();
|
||||
assert!(temp.as_path().to_str().is_some());
|
||||
}
|
||||
11
doc.sh
Executable file
11
doc.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
# generate documentation only for partiy and ethcore libraries
|
||||
|
||||
cargo doc --no-deps --verbose \
|
||||
-p ethash \
|
||||
-p ethcore-util \
|
||||
-p ethcore \
|
||||
-p ethsync \
|
||||
-p ethcore-rpc \
|
||||
-p parity \
|
||||
-p ethminer
|
||||
37
docker/ubuntu-dev/Dockerfile
Normal file
37
docker/ubuntu-dev/Dockerfile
Normal file
@@ -0,0 +1,37 @@
|
||||
FROM ubuntu:14.04
|
||||
|
||||
# install tools and dependencies
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
# make
|
||||
build-essential \
|
||||
# add-apt-repository
|
||||
software-properties-common \
|
||||
curl \
|
||||
g++ \
|
||||
wget \
|
||||
git \
|
||||
# evmjit dependencies
|
||||
zlib1g-dev \
|
||||
libedit-dev
|
||||
|
||||
# cmake, llvm and rocksdb ppas. then update ppas
|
||||
RUN add-apt-repository -y "ppa:george-edison55/cmake-3.x" && \
|
||||
add-apt-repository "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.7 main" && \
|
||||
apt-get update && \
|
||||
apt-get install -y --force-yes cmake llvm-3.7-dev
|
||||
|
||||
# install evmjit
|
||||
RUN git clone https://github.com/debris/evmjit && \
|
||||
cd evmjit && \
|
||||
mkdir build && cd build && \
|
||||
cmake .. && make && make install && cd
|
||||
|
||||
# install multirust
|
||||
RUN curl -sf https://raw.githubusercontent.com/brson/multirust/master/blastoff.sh | sh -s -- --yes
|
||||
|
||||
# export rust LIBRARY_PATH
|
||||
ENV LIBRARY_PATH /usr/local/lib
|
||||
|
||||
# show backtraces
|
||||
ENV RUST_BACKTRACE 1
|
||||
42
docker/ubuntu-jit/Dockerfile
Normal file
42
docker/ubuntu-jit/Dockerfile
Normal file
@@ -0,0 +1,42 @@
|
||||
FROM ubuntu:14.04
|
||||
|
||||
# install tools and dependencies
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
# make
|
||||
build-essential \
|
||||
# add-apt-repository
|
||||
software-properties-common \
|
||||
curl \
|
||||
wget \
|
||||
git \
|
||||
g++ \
|
||||
# evmjit dependencies
|
||||
zlib1g-dev \
|
||||
libedit-dev
|
||||
|
||||
# cmake, llvm and rocksdb ppas. then update ppas
|
||||
RUN add-apt-repository -y "ppa:george-edison55/cmake-3.x" && \
|
||||
add-apt-repository "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.7 main" && \
|
||||
apt-get update && \
|
||||
apt-get install -y --force-yes cmake llvm-3.7-dev
|
||||
|
||||
# install evmjit
|
||||
RUN git clone https://github.com/debris/evmjit && \
|
||||
cd evmjit && \
|
||||
mkdir build && cd build && \
|
||||
cmake .. && make && make install && cd
|
||||
|
||||
# install multirust
|
||||
RUN curl -sf https://raw.githubusercontent.com/brson/multirust/master/blastoff.sh | sh -s -- --yes
|
||||
|
||||
# export rust LIBRARY_PATH
|
||||
ENV LIBRARY_PATH /usr/local/lib
|
||||
|
||||
# show backtraces
|
||||
ENV RUST_BACKTRACE 1
|
||||
|
||||
# build parity
|
||||
RUN git clone https://github.com/ethcore/parity && \
|
||||
cd parity && \
|
||||
cargo build --release --features ethcore/jit
|
||||
23
docker/ubuntu/Dockerfile
Normal file
23
docker/ubuntu/Dockerfile
Normal file
@@ -0,0 +1,23 @@
|
||||
FROM ubuntu:14.04
|
||||
|
||||
# install tools and dependencies
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
g++ \
|
||||
curl \
|
||||
git \
|
||||
make
|
||||
|
||||
# install multirust
|
||||
RUN curl -sf https://raw.githubusercontent.com/brson/multirust/master/blastoff.sh | sh -s -- --yes
|
||||
|
||||
# export rust LIBRARY_PATH
|
||||
ENV LIBRARY_PATH /usr/local/lib
|
||||
|
||||
# show backtraces
|
||||
ENV RUST_BACKTRACE 1
|
||||
|
||||
# build parity
|
||||
RUN git clone https://github.com/ethcore/parity && \
|
||||
cd parity && \
|
||||
cargo build --release
|
||||
@@ -1,228 +0,0 @@
|
||||
Note: Parity 0.9 reached End-of-Life on 2016-05-02 (EOL).
|
||||
|
||||
## Parity [beta-0.9.1](https://github.com/paritytech/parity/releases/tag/beta-0.9.1) (2016-02-16)
|
||||
|
||||
Homestead transition block changed to 1100000.
|
||||
|
||||
- Beta patch to 0.9.1 [#445](https://github.com/paritytech/parity/pull/445)
|
||||
- Delay homestead transition [#430](https://github.com/paritytech/parity/pull/430)
|
||||
- (BETA) https link in the installer (?) [#392](https://github.com/paritytech/parity/pull/392)
|
||||
- beta: Check for handshake expiration before attempting replace [#377](https://github.com/paritytech/parity/pull/377)
|
||||
|
||||
## Parity [beta-0.9](https://github.com/paritytech/parity/releases/tag/beta-0.9) (2016-02-08)
|
||||
|
||||
First Parity Beta 0.9 released.
|
||||
|
||||
- Panic on missing counters; Client cleanup [#368](https://github.com/paritytech/parity/pull/368)
|
||||
- Update README for new PPAs. [#369](https://github.com/paritytech/parity/pull/369)
|
||||
- block_queue::clear should be more thorough [#365](https://github.com/paritytech/parity/pull/365)
|
||||
- Fixed an issue with forked counters [#363](https://github.com/paritytech/parity/pull/363)
|
||||
- Install parity [#362](https://github.com/paritytech/parity/pull/362)
|
||||
- DB directory versioning [#358](https://github.com/paritytech/parity/pull/358)
|
||||
- Raise FD limit for MacOS [#357](https://github.com/paritytech/parity/pull/357)
|
||||
- Travis slack integration. [#356](https://github.com/paritytech/parity/pull/356)
|
||||
- SignedTransaction structure [#350](https://github.com/paritytech/parity/pull/350)
|
||||
- License [#354](https://github.com/paritytech/parity/pull/354)
|
||||
- Performance optimizations [#353](https://github.com/paritytech/parity/pull/353)
|
||||
- Gitter in README. [#355](https://github.com/paritytech/parity/pull/355)
|
||||
- test efforts, receipt requests [#352](https://github.com/paritytech/parity/pull/352)
|
||||
- sync tests setup & local module coverage [#348](https://github.com/paritytech/parity/pull/348)
|
||||
- install parity script [#347](https://github.com/paritytech/parity/pull/347)
|
||||
- evmjit homestead merge [#342](https://github.com/paritytech/parity/pull/342)
|
||||
- Fixed sync stalling on fork [#343](https://github.com/paritytech/parity/pull/343)
|
||||
- Remerge 264 [#334](https://github.com/paritytech/parity/pull/334)
|
||||
- Ethsync tests bfix [#339](https://github.com/paritytech/parity/pull/339)
|
||||
- Fix default options. [#335](https://github.com/paritytech/parity/pull/335)
|
||||
- sync queue limit hotfix [#338](https://github.com/paritytech/parity/pull/338)
|
||||
- Network tests, separate local coverage for utils [#333](https://github.com/paritytech/parity/pull/333)
|
||||
- fix parity version so netstats can parse it [#332](https://github.com/paritytech/parity/pull/332)
|
||||
- reveal surprise [#331](https://github.com/paritytech/parity/pull/331)
|
||||
- Revert removal of `new_code`. [#330](https://github.com/paritytech/parity/pull/330)
|
||||
- Network mod tests first part [#329](https://github.com/paritytech/parity/pull/329)
|
||||
- Look ma no `dead_code` [#323](https://github.com/paritytech/parity/pull/323)
|
||||
- Fixing JIT, Updating hook to run `ethcore` tests. [#326](https://github.com/paritytech/parity/pull/326)
|
||||
- Final docs [#327](https://github.com/paritytech/parity/pull/327)
|
||||
- update install-deps.sh [#316](https://github.com/paritytech/parity/pull/316)
|
||||
- Finish all my docs. Fix previous test compilation. [#320](https://github.com/paritytech/parity/pull/320)
|
||||
- Additional evm tests (extops, call, jumps) and some docs [#317](https://github.com/paritytech/parity/pull/317)
|
||||
- More documentation. [#318](https://github.com/paritytech/parity/pull/318)
|
||||
- Additional documentation. [#315](https://github.com/paritytech/parity/pull/315)
|
||||
- unused functions cleanup [#310](https://github.com/paritytech/parity/pull/310)
|
||||
- update ethcore.github.io documentation automatically [#311](https://github.com/paritytech/parity/pull/311)
|
||||
- Another try with travis ci credentials [#314](https://github.com/paritytech/parity/pull/314)
|
||||
- Document some stuff. [#309](https://github.com/paritytech/parity/pull/309)
|
||||
- Check block parent on import; Peer timeouts [#303](https://github.com/paritytech/parity/pull/303)
|
||||
- Increasing coverage for evm. [#306](https://github.com/paritytech/parity/pull/306)
|
||||
- ethcore docs [#301](https://github.com/paritytech/parity/pull/301)
|
||||
- Replacing secure token for deployment [#305](https://github.com/paritytech/parity/pull/305)
|
||||
- doc.sh [#299](https://github.com/paritytech/parity/pull/299)
|
||||
- Building beta-* and stable-* tags [#302](https://github.com/paritytech/parity/pull/302)
|
||||
- Deploying artifacts for tags (release/beta) [#300](https://github.com/paritytech/parity/pull/300)
|
||||
- cov.sh to show coverage locally [#298](https://github.com/paritytech/parity/pull/298)
|
||||
- benchmark fixes [#297](https://github.com/paritytech/parity/pull/297)
|
||||
- Include JSONRPC CLI options. [#296](https://github.com/paritytech/parity/pull/296)
|
||||
- travis.yml fixes [#293](https://github.com/paritytech/parity/pull/293)
|
||||
- Improve version string. [#295](https://github.com/paritytech/parity/pull/295)
|
||||
- Fixed block queue test [#294](https://github.com/paritytech/parity/pull/294)
|
||||
- Util docs [#292](https://github.com/paritytech/parity/pull/292)
|
||||
- fixed building docs [#289](https://github.com/paritytech/parity/pull/289)
|
||||
- update travis to build PRs only against master [#290](https://github.com/paritytech/parity/pull/290)
|
||||
- Coverage effort [#272](https://github.com/paritytech/parity/pull/272)
|
||||
- updated docker containers [#288](https://github.com/paritytech/parity/pull/288)
|
||||
- rpc module fixes [#287](https://github.com/paritytech/parity/pull/287)
|
||||
- Test for Receipt RLP. [#282](https://github.com/paritytech/parity/pull/282)
|
||||
- Building from source guide [#284](https://github.com/paritytech/parity/pull/284)
|
||||
- Fixed neted empty list RLP encoding [#283](https://github.com/paritytech/parity/pull/283)
|
||||
- Fix CALLDATACOPY (and bonus CODECOPY, too!). [#279](https://github.com/paritytech/parity/pull/279)
|
||||
- added travis && coveralls badge to README.md [#280](https://github.com/paritytech/parity/pull/280)
|
||||
- coveralls coverage [#277](https://github.com/paritytech/parity/pull/277)
|
||||
- Travis [in progress] [#257](https://github.com/paritytech/parity/pull/257)
|
||||
- Travis on reorganized repo [#276](https://github.com/paritytech/parity/pull/276)
|
||||
- umbrella project [#275](https://github.com/paritytech/parity/pull/275)
|
||||
- Ethash disk cache [#273](https://github.com/paritytech/parity/pull/273)
|
||||
- Parity executable name and version [#274](https://github.com/paritytech/parity/pull/274)
|
||||
- Dockerfile [#195](https://github.com/paritytech/parity/pull/195)
|
||||
- Garbage collection test fix [#267](https://github.com/paritytech/parity/pull/267)
|
||||
- Fix stCallCreateCallCodeTest, add more tests [#271](https://github.com/paritytech/parity/pull/271)
|
||||
- Moved sync out of ethcore crate; Added block validation [#265](https://github.com/paritytech/parity/pull/265)
|
||||
- RLP encoder refactoring [#252](https://github.com/paritytech/parity/pull/252)
|
||||
- Chain sync tests and minor refactoring [#264](https://github.com/paritytech/parity/pull/264)
|
||||
- Common log init function [#263](https://github.com/paritytech/parity/pull/263)
|
||||
- changed max vm depth from 128 to 64, change homestead block to 1_000_000 [#262](https://github.com/paritytech/parity/pull/262)
|
||||
- fixed blockchain tests crash on log init [#261](https://github.com/paritytech/parity/pull/261)
|
||||
- Blockchain tests and some helpers for guarding temp directory [#256](https://github.com/paritytech/parity/pull/256)
|
||||
- Fix logging and random tests. [#260](https://github.com/paritytech/parity/pull/260)
|
||||
- Fix difficulty calculation algo. [#259](https://github.com/paritytech/parity/pull/259)
|
||||
- fix submodule version [#258](https://github.com/paritytech/parity/pull/258)
|
||||
- temp dir spawn refactoring [#246](https://github.com/paritytech/parity/pull/246)
|
||||
- fixed tests submodule branch [#254](https://github.com/paritytech/parity/pull/254)
|
||||
- rpc net methods returns real peer count && protocol version [#253](https://github.com/paritytech/parity/pull/253)
|
||||
- Add homestead & random tests. [#245](https://github.com/paritytech/parity/pull/245)
|
||||
- Fixing suicide with self-refund to be consistent with CPP. [#247](https://github.com/paritytech/parity/pull/247)
|
||||
- stubs for rpc methods [#251](https://github.com/paritytech/parity/pull/251)
|
||||
- clippy, missing docs, renaming etc. [#244](https://github.com/paritytech/parity/pull/244)
|
||||
- impl missing methods in tests [#243](https://github.com/paritytech/parity/pull/243)
|
||||
- General tests and some helpers [#239](https://github.com/paritytech/parity/pull/239)
|
||||
- Note additional tests are fixed, fix doc test. [#242](https://github.com/paritytech/parity/pull/242)
|
||||
- jsonrpc http server [#193](https://github.com/paritytech/parity/pull/193)
|
||||
- Ethash nonce is H64 not a u64 [#240](https://github.com/paritytech/parity/pull/240)
|
||||
- Fix import for bcMultiChainTest [#236](https://github.com/paritytech/parity/pull/236)
|
||||
- Client basic tests [#232](https://github.com/paritytech/parity/pull/232)
|
||||
- Fix ensure_db_good() and flush_queue(), block refactoring, check block format, be strict. [#231](https://github.com/paritytech/parity/pull/231)
|
||||
- Rlp [#207](https://github.com/paritytech/parity/pull/207)
|
||||
- Schedule documentation [#219](https://github.com/paritytech/parity/pull/219)
|
||||
- U256<->H256 Conversion [#206](https://github.com/paritytech/parity/pull/206)
|
||||
- Spawning new thread when we are reaching stack limit [#217](https://github.com/paritytech/parity/pull/217)
|
||||
- Blockchain tests [#211](https://github.com/paritytech/parity/pull/211)
|
||||
- fixed failing sync test [#218](https://github.com/paritytech/parity/pull/218)
|
||||
- Removing println [#216](https://github.com/paritytech/parity/pull/216)
|
||||
- Cleaning readme [#212](https://github.com/paritytech/parity/pull/212)
|
||||
- Fixing delegatecall [#196](https://github.com/paritytech/parity/pull/196)
|
||||
- Autogenerate the Args from the docopt macro. [#205](https://github.com/paritytech/parity/pull/205)
|
||||
- Networking fixes [#202](https://github.com/paritytech/parity/pull/202)
|
||||
- Argument parsing from CLI [#204](https://github.com/paritytech/parity/pull/204)
|
||||
- Removed wildcard from clippy version [#203](https://github.com/paritytech/parity/pull/203)
|
||||
- Fixed tests and tweaked sync progress report [#201](https://github.com/paritytech/parity/pull/201)
|
||||
- Heavy tests [#199](https://github.com/paritytech/parity/pull/199)
|
||||
- Mutithreaded IO [#198](https://github.com/paritytech/parity/pull/198)
|
||||
- Populating last_hashes [#197](https://github.com/paritytech/parity/pull/197)
|
||||
- Fixing clippy stuff [#170](https://github.com/paritytech/parity/pull/170)
|
||||
- basic .travis.yml [#194](https://github.com/paritytech/parity/pull/194)
|
||||
- Generating coverage reports. [#190](https://github.com/paritytech/parity/pull/190)
|
||||
- Adding doc requests comments [#192](https://github.com/paritytech/parity/pull/192)
|
||||
- moved src/bin/client.rs -> src/bin/client/main.rs [#185](https://github.com/paritytech/parity/pull/185)
|
||||
- removed overflowing_shr [#188](https://github.com/paritytech/parity/pull/188)
|
||||
- fixed wrapping ops on latest nightly [#187](https://github.com/paritytech/parity/pull/187)
|
||||
- Pruned state DB [#176](https://github.com/paritytech/parity/pull/176)
|
||||
- Memory management for cache [#180](https://github.com/paritytech/parity/pull/180)
|
||||
- Implement signs having low-s. [#183](https://github.com/paritytech/parity/pull/183)
|
||||
- Introduce sha3 crate and use it in ethash [#178](https://github.com/paritytech/parity/pull/178)
|
||||
- Multithreaded block queue [#173](https://github.com/paritytech/parity/pull/173)
|
||||
- Iterator for NibbleSlice and TrieDB. [#171](https://github.com/paritytech/parity/pull/171)
|
||||
- Handling all possible overflows [#145](https://github.com/paritytech/parity/pull/145)
|
||||
- Global secp256k1 context [#164](https://github.com/paritytech/parity/pull/164)
|
||||
- Ethash [#152](https://github.com/paritytech/parity/pull/152)
|
||||
- Move util into here [#153](https://github.com/paritytech/parity/pull/153)
|
||||
- EVM Interpreter [#103](https://github.com/paritytech/parity/pull/103)
|
||||
- Homestead transition support, maybe. [#141](https://github.com/paritytech/parity/pull/141)
|
||||
- externalities refactor [#131](https://github.com/paritytech/parity/pull/131)
|
||||
- More open files. [#140](https://github.com/paritytech/parity/pull/140)
|
||||
- Single array for logs output. [#133](https://github.com/paritytech/parity/pull/133)
|
||||
- Client app event handler [#132](https://github.com/paritytech/parity/pull/132)
|
||||
- Various consensus fixes. [#130](https://github.com/paritytech/parity/pull/130)
|
||||
- callcode builtins tests pass [#127](https://github.com/paritytech/parity/pull/127)
|
||||
- Client state syncing [#119](https://github.com/paritytech/parity/pull/119)
|
||||
- Split externalities from executive. [#126](https://github.com/paritytech/parity/pull/126)
|
||||
- executive error on not enoguh base gas [#124](https://github.com/paritytech/parity/pull/124)
|
||||
- Gav [#125](https://github.com/paritytech/parity/pull/125)
|
||||
- builtin sets excepted to true [#123](https://github.com/paritytech/parity/pull/123)
|
||||
- More state tests. [#122](https://github.com/paritytech/parity/pull/122)
|
||||
- updated to rocksdb wrapper version 0.3 [#121](https://github.com/paritytech/parity/pull/121)
|
||||
- out_of_gas -> excepted [#120](https://github.com/paritytech/parity/pull/120)
|
||||
- Parametrizing evm::Factory [#111](https://github.com/paritytech/parity/pull/111)
|
||||
- stLogs tests passing [#118](https://github.com/paritytech/parity/pull/118)
|
||||
- Fix executive. [#117](https://github.com/paritytech/parity/pull/117)
|
||||
- Fixes for marek's shooting from the hip. [#116](https://github.com/paritytech/parity/pull/116)
|
||||
- Executive revert fix [#115](https://github.com/paritytech/parity/pull/115)
|
||||
- Fix storage/account and add butress test. [#114](https://github.com/paritytech/parity/pull/114)
|
||||
- Refactored Pod & Diff types into separate files, JSON infrastructure revamp. [#113](https://github.com/paritytech/parity/pull/113)
|
||||
- Fix storage stuff and introduce per-item dirty-tracking. [#112](https://github.com/paritytech/parity/pull/112)
|
||||
- Check logs in state tests. [#109](https://github.com/paritytech/parity/pull/109)
|
||||
- executive gas calculation fixes [#108](https://github.com/paritytech/parity/pull/108)
|
||||
- proper gas calculation in executive [#107](https://github.com/paritytech/parity/pull/107)
|
||||
- Fixing MaxDepth param for executive [#105](https://github.com/paritytech/parity/pull/105)
|
||||
- Fix determination of state roots. [#106](https://github.com/paritytech/parity/pull/106)
|
||||
- transact substracts tx_gas [#104](https://github.com/paritytech/parity/pull/104)
|
||||
- Pretty-print and fix for state. [#102](https://github.com/paritytech/parity/pull/102)
|
||||
- Tier step price. [#101](https://github.com/paritytech/parity/pull/101)
|
||||
- Refactor Diff datastructures. [#100](https://github.com/paritytech/parity/pull/100)
|
||||
- externalities use u256 instead of u64 for gas calculation [#99](https://github.com/paritytech/parity/pull/99)
|
||||
- Executive tests [#97](https://github.com/paritytech/parity/pull/97)
|
||||
- State conensus tests now print mismatching diff on fail. [#98](https://github.com/paritytech/parity/pull/98)
|
||||
- State testing framework. First test is failing. [#96](https://github.com/paritytech/parity/pull/96)
|
||||
- executive tests [#95](https://github.com/paritytech/parity/pull/95)
|
||||
- Use U512s for ether cost calculation, complete transaction API [#94](https://github.com/paritytech/parity/pull/94)
|
||||
- Utils for consensus test decoding and better layout. [#93](https://github.com/paritytech/parity/pull/93)
|
||||
- executive fixes + tests [#89](https://github.com/paritytech/parity/pull/89)
|
||||
- All transaction tests pass. Nicer testing framework. [#92](https://github.com/paritytech/parity/pull/92)
|
||||
- Block verification tests; BlockProvider blockchain trait for testing [#88](https://github.com/paritytech/parity/pull/88)
|
||||
- State::exists, docs and tests. [#87](https://github.com/paritytech/parity/pull/87)
|
||||
- Add tests module, add two more transaction tests. [#86](https://github.com/paritytech/parity/pull/86)
|
||||
- bring back removed tests, removed build warnings [#82](https://github.com/paritytech/parity/pull/82)
|
||||
- Nicer transaction validation API. Nicer OutOfBounds API in general. [#85](https://github.com/paritytech/parity/pull/85)
|
||||
- Transaction fixes and consensus tests (all passing) [#84](https://github.com/paritytech/parity/pull/84)
|
||||
- fixed getting block info in evmjit + tests [#81](https://github.com/paritytech/parity/pull/81)
|
||||
- evm tests cleanup [#80](https://github.com/paritytech/parity/pull/80)
|
||||
- renamed VmFactory -> Factory [#77](https://github.com/paritytech/parity/pull/77)
|
||||
- fixed rust-evmjit description of improper_ctypes usage [#76](https://github.com/paritytech/parity/pull/76)
|
||||
- jit feature enabled by default [#75](https://github.com/paritytech/parity/pull/75)
|
||||
- evm [#52](https://github.com/paritytech/parity/pull/52)
|
||||
- state clone [#74](https://github.com/paritytech/parity/pull/74)
|
||||
- Block Verification (no tests yet) [#72](https://github.com/paritytech/parity/pull/72)
|
||||
- Improvements to LogEntry and Transaction [#73](https://github.com/paritytech/parity/pull/73)
|
||||
- Use getter in header in preparation for a Header trait; additional testing in enact_block(). [#64](https://github.com/paritytech/parity/pull/64)
|
||||
- BlockChain sync and Client app [#55](https://github.com/paritytech/parity/pull/55)
|
||||
- Block enactment (including test) [#63](https://github.com/paritytech/parity/pull/63)
|
||||
- Block complete. Needs tests. [#62](https://github.com/paritytech/parity/pull/62)
|
||||
- More on OpenBlock::close; State::kill_account added [#61](https://github.com/paritytech/parity/pull/61)
|
||||
- Remove genesis module, add more chain specs and separate out ethereum-specific stuff [#60](https://github.com/paritytech/parity/pull/60)
|
||||
- State::new_contract, camelCase engine params, missing param [#59](https://github.com/paritytech/parity/pull/59)
|
||||
- Use reorganisation [#58](https://github.com/paritytech/parity/pull/58)
|
||||
- Initial Ethash/Block skeleton implementations. [#57](https://github.com/paritytech/parity/pull/57)
|
||||
- Spec with tested Morden genesis decoder and builtins. [#54](https://github.com/paritytech/parity/pull/54)
|
||||
- Move all chain parameters into `engine_params` [#50](https://github.com/paritytech/parity/pull/50)
|
||||
- jit ffi improvements [please review] [#51](https://github.com/paritytech/parity/pull/51)
|
||||
- blockchain [please review] [#34](https://github.com/paritytech/parity/pull/34)
|
||||
- Move information from networkparams.rs into spec.rs [#48](https://github.com/paritytech/parity/pull/48)
|
||||
- Move bulking out in Engine/Params. [#47](https://github.com/paritytech/parity/pull/47)
|
||||
- Removed need for mutation in State. [#46](https://github.com/paritytech/parity/pull/46)
|
||||
- State::code and State::storage_at + tests. [#45](https://github.com/paritytech/parity/pull/45)
|
||||
- State functions for balance and nonce operations [#44](https://github.com/paritytech/parity/pull/44)
|
||||
- Account::storage_at, Account::ensure_cached and tests. [#43](https://github.com/paritytech/parity/pull/43)
|
||||
- Additional tests. [#42](https://github.com/paritytech/parity/pull/42)
|
||||
- seal todo done [#41](https://github.com/paritytech/parity/pull/41)
|
||||
- missing rustc_serialize crate && rlp `as_list` function [#40](https://github.com/paritytech/parity/pull/40)
|
||||
- More methods in Account, documentation and tests. [#39](https://github.com/paritytech/parity/pull/39)
|
||||
- Minor reworking of Account. [#38](https://github.com/paritytech/parity/pull/38)
|
||||
- Add Account and State classes. [#37](https://github.com/paritytech/parity/pull/37)
|
||||
- Revert regressions [#36](https://github.com/paritytech/parity/pull/36)
|
||||
@@ -1,317 +0,0 @@
|
||||
Note: Parity 1.0 reached End-of-Life on 2016-06-24 (EOL).
|
||||
|
||||
## Parity [v1.0.2](https://github.com/paritytech/parity/releases/tag/v1.0.2) (2016-04-11)
|
||||
|
||||
Parity 1.0.2 release improves Json RPC compatibility and fixes a number of stability issues.
|
||||
|
||||
- Flush password prompt [#1031](https://github.com/paritytech/parity/pull/1031)
|
||||
- [beta] dependencies update [#949](https://github.com/paritytech/parity/pull/949)
|
||||
- Master to beta v1.0.2 [#922](https://github.com/paritytech/parity/pull/922)
|
||||
- Master to beta 1.0.2 [#908](https://github.com/paritytech/parity/pull/908)
|
||||
|
||||
## Parity [v1.0.1](https://github.com/paritytech/parity/releases/tag/v1.0.1) (2016-03-28)
|
||||
|
||||
Parity 1.0.1 update fixes a number of issues with Json RPC, transaction propagation and syncing.
|
||||
|
||||
- Imporved sync error handling [#905](https://github.com/paritytech/parity/pull/905)
|
||||
- Publish locally-made transactions to peers. [#851](https://github.com/paritytech/parity/pull/851)
|
||||
- Merge fixes from master to beta [#845](https://github.com/paritytech/parity/pull/845)
|
||||
- Full sync restart on bad block [#844](https://github.com/paritytech/parity/pull/844)
|
||||
- Make BlockNumber optional, fix eth_call [#828](https://github.com/paritytech/parity/pull/828)
|
||||
- Web3sha3 beta [#826](https://github.com/paritytech/parity/pull/826)
|
||||
- Use network id for the web3_net_version return. [#821](https://github.com/paritytech/parity/pull/821)
|
||||
- Fix mining from spinning [#806](https://github.com/paritytech/parity/pull/806)
|
||||
- Merge master to beta [#796](https://github.com/paritytech/parity/pull/796)
|
||||
|
||||
## Parity [v1.0.0](https://github.com/paritytech/parity/releases/tag/v1.0.0) (2016-03-24)
|
||||
|
||||
Parity 1.0.0 release adds the following features:
|
||||
|
||||
- Standard JsonRPC interface.
|
||||
- Full Homestead compatibility.
|
||||
- Transaction management.
|
||||
- Mining with external miner.
|
||||
- Account management.
|
||||
- Geth key chain compatibility.
|
||||
- Additional command line options.
|
||||
- State trie pruning.
|
||||
- Cache and queue footprint.
|
||||
- Network discovery & NAT traversal.
|
||||
- Custom chain specification files.
|
||||
|
||||
Note that in this release the state database is in archive (full) mode by default. Run with one of the `--pruning` options to enable pruning.
|
||||
|
||||
- First part of multi-mining support [#804](https://github.com/paritytech/parity/pull/804)
|
||||
- Fixing future-current transactions clash [#802](https://github.com/paritytech/parity/pull/802)
|
||||
- Increase threads to num_cpus & fix author reporting [#800](https://github.com/paritytech/parity/pull/800)
|
||||
- another batch of rpc improvements [#798](https://github.com/paritytech/parity/pull/798)
|
||||
- Avoid tracing DELEGATECALL and CALLCODE. Plus tests for it. [#794](https://github.com/paritytech/parity/pull/794)
|
||||
- complete getting started steps for OS X [#793](https://github.com/paritytech/parity/pull/793)
|
||||
- Auto detect available port (with fixed test) [#788](https://github.com/paritytech/parity/pull/788)
|
||||
- eth_getTransactionReceipt [#792](https://github.com/paritytech/parity/pull/792)
|
||||
- Comprehensive tests for tracing transactions [#791](https://github.com/paritytech/parity/pull/791)
|
||||
- Disable preparing work package if miners don't ask for it. [#771](https://github.com/paritytech/parity/pull/771)
|
||||
- Listen on all interfaces for JSONRPC by default. [#786](https://github.com/paritytech/parity/pull/786)
|
||||
- eth_call [#783](https://github.com/paritytech/parity/pull/783)
|
||||
- Revert "Auto detect available port" [#789](https://github.com/paritytech/parity/pull/789)
|
||||
- added output to execution result [#777](https://github.com/paritytech/parity/pull/777)
|
||||
- Auto detect available port [#782](https://github.com/paritytech/parity/pull/782)
|
||||
- Allow 0x prefix for --author. [#785](https://github.com/paritytech/parity/pull/785)
|
||||
- updated dependencies, moved rpctest to its own submodule [#784](https://github.com/paritytech/parity/pull/784)
|
||||
- use ethjson module to load chain json tests [#778](https://github.com/paritytech/parity/pull/778)
|
||||
- Tracing implemented. [#772](https://github.com/paritytech/parity/pull/772)
|
||||
- test ethjson module on travis [#780](https://github.com/paritytech/parity/pull/780)
|
||||
- batch of rpc fixes [#775](https://github.com/paritytech/parity/pull/775)
|
||||
- rpctest executable [#757](https://github.com/paritytech/parity/pull/757)
|
||||
- Refactoring error transaction_queue error handling and `update_sealing` method. [#753](https://github.com/paritytech/parity/pull/753)
|
||||
- Avoid importing transactions with gas above 1.1*block_gas_limit to transaction queue [#760](https://github.com/paritytech/parity/pull/760)
|
||||
- Removing transactions that failed to be pushed to block. [#752](https://github.com/paritytech/parity/pull/752)
|
||||
- Updating clippy [#766](https://github.com/paritytech/parity/pull/766)
|
||||
- Attempting to add all transactions to mined block [#754](https://github.com/paritytech/parity/pull/754)
|
||||
- Prettier version w/o git dir; Use rustc compile time version [#761](https://github.com/paritytech/parity/pull/761)
|
||||
- Stop adding transactions to queue while not fully synced [#751](https://github.com/paritytech/parity/pull/751)
|
||||
- Verify sender's balance before importing transaction to queue [#746](https://github.com/paritytech/parity/pull/746)
|
||||
- Returning number of transactions pending in block not queue [#750](https://github.com/paritytech/parity/pull/750)
|
||||
- Speeding up build [#733](https://github.com/paritytech/parity/pull/733)
|
||||
- adding check for a sync when giving work to miner [#742](https://github.com/paritytech/parity/pull/742)
|
||||
- json deserialization module [#745](https://github.com/paritytech/parity/pull/745)
|
||||
- Update install-parity.sh [#749](https://github.com/paritytech/parity/pull/749)
|
||||
- Restart sync on getting old unknown header [#747](https://github.com/paritytech/parity/pull/747)
|
||||
- Missing return for #737 [#744](https://github.com/paritytech/parity/pull/744)
|
||||
- Enact block with uncles test [#741](https://github.com/paritytech/parity/pull/741)
|
||||
- Fix outdated libc version on dependency [#740](https://github.com/paritytech/parity/pull/740)
|
||||
- Fixing possible race in transaction queue [#735](https://github.com/paritytech/parity/pull/735)
|
||||
- Sync fixed again [#737](https://github.com/paritytech/parity/pull/737)
|
||||
- Don't change best block until extras is committed. [#734](https://github.com/paritytech/parity/pull/734)
|
||||
- stable only until travis speedup [#736](https://github.com/paritytech/parity/pull/736)
|
||||
- Optimizing uint operations (architecture independent) [#629](https://github.com/paritytech/parity/pull/629)
|
||||
- Add RLP, not a data item. [#725](https://github.com/paritytech/parity/pull/725)
|
||||
- PV63 receipts response [#687](https://github.com/paritytech/parity/pull/687)
|
||||
- another batch of rpc tests [#723](https://github.com/paritytech/parity/pull/723)
|
||||
- dockerfiles update [#726](https://github.com/paritytech/parity/pull/726)
|
||||
- Lock reports to avoid out of order badness. [#721](https://github.com/paritytech/parity/pull/721)
|
||||
- Fixed handshake leak [#722](https://github.com/paritytech/parity/pull/722)
|
||||
- Allow configuration of target gas limit. [#719](https://github.com/paritytech/parity/pull/719)
|
||||
- Version 1.1 in master [#714](https://github.com/paritytech/parity/pull/714)
|
||||
- Silence UDP warnings [#720](https://github.com/paritytech/parity/pull/720)
|
||||
- Rpc personal tests [#715](https://github.com/paritytech/parity/pull/715)
|
||||
- Fixing warnings [#704](https://github.com/paritytech/parity/pull/704)
|
||||
- docopts cleanups [#713](https://github.com/paritytech/parity/pull/713)
|
||||
- Removed rocksdb build dependency [#717](https://github.com/paritytech/parity/pull/717)
|
||||
- Fixed splitting Neighbours packet [#710](https://github.com/paritytech/parity/pull/710)
|
||||
- management of account expiration & memory [#701](https://github.com/paritytech/parity/pull/701)
|
||||
- Remove EarlyMerge from user docs. [#708](https://github.com/paritytech/parity/pull/708)
|
||||
- Fixes and traces for refcountdb. [#705](https://github.com/paritytech/parity/pull/705)
|
||||
- Check for NULL_RLP in AccountDB [#706](https://github.com/paritytech/parity/pull/706)
|
||||
- ethminer as crate [#700](https://github.com/paritytech/parity/pull/700)
|
||||
- Old ref-counted DB code [#692](https://github.com/paritytech/parity/pull/692)
|
||||
- next batch of rpc tests and fixes [#699](https://github.com/paritytech/parity/pull/699)
|
||||
- implemented eth_geStorageAt rpc method, added more tests for rpc [#695](https://github.com/paritytech/parity/pull/695)
|
||||
- Fix JournalDB era marker [#690](https://github.com/paritytech/parity/pull/690)
|
||||
- More sync fixes [#685](https://github.com/paritytech/parity/pull/685)
|
||||
- mark some key tests as heavy [#694](https://github.com/paritytech/parity/pull/694)
|
||||
- Limit incoming connections [#693](https://github.com/paritytech/parity/pull/693)
|
||||
- Updating clippy [#688](https://github.com/paritytech/parity/pull/688)
|
||||
- eth_accounts, eth_getBalance rpc functions && tests [#691](https://github.com/paritytech/parity/pull/691)
|
||||
- state query for archive jdb [#683](https://github.com/paritytech/parity/pull/683)
|
||||
- Fix for option 1 of JournalDB [#658](https://github.com/paritytech/parity/pull/658)
|
||||
- Rename into something that is a little more descriptive. [#689](https://github.com/paritytech/parity/pull/689)
|
||||
- JournalDB with in-memory overlay (option2) [#634](https://github.com/paritytech/parity/pull/634)
|
||||
- additional (failing) SecretStore test [#682](https://github.com/paritytech/parity/pull/682)
|
||||
- Updating clippy & fixing warnings. [#670](https://github.com/paritytech/parity/pull/670)
|
||||
- rpc web3 tests [#681](https://github.com/paritytech/parity/pull/681)
|
||||
- Making personal json-rpc configurable via cli [#677](https://github.com/paritytech/parity/pull/677)
|
||||
- RPC Pending Transactions Filter [#661](https://github.com/paritytech/parity/pull/661)
|
||||
- Rearrange journaldb infrastructure to make more extensible [#678](https://github.com/paritytech/parity/pull/678)
|
||||
- JournalDB -> Box<JournalDB>, and it's a trait. [#673](https://github.com/paritytech/parity/pull/673)
|
||||
- fix warning for transaction_queue.add usage [#676](https://github.com/paritytech/parity/pull/676)
|
||||
- Adding std::mem back (only for asm) [#680](https://github.com/paritytech/parity/pull/680)
|
||||
- update readme to exclude beta step (stable is ok) [#679](https://github.com/paritytech/parity/pull/679)
|
||||
- fixed U256 and transaction request deserialization [#675](https://github.com/paritytech/parity/pull/675)
|
||||
- More geth compatibility. [#666](https://github.com/paritytech/parity/pull/666)
|
||||
- Removing running clippy by default on nightly. [#671](https://github.com/paritytech/parity/pull/671)
|
||||
- rpc net submodule tests [#667](https://github.com/paritytech/parity/pull/667)
|
||||
- Client module overhaul [#665](https://github.com/paritytech/parity/pull/665)
|
||||
- Rpc transaction signing [#587](https://github.com/paritytech/parity/pull/587)
|
||||
- Transaction queue exposed via JSON rpc. [#652](https://github.com/paritytech/parity/pull/652)
|
||||
- Remove unneeded locking [#499](https://github.com/paritytech/parity/pull/499)
|
||||
- extend sync status interface to sync provider [#664](https://github.com/paritytech/parity/pull/664)
|
||||
- --archive is default. --pruning is option. [#663](https://github.com/paritytech/parity/pull/663)
|
||||
- jsonrpc uses client and sync interfaces [#641](https://github.com/paritytech/parity/pull/641)
|
||||
- Expose transaction insertion in sync lib [#609](https://github.com/paritytech/parity/pull/609)
|
||||
- Removing get prefix from poll_info [#660](https://github.com/paritytech/parity/pull/660)
|
||||
- Tx queue update height bug [#657](https://github.com/paritytech/parity/pull/657)
|
||||
- Tx_queue_docs -> To master [#651](https://github.com/paritytech/parity/pull/651)
|
||||
- blockchain import_route [#645](https://github.com/paritytech/parity/pull/645)
|
||||
- Stop workers before stopping event loop [#655](https://github.com/paritytech/parity/pull/655)
|
||||
- Validate sender before importing to queue [#650](https://github.com/paritytech/parity/pull/650)
|
||||
- Gas price threshold for transactions [#640](https://github.com/paritytech/parity/pull/640)
|
||||
- `dev` feature enabled when compiling without `--release` [#627](https://github.com/paritytech/parity/pull/627)
|
||||
- Don't call mark_as_bad needlessly [#648](https://github.com/paritytech/parity/pull/648)
|
||||
- Fixed sync handling large forks [#647](https://github.com/paritytech/parity/pull/647)
|
||||
- Additional documentation for transaction queue [#631](https://github.com/paritytech/parity/pull/631)
|
||||
- Transaction Queue Integration [#607](https://github.com/paritytech/parity/pull/607)
|
||||
- Keys cli [#639](https://github.com/paritytech/parity/pull/639)
|
||||
- fix build warning [#643](https://github.com/paritytech/parity/pull/643)
|
||||
- updated jsonrpc-core and http-server libs [#642](https://github.com/paritytech/parity/pull/642)
|
||||
- jsonrpc panics gracefully shutdown client [#638](https://github.com/paritytech/parity/pull/638)
|
||||
- Fixing CLI parameters [#633](https://github.com/paritytech/parity/pull/633)
|
||||
- Normal CLI options with geth. [#628](https://github.com/paritytech/parity/pull/628)
|
||||
- Do not remove the peer immediatelly on send error [#626](https://github.com/paritytech/parity/pull/626)
|
||||
- Jsonrpc block behind [#622](https://github.com/paritytech/parity/pull/622)
|
||||
- Remove println!s. [#624](https://github.com/paritytech/parity/pull/624)
|
||||
- JournalDB option 1 fix [#613](https://github.com/paritytech/parity/pull/613)
|
||||
- Network tracing cleanup [#611](https://github.com/paritytech/parity/pull/611)
|
||||
- Revert "Transaction Queue integration" [#602](https://github.com/paritytech/parity/pull/602)
|
||||
- fix benches compilation [#601](https://github.com/paritytech/parity/pull/601)
|
||||
- Transaction Queue integration [#595](https://github.com/paritytech/parity/pull/595)
|
||||
- verifier trait improvements [#597](https://github.com/paritytech/parity/pull/597)
|
||||
- build on rust stable [#600](https://github.com/paritytech/parity/pull/600)
|
||||
- Geth import silent if no geth [#599](https://github.com/paritytech/parity/pull/599)
|
||||
- Additional journaldb logging and assert [#593](https://github.com/paritytech/parity/pull/593)
|
||||
- Uncle inclusion in block authoring. [#578](https://github.com/paritytech/parity/pull/578)
|
||||
- Fixed potential deadlock on startup [#592](https://github.com/paritytech/parity/pull/592)
|
||||
- Fixing an overflow panic [#591](https://github.com/paritytech/parity/pull/591)
|
||||
- Fixed one more case of sync stalling [#590](https://github.com/paritytech/parity/pull/590)
|
||||
- JournalDB can now operate in "archive" mode [#589](https://github.com/paritytech/parity/pull/589)
|
||||
- Secret store integration with client [#586](https://github.com/paritytech/parity/pull/586)
|
||||
- fix build on nightly rust [#588](https://github.com/paritytech/parity/pull/588)
|
||||
- deserialization for uint generic [#585](https://github.com/paritytech/parity/pull/585)
|
||||
- TransactionsQueue implementation [#559](https://github.com/paritytech/parity/pull/559)
|
||||
- JSON-RPC personal service (follows #582) [#583](https://github.com/paritytech/parity/pull/583)
|
||||
- making key directory thread-safe [#582](https://github.com/paritytech/parity/pull/582)
|
||||
- verifier trait [#581](https://github.com/paritytech/parity/pull/581)
|
||||
- shrink_to_fit after removing hashes. [#580](https://github.com/paritytech/parity/pull/580)
|
||||
- support for rpc polling [#504](https://github.com/paritytech/parity/pull/504)
|
||||
- limit serde codegen only to rpc types submodule [#569](https://github.com/paritytech/parity/pull/569)
|
||||
- fork test for Issue test/568 [#573](https://github.com/paritytech/parity/pull/573)
|
||||
- Fixing clippy warnings = small refactoring of `request_blocks` [#560](https://github.com/paritytech/parity/pull/560)
|
||||
- Improved journaldb logging [#571](https://github.com/paritytech/parity/pull/571)
|
||||
- Additional check to ancient enactments. [#570](https://github.com/paritytech/parity/pull/570)
|
||||
- chainfilter shouldnt exclude to_block from results [#564](https://github.com/paritytech/parity/pull/564)
|
||||
- Fix coverage test run [#567](https://github.com/paritytech/parity/pull/567)
|
||||
- Mining [#547](https://github.com/paritytech/parity/pull/547)
|
||||
- fix uint warnings [#565](https://github.com/paritytech/parity/pull/565)
|
||||
- Finished blockchain generator. [#562](https://github.com/paritytech/parity/pull/562)
|
||||
- fixed broken master [#563](https://github.com/paritytech/parity/pull/563)
|
||||
- uint to separate crate [#544](https://github.com/paritytech/parity/pull/544)
|
||||
- improved test chain generator [#554](https://github.com/paritytech/parity/pull/554)
|
||||
- Fixing spelling in propagade->propagate [#558](https://github.com/paritytech/parity/pull/558)
|
||||
- Changing RefCell to Cell in transaction. [#557](https://github.com/paritytech/parity/pull/557)
|
||||
- Fix for morden consensus. [#556](https://github.com/paritytech/parity/pull/556)
|
||||
- blockchain generator [#550](https://github.com/paritytech/parity/pull/550)
|
||||
- Sparse Table Implementation (Row, Col) -> Val [#545](https://github.com/paritytech/parity/pull/545)
|
||||
- fixup install script [#548](https://github.com/paritytech/parity/pull/548)
|
||||
- Fixing clippy warnings [#546](https://github.com/paritytech/parity/pull/546)
|
||||
- ignore out directory [#543](https://github.com/paritytech/parity/pull/543)
|
||||
- u256 full multiplication [#539](https://github.com/paritytech/parity/pull/539)
|
||||
- Fix panic when downloading stales, update homestead transition [#537](https://github.com/paritytech/parity/pull/537)
|
||||
- changing x64 asm config [#534](https://github.com/paritytech/parity/pull/534)
|
||||
- uncomment state transition tests [#533](https://github.com/paritytech/parity/pull/533)
|
||||
- jsonrpc uses weak pointers to client [#532](https://github.com/paritytech/parity/pull/532)
|
||||
- Morden switch to Homestead rules at #494,000. [#531](https://github.com/paritytech/parity/pull/531)
|
||||
- Blockchain module cleanup [#524](https://github.com/paritytech/parity/pull/524)
|
||||
- Multiplication issue + very exhaustive tests for it [#528](https://github.com/paritytech/parity/pull/528)
|
||||
- EIP-8 [#498](https://github.com/paritytech/parity/pull/498)
|
||||
- Make "random" trie tests fully deterministic. [#527](https://github.com/paritytech/parity/pull/527)
|
||||
- udpated serde to version 0.7.0 [#526](https://github.com/paritytech/parity/pull/526)
|
||||
- Better memory management [#516](https://github.com/paritytech/parity/pull/516)
|
||||
- Typo [#523](https://github.com/paritytech/parity/pull/523)
|
||||
- U512 add/sub optimize [#521](https://github.com/paritytech/parity/pull/521)
|
||||
- Account management + geth keystore import (no utility crate added) [#509](https://github.com/paritytech/parity/pull/509)
|
||||
- Delayed UPnP initialization [#505](https://github.com/paritytech/parity/pull/505)
|
||||
- Fixing marking blocks as bad & SyncMessage bugs + small client refactoring. [#503](https://github.com/paritytech/parity/pull/503)
|
||||
- optimization of U256 [#515](https://github.com/paritytech/parity/pull/515)
|
||||
- Removed rocksdb from build scripts and instructions [#520](https://github.com/paritytech/parity/pull/520)
|
||||
- RocksDB abstraction layer + Hash index for state DB [#464](https://github.com/paritytech/parity/pull/464)
|
||||
- bloomfilter [#418](https://github.com/paritytech/parity/pull/418)
|
||||
- Fixed a race condition when connecting peer disconnects immediately [#519](https://github.com/paritytech/parity/pull/519)
|
||||
- ignore intellij idea project files as well [#518](https://github.com/paritytech/parity/pull/518)
|
||||
- updated version of unicase [#517](https://github.com/paritytech/parity/pull/517)
|
||||
- jsonrpc security, cors headers, fixed #359 [#493](https://github.com/paritytech/parity/pull/493)
|
||||
- Rust implementations to replace data tables (#161) [#482](https://github.com/paritytech/parity/pull/482)
|
||||
- fix issue with starting requested block number was not included itself [#512](https://github.com/paritytech/parity/pull/512)
|
||||
- fixed travis --org GH_TOKEN [#510](https://github.com/paritytech/parity/pull/510)
|
||||
- Improved log format [#506](https://github.com/paritytech/parity/pull/506)
|
||||
- Log address on failed connection attempt [#502](https://github.com/paritytech/parity/pull/502)
|
||||
- Bumping clippy and fixing warnings. [#501](https://github.com/paritytech/parity/pull/501)
|
||||
- Bumping versions. Fixes #496 [#500](https://github.com/paritytech/parity/pull/500)
|
||||
- Manage final user-input errors. [#494](https://github.com/paritytech/parity/pull/494)
|
||||
- Remove unneeded code, fix minor potential issue with length. [#495](https://github.com/paritytech/parity/pull/495)
|
||||
- Remove "unknown" from version string. [#488](https://github.com/paritytech/parity/pull/488)
|
||||
- Include git commit date & hash. [#486](https://github.com/paritytech/parity/pull/486)
|
||||
- Use proper version string. [#485](https://github.com/paritytech/parity/pull/485)
|
||||
- Networking fixes [#480](https://github.com/paritytech/parity/pull/480)
|
||||
- Fix potential deadlock on node table update [#484](https://github.com/paritytech/parity/pull/484)
|
||||
- Squash more warnings [#481](https://github.com/paritytech/parity/pull/481)
|
||||
- dev/test/build tools to separate crate [#477](https://github.com/paritytech/parity/pull/477)
|
||||
- Back to original slab crate [#479](https://github.com/paritytech/parity/pull/479)
|
||||
- Better user errors. [#476](https://github.com/paritytech/parity/pull/476)
|
||||
- UDP Discovery [#440](https://github.com/paritytech/parity/pull/440)
|
||||
- update readme with rust override [#475](https://github.com/paritytech/parity/pull/475)
|
||||
- fixed warnings on rust beta [#474](https://github.com/paritytech/parity/pull/474)
|
||||
- Secret store (part2 - encrypted key/value svc) [#449](https://github.com/paritytech/parity/pull/449)
|
||||
- Kill bad test. [#473](https://github.com/paritytech/parity/pull/473)
|
||||
- Make clippy an optional dependency [#422](https://github.com/paritytech/parity/pull/422)
|
||||
- parity compiling fine [#469](https://github.com/paritytech/parity/pull/469)
|
||||
- compiling ethcore on beta [#468](https://github.com/paritytech/parity/pull/468)
|
||||
- Utils compiling in beta [#467](https://github.com/paritytech/parity/pull/467)
|
||||
- Get rid of lru_cache dependency [#466](https://github.com/paritytech/parity/pull/466)
|
||||
- Add daemonization. [#459](https://github.com/paritytech/parity/pull/459)
|
||||
- Master upgrade [#448](https://github.com/paritytech/parity/pull/448)
|
||||
- Remove contributing stuff now that we have CLA bot. [#447](https://github.com/paritytech/parity/pull/447)
|
||||
- Add Morden bootnode. [#446](https://github.com/paritytech/parity/pull/446)
|
||||
- beta fixes to master [#441](https://github.com/paritytech/parity/pull/441)
|
||||
- Secret store (part1 - key management) [#423](https://github.com/paritytech/parity/pull/423)
|
||||
- Use 1100000 as the homestead transition, fix build instructions. [#438](https://github.com/paritytech/parity/pull/438)
|
||||
- More sync and propagation fixes [#420](https://github.com/paritytech/parity/pull/420)
|
||||
- back to cargo crates [#436](https://github.com/paritytech/parity/pull/436)
|
||||
- Fixing clippy warnings [#435](https://github.com/paritytech/parity/pull/435)
|
||||
- preserving root cargo lock [#434](https://github.com/paritytech/parity/pull/434)
|
||||
- Nightly fix [#432](https://github.com/paritytech/parity/pull/432)
|
||||
- nightly fixes [#431](https://github.com/paritytech/parity/pull/431)
|
||||
- Delay Homestead transition from 1,000,000. [#429](https://github.com/paritytech/parity/pull/429)
|
||||
- Nightly fix effort (still should fail) [#428](https://github.com/paritytech/parity/pull/428)
|
||||
- clippy version update, docopt-macro moving to fork [#425](https://github.com/paritytech/parity/pull/425)
|
||||
- Network/Sync fixes and optimizations [#416](https://github.com/paritytech/parity/pull/416)
|
||||
- Use latest era instead of end era as journal marker [#414](https://github.com/paritytech/parity/pull/414)
|
||||
- api changes [#402](https://github.com/paritytech/parity/pull/402)
|
||||
- Option for no init nodes. [#408](https://github.com/paritytech/parity/pull/408)
|
||||
- Fixed block_bodies not returning a list [#406](https://github.com/paritytech/parity/pull/406)
|
||||
- Fix test. [#405](https://github.com/paritytech/parity/pull/405)
|
||||
- Allow path to be configured. [#404](https://github.com/paritytech/parity/pull/404)
|
||||
- Upnp [#400](https://github.com/paritytech/parity/pull/400)
|
||||
- eth_syncing, fixed #397 [#398](https://github.com/paritytech/parity/pull/398)
|
||||
- Using modified version of ctrlc that catches SIGTERM [#399](https://github.com/paritytech/parity/pull/399)
|
||||
- Catching panics. [#396](https://github.com/paritytech/parity/pull/396)
|
||||
- jsonrpc [#391](https://github.com/paritytech/parity/pull/391)
|
||||
- Externalities tests (still clumsy) [#394](https://github.com/paritytech/parity/pull/394)
|
||||
- excluding test code itself from coverage [#395](https://github.com/paritytech/parity/pull/395)
|
||||
- Additional tweaks to options. [#390](https://github.com/paritytech/parity/pull/390)
|
||||
- --chain option for setting which network to go on. [#388](https://github.com/paritytech/parity/pull/388)
|
||||
- Ethash unit tests final [#387](https://github.com/paritytech/parity/pull/387)
|
||||
- jsonrpc [#374](https://github.com/paritytech/parity/pull/374)
|
||||
- Editorconfig file. [#384](https://github.com/paritytech/parity/pull/384)
|
||||
- Coverage effort [in progress] [#382](https://github.com/paritytech/parity/pull/382)
|
||||
- making root kcov runner simular to the one running on CI [#380](https://github.com/paritytech/parity/pull/380)
|
||||
- add gcc as a dependency to dockerfiles [#381](https://github.com/paritytech/parity/pull/381)
|
||||
- Check for handshake expiration before attempting connection replace [#375](https://github.com/paritytech/parity/pull/375)
|
||||
- Blocks propagation [#364](https://github.com/paritytech/parity/pull/364)
|
||||
- Network params. [#376](https://github.com/paritytech/parity/pull/376)
|
||||
- Add parity-node-zero to bootnodes. [#373](https://github.com/paritytech/parity/pull/373)
|
||||
- kcov uses travis_job_id instead of coveralls token [#370](https://github.com/paritytech/parity/pull/370)
|
||||
- Add parity-node-zero.ethcore.io to boot nodes. [#371](https://github.com/paritytech/parity/pull/371)
|
||||
|
||||
## Parity [v1.0.0-rc1](https://github.com/paritytech/parity/releases/tag/v1.0.0-rc1) (2016-03-15)
|
||||
|
||||
First Parity 1.0.0 release candidate.
|
||||
|
||||
- Version 1.0 in beta [#712](https://github.com/paritytech/parity/pull/712)
|
||||
- Fix test for beta [#617](https://github.com/paritytech/parity/pull/617)
|
||||
- JournalDB fix option 1 for beta [#614](https://github.com/paritytech/parity/pull/614)
|
||||
- Failing test. [#606](https://github.com/paritytech/parity/pull/606)
|
||||
- Fix transition points [#604](https://github.com/paritytech/parity/pull/604)
|
||||
- (BETA) Update README.md [#549](https://github.com/paritytech/parity/pull/549)
|
||||
- (BETA) instructions for beta release channel [#456](https://github.com/paritytech/parity/pull/456)
|
||||
- (BETA) fix nightly - remerge [#454](https://github.com/paritytech/parity/pull/454)
|
||||
- (BETA) fixing nightly version for beta [#452](https://github.com/paritytech/parity/pull/452)
|
||||
@@ -1,154 +0,0 @@
|
||||
Note: Parity 1.1 reached End-of-Life on 2016-08-12 (EOL).
|
||||
|
||||
## Parity [v1.1.0](https://github.com/paritytech/parity/releases/tag/v1.1.0) (2016-05-02)
|
||||
|
||||
Parity 1.1.0 introduces:
|
||||
|
||||
- Transaction tracing. Parity now optionally indexes & stores message-call/"internal transaction" information and provides additional RPC for querying.
|
||||
- Web interface for logs, status & JSON RPC.
|
||||
- Improved JSON RPC compatibility.
|
||||
- Reduced memory footprint.
|
||||
- Optimized EVM interpreter performance.
|
||||
|
||||
Full Changes:
|
||||
|
||||
- Exposing default extra data via ethcore RPC [#1032](https://github.com/paritytech/parity/pull/1032)
|
||||
- Net etiquette [#1028](https://github.com/paritytech/parity/pull/1028)
|
||||
- Bumping clippy & fixing warnings [#1024](https://github.com/paritytech/parity/pull/1024)
|
||||
- Tracedb interface && cli [#997](https://github.com/paritytech/parity/pull/997)
|
||||
- Switching to geth-attach supporting version of rpc core and server [#1022](https://github.com/paritytech/parity/pull/1022)
|
||||
- Fixing status page displaying homestead [#1020](https://github.com/paritytech/parity/pull/1020)
|
||||
- Core tracedb functionality. [#996](https://github.com/paritytech/parity/pull/996)
|
||||
- RPC method for supported modules [#1019](https://github.com/paritytech/parity/pull/1019)
|
||||
- Updating status page [#1015](https://github.com/paritytech/parity/pull/1015)
|
||||
- Disabling wallet [#1017](https://github.com/paritytech/parity/pull/1017)
|
||||
- More detailed fatal error reporting [#1016](https://github.com/paritytech/parity/pull/1016)
|
||||
- Support 'pending' block in RPC [#1007](https://github.com/paritytech/parity/pull/1007)
|
||||
- Enable pending block when there is local transaction pending. [#1005](https://github.com/paritytech/parity/pull/1005)
|
||||
- updating key files permissions on save [#1010](https://github.com/paritytech/parity/pull/1010)
|
||||
- IPC JSON RPC (for external interface) [#1009](https://github.com/paritytech/parity/pull/1009)
|
||||
- Fixing Firefox authorization issues [#1013](https://github.com/paritytech/parity/pull/1013)
|
||||
- cargo update [#1012](https://github.com/paritytech/parity/pull/1012)
|
||||
- Switching to rust-url@1.0.0 [#1011](https://github.com/paritytech/parity/pull/1011)
|
||||
- Exception handling in RPC & WebApps [#988](https://github.com/paritytech/parity/pull/988)
|
||||
- Fixed uint deserialization from hex [#1008](https://github.com/paritytech/parity/pull/1008)
|
||||
- Tweak timeout and packet size to handle slow networks better [#1004](https://github.com/paritytech/parity/pull/1004)
|
||||
- db key is generic and can be made smaller [#1006](https://github.com/paritytech/parity/pull/1006)
|
||||
- IPC with new serialization [#998](https://github.com/paritytech/parity/pull/998)
|
||||
- make jsonrpc api engine agnostic [#1001](https://github.com/paritytech/parity/pull/1001)
|
||||
- updated cargo.lock [#1002](https://github.com/paritytech/parity/pull/1002)
|
||||
- updated parity dependencies [#993](https://github.com/paritytech/parity/pull/993)
|
||||
- Auto (with codegen) binary serializer [#980](https://github.com/paritytech/parity/pull/980)
|
||||
- Fixing transaction queue last_nonces update [#995](https://github.com/paritytech/parity/pull/995)
|
||||
- import route contains ommited blocks [#994](https://github.com/paritytech/parity/pull/994)
|
||||
- fixed encoding 0u8 [#992](https://github.com/paritytech/parity/pull/992)
|
||||
- Use latest netstats [#989](https://github.com/paritytech/parity/pull/989)
|
||||
- RPC shared external miner [#984](https://github.com/paritytech/parity/pull/984)
|
||||
- Additional RPC methods for settings [#983](https://github.com/paritytech/parity/pull/983)
|
||||
- Fixing transaction_queue deadlock [#985](https://github.com/paritytech/parity/pull/985)
|
||||
- Refactoring of `parity/main.rs` [#981](https://github.com/paritytech/parity/pull/981)
|
||||
- Fixing clippy warnings. [#982](https://github.com/paritytech/parity/pull/982)
|
||||
- Bumping status page [#977](https://github.com/paritytech/parity/pull/977)
|
||||
- querying extras separated to its own module [#972](https://github.com/paritytech/parity/pull/972)
|
||||
- Exposing application logs via RPC. [#976](https://github.com/paritytech/parity/pull/976)
|
||||
- Addressing binary serialization for db types [#966](https://github.com/paritytech/parity/pull/966)
|
||||
- removed redundant unwraps [#935](https://github.com/paritytech/parity/pull/935)
|
||||
- fixed transaction queue merge conflict [#975](https://github.com/paritytech/parity/pull/975)
|
||||
- Configurable limit for transaction queue (CLI & Ethcore-RPC) [#974](https://github.com/paritytech/parity/pull/974)
|
||||
- Enforce limit caused `last_nonce` to return incorrect values. [#973](https://github.com/paritytech/parity/pull/973)
|
||||
- Even more detailed errors for transaction queue [#969](https://github.com/paritytech/parity/pull/969)
|
||||
- temporary fix of panic in blockchain garbage collection [#970](https://github.com/paritytech/parity/pull/970)
|
||||
- IPC codegen - some minor fixes & enhancements [#967](https://github.com/paritytech/parity/pull/967)
|
||||
- Additional logging for transactions [#968](https://github.com/paritytech/parity/pull/968)
|
||||
- refactored blockchain extras keys building [#963](https://github.com/paritytech/parity/pull/963)
|
||||
- Using hyper-mio branch in webapps. [#957](https://github.com/paritytech/parity/pull/957)
|
||||
- Remove nanomsg from build-dependencies [#965](https://github.com/paritytech/parity/pull/965)
|
||||
- Fix build for --target=armv7-unknown-linux-gnueabihf [#964](https://github.com/paritytech/parity/pull/964)
|
||||
- IPC RPC codegen extra feature [#962](https://github.com/paritytech/parity/pull/962)
|
||||
- IPC RPC codegen for generic implementation [#961](https://github.com/paritytech/parity/pull/961)
|
||||
- using db_path directory when upgrading [#960](https://github.com/paritytech/parity/pull/960)
|
||||
- IPC hypervisor [#958](https://github.com/paritytech/parity/pull/958)
|
||||
- Removing a transaction from queue now removes all from this sender with lower nonces. [#950](https://github.com/paritytech/parity/pull/950)
|
||||
- bump status page version 0.1.7 [#955](https://github.com/paritytech/parity/pull/955)
|
||||
- Changing cors header to be optional [#956](https://github.com/paritytech/parity/pull/956)
|
||||
- Update ARM Dockerfile [#959](https://github.com/paritytech/parity/pull/959)
|
||||
- Sensible gas limits for eth_sendTransaction [#953](https://github.com/paritytech/parity/pull/953)
|
||||
- Fix upgrade script and make parity run when no .parity dir. [#954](https://github.com/paritytech/parity/pull/954)
|
||||
- Tracing and docs for --pruning=auto. [#952](https://github.com/paritytech/parity/pull/952)
|
||||
- IPC serialization for custom parameters [#946](https://github.com/paritytech/parity/pull/946)
|
||||
- default filter from block should be Latest, not Earliest [#948](https://github.com/paritytech/parity/pull/948)
|
||||
- README.md: removes sudo from multirust installation [#943](https://github.com/paritytech/parity/pull/943)
|
||||
- Disable long lines formatting + ethash example. [#939](https://github.com/paritytech/parity/pull/939)
|
||||
- Ethcore-specific RPC methods for altering miner parameters. [#934](https://github.com/paritytech/parity/pull/934)
|
||||
- Use ethcore nanomsg bindings [#941](https://github.com/paritytech/parity/pull/941)
|
||||
- Update IPC codegen to latest syntax libs [#938](https://github.com/paritytech/parity/pull/938)
|
||||
- IPC documentation [#937](https://github.com/paritytech/parity/pull/937)
|
||||
- Bumping clippy and fixing warnings. [#936](https://github.com/paritytech/parity/pull/936)
|
||||
- Pruning auto [#927](https://github.com/paritytech/parity/pull/927)
|
||||
- IPC persistent client link [#933](https://github.com/paritytech/parity/pull/933)
|
||||
- IPC persistent client link [#930](https://github.com/paritytech/parity/pull/930)
|
||||
- IPC handshake (negotiating protocol/api version) [#928](https://github.com/paritytech/parity/pull/928)
|
||||
- Upgrade logic between versions [#914](https://github.com/paritytech/parity/pull/914)
|
||||
- executive tracing cleanup [#903](https://github.com/paritytech/parity/pull/903)
|
||||
- Ethcore-specific RPC methods [#923](https://github.com/paritytech/parity/pull/923)
|
||||
- Parameter to allow user to force the sealing mechanism [#918](https://github.com/paritytech/parity/pull/918)
|
||||
- updated dependencies [#921](https://github.com/paritytech/parity/pull/921)
|
||||
- Fixed send transaction deadlock [#920](https://github.com/paritytech/parity/pull/920)
|
||||
- --unlock is comma-delimited. [#916](https://github.com/paritytech/parity/pull/916)
|
||||
- fixed eth_getLogs [#915](https://github.com/paritytech/parity/pull/915)
|
||||
- create provided custom dir for keys if none [#912](https://github.com/paritytech/parity/pull/912)
|
||||
- spec loading cleanup [#858](https://github.com/paritytech/parity/pull/858)
|
||||
- WebApps HTTP Basic Auth Support [#906](https://github.com/paritytech/parity/pull/906)
|
||||
- Removing match on constant [#888](https://github.com/paritytech/parity/pull/888)
|
||||
- Update auth.rs [#907](https://github.com/paritytech/parity/pull/907)
|
||||
- Enabling webapps compilation by default [#904](https://github.com/paritytech/parity/pull/904)
|
||||
- fixed #895 [#898](https://github.com/paritytech/parity/pull/898)
|
||||
- Support for compile-time included WebApplications. [#899](https://github.com/paritytech/parity/pull/899)
|
||||
- Propagate transaction queue [#894](https://github.com/paritytech/parity/pull/894)
|
||||
- Use new json RPC server [#901](https://github.com/paritytech/parity/pull/901)
|
||||
- Gracefully dying when trying to enable RPC and app is compiled without it. [#900](https://github.com/paritytech/parity/pull/900)
|
||||
- Additional logging and friendlier error messages [#893](https://github.com/paritytech/parity/pull/893)
|
||||
- Avoid signalling readiness when app is about to be closed. [#897](https://github.com/paritytech/parity/pull/897)
|
||||
- fixed #875 and added tests for eth_sendTransaction [#890](https://github.com/paritytech/parity/pull/890)
|
||||
- passing key path to all invocations [#891](https://github.com/paritytech/parity/pull/891)
|
||||
- Fixed eth_call nonce and gas handling [#892](https://github.com/paritytech/parity/pull/892)
|
||||
- ipc rpc with nano transport (simple duplex) [#886](https://github.com/paritytech/parity/pull/886)
|
||||
- Bumping clippy and fixing warnings [#889](https://github.com/paritytech/parity/pull/889)
|
||||
- More descriptive expectations to transaction queue consistency. [#878](https://github.com/paritytech/parity/pull/878)
|
||||
- uint bug - replace add with or [#879](https://github.com/paritytech/parity/pull/879)
|
||||
- Fixing typo in bigint [#877](https://github.com/paritytech/parity/pull/877)
|
||||
- update misleading cli help msg for author [#874](https://github.com/paritytech/parity/pull/874)
|
||||
- Find geth data store cross-platform. [#871](https://github.com/paritytech/parity/pull/871)
|
||||
- Import geth 1.4.0 keys [#872](https://github.com/paritytech/parity/pull/872)
|
||||
- Syntax helpers for IPC RPC (part 2) [#854](https://github.com/paritytech/parity/pull/854)
|
||||
- Fixed bootnode URL and error message [#870](https://github.com/paritytech/parity/pull/870)
|
||||
- replace popcnt with mov (861) [#867](https://github.com/paritytech/parity/pull/867)
|
||||
- weekly dependencies update [#865](https://github.com/paritytech/parity/pull/865)
|
||||
- Remove unused mut [#866](https://github.com/paritytech/parity/pull/866)
|
||||
- fixed #855 [#864](https://github.com/paritytech/parity/pull/864)
|
||||
- simplified trace from functions, removed clippy warnings [#862](https://github.com/paritytech/parity/pull/862)
|
||||
- Update deprecated HashDB methods in docs. [#857](https://github.com/paritytech/parity/pull/857)
|
||||
- refactored loading transaction json tests [#853](https://github.com/paritytech/parity/pull/853)
|
||||
- reorganised price info lookup [#852](https://github.com/paritytech/parity/pull/852)
|
||||
- Publish locally-made transactions to peers. [#850](https://github.com/paritytech/parity/pull/850)
|
||||
- Add generalbeck's token [#847](https://github.com/paritytech/parity/pull/847)
|
||||
- Fix response for mining. [#846](https://github.com/paritytech/parity/pull/846)
|
||||
- USD-based pricing of gas. [#843](https://github.com/paritytech/parity/pull/843)
|
||||
- Parity can accept older work packages [#811](https://github.com/paritytech/parity/pull/811)
|
||||
- Caching for computing seed hashes (#541) [#841](https://github.com/paritytech/parity/pull/841)
|
||||
- checking transaction queue for pending transaction [#838](https://github.com/paritytech/parity/pull/838)
|
||||
- refactored loading of state tests [#817](https://github.com/paritytech/parity/pull/817)
|
||||
- tests for deserialization of transaction from issue #835 [#837](https://github.com/paritytech/parity/pull/837)
|
||||
- unlocks with no expiration [on top of 833] [#834](https://github.com/paritytech/parity/pull/834)
|
||||
- Unlock accounts on CLI. [#833](https://github.com/paritytech/parity/pull/833)
|
||||
- Make BlockNumber optional, fix eth_call [#829](https://github.com/paritytech/parity/pull/829)
|
||||
- Test socket to common test code (ethcore-devtools) [#831](https://github.com/paritytech/parity/pull/831)
|
||||
- Use network id for the web3_net_version return. [#822](https://github.com/paritytech/parity/pull/822)
|
||||
- json-rpc web3_sha3 [#824](https://github.com/paritytech/parity/pull/824)
|
||||
- remove some unused files [#819](https://github.com/paritytech/parity/pull/819)
|
||||
- debug symbols for master/beta [#818](https://github.com/paritytech/parity/pull/818)
|
||||
- Syntax helpers for IPC RPC [#809](https://github.com/paritytech/parity/pull/809)
|
||||
- refactored loading of execution tests [#803](https://github.com/paritytech/parity/pull/803)
|
||||
- Rustfmt.toml [#805](https://github.com/paritytech/parity/pull/805)
|
||||
- install-partiy runs brew reinstall parity on osx [#810](https://github.com/paritytech/parity/pull/810)
|
||||
- Fix mining from spinning [#807](https://github.com/paritytech/parity/pull/807)
|
||||
@@ -1,469 +0,0 @@
|
||||
Note: Parity 1.10 reached End-of-Life on 2018-07-18 (EOL).
|
||||
|
||||
## Parity [v1.10.9](https://github.com/paritytech/parity/releases/tag/v1.10.9) (2018-07-07)
|
||||
|
||||
Parity 1.10.9 is a bug-fix release to improve performance and stability.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Stable: 1.10.9 backports ([#9016](https://github.com/paritytech/parity/pull/9016))
|
||||
- Parity-version: bump stable to 1.10.9
|
||||
- Scripts: remove md5 checksums ([#8884](https://github.com/paritytech/parity/pull/8884))
|
||||
- Add support for --chain tobalaba ([#8870](https://github.com/paritytech/parity/pull/8870))
|
||||
- Add support for --chain tobalaba
|
||||
- Only return error log for rustls ([#9025](https://github.com/paritytech/parity/pull/9025))
|
||||
- Fixes for misbehavior reporting in AuthorityRound ([#8998](https://github.com/paritytech/parity/pull/8998))
|
||||
- Aura: only report after checking for repeated skipped primaries
|
||||
- Aura: refactor duplicate code for getting epoch validator set
|
||||
- Aura: verify_external: report on validator set contract instance
|
||||
- Aura: use correct validator set epoch number when reporting
|
||||
- Aura: use epoch set when verifying blocks
|
||||
- Aura: report skipped primaries when generating seal
|
||||
- Aura: handle immediate transitions
|
||||
- Aura: don't report skipped steps from genesis to first block
|
||||
- Aura: fix reporting test
|
||||
- Aura: refactor duplicate code to handle immediate_transitions
|
||||
- Aura: let reporting fail on verify_block_basic
|
||||
- Aura: add comment about possible failure of reporting
|
||||
|
||||
## Parity [v1.10.8](https://github.com/paritytech/parity/releases/tag/v1.10.8) (2018-06-29)
|
||||
|
||||
Parity 1.10.8 is a bug-fix release to improve performance and stability.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Backports ([#8986](https://github.com/paritytech/parity/pull/8986))
|
||||
- Snap: downgrade rust to revision 1.26.2, ref snapcraft/+bug/1778530 ([#8984](https://github.com/paritytech/parity/pull/8984))
|
||||
- Snap: downgrade rust to revision 1.26.2, ref snapcraft/+bug/1778530
|
||||
- Snap: use plugin rust
|
||||
- Fix deadlock in blockchain. ([#8977](https://github.com/paritytech/parity/pull/8977))
|
||||
- Remove js-glue from workspace
|
||||
- Bump stable to 1.10.8 ([#8951](https://github.com/paritytech/parity/pull/8951))
|
||||
- Parity-version: bump stable to 1.10.8
|
||||
- Update ropsten.json ([#8926](https://github.com/paritytech/parity/pull/8926))
|
||||
- Scripts: minor improvements ([#8930](https://github.com/paritytech/parity/pull/8930))
|
||||
- CI: enable 'latest' docker tag on master pipeline
|
||||
- CI: mark both beta and stable as stable snap.
|
||||
- CI: sign all windows binaries
|
||||
- Scripts: remove whisper target not available in stable
|
||||
- Scripts: fix gitlab strip binaries
|
||||
- Scripts: fix docker build tag on latest using master ([#8952](https://github.com/paritytech/parity/pull/8952))
|
||||
- Rpc: cap gas limit of local calls ([#8943](https://github.com/paritytech/parity/pull/8943))
|
||||
|
||||
## Parity [v1.10.7](https://github.com/paritytech/parity/releases/tag/v1.10.7) (2018-06-20)
|
||||
|
||||
Parity 1.10.7 is a bug-fix release to improve performance and stability.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Backports ([#8919](https://github.com/paritytech/parity/pull/8919))
|
||||
- Fixed AuthorityRound deadlock on shutdown, closes [#8088](https://github.com/paritytech/parity/issues/8088) ([#8803](https://github.com/paritytech/parity/pull/8803))
|
||||
- CI: Fix docker tags ([#8822](https://github.com/paritytech/parity/pull/8822))
|
||||
- Scripts: enable docker builds for beta and stable
|
||||
- Scripts: docker latest should be beta not master
|
||||
- Scripts: docker latest is master
|
||||
- Fix concurrent access to signer queue ([#8854](https://github.com/paritytech/parity/pull/8854))
|
||||
- Fix concurrent access to signer queue
|
||||
- Put request back to the queue if confirmation failed
|
||||
- Typo: fix docs and rename functions to be more specific
|
||||
- Change trace info "Transaction" -> "Request"
|
||||
- Add new ovh bootnodes and fix port for foundation bootnode 3.2 ([#8886](https://github.com/paritytech/parity/pull/8886))
|
||||
- Add new ovh bootnodes and fix port for foundation bootnode 3.2
|
||||
- Remove old bootnodes.
|
||||
- Remove duplicate 1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082
|
||||
- Block 0 is valid in queries ([#8891](https://github.com/paritytech/parity/pull/8891))
|
||||
- Update jsonrpc libs, fixed ipc leak, closes [#8774](https://github.com/paritytech/parity/issues/8774) ([#8876](https://github.com/paritytech/parity/pull/8876))
|
||||
- Add ETC Cooperative-run load balanced parity node ([#8892](https://github.com/paritytech/parity/pull/8892))
|
||||
- Minor fix in chain supplier and light provider ([#8906](https://github.com/paritytech/parity/pull/8906))
|
||||
- Fix chain supplier increment
|
||||
- Fix light provider block_headers
|
||||
- Parity-version: stable release 1.10.7 ([#8855](https://github.com/paritytech/parity/pull/8855))
|
||||
- Cherry-pick network-specific release flag ([#8821](https://github.com/paritytech/parity/pull/8821))
|
||||
- Parity-version: bump stable to 1.10.7
|
||||
|
||||
## Parity [v1.10.6](https://github.com/paritytech/parity/releases/tag/v1.10.6) (2018-06-05)
|
||||
|
||||
Parity 1.10.6 is a security-relevant release. Please upgrade your nodes as soon as possible.
|
||||
|
||||
If you can not upgrade to 1.10+ yet, please use the following branches and build your own binaries from source:
|
||||
|
||||
- git checkout [old-stable-1.9](https://github.com/paritytech/parity/tree/old-stable-1.9) # `v1.9.8` (EOL)
|
||||
- git checkout [old-stable-1.8](https://github.com/paritytech/parity/tree/old-stable-1.8) # `v1.8.12` (EOL)
|
||||
- git checkout [old-stable-1.7](https://github.com/paritytech/parity/tree/old-stable-1.7) # `v1.7.14` (EOL)
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Parity-version: bump stable to 1.10.6 ([#8805](https://github.com/paritytech/parity/pull/8805))
|
||||
- Parity-version: bump stable to 1.10.6
|
||||
- Disallow unsigned transactions in case EIP-86 is disabled ([#8802](https://github.com/paritytech/parity/pull/8802))
|
||||
- Update shell32-sys to fix windows build ([#8793](https://github.com/paritytech/parity/pull/8793))
|
||||
- Backports ([#8782](https://github.com/paritytech/parity/pull/8782))
|
||||
- Fix light sync with initial validator-set contract ([#8528](https://github.com/paritytech/parity/pull/8528))
|
||||
- Fix #8468
|
||||
- Use U256::max_value() instead
|
||||
- Fix again
|
||||
- Also change initial transaction gas
|
||||
- Don't open Browser post-install on Mac ([#8641](https://github.com/paritytech/parity/pull/8641))
|
||||
- Prefix uint fmt with `0x` with alternate flag
|
||||
- Set the request index to that of the current request ([#8683](https://github.com/paritytech/parity/pull/8683))
|
||||
- Set the request index to that of the current request
|
||||
- Node table sorting according to last contact data ([#8541](https://github.com/paritytech/parity/pull/8541))
|
||||
- Network-devp2p: sort nodes in node table using last contact data
|
||||
- Network-devp2p: rename node contact types in node table json output
|
||||
- Network-devp2p: fix node table tests
|
||||
- Network-devp2p: note node failure when failed to establish connection
|
||||
- Network-devp2p: handle UselessPeer error
|
||||
- Network-devp2p: note failure when marking node as useless
|
||||
- Network-devp2p: handle UselessPeer disconnect ([#8686](https://github.com/paritytech/parity/pull/8686))
|
||||
- Parity: bump stable version to 1.10.5 ([#8749](https://github.com/paritytech/parity/pull/8749))
|
||||
- Parity: bump stable version to 1.10.5
|
||||
- Fix failing doc tests running on non-code
|
||||
|
||||
## Parity [v1.10.4](https://github.com/paritytech/parity/releases/tag/v1.10.4) (2018-05-15)
|
||||
|
||||
Parity 1.10.4 is a bug-fix release to improve performance and stability.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Backports ([#8623](https://github.com/paritytech/parity/pull/8623))
|
||||
- Fix account list double 0x display ([#8596](https://github.com/paritytech/parity/pull/8596))
|
||||
- Remove unused self import
|
||||
- Fix account list double 0x display
|
||||
- Trace precompiled contracts when the transfer value is not zero ([#8486](https://github.com/paritytech/parity/pull/8486))
|
||||
- Trace precompiled contracts when the transfer value is not zero
|
||||
- Add tests for precompiled CALL tracing
|
||||
- Use byzantium test machine for the new test
|
||||
- Add notes in comments on why we don't trace all precompileds
|
||||
- Use is_transferred instead of transferred
|
||||
- Gitlab test script fixes ([#8573](https://github.com/paritytech/parity/pull/8573))
|
||||
- Exclude /docs from modified files.
|
||||
- Ensure all references in the working tree are available
|
||||
- Remove duplicated line from test script
|
||||
- Bump stable to 1.10.4 ([#8626](https://github.com/paritytech/parity/pull/8626))
|
||||
- Allow stable snaps to be stable. ([#8582](https://github.com/paritytech/parity/pull/8582))
|
||||
|
||||
## Parity [v1.10.3](https://github.com/paritytech/parity/releases/tag/v1.10.3) (2018-05-08)
|
||||
|
||||
Parity 1.10.3 marks the first stable release on the 1.10 track. Among others, it improves performance and stability.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Backports ([#8557](https://github.com/paritytech/parity/pull/8557))
|
||||
- Update wasmi and pwasm-utils ([#8493](https://github.com/paritytech/parity/pull/8493))
|
||||
- Update wasmi to 0.2
|
||||
- Update pwasm-utils to 0.1.5
|
||||
- Fetching logs by hash in blockchain database ([#8463](https://github.com/paritytech/parity/pull/8463))
|
||||
- Fetch logs by hash in blockchain database
|
||||
- Fix tests
|
||||
- Add unit test for branch block logs fetching
|
||||
- Add docs that blocks must already be sorted
|
||||
- Handle branch block cases properly
|
||||
- typo: empty -> is_empty
|
||||
- Remove return_empty_if_none by using a closure
|
||||
- Use BTreeSet to avoid sorting again
|
||||
- Move is_canon to BlockChain
|
||||
- typo: pass value by reference
|
||||
- Use loop and wrap inside blocks to simplify the code
|
||||
- typo: missed a comment
|
||||
- Pass on storage keys tracing to handle the case when it is not modified ([#8491](https://github.com/paritytech/parity/pull/8491))
|
||||
- Pass on storage keys even if it is not modified
|
||||
- typo: account and storage query `to_pod_diff` builds both `touched_addresses` merge and storage keys merge.
|
||||
- Fix tests
|
||||
- Use state query directly because of suicided accounts
|
||||
- Fix a RefCell borrow issue
|
||||
- Add tests for unmodified storage trace
|
||||
- Address grumbles
|
||||
- typo: remove unwanted empty line
|
||||
- ensure_cached compiles with the original signature
|
||||
- Enable WebAssembly and Byzantium for Ellaism ([#8520](https://github.com/paritytech/parity/pull/8520))
|
||||
- Enable WebAssembly and Byzantium for Ellaism
|
||||
- Fix indentation
|
||||
- Remove empty lines
|
||||
- Fix compilation.
|
||||
- Stabilize 1.10.3 ([#8474](https://github.com/paritytech/parity/pull/8474))
|
||||
- Stabelize 1.10
|
||||
- Bump stable to 1.10.3
|
||||
- Update Gitlab scripts
|
||||
- Fix snap builds ([#8483](https://github.com/paritytech/parity/pull/8483))
|
||||
- Fix docker build ([#8462](https://github.com/paritytech/parity/pull/8462))
|
||||
- Use `master` as Docker's `latest` (`beta-release` is not used anymore)
|
||||
|
||||
## Parity [v1.10.2](https://github.com/paritytech/parity/releases/tag/v1.10.2) (2018-04-24)
|
||||
|
||||
Parity 1.10.2 is a bug-fix release to improve performance and stability.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Update Parity beta to 1.10.2 + Backports ([#8455](https://github.com/paritytech/parity/pull/8455))
|
||||
- Update Parity beta to 1.10.2
|
||||
- Allow 32-bit pipelines to fail ([#8454](https://github.com/paritytech/parity/pull/8454))
|
||||
- Disable 32-bit targets for Gitlab
|
||||
- Rename Linux pipelines
|
||||
- Update wasmi ([#8452](https://github.com/paritytech/parity/pull/8452))
|
||||
- Fix Cargo.lock
|
||||
- Backports ([#8450](https://github.com/paritytech/parity/pull/8450))
|
||||
- Use forked app_dirs crate for reverted Windows dir behavior ([#8438](https://github.com/paritytech/parity/pull/8438))
|
||||
- Remove unused app_dirs dependency in CLI
|
||||
- Use forked app_dirs crate for reverted Windows dir behavior
|
||||
- Remove Tendermint extra_info due to seal inconsistencies ([#8367](https://github.com/paritytech/parity/pull/8367))
|
||||
- Handle queue import errors a bit more gracefully ([#8385](https://github.com/paritytech/parity/pull/8385))
|
||||
- Improve VM executor stack size estimation rules ([#8439](https://github.com/paritytech/parity/pull/8439))
|
||||
- Improve VM executor stack size estimation rules
|
||||
- Typo: docs add "(Debug build)" comment
|
||||
- Fix an off by one typo and set minimal stack size
|
||||
- Use saturating_sub to avoid potential overflow
|
||||
|
||||
## Parity [v1.10.1](https://github.com/paritytech/parity/releases/tag/v1.10.1) (2018-04-17)
|
||||
|
||||
Parity 1.10.1 is a bug-fix release to improve performance and stability. Among other changes, you can now use `--warp-barrier [BLOCK]` to specify a minimum block number to `--warp` to. This is useful in cases where clients restore to outdated snapshots far behind the latest chain head.
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Bump beta to 1.10.1 ([#8350](https://github.com/paritytech/parity/pull/8350))
|
||||
- Bump beta to 1.10.1
|
||||
- Unflag critical release
|
||||
- Backports ([#8346](https://github.com/paritytech/parity/pull/8346))
|
||||
- Warp-only sync with warp-barrier [blocknumber] flag. ([#8228](https://github.com/paritytech/parity/pull/8228))
|
||||
- Warp-only sync with warp-after [blocknumber] flag.
|
||||
- Fix tests.
|
||||
- Fix configuration tests.
|
||||
- Rename to warp barrier.
|
||||
- Allow unsafe js eval on Parity Wallet. ([#8204](https://github.com/paritytech/parity/pull/8204))
|
||||
- Update musicoin spec in line with gmc v2.6.2 ([#8242](https://github.com/paritytech/parity/pull/8242))
|
||||
- Supress TemporaryInvalid verification failures. ([#8256](https://github.com/paritytech/parity/pull/8256))
|
||||
- Include suicided accounts in state diff ([#8297](https://github.com/paritytech/parity/pull/8297))
|
||||
- Include suicided accounts in state diff
|
||||
- Shorten form match -> if let
|
||||
- Test suicide trace diff in State
|
||||
- Replace_home for password_files, reserved_peers and log_file ([#8324](https://github.com/paritytech/parity/pull/8324))
|
||||
- Replace_home for password_files, reserved_peers and log_file
|
||||
- Typo: arg_log_file is Option
|
||||
- Enable UI by default, but only display info page.
|
||||
- Fix test.
|
||||
- Fix naming and remove old todo.
|
||||
- Change "wallet" with "browser UI"
|
||||
- Change name Wallet -> UI ([#8164](https://github.com/paritytech/parity/pull/8164)) ([#8205](https://github.com/paritytech/parity/pull/8205))
|
||||
- Change name Wallet -> UI
|
||||
- Make warning bold
|
||||
- Backport [#8099](https://github.com/paritytech/parity/pull/8099) ([#8132](https://github.com/paritytech/parity/pull/8132))
|
||||
- WASM libs ([#8220](https://github.com/paritytech/parity/pull/8220))
|
||||
- Bump wasm libs ([#8171](https://github.com/paritytech/parity/pull/8171))
|
||||
- Bump wasmi version ([#8209](https://github.com/paritytech/parity/pull/8209))
|
||||
- Update hyper to 0.11.24 ([#8203](https://github.com/paritytech/parity/pull/8203))
|
||||
- Updated jsonrpc to include latest backports (beta) ([#8181](https://github.com/paritytech/parity/pull/8181))
|
||||
- Updated jsonrpc to include latest backports
|
||||
- Update dependencies.
|
||||
|
||||
## Parity [v1.10.0](https://github.com/paritytech/parity/releases/tag/v1.10.0) (2018-03-22)
|
||||
|
||||
This is the Parity 1.10.0-beta release! Cool!
|
||||
|
||||
### Disabling the Parity Wallet
|
||||
|
||||
The **Parity Wallet (a.k.a. "UI") is now disabled by default**. We are preparing to split the wallet from the core client.
|
||||
|
||||
To reactivate the parity wallet, you have to run Parity either with `parity --force-ui` (not recommended) or `parity ui` (deprecated) from the command line. Or, if you feel super fancy and want to test our pre-releases of the stand-alone electron wallet, head over to the [Parity-JS repositories and check the releases](https://github.com/Parity-JS/shell/releases).
|
||||
|
||||
Further reading:
|
||||
|
||||
- [Docs: Parity Wallet](https://wiki.parity.io/Parity-Wallet)
|
||||
- [Docs: How to customize Parity UI?](https://wiki.parity.io/FAQ-Customize-Parity-UI.html)
|
||||
- [Github: Parity-JS](https://github.com/parity-js)
|
||||
|
||||
### Introducing the Wasm VM
|
||||
|
||||
We are excited to announce support for **Wasm Smart Contracts on Kovan network**. The hard-fork to activate the Wasm-VM will take place on block `6_600_000`.
|
||||
|
||||
To enable Wasm contracts on your custom network, just schedule a `wasmActivationTransition` at your favorite block number (e.g., `42`, `666`, or `0xbada55`). To hack your first Wasm smart contracts in Rust, have a look at the [Parity Wasm Tutorials](https://github.com/paritytech/pwasm-tutorial).
|
||||
|
||||
Further reading:
|
||||
|
||||
- [Docs: WebAssembly (wasm)](https://wiki.parity.io/WebAssembly-Home)
|
||||
- [Docs: Wasm VM Design](https://wiki.parity.io/WebAssembly-Design)
|
||||
- [Docs: Wasm tutorials and examples](https://wiki.parity.io/WebAssembly-Links)
|
||||
|
||||
### Empty step messages in PoA
|
||||
|
||||
To **reduce blockchain bloat, proof-of-authority networks can now enable _empty step messages_ which replace empty blocks**. Each step message will be signed and broadcasted by the issuing authorities, and included and rewarded in the next non-empty block.
|
||||
|
||||
To enable empty step messages, set the `emptyStepsTransition` to your favorite block number. You can also specify a maximum number of empty steps with `maximumEmptySteps` in your chain spec.
|
||||
|
||||
### Other noteworthy changes
|
||||
|
||||
We removed the old database migrations from 2016. In case you upgrade Parity from a really, really old version, you will have to reset your database manually first with `parity <options> db kill`.
|
||||
|
||||
We fixed DELEGATECALL `from` and `to` fields, see [#7166](https://github.com/paritytech/parity/issues/7166).
|
||||
|
||||
We reduced the default USD per transaction value to 0.0001. Thanks, @MysticRyuujin!
|
||||
|
||||
The Musicoin chain is now enabled with Byzantium features starting at block `2_222_222`.
|
||||
|
||||
### Overview of all changes included
|
||||
|
||||
The full list of included changes:
|
||||
|
||||
- Re-enable signer, even with no UI. ([#8167](https://github.com/paritytech/parity/pull/8167)) ([#8168](https://github.com/paritytech/parity/pull/8168))
|
||||
- Re-enable signer, even with no UI.
|
||||
- Fix message.
|
||||
- Beta Backports ([#8136](https://github.com/paritytech/parity/pull/8136))
|
||||
- Support parity protocol. ([#8035](https://github.com/paritytech/parity/pull/8035))
|
||||
- updater: apply exponential backoff after download failure ([#8059](https://github.com/paritytech/parity/pull/8059))
|
||||
- updater: apply exponential backoff after download failure
|
||||
- updater: reset backoff on new release
|
||||
- Max code size on Kovan ([#8067](https://github.com/paritytech/parity/pull/8067))
|
||||
- Enable code size limit on kovan
|
||||
- Fix formatting.
|
||||
- Limit incoming connections. ([#8060](https://github.com/paritytech/parity/pull/8060))
|
||||
- Limit ingress connections
|
||||
- Optimized handshakes logging
|
||||
- WASM libraries bump ([#7970](https://github.com/paritytech/parity/pull/7970))
|
||||
- update wasmi, parity-wasm, wasm-utils to latest version
|
||||
- Update to new wasmi & error handling
|
||||
- also utilize new stack limiter
|
||||
- fix typo
|
||||
- replace dependency url
|
||||
- Cargo.lock update
|
||||
- add some dos protection ([#8084](https://github.com/paritytech/parity/pull/8084))
|
||||
- revert removing blooms ([#8066](https://github.com/paritytech/parity/pull/8066))
|
||||
- Revert "fix traces, removed bloomchain crate, closes [#7228](https://github.com/paritytech/parity/issues/7228), closes [#7167](https://github.com/paritytech/parity/issues/7167)"
|
||||
- Revert "fixed broken logs ([#7934](https://github.com/paritytech/parity/pull/7934))"
|
||||
- fixed broken logs
|
||||
- bring back old lock order
|
||||
- remove migration v13
|
||||
- revert CURRENT_VERSION to 12 in migration.rs
|
||||
- more dos protection ([#8104](https://github.com/paritytech/parity/pull/8104))
|
||||
- Const time comparison ([#8113](https://github.com/paritytech/parity/pull/8113))
|
||||
- Use `subtle::slices_equal` for constant time comparison.
|
||||
- Also update the existing version of subtle in `ethcrypto` from 0.1 to 0.5
|
||||
- Test specifically for InvalidPassword error.
|
||||
- fix trace filter returning returning unrelated reward calls, closes #8070 ([#8098](https://github.com/paritytech/parity/pull/8098))
|
||||
- network: init discovery using healthy nodes ([#8061](https://github.com/paritytech/parity/pull/8061))
|
||||
- network: init discovery using healthy nodes
|
||||
- network: fix style grumble
|
||||
- network: fix typo
|
||||
- Postpone Kovan hard fork ([#8137](https://github.com/paritytech/parity/pull/8137))
|
||||
- ethcore: postpone Kovan hard fork
|
||||
- util: update version fork metadata
|
||||
- Disable UI by default. ([#8105](https://github.com/paritytech/parity/pull/8105))
|
||||
- dapps: update parity-ui dependencies ([#8160](https://github.com/paritytech/parity/pull/8160))
|
||||
- Probe changes one step deeper ([#8134](https://github.com/paritytech/parity/pull/8134)) ([#8135](https://github.com/paritytech/parity/pull/8135))
|
||||
- Beta backports ([#8053](https://github.com/paritytech/parity/pull/8053))
|
||||
- CI: Fix cargo cache ([#7968](https://github.com/paritytech/parity/pull/7968))
|
||||
- Fix cache
|
||||
- Only clean locked cargo cache on windows
|
||||
- fixed ethstore sign ([#8026](https://github.com/paritytech/parity/pull/8026))
|
||||
- fixed parsing ethash seals and verify_block_undordered ([#8031](https://github.com/paritytech/parity/pull/8031))
|
||||
- fix for verify_block_basic crashing on invalid transaction rlp ([#8032](https://github.com/paritytech/parity/pull/8032))
|
||||
- fix cache & snapcraft CI build ([#8052](https://github.com/paritytech/parity/pull/8052))
|
||||
- Add MCIP-6 Byzyantium transition to Musicoin spec ([#7841](https://github.com/paritytech/parity/pull/7841))
|
||||
- Add test chain spec for musicoin byzantium testnet
|
||||
- Add MCIP-6 Byzyantium transition to Musicoin spec
|
||||
- Update mcip6_byz.json
|
||||
- ethcore: update musicoin byzantium block number
|
||||
- ethcore: update musicoin bootnodes
|
||||
- Update musicoin.json
|
||||
- More bootnodes.
|
||||
- Make 1.10 beta ([#8022](https://github.com/paritytech/parity/pull/8022))
|
||||
- Make 1.10 beta
|
||||
- Fix gitlab builds
|
||||
- SecretStore: secretstore_generateDocumentKey RPC ([#7864](https://github.com/paritytech/parity/pull/7864))
|
||||
- SecretStore: ECDSA session for cases when 2*t < N ([#7739](https://github.com/paritytech/parity/pull/7739))
|
||||
- bump tiny-keccak ([#8019](https://github.com/paritytech/parity/pull/8019))
|
||||
- Remove un-necessary comment ([#8014](https://github.com/paritytech/parity/pull/8014))
|
||||
- clean up account fmt::Debug ([#7983](https://github.com/paritytech/parity/pull/7983))
|
||||
- improve quality of vote_collector module ([#7984](https://github.com/paritytech/parity/pull/7984))
|
||||
- ExecutedBlock cleanup ([#7991](https://github.com/paritytech/parity/pull/7991))
|
||||
- Hardware-wallet/usb-subscribe-refactor ([#7860](https://github.com/paritytech/parity/pull/7860))
|
||||
- remove wildcard imports from views, make tests more idiomatic ([#7986](https://github.com/paritytech/parity/pull/7986))
|
||||
- moved PerfTimer to a separate crate - "trace-time" ([#7985](https://github.com/paritytech/parity/pull/7985))
|
||||
- clean up ethcore::spec module imports ([#7990](https://github.com/paritytech/parity/pull/7990))
|
||||
- rpc: don't include current block in new_block_filter ([#7982](https://github.com/paritytech/parity/pull/7982))
|
||||
- fix traces, removed bloomchain crate ([#7979](https://github.com/paritytech/parity/pull/7979))
|
||||
- simplify compression and move it out of rlp crate ([#7957](https://github.com/paritytech/parity/pull/7957))
|
||||
- removed old migrations ([#7974](https://github.com/paritytech/parity/pull/7974))
|
||||
- Reject too large packets in snapshot sync. ([#7977](https://github.com/paritytech/parity/pull/7977))
|
||||
- fixed broken logs ([#7934](https://github.com/paritytech/parity/pull/7934))
|
||||
- Increase max download limit to 128MB ([#7965](https://github.com/paritytech/parity/pull/7965))
|
||||
- Calculate proper keccak256/sha3 using parity. ([#7953](https://github.com/paritytech/parity/pull/7953))
|
||||
- Add changelog for 1.8.10 stable and 1.9.3 beta ([#7947](https://github.com/paritytech/parity/pull/7947))
|
||||
- kvdb-rocksdb: remove buffered operations when committing transaction ([#7950](https://github.com/paritytech/parity/pull/7950))
|
||||
- Bump WebSockets ([#7952](https://github.com/paritytech/parity/pull/7952))
|
||||
- removed redundant Bloom conversions ([#7932](https://github.com/paritytech/parity/pull/7932))
|
||||
- simplify RefInfo fmt ([#7929](https://github.com/paritytech/parity/pull/7929))
|
||||
- Kovan WASM fork code ([#7849](https://github.com/paritytech/parity/pull/7849))
|
||||
- bring back trie and triehash benches ([#7926](https://github.com/paritytech/parity/pull/7926))
|
||||
- removed redundant PodAccount::new method ([#7928](https://github.com/paritytech/parity/pull/7928))
|
||||
- removed dummy wrapper structure - LogGroupPosition ([#7922](https://github.com/paritytech/parity/pull/7922))
|
||||
- spec: Validate required divisor fields are not 0 ([#7933](https://github.com/paritytech/parity/pull/7933))
|
||||
- simplify Client::filter_traces method ([#7936](https://github.com/paritytech/parity/pull/7936))
|
||||
- gitlab cache ([#7921](https://github.com/paritytech/parity/pull/7921))
|
||||
- Fix a division by zero in light client RPC handler ([#7917](https://github.com/paritytech/parity/pull/7917))
|
||||
- triehash optimisations ([#7920](https://github.com/paritytech/parity/pull/7920))
|
||||
- removed redundant Blockchain::db method ([#7919](https://github.com/paritytech/parity/pull/7919))
|
||||
- removed redundant Blockchain::rewind method ([#7918](https://github.com/paritytech/parity/pull/7918))
|
||||
- Pending transactions subscription ([#7906](https://github.com/paritytech/parity/pull/7906))
|
||||
- removed redundant otry! macro from ethcore ([#7916](https://github.com/paritytech/parity/pull/7916))
|
||||
- Make block generator easier to use ([#7888](https://github.com/paritytech/parity/pull/7888))
|
||||
- ECIP 1041 - Remove Difficulty Bomb ([#7905](https://github.com/paritytech/parity/pull/7905))
|
||||
- Fix CSP for dapps that require eval. ([#7867](https://github.com/paritytech/parity/pull/7867))
|
||||
- Fix gitlab ([#7901](https://github.com/paritytech/parity/pull/7901))
|
||||
- Gitlb snap master patch ([#7900](https://github.com/paritytech/parity/pull/7900))
|
||||
- fix snap build master ([#7896](https://github.com/paritytech/parity/pull/7896))
|
||||
- Fix wallet import ([#7873](https://github.com/paritytech/parity/pull/7873))
|
||||
- Fix snapcraft nightly ([#7884](https://github.com/paritytech/parity/pull/7884))
|
||||
- Add a timeout for light client sync requests ([#7848](https://github.com/paritytech/parity/pull/7848))
|
||||
- SecretStore: fixed test ([#7878](https://github.com/paritytech/parity/pull/7878))
|
||||
- Fix checksums and auto-update push ([#7846](https://github.com/paritytech/parity/pull/7846))
|
||||
- Forward-port snap fixes ([#7831](https://github.com/paritytech/parity/pull/7831))
|
||||
- Update gitlab-test.sh ([#7883](https://github.com/paritytech/parity/pull/7883))
|
||||
- Fix installer binary names for macos and windows ([#7881](https://github.com/paritytech/parity/pull/7881))
|
||||
- Fix string typo: "develoopment" -> "development" ([#7874](https://github.com/paritytech/parity/pull/7874))
|
||||
- Update the instructions to install the stable snap ([#7876](https://github.com/paritytech/parity/pull/7876))
|
||||
- SecretStore: 'broadcast' decryption session ([#7843](https://github.com/paritytech/parity/pull/7843))
|
||||
- Flush keyfiles. Resolves #7632 ([#7868](https://github.com/paritytech/parity/pull/7868))
|
||||
- Read registry_address from given block ([#7866](https://github.com/paritytech/parity/pull/7866))
|
||||
- Clean up docs formatting for Wasm runtime ([#7869](https://github.com/paritytech/parity/pull/7869))
|
||||
- WASM: Disable internal memory ([#7842](https://github.com/paritytech/parity/pull/7842))
|
||||
- Update gitlab-build.sh ([#7855](https://github.com/paritytech/parity/pull/7855))
|
||||
- ethabi version 5 ([#7723](https://github.com/paritytech/parity/pull/7723))
|
||||
- Light client: randomize the peer we dispatch requests to ([#7844](https://github.com/paritytech/parity/pull/7844))
|
||||
- Store updater metadata in a single place ([#7832](https://github.com/paritytech/parity/pull/7832))
|
||||
- Add new EF ropstens nodes. ([#7824](https://github.com/paritytech/parity/pull/7824))
|
||||
- refactor stratum to remove retain cycle ([#7827](https://github.com/paritytech/parity/pull/7827))
|
||||
- Bump jsonrpc. ([#7828](https://github.com/paritytech/parity/pull/7828))
|
||||
- Add binary identifiers and sha256sum to builds ([#7830](https://github.com/paritytech/parity/pull/7830))
|
||||
- Update references to UI shell & wallet ([#7808](https://github.com/paritytech/parity/pull/7808))
|
||||
- Adjust storage update evm-style ([#7812](https://github.com/paritytech/parity/pull/7812))
|
||||
- Updated WASM Runtime & new interpreter (wasmi) ([#7796](https://github.com/paritytech/parity/pull/7796))
|
||||
- SecretStore: ignore removed authorities when running auto-migration ([#7674](https://github.com/paritytech/parity/pull/7674))
|
||||
- Fix build ([#7807](https://github.com/paritytech/parity/pull/7807))
|
||||
- Move js & js-old code to github.com/parity-js ([#7685](https://github.com/paritytech/parity/pull/7685))
|
||||
- More changelogs :) ([#7782](https://github.com/paritytech/parity/pull/7782))
|
||||
- Actualized API set in help ([#7790](https://github.com/paritytech/parity/pull/7790))
|
||||
- Removed obsolete file ([#7788](https://github.com/paritytech/parity/pull/7788))
|
||||
- Update ropsten bootnodes ([#7776](https://github.com/paritytech/parity/pull/7776))
|
||||
- CHANGELOG for 1.9.1 and 1.8.8 ([#7775](https://github.com/paritytech/parity/pull/7775))
|
||||
- Enable byzantium features on non-ethash chains ([#7753](https://github.com/paritytech/parity/pull/7753))
|
||||
- Fix client not being dropped on shutdown ([#7695](https://github.com/paritytech/parity/pull/7695))
|
||||
- Filter-out nodes.json ([#7716](https://github.com/paritytech/parity/pull/7716))
|
||||
- Removes redundant parentheses ([#7721](https://github.com/paritytech/parity/pull/7721))
|
||||
- Transaction-pool fixes ([#7741](https://github.com/paritytech/parity/pull/7741))
|
||||
- More visible download link in README.md ([#7707](https://github.com/paritytech/parity/pull/7707))
|
||||
- Changelog for 1.9.0 ([#7664](https://github.com/paritytech/parity/pull/7664))
|
||||
- Add scroll when too many accounts ([#7677](https://github.com/paritytech/parity/pull/7677))
|
||||
- SecretStore: return HTTP 403 (access denied) if consensus is unreachable ([#7656](https://github.com/paritytech/parity/pull/7656))
|
||||
- Moved StopGaurd to it's own crate ([#7635](https://github.com/paritytech/parity/pull/7635))
|
||||
|
||||
## Previous releases
|
||||
|
||||
- [CHANGELOG-1.9](docs/CHANGELOG-1.9.md) (_stable_)
|
||||
- [CHANGELOG-1.8](docs/CHANGELOG-1.8.md) (EOL: 2018-03-22)
|
||||
- [CHANGELOG-1.7](docs/CHANGELOG-1.7.md) (EOL: 2018-01-25)
|
||||
- [CHANGELOG-1.6](docs/CHANGELOG-1.6.md) (EOL: 2017-10-15)
|
||||
- [CHANGELOG-1.5](docs/CHANGELOG-1.5.md) (EOL: 2017-07-28)
|
||||
- [CHANGELOG-1.4](docs/CHANGELOG-1.4.md) (EOL: 2017-03-13)
|
||||
- [CHANGELOG-1.3](docs/CHANGELOG-1.3.md) (EOL: 2017-01-19)
|
||||
- [CHANGELOG-1.2](docs/CHANGELOG-1.2.md) (EOL: 2016-11-07)
|
||||
- [CHANGELOG-1.1](docs/CHANGELOG-1.1.md) (EOL: 2016-08-12)
|
||||
- [CHANGELOG-1.0](docs/CHANGELOG-1.0.md) (EOL: 2016-06-24)
|
||||
- [CHANGELOG-0.9](docs/CHANGELOG-0.9.md) (EOL: 2016-05-02)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user