Merge branch 'master' into whisper-testing

This commit is contained in:
Robert Habermeier 2017-12-06 15:23:39 +01:00
commit f61feb33fd
945 changed files with 17191 additions and 64340 deletions

84
.github/CODE_OF_CONDUCT.md vendored Normal file
View File

@ -0,0 +1,84 @@
# 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 peoples 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 someones 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 venuesonline and in-personas 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/)

33
.github/CONTRIBUTING.md vendored Normal file
View File

@ -0,0 +1,33 @@
# Contributing Guidelines
## Do you have a question?
Check out our [Basic Usage](https://github.com/paritytech/parity/wiki/Basic-Usage), [Configuration](https://github.com/paritytech/parity/wiki/Configuring-Parity), and [FAQ](https://github.com/paritytech/parity/wiki/FAQ) articles on our [wiki](https://github.com/paritytech/parity/wiki)!
See also frequently asked questions [tagged with `parity`](https://ethereum.stackexchange.com/questions/tagged/parity?sort=votes&pageSize=50) on Stack Exchange.
## 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/issues/new) in our repository and state:
- What's your Parity version?
- What's your operating system and version?
- How did you install parity?
- 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, please **fork it**, fix bugs or implement features, and [propose a pull request](https://github.com/paritytech/parity/compare).
Please, refer to the [Coding Guide](https://github.com/paritytech/parity/wiki/Coding-guide) in our wiki for more details about hacking on Parity.
## License.
By contributing to Parity, you agree that your contributions will be licensed under the [GPLv3 License](../LICENSE).
Each contributor has to sign our Contributor License Agreement. The purpose of the CLA is to ensure that the guardian of a project's outputs has the necessary ownership or grants of rights over all contributions to allow them to distribute under the chosen license. You can read and sign our full Contributor License Agreement at [cla.parity.io](https://cla.parity.io) before submitting a pull request.

View File

@ -2,11 +2,10 @@ _Before filing a new issue, please **provide the following information**._
> I'm running:
>
> - **Parity version**: 0.0.0
> - **Operating system**: Windows / MacOS / Linux
> - **And installed**: via installer / homebrew / binaries / from source
> - **Which Parity version?**: 0.0.0
> - **Which operating system?**: Windows / MacOS / Linux
> - **How installed?**: via installer / homebrew / binaries / from source
> - **Are you fully synchronized?**: no / yes
> - **Did you try to restart the node?**: no / yes
_Your issue description goes here below. Try to include **actual** vs. **expected behavior** and **steps to reproduce** the issue._
---

3
.gitignore vendored
View File

@ -24,6 +24,9 @@
npm-debug.log
node_modules
# js build artifacts
.git-release.log
# gdb files
.gdb_history

View File

@ -690,9 +690,10 @@ js-release:
script:
- rustup default stable
- echo $JS_FILES_MODIFIED
- if [ $JS_FILES_MODIFIED -eq 0 ]; then echo "Skipping JS rebuild since no JS files modified."; else ./js/scripts/build.sh && ./js/scripts/release.sh; fi
- if [ $JS_FILES_MODIFIED -eq 0 ]; then echo "Skipping JS rebuild since no JS files modified."; else ./js/scripts/build.sh && ./js/scripts/push-precompiled.sh; fi
- echo $JS_OLD_FILES_MODIFIED
- if [ $JS_OLD_FILES_MODIFIED -eq 0 ]; then echo "Skipping JS (old) rebuild since no JS files modified."; else ./js-old/scripts/build.sh && ./js-old/scripts/release.sh; fi
- if [ $JS_OLD_FILES_MODIFIED -eq 0 ]; then echo "Skipping JS (old) rebuild since no JS files modified."; else ./js-old/scripts/build.sh && ./js-old/scripts/push-precompiled.sh; fi
- if [ $JS_FILES_MODIFIED -eq 0 ] && [ $JS_OLD_FILES_MODIFIED -eq 0 ]; then echo "Skipping Cargo update since no JS files modified."; else ./js/scripts/push-cargo.sh; fi
tags:
- javascript

View File

@ -1,3 +1,42 @@
## Parity [v1.8.3](https://github.com/paritytech/parity/releases/tag/v1.8.3) (2017-11-15)
Parity 1.8.3 contains several bug-fixes and removes the ability to deploy built-in multi-signature wallets.
The full list of included changes:
- Backports to beta ([#7043](https://github.com/paritytech/parity/pull/7043))
- pwasm-std update ([#7018](https://github.com/paritytech/parity/pull/7018))
- Version 1.8.3
- Make CLI arguments parsing more backwards compatible ([#7004](https://github.com/paritytech/parity/pull/7004))
- Skip nonce check for gas estimation ([#6997](https://github.com/paritytech/parity/pull/6997))
- Events in WASM runtime ([#6967](https://github.com/paritytech/parity/pull/6967))
- Return decoded seal fields. ([#6932](https://github.com/paritytech/parity/pull/6932))
- Fix serialization of status in transaction receipts. ([#6926](https://github.com/paritytech/parity/pull/6926))
- Windows fixes ([#6921](https://github.com/paritytech/parity/pull/6921))
- Disallow built-in multi-sig deploy (only watch) ([#7014](https://github.com/paritytech/parity/pull/7014))
- Add hint in ActionParams for splitting code/data ([#6968](https://github.com/paritytech/parity/pull/6968))
- Action params and embedded params handling
- Fix name-spaces
## Parity [v1.8.2](https://github.com/paritytech/parity/releases/tag/v1.8.2) (2017-10-26)
Parity 1.8.2 fixes an important potential consensus issue and a few additional minor issues:
- `blockNumber` transaction field is now returned correctly in RPC calls.
- Possible crash when `--force-sealing` option is used.
The full list of included changes:
- Beta Backports ([#6891](https://github.com/paritytech/parity/pull/6891))
- Bump to v1.8.2
- Refactor static context check in CREATE. ([#6886](https://github.com/paritytech/parity/pull/6886))
- Refactor static context check in CREATE.
- Fix wasm.
- Fix serialization of non-localized transactions ([#6868](https://github.com/paritytech/parity/pull/6868))
- Fix serialization of non-localized transactions.
- Return proper SignedTransactions representation.
- Allow force sealing and reseal=0 for non-dev chains. ([#6878](https://github.com/paritytech/parity/pull/6878))
## Parity [v1.8.1](https://github.com/paritytech/parity/releases/tag/v1.8.1) (2017-10-20)
Parity 1.8.1 fixes several bugs with token balances, tweaks snapshot-sync, improves the performance of nodes with huge amounts of accounts and changes the Trezor account derivation path.
@ -13,7 +52,7 @@ Parity 1.8.1 fixes several bugs with token balances, tweaks snapshot-sync, impro
If you don't want to downgrade or move your funds off your Trezor-device, you can also use the official Trezor application or other wallets allowing to select the derivation path to access the funds.
Full list of included changes:
The full list of included changes:
- Add ECIP1017 to Morden config ([#6845](https://github.com/paritytech/parity/pull/6845))
- Ethstore optimizations ([#6844](https://github.com/paritytech/parity/pull/6844))
@ -45,7 +84,7 @@ Further, users upgrading from 1.7 should acknowledge the following changes:
- `trace_filter` RPC method now comes with pagination. [#6312](https://github.com/paritytech/parity/pull/6312)
- Added tracing of rewards on closing blocks. [#6194](https://github.com/paritytech/parity/pull/6194)
Full list of included changes:
The full list of included changes:
- Updated ethabi to fix auto-update ([#6771](https://github.com/paritytech/parity/pull/6771))
- Fixed kovan chain validation ([#6760](https://github.com/paritytech/parity/pull/6760))

526
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -31,7 +31,7 @@ futures-cpupool = "0.1"
fdlimit = "0.1"
ws2_32-sys = "0.2"
ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" }
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.8" }
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.9" }
ethsync = { path = "sync" }
ethcore = { path = "ethcore" }
ethcore-util = { path = "util" }
@ -58,7 +58,7 @@ parity-updater = { path = "updater" }
parity-whisper = { path = "whisper" }
path = { path = "util/path" }
panic_hook = { path = "panic_hook" }
hash = { path = "util/hash" }
keccak-hash = { path = "util/hash" }
migration = { path = "util/migration" }
kvdb = { path = "util/kvdb" }
kvdb-rocksdb = { path = "util/kvdb-rocksdb" }
@ -116,4 +116,4 @@ lto = false
panic = "abort"
[workspace]
members = ["ethstore/cli", "ethkey/cli", "evmbin", "whisper", "chainspec", "dapps/js-glue"]
members = ["ethstore/cli", "ethkey/cli", "evmbin", "whisper", "chainspec", "dapps/js-glue", "ethcore/wasm/run"]

View File

@ -25,8 +25,8 @@ unicase = "1.4"
zip = { version = "0.1", default-features = false }
itertools = "0.5"
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.8" }
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.8" }
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.9" }
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.9" }
ethcore-util = { path = "../util" }
ethcore-bigint = { path = "../util/bigint" }
@ -36,7 +36,7 @@ node-health = { path = "./node-health" }
parity-hash-fetch = { path = "../hash-fetch" }
parity-reactor = { path = "../util/reactor" }
parity-ui = { path = "./ui" }
hash = { path = "../util/hash" }
keccak-hash = { path = "../util/hash" }
clippy = { version = "0.0.103", optional = true}

View File

@ -1,7 +1,7 @@
[package]
description = "Base Package for all Parity built-in dapps"
name = "parity-dapps-glue"
version = "1.9.0"
version = "1.9.1"
license = "GPL-3.0"
authors = ["Parity Technologies <admin@parity.io>"]
build = "build.rs"

View File

@ -25,7 +25,7 @@ mod platform {
use std::process::Command;
pub static NPM_CMD: &'static str = "npm";
pub fn handle_fd(cmd: &mut Command) -> &mut Command {
pub fn handle_cmd(cmd: &mut Command) -> &mut Command {
cmd
}
}
@ -34,14 +34,14 @@ mod platform {
mod platform {
use std::process::{Command, Stdio};
pub static NPM_CMD: &'static str = "npm.cmd";
pub static NPM_CMD: &'static str = "cmd.exe";
// NOTE [ToDr] For some reason on windows
// We cannot have any file descriptors open when running a child process
// during build phase.
pub fn handle_fd(cmd: &mut Command) -> &mut Command {
// The command doesn't have %~dp0 set properly
// and it cannot load globally installed node.exe
pub fn handle_cmd(cmd: &mut Command) -> &mut Command {
cmd.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.arg("/c")
.arg("npm.cmd")
}
}
@ -58,7 +58,7 @@ pub fn build(_path: &str, _dest: &str) {
#[cfg(not(feature = "use-precompiled-js"))]
pub fn build(path: &str, dest: &str) {
let child = platform::handle_fd(&mut Command::new(platform::NPM_CMD))
let child = platform::handle_cmd(&mut Command::new(platform::NPM_CMD))
.arg("install")
.arg("--no-progress")
.current_dir(path)
@ -66,7 +66,7 @@ pub fn build(path: &str, dest: &str) {
.unwrap_or_else(|e| die("Installing node.js dependencies with npm", e));
assert!(child.success(), "There was an error installing dependencies.");
let child = platform::handle_fd(&mut Command::new(platform::NPM_CMD))
let child = platform::handle_cmd(&mut Command::new(platform::NPM_CMD))
.arg("run")
.arg("build")
.env("NODE_ENV", "production")

View File

@ -26,6 +26,8 @@ pub struct App {
pub author: String,
#[serde(rename="iconUrl")]
pub icon_url: String,
#[serde(rename="localUrl")]
pub local_url: Option<String>,
}
impl App {
@ -38,6 +40,7 @@ impl App {
version: info.version.to_owned(),
author: info.author.to_owned(),
icon_url: info.icon_url.to_owned(),
local_url: info.local_url.to_owned(),
}
}
}
@ -50,6 +53,7 @@ impl Into<EndpointInfo> for App {
version: self.version,
author: self.author,
icon_url: self.icon_url,
local_url: self.local_url,
}
}
}

View File

@ -306,6 +306,7 @@ mod tests {
version: "".into(),
author: "".into(),
icon_url: "".into(),
local_url: Some("".into()),
}, Default::default(), None);
// when

View File

@ -56,6 +56,7 @@ fn read_manifest(name: &str, mut path: PathBuf) -> EndpointInfo {
version: "0.0.0".into(),
author: "?".into(),
icon_url: "icon.png".into(),
local_url: None,
}
})
}

View File

@ -18,7 +18,7 @@
use std::collections::BTreeMap;
use jsonrpc_core::BoxFuture;
use futures::Future;
use hyper;
#[derive(Debug, PartialEq, Default, Clone)]
@ -44,10 +44,11 @@ pub struct EndpointInfo {
pub version: String,
pub author: String,
pub icon_url: String,
pub local_url: Option<String>,
}
pub type Endpoints = BTreeMap<String, Box<Endpoint>>;
pub type Response = BoxFuture<hyper::Response, hyper::Error>;
pub type Response = Box<Future<Item=hyper::Response, Error=hyper::Error> + Send>;
pub type Request = hyper::Request;
pub trait Endpoint : Send + Sync {

View File

@ -24,7 +24,6 @@ use fetch::{self, Fetch};
use futures::sync::oneshot;
use futures::{self, Future};
use hyper::{self, Method, StatusCode};
use jsonrpc_core::BoxFuture;
use parking_lot::Mutex;
use endpoint::{self, EndpointPath};
@ -212,7 +211,7 @@ impl Errors {
enum FetchState {
Error(ContentHandler),
InProgress(BoxFuture<FetchState, ()>),
InProgress(Box<Future<Item=FetchState, Error=()> + Send>),
Streaming(hyper::Response),
Done(local::Dapp, endpoint::Response),
Empty,
@ -289,7 +288,7 @@ impl ContentFetcherHandler {
path: EndpointPath,
errors: Errors,
installer: H,
) -> BoxFuture<FetchState, ()> {
) -> Box<Future<Item=FetchState, Error=()> + Send> {
// Start fetching the content
let fetch2 = fetch.clone();
let future = fetch.fetch_with_abort(url, abort.into()).then(move |result| {

View File

@ -32,7 +32,6 @@ extern crate serde_json;
extern crate unicase;
extern crate zip;
extern crate jsonrpc_core;
extern crate jsonrpc_http_server;
extern crate ethcore_util as util;
@ -43,7 +42,7 @@ extern crate node_health;
extern crate parity_dapps_glue as parity_dapps;
extern crate parity_hash_fetch as hash_fetch;
extern crate parity_ui;
extern crate hash;
extern crate keccak_hash as hash;
#[macro_use]
extern crate futures;
@ -52,10 +51,12 @@ extern crate log;
#[macro_use]
extern crate serde_derive;
#[cfg(test)]
extern crate env_logger;
#[cfg(test)]
extern crate ethcore_devtools as devtools;
#[cfg(test)]
extern crate env_logger;
extern crate jsonrpc_core;
#[cfg(test)]
extern crate parity_reactor;

View File

@ -132,6 +132,7 @@ impl From<Info> for EndpointInfo {
description: info.description.into(),
author: info.author.into(),
icon_url: info.icon_url.into(),
local_url: None,
version: info.version.into(),
}
}

View File

@ -180,14 +180,15 @@ fn should_return_fetched_dapp_content() {
assert_security_headers_for_embed(&response2.headers);
assert_eq!(
response2.body,
r#"BE
r#"D2
{
"id": "9c94e154dab8acf859b30ee80fc828fb1d38359d938751b65db71d460588d82a",
"name": "Gavcoin",
"description": "Gavcoin",
"version": "1.0.0",
"author": "",
"iconUrl": "icon.png"
"iconUrl": "icon.png",
"localUrl": null
}
0

View File

@ -14,8 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::env;
use std::str;
use std::{env, io, str};
use std::net::SocketAddr;
use std::path::{Path, PathBuf};
use std::sync::Arc;
@ -187,7 +186,7 @@ impl<T: Fetch> ServerBuilder<T> {
/// Asynchronously start server with no authentication,
/// returns result with `Server` handle on success or an error.
pub fn start_unsecured_http(self, addr: &SocketAddr, io: IoHandler) -> Result<Server, http::Error> {
pub fn start_unsecured_http(self, addr: &SocketAddr, io: IoHandler) -> io::Result<Server> {
let fetch = self.fetch_client();
Server::start_http(
addr,
@ -234,7 +233,7 @@ impl Server {
remote: Remote,
fetch: F,
serve_ui: bool,
) -> Result<Server, http::Error> {
) -> io::Result<Server> {
let health = NodeHealth::new(
sync_status.clone(),
TimeChecker::new::<String>(&[], CpuPool::new(1)),

View File

@ -13,8 +13,8 @@ rustc_version = "0.1"
parity-ui-dev = { path = "../../js", optional = true }
parity-ui-old-dev = { path = "../../js-old", optional = true }
# This is managed by the js/scripts/release.sh script on CI - keep it in a single line
parity-ui-old-precompiled = { git = "https://github.com/paritytech/js-precompiled.git", optional = true, branch = "v1" }
parity-ui-precompiled = { git = "https://github.com/paritytech/js-precompiled.git", optional = true, branch = "master" }
parity-ui-old-precompiled = { git = "https://github.com/js-dist-paritytech/parity-master-1-9-v1.git", optional = true }
parity-ui-precompiled = { git = "https://github.com/js-dist-paritytech/parity-master-1-9-shell.git", optional = true }
[features]
no-precompiled-js = ["parity-ui-dev", "parity-ui-old-dev"]

View File

@ -1,8 +1,26 @@
### Parity [v1.7.9](https://github.com/paritytech/parity/releases/tag/v1.7.9) (2017-11-14)
Parity 1.7.9 removes the ability to deploy built-in multi-signature wallets.
The full list of included changes:
- Bump to v1.7.9 ([#7047](https://github.com/paritytech/parity/pull/7047))
- Disallow built-in multi-sig deploy (only watch) ([#7017](https://github.com/paritytech/parity/pull/7017))
### Parity [v1.7.8](https://github.com/paritytech/parity/releases/tag/v1.7.8) (2017-10-26)
Parity 1.7.8 fixes a critical Byzantium consensus issue. Update is highly recommended.
The full list of included changes:
- Refactor static context check in CREATE ([#6889](https://github.com/paritytech/parity/pull/6889))
- Bump to v1.7.8 ([#6890](https://github.com/paritytech/parity/pull/6890))
## Parity [v1.7.7](https://github.com/paritytech/parity/releases/tag/v1.7.7) (2017-10-15)
Parity 1.7.7 Fixes an issue with auto-update system. Updating is recommended, but not required for Byzantium.
Parity 1.7.7 fixes an issue with auto-update system. Updating is recommended, but not required for Byzantium.
Full list of included changes:
The full list of included changes:
- Fix auto-update ([#6769](https://github.com/paritytech/parity/pull/6759))
- Bump to v1.7.7
@ -13,7 +31,7 @@ Full list of included changes:
Parity 1.7.6 includes a critical consensus-relevant fix for the Byzantium hard-fork. Please upgrade your Ethereum client before block number `4_370_000`.
Full list of included changes:
The full list of included changes:
- Fixed modexp gas calculation overflow ([#6746](https://github.com/paritytech/parity/pull/6746))
- Fixed modexp gas calculation overflow ([#6741](https://github.com/paritytech/parity/pull/6741))
@ -25,7 +43,7 @@ Parity 1.7.5 includes a critical consensus-relevant fix for the Byzantium hard-f
Parity 1.7.5 is the first stable release of the 1.7 branch. With this release the support for 1.6 releases ends. Please upgrade your stable nodes to 1.7.5.
Full list of included changes:
The full list of included changes:
- Backport stable - Fixes Badges ([#6731](https://github.com/paritytech/parity/pull/6731))
- Fix badges not showing up ([#6730](https://github.com/paritytech/parity/pull/6730))
@ -52,7 +70,7 @@ Full list of included changes:
Parity 1.7.4 includes a critical consensus-relevant fix for the Byzantium hard-fork. Please upgrade your Ethereum client before block number `4_370_000`.
Full list of included changes:
The full list of included changes:
- Backport ([#6715](https://github.com/paritytech/parity/pull/6715))
- Fix estimate gas if from is not provided. ([#6714](https://github.com/paritytech/parity/pull/6714))
@ -80,7 +98,7 @@ Parity 1.7.3 enables the Byzantium fork for Ethereum main network on Block 4_370
- Revised timeout and batch size constants for bigger blocks.
- Renamed RPC receipt `statusCode` field to `status`.
Full list of included changes:
The full list of included changes:
- Backporting ([#6676](https://github.com/paritytech/parity/pull/6676))
- Fix wallet view ([#6597](https://github.com/paritytech/parity/pull/6597))
@ -128,7 +146,7 @@ Parity 1.7.2 is a bug-fix release to improve performance and stability. Among ot
- Tweaked warp-sync to quickly catch up with chains fallen back more than 10,000 blocks.
- Fixes to the Chrome extension and macOS installer upgrades.
Full list of included changes:
The full list of included changes:
- Fix output from eth_call. ([#6538](https://github.com/paritytech/parity/pull/6538))
- Ropsten fork ([#6532](https://github.com/paritytech/parity/pull/6532))
@ -279,7 +297,7 @@ Parity 1.7.0 is a major release introducing several important features:
- **PubSub API**. https://github.com/paritytech/parity/wiki/JSONRPC-Parity-Pub-Sub-module
- **Signer apps for IOS and Android**.
Full list of included changes:
The full list of included changes:
- Backports [#6163](https://github.com/paritytech/parity/pull/6163)
- Light client improvements ([#6156](https://github.com/paritytech/parity/pull/6156))

View File

@ -7,7 +7,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
log = "0.3"
hash = { path = "../util/hash" }
keccak-hash = { path = "../util/hash" }
primal = "0.2.3"
parking_lot = "0.4"
crunchy = "0.1.0"

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
extern crate hash;
extern crate keccak_hash as hash;
pub type H256 = [u8; 32];

View File

@ -19,7 +19,7 @@ ethcore-bloom-journal = { path = "../util/bloom" }
ethcore-bytes = { path = "../util/bytes" }
hashdb = { path = "../util/hashdb" }
memorydb = { path = "../util/memorydb" }
patricia_trie = { path = "../util/patricia_trie" }
patricia-trie = { path = "../util/patricia_trie" }
ethcore-devtools = { path = "../devtools" }
ethcore-io = { path = "../util/io" }
ethcore-logger = { path = "../logger" }
@ -54,7 +54,7 @@ kvdb = { path = "../util/kvdb" }
kvdb-rocksdb = { path = "../util/kvdb-rocksdb" }
kvdb-memorydb = { path = "../util/kvdb-memorydb" }
util-error = { path = "../util/error" }
snappy = { path = "../util/snappy" }
snappy = { git = "https://github.com/paritytech/rust-snappy" }
migration = { path = "../util/migration" }
macros = { path = "../util/macros" }
rust-crypto = "0.2.34"
@ -67,7 +67,7 @@ table = { path = "../util/table" }
bloomable = { path = "../util/bloomable" }
vm = { path = "vm" }
wasm = { path = "wasm" }
hash = { path = "../util/hash" }
keccak-hash = { path = "../util/hash" }
triehash = { path = "../util/triehash" }
semantic_version = { path = "../util/semantic_version" }
unexpected = { path = "../util/unexpected" }

View File

@ -12,7 +12,7 @@ heapsize = "0.4"
lazy_static = "0.2"
log = "0.3"
vm = { path = "../vm" }
hash = { path = "../../util/hash" }
keccak-hash = { path = "../../util/hash" }
parking_lot = "0.4"
memory-cache = { path = "../../util/memory_cache" }

View File

@ -22,7 +22,7 @@ extern crate ethcore_bigint as bigint;
extern crate parking_lot;
extern crate heapsize;
extern crate vm;
extern crate hash;
extern crate keccak_hash as hash;
extern crate memory_cache;
#[macro_use]

View File

@ -13,10 +13,9 @@ ethcore-util = { path = "../../util" }
ethcore-bigint = { path = "../../util/bigint" }
ethcore-bytes = { path = "../../util/bytes" }
memorydb = { path = "../../util/memorydb" }
patricia_trie = { path = "../../util/patricia_trie" }
patricia-trie = { path = "../../util/patricia_trie" }
ethcore-network = { path = "../../util/network" }
ethcore-io = { path = "../../util/io" }
ethcore-devtools = { path = "../../devtools" }
evm = { path = "../evm" }
heapsize = "0.4"
vm = { path = "../vm" }
@ -32,12 +31,15 @@ serde = "1.0"
serde_derive = "1.0"
parking_lot = "0.4"
stats = { path = "../../util/stats" }
hash = { path = "../../util/hash" }
keccak-hash = { path = "../../util/hash" }
triehash = { path = "../../util/triehash" }
kvdb = { path = "../../util/kvdb" }
kvdb-rocksdb = { path = "../../util/kvdb-rocksdb" }
kvdb-memorydb = { path = "../../util/kvdb-memorydb" }
memory-cache = { path = "../../util/memory_cache" }
[dev-dependencies]
tempdir = "0.3"
[features]
default = []

View File

@ -120,7 +120,6 @@ impl<T: ChainDataFetcher> IoHandler<ClientIoMessage> for ImportBlocks<T> {
#[cfg(test)]
mod tests {
use super::Service;
use devtools::RandomTempPath;
use ethcore::spec::Spec;
use std::sync::Arc;
@ -128,13 +127,14 @@ mod tests {
use client::fetch;
use time::Duration;
use parking_lot::Mutex;
use tempdir::TempDir;
#[test]
fn it_works() {
let tempdir = TempDir::new("").unwrap();
let spec = Spec::new_test();
let temp_path = RandomTempPath::new();
let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::hours(6))));
Service::start(Default::default(), &spec, fetch::unavailable(), temp_path.as_path(), cache).unwrap();
Service::start(Default::default(), &spec, fetch::unavailable(), tempdir.path(), cache).unwrap();
}
}

View File

@ -76,7 +76,7 @@ extern crate smallvec;
extern crate stats;
extern crate time;
extern crate vm;
extern crate hash;
extern crate keccak_hash as hash;
extern crate triehash;
extern crate kvdb;
extern crate kvdb_memorydb;
@ -84,4 +84,4 @@ extern crate kvdb_rocksdb;
extern crate memory_cache;
#[cfg(test)]
extern crate ethcore_devtools as devtools;
extern crate tempdir;

View File

@ -17,10 +17,8 @@
//! Defines error types and levels of punishment to use upon
//! encountering.
use rlp::DecoderError;
use network::NetworkError;
use std::fmt;
use {rlp, network};
/// Levels of punishment.
///
@ -41,9 +39,9 @@ pub enum Punishment {
#[derive(Debug)]
pub enum Error {
/// An RLP decoding error.
Rlp(DecoderError),
Rlp(rlp::DecoderError),
/// A network error.
Network(NetworkError),
Network(network::Error),
/// Out of credits.
NoCredits,
/// Unrecognized packet code.
@ -92,14 +90,14 @@ impl Error {
}
}
impl From<DecoderError> for Error {
fn from(err: DecoderError) -> Self {
impl From<rlp::DecoderError> for Error {
fn from(err: rlp::DecoderError) -> Self {
Error::Rlp(err)
}
}
impl From<NetworkError> for Error {
fn from(err: NetworkError) -> Self {
impl From<network::Error> for Error {
fn from(err: network::Error) -> Self {
Error::Network(err)
}
}

View File

@ -264,8 +264,9 @@ mod tests {
#[test]
fn file_store() {
let path = ::devtools::RandomTempPath::new();
let store = FileStore(path.as_path().clone());
let tempdir = ::tempdir::TempDir::new("").unwrap();
let path = tempdir.path().join("file");
let store = FileStore(path);
let mut samples = store.load();
assert!(samples.is_empty());

View File

@ -321,6 +321,11 @@ impl TransactionQueue {
self.by_hash.remove(&hash);
}
}
/// Get a transaction by hash.
pub fn get(&self, hash: &H256) -> Option<&PendingTransaction> {
self.by_hash.get(&hash)
}
}
#[cfg(test)]

View File

@ -23,7 +23,9 @@
]
},
"validateScoreTransition": 1000000,
"validateStepTransition": 1500000
"validateStepTransition": 1500000,
"maximumUncleCountTransition": 10000000,
"maximumUncleCount": 2
}
}
},

@ -1 +1 @@
Subproject commit c8129ce2f36c26ed634eda786960978a28e28d0e
Subproject commit d6185ea16eaba7ff685c069c2064819f9549c4d7

View File

@ -178,6 +178,13 @@ impl AccountProvider {
}
}
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 {

View File

@ -380,8 +380,13 @@ impl<'x> OpenBlock<'x> {
/// NOTE Will check chain constraints and the uncle number but will NOT check
/// that the header itself is actually valid.
pub fn push_uncle(&mut self, valid_uncle_header: Header) -> Result<(), BlockError> {
if self.block.uncles.len() + 1 > self.engine.maximum_uncle_count() {
return Err(BlockError::TooManyUncles(OutOfBounds{min: None, max: Some(self.engine.maximum_uncle_count()), found: self.block.uncles.len() + 1}));
let max_uncles = self.engine.maximum_uncle_count(self.block.header().number());
if self.block.uncles.len() + 1 > max_uncles {
return Err(BlockError::TooManyUncles(OutOfBounds{
min: None,
max: Some(max_uncles),
found: self.block.uncles.len() + 1,
}));
}
// TODO: check number
// TODO: check not a direct ancestor (use last_hashes for that)

View File

@ -1231,12 +1231,12 @@ impl BlockChainClient for Client {
}
fn estimate_gas(&self, t: &SignedTransaction, block: BlockId) -> Result<U256, CallError> {
const UPPER_CEILING: u64 = 1_000_000_000_000u64;
let (mut upper, env_info) = {
let (mut upper, max_upper, env_info) = {
let mut env_info = self.env_info(block).ok_or(CallError::StatePruned)?;
let initial_upper = env_info.gas_limit;
env_info.gas_limit = UPPER_CEILING.into();
(initial_upper, env_info)
let init = env_info.gas_limit;
let max = init * U256::from(10);
env_info.gas_limit = max;
(init, max, env_info)
};
// that's just a copy of the state.
@ -1257,9 +1257,7 @@ impl BlockChainClient for Client {
};
if !cond(upper)? {
// impossible at block gas limit - try `UPPER_CEILING` instead.
// TODO: consider raising limit by powers of two.
upper = UPPER_CEILING.into();
upper = max_upper;
if !cond(upper)? {
trace!(target: "estimate_gas", "estimate_gas failed with {}", upper);
let err = ExecutionError::Internal(format!("Requires higher than upper limit of {}", upper));
@ -1872,7 +1870,7 @@ impl MiningBlockChainClient for Client {
.find_uncle_headers(&h, engine.maximum_uncle_age())
.unwrap_or_else(Vec::new)
.into_iter()
.take(engine.maximum_uncle_count())
.take(engine.maximum_uncle_count(open_block.header().number()))
.foreach(|h| {
open_block.push_uncle(h).expect("pushing maximum_uncle_count;
open_block was just created;
@ -1887,7 +1885,7 @@ impl MiningBlockChainClient for Client {
fn reopen_block(&self, block: ClosedBlock) -> OpenBlock {
let engine = &*self.engine;
let mut block = block.reopen(engine);
let max_uncles = engine.maximum_uncle_count();
let max_uncles = engine.maximum_uncle_count(block.header().number());
if block.uncles().len() < max_uncles {
let chain = self.chain.read();
let h = chain.best_block_hash();

View File

@ -65,6 +65,10 @@ pub struct AuthorityRoundParams {
pub immediate_transitions: bool,
/// Block reward in base units.
pub block_reward: U256,
/// Number of accepted uncles transition block.
pub maximum_uncle_count_transition: u64,
/// Number of accepted uncles.
pub maximum_uncle_count: usize,
}
impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
@ -77,6 +81,8 @@ impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
validate_step_transition: p.validate_step_transition.map_or(0, Into::into),
immediate_transitions: p.immediate_transitions.unwrap_or(false),
block_reward: p.block_reward.map_or_else(Default::default, Into::into),
maximum_uncle_count_transition: p.maximum_uncle_count_transition.map_or(0, Into::into),
maximum_uncle_count: p.maximum_uncle_count.map_or(0, Into::into),
}
}
}
@ -218,6 +224,8 @@ pub struct AuthorityRound {
epoch_manager: Mutex<EpochManager>,
immediate_transitions: bool,
block_reward: U256,
maximum_uncle_count_transition: u64,
maximum_uncle_count: usize,
machine: EthereumMachine,
}
@ -365,6 +373,8 @@ impl AuthorityRound {
epoch_manager: Mutex::new(EpochManager::blank()),
immediate_transitions: our_params.immediate_transitions,
block_reward: our_params.block_reward,
maximum_uncle_count_transition: our_params.maximum_uncle_count_transition,
maximum_uncle_count: our_params.maximum_uncle_count,
machine: machine,
});
@ -436,8 +446,16 @@ impl Engine<EthereumMachine> for AuthorityRound {
]
}
fn maximum_uncle_count(&self, block: BlockNumber) -> usize {
if block >= self.maximum_uncle_count_transition {
self.maximum_uncle_count
} else {
// fallback to default value
2
}
}
fn populate_from_parent(&self, header: &mut Header, parent: &Header) {
// Chain scoring: total weight is sqrt(U256::max_value())*height - step
let new_difficulty = U256::from(U128::max_value()) + header_step(parent).expect("Header has been verified; qed").into() - self.step.load().into();
header.set_difficulty(new_difficulty);
}
@ -949,6 +967,8 @@ mod tests {
validate_score_transition: 0,
validate_step_transition: 0,
immediate_transitions: true,
maximum_uncle_count_transition: 0,
maximum_uncle_count: 0,
block_reward: Default::default(),
};
@ -976,4 +996,31 @@ mod tests {
assert!(aura.verify_block_family(&header, &parent_header).is_ok());
assert_eq!(last_benign.load(AtomicOrdering::SeqCst), 1);
}
#[test]
fn test_uncles_transition() {
let last_benign = Arc::new(AtomicUsize::new(0));
let params = AuthorityRoundParams {
step_duration: Default::default(),
start_step: Some(1),
validators: Box::new(TestSet::new(Default::default(), last_benign.clone())),
validate_score_transition: 0,
validate_step_transition: 0,
immediate_transitions: true,
maximum_uncle_count_transition: 1,
maximum_uncle_count: 0,
block_reward: Default::default(),
};
let aura = {
let mut c_params = ::spec::CommonParams::default();
c_params.gas_limit_bound_divisor = 5.into();
let machine = ::machine::EthereumMachine::regular(c_params, Default::default());
AuthorityRound::new(params, machine).unwrap()
};
assert_eq!(aura.maximum_uncle_count(0), 2);
assert_eq!(aura.maximum_uncle_count(1), 0);
assert_eq!(aura.maximum_uncle_count(100), 0);
}
}

View File

@ -192,7 +192,8 @@ pub trait Engine<M: Machine>: Sync + Send {
fn extra_info(&self, _header: &M::Header) -> BTreeMap<String, String> { BTreeMap::new() }
/// Maximum number of uncles a block is allowed to declare.
fn maximum_uncle_count(&self) -> usize { 2 }
fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 0 }
/// The number of generations back that uncles can be.
fn maximum_uncle_age(&self) -> usize { 6 }
@ -363,7 +364,7 @@ pub trait EthEngine: Engine<::machine::EthereumMachine> {
}
/// The nonce with which accounts begin at given block.
fn account_start_nonce(&self, block: u64) -> U256 {
fn account_start_nonce(&self, block: BlockNumber) -> U256 {
self.machine().account_start_nonce(block)
}

View File

@ -16,6 +16,7 @@
use bigint::prelude::U256;
use engines::Engine;
use header::BlockNumber;
use parity_machine::{Header, LiveBlock, WithBalances};
/// Params for a null engine.
@ -95,6 +96,8 @@ impl<M: WithBalances> Engine<M> for NullEngine<M> {
self.machine.note_rewards(block, &[(author, result_block_reward)], &uncle_rewards)
}
fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 2 }
fn verify_local_seal(&self, _header: &M::Header) -> Result<(), M::Error> {
Ok(())
}

View File

@ -450,7 +450,7 @@ impl Engine<EthereumMachine> for Tendermint {
fn machine(&self) -> &EthereumMachine { &self.machine }
fn maximum_uncle_count(&self) -> usize { 0 }
fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 0 }
fn maximum_uncle_age(&self) -> usize { 0 }

View File

@ -26,7 +26,7 @@ use util::Address;
use unexpected::{OutOfBounds, Mismatch};
use block::*;
use error::{BlockError, Error};
use header::Header;
use header::{Header, BlockNumber};
use engines::{self, Engine};
use ethjson;
use rlp::{self, UntrustedRlp};
@ -181,6 +181,8 @@ impl Engine<EthereumMachine> for Arc<Ethash> {
}
}
fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 2 }
fn populate_from_parent(&self, header: &mut Header, parent: &Header) {
let difficulty = self.calculate_difficulty(header, parent);
header.set_difficulty(difficulty);
@ -447,9 +449,12 @@ fn ecip1017_eras_block_reward(era_rounds: u64, mut reward: U256, block_number:u6
} else {
block_number / era_rounds
};
let mut divi = U256::from(1);
for _ in 0..eras {
reward = reward / U256::from(5) * U256::from(4);
reward = reward * U256::from(4);
divi = divi * U256::from(5);
}
reward = reward / divi;
(eras, reward)
}
@ -515,6 +520,11 @@ mod tests {
let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number);
assert_eq!(15, eras);
assert_eq!(U256::from_str("271000000000000").unwrap(), reward);
let block_number = 250000000;
let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number);
assert_eq!(49, eras);
assert_eq!(U256::from_str("51212FFBAF0A").unwrap(), reward);
}
#[test]

View File

@ -103,7 +103,7 @@ extern crate price_info;
extern crate rand;
extern crate rayon;
extern crate rlp;
extern crate hash;
extern crate keccak_hash as hash;
extern crate heapsize;
extern crate memorydb;
extern crate patricia_trie as trie;

View File

@ -137,9 +137,14 @@ pub fn verify_block_family(header: &Header, parent: &Header, engine: &EthEngine,
fn verify_uncles(header: &Header, bytes: &[u8], bc: &BlockProvider, engine: &EthEngine) -> Result<(), Error> {
let num_uncles = UntrustedRlp::new(bytes).at(2)?.item_count()?;
let max_uncles = engine.maximum_uncle_count(header.number());
if num_uncles != 0 {
if num_uncles > engine.maximum_uncle_count() {
return Err(From::from(BlockError::TooManyUncles(OutOfBounds { min: None, max: Some(engine.maximum_uncle_count()), found: num_uncles })));
if num_uncles > max_uncles {
return Err(From::from(BlockError::TooManyUncles(OutOfBounds {
min: None,
max: Some(max_uncles),
found: num_uncles,
})));
}
let mut excluded = HashSet::new();
@ -653,7 +658,7 @@ mod tests {
let mut bad_uncles = good_uncles.clone();
bad_uncles.push(good_uncle1.clone());
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &bad_uncles), engine, &bc),
TooManyUncles(OutOfBounds { max: Some(engine.maximum_uncle_count()), min: None, found: bad_uncles.len() }));
TooManyUncles(OutOfBounds { max: Some(engine.maximum_uncle_count(header.number())), min: None, found: bad_uncles.len() }));
header = good.clone();
bad_uncles = vec![ good_uncle1.clone(), good_uncle1.clone() ];

View File

@ -12,7 +12,7 @@ ethcore-util = { path = "../../util" }
ethcore-bigint = { path = "../../util/bigint" }
ethjson = { path = "../../json" }
bloomable = { path = "../../util/bloomable" }
hash = { path = "../../util/hash" }
keccak-hash = { path = "../../util/hash" }
heapsize = "0.4"
[dev-dependencies]

View File

@ -24,7 +24,7 @@ extern crate rlp;
#[macro_use]
extern crate rlp_derive;
extern crate bloomable;
extern crate hash;
extern crate keccak_hash as hash;
extern crate heapsize;
#[cfg(test)]

View File

@ -8,9 +8,9 @@ byteorder = "1.0"
ethcore-util = { path = "../../util" }
ethcore-bytes = { path = "../../util/bytes" }
ethcore-bigint = { path = "../../util/bigint" }
patricia_trie = { path = "../../util/patricia_trie" }
patricia-trie = { path = "../../util/patricia_trie" }
log = "0.3"
common-types = { path = "../types" }
ethjson = { path = "../../json" }
rlp = { path = "../../util/rlp" }
hash = { path = "../../util/hash" }
keccak-hash = { path = "../../util/hash" }

View File

@ -22,7 +22,7 @@ extern crate ethcore_bytes as bytes;
extern crate common_types as types;
extern crate ethjson;
extern crate rlp;
extern crate hash;
extern crate keccak_hash as hash;
extern crate patricia_trie as trie;
mod action_params;

View File

@ -127,12 +127,14 @@ pub struct WasmCosts {
pub mul: u32,
/// Memory (load/store) operations multiplier.
pub mem: u32,
/// Memory copy operation.
/// Memory copy operation, per byte.
pub mem_copy: u32,
/// Memory move operation, per byte.
pub mem_move: u32,
/// Memory set operation, per byte.
pub mem_set: u32,
/// Static region charge, per byte.
pub static_region: u32,
/// General static query of u64 value from env-info
pub static_u64: u32,
/// General static query of U256 value from env-info
pub static_u256: u32,
/// General static query of Address value from env-info
@ -147,11 +149,9 @@ impl Default for WasmCosts {
mul: 4,
mem: 2,
mem_copy: 1,
mem_move: 1,
mem_set: 1,
static_region: 1,
// due to runtime issues, this can be slow
static_u64: 32,
static_u256: 64,
static_address: 40,
}

View File

@ -0,0 +1,19 @@
[package]
name = "pwasm-run-test"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
serde = "1"
serde_json = "1"
serde_derive = "1"
ethcore-bigint = { path = "../../../util/bigint" }
ethjson = { path = "../../../json" }
vm = { path = "../../vm" }
wasm = { path = "../" }
clap = "2.24"
ethcore-logger = { path = "../../../logger" }
rustc-hex = "1"
[features]
default = ["ethcore-bigint/std"]

View File

@ -0,0 +1,45 @@
[
{
"caption": "Sample test",
"source": "./res/sample1.wasm",
"address": "0x1000000000000000000000000000000000000001",
"sender": "0x1000000000000000000000000000000000000002",
"value": "0x0000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": 100000,
"payload": "0x",
"asserts": [
{ "Return": "0x01" },
{ "UsedGas": 17 },
{ "HasCall": { "codeAddress": "0x1000000000000000000000000000000000000002" }},
{ "HasStorage":
{
"key": "0x0000000000000000000000000000000000000000000000000000000000000001",
"value": "0x0000000000000000000000000000000000000000000000000000000000000002"
}
}
]
},
{
"caption": "Keccak test",
"source": "./res/sample2.wasm",
"payload": "0x736f6d657468696e67",
"gasLimit": 100000,
"asserts": [
{ "Return": "0x68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87" }
]
},
{
"caption": "Token total supply",
"source": {
"constructor": "./res/sample3.wasm",
"sender": "0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f",
"at": "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"arguments": "0x0000000000000000000000000000000000000000000000000000000010000000"
},
"payload": "0x18160ddd",
"gasLimit": 100000,
"asserts": [
{ "Return": "0x0000000000000000000000000000000000000000000000000000000010000000" }
]
}
]

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,70 @@
use std::borrow::Cow;
use ethjson::uint::Uint;
use ethjson::hash::{Address, H256};
use ethjson::bytes::Bytes;
#[derive(Deserialize)]
#[serde(untagged)]
pub enum Source {
Raw(Cow<'static, String>),
Constructor {
#[serde(rename="constructor")]
source: Cow<'static, String>,
arguments: Bytes,
sender: Address,
at: Address,
},
}
impl Source {
pub fn as_ref(&self) -> &str {
match *self {
Source::Raw(ref r) => r.as_ref(),
Source::Constructor { ref source, .. } => source.as_ref(),
}
}
}
#[derive(Deserialize)]
pub struct Fixture {
pub caption: Cow<'static, String>,
pub source: Source,
pub address: Option<Address>,
pub sender: Option<Address>,
pub value: Option<Uint>,
#[serde(rename="gasLimit")]
pub gas_limit: Option<u64>,
pub payload: Option<Bytes>,
pub storage: Option<Vec<StorageEntry>>,
pub asserts: Vec<Assert>,
}
#[derive(Deserialize, Debug)]
pub struct StorageEntry {
pub key: Uint,
pub value: Uint,
}
#[derive(Deserialize, Debug, Clone)]
pub struct CallLocator {
pub sender: Option<Address>,
pub receiver: Option<Address>,
pub value: Option<Uint>,
pub data: Option<Bytes>,
#[serde(rename="codeAddress")]
pub code_address: Option<Address>,
}
#[derive(Deserialize, Debug)]
pub struct StorageAssert {
pub key: H256,
pub value: H256,
}
#[derive(Deserialize, Debug)]
pub enum Assert {
HasCall(CallLocator),
HasStorage(StorageAssert),
UsedGas(u64),
Return(Bytes),
}

View File

@ -0,0 +1,46 @@
extern crate serde;
extern crate serde_json;
#[macro_use] extern crate serde_derive;
extern crate ethcore_bigint;
extern crate ethjson;
extern crate wasm;
extern crate vm;
extern crate clap;
extern crate ethcore_logger;
extern crate rustc_hex;
mod fixture;
mod runner;
use fixture::Fixture;
use clap::{App, Arg};
use std::fs;
fn main() {
::ethcore_logger::init_log();
let matches = App::new("pwasm-run-test")
.arg(Arg::with_name("target")
.index(1)
.required(true)
.multiple(true)
.help("JSON fixture"))
.get_matches();
let mut exit_code = 0;
for target in matches.values_of("target").expect("No target parameter") {
let mut f = fs::File::open(target).expect("Failed to open file");
let fixtures: Vec<Fixture> = serde_json::from_reader(&mut f).expect("Failed to deserialize json");
for fixture in fixtures.into_iter() {
let fails = runner::run_fixture(&fixture);
for fail in fails.iter() {
exit_code = 1;
println!("Failed assert in test \"{}\" ('{}'): {}", fixture.caption.as_ref(), target, fail);
}
}
}
std::process::exit(exit_code);
}

View File

@ -0,0 +1,261 @@
use fixture::{Fixture, Assert, CallLocator, Source};
use wasm::WasmInterpreter;
use vm::{self, Vm, GasLeft, ActionParams, ActionValue, ParamsType};
use vm::tests::FakeExt;
use std::io::{self, Read};
use std::{fs, path, fmt};
use std::sync::Arc;
use ethcore_bigint::prelude::{U256, H256, H160};
use rustc_hex::ToHex;
fn load_code<P: AsRef<path::Path>>(p: P) -> io::Result<Vec<u8>> {
let mut result = Vec::new();
let mut f = fs::File::open(p)?;
f.read_to_end(&mut result)?;
Ok(result)
}
fn wasm_interpreter() -> WasmInterpreter {
WasmInterpreter::new().expect("wasm interpreter to create without errors")
}
#[derive(Debug)]
pub enum SpecNonconformity {
Address,
}
#[derive(Debug)]
pub enum Fail {
Return { expected: Vec<u8>, actual: Vec<u8> },
UsedGas { expected: u64, actual: u64 },
Runtime(String),
Load(io::Error),
NoCall(CallLocator),
StorageMismatch { key: H256, expected: H256, actual: Option<H256> },
Nonconformity(SpecNonconformity)
}
impl Fail {
fn runtime(err: vm::Error) -> Vec<Fail> {
vec![Fail::Runtime(format!("{}", err))]
}
fn load(err: io::Error) -> Vec<Fail> {
vec![Fail::Load(err)]
}
fn nononformity(kind: SpecNonconformity) -> Vec<Fail> {
vec![Fail::Nonconformity(kind)]
}
}
impl fmt::Display for Fail {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Fail::*;
match *self {
Return { ref expected, ref actual } =>
write!(
f,
"Expected to return result: 0x{} ({} bytes), but got 0x{} ({} bytes)",
expected.to_hex(),
expected.len(),
actual.to_hex(),
actual.len()
),
UsedGas { expected, actual } =>
write!(f, "Expected to use gas: {}, but got actual gas used: {}", expected, actual),
Runtime(ref s) =>
write!(f, "WASM Runtime error: {}", s),
Load(ref e) =>
write!(f, "Load i/o error: {}", e),
NoCall(ref call) =>
write!(f, "Call not found: {:?}", call),
StorageMismatch { ref key, ref expected, actual: Some(ref actual)} =>
write!(
f,
"Storage key {} value mismatch, expected {}, got: {}",
key.as_ref().to_vec().to_hex(),
expected.as_ref().to_vec().to_hex(),
actual.as_ref().to_vec().to_hex(),
),
StorageMismatch { ref key, ref expected, actual: None} =>
write!(
f,
"No expected storage value for key {} found, expected {}",
key.as_ref().to_vec().to_hex(),
expected.as_ref().to_vec().to_hex(),
),
Nonconformity(SpecNonconformity::Address) =>
write!(f, "Cannot use address when constructor is specified!"),
}
}
}
pub fn construct(
ext: &mut vm::Ext,
source: Vec<u8>,
arguments: Vec<u8>,
sender: H160,
at: H160,
) -> Result<Vec<u8>, vm::Error> {
let mut params = ActionParams::default();
params.sender = sender;
params.address = at;
params.gas = U256::from(100_000_000);
params.data = Some(arguments);
params.code = Some(Arc::new(source));
params.params_type = ParamsType::Separate;
Ok(
match wasm_interpreter().exec(params, ext)? {
GasLeft::Known(_) => Vec::new(),
GasLeft::NeedsReturn { data, .. } => data.to_vec(),
}
)
}
pub fn run_fixture(fixture: &Fixture) -> Vec<Fail> {
let mut params = ActionParams::default();
let source = match load_code(fixture.source.as_ref()) {
Ok(code) => code,
Err(e) => { return Fail::load(e); },
};
let mut ext = FakeExt::new();
params.code = Some(Arc::new(
if let Source::Constructor { ref arguments, ref sender, ref at, .. } = fixture.source {
match construct(&mut ext, source, arguments.clone().into(), sender.clone().into(), at.clone().into()) {
Ok(code) => code,
Err(e) => { return Fail::runtime(e); }
}
} else {
source
}
));
if let Some(ref sender) = fixture.sender {
params.sender = sender.clone().into();
}
if let Some(ref address) = fixture.address {
if let Source::Constructor { .. } = fixture.source {
return Fail::nononformity(SpecNonconformity::Address);
}
params.address = address.clone().into();
} else if let Source::Constructor { ref at, .. } = fixture.source {
params.address = at.clone().into();
}
if let Some(gas_limit) = fixture.gas_limit {
params.gas = U256::from(gas_limit);
}
if let Some(ref data) = fixture.payload {
params.data = Some(data.clone().into())
}
if let Some(value) = fixture.value {
params.value = ActionValue::Transfer(value.clone().into())
}
if let Some(ref storage) = fixture.storage {
for storage_entry in storage.iter() {
let key: U256 = storage_entry.key.into();
let val: U256 = storage_entry.value.into();
ext.store.insert(key.into(), val.into());
}
}
let mut interpreter = wasm_interpreter();
let interpreter_return = match interpreter.exec(params, &mut ext) {
Ok(ret) => ret,
Err(e) => { return Fail::runtime(e); }
};
let (gas_left, result) = match interpreter_return {
GasLeft::Known(gas) => { (gas, Vec::new()) },
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
};
let mut fails = Vec::new();
for assert in fixture.asserts.iter() {
match *assert {
Assert::Return(ref data) => {
if &data[..] != &result[..] {
fails.push(Fail::Return { expected: (&data[..]).to_vec(), actual: (&result[..]).to_vec() })
}
},
Assert::UsedGas(gas) => {
let used_gas = fixture.gas_limit.unwrap_or(0) - gas_left.low_u64();
if gas != used_gas {
fails.push(Fail::UsedGas { expected: gas, actual: used_gas });
}
},
Assert::HasCall(ref locator) => {
let mut found = false;
for fake_call in ext.calls.iter() {
let mut match_ = true;
if let Some(ref data) = locator.data {
if data.as_ref() != &fake_call.data[..] { match_ = false; }
}
if let Some(ref code_addr) = locator.code_address {
if fake_call.code_address.unwrap_or(H160::zero()) != code_addr.clone().into() { match_ = false }
}
if let Some(ref sender) = locator.sender {
if fake_call.sender_address.unwrap_or(H160::zero()) != sender.clone().into() { match_ = false }
}
if let Some(ref receiver) = locator.receiver {
if fake_call.receive_address.unwrap_or(H160::zero()) != receiver.clone().into() { match_ = false }
}
if match_ {
found = true;
break;
}
}
if !found {
fails.push(Fail::NoCall(locator.clone()))
}
},
Assert::HasStorage(ref storage_entry) => {
let expected_storage_key: H256 = storage_entry.key.clone().into();
let expected_storage_value: H256 = storage_entry.value.clone().into();
let val = ext.store.get(&expected_storage_key);
if let Some(val) = val {
if val != &expected_storage_value {
fails.push(Fail::StorageMismatch {
key: expected_storage_key,
expected: expected_storage_value,
actual: Some(val.clone())
})
}
} else {
fails.push(Fail::StorageMismatch {
key: expected_storage_key,
expected: expected_storage_value,
actual: None,
})
}
},
}
}
fails
}

View File

@ -25,12 +25,12 @@ pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[
Static(
"_storage_read",
&[I32; 2],
Some(I32),
None,
),
Static(
"_storage_write",
&[I32; 2],
Some(I32),
None,
),
Static(
"_balance",
@ -38,12 +38,12 @@ pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[
None,
),
Static(
"_malloc",
"_ext_malloc",
&[I32],
Some(I32),
),
Static(
"_free",
"_ext_free",
&[I32],
None,
),
@ -92,6 +92,21 @@ pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[
&[I32; 3],
Some(I32),
),
Static(
"_ext_memcpy",
&[I32; 3],
Some(I32),
),
Static(
"_ext_memset",
&[I32; 3],
Some(I32),
),
Static(
"_ext_memmove",
&[I32; 3],
Some(I32),
),
Static(
"_panic",
&[I32; 2],
@ -99,8 +114,8 @@ pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[
),
Static(
"_blockhash",
&[I32; 3],
Some(I32),
&[I64, I32],
None,
),
Static(
"_coinbase",
@ -130,12 +145,12 @@ pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[
Static(
"_timestamp",
&[],
Some(I32),
Some(I64),
),
Static(
"_blocknumber",
&[],
Some(I32),
Some(I64),
),
Static(
"_difficulty",
@ -147,6 +162,11 @@ pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[
&[I32],
None,
),
Static(
"_elog",
&[I32; 4],
None,
),
// TODO: Get rid of it also somehow?
Static(
@ -157,8 +177,8 @@ pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[
Static(
"_llvm_bswap_i64",
&[I32; 2],
Some(I32)
&[I64],
Some(I64)
),
];

View File

@ -31,6 +31,7 @@ mod result;
#[cfg(test)]
mod tests;
mod env;
mod panic_payload;
const DEFAULT_STACK_SPACE: u32 = 5 * 1024 * 1024;

View File

@ -0,0 +1,168 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use byteorder::{LittleEndian, ReadBytesExt};
use std::io::{self, Read};
#[derive(Debug, PartialEq, Eq)]
pub struct PanicPayload {
pub msg: Option<String>,
pub file: Option<String>,
pub line: Option<u32>,
pub col: Option<u32>,
}
fn read_string(rdr: &mut io::Cursor<&[u8]>) -> io::Result<Option<String>> {
let string_len = rdr.read_u32::<LittleEndian>()?;
let string = if string_len == 0 {
None
} else {
let mut content = vec![0; string_len as usize];
rdr.read_exact(&mut content)?;
Some(String::from_utf8_lossy(&content).into_owned())
};
Ok(string)
}
pub fn decode(raw: &[u8]) -> PanicPayload {
let mut rdr = io::Cursor::new(raw);
let msg = read_string(&mut rdr).ok().and_then(|x| x);
let file = read_string(&mut rdr).ok().and_then(|x| x);
let line = rdr.read_u32::<LittleEndian>().ok();
let col = rdr.read_u32::<LittleEndian>().ok();
PanicPayload {
msg: msg,
file: file,
line: line,
col: col,
}
}
#[cfg(test)]
mod tests {
use super::*;
use byteorder::WriteBytesExt;
fn write_u32(payload: &mut Vec<u8>, val: u32) {
payload.write_u32::<LittleEndian>(val).unwrap();
}
fn write_bytes(payload: &mut Vec<u8>, bytes: &[u8]) {
write_u32(payload, bytes.len() as u32);
payload.extend(bytes);
}
#[test]
fn it_works() {
let mut raw = Vec::new();
write_bytes(&mut raw, b"msg");
write_bytes(&mut raw, b"file");
write_u32(&mut raw, 1);
write_u32(&mut raw, 2);
let payload = decode(&raw);
assert_eq!(
payload,
PanicPayload {
msg: Some("msg".to_string()),
file: Some("file".to_string()),
line: Some(1),
col: Some(2),
}
);
}
#[test]
fn only_msg() {
let mut raw = Vec::new();
write_bytes(&mut raw, b"msg");
let payload = decode(&raw);
assert_eq!(
payload,
PanicPayload {
msg: Some("msg".to_string()),
file: None,
line: None,
col: None,
}
);
}
#[test]
fn invalid_utf8() {
let mut raw = Vec::new();
write_bytes(&mut raw, b"\xF0\x90\x80msg");
write_bytes(&mut raw, b"file");
write_u32(&mut raw, 1);
write_u32(&mut raw, 2);
let payload = decode(&raw);
assert_eq!(
payload,
PanicPayload {
msg: Some("<EFBFBD>msg".to_string()),
file: Some("file".to_string()),
line: Some(1),
col: Some(2),
}
);
}
#[test]
fn trailing_data() {
let mut raw = Vec::new();
write_bytes(&mut raw, b"msg");
write_bytes(&mut raw, b"file");
write_u32(&mut raw, 1);
write_u32(&mut raw, 2);
write_u32(&mut raw, 0xdeadbeef);
let payload = decode(&raw);
assert_eq!(
payload,
PanicPayload {
msg: Some("msg".to_string()),
file: Some("file".to_string()),
line: Some(1),
col: Some(2),
}
);
}
#[test]
fn empty_str_is_none() {
let mut raw = Vec::new();
write_bytes(&mut raw, b"msg");
write_bytes(&mut raw, b"");
let payload = decode(&raw);
assert_eq!(
payload,
PanicPayload {
msg: Some("msg".to_string()),
file: None,
line: None,
col: None,
}
);
}
}

View File

@ -21,6 +21,7 @@ use std::sync::Arc;
use byteorder::{LittleEndian, ByteOrder};
use vm;
use panic_payload;
use parity_wasm::interpreter;
use wasm_utils::rules;
use bigint::prelude::U256;
@ -55,6 +56,8 @@ pub enum UserTrap {
Unknown,
/// Passed string had invalid utf-8 encoding
BadUtf8,
/// Log event error
Log,
/// Other error in native code
Other,
/// Panic with message
@ -75,6 +78,7 @@ impl ::std::fmt::Display for UserTrap {
UserTrap::AllocationFailed => write!(f, "Memory allocation failed (OOM)"),
UserTrap::BadUtf8 => write!(f, "String encoding is bad utf-8 sequence"),
UserTrap::GasLimit => write!(f, "Invocation resulted in gas limit violated"),
UserTrap::Log => write!(f, "Error occured while logging an event"),
UserTrap::Other => write!(f, "Other unspecified error"),
UserTrap::Panic(ref msg) => write!(f, "Panic: {}", msg),
}
@ -164,7 +168,7 @@ impl<'a, 'b> Runtime<'a, 'b> {
self.ext.set_storage(key, val).map_err(|_| UserTrap::StorageUpdateError)?;
Ok(Some(0i32.into()))
Ok(None)
}
/// Read from the storage to wasm memory
@ -180,7 +184,7 @@ impl<'a, 'b> Runtime<'a, 'b> {
self.memory.set(val_ptr as u32, &*val)?;
Ok(Some(0.into()))
Ok(None)
}
/// Fetches balance for address
@ -232,6 +236,21 @@ impl<'a, 'b> Runtime<'a, 'b> {
}
}
pub fn overflow_charge<F>(&mut self, f: F) -> Result<(), InterpreterError>
where F: FnOnce(&vm::Schedule) -> Option<u64>
{
let amount = match f(self.ext.schedule()) {
Some(amount) => amount,
None => { return Err(UserTrap::GasLimit.into()); }
};
if !self.charge_gas(amount as u64) {
Err(UserTrap::GasLimit.into())
} else {
Ok(())
}
}
/// Invoke create in the state runtime
pub fn create(&mut self, context: InterpreterCallerContext)
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
@ -542,43 +561,92 @@ impl<'a, 'b> Runtime<'a, 'b> {
fn mem_copy(&mut self, context: InterpreterCallerContext)
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
{
//
// method signature:
// fn memcpy(dest: *const u8, src: *const u8, len: u32) -> *mut u8;
//
let len = context.value_stack.pop_as::<i32>()? as u32;
let dst = context.value_stack.pop_as::<i32>()? as u32;
let src = context.value_stack.pop_as::<i32>()? as u32;
let dst = context.value_stack.pop_as::<i32>()? as u32;
self.charge(|schedule| schedule.wasm.mem_copy as u64 * len as u64)?;
let mem = self.memory().get(src, len as usize)?;
self.memory().set(dst, &mem)?;
self.memory().copy_nonoverlapping(src as usize, dst as usize, len as usize)?;
Ok(Some(0i32.into()))
Ok(Some(Into::into(dst as i32)))
}
fn bswap_32(x: u32) -> u32 {
x >> 24 | x >> 8 & 0xff00 | x << 8 & 0xff0000 | x << 24
fn mem_move(&mut self, context: InterpreterCallerContext)
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
{
//
// method signature:
// fn memmove(dest: *const u8, src: *const u8, len: u32) -> *mut u8;
//
let len = context.value_stack.pop_as::<i32>()? as u32;
let src = context.value_stack.pop_as::<i32>()? as u32;
let dst = context.value_stack.pop_as::<i32>()? as u32;
self.charge(|schedule| schedule.wasm.mem_move as u64 * len as u64)?;
self.memory().copy(src as usize, dst as usize, len as usize)?;
Ok(Some(Into::into(dst as i32)))
}
fn mem_set(&mut self, context: InterpreterCallerContext)
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
{
//
// method signature:
// fn memset(dest: *const u8, c: u32, len: u32) -> *mut u8;
//
let len = context.value_stack.pop_as::<i32>()? as u32;
let c = context.value_stack.pop_as::<i32>()? as u32;
let dst = context.value_stack.pop_as::<i32>()? as u32;
self.charge(|schedule| schedule.wasm.mem_set as u64 * len as u64)?;
self.memory().clear(dst as usize, c as u8, len as usize)?;
Ok(Some(Into::into(dst as i32)))
}
fn bitswap_i64(&mut self, context: InterpreterCallerContext)
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
{
let x1 = context.value_stack.pop_as::<i32>()?;
let x2 = context.value_stack.pop_as::<i32>()?;
let x = context.value_stack.pop_as::<i64>()?;
let result = x.swap_bytes();
let result = ((Runtime::bswap_32(x2 as u32) as u64) << 32
| Runtime::bswap_32(x1 as u32) as u64) as i64;
self.return_i64(result)
Ok(Some(result.into()))
}
fn user_panic(&mut self, context: InterpreterCallerContext)
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
{
let msg_len = context.value_stack.pop_as::<i32>()? as u32;
let msg_ptr = context.value_stack.pop_as::<i32>()? as u32;
let msg = String::from_utf8(self.memory.get(msg_ptr, msg_len as usize)?)
.map_err(|_| UserTrap::BadUtf8)?;
let payload_len = context.value_stack.pop_as::<i32>()? as u32;
let payload_ptr = context.value_stack.pop_as::<i32>()? as u32;
let raw_payload = self.memory.get(payload_ptr, payload_len as usize)?;
let payload = panic_payload::decode(&raw_payload);
let msg = format!(
"{msg}, {file}:{line}:{col}",
msg = payload
.msg
.as_ref()
.map(String::as_ref)
.unwrap_or("<msg was stripped>"),
file = payload
.file
.as_ref()
.map(String::as_ref)
.unwrap_or("<unknown>"),
line = payload.line.unwrap_or(0),
col = payload.col.unwrap_or(0)
);
trace!(target: "wasm", "Contract custom panic message: {}", msg);
Err(UserTrap::Panic(msg).into())
@ -588,19 +656,16 @@ impl<'a, 'b> Runtime<'a, 'b> {
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
{
let return_ptr = context.value_stack.pop_as::<i32>()? as u32;
let block_hi = context.value_stack.pop_as::<i32>()? as u32;
let block_lo = context.value_stack.pop_as::<i32>()? as u32;
let block_num = context.value_stack.pop_as::<i64>()? as u64;
self.charge(|schedule| schedule.blockhash_gas as u64)?;
let block_num = (block_hi as u64) << 32 | block_lo as u64;
trace!("Requesting block hash for block #{}", block_num);
let hash = self.ext.blockhash(&U256::from(block_num));
self.memory.set(return_ptr, &*hash)?;
Ok(Some(0i32.into()))
Ok(None)
}
fn return_address_ptr(&mut self, ptr: u32, val: Address) -> Result<(), InterpreterError>
@ -676,14 +741,14 @@ impl<'a, 'b> Runtime<'a, 'b> {
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
{
let timestamp = self.ext.env_info().timestamp as i64;
self.return_i64(timestamp)
Ok(Some(timestamp.into()))
}
fn block_number(&mut self, _context: InterpreterCallerContext)
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
{
let block_number: u64 = self.ext.env_info().number.into();
self.return_i64(block_number as i64)
let block_number = self.ext.env_info().number as i64;
Ok(Some(block_number.into()))
}
fn difficulty(&mut self, context: InterpreterCallerContext)
@ -708,25 +773,6 @@ impl<'a, 'b> Runtime<'a, 'b> {
Ok(None)
}
fn return_i64(&mut self, val: i64) -> Result<Option<interpreter::RuntimeValue>, InterpreterError> {
self.charge(|schedule| schedule.wasm.static_u64 as u64)?;
let uval = val as u64;
let hi = (uval >> 32) as i32;
let lo = (uval << 32 >> 32) as i32;
let target = self.instance.module("contract").ok_or(UserTrap::Other)?;
target.execute_export(
"setTempRet0",
self.execution_params().add_argument(
interpreter::RuntimeValue::I32(hi).into()
),
)?;
Ok(Some(
(lo).into()
))
}
pub fn execution_params(&mut self) -> interpreter::ExecutionParams<UserTrap> {
use super::env;
@ -749,6 +795,44 @@ impl<'a, 'b> Runtime<'a, 'b> {
pub fn ext(&mut self) -> &mut vm::Ext {
self.ext
}
pub fn log(&mut self, context: InterpreterCallerContext)
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
{
// signature is:
// pub fn elog(topic_ptr: *const u8, topic_count: u32, data_ptr: *const u8, data_len: u32);
let data_len = context.value_stack.pop_as::<i32>()? as u32;
let data_ptr = context.value_stack.pop_as::<i32>()? as u32;
let topic_count = context.value_stack.pop_as::<i32>()? as u32;
let topic_ptr = context.value_stack.pop_as::<i32>()? as u32;
if topic_count > 4 {
return Err(UserTrap::Log.into());
}
self.overflow_charge(|schedule|
{
let topics_gas = schedule.log_gas as u64 + schedule.log_topic_gas as u64 * topic_count as u64;
(schedule.log_data_gas as u64)
.checked_mul(schedule.log_data_gas as u64)
.and_then(|data_gas| data_gas.checked_add(topics_gas))
}
)?;
let mut topics: Vec<H256> = Vec::with_capacity(topic_count as usize);
topics.resize(topic_count as usize, H256::zero());
for i in 0..topic_count {
let offset = i.checked_mul(32).ok_or(UserTrap::MemoryAccessViolation)?
.checked_add(topic_ptr).ok_or(UserTrap::MemoryAccessViolation)?;
*topics.get_mut(i as usize)
.expect("topics is resized to `topic_count`, i is in 0..topic count iterator, get_mut uses i as an indexer, get_mut cannot fail; qed")
= H256::from(&self.memory.get(offset, 32)?[..]);
}
self.ext.log(topics, &self.memory.get(data_ptr, data_len as usize)?).map_err(|_| UserTrap::Log)?;
Ok(None)
}
}
impl<'a, 'b> interpreter::UserFunctionExecutor<UserTrap> for Runtime<'a, 'b> {
@ -756,10 +840,10 @@ impl<'a, 'b> interpreter::UserFunctionExecutor<UserTrap> for Runtime<'a, 'b> {
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
{
match name {
"_malloc" => {
"_ext_malloc" => {
self.malloc(context)
},
"_free" => {
"_ext_free" => {
// Since it is arena allocator, free does nothing
// todo: update if changed
self.user_noop(context)
@ -797,6 +881,15 @@ impl<'a, 'b> interpreter::UserFunctionExecutor<UserTrap> for Runtime<'a, 'b> {
"_emscripten_memcpy_big" => {
self.mem_copy(context)
},
"_ext_memcpy" => {
self.mem_copy(context)
},
"_ext_memmove" => {
self.mem_move(context)
},
"_ext_memset" => {
self.mem_set(context)
},
"_llvm_bswap_i64" => {
self.bitswap_i64(context)
},
@ -833,6 +926,9 @@ impl<'a, 'b> interpreter::UserFunctionExecutor<UserTrap> for Runtime<'a, 'b> {
"_value" => {
self.value(context)
},
"_elog" => {
self.log(context)
},
_ => {
trace!(target: "wasm", "Trapped due to unhandled function: '{}'", name);
Ok(self.unknown_trap(context)?)

View File

@ -60,7 +60,7 @@ fn empty() {
test_finalize(interpreter.exec(params, &mut ext)).unwrap()
};
assert_eq!(gas_left, U256::from(99_976));
assert_eq!(gas_left, U256::from(99_982));
}
// This test checks if the contract deserializes payload header properly.
@ -89,7 +89,6 @@ fn logger() {
test_finalize(interpreter.exec(params, &mut ext)).unwrap()
};
assert_eq!(gas_left, U256::from(15_177));
let address_val: H256 = address.into();
assert_eq!(
ext.store.get(&"0100000000000000000000000000000000000000000000000000000000000000".parse().unwrap()).expect("storage key to exist"),
@ -113,6 +112,7 @@ fn logger() {
U256::from(1_000_000_000),
"Logger sets 0x04 key to the trasferred value"
);
assert_eq!(gas_left, U256::from(19_147));
}
// This test checks if the contract can allocate memory and pass pointer to the result stream properly.
@ -142,13 +142,12 @@ fn identity() {
}
};
assert_eq!(gas_left, U256::from(99_695));
assert_eq!(
Address::from_slice(&result),
sender,
"Idenity test contract does not return the sender passed"
);
assert_eq!(gas_left, U256::from(99_844));
}
// Dispersion test sends byte array and expect the contract to 'disperse' the original elements with
@ -176,12 +175,12 @@ fn dispersion() {
}
};
assert_eq!(gas_left, U256::from(96_543));
assert_eq!(
result,
vec![0u8, 0, 125, 11, 197, 7, 255, 8, 19, 0]
);
assert_eq!(gas_left, U256::from(96_393));
}
#[test]
@ -205,12 +204,11 @@ fn suicide_not() {
}
};
assert_eq!(gas_left, U256::from(96_822));
assert_eq!(
result,
vec![0u8]
);
assert_eq!(gas_left, U256::from(96_725));
}
#[test]
@ -241,8 +239,8 @@ fn suicide() {
}
};
assert_eq!(gas_left, U256::from(96_580));
assert!(ext.suicides.contains(&refund));
assert_eq!(gas_left, U256::from(96_687));
}
#[test]
@ -272,7 +270,7 @@ fn create() {
assert!(ext.calls.contains(
&FakeCall {
call_type: FakeCallType::Create,
gas: U256::from(62_324),
gas: U256::from(65_899),
sender_address: None,
receive_address: None,
value: Some(1_000_000_000.into()),
@ -280,7 +278,7 @@ fn create() {
code_address: None,
}
));
assert_eq!(gas_left, U256::from(62_289));
assert_eq!(gas_left, U256::from(65_892));
}
@ -314,7 +312,7 @@ fn call_code() {
assert!(ext.calls.contains(
&FakeCall {
call_type: FakeCallType::Call,
gas: U256::from(95_585),
gas: U256::from(98_713),
sender_address: Some(sender),
receive_address: Some(receiver),
value: None,
@ -322,11 +320,11 @@ fn call_code() {
code_address: Some("0d13710000000000000000000000000000000000".parse().unwrap()),
}
));
assert_eq!(gas_left, U256::from(90_665));
// siphash result
let res = LittleEndian::read_u32(&result[..]);
assert_eq!(res, 4198595614);
assert_eq!(gas_left, U256::from(93_855));
}
#[test]
@ -359,7 +357,7 @@ fn call_static() {
assert!(ext.calls.contains(
&FakeCall {
call_type: FakeCallType::Call,
gas: U256::from(95_585),
gas: U256::from(98_713),
sender_address: Some(sender),
receive_address: Some(receiver),
value: None,
@ -367,11 +365,12 @@ fn call_static() {
code_address: Some("13077bfb00000000000000000000000000000000".parse().unwrap()),
}
));
assert_eq!(gas_left, U256::from(90_665));
// siphash result
let res = LittleEndian::read_u32(&result[..]);
assert_eq!(res, 317632590);
assert_eq!(gas_left, U256::from(93_855));
}
// Realloc test
@ -393,8 +392,8 @@ fn realloc() {
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
}
};
assert_eq!(gas_left, U256::from(96_811));
assert_eq!(result, vec![0u8; 2]);
assert_eq!(gas_left, U256::from(96_723));
}
// Tests that contract's ability to read from a storage
@ -419,8 +418,8 @@ fn storage_read() {
}
};
assert_eq!(gas_left, U256::from(96_645));
assert_eq!(Address::from(&result[12..32]), address);
assert_eq!(gas_left, U256::from(99_767));
}
// Tests keccak calculation
@ -446,9 +445,97 @@ fn keccak() {
};
assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87"));
assert_eq!(gas_left, U256::from(80_452));
assert_eq!(gas_left, U256::from(81_446));
}
// memcpy test.
#[test]
fn memcpy() {
::ethcore_logger::init_log();
let code = load_sample!("mem.wasm");
let mut test_payload = Vec::with_capacity(8192);
for i in 0..8192 {
test_payload.push((i % 255) as u8);
}
let mut data = vec![0u8];
data.extend(&test_payload);
let mut params = ActionParams::default();
params.gas = U256::from(100_000);
params.code = Some(Arc::new(code));
params.data = Some(data);
let mut ext = FakeExt::new();
let (gas_left, result) = {
let mut interpreter = wasm_interpreter();
let result = interpreter.exec(params, &mut ext).expect("Interpreter to execute without any errors");
match result {
GasLeft::Known(_) => { panic!("mem should return payload"); },
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
}
};
assert_eq!(result, test_payload);
assert_eq!(gas_left, U256::from(72_216));
}
// memmove test.
#[test]
fn memmove() {
::ethcore_logger::init_log();
let code = load_sample!("mem.wasm");
let mut test_payload = Vec::with_capacity(8192);
for i in 0..8192 {
test_payload.push((i % 255) as u8);
}
let mut data = vec![1u8];
data.extend(&test_payload);
let mut params = ActionParams::default();
params.gas = U256::from(100_000);
params.code = Some(Arc::new(code));
params.data = Some(data);
let mut ext = FakeExt::new();
let (gas_left, result) = {
let mut interpreter = wasm_interpreter();
let result = interpreter.exec(params, &mut ext).expect("Interpreter to execute without any errors");
match result {
GasLeft::Known(_) => { panic!("mem should return payload"); },
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
}
};
assert_eq!(result, test_payload);
assert_eq!(gas_left, U256::from(72_216));
}
// memset test
#[test]
fn memset() {
::ethcore_logger::init_log();
let code = load_sample!("mem.wasm");
let mut params = ActionParams::default();
params.gas = U256::from(100_000);
params.code = Some(Arc::new(code));
params.data = Some(vec![2u8, 228u8]);
let mut ext = FakeExt::new();
let (gas_left, result) = {
let mut interpreter = wasm_interpreter();
let result = interpreter.exec(params, &mut ext).expect("Interpreter to execute without any errors");
match result {
GasLeft::Known(_) => { panic!("mem should return payload"); },
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
}
};
assert_eq!(result, vec![228u8; 8192]);
assert_eq!(gas_left, U256::from(72_196));
}
macro_rules! reqrep_test {
($name: expr, $input: expr) => {
@ -500,11 +587,11 @@ fn math_add() {
}
).expect("Interpreter to execute without any errors");
assert_eq!(gas_left, U256::from(94_666));
assert_eq!(
U256::from_dec_str("1888888888888888888888888888887").unwrap(),
(&result[..]).into()
);
assert_eq!(gas_left, U256::from(95_524));
}
// multiplication
@ -522,11 +609,11 @@ fn math_mul() {
}
).expect("Interpreter to execute without any errors");
assert_eq!(gas_left, U256::from(93_719));
assert_eq!(
U256::from_dec_str("888888888888888888888888888887111111111111111111111111111112").unwrap(),
(&result[..]).into()
);
assert_eq!(gas_left, U256::from(94_674));
}
// subtraction
@ -544,11 +631,11 @@ fn math_sub() {
}
).expect("Interpreter to execute without any errors");
assert_eq!(gas_left, U256::from(94_718));
assert_eq!(
U256::from_dec_str("111111111111111111111111111111").unwrap(),
(&result[..]).into()
);
assert_eq!(gas_left, U256::from(95_516));
}
// subtraction with overflow
@ -566,7 +653,10 @@ fn math_sub_with_overflow() {
}
);
assert_eq!(result, Err(vm::Error::Wasm("Wasm runtime error: User(Panic(\"arithmetic operation overflow\"))".into())));
match result {
Err(vm::Error::Wasm(_)) => {},
_ => panic!("Unexpected result {:?}", result),
}
}
#[test]
@ -583,11 +673,11 @@ fn math_div() {
}
).expect("Interpreter to execute without any errors");
assert_eq!(gas_left, U256::from(86_996));
assert_eq!(
U256::from_dec_str("1125000").unwrap(),
(&result[..]).into()
);
assert_eq!(gas_left, U256::from(88_514));
}
// This test checks the ability of wasm contract to invoke
@ -675,12 +765,11 @@ fn externs() {
"Gas limit requested and returned does not match"
);
assert_eq!(gas_left, U256::from(91_857));
assert_eq!(gas_left, U256::from(94_858));
}
#[test]
fn embedded_keccak() {
::ethcore_logger::init_log();
let mut code = load_sample!("keccak.wasm");
code.extend_from_slice(b"something");
@ -702,5 +791,40 @@ fn embedded_keccak() {
};
assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87"));
assert_eq!(gas_left, U256::from(80_452));
assert_eq!(gas_left, U256::from(81_446));
}
/// This test checks the correctness of log extern
/// Target test puts one event with two topic [keccak(input), reverse(keccak(input))]
/// and reversed input as a data
#[test]
fn events() {
::ethcore_logger::init_log();
let code = load_sample!("events.wasm");
let mut params = ActionParams::default();
params.gas = U256::from(100_000);
params.code = Some(Arc::new(code));
params.data = Some(b"something".to_vec());
let mut ext = FakeExt::new();
let (gas_left, result) = {
let mut interpreter = wasm_interpreter();
let result = interpreter.exec(params, &mut ext).expect("Interpreter to execute without any errors");
match result {
GasLeft::Known(_) => { panic!("events should return payload"); },
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
}
};
assert_eq!(ext.logs.len(), 1);
let log_entry = &ext.logs[0];
assert_eq!(log_entry.topics.len(), 2);
assert_eq!(&log_entry.topics[0], &H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87"));
assert_eq!(&log_entry.topics[1], &H256::from("871d5ea37430753faab7dff7a7187783517d83bd822c02e28a164c887e1d3768"));
assert_eq!(&log_entry.data, b"gnihtemos");
assert_eq!(&result, b"gnihtemos");
assert_eq!(gas_left, U256::from(79_637));
}

View File

@ -1,14 +1,17 @@
[package]
name = "ethkey"
version = "0.2.0"
version = "0.3.0"
authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
rand = "0.3.14"
lazy_static = "0.2"
tiny-keccak = "1.3"
eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1" }
rustc-hex = "1.0"
ethcore-bigint = { path = "../util/bigint" }
rust-crypto = "0.2"
byteorder = "1.0"
edit-distance = "2.0"
eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1" }
ethcore-bigint = { path = "../util/bigint" }
lazy_static = "0.2"
log = "0.3"
parity-wordlist = "1.2"
rand = "0.3.14"
rust-crypto = "0.2"
rustc-hex = "1.0"
tiny-keccak = "1.3"

View File

@ -16,13 +16,13 @@ Ethereum keys generator.
Copyright 2016, 2017 Parity Technologies (UK) Ltd
Usage:
ethkey info <secret> [options]
ethkey info <secret-or-phrase> [options]
ethkey generate random [options]
ethkey generate prefix <prefix> <iterations> [options]
ethkey generate brain <seed> [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:
@ -30,15 +30,15 @@ Options:
-s, --secret Display only the secret.
-p, --public Display only the public.
-a, --address Display only the address.
-b, --brain Use parity brain wallet algorithm.
Commands:
info Display public and address of the secret.
generate Generates new ethereum key.
random Random generation.
prefix Random generation, but address must start with a prefix
brain Generate new key from string seed.
generate random Generates new random ethereum key.
generate prefix Random generation, but address must start with a prefix.
sign Sign message using secret.
verify Verify signer of the signature.
recover Try to find brain phrase matching given address from partial phrase.
```
### Examples
@ -60,20 +60,22 @@ address: 26d1ec50b4e62c1d1a40d16e7cacc6a6580757d5
--
#### `generate brain <seed>`
*Generate new brain-wallet keypair using 16384 iterations.*
- `<seed>` - brain-wallet seed, any string
#### `info --brain <phrase>`
*Display info about private key generate from brain wallet recovery phrase.*
- `<phrase>` - Parity recovery phrase, 12 words
```
ethkey generate brain "this is sparta"
ethkey info --brain "this is sparta"
```
```
secret: 17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55
public: 689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124
address: 26d1ec50b4e62c1d1a40d16e7cacc6a6580757d5
The recover phrase was not generated by Parity: The word 'this' does not come from the dictionary.
secret: aa22b54c0cb43ee30a014afe5ef3664b1cde299feabca46cd3167a85a57c39f2
public: c4c5398da6843632c123f543d714d2d2277716c11ff612b2a2f23c6bda4d6f0327c31cd58c55a9572c3cc141dade0c32747a13b7ef34c241b26c84adbb28fcf4
address: 006e27b6a72e1f34c626762f3c4761547aff1421
```
--
@ -93,14 +95,30 @@ address: a8fa5dd30a87bb9e3288d604eb74949c515ab66e
--
#### `generate prefix <prefix> <iterations>`
#### `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.
- `<iterations>` - maximum number of tries before generation is assumed to be a failure.
```
ethkey generate prefix ff 1000
ethkey generate prefix ff
```
```
@ -111,6 +129,24 @@ 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.*
@ -159,9 +195,32 @@ ethkey verify address 689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6
true
```
--
# Ethcore toolchain
*this project is a part of the ethcore toolchain*
#### `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 toolchain
*this project is a part of the parity toolchain*
- [**ethkey**](https://github.com/paritytech/ethkey) - Ethereum keys generator and signer.
- [**ethstore**](https://github.com/paritytech/ethstore) - Ethereum key management.

View File

@ -4,12 +4,15 @@ version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
docopt = "0.8"
env_logger = "0.4"
ethkey = { path = "../" }
panic_hook = { path = "../../panic_hook" }
parity-wordlist="1.2"
rustc-hex = "1.0"
serde = "1.0"
serde_derive = "1.0"
rustc-hex = "1.0"
docopt = "0.8"
panic_hook = { path = "../../panic_hook" }
threadpool = "1.7"
[[bin]]
name = "ethkey"

View File

@ -15,32 +15,36 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
extern crate docopt;
extern crate rustc_hex;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate env_logger;
extern crate ethkey;
extern crate panic_hook;
extern crate parity_wordlist;
extern crate rustc_hex;
extern crate serde;
extern crate threadpool;
#[macro_use]
extern crate serde_derive;
use std::{env, fmt, process};
use std::num::ParseIntError;
use std::{env, fmt, process, io, sync};
use docopt::Docopt;
use ethkey::{KeyPair, Random, Brain, BrainPrefix, Prefix, Error as EthkeyError, Generator, sign, verify_public, verify_address, brain_recover};
use rustc_hex::{ToHex, FromHex, FromHexError};
use ethkey::{KeyPair, Random, Brain, Prefix, Error as EthkeyError, Generator, sign, verify_public, verify_address};
use std::io;
pub const USAGE: &'static str = r#"
Ethereum keys generator.
Copyright 2016, 2017 Parity Technologies (UK) Ltd
Usage:
ethkey info <secret> [options]
ethkey info <secret-or-phrase> [options]
ethkey generate random [options]
ethkey generate prefix <prefix> <iterations> [options]
ethkey generate brain <seed> [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:
@ -48,15 +52,15 @@ Options:
-s, --secret Display only the secret.
-p, --public Display only the public.
-a, --address Display only the address.
-b, --brain Use parity brain wallet algorithm.
Commands:
info Display public and address of the secret.
generate Generates new ethereum key.
random Random generation.
prefix Random generation, but address must start with a prefix
brain Generate new key from string seed.
generate random Generates new random ethereum key.
generate prefix Random generation, but address must start with a prefix.
sign Sign message using secret.
verify Verify signer of the signature.
recover Try to find brain phrase matching given address from partial phrase.
"#;
#[derive(Debug, Deserialize)]
@ -65,15 +69,15 @@ struct Args {
cmd_generate: bool,
cmd_random: bool,
cmd_prefix: bool,
cmd_brain: bool,
cmd_sign: bool,
cmd_verify: bool,
cmd_public: bool,
cmd_address: bool,
cmd_recover: bool,
arg_prefix: String,
arg_iterations: String,
arg_seed: String,
arg_secret: String,
arg_secret_or_phrase: String,
arg_known_phrase: String,
arg_message: String,
arg_public: String,
arg_address: String,
@ -81,6 +85,7 @@ struct Args {
flag_secret: bool,
flag_public: bool,
flag_address: bool,
flag_brain: bool,
}
#[derive(Debug)]
@ -157,6 +162,7 @@ impl DisplayMode {
fn main() {
panic_hook::set();
env_logger::init().expect("Logger initialized only once.");
match execute(env::args()) {
Ok(ok) => println!("{}", ok),
@ -167,9 +173,13 @@ fn main() {
}
}
fn display(keypair: KeyPair, mode: DisplayMode) -> String {
fn display(result: (KeyPair, Option<String>), mode: DisplayMode) -> String {
let keypair = result.0;
match mode {
DisplayMode::KeyPair => format!("{}", keypair),
DisplayMode::KeyPair => match result.1 {
Some(extra_data) => format!("{}\n{}", extra_data, keypair),
None => format!("{}", keypair)
},
DisplayMode::Secret => format!("{}", keypair.secret().to_hex()),
DisplayMode::Public => format!("{:?}", keypair.public()),
DisplayMode::Address => format!("{:?}", keypair.address()),
@ -182,23 +192,53 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
return if args.cmd_info {
let display_mode = DisplayMode::new(&args);
let secret = args.arg_secret.parse().map_err(|_| EthkeyError::InvalidSecret)?;
let keypair = KeyPair::from_secret(secret)?;
Ok(display(keypair, display_mode))
let result = if args.flag_brain {
let phrase = args.arg_secret_or_phrase;
let phrase_info = validate_phrase(&phrase);
let keypair = Brain::new(phrase).generate().expect("Brain wallet generator is infallible; qed");
(keypair, Some(phrase_info))
} else {
let secret = args.arg_secret_or_phrase.parse().map_err(|_| EthkeyError::InvalidSecret)?;
(KeyPair::from_secret(secret)?, None)
};
Ok(display(result, display_mode))
} else if args.cmd_generate {
let display_mode = DisplayMode::new(&args);
let keypair = if args.cmd_random {
Random.generate()?
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 iterations = usize::from_str_radix(&args.arg_iterations, 10)?;
Prefix::new(prefix, iterations).generate()?
} else if args.cmd_brain {
Brain::new(args.arg_seed).generate().expect("Brain wallet generator is infallible; qed")
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 {
unreachable!();
let result = Prefix::new(prefix, iterations).generate();
result.map(|res| (res, None))
};
Ok(display(keypair, display_mode))
Ok(res.map(Some).unwrap_or(None))
}
})?
} else {
return Ok(format!("{}", USAGE))
};
Ok(display(result, display_mode))
} else if args.cmd_sign {
let secret = args.arg_secret.parse().map_err(|_| EthkeyError::InvalidSecret)?;
let message = args.arg_message.parse().map_err(|_| EthkeyError::InvalidMessage)?;
@ -214,12 +254,87 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
let address = args.arg_address.parse().map_err(|_| EthkeyError::InvalidAddress)?;
verify_address(&address, &signature, &message)?
} else {
unreachable!();
return Ok(format!("{}", USAGE))
};
Ok(format!("{}", ok))
} else {
unreachable!();
} 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)]
@ -242,13 +357,15 @@ address: 26d1ec50b4e62c1d1a40d16e7cacc6a6580757d5".to_owned();
#[test]
fn brain() {
let command = vec!["ethkey", "generate", "brain", "this is sparta"]
let command = vec!["ethkey", "info", "--brain", "this is sparta"]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();
let expected =
"secret: aa22b54c0cb43ee30a014afe5ef3664b1cde299feabca46cd3167a85a57c39f2
"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);
@ -256,7 +373,7 @@ address: 006e27b6a72e1f34c626762f3c4761547aff1421".to_owned();
#[test]
fn secret() {
let command = vec!["ethkey", "generate", "brain", "this is sparta", "--secret"]
let command = vec!["ethkey", "info", "--brain", "this is sparta", "--secret"]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();
@ -267,7 +384,7 @@ address: 006e27b6a72e1f34c626762f3c4761547aff1421".to_owned();
#[test]
fn public() {
let command = vec!["ethkey", "generate", "brain", "this is sparta", "--public"]
let command = vec!["ethkey", "info", "--brain", "this is sparta", "--public"]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();
@ -278,7 +395,7 @@ address: 006e27b6a72e1f34c626762f3c4761547aff1421".to_owned();
#[test]
fn address() {
let command = vec!["ethkey", "generate", "brain", "this is sparta", "--address"]
let command = vec!["ethkey", "info", "-b", "this is sparta", "--address"]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();

View File

@ -16,6 +16,7 @@
use keccak::Keccak256;
use super::{KeyPair, Generator, Secret};
use parity_wordlist;
/// Simple brainwallet.
pub struct Brain(String);
@ -24,13 +25,17 @@ impl Brain {
pub fn new(s: String) -> Self {
Brain(s)
}
pub fn validate_phrase(phrase: &str, expected_words: usize) -> Result<(), ::WordlistError> {
parity_wordlist::validate_phrase(phrase, expected_words)
}
}
impl Generator for Brain {
type Error = ::Void;
fn generate(self) -> Result<KeyPair, Self::Error> {
let seed = self.0;
fn generate(&mut self) -> Result<KeyPair, Self::Error> {
let seed = self.0.clone();
let mut secret = seed.into_bytes().keccak256();
let mut i = 0;
@ -43,7 +48,10 @@ impl Generator for Brain {
if let Ok(pair) = Secret::from_unsafe_slice(&secret)
.and_then(KeyPair::from_secret)
{
if pair.address()[0] == 0 { return Ok(pair) }
if pair.address()[0] == 0 {
trace!("Testing: {}, got: {:?}", self.0, pair.address());
return Ok(pair)
}
}
},
}

View File

@ -0,0 +1,70 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use super::{Generator, KeyPair, Error, Brain};
use parity_wordlist as wordlist;
/// Tries to find brain-seed keypair with address starting with given prefix.
pub struct BrainPrefix {
prefix: Vec<u8>,
iterations: usize,
no_of_words: usize,
last_phrase: String,
}
impl BrainPrefix {
pub fn new(prefix: Vec<u8>, iterations: usize, no_of_words: usize) -> Self {
BrainPrefix {
prefix,
iterations,
no_of_words,
last_phrase: String::new(),
}
}
pub fn phrase(&self) -> &str {
&self.last_phrase
}
}
impl Generator for BrainPrefix {
type Error = Error;
fn generate(&mut self) -> Result<KeyPair, Error> {
for _ in 0..self.iterations {
let phrase = wordlist::random_phrase(self.no_of_words);
let keypair = Brain::new(phrase.clone()).generate().unwrap();
if keypair.address().starts_with(&self.prefix) {
self.last_phrase = phrase;
return Ok(keypair)
}
}
Err(Error::Custom("Could not find keypair".into()))
}
}
#[cfg(test)]
mod tests {
use {Generator, BrainPrefix};
#[test]
fn prefix_generator() {
let prefix = vec![0x00u8];
let keypair = BrainPrefix::new(prefix.clone(), usize::max_value(), 12).generate().unwrap();
assert!(keypair.address().starts_with(&prefix));
}
}

175
ethkey/src/brain_recover.rs Normal file
View File

@ -0,0 +1,175 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::collections::HashSet;
use edit_distance::edit_distance;
use parity_wordlist;
use super::{Address, Brain, Generator};
/// Tries to find a phrase for address, given the number
/// of expected words and a partial phrase.
///
/// Returns `None` if phrase couldn't be found.
pub fn brain_recover(
address: &Address,
known_phrase: &str,
expected_words: usize,
) -> Option<String> {
let it = PhrasesIterator::from_known_phrase(known_phrase, expected_words);
for phrase in it {
let keypair = Brain::new(phrase.clone()).generate().expect("Brain wallets are infallible; qed");
trace!("Testing: {}, got: {:?}", phrase, keypair.address());
if &keypair.address() == address {
return Some(phrase);
}
}
None
}
fn generate_substitutions(word: &str) -> Vec<&'static str> {
let mut words = parity_wordlist::WORDS.iter().cloned()
.map(|w| (edit_distance(w, word), w))
.collect::<Vec<_>>();
words.sort_by(|a, b| a.0.cmp(&b.0));
words.into_iter()
.map(|pair| pair.1)
.collect()
}
/// Iterator over possible
pub struct PhrasesIterator {
words: Vec<Vec<&'static str>>,
combinations: u64,
indexes: Vec<usize>,
has_next: bool,
}
impl PhrasesIterator {
pub fn from_known_phrase(known_phrase: &str, expected_words: usize) -> Self {
let known_words = parity_wordlist::WORDS.iter().cloned().collect::<HashSet<_>>();
let mut words = known_phrase.split(' ')
.map(|word| match known_words.get(word) {
None => {
info!("Invalid word '{}', looking for potential substitutions.", word);
let substitutions = generate_substitutions(word);
info!("Closest words: {:?}", &substitutions[..10]);
substitutions
},
Some(word) => vec![*word],
})
.collect::<Vec<_>>();
// add missing words
if words.len() < expected_words {
let to_add = expected_words - words.len();
info!("Number of words is insuficcient adding {} more.", to_add);
for _ in 0..to_add {
words.push(parity_wordlist::WORDS.iter().cloned().collect());
}
}
// start searching
PhrasesIterator::new(words)
}
pub fn new(words: Vec<Vec<&'static str>>) -> Self {
let combinations = words.iter().fold(1u64, |acc, x| acc * x.len() as u64);
let indexes = words.iter().map(|_| 0).collect();
info!("Starting to test {} possible combinations.", combinations);
PhrasesIterator {
words,
combinations,
indexes,
has_next: combinations > 0,
}
}
pub fn combinations(&self) -> u64 {
self.combinations
}
fn current(&self) -> String {
let mut s = self.words[0][self.indexes[0]].to_owned();
for i in 1..self.indexes.len() {
s.push(' ');
s.push_str(self.words[i][self.indexes[i]]);
}
s
}
fn next_index(&mut self) -> bool {
let mut pos = self.indexes.len();
while pos > 0 {
pos -= 1;
self.indexes[pos] += 1;
if self.indexes[pos] >= self.words[pos].len() {
self.indexes[pos] = 0;
} else {
return true;
}
}
false
}
}
impl Iterator for PhrasesIterator {
type Item = String;
fn next(&mut self) -> Option<String> {
if !self.has_next {
return None;
}
let phrase = self.current();
self.has_next = self.next_index();
Some(phrase)
}
}
#[cfg(test)]
mod tests {
use super::PhrasesIterator;
#[test]
fn should_generate_possible_combinations() {
let mut it = PhrasesIterator::new(vec![
vec!["1", "2", "3"],
vec!["test"],
vec!["a", "b", "c"],
]);
assert_eq!(it.combinations(), 9);
assert_eq!(it.next(), Some("1 test a".to_owned()));
assert_eq!(it.next(), Some("1 test b".to_owned()));
assert_eq!(it.next(), Some("1 test c".to_owned()));
assert_eq!(it.next(), Some("2 test a".to_owned()));
assert_eq!(it.next(), Some("2 test b".to_owned()));
assert_eq!(it.next(), Some("2 test c".to_owned()));
assert_eq!(it.next(), Some("3 test a".to_owned()));
assert_eq!(it.next(), Some("3 test b".to_owned()));
assert_eq!(it.next(), Some("3 test c".to_owned()));
assert_eq!(it.next(), None);
}
}

View File

@ -14,18 +14,25 @@
// 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 rand;
extern crate tiny_keccak;
extern crate secp256k1;
extern crate rustc_hex;
extern crate ethcore_bigint as bigint;
extern crate crypto as rcrypto;
// #![warn(missing_docs)]
extern crate byteorder;
extern crate crypto as rcrypto;
extern crate edit_distance;
extern crate ethcore_bigint as bigint;
extern crate parity_wordlist;
extern crate rand;
extern crate rustc_hex;
extern crate secp256k1;
extern crate tiny_keccak;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
mod brain;
mod brain_prefix;
mod error;
mod keypair;
mod keccak;
@ -35,9 +42,12 @@ mod signature;
mod secret;
mod extended;
pub mod brain_recover;
pub mod math;
pub use self::parity_wordlist::Error as WordlistError;
pub use self::brain::Brain;
pub use self::brain_prefix::BrainPrefix;
pub use self::error::Error;
pub use self::keypair::{KeyPair, public_to_address};
pub use self::math::public_is_valid;
@ -62,7 +72,7 @@ pub trait Generator {
type Error;
/// Should be called to generate new keypair.
fn generate(self) -> Result<KeyPair, Self::Error>;
fn generate(&mut self) -> Result<KeyPair, Self::Error>;
}
pub type Address = H160;

View File

@ -34,7 +34,7 @@ impl Prefix {
impl Generator for Prefix {
type Error = Error;
fn generate(self) -> Result<KeyPair, Error> {
fn generate(&mut self) -> Result<KeyPair, Error> {
for _ in 0..self.iterations {
let keypair = Random.generate()?;
if keypair.address().starts_with(&self.prefix) {

View File

@ -23,7 +23,7 @@ pub struct Random;
impl Generator for Random {
type Error = ::std::io::Error;
fn generate(self) -> Result<KeyPair, Self::Error> {
fn generate(&mut self) -> Result<KeyPair, Self::Error> {
let mut rng = OsRng::new()?;
match rng.generate() {
Ok(pair) => Ok(pair),
@ -32,10 +32,10 @@ impl Generator for Random {
}
}
impl<'a> Generator for &'a mut OsRng {
impl Generator for OsRng {
type Error = ::Void;
fn generate(self) -> Result<KeyPair, Self::Error> {
fn generate(&mut self) -> Result<KeyPair, Self::Error> {
let (sec, publ) = SECP256K1.generate_keypair(self)
.expect("context always created with full capabilities; qed");

View File

@ -1,6 +1,6 @@
[package]
name = "ethstore"
version = "0.1.0"
version = "0.2.0"
authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]

View File

@ -21,6 +21,7 @@ Usage:
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]
@ -50,6 +51,7 @@ Commands:
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.
@ -164,6 +166,25 @@ 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]`
@ -318,8 +339,8 @@ OK
--
# Ethcore toolchain
*this project is a part of the ethcore toolchain*
# Parity toolchain
*this project is a part of the parity toolchain*
- [**ethkey**](https://github.com/paritytech/ethkey) - Ethereum keys generator and signer.
- [**ethstore**](https://github.com/paritytech/ethstore) - Ethereum key management.

View File

@ -4,10 +4,12 @@ version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
docopt = "0.8"
num_cpus = "1.6"
rustc-hex = "1.0"
serde = "1.0"
serde_derive = "1.0"
docopt = "0.8"
parking_lot = "0.4"
ethstore = { path = "../" }
panic_hook = { path = "../../panic_hook" }

50
ethstore/cli/src/crack.rs Normal file
View File

@ -0,0 +1,50 @@
use std::{cmp, thread};
use std::sync::Arc;
use std::collections::VecDeque;
use parking_lot::Mutex;
use ethstore::{PresaleWallet, Error};
use num_cpus;
pub fn run(passwords: VecDeque<String>, 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<String>>>, 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);
passwords.lock().clear();
return;
},
_ if counter % 100 == 0 => print!("."),
_ => {},
}
}
}
}

View File

@ -14,21 +14,27 @@
// 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_hex;
extern crate docopt;
extern crate ethstore;
extern crate num_cpus;
extern crate panic_hook;
extern crate parking_lot;
extern crate rustc_hex;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate ethstore;
extern crate panic_hook;
use std::{env, process, fs, fmt};
use std::collections::VecDeque;
use std::io::Read;
use std::{env, process, fs, fmt};
use docopt::Docopt;
use ethstore::ethkey::Address;
use ethstore::dir::{paths, KeyDirectory, RootDiskDirectory};
use ethstore::{EthStore, SimpleSecretStore, SecretStore, import_accounts, PresaleWallet,
SecretVaultRef, StoreAccountRef};
use ethstore::ethkey::Address;
use ethstore::{EthStore, SimpleSecretStore, SecretStore, import_accounts, PresaleWallet, SecretVaultRef, StoreAccountRef};
mod crack;
pub const USAGE: &'static str = r#"
Ethereum key management.
@ -40,6 +46,7 @@ Usage:
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]
@ -69,6 +76,7 @@ Commands:
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.
@ -86,6 +94,7 @@ struct Args {
cmd_list: bool,
cmd_import: bool,
cmd_import_wallet: bool,
cmd_find_wallet_pass: bool,
cmd_remove: bool,
cmd_sign: bool,
cmd_public: bool,
@ -209,8 +218,8 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
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 address = store.insert_account(vault_ref, secret, &password)?;
Ok(format!("0x{:?}", address))
let account_ref = store.insert_account(vault_ref, secret, &password)?;
Ok(format!("0x{:?}", 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)?;
@ -237,8 +246,13 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
let password = load_password(&args.arg_password)?;
let kp = wallet.decrypt(&password)?;
let vault_ref = open_args_vault(&store, &args)?;
let address = store.insert_account(vault_ref, kp.secret().clone(), &password)?;
Ok(format!("0x{:?}", address))
let account_ref = store.insert_account(vault_ref, kp.secret().clone(), &password)?;
Ok(format!("0x{:?}", account_ref.address))
} else if args.cmd_find_wallet_pass {
let passwords = load_password(&args.arg_password)?;
let passwords = passwords.lines().map(str::to_owned).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)?;

View File

@ -14,7 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::iter::repeat;
use std::str;
use ethkey::Secret;
use {json, Error, crypto};
@ -90,9 +89,7 @@ impl Crypto {
// 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::new();
ciphertext.grow(plain_len);
ciphertext.extend(repeat(0).take(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(&derived_left_bits, &iv, plain, &mut *ciphertext);
@ -143,9 +140,7 @@ impl Crypto {
return Err(Error::InvalidPassword);
}
let mut plain: SmallVec<[u8; 32]> = SmallVec::new();
plain.grow(expected_len);
plain.extend(repeat(0).take(expected_len));
let mut plain: SmallVec<[u8; 32]> = SmallVec::from_vec(vec![0; expected_len]);
match self.cipher {
Cipher::Aes128Ctr(ref params) => {

View File

@ -19,7 +19,7 @@ ethcore-bigint = { path = "../util/bigint" }
ethcore-bytes = { path = "../util/bytes" }
parity-reactor = { path = "../util/reactor" }
native-contracts = { path = "../ethcore/native_contracts" }
hash = { path = "../util/hash" }
keccak-hash = { path = "../util/hash" }
[dev-dependencies]
ethabi = "4.0"

View File

@ -25,7 +25,7 @@ extern crate ethcore_util as util;
extern crate ethcore_bigint as bigint;
extern crate ethcore_bytes as bytes;
extern crate futures;
extern crate hash;
extern crate keccak_hash as hash;
extern crate mime;
extern crate mime_guess;
extern crate native_contracts;

View File

@ -7,11 +7,10 @@ authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
ethcore = { path = "../ethcore" }
ethcore-util = { path = "../util" }
ethcore-bigint = { path = "../util/bigint" }
ethcore-bytes = { path = "../util/bytes" }
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.8" }
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.8" }
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.9" }
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.9" }
rlp = { path = "../util/rlp" }
cid = "0.2"
multihash = "0.6"

View File

@ -20,7 +20,6 @@ extern crate unicase;
extern crate rlp;
extern crate ethcore;
extern crate ethcore_util as util;
extern crate ethcore_bigint as bigint;
extern crate ethcore_bytes as bytes;
extern crate jsonrpc_core as core;

View File

@ -1,34 +0,0 @@
# @parity/etherscan
A thin, lightweight promise wrapper for the api.etherscan.io/apis service, exposing a common endpoint for use in JavaScript applications.
[https://github.com/paritytech/parity/tree/master/js/src/3rdparty/etherscan](https://github.com/paritytech/parity/tree/master/js/src/3rdparty/etherscan)
## usage
installation -
```
npm install --save @parity/etherscan
```
Usage -
```
const etherscan = require('@parity/etherscan');
// api calls goes here
```
## api
account (exposed on etherscan.account) -
- `balance(address)`
- `balances(addresses)` (array or addresses)
- `transactions(address, page)` (page offset starts at 0, returns 25)
stats (exposed on etherscan.stats) -
- `price()`
- `supply()`

View File

@ -1,33 +0,0 @@
{
"name": "@parity/etherscan",
"description": "The Parity Promise-based library for interfacing with Etherscan over HTTP",
"version": "0.0.0",
"main": "library.js",
"author": "Parity Team <admin@parity.io>",
"maintainers": [
"Jaco Greeff"
],
"contributors": [],
"license": "GPL-3.0",
"repository": {
"type": "git",
"url": "git+https://github.com/paritytech/parity.git"
},
"keywords": [
"Ethereum",
"ABI",
"API",
"RPC",
"Parity",
"Promise"
],
"scripts": {
},
"devDependencies": {
"chai": "3.5.0",
"mocha": "3.2.0"
},
"dependencies": {
"node-fetch": "~1.6.3"
}
}

View File

@ -1,5 +0,0 @@
# @parity/jsonrpc
JSON and JS interface defintions for RPC calls.
[https://github.com/paritytech/parity/tree/master/js/src/jsonrpc](https://github.com/paritytech/parity/tree/master/js/src/jsonrpc)

View File

@ -1,29 +0,0 @@
{
"name": "@parity/jsonrpc",
"description": "JSON and JS interface defintions for RPC",
"version": "0.0.0",
"main": "library.js",
"author": "Parity Team <admin@parity.io>",
"maintainers": [
"Jaco Greeff"
],
"contributors": [],
"license": "GPL-3.0",
"repository": {
"type": "git",
"url": "git+https://github.com/paritytech/parity.git"
},
"keywords": [
"Ethereum",
"ABI",
"API",
"RPC",
"Parity"
],
"scripts": {
},
"devDependencies": {
},
"dependencies": {
}
}

View File

@ -1,83 +0,0 @@
# @parity/parity.js
Parity.js is a thin, fast, Promise-based wrapper around the Ethereum APIs.
[https://github.com/paritytech/parity/tree/master/js/src/api](https://github.com/paritytech/parity/tree/master/js/src/api)
## installation
Install the package with `npm install --save @parity/parity.js`
## usage
### initialisation
```javascript
// import the actual Api class
import { Api } from '@parity/parity.js';
// do the setup
const transport = new Api.Transport.Http('http://localhost:8545');
const api = new Api(transport);
```
### making calls
perform a call
```javascript
api.eth
.coinbase()
.then((coinbase) => {
console.log(`The coinbase is ${coinbase}`);
});
```
multiple promises
```javascript
Promise
.all([
api.eth.coinbase(),
api.net.listening()
])
.then(([coinbase, listening]) => {
// do stuff here
});
```
chaining promises
```javascript
api.eth
.newFilter({...})
.then((filterId) => api.eth.getFilterChanges(filterId))
.then((changes) => {
console.log(changes);
});
```
### contracts
attach contract
```javascript
const abi = [{ name: 'callMe', inputs: [{ type: 'bool', ...}, { type: 'string', ...}]}, ...abi...];
const address = '0x123456...9abc';
const contract = new api.newContract(abi, address);
```
find & call a function
```javascript
contract.instance
.callMe
.call({ gas: 21000 }, [true, 'someString']) // or estimateGas or postTransaction
.then((result) => {
console.log(`the result was ${result}`);
});
```
## apis
APIs implement the calls as exposed in the [Ethcore JSON Ethereum RPC](https://github.com/paritytech/ethereum-rpc-json/) definitions. Mapping follows the naming conventions of the originals, i.e. `eth_call` becomes `eth.call`, `personal_accounts` becomes `personal.accounts`, etc.

View File

@ -1,33 +0,0 @@
{
"name": "@parity/parity.js",
"description": "The Parity Promise-based API & ABI library for interfacing with Ethereum over RPC",
"version": "0.0.0",
"main": "library.js",
"author": "Parity Team <admin@parity.io>",
"maintainers": [
"Jaco Greeff"
],
"contributors": [],
"license": "GPL-3.0",
"repository": {
"type": "git",
"url": "git+https://github.com/paritytech/parity.git"
},
"keywords": [
"Ethereum",
"ABI",
"API",
"RPC",
"Parity",
"Promise"
],
"scripts": {
},
"devDependencies": {
},
"dependencies": {
"bignumber.js": "~2.3.0",
"js-sha3": "~0.5.2",
"node-fetch": "~1.6.3"
}
}

View File

@ -1,26 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
const parity = require('../');
describe('load the Parity library', function () {
it('should no throw any error', () => {
expect(parity).to.be.ok;
expect(parity.Api).to.be.ok;
expect(parity.Abi).to.be.ok;
});
});

View File

@ -1,34 +0,0 @@
# @parity/shapeshift
A thin ES6 promise wrapper around the shapeshift.io APIs as documented at https://shapeshift.io/api
[https://github.com/paritytech/parity/tree/master/js/src/3rdparty/shapeshift](https://github.com/paritytech/parity/tree/master/js/src/3rdparty/shapeshift)
## usage
installation -
```
npm install --save @parity/shapeshift
```
Usage -
```
const APIKEY = 'private affiliate key or undefined';
const shapeshift = require('@parity/shapeshift')(APIKEY);
// api calls goes here
```
## api
queries -
- `getCoins()` [https://shapeshift.io/api#api-104](https://shapeshift.io/api#api-104)
- `getMarketInfo(pair)` [https://shapeshift.io/api#api-103](https://shapeshift.io/api#api-103)
- `getStatus(depositAddress)` [https://shapeshift.io/api#api-5](https://shapeshift.io/api#api-5)
transactions -
- `shift(toAddress, returnAddress, pair)` [https://shapeshift.io/api#api-7](https://shapeshift.io/api#api-7)

View File

@ -1,31 +0,0 @@
{
"name": "@parity/shapeshift",
"description": "The Parity Promise-based library for interfacing with ShapeShift over HTTP",
"version": "0.0.0",
"main": "library.js",
"author": "Parity Team <admin@parity.io>",
"maintainers": [
"Jaco Greeff"
],
"contributors": [],
"license": "GPL-3.0",
"repository": {
"type": "git",
"url": "git+https://github.com/paritytech/parity.git"
},
"keywords": [
"Ethereum",
"ABI",
"API",
"RPC",
"Parity",
"Promise"
],
"scripts": {
},
"devDependencies": {
},
"dependencies": {
"node-fetch": "~1.6.3"
}
}

View File

@ -1,29 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
const chai = require('chai');
// const chaiAsPromised from 'chai-as-promised';
// const chaiEnzyme from 'chai-enzyme';
// const sinonChai from 'sinon-chai';
// chai.use(chaiAsPromised);
// chai.use(chaiEnzyme());
// chai.use(sinonChai);
// expose expect to global so we won't have to manually import & define it in every test
global.expect = chai.expect;
module.exports = {};

View File

@ -1 +0,0 @@
-r ./test/mocha.config

5653
js-old/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "parity.js",
"version": "1.8.18",
"name": "@parity/dapp-v1",
"version": "1.9.99",
"main": "release/index.js",
"jsnext:main": "src/index.js",
"author": "Parity Team <admin@parity.io>",
@ -27,42 +27,16 @@
],
"scripts": {
"install": "napa",
"analize": "npm run analize:lib && npm run analize:dll && npm run analize:app",
"analize:app": "WPANALIZE=1 webpack --config webpack/app --json > .build/analize.app.json && cat .build/analize.app.json | webpack-bundle-size-analyzer",
"analize:lib": "WPANALIZE=1 webpack --config webpack/libraries --json > .build/analize.lib.json && cat .build/analize.lib.json | webpack-bundle-size-analyzer",
"analize:dll": "WPANALIZE=1 webpack --config webpack/vendor --json > .build/analize.dll.json && cat .build/analize.dll.json | webpack-bundle-size-analyzer",
"build": "npm run build:lib && npm run build:dll && npm run build:app && npm run build:embed",
"build": "npm run build:dll && npm run build:app",
"build:app": "webpack --config webpack/app",
"build:lib": "webpack --config webpack/libraries",
"build:dll": "webpack --config webpack/vendor",
"build:markdown": "babel-node ./scripts/build-rpc-markdown.js",
"build:json": "babel-node ./scripts/build-rpc-json.js",
"build:embed": "EMBED=1 node webpack/embed",
"build:i18n": "npm run clean && npm run build && babel-node ./scripts/build-i18n.js",
"ci:build": "npm run ci:build:lib && npm run ci:build:dll && npm run ci:build:app && npm run ci:build:embed",
"ci:build:app": "NODE_ENV=production webpack --config webpack/app",
"ci:build:lib": "NODE_ENV=production webpack --config webpack/libraries",
"ci:build:dll": "NODE_ENV=production webpack --config webpack/vendor",
"ci:build:npm": "NODE_ENV=production webpack --config webpack/npm",
"ci:build:jsonrpc": "babel-node ./scripts/build-rpc-json.js --output .npmjs/jsonrpc",
"ci:build:embed": "NODE_ENV=production EMBED=1 node webpack/embed",
"start": "npm run clean && npm install && npm run build:lib && npm run build:dll && npm run start:app",
"start:app": "node webpack/dev.server",
"clean": "rm -rf ./.build ./.coverage ./.happypack ./.npmjs ./build ./node_modules/.cache ./node_modules/@parity",
"coveralls": "npm run testCoverage && coveralls < coverage/lcov.info",
"ci:build": "cross-env NODE_ENV=production npm run build",
"clean": "rimraf ./.build ./.coverage ./.happypack ./.npmjs ./build ./node_modules/.cache",
"lint": "npm run lint:css && npm run lint:js",
"lint:cached": "npm run lint:css && npm run lint:js:cached",
"lint:css": "stylelint ./src/**/*.css",
"lint:fix": "npm run lint:js:fix",
"lint:i18n": "babel-node ./scripts/lint-i18n.js",
"lint:js": "eslint --ignore-path .gitignore ./src/",
"lint:js:cached": "eslint --cache --ignore-path .gitignore ./src/",
"lint:js:fix": "eslint --fix --ignore-path .gitignore ./src/",
"test": "NODE_ENV=test mocha --compilers ejs:ejsify 'src/**/*.spec.js'",
"test:coverage": "NODE_ENV=test istanbul cover _mocha -- --compilers ejs:ejsify 'src/**/*.spec.js'",
"test:e2e": "NODE_ENV=test mocha 'src/**/*.e2e.js'",
"test:npm": "(cd .npmjs && npm i) && node test/npmParity && node test/npmJsonRpc && (rm -rf .npmjs/node_modules)",
"prepush": "npm run lint:cached"
"test": "NODE_ENV=test mocha 'src/**/*.spec.js'",
"watch": "webpack --watch --config webpack/app"
},
"napa": {
"qrcode-generator": "kazuhikoarase/qrcode-generator"
@ -98,9 +72,9 @@
"copy-webpack-plugin": "4.0.1",
"core-js": "2.4.1",
"coveralls": "2.11.16",
"cross-env": "5.1.1",
"css-loader": "0.26.1",
"ejs-loader": "0.3.0",
"ejsify": "1.0.0",
"empty-module": "0.0.2",
"enzyme": "2.7.1",
"eslint": "3.16.1",
@ -118,9 +92,6 @@
"html-loader": "0.4.4",
"html-webpack-plugin": "2.28.0",
"http-proxy-middleware": "0.17.3",
"husky": "0.13.1",
"ignore-styles": "5.0.1",
"image-webpack-loader": "3.2.0",
"istanbul": "1.0.0-alpha.2",
"jsdom": "9.11.0",
"json-loader": "0.5.4",
@ -140,6 +111,7 @@
"react-addons-test-utils": "15.4.2",
"react-hot-loader": "3.0.0-beta.6",
"react-intl-aggregate-webpack-plugin": "0.0.1",
"rimraf": "2.6.2",
"rucksack-css": "0.9.1",
"script-ext-html-webpack-plugin": "1.7.1",
"serviceworker-webpack-plugin": "0.2.0",
@ -160,9 +132,9 @@
"yargs": "6.6.0"
},
"dependencies": {
"@parity/wordlist": "1.0.1",
"arraybuffer-loader": "0.2.2",
"babel-runtime": "6.23.0",
"@parity/abi": "2.1.x",
"@parity/api": "2.1.x",
"@parity/wordlist": "1.1.x",
"base32.js": "0.1.0",
"bignumber.js": "3.0.1",
"blockies": "0.0.2",
@ -234,7 +206,6 @@
"web3": "0.17.0-beta",
"whatwg-fetch": "2.0.1",
"worker-loader": "^0.8.0",
"yarn": "^0.21.3",
"zxcvbn": "4.4.1"
}
}

View File

@ -1,70 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import fs from 'fs';
import path from 'path';
import yargs from 'yargs';
import interfaces from '../src/jsonrpc';
const argv = yargs.default('output', 'release').argv;
const INDEX_JSON = path.join(__dirname, `../${argv.output}/index.json`);
const methods = [];
function formatDescription (obj) {
const optional = obj.optional ? '(optional) ' : '';
const defaults = obj.default ? `(default: ${obj.default}) ` : '';
return `${obj.type.name} - ${optional}${defaults}${obj.desc}`;
}
function formatType (obj) {
if (obj.type === Object && obj.details) {
const formatted = {};
Object.keys(obj.details).sort().forEach((key) => {
formatted[key] = formatType(obj.details[key]);
});
return {
desc: formatDescription(obj),
details: formatted
};
} else if (obj.type && obj.type.name) {
return formatDescription(obj);
}
return obj;
}
Object.keys(interfaces).sort().forEach((group) => {
Object.keys(interfaces[group]).sort().forEach((name) => {
const method = interfaces[group][name];
const deprecated = method.deprecated ? ' (Deprecated and not supported, to be removed in a future version)' : '';
methods.push({
name: `${group}_${name}`,
desc: `${method.desc}${deprecated}`,
params: method.params.map(formatType),
returns: formatType(method.returns),
inputFormatters: method.params.map((param) => param.format || null),
outputFormatter: method.returns.format || null
});
});
});
fs.writeFileSync(INDEX_JSON, JSON.stringify({ methods: methods }, null, 2), 'utf8');

View File

@ -1,328 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import fs from 'fs';
import path from 'path';
import { isPlainObject } from 'lodash';
import { info, warn, error } from './helpers/log';
import { Dummy } from '../src/jsonrpc/helpers';
import interfaces from '../src/jsonrpc';
import rustMethods from './helpers/parsed-rpc-traits';
const ROOT_DIR = path.join(__dirname, '../docs');
if (!fs.existsSync(ROOT_DIR)) {
fs.mkdirSync(ROOT_DIR);
}
Object.keys(rustMethods).forEach((group) => {
Object.keys(rustMethods[group]).forEach((method) => {
if (interfaces[group] == null || interfaces[group][method] == null) {
error(`${group}_${method} is defined in Rust traits, but not in js/src/jsonrpc/interfaces`);
}
});
});
function printType (type, obj) {
if (!type) {
throw new Error(`Invalid type in ${JSON.stringify(obj)}`);
}
return type.print || `\`${type.name}\``;
}
function formatDescription (obj, prefix = '', indent = '') {
const optional = obj.optional ? '(optional) ' : '';
const defaults = obj.default ? `(default: \`${obj.default}\`) ` : '';
return `${indent}${prefix}${printType(obj.type, obj)} - ${optional}${defaults}${obj.desc}`;
}
function formatType (obj) {
if (obj == null || obj.type == null) {
return obj;
}
const details = obj.details || obj.type.details;
if (details) {
const sub = Object.keys(details).map((key) => {
return formatDescription(details[key], `\`${key}\`: `, ' - ');
}).join('\n');
return `${formatDescription(obj)}\n${sub}`;
} else if (obj.type && obj.type.name) {
return formatDescription(obj);
}
return obj;
}
const rpcReqTemplate = {
method: 'web3_clientVersion',
params: [],
id: 1,
jsonrpc: '2.0'
};
const { isDummy } = Dummy;
const { isArray } = Array;
// Checks if a field definition has an example,
// or describes an object with fields that recursively have examples of their own,
// or is optional.
function hasExample ({ optional, example, details } = {}) {
if (optional || example !== undefined) {
return true;
}
if (details !== undefined) {
const values = Object.keys(details).map((key) => details[key]);
return values.every(hasExample);
}
return false;
}
// Remove all optional (trailing) params without examples from an array
function removeOptionalWithoutExamples (arr) {
return arr.filter(({ optional, example, details }) => {
return !optional || example !== undefined || details !== undefined;
});
}
// Grabs JSON compatible
function getExample (obj) {
if (isArray(obj)) {
return removeOptionalWithoutExamples(obj).map(getExample);
}
const { example, details } = obj;
if (example === undefined && details !== undefined) {
const nested = {};
Object.keys(details).forEach((key) => {
nested[key] = getExample(details[key]);
});
return nested;
}
return example;
}
function stringifyExample (example, dent = '') {
const indent = `${dent} `;
if (isDummy(example)) {
return example.toString();
}
if (isArray(example)) {
const last = example.length - 1;
// If all elements are dummies, print out a single line.
// Also covers empty arrays.
if (example.every(isDummy)) {
const dummies = example.map(d => d.toString());
return `[${dummies.join(', ')}]`;
}
// For arrays containing just one object or string, don't unwind the array to multiline
if (last === 0 && (isPlainObject(example[0]) || typeof example[0] === 'string')) {
return `[${stringifyExample(example[0], dent)}]`;
}
const elements = example.map((value, index) => {
const comma = index !== last ? ',' : '';
const comment = value != null && value._comment ? ` // ${value._comment}` : '';
return `${stringifyExample(value, indent)}${comma}${comment}`;
});
return `[\n${indent}${elements.join(`\n${indent}`)}\n${dent}]`;
}
if (isPlainObject(example)) {
const keys = Object.keys(example);
const last = keys.length - 1;
// print out an empty object
if (last === -1) {
return '{}';
}
const elements = keys.map((key, index) => {
const value = example[key];
const comma = index !== last ? ',' : '';
const comment = value && value._comment ? ` // ${example[key]._comment}` : '';
return `${JSON.stringify(key)}: ${stringifyExample(value, indent)}${comma}${comment}`;
});
return `{\n${indent}${elements.join(`\n${indent}`)}\n${dent}}`;
}
return JSON.stringify(example);
}
function buildExample (name, method) {
// deprecated, don't care
if (method.deprecated) {
return '';
}
const logPostfix = method.subdoc ? ` (${method.subdoc})` : '';
const hasReqExample = method.params.every(hasExample);
const hasResExample = hasExample(method.returns);
if (!hasReqExample && !hasResExample) {
error(`${name} has no examples${logPostfix}`);
return '';
}
const examples = [];
if (hasReqExample) {
const params = getExample(method.params);
const req = Dummy.stringifyJSON(Object.assign({}, rpcReqTemplate, { method: name, params }));
examples.push(`Request\n\`\`\`bash\ncurl --data '${req}' -H "Content-Type: application/json" -X POST localhost:8545\n\`\`\``);
} else {
warn(`${name} has a response example but not a request example${logPostfix}`);
}
if (hasResExample) {
const res = stringifyExample({
id: 1,
jsonrpc: '2.0',
result: getExample(method.returns)
});
examples.push(`Response\n\`\`\`js\n${res}\n\`\`\``);
} else {
if (typeof method.returns === 'string') {
info(`${name} has a request example and only text description for response${logPostfix}`);
} else {
warn(`${name} has a request example but not a response example${logPostfix}`);
}
}
return `\n\n#### Example\n\n${examples.join('\n\n')}`;
}
function buildParameters (params) {
if (params.length === 0) {
return '';
}
let md = `0. ${params.map(formatType).join('\n0. ')}`;
if (params.length > 0 && params.every(hasExample) && !isDummy(params[0].example)) {
const example = getExample(params);
md = `${md}\n\n\`\`\`js\nparams: ${stringifyExample(example)}\n\`\`\``;
}
return md;
}
Object.keys(interfaces).sort().forEach((group) => {
const spec = interfaces[group];
for (const key in spec) {
const method = spec[key];
if (!method || !method.subdoc) {
continue;
}
const subgroup = `${group}_${method.subdoc}`;
interfaces[subgroup] = interfaces[subgroup] || {};
interfaces[subgroup][key] = method;
delete spec[key];
}
});
Object.keys(interfaces).sort().forEach((group) => {
let preamble = `# The \`${group}\` Module`;
let markdown = `## JSON-RPC methods\n`;
const spec = interfaces[group];
if (spec._preamble) {
preamble = `${preamble}\n\n${spec._preamble}`;
}
const content = [];
const tocMain = [];
const tocSections = {};
// Comparator that will sort by sections first, names second
function methodComparator (a, b) {
const sectionA = spec[a].section || '';
const sectionB = spec[b].section || '';
return sectionA.localeCompare(sectionB) || a.localeCompare(b);
}
Object.keys(spec).sort(methodComparator).forEach((iname) => {
const method = spec[iname];
const groupName = group.replace(/_.*$/, '');
const name = `${groupName}_${iname}`;
if (method.nodoc || method.deprecated) {
info(`Skipping ${name}: ${method.nodoc || 'Deprecated'}`);
return;
}
if (rustMethods[groupName] == null || rustMethods[groupName][iname] == null) {
error(`${name} is defined in js/src/jsonrpc/interfaces, but not in Rust traits`);
}
const desc = method.desc;
const params = buildParameters(method.params);
const returns = `- ${formatType(method.returns)}`;
const example = buildExample(name, method);
const { section } = method;
const toc = section ? tocSections[section] = tocSections[section] || [] : tocMain;
toc.push(`- [${name}](#${name.toLowerCase()})`);
content.push(`### ${name}\n\n${desc}\n\n#### Parameters\n\n${params || 'None'}\n\n#### Returns\n\n${returns || 'None'}${example}`);
});
markdown = `${markdown}\n${tocMain.join('\n')}`;
Object.keys(tocSections).sort().forEach((section) => {
markdown = `${markdown}\n\n#### ${section}\n${tocSections[section].join('\n')}`;
});
markdown = `${markdown}\n\n## JSON-RPC API Reference\n\n${content.join('\n\n***\n\n')}\n\n`;
const mdFile = path.join(ROOT_DIR, `${group}.md`);
fs.writeFileSync(mdFile, `${preamble}\n\n${markdown}`, 'utf8');
});

View File

@ -1,36 +0,0 @@
#!/bin/bash
set -e
# variables
PACKAGES=( "parity" "etherscan" "shapeshift" "jsonrpc" )
# change into the build directory
BASEDIR=`dirname $0`
cd $BASEDIR/..
# build jsonrpc
echo "*** Building JSONRPC .json"
mkdir -p .npmjs/jsonrpc
npm run ci:build:jsonrpc
# build all packages
echo "*** Building packages for npmjs"
echo "$NPM_TOKEN" >> ~/.npmrc
for PACKAGE in ${PACKAGES[@]}
do
echo "*** Building $PACKAGE"
LIBRARY=$PACKAGE npm run ci:build:npm
DIRECTORY=.npmjs/$PACKAGE
cd $DIRECTORY
echo "*** Publishing $PACKAGE from $DIRECTORY"
echo "npm publish --access public || true"
cd ../..
done
cd ..
# exit with exit code
exit 0

View File

@ -1,32 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import chalk from 'chalk';
// INFO Logging helper
export function info (log) {
console.log(chalk.blue(`INFO:\t${log}`));
}
// WARN Logging helper
export function warn (log) {
console.warn(chalk.yellow(`WARN:\t${log}`));
}
// ERROR Logging helper
export function error (log) {
console.error(chalk.red(`ERROR:\t${log}`));
}

Some files were not shown because too many files have changed in this diff Show More