diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..a737b1a88 --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -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 people’s personally identifying information ("doxing"). +* Personal insults, particularly those related to gender, sexual orientation, race, religion, or disability. +* Inappropriate photography or recording. +* Inappropriate physical contact. You should have someone’s consent before touching them. +* Unwelcome sexual attention. This includes, sexualized comments or jokes; inappropriate touching, groping, and unwelcomed sexual advances. +* Deliberate intimidation, stalking or following (online or in person). +* Advocating for, or encouraging, any of the above behavior. +* Sustained disruption of community events, including talks and presentations. + +## 5. Consequences of Unacceptable Behavior + +Unacceptable behavior from any community member, including sponsors and those with decision-making authority, will not be tolerated. + +Anyone asked to stop unacceptable behavior is expected to comply immediately. + +If a community member engages in unacceptable behavior, the community organizers may take any action they deem appropriate, up to and including a temporary ban or permanent expulsion from the community without warning (and without refund in the case of a paid event). + +## 6. Reporting Guidelines + +If you are subject to or witness unacceptable behavior, or have any other concerns, please notify a community organizer as soon as possible. community@parity.io. + +Link to reporting guidelines: [CONTRIBUTING.md](CONTRIBUTING.md) + +Link to security policy: [SECURITY.md](../SECURITY.md) + +Additionally, community organizers are available to help community members engage with local law enforcement or to otherwise help those experiencing unacceptable behavior feel safe. In the context of in-person events, organizers will also provide escorts as desired by the person experiencing distress. + +## 7. Addressing Grievances + +If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify Parity Technologies with a concise description of your grievance. Your grievance will be handled in accordance with our existing governing policies. + +## 8. Scope + +We expect all community participants (contributors, paid or otherwise; sponsors; and other guests) to abide by this Code of Conduct in all community venues–online and in-person–as well as in all one-on-one communications pertaining to community business. + +This code of conduct and its related procedures also applies to unacceptable behavior occurring outside the scope of community activities when such behavior has the potential to adversely affect the safety and well-being of community members. + +## 9. Contact info + +You can contact Parity via Email: community@parity.io + +## 10. License and attribution + +This Code of Conduct is distributed under a [Creative Commons Attribution-ShareAlike license](http://creativecommons.org/licenses/by-sa/3.0/). + +Portions of text derived from the [Django Code of Conduct](https://www.djangoproject.com/conduct/) and the [Geek Feminism Anti-Harassment Policy](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Policy). + +Retrieved on November 22, 2016 from [http://citizencodeofconduct.org/](http://citizencodeofconduct.org/) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 000000000..e3438f10a --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -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. diff --git a/CHANGELOG.md b/CHANGELOG.md index a50ac02db..329e6040a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,48 @@ +## Parity [v1.8.4](https://github.com/paritytech/parity/releases/tag/v1.8.4) (2017-12-12) + +Parity 1.8.4 applies fixes for Proof-of-Authority networks and schedules the Kovan-Byzantium hard-fork. + +- The Kovan testnet will fork on block `5067000` at `Thu Dec 14 2017 05:40:03 UTC`. + - This enables Byzantium features on Kovan. + - This disables uncles on Kovan for stability reasons. +- Proof-of-Authority networks are advised to set `maximumUncleCount` to 0 in a future `maximumUncleCountTransition` for stability reasons. + - See the [Kovan chain spec](https://github.com/paritytech/parity/blob/master/ethcore/res/ethereum/kovan.json) for an example. + - New PoA networks created with Parity will have this feature enabled by default. + +Furthermore, this release includes the ECIP-1039 Monetary policy rounding specification for Ethereum Classic, reduces the maximum Ethash-block timestamp drift to 15 seconds, and fixes various bugs for WASM and the RPC APIs. + +The full list of included changes: + +- Beta Backports and HF block update ([#7244](https://github.com/paritytech/parity/pull/7244)) + - Reduce max block timestamp drift to 15 seconds ([#7240](https://github.com/paritytech/parity/pull/7240)) + - Add test for block timestamp validation within allowed drift + - Update kovan HF block number. +- Beta Kovan HF ([#7234](https://github.com/paritytech/parity/pull/7234)) + - Kovan HF. + - Bump version. + - Fix aura difficulty race ([#7198](https://github.com/paritytech/parity/pull/7198)) + - Fix test key + - Extract out score calculation + - Fix build + - Update kovan HF block number. + - Add missing byzantium builtins. + - Bump installers versions. + - Increase allowed time drift to 10s. ([#7238](https://github.com/paritytech/parity/pull/7238)) +- Beta Backports ([#7197](https://github.com/paritytech/parity/pull/7197)) + - Maximum uncle count transition ([#7196](https://github.com/paritytech/parity/pull/7196)) + - Enable delayed maximum_uncle_count activation. + - Fix tests. + - Defer kovan HF. + - Disable uncles by default ([#7006](https://github.com/paritytech/parity/pull/7006)) + - Escape inifinite loop in estimte_gas ([#7075](https://github.com/paritytech/parity/pull/7075)) + - ECIP-1039: Monetary policy rounding specification ([#7067](https://github.com/paritytech/parity/pull/7067)) + - WASM Remove blockhash error ([#7121](https://github.com/paritytech/parity/pull/7121)) + - Remove blockhash error + - Update tests. + - WASM storage_read and storage_write don't return anything ([#7110](https://github.com/paritytech/parity/pull/7110)) + - WASM parse payload from panics ([#7097](https://github.com/paritytech/parity/pull/7097)) + - Fix no-default-features. ([#7096](https://github.com/paritytech/parity/pull/7096)) + ## Parity [v1.8.3](https://github.com/paritytech/parity/releases/tag/v1.8.3) (2017-11-15) Parity 1.8.3 contains several bug-fixes and removes the ability to deploy built-in multi-signature wallets. diff --git a/Cargo.lock b/Cargo.lock index 51f9b6d50..03191123a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -244,28 +244,6 @@ dependencies = [ "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "clippy" -version = "0.0.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "clippy_lints 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "clippy_lints" -version = "0.0.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", - "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "coco" version = "0.1.1" @@ -480,7 +458,6 @@ dependencies = [ "bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "bn 0.4.4 (git+https://github.com/paritytech/bn)", "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "common-types 0.1.0", "crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "ethash 1.9.0", @@ -554,6 +531,19 @@ dependencies = [ "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ethcore-bigint" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bigint 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "heapsize 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "plain_hasher 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ethcore-bloom-journal" version = "0.1.0" @@ -641,7 +631,6 @@ version = "1.9.0" dependencies = [ "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-bigint 0.2.1", "ethcore-bytes 0.1.0", @@ -727,32 +716,14 @@ dependencies = [ name = "ethcore-util" version = "1.9.0" dependencies = [ - "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "elastic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "eth-secp256k1 0.5.7 (git+https://github.com/paritytech/rust-secp256k1)", "ethcore-bigint 0.2.1", "ethcore-bytes 0.1.0", - "ethcore-logger 1.9.0", "hashdb 0.1.1", - "heapsize 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "journaldb 0.1.0", "keccak-hash 0.1.0", - "kvdb 0.1.0", - "kvdb-memorydb 0.1.0", - "libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "memorydb 0.1.1", - "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "patricia-trie 0.1.0", - "rlp 0.2.1", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "triehash 0.1.0", - "util-error 0.1.0", - "vergen 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -771,7 +742,6 @@ dependencies = [ name = "ethjson" version = "0.1.0" dependencies = [ - "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-bigint 0.2.1", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", @@ -853,7 +823,6 @@ dependencies = [ name = "ethsync" version = "1.9.0" dependencies = [ - "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore 1.9.0", "ethcore-bigint 0.2.1", @@ -1743,11 +1712,6 @@ dependencies = [ "odds 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "nom" -version = "1.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "ntp" version = "0.3.1" @@ -1916,7 +1880,6 @@ dependencies = [ "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.26.2 (registry+https://github.com/rust-lang/crates.io-index)", - "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)", "daemonize 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1959,6 +1922,7 @@ dependencies = [ "parity-rpc 1.9.0", "parity-rpc-client 1.4.0", "parity-updater 1.9.0", + "parity-version 0.1.0", "parity-whisper 0.1.0", "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "path 0.1.0", @@ -1984,7 +1948,6 @@ name = "parity-dapps" version = "1.9.0" dependencies = [ "base32 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-bigint 0.2.1", "ethcore-bytes 0.1.0", @@ -2005,6 +1968,7 @@ dependencies = [ "parity-hash-fetch 1.9.0", "parity-reactor 0.1.0", "parity-ui 1.9.0", + "parity-version 0.1.0", "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2020,7 +1984,6 @@ name = "parity-dapps-glue" version = "1.9.1" dependencies = [ "aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", - "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 2.0.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)", "quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2118,7 +2081,6 @@ version = "1.9.0" dependencies = [ "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "cid 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "ethash 1.9.0", "ethcore 1.9.0", "ethcore-bigint 0.2.1", @@ -2154,6 +2116,7 @@ dependencies = [ "order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "parity-reactor 0.1.0", "parity-updater 1.9.0", + "parity-version 0.1.0", "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2234,7 +2197,7 @@ dependencies = [ [[package]] name = "parity-ui-old-precompiled" version = "1.9.0" -source = "git+https://github.com/js-dist-paritytech/parity-master-1-9-v1.git#3c4b36c8f9b182242c1355289dc448eac95c19c5" +source = "git+https://github.com/js-dist-paritytech/parity-master-1-9-v1.git#d95164e94df10cb2bbcaa5dad36d6b61b760c3e1" dependencies = [ "parity-dapps-glue 1.9.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2242,7 +2205,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.9.0" -source = "git+https://github.com/js-dist-paritytech/parity-master-1-9-shell.git#6dad0d219f9ad72f948f5f4138df3d6cf987b655" +source = "git+https://github.com/js-dist-paritytech/parity-master-1-9-shell.git#3bfd9abfca2ccadeb60aa6610ea0fbb9a8b74ac5" dependencies = [ "parity-dapps-glue 1.9.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2261,12 +2224,24 @@ dependencies = [ "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "parity-hash-fetch 1.9.0", "parity-reactor 0.1.0", + "parity-version 0.1.0", "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "path 0.1.0", "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parity-version" +version = "0.1.0" +dependencies = [ + "ethcore-bytes 0.1.0", + "rlp 0.2.1", + "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "vergen 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "parity-wasm" version = "0.15.3" @@ -2410,6 +2385,14 @@ dependencies = [ "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "plain_hasher" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "podio" version = "0.1.5" @@ -2538,11 +2521,6 @@ name = "quick-error" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "quine-mc_cluskey" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "quote" version = "0.3.15" @@ -2603,11 +2581,6 @@ dependencies = [ "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "regex-syntax" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "regex-syntax" version = "0.4.1" @@ -2836,14 +2809,6 @@ name = "semver" version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "semver" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nom 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "semver" version = "0.6.0" @@ -3256,14 +3221,6 @@ dependencies = [ "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "toml" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "toml" version = "0.4.5" @@ -3277,6 +3234,16 @@ name = "traitobject" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "transaction-pool" +version = "1.9.0" +dependencies = [ + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore-bigint 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "transient-hashmap" version = "0.4.0" @@ -3585,8 +3552,6 @@ dependencies = [ "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" "checksum cid 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "34aa7da06f10541fbca6850719cdaa8fa03060a5d2fb33840f149cf8133a00c7" "checksum clap 2.26.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3451e409013178663435d6f15fdb212f14ee4424a3d74f979d081d0a66b6f1f2" -"checksum clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "5b4fabf979ddf6419a313c1c0ada4a5b95cfd2049c56e8418d622d27b4b6ff32" -"checksum clippy_lints 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "ce96ec05bfe018a0d5d43da115e54850ea2217981ff0f2e462780ab9d594651a" "checksum coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c06169f5beb7e31c7c67ebf5540b8b472d23e3eade3b2ec7d1f5b504a85f91bd" "checksum conv 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299" "checksum cookie 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d53b80dde876f47f03cda35303e368a79b91c70b0d65ecba5fd5280944a08591" @@ -3609,6 +3574,7 @@ dependencies = [ "checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" "checksum eth-secp256k1 0.5.7 (git+https://github.com/paritytech/rust-secp256k1)" = "" "checksum ethabi 4.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c819a3adef0413a2519cbd9a19a35dd1c20c7a0110705beaba8aa4aa87eda95f" +"checksum ethcore-bigint 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcb5af77e74a8f70e9c3337e069c37bc82178ef1b459c02091f73c4ad5281eb5" "checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa" "checksum flate2 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)" = "e6234dd4468ae5d1e2dbb06fe2b058696fdc50a339c68a393aefbf00bc81e423" "checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344" @@ -3679,7 +3645,6 @@ dependencies = [ "checksum native-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04b781c9134a954c84f0594b9ab3f5606abc516030388e8511887ef4c204a1e5" "checksum net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)" = "3a80f842784ef6c9a958b68b7516bc7e35883c614004dd94959a4dca1b716c09" "checksum nodrop 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "52cd74cd09beba596430cc6e3091b74007169a56246e1262f0ba451ea95117b2" -"checksum nom 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce" "checksum ntp 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "143149743832c6543b60a8ef2a26cd9122dfecec2b767158e852a7beecf6d7a0" "checksum num 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "a311b77ebdc5dd4cf6449d81e4135d9f0e3b153839ac90e648a8ef538f923525" "checksum num-bigint 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "8fd0f8dbb4c0960998958a796281d88c16fbe68d87b1baa6f31e2979e81fd0bd" @@ -3711,6 +3676,7 @@ dependencies = [ "checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03" "checksum phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "07e24b0ca9643bdecd0632f2b3da6b1b89bbb0030e0b992afc1113b23a7bc2f2" "checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903" +"checksum plain_hasher 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83ae80873992f511142c07d0ec6c44de5636628fdb7e204abd655932ea79d995" "checksum podio 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e5422a1ee1bc57cc47ae717b0137314258138f38fd5f3cea083f43a9725383a0" "checksum pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2412f3332a07c7a2a50168988dcc184f32180a9758ad470390e5f55e089f6b6e" "checksum primal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0e31b86efadeaeb1235452171a66689682783149a6249ff334a2c5d8218d00a4" @@ -3724,7 +3690,6 @@ dependencies = [ "checksum quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9e25fa23c044c1803f43ca59c98dac608976dd04ce799411edd58ece776d4" "checksum quasi_macros 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29cec87bc2816766d7e4168302d505dd06b0a825aed41b00633d296e922e02dd" "checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4" -"checksum quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "eb250fd207a4729c976794d03db689c9be1d634ab5a1c9da9492a13d8fecbcdf" "checksum rayon 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a77c51c07654ddd93f6cb543c7a849863b03abc7e82591afda6dc8ad4ac3ac4a" @@ -3732,7 +3697,6 @@ dependencies = [ "checksum rayon-core 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7febc28567082c345f10cddc3612c6ea020fc3297a1977d472cf9fdb73e6e493" "checksum redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "8dde11f18c108289bef24469638a04dce49da56084f2d50618b226e47eb04509" "checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b" -"checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957" "checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db" "checksum reqwest 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5866613d84e2a39c0479a960bf2d0eff1fbfc934f02cd42b5c08c1e1efc5b1fd" "checksum ring 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)" = "24293de46bac74c9b9c05b40ff8496bbc8b9ae242a9b89f754e1154a43bc7c4c" @@ -3755,7 +3719,6 @@ dependencies = [ "checksum security-framework 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "dfa44ee9c54ce5eecc9de7d5acbad112ee58755239381f687e564004ba4a2332" "checksum security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "5421621e836278a0b139268f36eee0dc7e389b784dc3f79d8f11aabadf41bead" "checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac" -"checksum semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2d5b7638a1f03815d94e88cb3b3c08e87f0db4d683ef499d1836aaf70a45623f" "checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "6a7046c9d4c6c522d10b2d098f9bebe2bef227e0e74044d8c1bfcf6b476af799" @@ -3803,7 +3766,6 @@ dependencies = [ "checksum tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6131e780037787ff1b3f8aad9da83bca02438b72277850dd6ad0d455e0e20efc" "checksum tokio-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d88e411cac1c87e405e4090be004493c5d8072a370661033b1a64ea205ec2e13" "checksum tokio-uds 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6116c71be48f8f1656551fd16458247fdd6c03201d7893ad81189055fcde03e8" -"checksum toml 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)" = "0590d72182e50e879c4da3b11c6488dae18fccb1ae0c7a3eda18e16795844796" "checksum toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7540f4ffc193e0d3c94121edb19b055670d369f77d5804db11ae053a45b6e7e" "checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" "checksum transient-hashmap 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "715254c8f0811be1a79ad3ea5e6fa3c8eddec2b03d7f5ba78cf093e56d79c24f" diff --git a/Cargo.toml b/Cargo.toml index 17250c32c..a79dafd3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,7 @@ parity-reactor = { path = "util/reactor" } parity-rpc = { path = "rpc" } parity-rpc-client = { path = "rpc_client" } parity-updater = { path = "updater" } +parity-version = { path = "util/version" } parity-whisper = { path = "whisper" } path = { path = "util/path" } panic_hook = { path = "panic_hook" } @@ -65,7 +66,6 @@ kvdb-rocksdb = { path = "util/kvdb-rocksdb" } journaldb = { path = "util/journaldb" } parity-dapps = { path = "dapps", optional = true } -clippy = { version = "0.0.103", optional = true} ethcore-secretstore = { path = "secret_store", optional = true } [build-dependencies] @@ -94,13 +94,11 @@ ui-precompiled = [ ui-enabled = ["dapps"] dapps = ["parity-dapps"] jit = ["ethcore/jit"] -dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "parity-rpc/dev", "parity-dapps/dev"] json-tests = ["ethcore/json-tests"] test-heavy = ["ethcore/test-heavy"] evm-debug = ["ethcore/evm-debug"] evm-debug-tests = ["ethcore/evm-debug-tests"] slow-blocks = ["ethcore/slow-blocks"] -final = ["ethcore-util/final"] secretstore = ["ethcore-secretstore"] [[bin]] @@ -116,4 +114,13 @@ lto = false panic = "abort" [workspace] -members = ["ethstore/cli", "ethkey/cli", "evmbin", "whisper", "chainspec", "dapps/js-glue", "ethcore/wasm/run"] +members = [ + "chainspec", + "dapps/js-glue", + "ethcore/wasm/run", + "ethkey/cli", + "ethstore/cli", + "evmbin", + "transaction-pool", + "whisper", +] diff --git a/README.md b/README.md index 770cbca44..e5d9c0383 100644 --- a/README.md +++ b/README.md @@ -94,12 +94,25 @@ $ cargo build --release ``` This will produce an executable in the `./target/release` subdirectory. + Note: if cargo fails to parse manifest try: ```bash $ ~/.cargo/bin/cargo build --release ``` +Note: When compiling a crate and you receive the following error: + +``` +error: the crate is compiled with the panic strategy `abort` which is incompatible with this crate's strategy of `unwind` +``` + +Cleaning the repository will most likely solve the issue, try: + +```bash +$ cargo clean +``` + This will always compile the latest nightly builds. If you want to build stable or beta, do a `git checkout stable` or `git checkout beta` first. ---- diff --git a/dapps/Cargo.toml b/dapps/Cargo.toml index f3e02826d..ac9becaa6 100644 --- a/dapps/Cargo.toml +++ b/dapps/Cargo.toml @@ -37,15 +37,12 @@ parity-hash-fetch = { path = "../hash-fetch" } parity-reactor = { path = "../util/reactor" } parity-ui = { path = "./ui" } keccak-hash = { path = "../util/hash" } - -clippy = { version = "0.0.103", optional = true} +parity-version = { path = "../util/version" } [dev-dependencies] env_logger = "0.4" ethcore-devtools = { path = "../devtools" } [features] -dev = ["clippy", "ethcore-util/dev"] - ui = ["parity-ui/no-precompiled-js"] ui-precompiled = ["parity-ui/use-precompiled-js"] diff --git a/dapps/js-glue/Cargo.toml b/dapps/js-glue/Cargo.toml index 4cad9cdab..efe92bbda 100644 --- a/dapps/js-glue/Cargo.toml +++ b/dapps/js-glue/Cargo.toml @@ -18,13 +18,10 @@ quasi = { version = "0.32", default-features = false } quasi_macros = { version = "0.32", optional = true } syntex = { version = "0.58", optional = true } syntex_syntax = { version = "0.58", optional = true } -clippy = { version = "0.0.103", optional = true } [features] -dev = ["clippy"] default = ["with-syntex"] nightly = ["quasi_macros"] -nightly-testing = ["clippy"] with-syntex = ["quasi/with-syntex", "quasi_codegen", "quasi_codegen/with-syntex", "syntex", "syntex_syntax"] use-precompiled-js = [] diff --git a/dapps/node-health/src/time.rs b/dapps/node-health/src/time.rs index 78f1fdd5f..c3da050a4 100644 --- a/dapps/node-health/src/time.rs +++ b/dapps/node-health/src/time.rs @@ -193,7 +193,7 @@ const UPDATE_TIMEOUT_ERR_SECS: u64 = 60; const UPDATE_TIMEOUT_INCOMPLETE_SECS: u64 = 10; /// Maximal valid time drift. -pub const MAX_DRIFT: i64 = 500; +pub const MAX_DRIFT: i64 = 10_000; type BoxFuture = Box + Send>; diff --git a/dapps/src/apps/fetcher/mod.rs b/dapps/src/apps/fetcher/mod.rs index c75569c51..797b69947 100644 --- a/dapps/src/apps/fetcher/mod.rs +++ b/dapps/src/apps/fetcher/mod.rs @@ -200,39 +200,57 @@ impl Endpoint for ContentFetcher { Some(URLHintResult::Dapp(_)) if self.only_content => { (None, Self::dapps_disabled(self.embeddable_on.clone())) }, - Some(URLHintResult::Dapp(dapp)) => { - let handler = ContentFetcherHandler::new( - req.method(), - &dapp.url(), - path, - installers::Dapp::new( - content_id.clone(), - self.cache_path.clone(), - Box::new(on_done), - self.embeddable_on.clone(), - self.pool.clone(), - ), - self.embeddable_on.clone(), - self.fetch.clone(), - ); - - (Some(ContentStatus::Fetching(handler.fetch_control())), Box::new(handler) as endpoint::Response) - }, - Some(URLHintResult::Content(content)) => { - let handler = ContentFetcherHandler::new( - req.method(), - &content.url, - path, - installers::Content::new( - content_id.clone(), - content.mime, - self.cache_path.clone(), - Box::new(on_done), - self.pool.clone(), - ), - self.embeddable_on.clone(), - self.fetch.clone(), - ); + Some(content) => { + let handler = match content { + URLHintResult::Dapp(dapp) => { + ContentFetcherHandler::new( + req.method(), + &dapp.url(), + path, + installers::Dapp::new( + content_id.clone(), + self.cache_path.clone(), + Box::new(on_done), + self.embeddable_on.clone(), + self.pool.clone(), + ), + self.embeddable_on.clone(), + self.fetch.clone(), + ) + }, + URLHintResult::GithubDapp(content) => { + ContentFetcherHandler::new( + req.method(), + &content.url, + path, + installers::Dapp::new( + content_id.clone(), + self.cache_path.clone(), + Box::new(on_done), + self.embeddable_on.clone(), + self.pool.clone(), + ), + self.embeddable_on.clone(), + self.fetch.clone(), + ) + }, + URLHintResult::Content(content) => { + ContentFetcherHandler::new( + req.method(), + &content.url, + path, + installers::Content::new( + content_id.clone(), + content.mime, + self.cache_path.clone(), + Box::new(on_done), + self.pool.clone(), + ), + self.embeddable_on.clone(), + self.fetch.clone(), + ) + }, + }; (Some(ContentStatus::Fetching(handler.fetch_control())), Box::new(handler) as endpoint::Response) }, diff --git a/dapps/src/handlers/content.rs b/dapps/src/handlers/content.rs index 13d1fb0b9..711a985bd 100644 --- a/dapps/src/handlers/content.rs +++ b/dapps/src/handlers/content.rs @@ -19,7 +19,7 @@ use hyper::{self, mime, header}; use hyper::StatusCode; -use util::version; +use parity_version::version; use handlers::add_security_headers; use Embeddable; diff --git a/dapps/src/lib.rs b/dapps/src/lib.rs index 85087242b..19dd7f70b 100644 --- a/dapps/src/lib.rs +++ b/dapps/src/lib.rs @@ -16,8 +16,6 @@ //! Ethcore Webapplications for Parity #![warn(missing_docs)] -#![cfg_attr(feature="nightly", feature(plugin))] -#![cfg_attr(feature="nightly", plugin(clippy))] extern crate base32; extern crate futures_cpupool; @@ -43,6 +41,7 @@ extern crate parity_dapps_glue as parity_dapps; extern crate parity_hash_fetch as hash_fetch; extern crate parity_ui; extern crate keccak_hash as hash; +extern crate parity_version; #[macro_use] extern crate futures; diff --git a/dapps/src/page/handler.rs b/dapps/src/page/handler.rs index 56c9a4e1d..576455115 100644 --- a/dapps/src/page/handler.rs +++ b/dapps/src/page/handler.rs @@ -17,8 +17,9 @@ use std::io; use std::time::{Duration, SystemTime}; use hyper::{self, header, StatusCode}; -use hyper::mime::Mime; +use hyper::mime::{self, Mime}; +use apps; use handlers::{Reader, ContentHandler, add_security_headers}; use {Embeddable}; @@ -95,7 +96,18 @@ impl PageHandler { add_security_headers(&mut headers, self.safe_to_embed_on); } - let (reader, body) = Reader::pair(file.into_reader(), Vec::new()); + let initial_content = if file.content_type().to_owned() == mime::TEXT_HTML { + let content = &format!( + r#""#, + apps::UTILS_PATH, + ); + + content.as_bytes().to_vec() + } else { + Vec::new() + }; + + let (reader, body) = Reader::pair(file.into_reader(), initial_content); res.set_body(body); (Some(reader), res) } diff --git a/dapps/src/tests/fetch.rs b/dapps/src/tests/fetch.rs index 69e407dfa..c6ae401e7 100644 --- a/dapps/src/tests/fetch.rs +++ b/dapps/src/tests/fetch.rs @@ -166,14 +166,15 @@ fn should_return_fetched_dapp_content() { response1.assert_status("HTTP/1.1 200 OK"); assert_security_headers_for_embed(&response1.headers); - assert_eq!( - response1.body, - r#"18 + assert!( + response1.body.contains(r#"18

Hello Gavcoin!

0 -"# +"#), + "Expected Gavcoin body: {}", + response1.body ); response2.assert_status("HTTP/1.1 200 OK"); diff --git a/dapps/src/tests/home.rs b/dapps/src/tests/home.rs index fa5c5b4c4..0ee065364 100644 --- a/dapps/src/tests/home.rs +++ b/dapps/src/tests/home.rs @@ -60,3 +60,32 @@ fn should_serve_home() { response.assert_header("Content-Type", "text/html"); assert_security_headers(&response.headers); } + + +#[test] +fn should_inject_js() { + // given + let server = serve_ui(); + + // when + let response = request(server, + "\ + GET / HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Connection: close\r\n\ + \r\n\ + {} + " + ); + + // then + response.assert_status("HTTP/1.1 200 OK"); + response.assert_header("Content-Type", "text/html"); + assert_eq!( + response.body.contains(r#"/inject.js">"#), + true, + "Expected inject script tag in: {}", + response.body + ); + assert_security_headers(&response.headers); +} diff --git a/docs/CHANGELOG-1.7.md b/docs/CHANGELOG-1.7.md index 25ebcd82e..7253660b1 100644 --- a/docs/CHANGELOG-1.7.md +++ b/docs/CHANGELOG-1.7.md @@ -1,3 +1,35 @@ +### Parity [v1.7.10](https://github.com/paritytech/parity/releases/tag/v1.7.10) (2017-12-11) + +Parity 1.7.10 applies fixes for Proof-of-Authority networks and schedules the Kovan-Byzantium hard-fork. + +- The Kovan testnet will fork on block `5067000` at `Thu Dec 14 2017 05:40:03 UTC`. + - This enables Byzantium features on Kovan. + - This disables uncles on Kovan for stability reasons. +- Proof-of-Authority networks are advised to set `maximumUncleCount` to 0 in a future `maximumUncleCountTransition` for stability reasons. See the [Kovan chain spec](https://github.com/paritytech/parity/blob/master/ethcore/res/ethereum/kovan.json) for an example. New PoA networks created with Parity will have this feature enabled by default. + +The full list of included changes: + +- Backports and HF block update ([#7243](https://github.com/paritytech/parity/pull/7243)) + - Reduce max block timestamp drift to 15 seconds ([#7240](https://github.com/paritytech/parity/pull/7240)) + - Add test for block timestamp validation within allowed drift + - Update kovan HF block number. ([#7259](https://github.com/paritytech/parity/pull/7259)) +- [stable] Backports and Kovan HF ([#7235](https://github.com/paritytech/parity/pull/7235)) + - Escape inifinite loop in estimte_gas ([#7075](https://github.com/paritytech/parity/pull/7075)) + - Disable uncles by default ([#7006](https://github.com/paritytech/parity/pull/7006)) + - Maximum uncle count transition ([#7196](https://github.com/paritytech/parity/pull/7196)) + - Enable delayed maximum_uncle_count activation. + - Fix tests. + - Defer kovan HF. + - Bump version. + - Kovan HF. + - Update Kovan HF block. + - Fix compilation issues. + - Fix aura test. + - Add missing byzantium builtins. + - Fix tests. + - Bump version for installers. + - Increase allowed time drift to 10s. ([#7238](https://github.com/paritytech/parity/pull/7238)) + ### 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. diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 8d7301438..27a8dccd7 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -11,7 +11,6 @@ ansi_term = "0.9" bloomchain = "0.1" bn = { git = "https://github.com/paritytech/bn" } byteorder = "1.0" -clippy = { version = "0.0.103", optional = true} common-types = { path = "types" } crossbeam = "0.2.9" ethash = { path = "../ethash" } @@ -83,6 +82,5 @@ evm-debug-tests = ["evm-debug"] slow-blocks = [] # Use SLOW_TX_DURATION="50" (compile time!) to track transactions over 50ms json-tests = [] test-heavy = [] -dev = ["clippy"] default = [] benches = [] diff --git a/ethcore/evm/src/interpreter/gasometer.rs b/ethcore/evm/src/interpreter/gasometer.rs index 082868b95..a7fe2a331 100644 --- a/ethcore/evm/src/interpreter/gasometer.rs +++ b/ethcore/evm/src/interpreter/gasometer.rs @@ -32,7 +32,6 @@ macro_rules! overflowing { }} } -#[cfg_attr(feature="dev", allow(enum_variant_names))] enum Request { Gas(Cost), GasMem(Cost, Cost), @@ -101,7 +100,6 @@ impl Gasometer { } } - #[cfg_attr(feature="dev", allow(cyclomatic_complexity))] /// Determine how much gas is used by the given instruction, given the machine's state. /// /// We guarantee that the final element of the returned tuple (`provided`) will be `Some` diff --git a/ethcore/evm/src/interpreter/mod.rs b/ethcore/evm/src/interpreter/mod.rs index 2042bbb1d..6ea218ecf 100644 --- a/ethcore/evm/src/interpreter/mod.rs +++ b/ethcore/evm/src/interpreter/mod.rs @@ -66,7 +66,6 @@ struct CodeReader<'a> { code: &'a [u8] } -#[cfg_attr(feature="dev", allow(len_without_is_empty))] impl<'a> CodeReader<'a> { /// Create new code reader - starting at position 0. @@ -287,7 +286,6 @@ impl Interpreter { } } - #[cfg_attr(feature="dev", allow(too_many_arguments))] fn exec_instruction( &mut self, gas: Cost, diff --git a/ethcore/res/ethereum/ellaism.json b/ethcore/res/ethereum/ellaism.json new file mode 100644 index 000000000..3036992eb --- /dev/null +++ b/ethcore/res/ethereum/ellaism.json @@ -0,0 +1,72 @@ +{ + "name": "Ellaism", + "dataDir": "ellaism", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x020000", + "difficultyBoundDivisor": "0x0800", + "durationLimit": "0x0d", + "blockReward": "0x4563918244F40000", + "homesteadTransition": "0x0", + "bombDefuseTransition": "0x0", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "ecip1017EraRounds": 10000000, + + "eip161abcTransition": "0x7fffffffffffffff", + "eip161dTransition": "0x7fffffffffffffff" + } + } + }, + "params": { + "gasLimitBoundDivisor": "0x0400", + "registrar": "0x3bb2bb5c6c9c9b7f4EF430b47Dc7e026310042ea", + "accountStartNonce": "0x00", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID": "0x40", + "chainID": "0x40", + "eip155Transition": "0x0", + "eip98Transition": "0x7fffffffffffff", + "eip86Transition": "0x7fffffffffffff" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000040", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x40000000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000", + "gasLimit": "0x1388" + }, + "nodes": [ + "enode://98b48cc7149326d00a57994ad55014a095a3e5cd4f0144cd7b034fb667d8e8017082bd90047d72c4403798b8ece11a33bd2e344d6500faba30889ebcfa5316fa@172.104.163.204:30303", + "enode://834246cc2a7584df29ccdcf3b5366f118a0e291264980376769e809665a02c4caf0d68c43eecf8390dbeaf861823b05583807af0a62542a1f3f717046b958a76@45.77.106.33:30303", + "enode://d1373d7187a20d695220fafb5746a289786bad92469d6bbe800f07572f8f444479f507cfcb8fcd8f5bee5dd44efb6185df4a1e4f1a7170408ada9876ef5b3fe4@178.79.189.58:30303", + "enode://d8059dcb137cb52b8960ca82613eeba1d121105572decd8f1d3ea22b09070645eeab548d2a3cd2914f206e1331c7870bd2bd5a231ebac6b3d4886ec3b8e627e5@173.212.216.105:30303", + "enode://5a89c8664d29a321fd4cb1b55b0f0be832ce376b5e7feb14a2073fdbd9bd7b7394169ed289dd991112b42ecfb74ea36e436bc72a1c99dcdb50d96eaf3b0ed254@213.136.91.42:30303", + "enode://9215ad77bd081e35013cb42a8ceadff9d8e94a78fcc680dff1752a54e7484badff0904e331c4b40a68be593782e55acfd800f076d22f9d2832e8483733ade149@213.14.82.125:30303", + "enode://07913818dafbadf44d4fc796fa414ec1d720ecfb087eff37efbe7134556658e92351559de788fa319c291e40b915cc26d902069d03bd935553d4efa688bdbbf8@45.32.19.37:30303", + "enode://645a59b6e6e20ed8864767a1d0c731f89ae276ed4e04c4f10becce655532d95cbe1bc9e2da3f13a6564f9ca8fe46fab2781a380b3a89148bccac883d6068f684@45.77.159.123:30303", + "enode://7c2f43b2e7fded9469917311574d267427e62cd12e1864effd15f31c1246e4e955463d843acaa37309693a515df7986cb6d160b7e85a4ca2779a798a72d90850@108.61.194.191:30303", + "enode://5dd35866da95aea15211fb1f98684f6e8c4e355e6aa3cc17585680ed53fa164477b8c52cb6ca4b24ec4d80f3d48ff9212b53feb131d825c7945a3abaaf02d24d@178.79.189.58:60606", + "enode://6c585c18024eb902ca093278af73b04863ac904caabc39ac2920c23532307c572ad92afd828a990c980d272b1f26307f2409cc97aec3ff9fe866732cae49a8c2@144.217.163.224:31337", + "enode://edd90c4cc64528802ad52fd127d80b641ff80fd43fa5292fb111c8bd2914482dffee288fd1b0d26440c6b2c669b10a53cbcd37c895ba0d6194110e100a965b2d@188.166.179.159:30303", + "enode://d19783546438e8bfc11a35457ff1f47871a2ce4801b9b2dbe7606321a9ce29007305497eb8c98d7ae9dc5a913ee5533c3691b1080f7066697c4276e6140d2eac@45.77.47.184:30303", + "enode://13ed57615447bc9bf1da4e28249369babc00d2530d6c103c12350453e469a5e90cbcdb787c436977467f5864be6e64f2520180fc60b841c8c3daf84df9450190@104.207.152.17:30303", + "enode://59c228a6a0939a0b53edf6924bc7bd1384652dc1da0777495acd0707975f485f54a77c7b2dcbeece9340a43ccd9c7ea70f0cdfe48936537d238b50e5cb5dc0b2@45.77.233.0:30303", + "enode://9d960373335c1cc38ca696dea8f2893e2a071c8f21524f21e8aae22be032acc3b67797b1d21e866f9d832943ae7d9555b8466c6ab34f473d21e547114952df37@213.32.53.183:30303" + ], + "accounts": { + "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } } + } +} diff --git a/ethcore/res/ethereum/foundation.json b/ethcore/res/ethereum/foundation.json index 620070e07..c7ff63bf6 100644 --- a/ethcore/res/ethereum/foundation.json +++ b/ethcore/res/ethereum/foundation.json @@ -173,6 +173,11 @@ "stateRoot": "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544" }, "nodes": [ + "enode://5a62f19d35c0da8b576c9414568c728d4744e6e9d436c0f9db27456400011414f515871f13a6b8e0468534b5116cfe765d7630f680f1707a38467940a9f62511@45.55.33.62:30303", + "enode://605e04a43b1156966b3a3b66b980c87b7f18522f7f712035f84576016be909a2798a438b2b17b1a8c58db314d88539a77419ca4be36148c086900fba487c9d39@188.166.255.12:30303", + "enode://dc72806c3aa8fda207c8c018aba8d6cf143728b3628b6ded8d5e8cdeb8aa05cbd53f710ecd014c9a8f0d1e98f2874bff8afb15a229202f510a9c0258d1f6d109@159.203.210.80:30303", + "enode://01f76fa0561eca2b9a7e224378dd854278735f1449793c46ad0c4e79e8775d080c21dcc455be391e90a98153c3b05dcc8935c8440de7b56fe6d67251e33f4e3c@51.15.42.252:30303", + "enode://2c9059f05c352b29d559192fe6bca272d965c9f2290632a2cfda7f83da7d2634f3ec45ae3a72c54dd4204926fb8082dcf9686e0d7504257541c86fc8569bcf4b@163.172.171.38:30303", "enode://efe4f2493f4aff2d641b1db8366b96ddacfe13e7a6e9c8f8f8cf49f9cdba0fdf3258d8c8f8d0c5db529f8123c8f1d95f36d54d590ca1bb366a5818b9a4ba521c@163.172.187.252:30303", "enode://cd6611461840543d5b9c56fbf088736154c699c43973b3a1a32390cf27106f87e58a818a606ccb05f3866de95a4fe860786fea71bf891ea95f234480d3022aa3@163.172.157.114:30303", "enode://bcc7240543fe2cf86f5e9093d05753dd83343f8fda7bf0e833f65985c73afccf8f981301e13ef49c4804491eab043647374df1c4adf85766af88a624ecc3330e@136.243.154.244:30303", @@ -184,13 +189,7 @@ "enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303", "enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303", "enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303", - "enode://4cd540b2c3292e17cff39922e864094bf8b0741fcc8c5dcea14957e389d7944c70278d872902e3d0345927f621547efa659013c400865485ab4bfa0c6596936f@138.201.144.135:30303", - - "enode://89d5dc2a81e574c19d0465f497c1af96732d1b61a41de89c2a37f35707689ac416529fae1038809852b235c2d30fd325abdc57c122feeefbeaaf802cc7e9580d@45.55.33.62:30303", - "enode://605e04a43b1156966b3a3b66b980c87b7f18522f7f712035f84576016be909a2798a438b2b17b1a8c58db314d88539a77419ca4be36148c086900fba487c9d39@188.166.255.12:30303", - "enode://016b20125f447a3b203a3cae953b2ede8ffe51290c071e7599294be84317635730c397b8ff74404d6be412d539ee5bb5c3c700618723d3b53958c92bd33eaa82@159.203.210.80:30303", - "enode://01f76fa0561eca2b9a7e224378dd854278735f1449793c46ad0c4e79e8775d080c21dcc455be391e90a98153c3b05dcc8935c8440de7b56fe6d67251e33f4e3c@51.15.42.252:30303", - "enode://8d91c8137890d29110b9463882f17ae4e279cd2c90cf56573187ed1c8546fca5f590a9f05e9f108eb1bd91767ed01ede4daad9e001b61727885eaa246ddb39c2@163.172.171.38:30303" + "enode://4cd540b2c3292e17cff39922e864094bf8b0741fcc8c5dcea14957e389d7944c70278d872902e3d0345927f621547efa659013c400865485ab4bfa0c6596936f@138.201.144.135:30303" ], "accounts": { "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, diff --git a/ethcore/res/ethereum/kovan.json b/ethcore/res/ethereum/kovan.json index 71eab873d..64f1c947c 100644 --- a/ethcore/res/ethereum/kovan.json +++ b/ethcore/res/ethereum/kovan.json @@ -12,19 +12,17 @@ "0x00427feae2419c15b89d1c21af10d1b6650a4d3d", "0x4Ed9B08e6354C70fE6F8CB0411b0d3246b424d6c", "0x0020ee4Be0e2027d76603cB751eE069519bA81A1", - "0x0010f94b296a852aaac52ea6c5ac72e03afd032d", - "0x007733a1FE69CF3f2CF989F81C7b4cAc1693387A", "0x00E6d2b931F55a3f1701c7389d592a7778897879", "0x00e4a10650e5a6D6001C38ff8E64F97016a1645c", - "0x00a0a24b9f0e5ec7aa4c7389b8302fd0123194de" ] }, "validateScoreTransition": 1000000, "validateStepTransition": 1500000, - "maximumUncleCount": 2 + "maximumUncleCountTransition": 5067000, + "maximumUncleCount": 0 } } }, @@ -38,7 +36,11 @@ "forkCanonHash": "0x0a66d93c2f727dca618fabaf70c39b37018c73d78b939d8b11efbbd09034778f", "validateReceiptsTransition" : 1000000, "eip155Transition": 1000000, - "validateChainIdTransition": 1000000 + "validateChainIdTransition": 1000000, + "eip140Transition": 5067000, + "eip211Transition": 5067000, + "eip214Transition": 5067000, + "eip658Transition": 5067000 }, "genesis": { "seal": { @@ -55,13 +57,17 @@ "0x0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, "0x0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, "0x0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, + "0x0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": 5067000, "pricing": { "modexp": { "divisor": 20 } } } }, + "0x0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": 5067000, "pricing": { "linear": { "base": 500, "word": 0 } } } }, + "0x0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": 5067000, "pricing": { "linear": { "base": 40000, "word": 0 } } } }, + "0x0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": 5067000, "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } }, "0x00521965e7bd230323c423d96c657db5b79d099f": { "balance": "1606938044258990275541962092341162602522202993782792835301376" } }, "nodes": [ - "enode://0518a3d35d4a7b3e8c433e7ffd2355d84a1304ceb5ef349787b556197f0c87fad09daed760635b97d52179d645d3e6d16a37d2cc0a9945c2ddf585684beb39ac@40.68.248.100:30303", - "enode://dcf984764db421fa0cd8dc7fc02ae378545723abb94d179f55325514cc30185eaea3dcefde6e358b7cdbe970c50b7c49e841618713a9a72d6f3f59ad9949ec6b@52.165.239.18:30303", + "enode://56abaf065581a5985b8c5f4f88bd202526482761ba10be9bfdcd14846dd01f652ec33fde0f8c0fd1db19b59a4c04465681fcef50e11380ca88d25996191c52de@40.71.221.215:30303", + "enode://d07827483dc47b368eaf88454fb04b41b7452cf454e194e2bd4c14f98a3278fed5d819dbecd0d010407fc7688d941ee1e58d4f9c6354d3da3be92f55c17d7ce3@52.166.117.77:30303", + "enode://8fa162563a8e5a05eef3e1cd5abc5828c71344f7277bb788a395cce4a0e30baf2b34b92fe0b2dbbba2313ee40236bae2aab3c9811941b9f5a7e8e90aaa27ecba@52.165.239.18:30303", "enode://7e2e7f00784f516939f94e22bdc6cf96153603ca2b5df1c7cc0f90a38e7a2f218ffb1c05b156835e8b49086d11fdd1b3e2965be16baa55204167aa9bf536a4d9@52.243.47.56:30303", - "enode://d51b3e98bf35addf2f1d0ea1ffc90483e24d7c60b0fb3be1701e818f3d6778c06e53fdec737a534fe222956296f9d6e909baa025916a94601897e5c7136a7d95@40.71.221.215:30303", - "enode://419d42e300e8fd379ff6d045d93d7e66a091441e7b3c9f1d3d10088d8634ad37721e6bf86148f78c3f1b9f1360dc566ca8ee830b2d2079bc9f7171ea6152bb64@52.166.117.77:30303" + "enode://0518a3d35d4a7b3e8c433e7ffd2355d84a1304ceb5ef349787b556197f0c87fad09daed760635b97d52179d645d3e6d16a37d2cc0a9945c2ddf585684beb39ac@40.68.248.100:30303" ] } diff --git a/ethcore/res/wasm-tests b/ethcore/res/wasm-tests index d6185ea16..d9d6133c1 160000 --- a/ethcore/res/wasm-tests +++ b/ethcore/res/wasm-tests @@ -1 +1 @@ -Subproject commit d6185ea16eaba7ff685c069c2064819f9549c4d7 +Subproject commit d9d6133c1bc5dca4c74c9eb758a39546a0d46b45 diff --git a/ethcore/src/basic_types.rs b/ethcore/src/basic_types.rs index 838834eea..e7f372352 100644 --- a/ethcore/src/basic_types.rs +++ b/ethcore/src/basic_types.rs @@ -22,7 +22,6 @@ pub type LogBloom = ::log_entry::LogBloom; /// Constant 2048-bit datum for 0. Often used as a default. pub static ZERO_LOGBLOOM: LogBloom = ::bigint::hash::H2048([0x00; 256]); -#[cfg_attr(feature="dev", allow(enum_variant_names))] /// Semantic boolean for when a seal/signature is included. pub enum Seal { /// The seal/signature is included. diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index d701a17eb..2925f8d44 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -301,7 +301,6 @@ pub struct SealedBlock { } impl<'x> OpenBlock<'x> { - #[cfg_attr(feature="dev", allow(too_many_arguments))] /// Create a new `OpenBlock` ready for transaction pushing. pub fn new( engine: &'x EthEngine, @@ -380,8 +379,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) @@ -609,7 +613,6 @@ impl IsBlock for SealedBlock { } /// Enact the block given by block header, transactions and uncles -#[cfg_attr(feature="dev", allow(too_many_arguments))] pub fn enact( header: &Header, transactions: &[SignedTransaction], @@ -683,7 +686,6 @@ fn push_transactions(block: &mut OpenBlock, transactions: &[SignedTransaction]) // TODO [ToDr] Pass `PreverifiedBlock` by move, this will avoid unecessary allocation /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header -#[cfg_attr(feature="dev", allow(too_many_arguments))] pub fn enact_verified( block: &PreverifiedBlock, engine: &EthEngine, @@ -726,7 +728,6 @@ mod tests { use transaction::SignedTransaction; /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header - #[cfg_attr(feature="dev", allow(too_many_arguments))] fn enact_bytes( block_bytes: &[u8], engine: &EthEngine, @@ -773,7 +774,6 @@ mod tests { } /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards - #[cfg_attr(feature="dev", allow(too_many_arguments))] fn enact_and_seal( block_bytes: &[u8], engine: &EthEngine, diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 8e71d5f42..cdd8f82c3 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -970,7 +970,6 @@ impl BlockChain { self.cache_man.lock().note_used(CacheId::BlockDetails(block_hash)); } - #[cfg_attr(feature="dev", allow(similar_names))] /// Inserts the block into backing cache database. /// Expects the block to be valid and already verified. /// If the block is already known, does nothing. @@ -1475,7 +1474,6 @@ impl BlockChain { #[cfg(test)] mod tests { - #![cfg_attr(feature="dev", allow(similar_names))] use std::sync::Arc; use rustc_hex::FromHex; use hash::keccak; @@ -1583,7 +1581,6 @@ mod tests { } #[test] - #[cfg_attr(feature="dev", allow(cyclomatic_complexity))] fn test_find_uncles() { let mut canon_chain = ChainGenerator::default(); let mut finalizer = BlockFinalizer::default(); @@ -1793,7 +1790,6 @@ mod tests { } #[test] - #[cfg_attr(feature="dev", allow(cyclomatic_complexity))] fn test_small_fork() { let mut canon_chain = ChainGenerator::default(); let mut finalizer = BlockFinalizer::default(); diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index c78c230ca..ade0844ec 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -1855,7 +1855,7 @@ impl MiningBlockChainClient for Client { let mut open_block = OpenBlock::new( engine, self.factories.clone(), - false, // TODO: this will need to be parameterised once we want to do immediate mining insertion. + self.tracedb.read().tracing_enabled(), self.state_db.lock().boxed_clone_canon(&h), best_header, self.build_last_hashes(h.clone()), @@ -1870,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; @@ -1885,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(); diff --git a/ethcore/src/engines/authority_round/mod.rs b/ethcore/src/engines/authority_round/mod.rs index 8c693ca07..d9e1cb3b7 100644 --- a/ethcore/src/engines/authority_round/mod.rs +++ b/ethcore/src/engines/authority_round/mod.rs @@ -51,8 +51,12 @@ mod finality; /// `AuthorityRound` params. pub struct AuthorityRoundParams { - /// Time to wait before next block or authority switching. - pub step_duration: Duration, + /// Time to wait before next block or authority switching, + /// in seconds. + /// + /// Deliberately typed as u16 as too high of a value leads + /// to slow block issuance. + pub step_duration: u16, /// Starting step, pub start_step: Option, /// Valid validators. @@ -65,20 +69,30 @@ 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, } +const U16_MAX: usize = ::std::u16::MAX as usize; + impl From for AuthorityRoundParams { fn from(p: ethjson::spec::AuthorityRoundParams) -> Self { + let mut step_duration_usize: usize = p.step_duration.into(); + if step_duration_usize > U16_MAX { + step_duration_usize = U16_MAX; + warn!(target: "engine", "step_duration is too high ({}), setting it to {}", step_duration_usize, U16_MAX); + } AuthorityRoundParams { - step_duration: Duration::from_secs(p.step_duration.into()), + step_duration: step_duration_usize as u16, validators: new_validator_set(p.validators), start_step: p.start_step.map(Into::into), validate_score_transition: p.validate_score_transition.map_or(0, Into::into), 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), } } @@ -89,26 +103,47 @@ impl From for AuthorityRoundParams { struct Step { calibrate: bool, // whether calibration is enabled. inner: AtomicUsize, - duration: Duration, + duration: u16, } impl Step { fn load(&self) -> usize { self.inner.load(AtomicOrdering::SeqCst) } fn duration_remaining(&self) -> Duration { let now = unix_now(); - let step_end = self.duration * (self.load() as u32 + 1); - if step_end > now { - step_end - now - } else { - Duration::from_secs(0) + let expected_seconds = (self.load() as u64) + .checked_add(1) + .and_then(|ctr| ctr.checked_mul(self.duration as u64)); + match expected_seconds { + Some(secs) => { + let step_end = Duration::from_secs(secs); + if step_end > now { + step_end - now + } else { + Duration::from_secs(0) + } + }, + None => { + let ctr = self.load(); + error!(target: "engine", "Step counter is too high: {}, aborting", ctr); + panic!("step counter is too high: {}", ctr) + }, } + } fn increment(&self) { - self.inner.fetch_add(1, AtomicOrdering::SeqCst); + use std::usize; + // fetch_add won't panic on overflow but will rather wrap + // around, leading to zero as the step counter, which might + // lead to unexpected situations, so it's better to shut down. + if self.inner.fetch_add(1, AtomicOrdering::SeqCst) == usize::MAX { + error!(target: "engine", "Step counter is too high: {}, aborting", usize::MAX); + panic!("step counter is too high: {}", usize::MAX); + } + } fn calibrate(&self) { if self.calibrate { - let new_step = unix_now().as_secs() / self.duration.as_secs(); + let new_step = unix_now().as_secs() / (self.duration as u64); self.inner.store(new_step as usize, AtomicOrdering::SeqCst); } } @@ -123,6 +158,11 @@ impl Step { } } +// Chain scoring: total weight is sqrt(U256::max_value())*height - step +fn calculate_score(parent_step: U256, current_step: U256) -> U256 { + U256::from(U128::max_value()) + parent_step - current_step +} + struct EpochManager { epoch_transition_hash: H256, epoch_transition_number: BlockNumber, @@ -221,6 +261,7 @@ pub struct AuthorityRound { epoch_manager: Mutex, immediate_transitions: bool, block_reward: U256, + maximum_uncle_count_transition: u64, maximum_uncle_count: usize, machine: EthereumMachine, } @@ -350,8 +391,12 @@ impl AsMillis for Duration { impl AuthorityRound { /// Create a new instance of AuthorityRound engine. pub fn new(our_params: AuthorityRoundParams, machine: EthereumMachine) -> Result, Error> { + if our_params.step_duration == 0 { + error!(target: "engine", "Authority Round step duration can't be zero, aborting"); + panic!("authority_round: step duration can't be zero") + } let should_timeout = our_params.start_step.is_none(); - let initial_step = our_params.start_step.unwrap_or_else(|| (unix_now().as_secs() / our_params.step_duration.as_secs())) as usize; + let initial_step = our_params.start_step.unwrap_or_else(|| (unix_now().as_secs() / (our_params.step_duration as u64))) as usize; let engine = Arc::new( AuthorityRound { transition_service: IoService::<()>::start()?, @@ -369,6 +414,7 @@ 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, }); @@ -441,15 +487,23 @@ impl Engine for AuthorityRound { ] } - fn maximum_uncle_count(&self) -> usize { self.maximum_uncle_count } + 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); + let parent_step = header_step(parent).expect("Header has been verified; qed"); + let score = calculate_score(parent_step.into(), self.step.load().into()); + header.set_difficulty(score); } fn seals_internally(&self) -> Option { + // TODO: accept a `&Call` here so we can query the validator set. Some(self.signer.read().is_some()) } @@ -457,14 +511,23 @@ impl Engine for AuthorityRound { /// /// This operation is synchronous and may (quite reasonably) not be available, in which case /// `Seal::None` will be returned. - fn generate_seal(&self, block: &ExecutedBlock) -> Seal { + fn generate_seal(&self, block: &ExecutedBlock, parent: &Header) -> Seal { // first check to avoid generating signature most of the time // (but there's still a race to the `compare_and_swap`) if !self.can_propose.load(AtomicOrdering::SeqCst) { return Seal::None; } let header = block.header(); + let parent_step: U256 = header_step(parent) + .expect("Header has been verified; qed").into(); + let step = self.step.load(); + let expected_diff = calculate_score(parent_step, step.into()); + + if header.difficulty() != &expected_diff { + return Seal::None; + } + // fetch correct validator set for current epoch, taking into account // finality of previous transitions. let active_set; @@ -491,6 +554,13 @@ impl Engine for AuthorityRound { }; if is_step_proposer(validators, header.parent_hash(), step, header.author()) { + // this is guarded against by `can_propose` unless the block was signed + // on the same step (implies same key) and on a different node. + if parent_step == step.into() { + warn!("Attempted to seal block on the same step as parent. Is this authority sealing with more than one node?"); + return Seal::None; + } + if let Ok(signature) = self.sign(header.bare_hash()) { trace!(target: "engine", "generate_seal: Issuing a block for step {}.", step); @@ -505,6 +575,7 @@ impl Engine for AuthorityRound { trace!(target: "engine", "generate_seal: {} not a proposer for step {}.", header.author(), step); } + Seal::None } @@ -546,7 +617,7 @@ impl Engine for AuthorityRound { } /// Check the number of seal fields. - fn verify_block_basic(&self, header: &Header,) -> Result<(), Error> { + fn verify_block_basic(&self, header: &Header) -> Result<(), Error> { if header.number() >= self.validate_score_transition && *header.difficulty() >= U256::from(U128::max_value()) { Err(From::from(BlockError::DifficultyOutOfBounds( OutOfBounds { min: None, max: Some(U256::from(U128::max_value())), found: *header.difficulty() } @@ -857,17 +928,51 @@ mod tests { let b2 = b2.close_and_lock(); engine.set_signer(tap.clone(), addr1, "1".into()); - if let Seal::Regular(seal) = engine.generate_seal(b1.block()) { + if let Seal::Regular(seal) = engine.generate_seal(b1.block(), &genesis_header) { assert!(b1.clone().try_seal(engine, seal).is_ok()); // Second proposal is forbidden. - assert!(engine.generate_seal(b1.block()) == Seal::None); + assert!(engine.generate_seal(b1.block(), &genesis_header) == Seal::None); } engine.set_signer(tap, addr2, "2".into()); - if let Seal::Regular(seal) = engine.generate_seal(b2.block()) { + if let Seal::Regular(seal) = engine.generate_seal(b2.block(), &genesis_header) { assert!(b2.clone().try_seal(engine, seal).is_ok()); // Second proposal is forbidden. - assert!(engine.generate_seal(b2.block()) == Seal::None); + assert!(engine.generate_seal(b2.block(), &genesis_header) == Seal::None); + } + } + + #[test] + fn checks_difficulty_in_generate_seal() { + let tap = Arc::new(AccountProvider::transient_provider()); + let addr1 = tap.insert_account(keccak("1").into(), "1").unwrap(); + let addr2 = tap.insert_account(keccak("0").into(), "0").unwrap(); + + let spec = Spec::new_test_round(); + let engine = &*spec.engine; + + let genesis_header = spec.genesis_header(); + let db1 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); + let db2 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); + let last_hashes = Arc::new(vec![genesis_header.hash()]); + + let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap(); + let b1 = b1.close_and_lock(); + let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes, addr2, (3141562.into(), 31415620.into()), vec![], false).unwrap(); + let b2 = b2.close_and_lock(); + + engine.set_signer(tap.clone(), addr1, "1".into()); + match engine.generate_seal(b1.block(), &genesis_header) { + Seal::None | Seal::Proposal(_) => panic!("wrong seal"), + Seal::Regular(_) => { + engine.step(); + + engine.set_signer(tap.clone(), addr2, "0".into()); + match engine.generate_seal(b2.block(), &genesis_header) { + Seal::Regular(_) | Seal::Proposal(_) => panic!("sealed despite wrong difficulty"), + Seal::None => {} + } + } } } @@ -950,12 +1055,13 @@ mod tests { fn reports_skipped() { let last_benign = Arc::new(AtomicUsize::new(0)); let params = AuthorityRoundParams { - step_duration: Default::default(), + step_duration: 1, 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: 0, maximum_uncle_count: 0, block_reward: Default::default(), }; @@ -984,4 +1090,77 @@ 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: 1, + 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); + } + + #[test] + #[should_panic(expected="counter is too high")] + fn test_counter_increment_too_high() { + use super::Step; + let step = Step { + calibrate: false, + inner: AtomicUsize::new(::std::usize::MAX), + duration: 1, + }; + step.increment(); + } + + #[test] + #[should_panic(expected="counter is too high")] + fn test_counter_duration_remaining_too_high() { + use super::Step; + let step = Step { + calibrate: false, + inner: AtomicUsize::new(::std::usize::MAX), + duration: 1, + }; + step.duration_remaining(); + } + + #[test] + #[should_panic(expected="authority_round: step duration can't be zero")] + fn test_step_duration_zero() { + let last_benign = Arc::new(AtomicUsize::new(0)); + let params = AuthorityRoundParams { + step_duration: 0, + 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: 0, + maximum_uncle_count: 0, + block_reward: Default::default(), + }; + + 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(); + } } diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index b0d30b6ee..8aedbff72 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -107,7 +107,7 @@ impl Engine for BasicAuthority { } /// Attempt to seal the block internally. - fn generate_seal(&self, block: &ExecutedBlock) -> Seal { + fn generate_seal(&self, block: &ExecutedBlock, _parent: &Header) -> Seal { let header = block.header(); let author = header.author(); if self.validators.contains(header.parent_hash(), author) { @@ -251,7 +251,7 @@ mod tests { let last_hashes = Arc::new(vec![genesis_header.hash()]); let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![], false).unwrap(); let b = b.close_and_lock(); - if let Seal::Regular(seal) = engine.generate_seal(b.block()) { + if let Seal::Regular(seal) = engine.generate_seal(b.block(), &genesis_header) { assert!(b.try_seal(engine, seal).is_ok()); } } diff --git a/ethcore/src/engines/instant_seal.rs b/ethcore/src/engines/instant_seal.rs index a0700099d..6c6aef415 100644 --- a/ethcore/src/engines/instant_seal.rs +++ b/ethcore/src/engines/instant_seal.rs @@ -43,7 +43,7 @@ impl Engine for InstantSeal fn seals_internally(&self) -> Option { Some(true) } - fn generate_seal(&self, block: &M::LiveBlock) -> Seal { + fn generate_seal(&self, block: &M::LiveBlock, _parent: &M::Header) -> Seal { if block.transactions().is_empty() { Seal::None } else { Seal::Regular(Vec::new()) } } @@ -72,7 +72,7 @@ mod tests { let last_hashes = Arc::new(vec![genesis_header.hash()]); let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::default(), (3141562.into(), 31415620.into()), vec![], false).unwrap(); let b = b.close_and_lock(); - if let Seal::Regular(seal) = engine.generate_seal(b.block()) { + if let Seal::Regular(seal) = engine.generate_seal(b.block(), &genesis_header) { assert!(b.try_seal(engine, seal).is_ok()); } } diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 89de861bb..88ad68f63 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -192,7 +192,8 @@ pub trait Engine: Sync + Send { fn extra_info(&self, _header: &M::Header) -> BTreeMap { BTreeMap::new() } /// Maximum number of uncles a block is allowed to declare. - fn maximum_uncle_count(&self) -> usize { 0 } + 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 } @@ -225,7 +226,7 @@ pub trait Engine: Sync + Send { /// /// It is fine to require access to state or a full client for this function, since /// light clients do not generate seals. - fn generate_seal(&self, _block: &M::LiveBlock) -> Seal { Seal::None } + fn generate_seal(&self, _block: &M::LiveBlock, _parent: &M::Header) -> Seal { Seal::None } /// Verify a locally-generated seal of a header. /// @@ -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) } diff --git a/ethcore/src/engines/null_engine.rs b/ethcore/src/engines/null_engine.rs index 518f0f8f5..cc48a225a 100644 --- a/ethcore/src/engines/null_engine.rs +++ b/ethcore/src/engines/null_engine.rs @@ -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,7 +96,7 @@ impl Engine for NullEngine { self.machine.note_rewards(block, &[(author, result_block_reward)], &uncle_rewards) } - fn maximum_uncle_count(&self) -> usize { 2 } + fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 2 } fn verify_local_seal(&self, _header: &M::Header) -> Result<(), M::Error> { Ok(()) diff --git a/ethcore/src/engines/tendermint/mod.rs b/ethcore/src/engines/tendermint/mod.rs index 97cc18c03..af1a5a013 100644 --- a/ethcore/src/engines/tendermint/mod.rs +++ b/ethcore/src/engines/tendermint/mod.rs @@ -450,7 +450,7 @@ impl Engine 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 } @@ -483,7 +483,7 @@ impl Engine for Tendermint { /// /// This operation is synchronous and may (quite reasonably) not be available, in which case /// `Seal::None` will be returned. - fn generate_seal(&self, block: &ExecutedBlock) -> Seal { + fn generate_seal(&self, block: &ExecutedBlock, _parent: &Header) -> Seal { let header = block.header(); let author = header.author(); // Only proposer can generate seal if None was generated. @@ -805,7 +805,7 @@ mod tests { let last_hashes = Arc::new(vec![genesis_header.hash()]); let b = OpenBlock::new(spec.engine.as_ref(), Default::default(), false, db.boxed_clone(), &genesis_header, last_hashes, proposer, (3141562.into(), 31415620.into()), vec![], false).unwrap(); let b = b.close(); - if let Seal::Proposal(seal) = spec.engine.generate_seal(b.block()) { + if let Seal::Proposal(seal) = spec.engine.generate_seal(b.block(), &genesis_header) { (b, seal) } else { panic!() diff --git a/ethcore/src/engines/validator_set/contract.rs b/ethcore/src/engines/validator_set/contract.rs index 54e86b98b..da35484c6 100644 --- a/ethcore/src/engines/validator_set/contract.rs +++ b/ethcore/src/engines/validator_set/contract.rs @@ -58,7 +58,7 @@ impl ValidatorContract { let client = self.client.read().clone(); Box::new(move |a, d| client.as_ref() .and_then(Weak::upgrade) - .ok_or("No client!".into()) + .ok_or_else(|| "No client!".into()) .and_then(|c| { match c.as_full_client() { Some(c) => c.transact_contract(a, d) diff --git a/ethcore/src/engines/validator_set/multi.rs b/ethcore/src/engines/validator_set/multi.rs index 3f53b0363..c46cd2276 100644 --- a/ethcore/src/engines/validator_set/multi.rs +++ b/ethcore/src/engines/validator_set/multi.rs @@ -138,7 +138,7 @@ impl ValidatorSet for Multi { } *self.block_number.write() = Box::new(move |id| client .upgrade() - .ok_or("No client!".into()) + .ok_or_else(|| "No client!".into()) .and_then(|c| c.block_number(id).ok_or("Unknown block".into()))); } } diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/src/engines/validator_set/safe_contract.rs index 695f2a658..3fe9bda6d 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -311,7 +311,7 @@ impl ValidatorSet for ValidatorSafeContract { let client = self.client.read().clone(); Box::new(move |addr, data| client.as_ref() .and_then(Weak::upgrade) - .ok_or("No client!".into()) + .ok_or_else(|| "No client!".into()) .and_then(|c| { match c.as_full_client() { Some(c) => c.call_contract(id, addr, data), diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 9548fe511..70fdcbb82 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -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,7 +181,7 @@ impl Engine for Arc { } } - fn maximum_uncle_count(&self) -> usize { 2 } + 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); @@ -336,7 +336,6 @@ impl Engine for Arc { } } -#[cfg_attr(feature="dev", allow(wrong_self_convention))] impl Ethash { fn calculate_difficulty(&self, header: &Header, parent: &Header) -> U256 { const EXP_DIFF_PERIOD: u64 = 100_000; @@ -520,7 +519,7 @@ 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); diff --git a/ethcore/src/ethereum/mod.rs b/ethcore/src/ethereum/mod.rs index 09b272cb3..2ecef4941 100644 --- a/ethcore/src/ethereum/mod.rs +++ b/ethcore/src/ethereum/mod.rs @@ -75,6 +75,11 @@ pub fn new_musicoin<'a, T: Into>>(params: T) -> Spec { load(params.into(), include_bytes!("../../res/ethereum/musicoin.json")) } +/// Create a new Ellaism mainnet chain spec. +pub fn new_ellaism<'a, T: Into>>(params: T) -> Spec { + load(params.into(), include_bytes!("../../res/ethereum/ellaism.json")) +} + /// Create a new Kovan testnet chain spec. pub fn new_kovan<'a, T: Into>>(params: T) -> Spec { load(params.into(), include_bytes!("../../res/ethereum/kovan.json")) diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index ff7805299..a88eae85f 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -85,7 +85,6 @@ impl<'a, T: 'a, V: 'a, B: 'a> Externalities<'a, T, V, B> where T: Tracer, V: VMTracer, B: StateBackend { /// Basic `Externalities` constructor. - #[cfg_attr(feature="dev", allow(too_many_arguments))] pub fn new(state: &'a mut State, env_info: &'a EnvInfo, machine: &'a Machine, @@ -302,7 +301,6 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> Ok(self.state.code_size(address)?.unwrap_or(0)) } - #[cfg_attr(feature="dev", allow(match_ref_pats))] fn ret(mut self, gas: &U256, data: &ReturnData, apply_state: bool) -> vm::Result where Self: Sized { let handle_copy = |to: &mut Option<&mut Bytes>| { diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index b969a39dd..fdbd35e95 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -16,23 +16,6 @@ #![warn(missing_docs)] #![cfg_attr(feature="benches", feature(test))] -#![cfg_attr(feature="dev", feature(plugin))] -#![cfg_attr(feature="dev", plugin(clippy))] - -// Clippy settings -// Most of the time much more readable -#![cfg_attr(feature="dev", allow(needless_range_loop))] -// Shorter than if-else -#![cfg_attr(feature="dev", allow(match_bool))] -// Keeps consistency (all lines with `.clone()`). -#![cfg_attr(feature="dev", allow(clone_on_copy))] -// Complains on Box when implementing From> -#![cfg_attr(feature="dev", allow(boxed_local))] -// Complains about nested modules with same name as parent -#![cfg_attr(feature="dev", allow(module_inception))] -// TODO [todr] a lot of warnings to be fixed -#![cfg_attr(feature="dev", allow(assign_op_pattern))] - //! Ethcore library //! diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 6ef26c48b..df82a926c 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -366,7 +366,6 @@ impl Miner { ) } - #[cfg_attr(feature="dev", allow(match_same_arms))] /// Prepares new block for sealing including top transactions from queue. fn prepare_block(&self, chain: &MiningBlockChainClient) -> (ClosedBlock, Option) { let _timer = PerfTimer::new("prepare_block"); @@ -534,7 +533,13 @@ impl Miner { fn seal_and_import_block_internally(&self, chain: &MiningBlockChainClient, block: ClosedBlock) -> bool { if !block.transactions().is_empty() || self.forced_sealing() || Instant::now() > *self.next_mandatory_reseal.read() { trace!(target: "miner", "seal_block_internally: attempting internal seal."); - match self.engine.generate_seal(block.block()) { + + let parent_header = match chain.block_header(BlockId::Hash(*block.header().parent_hash())) { + Some(hdr) => hdr.decode(), + None => return false, + }; + + match self.engine.generate_seal(block.block(), &parent_header) { // Save proposal for later seal submission and broadcast it. Seal::Proposal(seal) => { trace!(target: "miner", "Received a Proposal seal."); @@ -702,8 +707,6 @@ impl Miner { /// Are we allowed to do a non-mandatory reseal? fn tx_reseal_allowed(&self) -> bool { Instant::now() > *self.next_allowed_reseal.lock() } - #[cfg_attr(feature="dev", allow(wrong_self_convention))] - #[cfg_attr(feature="dev", allow(redundant_closure))] fn from_pending_block(&self, latest_block_number: BlockNumber, from_chain: F, map_block: G) -> H where F: Fn() -> H, G: FnOnce(&ClosedBlock) -> H { let sealing_work = self.sealing_work.lock(); @@ -863,7 +866,6 @@ impl MinerService for Miner { results } - #[cfg_attr(feature="dev", allow(collapsible_if))] fn import_own_transaction( &self, chain: &MiningBlockChainClient, diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs index 6e1504c21..e82591cf1 100644 --- a/ethcore/src/miner/mod.rs +++ b/ethcore/src/miner/mod.rs @@ -15,8 +15,6 @@ // along with Parity. If not, see . #![warn(missing_docs)] -#![cfg_attr(all(nightly, feature="dev"), feature(plugin))] -#![cfg_attr(all(nightly, feature="dev"), plugin(clippy))] //! Miner module //! Keeps track of transactions and mined block. diff --git a/ethcore/src/miner/transaction_queue.rs b/ethcore/src/miner/transaction_queue.rs index 9cb835b28..f553e187e 100644 --- a/ethcore/src/miner/transaction_queue.rs +++ b/ethcore/src/miner/transaction_queue.rs @@ -136,7 +136,6 @@ impl PartialOrd for TransactionOrigin { } impl Ord for TransactionOrigin { - #[cfg_attr(feature="dev", allow(match_same_arms))] fn cmp(&self, other: &TransactionOrigin) -> Ordering { if *other == *self { return Ordering::Equal; @@ -516,7 +515,6 @@ pub struct AccountDetails { const GAS_PRICE_BUMP_SHIFT: usize = 3; // 2 = 25%, 3 = 12.5%, 4 = 6.25% /// Describes the strategy used to prioritize transactions in the queue. -#[cfg_attr(feature="dev", allow(enum_variant_names))] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum PrioritizationStrategy { /// Use only gas price. Disregards the actual computation cost of the transaction. diff --git a/ethcore/src/service.rs b/ethcore/src/service.rs index 078f02d46..a4b5b22f6 100644 --- a/ethcore/src/service.rs +++ b/ethcore/src/service.rs @@ -186,7 +186,6 @@ impl IoHandler for ClientIoHandler { } } - #[cfg_attr(feature="dev", allow(single_match))] fn message(&self, _io: &IoContext, net_message: &ClientIoMessage) { use std::thread; diff --git a/ethcore/src/snapshot/tests/helpers.rs b/ethcore/src/snapshot/tests/helpers.rs index a6f0cbb05..ee1238252 100644 --- a/ethcore/src/snapshot/tests/helpers.rs +++ b/ethcore/src/snapshot/tests/helpers.rs @@ -57,7 +57,6 @@ impl StateProducer { } } - #[cfg_attr(feature="dev", allow(let_and_return))] /// Tick the state producer. This alters the state, writing new data into /// the database. pub fn tick(&mut self, rng: &mut R, db: &mut HashDB) { diff --git a/ethcore/src/state/account.rs b/ethcore/src/state/account.rs index 2d9a37e81..f4b6f0b4b 100644 --- a/ethcore/src/state/account.rs +++ b/ethcore/src/state/account.rs @@ -35,6 +35,15 @@ use std::cell::{RefCell, Cell}; const STORAGE_CACHE_ITEMS: usize = 8192; +/// Boolean type for clean/dirty status. +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub enum Filth { + /// Data has not been changed. + Clean, + /// Data has been changed. + Dirty, +} + /// Single account in the system. /// Keeps track of changes to the code and storage. /// The changes are applied in `commit_storage` and `commit_code` diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index a7a5a303b..532094a4f 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -604,7 +604,6 @@ impl State { } /// Add `incr` to the balance of account `a`. - #[cfg_attr(feature="dev", allow(single_match))] pub fn add_balance(&mut self, a: &Address, incr: &U256, cleanup_mode: CleanupMode) -> trie::Result<()> { trace!(target: "state", "add_balance({}, {}): {}", a, incr, self.balance(a)?); let is_value_transfer = !incr.is_zero(); @@ -744,8 +743,6 @@ impl State { } /// Commits our cached account changes into the trie. - #[cfg_attr(feature="dev", allow(match_ref_pats))] - #[cfg_attr(feature="dev", allow(needless_borrow))] pub fn commit(&mut self) -> Result<(), Error> { // first, commit the sub trees. let mut accounts = self.cache.borrow_mut(); diff --git a/ethcore/src/state/substate.rs b/ethcore/src/state/substate.rs index 8d90c64d1..5d385089f 100644 --- a/ethcore/src/state/substate.rs +++ b/ethcore/src/state/substate.rs @@ -58,7 +58,6 @@ impl Substate { } /// Get the cleanup mode object from this. - #[cfg_attr(feature="dev", allow(wrong_self_convention))] pub fn to_cleanup_mode(&mut self, schedule: &Schedule) -> CleanupMode { match (schedule.kill_dust != CleanDustMode::Off, schedule.no_empty, schedule.kill_empty) { (false, false, _) => CleanupMode::ForceCreate, diff --git a/ethcore/src/state_db.rs b/ethcore/src/state_db.rs index 953b7b388..1b8d4c406 100644 --- a/ethcore/src/state_db.rs +++ b/ethcore/src/state_db.rs @@ -427,7 +427,6 @@ impl state::Backend for StateDB { cache.accounts.get_mut(addr).map(|a| a.as_ref().map(|a| a.clone_basic())) } - #[cfg_attr(feature="dev", allow(map_clone))] fn get_cached_code(&self, hash: &H256) -> Option>> { let mut cache = self.code_cache.lock(); diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index 9d7136cb1..e90eedc4b 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -34,7 +34,6 @@ use cache_manager::CacheManager; const TRACE_DB_VER: &'static [u8] = b"1.0"; #[derive(Debug, Copy, Clone)] -#[cfg_attr(feature="dev", allow(enum_variant_names))] enum TraceDBIndex { /// Block traces index. BlockTraces = 0, diff --git a/ethcore/src/verification/queue/mod.rs b/ethcore/src/verification/queue/mod.rs index bf7c9ef5c..5cd0f35f7 100644 --- a/ethcore/src/verification/queue/mod.rs +++ b/ethcore/src/verification/queue/mod.rs @@ -164,7 +164,6 @@ struct QueueSignal { } impl QueueSignal { - #[cfg_attr(feature="dev", allow(bool_comparison))] fn set_sync(&self) { // Do not signal when we are about to close if self.deleting.load(AtomicOrdering::Relaxed) { @@ -179,7 +178,6 @@ impl QueueSignal { } } - #[cfg_attr(feature="dev", allow(bool_comparison))] fn set_async(&self) { // Do not signal when we are about to close if self.deleting.load(AtomicOrdering::Relaxed) { diff --git a/ethcore/src/verification/verification.rs b/ethcore/src/verification/verification.rs index c56641916..18b86c090 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/src/verification/verification.rs @@ -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(); @@ -268,7 +273,7 @@ pub fn verify_header_params(header: &Header, engine: &EthEngine, is_full: bool) } if is_full { - let max_time = get_time().sec as u64 + 30; + let max_time = get_time().sec as u64 + 15; if header.timestamp() > max_time { return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: Some(max_time), min: None, found: header.timestamp() }))) } @@ -493,7 +498,6 @@ mod tests { } #[test] - #[cfg_attr(feature="dev", allow(similar_names))] fn test_verify_block() { use rlp::RlpStream; @@ -641,9 +645,15 @@ mod tests { check_fail_timestamp(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine)); header = good.clone(); - header.set_timestamp(get_time().sec as u64 + 40); + header.set_timestamp(get_time().sec as u64 + 20); check_fail_timestamp(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine)); + header = good.clone(); + header.set_timestamp(get_time().sec as u64 + 10); + header.set_uncles_hash(good_uncles_hash.clone()); + header.set_transactions_root(good_transactions_root.clone()); + check_ok(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine)); + header = good.clone(); header.set_number(9); check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine, &bc), @@ -653,7 +663,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() ]; diff --git a/ethcore/vm/src/ext.rs b/ethcore/vm/src/ext.rs index 3f5004586..daf00e074 100644 --- a/ethcore/vm/src/ext.rs +++ b/ethcore/vm/src/ext.rs @@ -96,7 +96,6 @@ pub trait Ext { /// Returns Err, if we run out of gas. /// Otherwise returns call_result which contains gas left /// and true if subcall was successfull. - #[cfg_attr(feature="dev", allow(too_many_arguments))] fn call(&mut self, gas: &U256, sender_address: &Address, diff --git a/ethcore/wasm/src/env.rs b/ethcore/wasm/src/env.rs index ed4bda68b..cae673858 100644 --- a/ethcore/wasm/src/env.rs +++ b/ethcore/wasm/src/env.rs @@ -69,17 +69,17 @@ pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[ ), Static( "_ccall", - &[I32; 6], + &[I64, I32, I32, I32, I32, I32, I32], Some(I32), ), Static( "_dcall", - &[I32; 5], + &[I64, I32, I32, I32, I32, I32], Some(I32), ), Static( "_scall", - &[I32; 5], + &[I64, I32, I32, I32, I32, I32], Some(I32), ), Static( diff --git a/ethcore/wasm/src/lib.rs b/ethcore/wasm/src/lib.rs index 30ce03665..5b4e59fcb 100644 --- a/ethcore/wasm/src/lib.rs +++ b/ethcore/wasm/src/lib.rs @@ -111,6 +111,7 @@ impl vm::Vm for WasmInterpreter { address: params.address, sender: params.sender, origin: params.origin, + code_address: params.code_address, value: params.value.value(), }, &self.program, diff --git a/ethcore/wasm/src/runtime.rs b/ethcore/wasm/src/runtime.rs index bad325fbf..700233951 100644 --- a/ethcore/wasm/src/runtime.rs +++ b/ethcore/wasm/src/runtime.rs @@ -104,6 +104,7 @@ pub struct RuntimeContext { pub address: Address, pub sender: Address, pub origin: Address, + pub code_address: Address, pub value: U256, } @@ -305,6 +306,7 @@ impl<'a, 'b> Runtime<'a, 'b> { // // method signature: // fn ( + // gas: i64, // address: *const u8, // val_ptr: *const u8, // input_ptr: *const u8, @@ -323,6 +325,7 @@ impl<'a, 'b> Runtime<'a, 'b> { // // signature (same as static call): // fn ( + // gas: i64, // address: *const u8, // input_ptr: *const u8, // input_len: u32, @@ -330,7 +333,7 @@ impl<'a, 'b> Runtime<'a, 'b> { // result_len: u32, // ) -> i32 - self.do_call(false, CallType::CallCode, context) + self.do_call(false, CallType::DelegateCall, context) } fn do_call( @@ -363,6 +366,9 @@ impl<'a, 'b> Runtime<'a, 'b> { let address = self.pop_address(&mut context)?; trace!(target: "wasm", " address: {:?}", address); + let gas = context.value_stack.pop_as::()? as u64; + trace!(target: "wasm", " gas: {:?}", gas); + if let Some(ref val) = val { let address_balance = self.ext.balance(&self.context.address) .map_err(|_| UserTrap::BalanceQueryError)?; @@ -377,16 +383,16 @@ impl<'a, 'b> Runtime<'a, 'b> { let mut result = Vec::with_capacity(result_alloc_len as usize); result.resize(result_alloc_len as usize, 0); - let gas = self.gas_left() - .map_err(|_| UserTrap::InvalidGasState)? - .into(); + // todo: optimize to use memory views once it's in let payload = self.memory.get(input_ptr, input_len as usize)?; + self.charge(|_| gas.into())?; + let call_result = self.ext.call( - &gas, - &self.context.sender, - &self.context.address, + &gas.into(), + match call_type { CallType::DelegateCall => &self.context.sender, _ => &self.context.address }, + match call_type { CallType::Call | CallType::StaticCall => &address, _ => &self.context.address }, val, &payload, &address, @@ -396,12 +402,16 @@ impl<'a, 'b> Runtime<'a, 'b> { match call_result { vm::MessageCallResult::Success(gas_left, _) => { - self.gas_counter = self.gas_limit - gas_left.low_u64(); + // cannot overflow, before making call gas_counter was incremented with gas, and gas_left < gas + self.gas_counter = self.gas_counter - gas_left.low_u64(); + self.memory.set(result_ptr, &result)?; Ok(Some(0i32.into())) }, vm::MessageCallResult::Reverted(gas_left, _) => { - self.gas_counter = self.gas_limit - gas_left.low_u64(); + // cannot overflow, before making call gas_counter was incremented with gas, and gas_left < gas + self.gas_counter = self.gas_counter - gas_left.low_u64(); + self.memory.set(result_ptr, &result)?; Ok(Some((-1i32).into())) }, @@ -416,6 +426,7 @@ impl<'a, 'b> Runtime<'a, 'b> { { // signature (same as code call): // fn ( + // gas: i64, // address: *const u8, // input_ptr: *const u8, // input_len: u32, diff --git a/ethcore/wasm/src/tests.rs b/ethcore/wasm/src/tests.rs index a387f9292..115199aeb 100644 --- a/ethcore/wasm/src/tests.rs +++ b/ethcore/wasm/src/tests.rs @@ -60,7 +60,7 @@ fn empty() { test_finalize(interpreter.exec(params, &mut ext)).unwrap() }; - assert_eq!(gas_left, U256::from(99_982)); + assert_eq!(gas_left, U256::from(96_678)); } // This test checks if the contract deserializes payload header properly. @@ -112,7 +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)); + assert_eq!(gas_left, U256::from(15_860)); } // This test checks if the contract can allocate memory and pass pointer to the result stream properly. @@ -147,7 +147,7 @@ fn identity() { sender, "Idenity test contract does not return the sender passed" ); - assert_eq!(gas_left, U256::from(99_844)); + assert_eq!(gas_left, U256::from(96_540)); } // Dispersion test sends byte array and expect the contract to 'disperse' the original elements with @@ -180,7 +180,7 @@ fn dispersion() { result, vec![0u8, 0, 125, 11, 197, 7, 255, 8, 19, 0] ); - assert_eq!(gas_left, U256::from(96_393)); + assert_eq!(gas_left, U256::from(96_116)); } #[test] @@ -208,7 +208,7 @@ fn suicide_not() { result, vec![0u8] ); - assert_eq!(gas_left, U256::from(96_725)); + assert_eq!(gas_left, U256::from(96_461)); } #[test] @@ -240,7 +240,7 @@ fn suicide() { }; assert!(ext.suicides.contains(&refund)); - assert_eq!(gas_left, U256::from(96_687)); + assert_eq!(gas_left, U256::from(96_429)); } #[test] @@ -270,7 +270,7 @@ fn create() { assert!(ext.calls.contains( &FakeCall { call_type: FakeCallType::Create, - gas: U256::from(65_899), + gas: U256::from(62_545), sender_address: None, receive_address: None, value: Some(1_000_000_000.into()), @@ -278,9 +278,52 @@ fn create() { code_address: None, } )); - assert_eq!(gas_left, U256::from(65_892)); + assert_eq!(gas_left, U256::from(62_538)); } +#[test] +fn call_msg() { + ::ethcore_logger::init_log(); + + let sender: Address = "01030507090b0d0f11131517191b1d1f21232527".parse().unwrap(); + let receiver: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap(); + let contract_address: Address = "0d461d4174b4ae35775c4a342f1e5e1e4e6c4db5".parse().unwrap(); + + let mut params = ActionParams::default(); + params.sender = sender.clone(); + params.address = receiver.clone(); + params.code_address = contract_address.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(load_sample!("call.wasm"))); + params.data = Some(Vec::new()); + + let mut ext = FakeExt::new(); + ext.balances.insert(receiver.clone(), U256::from(10000000000u64)); + + let gas_left = { + let mut interpreter = wasm_interpreter(); + let result = interpreter.exec(params, &mut ext).expect("Interpreter to execute without any errors"); + match result { + GasLeft::Known(gas_left) => gas_left, + GasLeft::NeedsReturn { .. } => { panic!("Call test should not return payload"); }, + } + }; + + trace!(target: "wasm", "fake_calls: {:?}", &ext.calls); + assert!(ext.calls.contains( + &FakeCall { + call_type: FakeCallType::Call, + gas: U256::from(33_000), + sender_address: Some(receiver), + receive_address: Some(Address::from([99, 88, 77, 66, 55, 44, 33, 22, 11, 0, 11, 22, 33, 44, 55, 66, 77, 88, 99, 0])), + value: Some(1000000000.into()), + data: vec![129u8, 123, 113, 107, 101, 97], + code_address: Some(Address::from([99, 88, 77, 66, 55, 44, 33, 22, 11, 0, 11, 22, 33, 44, 55, 66, 77, 88, 99, 0])), + } + )); + + assert_eq!(gas_left, U256::from(95_699)); +} #[test] fn call_code() { @@ -312,7 +355,7 @@ fn call_code() { assert!(ext.calls.contains( &FakeCall { call_type: FakeCallType::Call, - gas: U256::from(98_713), + gas: U256::from(20_000), sender_address: Some(sender), receive_address: Some(receiver), value: None, @@ -324,7 +367,7 @@ fn call_code() { // siphash result let res = LittleEndian::read_u32(&result[..]); assert_eq!(res, 4198595614); - assert_eq!(gas_left, U256::from(93_855)); + assert_eq!(gas_left, U256::from(90_550)); } #[test] @@ -333,6 +376,7 @@ fn call_static() { let sender: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap(); let receiver: Address = "01030507090b0d0f11131517191b1d1f21232527".parse().unwrap(); + let contract_address: Address = "0d461d4174b4ae35775c4a342f1e5e1e4e6c4db5".parse().unwrap(); let mut params = ActionParams::default(); params.sender = sender.clone(); @@ -341,6 +385,7 @@ fn call_static() { params.code = Some(Arc::new(load_sample!("call_static.wasm"))); params.data = Some(Vec::new()); params.value = ActionValue::transfer(1_000_000_000); + params.code_address = contract_address.clone(); let mut ext = FakeExt::new(); @@ -357,9 +402,9 @@ fn call_static() { assert!(ext.calls.contains( &FakeCall { call_type: FakeCallType::Call, - gas: U256::from(98_713), - sender_address: Some(sender), - receive_address: Some(receiver), + gas: U256::from(20_000), + sender_address: Some(receiver), + receive_address: Some("13077bfb00000000000000000000000000000000".parse().unwrap()), value: None, data: vec![1u8, 2, 3, 5, 7, 11], code_address: Some("13077bfb00000000000000000000000000000000".parse().unwrap()), @@ -370,7 +415,7 @@ fn call_static() { let res = LittleEndian::read_u32(&result[..]); assert_eq!(res, 317632590); - assert_eq!(gas_left, U256::from(93_855)); + assert_eq!(gas_left, U256::from(90_550)); } // Realloc test @@ -393,7 +438,7 @@ fn realloc() { } }; assert_eq!(result, vec![0u8; 2]); - assert_eq!(gas_left, U256::from(96_723)); + assert_eq!(gas_left, U256::from(96_445)); } // Tests that contract's ability to read from a storage @@ -419,7 +464,7 @@ fn storage_read() { }; assert_eq!(Address::from(&result[12..32]), address); - assert_eq!(gas_left, U256::from(99_767)); + assert_eq!(gas_left, U256::from(96_463)); } // Tests keccak calculation @@ -445,7 +490,7 @@ fn keccak() { }; assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87")); - assert_eq!(gas_left, U256::from(81_446)); + assert_eq!(gas_left, U256::from(81_067)); } // memcpy test. @@ -477,7 +522,7 @@ fn memcpy() { }; assert_eq!(result, test_payload); - assert_eq!(gas_left, U256::from(72_216)); + assert_eq!(gas_left, U256::from(71_940)); } // memmove test. @@ -509,7 +554,7 @@ fn memmove() { }; assert_eq!(result, test_payload); - assert_eq!(gas_left, U256::from(72_216)); + assert_eq!(gas_left, U256::from(71_940)); } // memset test @@ -534,7 +579,7 @@ fn memset() { }; assert_eq!(result, vec![228u8; 8192]); - assert_eq!(gas_left, U256::from(72_196)); + assert_eq!(gas_left, U256::from(71_921)); } macro_rules! reqrep_test { @@ -591,7 +636,7 @@ fn math_add() { U256::from_dec_str("1888888888888888888888888888887").unwrap(), (&result[..]).into() ); - assert_eq!(gas_left, U256::from(95_524)); + assert_eq!(gas_left, U256::from(95_384)); } // multiplication @@ -613,7 +658,7 @@ fn math_mul() { U256::from_dec_str("888888888888888888888888888887111111111111111111111111111112").unwrap(), (&result[..]).into() ); - assert_eq!(gas_left, U256::from(94_674)); + assert_eq!(gas_left, U256::from(94_374)); } // subtraction @@ -635,7 +680,7 @@ fn math_sub() { U256::from_dec_str("111111111111111111111111111111").unwrap(), (&result[..]).into() ); - assert_eq!(gas_left, U256::from(95_516)); + assert_eq!(gas_left, U256::from(95_372)); } // subtraction with overflow @@ -677,7 +722,7 @@ fn math_div() { U256::from_dec_str("1125000").unwrap(), (&result[..]).into() ); - assert_eq!(gas_left, U256::from(88_514)); + assert_eq!(gas_left, U256::from(88_356)); } // This test checks the ability of wasm contract to invoke @@ -765,7 +810,7 @@ fn externs() { "Gas limit requested and returned does not match" ); - assert_eq!(gas_left, U256::from(94_858)); + assert_eq!(gas_left, U256::from(95_321)); } #[test] @@ -791,7 +836,7 @@ fn embedded_keccak() { }; assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87")); - assert_eq!(gas_left, U256::from(81_446)); + assert_eq!(gas_left, U256::from(81_067)); } /// This test checks the correctness of log extern @@ -826,5 +871,5 @@ fn events() { assert_eq!(&log_entry.data, b"gnihtemos"); assert_eq!(&result, b"gnihtemos"); - assert_eq!(gas_left, U256::from(79_637)); + assert_eq!(gas_left, U256::from(79_206)); } diff --git a/ethcrypto/src/lib.rs b/ethcrypto/src/lib.rs index 3d34a7975..15b8640ee 100644 --- a/ethcrypto/src/lib.rs +++ b/ethcrypto/src/lib.rs @@ -174,7 +174,6 @@ pub mod aes { } /// ECDH functions -#[cfg_attr(feature="dev", allow(similar_names))] pub mod ecdh { use secp256k1::{ecdh, key, Error as SecpError}; use ethkey::{Secret, Public, SECP256K1}; @@ -199,7 +198,6 @@ pub mod ecdh { } /// ECIES function -#[cfg_attr(feature="dev", allow(similar_names))] pub mod ecies { use rcrypto::digest::Digest; use rcrypto::sha2::Sha256; diff --git a/ethstore/src/ethstore.rs b/ethstore/src/ethstore.rs index 1b8cf7ac2..07e7dd879 100755 --- a/ethstore/src/ethstore.rs +++ b/ethstore/src/ethstore.rs @@ -29,8 +29,6 @@ use presale::PresaleWallet; use json::{self, Uuid, OpaqueKeyFile}; use {import, Error, SimpleSecretStore, SecretStore, SecretVaultRef, StoreAccountRef, Derivation, OpaqueSecret}; -const REFRESH_TIME_SEC: u64 = 5; - /// Accounts store. pub struct EthStore { store: EthMultiStore, @@ -49,6 +47,16 @@ impl EthStore { }) } + /// Modify account refresh timeout - how often they are re-read from `KeyDirectory`. + /// + /// Setting this to low values (or 0) will cause new accounts to be picked up quickly, + /// although it may induce heavy disk reads and is not recommended if you manage many keys (say over 10k). + /// + /// By default refreshing is disabled, so only accounts created using this instance of `EthStore` are taken into account. + pub fn set_refresh_time(&self, time: Duration) { + self.store.set_refresh_time(time) + } + fn get(&self, account: &StoreAccountRef) -> Result { let mut accounts = self.store.get_accounts(account)?.into_iter(); accounts.next().ok_or(Error::InvalidAccount) @@ -254,6 +262,7 @@ pub struct EthMultiStore { struct Timestamp { dir_hash: Option, last_checked: Instant, + refresh_time: Duration, } impl EthMultiStore { @@ -272,16 +281,28 @@ impl EthMultiStore { timestamp: Mutex::new(Timestamp { dir_hash: None, last_checked: Instant::now(), + // by default we never refresh accounts + refresh_time: Duration::from_secs(u64::max_value()), }), }; store.reload_accounts()?; Ok(store) } + /// Modify account refresh timeout - how often they are re-read from `KeyDirectory`. + /// + /// Setting this to low values (or 0) will cause new accounts to be picked up quickly, + /// although it may induce heavy disk reads and is not recommended if you manage many keys (say over 10k). + /// + /// By default refreshing is disabled, so only accounts created using this instance of `EthStore` are taken into account. + pub fn set_refresh_time(&self, time: Duration) { + self.timestamp.lock().refresh_time = time; + } + fn reload_if_changed(&self) -> Result<(), Error> { let mut last_timestamp = self.timestamp.lock(); let now = Instant::now(); - if (now - last_timestamp.last_checked) > Duration::from_secs(REFRESH_TIME_SEC) { + if now - last_timestamp.last_checked > last_timestamp.refresh_time { let dir_hash = Some(self.dir.unique_repr()?); last_timestamp.last_checked = now; if last_timestamp.dir_hash == dir_hash { @@ -319,22 +340,23 @@ impl EthMultiStore { } fn get_accounts(&self, account: &StoreAccountRef) -> Result, Error> { - { + let from_cache = |account| { let cache = self.cache.read(); if let Some(accounts) = cache.get(account) { if !accounts.is_empty() { - return Ok(accounts.clone()) + return Some(accounts.clone()) } } - } - self.reload_if_changed()?; - let cache = self.cache.read(); - let accounts = cache.get(account).ok_or(Error::InvalidAccount)?; - if accounts.is_empty() { - Err(Error::InvalidAccount) - } else { - Ok(accounts.clone()) + None + }; + + match from_cache(account) { + Some(accounts) => Ok(accounts), + None => { + self.reload_if_changed()?; + from_cache(account).ok_or(Error::InvalidAccount) + } } } @@ -470,11 +492,20 @@ impl SimpleSecretStore for EthMultiStore { } fn account_ref(&self, address: &Address) -> Result { - use std::collections::Bound; - self.reload_if_changed()?; - let cache = self.cache.read(); - let mut r = cache.range((Bound::Included(*address), Bound::Included(*address))); - r.next().ok_or(Error::InvalidAccount).map(|(k, _)| k.clone()) + let read_from_cache = |address: &Address| { + use std::collections::Bound; + let cache = self.cache.read(); + let mut r = cache.range((Bound::Included(*address), Bound::Included(*address))); + r.next().map(|(k, _)| k.clone()) + }; + + match read_from_cache(address) { + Some(account) => Ok(account), + None => { + self.reload_if_changed()?; + read_from_cache(address).ok_or(Error::InvalidAccount) + } + } } fn accounts(&self) -> Result, Error> { diff --git a/hash-fetch/src/client.rs b/hash-fetch/src/client.rs index 6a756aa82..65b9d0d22 100644 --- a/hash-fetch/src/client.rs +++ b/hash-fetch/src/client.rs @@ -150,6 +150,9 @@ impl HashFetch for Client { URLHintResult::Dapp(dapp) => { dapp.url() }, + URLHintResult::GithubDapp(content) => { + content.url + }, URLHintResult::Content(content) => { content.url }, diff --git a/hash-fetch/src/urlhint.rs b/hash-fetch/src/urlhint.rs index 0670266df..c629cd8da 100644 --- a/hash-fetch/src/urlhint.rs +++ b/hash-fetch/src/urlhint.rs @@ -32,6 +32,10 @@ use bytes::Bytes; pub type BoxFuture = Box + Send>; const COMMIT_LEN: usize = 20; +/// GithubHint entries with commit set as `0x0..01` should be treated +/// as Github Dapp, downloadable zip files, than can be extracted, containing +/// the manifest.json file along with the dapp +static GITHUB_DAPP_COMMIT: &[u8; COMMIT_LEN] = &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; /// RAW Contract interface. /// Should execute transaction using current blockchain state. @@ -93,6 +97,8 @@ pub struct Content { pub enum URLHintResult { /// Dapp Dapp(GithubApp), + /// GithubDapp + GithubDapp(Content), /// Content Content(Content), } @@ -122,6 +128,15 @@ impl URLHintContract { } } +fn get_urlhint_content(account_slash_repo: String, owner: Address) -> Content { + let mime = guess_mime_type(&account_slash_repo).unwrap_or(mime::APPLICATION_JSON); + Content { + url: account_slash_repo, + mime, + owner, + } +} + fn decode_urlhint_output(output: (String, ::bigint::hash::H160, Address)) -> Option { let (account_slash_repo, commit, owner) = output; @@ -130,13 +145,15 @@ fn decode_urlhint_output(output: (String, ::bigint::hash::H160, Address)) -> Opt } let commit = GithubApp::commit(&commit); + if commit == Some(Default::default()) { - let mime = guess_mime_type(&account_slash_repo).unwrap_or(mime::APPLICATION_JSON); - return Some(URLHintResult::Content(Content { - url: account_slash_repo, - mime: mime, - owner: owner, - })); + let content = get_urlhint_content(account_slash_repo, owner); + return Some(URLHintResult::Content(content)); + } + + if commit == Some(*GITHUB_DAPP_COMMIT) { + let content = get_urlhint_content(account_slash_repo, owner); + return Some(URLHintResult::GithubDapp(content)); } let (account, repo) = { diff --git a/js-old/package-lock.json b/js-old/package-lock.json index fded97d5a..ae13792fb 100644 --- a/js-old/package-lock.json +++ b/js-old/package-lock.json @@ -15,9 +15,9 @@ } }, "@parity/api": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@parity/api/-/api-2.1.5.tgz", - "integrity": "sha512-HkvMIhIwDMEIyTmXqEjWn1C2qes0qJO270bQldRfCZf0XiOGXG726EzV3FUpUbVONCVQ9riDviAl3fw6D+N6nA==", + "version": "2.1.14", + "resolved": "https://registry.npmjs.org/@parity/api/-/api-2.1.14.tgz", + "integrity": "sha512-1IiaXJCXkhNUNeWiMcLKrdXcPnLjdH58zIc8zk4IgwFvJ3ZNriV1W6lGHghHB9GbNcHgpZdpYt4NUvu67oWZ9w==", "requires": { "@parity/abi": "2.1.2", "@parity/jsonrpc": "2.1.4", @@ -2165,7 +2165,6 @@ "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", "integrity": "sha1-CpSJ8UTecO+zzkMArM2zKeL8VDs=", - "dev": true, "requires": { "core-js": "2.4.1", "regenerator-runtime": "0.10.5" @@ -2174,8 +2173,7 @@ "regenerator-runtime": { "version": "0.10.5", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", - "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", - "dev": true + "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=" } } }, @@ -3282,8 +3280,7 @@ "core-js": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz", - "integrity": "sha1-TekR5mew6ukSTjQlS1OupvxhjT4=", - "dev": true + "integrity": "sha1-TekR5mew6ukSTjQlS1OupvxhjT4=" }, "core-util-is": { "version": "1.0.2", @@ -3419,11 +3416,6 @@ "randomfill": "1.0.3" } }, - "crypto-js": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.8.tgz", - "integrity": "sha1-cV8HC/YBTyrpkqmLOSkli3E/CNU=" - }, "css-color-names": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", @@ -11017,24 +11009,8 @@ "react-inspector": { "version": "github:paritytech/react-inspector#73b5214261a5131821eb9088f58d7e5f31210c23", "requires": { - "babel-runtime": "6.26.0", + "babel-runtime": "6.23.0", "is-dom": "1.0.9" - }, - "dependencies": { - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" - } - }, - "core-js": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", - "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=" - } } }, "react-intl": { @@ -13494,22 +13470,6 @@ } } }, - "web3": { - "version": "0.17.0-beta", - "resolved": "https://registry.npmjs.org/web3/-/web3-0.17.0-beta.tgz", - "integrity": "sha1-V684JFv/ejIJn3zleA+tW7wA2ls=", - "requires": { - "bignumber.js": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2", - "crypto-js": "3.1.8", - "utf8": "2.1.2", - "xmlhttprequest": "1.8.0" - }, - "dependencies": { - "bignumber.js": { - "version": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2" - } - } - }, "webidl-conversions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", @@ -13813,11 +13773,6 @@ "integrity": "sha1-TYuPHszTQZqjYgYb7O9RXh5VljU=", "dev": true }, - "xmlhttprequest": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", - "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" - }, "xss-filters": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/xss-filters/-/xss-filters-1.2.7.tgz", diff --git a/js-old/package.json b/js-old/package.json index 9022be9d6..4dd928a82 100644 --- a/js-old/package.json +++ b/js-old/package.json @@ -31,7 +31,7 @@ "build:app": "webpack --config webpack/app", "build:dll": "webpack --config webpack/vendor", "ci:build": "cross-env NODE_ENV=production npm run build", - "clean": "rimraf ./.build ./.coverage ./.happypack ./.npmjs ./build ./node_modules/.cache", + "clean": "rimraf ./.build ./.coverage ./.happypack ./.npmjs ./node_modules/.cache", "lint": "npm run lint:css && npm run lint:js", "lint:css": "stylelint ./src/**/*.css", "lint:js": "eslint --ignore-path .gitignore ./src/", @@ -133,7 +133,7 @@ }, "dependencies": { "@parity/abi": "2.1.x", - "@parity/api": "2.1.x", + "@parity/api": "^2.1.14", "@parity/wordlist": "1.1.x", "base32.js": "0.1.0", "bignumber.js": "3.0.1", @@ -203,7 +203,6 @@ "utf8": "2.1.2", "valid-url": "1.0.9", "validator": "6.2.0", - "web3": "0.17.0-beta", "whatwg-fetch": "2.0.1", "worker-loader": "^0.8.0", "zxcvbn": "4.4.1" diff --git a/js-old/scripts/test.js b/js-old/scripts/test.js index a6f526296..266bd623e 100644 --- a/js-old/scripts/test.js +++ b/js-old/scripts/test.js @@ -1 +1 @@ -// test script 21 +// test script 22 diff --git a/js-old/src/index.ejs b/js-old/src/index.ejs index b1a23576e..305568f5b 100644 --- a/js-old/src/index.ejs +++ b/js-old/src/index.ejs @@ -33,7 +33,6 @@
Loading
- diff --git a/js-old/src/ui/AccountCard/accountCard.css b/js-old/src/ui/AccountCard/accountCard.css index e6cafe656..d260b78b7 100644 --- a/js-old/src/ui/AccountCard/accountCard.css +++ b/js-old/src/ui/AccountCard/accountCard.css @@ -20,7 +20,6 @@ background-color: rgba(0, 0, 0, 0.8); display: flex; flex-direction: row; - height: 100%; overflow: hidden; transition: transform ease-out 0.1s; transform: scale(1); diff --git a/js-old/src/ui/Certifications/certifications.js b/js-old/src/ui/Certifications/certifications.js index 0693cf2d7..26ff3006d 100644 --- a/js-old/src/ui/Certifications/certifications.js +++ b/js-old/src/ui/Certifications/certifications.js @@ -19,7 +19,7 @@ import { connect } from 'react-redux'; import { hashToImageUrl } from '~/redux/providers/imagesReducer'; -import defaultIcon from '../../../assets/images/certifications/unknown.svg'; +import defaultIcon from '~/../assets/images/certifications/unknown.svg'; import styles from './certifications.css'; @@ -28,7 +28,6 @@ class Certifications extends Component { address: PropTypes.string.isRequired, certifications: PropTypes.array.isRequired, className: PropTypes.string, - dappsUrl: PropTypes.string.isRequired, showOnlyIcon: PropTypes.bool } @@ -48,7 +47,7 @@ class Certifications extends Component { renderCertification = (certification) => { const { name, icon } = certification; - const { dappsUrl, showOnlyIcon } = this.props; + const { showOnlyIcon } = this.props; const classNames = [ showOnlyIcon @@ -68,7 +67,7 @@ class Certifications extends Component { className={ styles.icon } src={ icon - ? `${dappsUrl}${hashToImageUrl(icon)}` + ? hashToImageUrl(icon) : defaultIcon } /> diff --git a/js-old/src/ui/Container/container.css b/js-old/src/ui/Container/container.css index e01387406..219962c7b 100644 --- a/js-old/src/ui/Container/container.css +++ b/js-old/src/ui/Container/container.css @@ -22,7 +22,8 @@ $transitionAll: all 0.75s cubic-bezier(0.23, 1, 0.32, 1); .container { background: $background; flex: 1; - height: 100%; + /* Broke after Chrome upgrade, just commenting for now */ + /* height: 100%; */ padding: 0em; max-width: 100%; position: relative; @@ -64,7 +65,8 @@ $transitionAll: all 0.75s cubic-bezier(0.23, 1, 0.32, 1); .padded { background-color: transparent !important; border-radius: 0 !important; - height: 100%; + /* Broke after Chrome upgrade, just commenting for now */ + /* height: 100%; */ position: relative; overflow: auto; } diff --git a/js-old/src/ui/Form/AddressSelect/addressSelect.css b/js-old/src/ui/Form/AddressSelect/addressSelect.css index 5b7ced748..46c902c21 100644 --- a/js-old/src/ui/Form/AddressSelect/addressSelect.css +++ b/js-old/src/ui/Form/AddressSelect/addressSelect.css @@ -105,9 +105,7 @@ } .inputContainer { - display: flex; - flex-direction: column; - flex: 1; + justify-content: flex-start; .input { font-size: 1.5em; @@ -134,8 +132,6 @@ } .category { - display: flex; - flex-direction: column; margin: 0 0.5em; max-width: 35em; @@ -149,12 +145,8 @@ } .cards { - flex: 1; overflow: auto; - display: flex; - flex-direction: column; - margin: 1em 0; } diff --git a/js-old/src/ui/Form/AddressSelect/addressSelect.js b/js-old/src/ui/Form/AddressSelect/addressSelect.js index daa34d988..f02320a99 100644 --- a/js-old/src/ui/Form/AddressSelect/addressSelect.js +++ b/js-old/src/ui/Form/AddressSelect/addressSelect.js @@ -335,7 +335,7 @@ class AddressSelect extends Component { content = (
-
{ cards }
+ { cards }
); } diff --git a/js-old/src/ui/IdentityIcon/identityIcon.js b/js-old/src/ui/IdentityIcon/identityIcon.js index 1ac7743e9..58c97e01d 100644 --- a/js-old/src/ui/IdentityIcon/identityIcon.js +++ b/js-old/src/ui/IdentityIcon/identityIcon.js @@ -60,11 +60,10 @@ class IdentityIcon extends Component { } updateIcon (_address, images) { - const { api } = this.context; const { button, inline, tiny } = this.props; if (images[_address]) { - this.setState({ iconsrc: `${api.dappsUrl}${images[_address]}` }); + this.setState({ iconsrc: images[_address] }); return; } diff --git a/js-old/src/ui/IdentityIcon/identityIcon.spec.js b/js-old/src/ui/IdentityIcon/identityIcon.spec.js index f6887d7d6..d321a53b7 100644 --- a/js-old/src/ui/IdentityIcon/identityIcon.spec.js +++ b/js-old/src/ui/IdentityIcon/identityIcon.spec.js @@ -80,7 +80,7 @@ describe('ui/IdentityIcon', () => { const img = render({ address: ADDRESS2 }).find('img'); expect(img).to.have.length(1); - expect(img.props().src).to.equal('dappsUrl/reduxImage'); + // expect(img.props().src).to.equal('dappsUrl/reduxImage'); }); it('renders an with no address specified', () => { diff --git a/js-old/src/ui/Portal/portal.css b/js-old/src/ui/Portal/portal.css index 85e8be4f1..96226f363 100644 --- a/js-old/src/ui/Portal/portal.css +++ b/js-old/src/ui/Portal/portal.css @@ -82,10 +82,9 @@ $popoverZ: 3600; /* but may well be) will scretch to non-visible areas. */ &.small { - margin: 1.5em auto; - max-width: 768px; - position: relative; - width: 75%; + left: 50%; + margin: 1.5em -384px; + width: 768px; } } diff --git a/js-old/src/ui/SelectionList/selectionList.css b/js-old/src/ui/SelectionList/selectionList.css index b6e6b05f9..00c658a13 100644 --- a/js-old/src/ui/SelectionList/selectionList.css +++ b/js-old/src/ui/SelectionList/selectionList.css @@ -19,7 +19,6 @@ cursor: pointer; display: flex; flex: 1; - height: 100%; position: relative; width: 100%; @@ -29,7 +28,6 @@ } .content { - height: 100%; width: 100%; &:hover { diff --git a/js-old/src/ui/StatusIndicator/statusIndicator.css b/js-old/src/ui/StatusIndicator/statusIndicator.css index fb2b6a8be..e139ed503 100644 --- a/js-old/src/ui/StatusIndicator/statusIndicator.css +++ b/js-old/src/ui/StatusIndicator/statusIndicator.css @@ -47,7 +47,6 @@ width: calc(.9em + 5px); text-transform: initial; vertical-align: bottom; - margin-top: -1em; > .bar { display: inline-block; diff --git a/js-old/src/ui/TokenImage/tokenImage.js b/js-old/src/ui/TokenImage/tokenImage.js index af7a80a02..31d647924 100644 --- a/js-old/src/ui/TokenImage/tokenImage.js +++ b/js-old/src/ui/TokenImage/tokenImage.js @@ -16,7 +16,9 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import { fetchTokens } from '~/redux/providers/tokensActions'; import unknownImage from '~/../assets/images/contracts/unknown-64x64.png'; class TokenImage extends Component { @@ -29,27 +31,39 @@ class TokenImage extends Component { token: PropTypes.shape({ image: PropTypes.string, address: PropTypes.string - }).isRequired + }).isRequired, + fetchTokens: PropTypes.func.isRequired }; state = { error: false }; + componentWillMount () { + const { token } = this.props; + + if (token.native) { + return; + } + + if (!token.fetched) { + if (!Number.isFinite(token.index)) { + return console.warn('no token index', token); + } + + this.props.fetchTokens([ token.index ]); + } + } + render () { const { error } = this.state; - const { api } = this.context; const { image, token } = this.props; const imageurl = token.image || image; let imagesrc = unknownImage; if (imageurl && !error) { - const host = /^(\/)?api/.test(imageurl) - ? api.dappsUrl - : ''; - - imagesrc = `${host}${imageurl}`; + imagesrc = imageurl; } return ( @@ -76,7 +90,13 @@ function mapStateToProps (iniState) { }; } +function mapDispatchToProps (dispatch) { + return bindActionCreators({ + fetchTokens + }, dispatch); +} + export default connect( mapStateToProps, - null + mapDispatchToProps )(TokenImage); diff --git a/js-old/src/views/Settings/Views/defaults.js b/js-old/src/views/Settings/Views/defaults.js index 988aa9742..bb9a648c7 100644 --- a/js-old/src/views/Settings/Views/defaults.js +++ b/js-old/src/views/Settings/Views/defaults.js @@ -51,7 +51,7 @@ const defaultViews = { }, contracts: { - active: false, + active: true, onlyPersonal: true, icon: , route: '/contracts', diff --git a/js/package-lock.json b/js/package-lock.json index 4dec9b1d5..6a6a2c32c 100644 --- a/js/package-lock.json +++ b/js/package-lock.json @@ -1,6 +1,6 @@ { "name": "Parity", - "version": "1.9.22", + "version": "1.9.40", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -15,9 +15,9 @@ } }, "@parity/api": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@parity/api/-/api-2.1.5.tgz", - "integrity": "sha512-HkvMIhIwDMEIyTmXqEjWn1C2qes0qJO270bQldRfCZf0XiOGXG726EzV3FUpUbVONCVQ9riDviAl3fw6D+N6nA==", + "version": "2.1.14", + "resolved": "https://registry.npmjs.org/@parity/api/-/api-2.1.14.tgz", + "integrity": "sha512-1IiaXJCXkhNUNeWiMcLKrdXcPnLjdH58zIc8zk4IgwFvJ3ZNriV1W6lGHghHB9GbNcHgpZdpYt4NUvu67oWZ9w==", "requires": { "@parity/abi": "2.1.2", "@parity/jsonrpc": "2.1.4", @@ -40,12 +40,6 @@ } } }, - "@parity/dapp-accounts": { - "version": "github:paritytech/dapp-accounts#ca55be1774563862e7b0cc72740d0747a60036b8", - "requires": { - "@parity/dapp-vaults": "github:paritytech/dapp-vaults#1bd5de3994227e6b733e7b3e985117a59f0ad638" - } - }, "@parity/dapp-console": { "version": "github:paritytech/dapp-console#f48baba86be6ee2f04ab208731c9529c7b6f92f8", "dev": true @@ -55,11 +49,11 @@ "dev": true }, "@parity/dapp-dapp-methods": { - "version": "github:js-dist-paritytech/dapp-dapp-methods#eb583c91c657d60767fa47b7a97aba76d9535e76", + "version": "github:js-dist-paritytech/dapp-dapp-methods#7245089a8e83274372cde3c9406ae155e2083f84", "dev": true, "requires": { - "@parity/api": "2.1.5", - "@parity/shared": "2.2.7", + "@parity/api": "2.1.14", + "@parity/shared": "2.2.14", "@parity/ui": "2.2.15", "lodash": "4.17.4", "mobx": "3.3.2", @@ -80,9 +74,9 @@ "integrity": "sha512-6bICFA1c1GBz4d7vratkoqovBezJNjc8VCwnZtpPTcyLeMshAhatPV4dGgJo/eHtlOCkKAeaAKatWZhEtXt/5g==", "dev": true, "requires": { - "@parity/api": "2.1.5", + "@parity/api": "2.1.14", "@parity/etherscan": "2.1.3", - "@parity/shared": "2.2.7", + "@parity/shared": "2.2.14", "bignumber.js": "3.0.1", "brace": "0.9.0", "date-difference": "1.0.0", @@ -529,6 +523,18 @@ "integrity": "sha1-jgOPbdsUvXZa4fS1IW4SCUUR4NA=", "dev": true }, + "semantic-ui-react": { + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/semantic-ui-react/-/semantic-ui-react-0.76.0.tgz", + "integrity": "sha512-CdiIT8n7ZwUlytZkYsQMnaVGmoIZI/mVs4lijvLcR568kcnlRkYYaFKhMLq5tFDQU6+QhdTD+8WebF7ov0Ql6Q==", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "classnames": "2.2.5", + "lodash": "4.17.4", + "prop-types": "15.6.0" + } + }, "webrtc-adapter": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-2.1.0.tgz", @@ -550,7 +556,7 @@ "version": "github:js-dist-paritytech/dapp-dapp-visible#84f40feb42f1707d7f463213a990600dc11978f7", "dev": true, "requires": { - "@parity/api": "2.1.5", + "@parity/api": "2.1.14", "@parity/ui": "2.2.15", "mobx": "3.3.2", "mobx-react": "4.3.5", @@ -570,9 +576,9 @@ "integrity": "sha512-6bICFA1c1GBz4d7vratkoqovBezJNjc8VCwnZtpPTcyLeMshAhatPV4dGgJo/eHtlOCkKAeaAKatWZhEtXt/5g==", "dev": true, "requires": { - "@parity/api": "2.1.5", + "@parity/api": "2.1.14", "@parity/etherscan": "2.1.3", - "@parity/shared": "2.2.7", + "@parity/shared": "2.2.14", "bignumber.js": "3.0.1", "brace": "0.9.0", "date-difference": "1.0.0", @@ -1027,6 +1033,26 @@ "integrity": "sha1-jgOPbdsUvXZa4fS1IW4SCUUR4NA=", "dev": true }, + "semantic-ui-react": { + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/semantic-ui-react/-/semantic-ui-react-0.76.0.tgz", + "integrity": "sha512-CdiIT8n7ZwUlytZkYsQMnaVGmoIZI/mVs4lijvLcR568kcnlRkYYaFKhMLq5tFDQU6+QhdTD+8WebF7ov0Ql6Q==", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "classnames": "2.2.5", + "lodash": "4.17.4", + "prop-types": "15.6.0" + }, + "dependencies": { + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", + "dev": true + } + } + }, "webrtc-adapter": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-2.1.0.tgz", @@ -1072,22 +1098,12 @@ "version": "github:paritytech/dapp-tokenreg#a347041854eec61fd9a416b9c9d7bd1bda6b9c3d", "dev": true }, - "@parity/dapp-vaults": { - "version": "github:paritytech/dapp-vaults#1bd5de3994227e6b733e7b3e985117a59f0ad638" - }, - "@parity/dapp-web": { - "version": "github:paritytech/dapp-web#6ea1fe0c7c0d01c43788dbf6a11d1573a443ce1d", - "dev": true, - "requires": { - "base32.js": "0.1.0" - } - }, "@parity/etherscan": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/@parity/etherscan/-/etherscan-2.1.3.tgz", "integrity": "sha512-GtQMaE8t7PDOcz/K4Ud+Z6EELB47+qG5V6R7iTJ4DcueXVgiMAXK5OiNeKF3Qjd1/M4FIJdFm5NTSdC7bR38+Q==", "requires": { - "@parity/api": "2.1.5", + "@parity/api": "2.1.14", "bignumber.js": "3.0.1", "es6-promise": "4.1.1", "node-fetch": "1.7.3", @@ -1111,22 +1127,70 @@ "u2f-api-polyfill": "0.4.3" } }, + "@parity/mobx": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@parity/mobx/-/mobx-1.0.3.tgz", + "integrity": "sha512-pVzsYjnVM1BN04kT0kcGyPQ8XvEQwz5xtFrEfnR06ezah5M4Vz3xpkLsc7oRjkMpdO6QoEZ1RU9hCSNDoPA0Ww==", + "requires": { + "@parity/ledger": "2.1.2", + "babel-preset-env": "1.6.1" + }, + "dependencies": { + "babel-preset-env": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.6.1.tgz", + "integrity": "sha512-W6VIyA6Ch9ePMI7VptNn2wBM6dbG0eSz25HEiL40nQXCsXGTGZSTZu1Iap+cj3Q0S5a7T9+529l/5Bkvd+afNA==", + "requires": { + "babel-plugin-check-es2015-constants": "6.22.0", + "babel-plugin-syntax-trailing-function-commas": "6.22.0", + "babel-plugin-transform-async-to-generator": "6.24.1", + "babel-plugin-transform-es2015-arrow-functions": "6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "6.22.0", + "babel-plugin-transform-es2015-block-scoping": "6.26.0", + "babel-plugin-transform-es2015-classes": "6.24.1", + "babel-plugin-transform-es2015-computed-properties": "6.24.1", + "babel-plugin-transform-es2015-destructuring": "6.23.0", + "babel-plugin-transform-es2015-duplicate-keys": "6.24.1", + "babel-plugin-transform-es2015-for-of": "6.23.0", + "babel-plugin-transform-es2015-function-name": "6.24.1", + "babel-plugin-transform-es2015-literals": "6.22.0", + "babel-plugin-transform-es2015-modules-amd": "6.24.1", + "babel-plugin-transform-es2015-modules-commonjs": "6.24.1", + "babel-plugin-transform-es2015-modules-systemjs": "6.24.1", + "babel-plugin-transform-es2015-modules-umd": "6.24.1", + "babel-plugin-transform-es2015-object-super": "6.24.1", + "babel-plugin-transform-es2015-parameters": "6.24.1", + "babel-plugin-transform-es2015-shorthand-properties": "6.24.1", + "babel-plugin-transform-es2015-spread": "6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "6.24.1", + "babel-plugin-transform-es2015-template-literals": "6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "6.23.0", + "babel-plugin-transform-es2015-unicode-regex": "6.24.1", + "babel-plugin-transform-exponentiation-operator": "6.24.1", + "babel-plugin-transform-regenerator": "6.26.0", + "browserslist": "2.10.0", + "invariant": "2.2.2", + "semver": "5.4.1" + } + } + } + }, "@parity/plugin-signer-account": { - "version": "github:paritytech/plugin-signer-account#bd647f1163ca49fe52aedb7c69ae1bed6f1d14b7" + "version": "github:paritytech/plugin-signer-account#e151e3ca4e0d51aec93219df3661212cc15aa4cc" }, "@parity/plugin-signer-default": { - "version": "github:paritytech/plugin-signer-default#1440c8c750b3d824a5569f4302102ce64fc63b72" + "version": "github:paritytech/plugin-signer-default#0d596844063849f7b3cce8fa2d8f7cc56e8caa58" }, "@parity/plugin-signer-hardware": { - "version": "github:paritytech/plugin-signer-hardware#8fc74af1e700afb883ea4e09aeee96a3ca4f288f" + "version": "github:paritytech/plugin-signer-hardware#ae9c943baf177c15bf478429f26c169fea198ef8" }, "@parity/plugin-signer-qr": { - "version": "github:paritytech/plugin-signer-qr#fe2a63955c636399719ba2aa6c58710de6cb1b89" + "version": "github:paritytech/plugin-signer-qr#c16423de5b8a8f68ebd5f1e78e084fa959329a9f" }, "@parity/shared": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/@parity/shared/-/shared-2.2.7.tgz", - "integrity": "sha512-pqJPgF+C2Q1RY3LTsye9OwhXO0GRruzvvz465U59pvXaMe6jF3HcgHvbEIL5OUbPGQ3TnrsDYmTAgy4/Bv2FKA==", + "version": "2.2.14", + "resolved": "https://registry.npmjs.org/@parity/shared/-/shared-2.2.14.tgz", + "integrity": "sha512-YM7ZqWNyquX0+j89NPPxFBCUPgaqIVrqQ4Iqml50kz9uC/Ev07A/OB/+yYmAJ+3io6LWvDc/U5K3CpaMltTLtg==", "requires": { "@parity/ledger": "2.1.2", "eventemitter3": "2.0.3", @@ -1182,13 +1246,14 @@ } }, "@parity/ui": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@parity/ui/-/ui-3.0.4.tgz", - "integrity": "sha512-/IS+6Qxr5HGAvdaB0xud9lU2c48Crg2dY7yzkb7V+99qvqYV/OAI31IffaBOt3UbLz/QWF+Da+ZOWT96eIVlAg==", + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/@parity/ui/-/ui-3.0.20.tgz", + "integrity": "sha512-YAbuHCQOVO/eJGH53Dm8cO3NnMBpJCu+JSRrDAEzSGQ8+pHyEbepxFWzwVVERhd6KDC3dUbuDTwa2CwpYFDUJw==", "requires": { - "@parity/api": "2.1.5", + "@parity/api": "2.1.14", "@parity/etherscan": "2.1.3", - "@parity/shared": "2.2.7", + "@parity/mobx": "1.0.3", + "@parity/shared": "2.2.14", "babel-runtime": "6.26.0", "bignumber.js": "4.1.0", "brace": "0.11.0", @@ -1210,7 +1275,7 @@ "react-dom": "16.1.1", "react-dropzone": "4.2.3", "react-element-to-jsx-string": "13.1.0", - "react-event-listener": "0.5.1", + "react-event-listener": "0.5.2", "react-intl": "2.4.0", "react-markdown": "3.0.2", "react-portal": "4.0.0", @@ -1252,6 +1317,17 @@ "requires": { "hoist-non-react-statics": "2.3.1" } + }, + "semantic-ui-react": { + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/semantic-ui-react/-/semantic-ui-react-0.76.0.tgz", + "integrity": "sha512-CdiIT8n7ZwUlytZkYsQMnaVGmoIZI/mVs4lijvLcR568kcnlRkYYaFKhMLq5tFDQU6+QhdTD+8WebF7ov0Ql6Q==", + "requires": { + "babel-runtime": "6.26.0", + "classnames": "2.2.5", + "lodash": "4.17.4", + "prop-types": "15.5.10" + } } } }, @@ -1292,7 +1368,6 @@ "version": "1.3.4", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", - "dev": true, "requires": { "mime-types": "2.1.17", "negotiator": "0.6.1" @@ -1365,7 +1440,6 @@ "version": "5.5.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.1.tgz", "integrity": "sha1-s4u4h22ehr7plJVqBOch6IskjrI=", - "dev": true, "requires": { "co": "4.6.0", "fast-deep-equal": "1.0.0", @@ -1433,6 +1507,11 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" + }, "anymatch": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", @@ -1595,8 +1674,7 @@ "asn1": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", - "dev": true + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" }, "asn1.js": { "version": "4.9.2", @@ -1621,8 +1699,7 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, "assertion-error": { "version": "1.0.2", @@ -1657,11 +1734,15 @@ "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", "dev": true }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "attr-accept": { "version": "1.1.0", @@ -1674,8 +1755,8 @@ "integrity": "sha512-C9yv/UF3X+eJTi/zvfxuyfxmLibYrntpF3qoJYrMeQwgUJOZrZvpJiMG2FMQ3qnhWtF/be4pYONBBw95ZGe3vA==", "dev": true, "requires": { - "browserslist": "2.9.1", - "caniuse-lite": "1.0.30000778", + "browserslist": "2.10.0", + "caniuse-lite": "1.0.30000780", "normalize-range": "0.1.2", "num2fraction": "1.2.2", "postcss": "6.0.14", @@ -1685,14 +1766,12 @@ "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", - "dev": true + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" }, "axobject-query": { "version": "0.1.0", @@ -1812,7 +1891,6 @@ "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", - "dev": true, "requires": { "babel-helper-explode-assignable-expression": "6.24.1", "babel-runtime": "6.26.0", @@ -1856,7 +1934,6 @@ "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", - "dev": true, "requires": { "babel-runtime": "6.26.0", "babel-traverse": "6.26.0", @@ -1928,7 +2005,6 @@ "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", - "dev": true, "requires": { "babel-helper-function-name": "6.24.1", "babel-runtime": "6.26.0", @@ -2050,8 +2126,7 @@ "babel-plugin-syntax-async-functions": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", - "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", - "dev": true + "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=" }, "babel-plugin-syntax-async-generators": { "version": "6.13.0", @@ -2092,8 +2167,7 @@ "babel-plugin-syntax-exponentiation-operator": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", - "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", - "dev": true + "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=" }, "babel-plugin-syntax-export-extensions": { "version": "6.13.0", @@ -2128,8 +2202,7 @@ "babel-plugin-syntax-trailing-function-commas": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", - "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", - "dev": true + "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=" }, "babel-plugin-transform-async-generator-functions": { "version": "6.24.1", @@ -2146,7 +2219,6 @@ "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", - "dev": true, "requires": { "babel-helper-remap-async-to-generator": "6.24.1", "babel-plugin-syntax-async-functions": "6.13.0", @@ -2333,7 +2405,6 @@ "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.24.1.tgz", "integrity": "sha1-0+MQtA72ZKNmIiAAl8bUQCmPK/4=", - "dev": true, "requires": { "babel-plugin-transform-strict-mode": "6.24.1", "babel-runtime": "6.26.0", @@ -2440,7 +2511,6 @@ "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", - "dev": true, "requires": { "babel-helper-builder-binary-assignment-operator-visitor": "6.24.1", "babel-plugin-syntax-exponentiation-operator": "6.13.0", @@ -2617,9 +2687,9 @@ } }, "babel-preset-env": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.6.0.tgz", - "integrity": "sha512-OVgtQRuOZKckrILgMA5rvctvFZPv72Gua9Rt006AiPoB0DJKGN07UmaQA+qRrYgK71MVct8fFhT0EyNWYorVew==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.6.1.tgz", + "integrity": "sha512-W6VIyA6Ch9ePMI7VptNn2wBM6dbG0eSz25HEiL40nQXCsXGTGZSTZu1Iap+cj3Q0S5a7T9+529l/5Bkvd+afNA==", "dev": true, "requires": { "babel-plugin-check-es2015-constants": "6.22.0", @@ -2649,7 +2719,7 @@ "babel-plugin-transform-es2015-unicode-regex": "6.24.1", "babel-plugin-transform-exponentiation-operator": "6.24.1", "babel-plugin-transform-regenerator": "6.26.0", - "browserslist": "2.9.1", + "browserslist": "2.10.0", "invariant": "2.2.2", "semver": "5.4.1" } @@ -2671,7 +2741,7 @@ "babel-plugin-transform-es2015-function-name": "6.24.1", "babel-plugin-transform-es2015-literals": "6.22.0", "babel-plugin-transform-es2015-modules-amd": "6.24.1", - "babel-plugin-transform-es2015-modules-commonjs": "6.26.0", + "babel-plugin-transform-es2015-modules-commonjs": "6.24.1", "babel-plugin-transform-es2015-modules-systemjs": "6.24.1", "babel-plugin-transform-es2015-modules-umd": "6.24.1", "babel-plugin-transform-es2015-object-super": "6.24.1", @@ -2683,19 +2753,6 @@ "babel-plugin-transform-es2015-typeof-symbol": "6.23.0", "babel-plugin-transform-es2015-unicode-regex": "6.24.1", "babel-plugin-transform-regenerator": "6.26.0" - }, - "dependencies": { - "babel-plugin-transform-es2015-modules-commonjs": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz", - "integrity": "sha1-DYOUApt9xqvhqX7xgeAHWN0uXYo=", - "requires": { - "babel-plugin-transform-strict-mode": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-types": "6.26.0" - } - } } }, "babel-preset-flow": { @@ -2805,7 +2862,7 @@ "babel-plugin-transform-es2015-unicode-regex": "6.24.1", "babel-plugin-transform-exponentiation-operator": "6.24.1", "babel-plugin-transform-regenerator": "6.26.0", - "browserslist": "2.9.1", + "browserslist": "2.10.0", "invariant": "2.2.2", "semver": "5.4.1" } @@ -2973,17 +3030,10 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, - "base32.js": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.1.0.tgz", - "integrity": "sha1-tYLexpPC8R6JPPBk7mrFthMaIgI=", - "dev": true - }, "base64-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", - "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==", - "dev": true + "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==" }, "batch": { "version": "0.6.1", @@ -3001,7 +3051,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", - "dev": true, "optional": true, "requires": { "tweetnacl": "0.14.5" @@ -3045,6 +3094,14 @@ "readable-stream": "2.3.3" } }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "requires": { + "inherits": "2.0.3" + } + }, "blockies": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/blockies/-/blockies-0.0.2.tgz", @@ -3053,14 +3110,48 @@ "bluebird": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", - "dev": true + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" }, "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" }, + "body-parser": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "1.0.4", + "debug": "2.6.9", + "depd": "1.1.1", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "on-finished": "2.3.0", + "qs": "6.5.1", + "raw-body": "2.3.2", + "type-is": "1.6.15" + }, + "dependencies": { + "http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "requires": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": "1.3.1" + } + }, + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" + } + } + }, "bonjour": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", @@ -3085,7 +3176,6 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", - "dev": true, "requires": { "hoek": "4.2.0" } @@ -3218,6 +3308,21 @@ "randombytes": "2.0.5" } }, + "browserify-sha3": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/browserify-sha3/-/browserify-sha3-0.0.1.tgz", + "integrity": "sha1-P/NKMAbvFcD7NWflQbkaI0ASPRE=", + "requires": { + "js-sha3": "0.3.1" + }, + "dependencies": { + "js-sha3": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.3.1.tgz", + "integrity": "sha1-hhIoAhQvCChQKg0d7h2V4lO7AkM=" + } + } + }, "browserify-sign": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", @@ -3243,13 +3348,12 @@ } }, "browserslist": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.9.1.tgz", - "integrity": "sha512-3n3nPdbUqn3nWmsy4PeSQthz2ja1ndpoXta+dwFFNhveGjMg6FXpWYe12vsTpNoXJbzx3j7GZXdtoVIdvh3JbA==", - "dev": true, + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.10.0.tgz", + "integrity": "sha512-WyvzSLsuAVPOjbljXnyeWl14Ae+ukAT8MUuagKVzIDvwBxl4UAwD1xqtyQs2eWYPGUKMeC3Ol62goqYuKqTTcw==", "requires": { - "caniuse-lite": "1.0.30000778", - "electron-to-chromium": "1.3.27" + "caniuse-lite": "1.0.30000780", + "electron-to-chromium": "1.3.28" } }, "bser": { @@ -3272,12 +3376,25 @@ "isarray": "1.0.0" } }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" + }, "buffer-indexof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", "dev": true }, + "buffer-to-arraybuffer": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.2.tgz", + "integrity": "sha1-0NgFZNwxhmoZdlFUh7OrYg23yEk=", + "requires": { + "tape": "3.6.1" + } + }, "buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", @@ -3297,8 +3414,7 @@ "bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "dev": true + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" }, "caller-path": { "version": "0.1.0", @@ -3353,7 +3469,7 @@ "dev": true, "requires": { "browserslist": "1.7.7", - "caniuse-db": "1.0.30000778", + "caniuse-db": "1.0.30000780", "lodash.memoize": "4.1.2", "lodash.uniq": "4.5.0" }, @@ -3364,23 +3480,22 @@ "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "dev": true, "requires": { - "caniuse-db": "1.0.30000778", - "electron-to-chromium": "1.3.27" + "caniuse-db": "1.0.30000780", + "electron-to-chromium": "1.3.28" } } } }, "caniuse-db": { - "version": "1.0.30000778", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000778.tgz", - "integrity": "sha1-Fnxg6VQqKqYFN8RG+ziB2FOjByo=", + "version": "1.0.30000780", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000780.tgz", + "integrity": "sha1-jRl3Vh0A/w8O0ra2YUAyirRQTAo=", "dev": true }, "caniuse-lite": { - "version": "1.0.30000778", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000778.tgz", - "integrity": "sha1-8efLixOx9nREAikddfC81MMWA2k=", - "dev": true + "version": "1.0.30000780", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000780.tgz", + "integrity": "sha1-H5CV8u/UlA4LpsWZKreptkzDW6Q=" }, "capture-stack-trace": { "version": "1.0.0", @@ -3397,8 +3512,7 @@ "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "center-align": { "version": "0.1.3", @@ -3652,8 +3766,7 @@ "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" }, "coa": { "version": "1.0.4", @@ -3801,7 +3914,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", - "dev": true, "requires": { "delayed-stream": "1.0.0" } @@ -3809,8 +3921,7 @@ "commander": { "version": "2.12.2", "resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz", - "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==", - "dev": true + "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==" }, "commondir": { "version": "1.0.1", @@ -3941,14 +4052,12 @@ "content-disposition": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", - "dev": true + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" }, "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, "content-type-parser": { "version": "1.0.2", @@ -3964,14 +4073,12 @@ "cookie": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", - "dev": true + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" }, "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", - "dev": true + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, "copy-to-clipboard": { "version": "3.0.8", @@ -4078,6 +4185,15 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "cors": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz", + "integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=", + "requires": { + "object-assign": "4.1.1", + "vary": "1.1.2" + } + }, "cosmiconfig": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", @@ -4363,7 +4479,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", - "dev": true, "requires": { "boom": "5.2.0" }, @@ -4372,7 +4487,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "dev": true, "requires": { "hoek": "4.2.0" } @@ -4398,11 +4512,6 @@ "randomfill": "1.0.3" } }, - "crypto-js": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.8.tgz", - "integrity": "sha1-cV8HC/YBTyrpkqmLOSkli3E/CNU=" - }, "css-color-names": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", @@ -4668,7 +4777,7 @@ "dev": true, "requires": { "browserslist": "1.7.7", - "caniuse-db": "1.0.30000778", + "caniuse-db": "1.0.30000780", "normalize-range": "0.1.2", "num2fraction": "1.2.2", "postcss": "5.2.18", @@ -4681,8 +4790,8 @@ "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "dev": true, "requires": { - "caniuse-db": "1.0.30000778", - "electron-to-chromium": "1.3.27" + "caniuse-db": "1.0.30000780", + "electron-to-chromium": "1.3.28" } }, "has-flag": { @@ -4834,7 +4943,6 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, "requires": { "assert-plus": "1.0.0" } @@ -4872,6 +4980,104 @@ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" }, + "decompress": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.0.tgz", + "integrity": "sha1-eu3YVCflqS2s/lVnSnxQXpbQH50=", + "requires": { + "decompress-tar": "4.1.1", + "decompress-tarbz2": "4.1.1", + "decompress-targz": "4.1.1", + "decompress-unzip": "4.0.1", + "graceful-fs": "4.1.11", + "make-dir": "1.1.0", + "pify": "2.3.0", + "strip-dirs": "2.1.0" + } + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "1.0.0" + } + }, + "decompress-tar": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", + "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", + "requires": { + "file-type": "5.2.0", + "is-stream": "1.1.0", + "tar-stream": "1.5.5" + } + }, + "decompress-tarbz2": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", + "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", + "requires": { + "decompress-tar": "4.1.1", + "file-type": "6.2.0", + "is-stream": "1.1.0", + "seek-bzip": "1.0.5", + "unbzip2-stream": "1.2.5" + }, + "dependencies": { + "file-type": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", + "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==" + } + } + }, + "decompress-targz": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", + "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", + "requires": { + "decompress-tar": "4.1.1", + "file-type": "5.2.0", + "is-stream": "1.1.0" + } + }, + "decompress-unzip": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", + "integrity": "sha1-3qrM39FK6vhVePczroIQ+bSEj2k=", + "requires": { + "file-type": "3.9.0", + "get-stream": "2.3.1", + "pify": "2.3.0", + "yauzl": "2.9.1" + }, + "dependencies": { + "file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=" + }, + "get-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", + "requires": { + "object-assign": "4.1.1", + "pinkie-promise": "2.0.1" + } + }, + "yauzl": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.9.1.tgz", + "integrity": "sha1-qBmB6nCleUYTOIPwKcWCGok1mn8=", + "requires": { + "buffer-crc32": "0.2.13", + "fd-slicer": "1.0.1" + } + } + } + }, "deep-eql": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", @@ -4949,8 +5155,7 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "delegates": { "version": "1.0.0", @@ -4960,8 +5165,7 @@ "depd": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", - "dev": true + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" }, "des.js": { "version": "1.0.0", @@ -4976,8 +5180,7 @@ "destroy": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", - "dev": true + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, "detect-indent": { "version": "4.0.0", @@ -5067,7 +5270,7 @@ "dev": true, "requires": { "browserslist": "1.7.7", - "caniuse-db": "1.0.30000778", + "caniuse-db": "1.0.30000780", "css-rule-stream": "1.1.0", "duplexer2": "0.0.2", "jsonfilter": "1.1.2", @@ -5086,8 +5289,8 @@ "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "dev": true, "requires": { - "caniuse-db": "1.0.30000778", - "electron-to-chromium": "1.3.27" + "caniuse-db": "1.0.30000780", + "electron-to-chromium": "1.3.28" } }, "camelcase": { @@ -5272,8 +5475,7 @@ "dom-walk": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz", - "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=", - "dev": true + "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=" }, "domain-browser": { "version": "1.1.7", @@ -5346,11 +5548,15 @@ "readable-stream": "2.3.3" } }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, "ecc-jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "dev": true, "optional": true, "requires": { "jsbn": "0.1.1" @@ -5365,8 +5571,7 @@ "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "dev": true + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "ejs-loader": { "version": "0.3.0", @@ -5457,10 +5662,9 @@ } }, "electron-to-chromium": { - "version": "1.3.27", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.27.tgz", - "integrity": "sha1-eOy4o5kGYYe7N07t412ccFZagD0=", - "dev": true + "version": "1.3.28", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.28.tgz", + "integrity": "sha1-jdTmRYCGZE6fnwoc8y4qH53/2e4=" }, "element-resize-detector": { "version": "1.1.12", @@ -5506,8 +5710,7 @@ "encodeurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", - "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=", - "dev": true + "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=" }, "encoding": { "version": "0.1.12", @@ -5730,8 +5933,7 @@ "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, "escape-string-regexp": { "version": "1.0.5", @@ -6276,8 +6478,21 @@ "etag": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/etag/-/etag-1.7.0.tgz", - "integrity": "sha1-A9MLX2fdbmMtKUXTDWZScxo01dg=", - "dev": true + "integrity": "sha1-A9MLX2fdbmMtKUXTDWZScxo01dg=" + }, + "eth-lib": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.1.27.tgz", + "integrity": "sha512-B8czsfkJYzn2UIEMwjc7Mbj+Cy72V+/OXH/tb44LV8jhrjizQJJ325xMOMyk3+ETa6r6oi0jsUY14+om8mQMWA==", + "requires": { + "bn.js": "4.11.8", + "elliptic": "6.4.0", + "keccakjs": "0.2.1", + "nano-json-stream-parser": "0.1.2", + "servify": "0.1.12", + "ws": "3.3.2", + "xhr-request-promise": "0.1.2" + } }, "ethereum-common": { "version": "0.0.18", @@ -6308,6 +6523,22 @@ "secp256k1": "3.4.0" } }, + "ethjs-unit": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", + "integrity": "sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk=", + "requires": { + "bn.js": "4.11.6", + "number-to-bn": "1.7.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" + } + } + }, "ethjs-util": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.4.tgz", @@ -6431,7 +6662,6 @@ "version": "4.14.1", "resolved": "https://registry.npmjs.org/express/-/express-4.14.1.tgz", "integrity": "sha1-ZGwjf3ZvFIwhIK/wc4F7nk1+DTM=", - "dev": true, "requires": { "accepts": "1.3.4", "array-flatten": "1.1.1", @@ -6464,14 +6694,12 @@ "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", - "dev": true + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, "debug": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", - "dev": true, "requires": { "ms": "0.7.1" } @@ -6479,20 +6707,17 @@ "ms": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", - "dev": true + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", - "dev": true + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, "qs": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.2.0.tgz", - "integrity": "sha1-O3hIwDwt7OaalSKw+ujEEm10Xzs=", - "dev": true + "integrity": "sha1-O3hIwDwt7OaalSKw+ujEEm10Xzs=" } } }, @@ -6582,20 +6807,17 @@ "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "fast-deep-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", - "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", - "dev": true + "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=" }, "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" }, "fast-levenshtein": { "version": "2.0.6", @@ -6652,7 +6874,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", - "dev": true, "requires": { "pend": "1.2.0" } @@ -6704,6 +6925,11 @@ "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-1.3.3.tgz", "integrity": "sha1-zdTETTqiZOrC9o7BZbx5HDSvEjI=" }, + "file-type": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", + "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=" + }, "filename-regex": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", @@ -6760,7 +6986,6 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.5.1.tgz", "integrity": "sha1-LEANjUUwk1vCMlScX6OF7Afeb80=", - "dev": true, "requires": { "debug": "2.2.0", "escape-html": "1.0.3", @@ -6773,7 +6998,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", - "dev": true, "requires": { "ms": "0.7.1" } @@ -6781,8 +7005,7 @@ "ms": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", - "dev": true + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" } } }, @@ -6830,6 +7053,14 @@ "integrity": "sha1-Bq1/4Z3dsQQiZEOAZKKjL+4SuHI=", "dev": true }, + "for-each": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.2.tgz", + "integrity": "sha1-LEBFC5NI6X8oEyJZO6lnBLmr1NQ=", + "requires": { + "is-function": "1.0.1" + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -6854,14 +7085,12 @@ "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, "form-data": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", - "dev": true, "requires": { "asynckit": "0.4.0", "combined-stream": "1.0.5", @@ -6880,14 +7109,12 @@ "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "dev": true + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" }, "fresh": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz", - "integrity": "sha1-ZR+DjiJCTnVm3hYdg1jKoZn4PU8=", - "dev": true + "integrity": "sha1-ZR+DjiJCTnVm3hYdg1jKoZn4PU8=" }, "fs-extra": { "version": "3.0.1", @@ -6900,6 +7127,36 @@ "universalify": "0.1.1" } }, + "fs-promise": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/fs-promise/-/fs-promise-2.0.3.tgz", + "integrity": "sha1-9k5PhUvPaJqovdy6JokW2z20aFQ=", + "requires": { + "any-promise": "1.3.0", + "fs-extra": "2.1.2", + "mz": "2.7.0", + "thenify-all": "1.6.0" + }, + "dependencies": { + "fs-extra": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-2.1.2.tgz", + "integrity": "sha1-BGxwFjzvmq1GsOSn+kZ/si1x3jU=", + "requires": { + "graceful-fs": "4.1.11", + "jsonfile": "2.4.0" + } + }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "requires": { + "graceful-fs": "4.1.11" + } + } + } + }, "fs-readdir-recursive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", @@ -6909,8 +7166,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "1.1.2", @@ -7811,6 +8067,17 @@ } } }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.2" + } + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -7896,14 +8163,12 @@ "get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, "requires": { "assert-plus": "1.0.0" } @@ -7917,7 +8182,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, "requires": { "fs.realpath": "1.0.0", "inflight": "1.0.6", @@ -7950,7 +8214,6 @@ "version": "4.3.2", "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", - "dev": true, "requires": { "min-document": "2.19.0", "process": "0.5.2" @@ -7959,8 +8222,7 @@ "process": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", - "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=", - "dev": true + "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=" } } }, @@ -8044,8 +8306,7 @@ "graceful-readlink": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", - "dev": true + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" }, "growl": { "version": "1.9.2", @@ -8139,14 +8400,12 @@ "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" }, "har-validator": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", - "dev": true, "requires": { "ajv": "5.5.1", "har-schema": "2.0.0" @@ -8175,6 +8434,19 @@ "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", "dev": true }, + "has-symbol-support-x": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.1.tgz", + "integrity": "sha512-JkaetveU7hFbqnAC1EV1sF4rlojU2D4Usc5CmS69l6NfmPDnpnFUegzFg33eDkkpNCxZ0mQp65HwUDrNFS/8MA==" + }, + "has-to-string-tag-x": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", + "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", + "requires": { + "has-symbol-support-x": "1.4.1" + } + }, "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -8201,7 +8473,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", - "dev": true, "requires": { "boom": "4.3.1", "cryptiles": "3.1.2", @@ -8250,8 +8521,7 @@ "hoek": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", - "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==", - "dev": true + "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==" }, "hoist-non-react-statics": { "version": "1.2.0", @@ -8481,13 +8751,17 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.5.1.tgz", "integrity": "sha1-eIwNLB3iyBuebowBhDtrl+uSB1A=", - "dev": true, "requires": { "inherits": "2.0.3", "setprototypeof": "1.0.2", "statuses": "1.3.1" } }, + "http-https": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz", + "integrity": "sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs=" + }, "http-parser-js": { "version": "0.4.9", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.9.tgz", @@ -8545,7 +8819,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, "requires": { "assert-plus": "1.0.0", "jsprim": "1.4.1", @@ -8592,8 +8865,7 @@ "ieee754": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", - "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=", - "dev": true + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" }, "ignore": { "version": "3.3.7", @@ -8641,7 +8913,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "1.4.0", "wrappy": "1.0.2" @@ -8813,8 +9084,7 @@ "ipaddr.js": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.4.0.tgz", - "integrity": "sha1-KWrKh4qCGBbluF0KKFqZvP9FgvA=", - "dev": true + "integrity": "sha1-KWrKh4qCGBbluF0KKFqZvP9FgvA=" }, "irregular-plurals": { "version": "1.4.0", @@ -8944,6 +9214,11 @@ "number-is-nan": "1.0.1" } }, + "is-function": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz", + "integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU=" + }, "is-glob": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", @@ -8975,6 +9250,11 @@ "xtend": "4.0.1" } }, + "is-natural-number": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", + "integrity": "sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=" + }, "is-npm": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", @@ -8995,6 +9275,11 @@ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" }, + "is-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", + "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=" + }, "is-path-cwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", @@ -9088,8 +9373,7 @@ "is-retry-allowed": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", - "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", - "dev": true + "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=" }, "is-root": { "version": "1.0.0", @@ -9132,8 +9416,7 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, "is-utf8": { "version": "0.2.1", @@ -9190,8 +9473,7 @@ "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, "istanbul": { "version": "1.0.0-alpha.2", @@ -9328,6 +9610,15 @@ "handlebars": "4.0.11" } }, + "isurl": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", + "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", + "requires": { + "has-to-string-tag-x": "1.4.1", + "is-object": "1.0.1" + } + }, "jest": { "version": "20.0.4", "resolved": "https://registry.npmjs.org/jest/-/jest-20.0.4.tgz", @@ -9750,7 +10041,6 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true, "optional": true }, "jsdom": { @@ -9802,14 +10092,12 @@ "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" }, "json-schema-traverse": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" }, "json-stable-stringify": { "version": "1.0.1", @@ -9823,8 +10111,7 @@ "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, "json3": { "version": "3.3.2", @@ -9922,7 +10209,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -9950,6 +10236,15 @@ "safe-buffer": "5.1.1" } }, + "keccakjs": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/keccakjs/-/keccakjs-0.2.1.tgz", + "integrity": "sha1-HWM6+QfvMFu/ny+mFtVsRFYd+k0=", + "requires": { + "browserify-sha3": "0.0.1", + "sha3": "1.2.0" + } + }, "keycode": { "version": "2.1.9", "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.1.9.tgz", @@ -9974,7 +10269,7 @@ "bindings": "1.3.0", "inherits": "2.0.3", "nan": "2.8.0", - "prebuild-install": "2.3.0" + "prebuild-install": "2.4.0" } }, "secp256k1": { @@ -9989,7 +10284,7 @@ "drbg.js": "1.0.1", "elliptic": "6.4.0", "nan": "2.8.0", - "prebuild-install": "2.3.0" + "prebuild-install": "2.4.0" } } } @@ -10449,8 +10744,7 @@ "lowercase-keys": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", - "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=", - "dev": true + "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=" }, "lru-cache": { "version": "4.1.1", @@ -10472,7 +10766,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.1.0.tgz", "integrity": "sha512-0Pkui4wLJ7rxvmfUvs87skoEaxmu0hCUApF8nonzpl7q//FWp9zu8W61Scz4sd/kUiqDxvUhtoam2efDyiBzcA==", - "dev": true, "requires": { "pify": "3.0.0" }, @@ -10480,8 +10773,7 @@ "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" } } }, @@ -10605,8 +10897,7 @@ "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "dev": true + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, "mem": { "version": "1.1.0", @@ -10665,14 +10956,12 @@ "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", - "dev": true + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "dev": true + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, "micromatch": { "version": "2.3.11", @@ -10714,14 +11003,12 @@ "mime-db": { "version": "1.30.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", - "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=", - "dev": true + "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" }, "mime-types": { "version": "2.1.17", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", - "dev": true, "requires": { "mime-db": "1.30.0" } @@ -10732,11 +11019,15 @@ "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=", "dev": true }, + "mimic-response": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.0.tgz", + "integrity": "sha1-3z02Uqc/3ta5sLJBRub9BSNTRY4=" + }, "min-document": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", - "dev": true, "requires": { "dom-walk": "0.1.1" } @@ -10772,6 +11063,14 @@ "minimist": "0.0.8" } }, + "mkdirp-promise": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", + "integrity": "sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE=", + "requires": { + "mkdirp": "0.5.1" + } + }, "mobx": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/mobx/-/mobx-3.3.2.tgz", @@ -10872,6 +11171,11 @@ } } }, + "mock-fs": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.4.2.tgz", + "integrity": "sha512-dF+yxZSojSiI8AXGoxj5qdFWpucndc54Ug+TwlpHFaV7j22MGG+OML2+FVa6xAZtjb/OFFQhOC37Jegx2GbEwA==" + }, "mock-local-storage": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/mock-local-storage/-/mock-local-storage-1.0.2.tgz", @@ -10900,6 +11204,11 @@ "resolved": "https://registry.npmjs.org/moment/-/moment-2.19.2.tgz", "integrity": "sha512-Rf6jiHPEfxp9+dlzxPTmRHbvoFXsh2L/U8hOupUMpnuecHQmI6cF6lUbJl3QqKPko1u6ujO+FxtcajLVfLpAtA==" }, + "mout": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/mout/-/mout-0.11.1.tgz", + "integrity": "sha1-ujYR318OWx/7/QEWa48C0fX6K5k=" + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -10939,11 +11248,26 @@ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, + "mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "requires": { + "any-promise": "1.3.0", + "object-assign": "4.1.1", + "thenify-all": "1.6.0" + } + }, "nan": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=" }, + "nano-json-stream-parser": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz", + "integrity": "sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18=" + }, "native-promise-only": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", @@ -10979,8 +11303,7 @@ "negotiator": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", - "dev": true + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" }, "no-case": { "version": "2.3.2", @@ -11261,6 +11584,22 @@ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, + "number-to-bn": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", + "integrity": "sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA=", + "requires": { + "bn.js": "4.11.6", + "strip-hex-prefix": "1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" + } + } + }, "nwmatcher": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.4.3.tgz", @@ -11270,8 +11609,7 @@ "oauth-sign": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", - "dev": true + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" }, "object-assign": { "version": "4.1.1", @@ -11284,6 +11622,11 @@ "integrity": "sha512-smRWXzkvxw72VquyZ0wggySl7PFUtoDhvhpdwgESXxUrH7vVhhp9asfup1+rVLrhsl7L45Ee1Q/l5R2Ul4MwUg==", "dev": true }, + "object-inspect": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-0.4.0.tgz", + "integrity": "sha1-9RV8EWwUVbJDsG7pdwM5LFrYn+w=" + }, "object-is": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz", @@ -11341,6 +11684,14 @@ "has": "1.0.1" } }, + "oboe": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.3.tgz", + "integrity": "sha1-K0hl29Rr6BIlcT9Om/5Lz09oCk8=", + "requires": { + "http-https": "1.0.0" + } + }, "obuf": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.1.tgz", @@ -11351,7 +11702,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, "requires": { "ee-first": "1.1.1" } @@ -11492,11 +11842,15 @@ "object-assign": "4.1.1" } }, + "p-cancelable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", + "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==" + }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" }, "p-limit": { "version": "1.1.0", @@ -11519,6 +11873,14 @@ "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", "dev": true }, + "p-timeout": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", + "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", + "requires": { + "p-finally": "1.0.0" + } + }, "package-json": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/package-json/-/package-json-2.4.0.tgz", @@ -11584,6 +11946,15 @@ "is-glob": "2.0.1" } }, + "parse-headers": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.1.tgz", + "integrity": "sha1-aug6eqJanZtwCswoaYzR8e1+lTY=", + "requires": { + "for-each": "0.3.2", + "trim": "0.0.1" + } + }, "parse-json": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", @@ -11607,8 +11978,7 @@ "parseurl": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", - "dev": true + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" }, "pascalcase": { "version": "0.1.1", @@ -11684,7 +12054,6 @@ "version": "3.0.14", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.14.tgz", "integrity": "sha512-gjsZW9O34fm0R7PaLHRJmLLVfSoesxztjPjE9o6R+qtVJij90ltg1joIovN9GKrRW3t1PzhDDG3UMEMFfZ+1wA==", - "dev": true, "requires": { "create-hash": "1.1.3", "create-hmac": "1.1.6", @@ -11696,8 +12065,7 @@ "pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" }, "performance-now": { "version": "2.1.0", @@ -12390,8 +12758,8 @@ "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "dev": true, "requires": { - "caniuse-db": "1.0.30000778", - "electron-to-chromium": "1.3.27" + "caniuse-db": "1.0.30000780", + "electron-to-chromium": "1.3.28" } }, "has-flag": { @@ -13107,9 +13475,9 @@ } }, "prebuild-install": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-2.3.0.tgz", - "integrity": "sha512-gzjq2oHB8oMbzJSsSh9MQ64zrXZGt092/uT4TLZlz2qnrPxpWqp4vYB7LZrDxnlxf5RfbCjkgDI/z0EIVuYzAw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-2.4.0.tgz", + "integrity": "sha512-2v/FFGgF8Z2UjmM7dHiEwfuldALnZZsBEfjY6Pk1wPu88gYexPkIBUJwHYJ7e/kasFu8zbtTPY5bY45Vc2MC6g==", "requires": { "expand-template": "1.1.0", "github-from-package": "0.0.0", @@ -13143,8 +13511,7 @@ "prepend-http": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" }, "preserve": { "version": "0.2.0", @@ -13248,7 +13615,6 @@ "version": "1.1.5", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.5.tgz", "integrity": "sha1-ccDuOxAt4/IC87ZPYI0XP8uhqRg=", - "dev": true, "requires": { "forwarded": "0.1.2", "ipaddr.js": "1.4.0" @@ -13291,8 +13657,7 @@ "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" }, "push.js": { "version": "0.0.11", @@ -13427,11 +13792,44 @@ "safe-buffer": "5.1.1" } }, + "randomhex": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/randomhex/-/randomhex-0.1.5.tgz", + "integrity": "sha1-us7vmCMpCRQA8qKRLGzQLxCU9YU=" + }, "range-parser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", - "dev": true + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + }, + "raw-body": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "unpipe": "1.0.0" + }, + "dependencies": { + "http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "requires": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": "1.3.1" + } + }, + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" + } + } }, "raw-loader": { "version": "0.5.1", @@ -13666,9 +14064,9 @@ "dev": true }, "react-event-listener": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/react-event-listener/-/react-event-listener-0.5.1.tgz", - "integrity": "sha1-ujYHbke8N8Wmf/XM1Kn/DxViEEA=", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/react-event-listener/-/react-event-listener-0.5.2.tgz", + "integrity": "sha512-E22Sc/PtzVWw/fRidkEy1ZNnpSMJARUVV/5LymsDe4NjIHzNcVpNLV/R2Kt40NN8X6tu/X5p2inCny7vqd97mg==", "requires": { "babel-runtime": "6.26.0", "fbjs": "0.8.16", @@ -15200,7 +15598,6 @@ "version": "2.83.0", "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", - "dev": true, "requires": { "aws-sign2": "0.7.0", "aws4": "1.6.0", @@ -15229,14 +15626,12 @@ "extend": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", - "dev": true + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" }, "uuid": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", - "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==", - "dev": true + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" } } }, @@ -15323,6 +15718,14 @@ "signal-exit": "3.0.2" } }, + "resumer": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", + "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", + "requires": { + "through": "2.3.8" + } + }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", @@ -15342,7 +15745,6 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", - "dev": true, "requires": { "glob": "7.1.2" } @@ -15372,9 +15774,9 @@ } }, "rtcpeerconnection-shim": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/rtcpeerconnection-shim/-/rtcpeerconnection-shim-1.2.2.tgz", - "integrity": "sha512-8gk72X25Z31XEkk5DZd6y4aziHgj0mZMB7xMv4mUrS6moTmZOrcKE8+rvEVRModMkaaUyspEVwBn8JGuG8Z1ww==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/rtcpeerconnection-shim/-/rtcpeerconnection-shim-1.2.4.tgz", + "integrity": "sha512-DQ65pr+e9GYC52dVj9Pqoh2cOC3gl4956XXbPWgGvPOqX3sA3nmv3qvf9OMyYq5MwHAQeLnYkGnJaBIRiBBIQw==", "requires": { "sdp": "2.5.0" } @@ -15470,6 +15872,33 @@ "ajv": "5.5.1" } }, + "scrypt": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/scrypt/-/scrypt-6.0.3.tgz", + "integrity": "sha1-BOAUpWgrU/pQwtXM4WfXGcBthw0=", + "requires": { + "nan": "2.8.0" + } + }, + "scrypt.js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/scrypt.js/-/scrypt.js-0.2.0.tgz", + "integrity": "sha1-r40UZbcemZARC+38WTuUeeA6ito=", + "requires": { + "scrypt": "6.0.3", + "scryptsy": "1.2.1" + }, + "dependencies": { + "scryptsy": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-1.2.1.tgz", + "integrity": "sha1-oyJfpLJST4AnAHYeKFW987LZIWM=", + "requires": { + "pbkdf2": "3.0.14" + } + } + } + }, "scryptsy": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-2.0.0.tgz", @@ -15495,6 +15924,24 @@ "safe-buffer": "5.1.1" } }, + "seek-bzip": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz", + "integrity": "sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w=", + "requires": { + "commander": "2.8.1" + }, + "dependencies": { + "commander": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "requires": { + "graceful-readlink": "1.0.1" + } + } + } + }, "select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -15519,12 +15966,13 @@ } }, "semantic-ui-react": { - "version": "0.76.0", - "resolved": "https://registry.npmjs.org/semantic-ui-react/-/semantic-ui-react-0.76.0.tgz", - "integrity": "sha512-CdiIT8n7ZwUlytZkYsQMnaVGmoIZI/mVs4lijvLcR568kcnlRkYYaFKhMLq5tFDQU6+QhdTD+8WebF7ov0Ql6Q==", + "version": "0.77.0", + "resolved": "https://registry.npmjs.org/semantic-ui-react/-/semantic-ui-react-0.77.0.tgz", + "integrity": "sha512-lUnlpbIbMtse335kjZAw8ClulCQAUWDm77pw2yY754mL02PlTNYtZQanoGjhBFts41YHpg6ExK156J/9Ynb8IQ==", "requires": { "babel-runtime": "6.26.0", "classnames": "2.2.5", + "fbjs": "0.8.16", "lodash": "4.17.4", "prop-types": "15.5.10" } @@ -15547,7 +15995,6 @@ "version": "0.14.2", "resolved": "https://registry.npmjs.org/send/-/send-0.14.2.tgz", "integrity": "sha1-ObBDiz9RC+Xcb2Z6EfcWiTaM3u8=", - "dev": true, "requires": { "debug": "2.2.0", "depd": "1.1.1", @@ -15568,7 +16015,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", - "dev": true, "requires": { "ms": "0.7.1" }, @@ -15576,22 +16022,19 @@ "ms": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", - "dev": true + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" } } }, "mime": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", - "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=", - "dev": true + "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=" }, "ms": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true + "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=" } } }, @@ -15640,7 +16083,6 @@ "version": "1.11.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.11.2.tgz", "integrity": "sha1-LPmIm9RDWjIMw2iVyapXvWYuasc=", - "dev": true, "requires": { "encodeurl": "1.0.1", "escape-html": "1.0.3", @@ -15654,6 +16096,18 @@ "integrity": "sha1-3hnuc77yGrPAdAo3sz22JGS6ves=", "dev": true }, + "servify": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/servify/-/servify-0.1.12.tgz", + "integrity": "sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw==", + "requires": { + "body-parser": "1.18.2", + "cors": "2.8.4", + "express": "4.14.1", + "request": "2.83.0", + "xhr": "2.4.1" + } + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -15673,8 +16127,7 @@ "setprototypeof": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.2.tgz", - "integrity": "sha1-gaVSFB7BBLiOic44MQOtXGZWTQg=", - "dev": true + "integrity": "sha1-gaVSFB7BBLiOic44MQOtXGZWTQg=" }, "sha.js": { "version": "2.4.9", @@ -15685,6 +16138,14 @@ "safe-buffer": "5.1.1" } }, + "sha3": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/sha3/-/sha3-1.2.0.tgz", + "integrity": "sha1-aYnxtwpJhwWHajc+LGKs6WqpOZo=", + "requires": { + "nan": "2.8.0" + } + }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", @@ -15729,6 +16190,11 @@ "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", "dev": true }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" + }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", @@ -15823,7 +16289,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", - "dev": true, "requires": { "hoek": "4.2.0" } @@ -16068,7 +16533,6 @@ "version": "1.13.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", - "dev": true, "requires": { "asn1": "0.2.3", "assert-plus": "1.0.0", @@ -16094,8 +16558,7 @@ "statuses": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", - "dev": true + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" }, "store": { "version": "1.3.20", @@ -16186,8 +16649,7 @@ "stringstream": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", - "dev": true + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" }, "strip-ansi": { "version": "3.0.1", @@ -16205,6 +16667,14 @@ "is-utf8": "0.2.1" } }, + "strip-dirs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", + "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", + "requires": { + "is-natural-number": "4.0.1" + } + }, "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", @@ -16280,8 +16750,8 @@ "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "dev": true, "requires": { - "caniuse-db": "1.0.30000778", - "electron-to-chromium": "1.3.27" + "caniuse-db": "1.0.30000780", + "electron-to-chromium": "1.3.28" } }, "has-flag": { @@ -16391,7 +16861,7 @@ "dev": true, "requires": { "browserslist": "1.7.7", - "caniuse-db": "1.0.30000778", + "caniuse-db": "1.0.30000780", "normalize-range": "0.1.2", "num2fraction": "1.2.2", "postcss": "5.2.18", @@ -16410,8 +16880,8 @@ "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "dev": true, "requires": { - "caniuse-db": "1.0.30000778", - "electron-to-chromium": "1.3.27" + "caniuse-db": "1.0.30000780", + "electron-to-chromium": "1.3.28" } }, "get-stdin": { @@ -16630,6 +17100,80 @@ "serviceworker-cache-polyfill": "4.0.0" } }, + "swarm-js": { + "version": "0.1.37", + "resolved": "https://registry.npmjs.org/swarm-js/-/swarm-js-0.1.37.tgz", + "integrity": "sha512-G8gi5fcXP/2upwiuOShJ258sIufBVztekgobr3cVgYXObZwJ5AXLqZn52AI+/ffft29pJexF9WNdUxjlkVehoQ==", + "requires": { + "bluebird": "3.5.1", + "buffer": "5.0.8", + "decompress": "4.2.0", + "eth-lib": "0.1.27", + "fs-extra": "2.1.2", + "fs-promise": "2.0.3", + "got": "7.1.0", + "mime-types": "2.1.17", + "mkdirp-promise": "5.0.1", + "mock-fs": "4.4.2", + "setimmediate": "1.0.5", + "tar.gz": "1.0.7", + "xhr-request-promise": "0.1.2" + }, + "dependencies": { + "buffer": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.0.8.tgz", + "integrity": "sha512-xXvjQhVNz50v2nPeoOsNqWCLGfiv4ji/gXZM28jnVwdLJxH4mFyqgqCKfaK9zf1KUbG6zTkjLOy7ou+jSMarGA==", + "requires": { + "base64-js": "1.2.1", + "ieee754": "1.1.8" + } + }, + "fs-extra": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-2.1.2.tgz", + "integrity": "sha1-BGxwFjzvmq1GsOSn+kZ/si1x3jU=", + "requires": { + "graceful-fs": "4.1.11", + "jsonfile": "2.4.0" + } + }, + "got": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", + "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", + "requires": { + "decompress-response": "3.3.0", + "duplexer3": "0.1.4", + "get-stream": "3.0.0", + "is-plain-obj": "1.1.0", + "is-retry-allowed": "1.1.0", + "is-stream": "1.1.0", + "isurl": "1.0.0", + "lowercase-keys": "1.0.0", + "p-cancelable": "0.3.0", + "p-timeout": "1.2.1", + "safe-buffer": "5.1.1", + "timed-out": "4.0.1", + "url-parse-lax": "1.0.0", + "url-to-options": "1.0.1" + } + }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "requires": { + "graceful-fs": "4.1.11" + } + }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" + } + } + }, "symbol-observable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.1.0.tgz", @@ -16740,6 +17284,65 @@ "integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI=", "dev": true }, + "tape": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/tape/-/tape-3.6.1.tgz", + "integrity": "sha1-SJPdU+KApfWMDOswwsDrs7zVHh8=", + "requires": { + "deep-equal": "0.2.2", + "defined": "0.0.0", + "glob": "3.2.11", + "inherits": "2.0.3", + "object-inspect": "0.4.0", + "resumer": "0.0.0", + "through": "2.3.8" + }, + "dependencies": { + "deep-equal": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.2.2.tgz", + "integrity": "sha1-hLdFiW80xoTpjyzg5Cq69Du6AX0=" + }, + "defined": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-0.0.0.tgz", + "integrity": "sha1-817qfXBekzuvE7LwOz+D2SFAOz4=" + }, + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", + "requires": { + "inherits": "2.0.3", + "minimatch": "0.3.0" + } + }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } + } + } + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } + }, "tar-fs": { "version": "1.16.0", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.0.tgz", @@ -16762,6 +17365,25 @@ "xtend": "4.0.1" } }, + "tar.gz": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/tar.gz/-/tar.gz-1.0.7.tgz", + "integrity": "sha512-uhGatJvds/3diZrETqMj4RxBR779LKlIE74SsMcn5JProZsfs9j0QBwWO1RW+IWNJxS2x8Zzra1+AW6OQHWphg==", + "requires": { + "bluebird": "2.11.0", + "commander": "2.12.2", + "fstream": "1.0.11", + "mout": "0.11.1", + "tar": "2.2.1" + }, + "dependencies": { + "bluebird": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", + "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=" + } + } + }, "test-exclude": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-4.1.1.tgz", @@ -16781,6 +17403,22 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "thenify": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", + "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=", + "requires": { + "any-promise": "1.3.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", + "requires": { + "thenify": "3.3.0" + } + }, "throat": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/throat/-/throat-3.2.0.tgz", @@ -16796,8 +17434,7 @@ "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, "through2": { "version": "0.2.3", @@ -16924,7 +17561,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", - "dev": true, "requires": { "punycode": "1.4.1" } @@ -16990,7 +17626,6 @@ "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true, "optional": true }, "type-check": { @@ -17012,7 +17647,6 @@ "version": "1.6.15", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", - "dev": true, "requires": { "media-typer": "0.3.0", "mime-types": "2.1.17" @@ -17028,7 +17662,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.2.tgz", "integrity": "sha1-EBezLZhP9VbroQD1AViauhrOLgQ=", - "dev": true, "requires": { "is-typedarray": "1.0.0" } @@ -17174,6 +17807,37 @@ } } }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" + }, + "unbzip2-stream": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.2.5.tgz", + "integrity": "sha512-izD3jxT8xkzwtXRUZjtmRwKnZoeECrfZ8ra/ketwOcusbZEp4mjULMnJOCfTDZBgGQAAY1AJ/IgxcwkavcX9Og==", + "requires": { + "buffer": "3.6.0", + "through": "2.3.8" + }, + "dependencies": { + "base64-js": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", + "integrity": "sha1-EQHpVE9KdrG8OybUUsqW16NeeXg=" + }, + "buffer": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-3.6.0.tgz", + "integrity": "sha1-pyyTb3e5a/UvX357RnGAYoVR3vs=", + "requires": { + "base64-js": "0.0.8", + "ieee754": "1.1.8", + "isarray": "1.0.0" + } + } + } + }, "underscore": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", @@ -17272,8 +17936,7 @@ "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "dev": true + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, "unzip-response": { "version": "1.0.2", @@ -17378,11 +18041,20 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", - "dev": true, "requires": { "prepend-http": "1.0.4" } }, + "url-set-query": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-set-query/-/url-set-query-1.0.0.tgz", + "integrity": "sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk=" + }, + "url-to-options": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", + "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=" + }, "user-home": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz", @@ -17430,8 +18102,7 @@ "utils-merge": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", - "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=", - "dev": true + "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=" }, "uuid": { "version": "3.0.0", @@ -17459,8 +18130,7 @@ "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "dev": true + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, "vendors": { "version": "1.0.1", @@ -17472,7 +18142,6 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, "requires": { "assert-plus": "1.0.0", "core-util-is": "1.0.2", @@ -17561,18 +18230,407 @@ } }, "web3": { - "version": "0.17.0-beta", - "resolved": "https://registry.npmjs.org/web3/-/web3-0.17.0-beta.tgz", - "integrity": "sha1-V684JFv/ejIJn3zleA+tW7wA2ls=", + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/web3/-/web3-1.0.0-beta.26.tgz", + "integrity": "sha1-u0ba9q78MT92iz3jnX9KjXvQZmM=", "requires": { - "bignumber.js": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2", - "crypto-js": "3.1.8", - "utf8": "2.1.2", - "xmlhttprequest": "1.8.0" + "web3-bzz": "1.0.0-beta.26", + "web3-core": "1.0.0-beta.26", + "web3-eth": "1.0.0-beta.26", + "web3-eth-personal": "1.0.0-beta.26", + "web3-net": "1.0.0-beta.26", + "web3-shh": "1.0.0-beta.26", + "web3-utils": "1.0.0-beta.26" + } + }, + "web3-bzz": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.0.0-beta.26.tgz", + "integrity": "sha1-WFihjN5XaHSAGoPR30IJX8lYWQw=", + "requires": { + "got": "7.1.0", + "swarm-js": "0.1.37", + "underscore": "1.8.3" }, "dependencies": { - "bignumber.js": { - "version": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2" + "got": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", + "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", + "requires": { + "decompress-response": "3.3.0", + "duplexer3": "0.1.4", + "get-stream": "3.0.0", + "is-plain-obj": "1.1.0", + "is-retry-allowed": "1.1.0", + "is-stream": "1.1.0", + "isurl": "1.0.0", + "lowercase-keys": "1.0.0", + "p-cancelable": "0.3.0", + "p-timeout": "1.2.1", + "safe-buffer": "5.1.1", + "timed-out": "4.0.1", + "url-parse-lax": "1.0.0", + "url-to-options": "1.0.1" + } + }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" + }, + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + } + } + }, + "web3-core": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.0.0-beta.26.tgz", + "integrity": "sha1-hczKK2KfmK3+sOK21+K31nepeVk=", + "requires": { + "web3-core-helpers": "1.0.0-beta.26", + "web3-core-method": "1.0.0-beta.26", + "web3-core-requestmanager": "1.0.0-beta.26", + "web3-utils": "1.0.0-beta.26" + } + }, + "web3-core-helpers": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.0.0-beta.26.tgz", + "integrity": "sha1-2G31xrMQ/FjFtv9Woz0mePu8PcM=", + "requires": { + "underscore": "1.8.3", + "web3-eth-iban": "1.0.0-beta.26", + "web3-utils": "1.0.0-beta.26" + }, + "dependencies": { + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + } + } + }, + "web3-core-method": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.0.0-beta.26.tgz", + "integrity": "sha1-SdhpoacvMiNXbIkmCe7kDTsiVXw=", + "requires": { + "underscore": "1.8.3", + "web3-core-helpers": "1.0.0-beta.26", + "web3-core-promievent": "1.0.0-beta.26", + "web3-core-subscriptions": "1.0.0-beta.26", + "web3-utils": "1.0.0-beta.26" + }, + "dependencies": { + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + } + } + }, + "web3-core-promievent": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.0.0-beta.26.tgz", + "integrity": "sha1-BkJSUZ35t+banCD1lKAuz+nDU8E=", + "requires": { + "bluebird": "3.3.1", + "eventemitter3": "1.1.1" + }, + "dependencies": { + "bluebird": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.3.1.tgz", + "integrity": "sha1-+Xrhlw9B2FF3KDBT6aEgFg5mxh0=" + }, + "eventemitter3": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.1.1.tgz", + "integrity": "sha1-R3hr2qCHyvext15zq8XH1UAVjNA=" + } + } + }, + "web3-core-requestmanager": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.0.0-beta.26.tgz", + "integrity": "sha1-dffvfy/GpLDTRr8AVCFXuB4UsDM=", + "requires": { + "underscore": "1.8.3", + "web3-core-helpers": "1.0.0-beta.26", + "web3-providers-http": "1.0.0-beta.26", + "web3-providers-ipc": "1.0.0-beta.26", + "web3-providers-ws": "1.0.0-beta.26" + }, + "dependencies": { + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + } + } + }, + "web3-core-subscriptions": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.0.0-beta.26.tgz", + "integrity": "sha1-0W0dbr3GDXCL9aR7hxZt1+jBl6A=", + "requires": { + "eventemitter3": "1.1.1", + "underscore": "1.8.3", + "web3-core-helpers": "1.0.0-beta.26" + }, + "dependencies": { + "eventemitter3": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.1.1.tgz", + "integrity": "sha1-R3hr2qCHyvext15zq8XH1UAVjNA=" + }, + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + } + } + }, + "web3-eth": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-1.0.0-beta.26.tgz", + "integrity": "sha1-aMAkw1a4ZWrDaVyPk9e2GzgQRKU=", + "requires": { + "underscore": "1.8.3", + "web3-core": "1.0.0-beta.26", + "web3-core-helpers": "1.0.0-beta.26", + "web3-core-method": "1.0.0-beta.26", + "web3-core-subscriptions": "1.0.0-beta.26", + "web3-eth-abi": "1.0.0-beta.26", + "web3-eth-accounts": "1.0.0-beta.26", + "web3-eth-contract": "1.0.0-beta.26", + "web3-eth-iban": "1.0.0-beta.26", + "web3-eth-personal": "1.0.0-beta.26", + "web3-net": "1.0.0-beta.26", + "web3-utils": "1.0.0-beta.26" + }, + "dependencies": { + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + } + } + }, + "web3-eth-abi": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.0.0-beta.26.tgz", + "integrity": "sha1-Ku3ASDxna1kcccBBJXIZj3omb+I=", + "requires": { + "bn.js": "4.11.6", + "underscore": "1.8.3", + "web3-core-helpers": "1.0.0-beta.26", + "web3-utils": "1.0.0-beta.26" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" + }, + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + } + } + }, + "web3-eth-accounts": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.0.0-beta.26.tgz", + "integrity": "sha1-N/18d3BCBGX95ZGCKYkad3OAehM=", + "requires": { + "bluebird": "3.3.1", + "eth-lib": "0.2.5", + "scrypt.js": "0.2.0", + "underscore": "1.8.3", + "uuid": "2.0.1", + "web3-core": "1.0.0-beta.26", + "web3-core-helpers": "1.0.0-beta.26", + "web3-core-method": "1.0.0-beta.26", + "web3-utils": "1.0.0-beta.26" + }, + "dependencies": { + "bluebird": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.3.1.tgz", + "integrity": "sha1-+Xrhlw9B2FF3KDBT6aEgFg5mxh0=" + }, + "eth-lib": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.5.tgz", + "integrity": "sha512-pXs4ryU+7S8MPpkQpNqG4JlXEec87kbXowQbYzRVV+c5XUccrO6WOxVPDicxql1AXSBzfmBSFVkvvG+H4htuxg==", + "requires": { + "bn.js": "4.11.8", + "elliptic": "6.4.0", + "xhr-request-promise": "0.1.2" + } + }, + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + }, + "uuid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz", + "integrity": "sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w=" + } + } + }, + "web3-eth-contract": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.0.0-beta.26.tgz", + "integrity": "sha1-fny3FXqrYMUi20353p3L2G2BOwk=", + "requires": { + "underscore": "1.8.3", + "web3-core": "1.0.0-beta.26", + "web3-core-helpers": "1.0.0-beta.26", + "web3-core-method": "1.0.0-beta.26", + "web3-core-promievent": "1.0.0-beta.26", + "web3-core-subscriptions": "1.0.0-beta.26", + "web3-eth-abi": "1.0.0-beta.26", + "web3-utils": "1.0.0-beta.26" + }, + "dependencies": { + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + } + } + }, + "web3-eth-iban": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.0.0-beta.26.tgz", + "integrity": "sha1-6MI2GOpapmJ73pHHPqi18ZGe43Q=", + "requires": { + "bn.js": "4.11.8", + "web3-utils": "1.0.0-beta.26" + } + }, + "web3-eth-personal": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.0.0-beta.26.tgz", + "integrity": "sha1-K4gDs01HJEfPW76BziVQSxMb7QY=", + "requires": { + "web3-core": "1.0.0-beta.26", + "web3-core-helpers": "1.0.0-beta.26", + "web3-core-method": "1.0.0-beta.26", + "web3-net": "1.0.0-beta.26", + "web3-utils": "1.0.0-beta.26" + } + }, + "web3-net": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-1.0.0-beta.26.tgz", + "integrity": "sha1-UY0oO1AANf7kgL9ocIljRyWrZLM=", + "requires": { + "web3-core": "1.0.0-beta.26", + "web3-core-method": "1.0.0-beta.26", + "web3-utils": "1.0.0-beta.26" + } + }, + "web3-providers-http": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.0.0-beta.26.tgz", + "integrity": "sha1-GwFUu3UY027TT5EKZl5FFSoKyKE=", + "requires": { + "web3-core-helpers": "1.0.0-beta.26", + "xhr2": "0.1.4" + } + }, + "web3-providers-ipc": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.0.0-beta.26.tgz", + "integrity": "sha1-HffepV5nE1yQRaJsUzso0bbJ2mQ=", + "requires": { + "oboe": "2.1.3", + "underscore": "1.8.3", + "web3-core-helpers": "1.0.0-beta.26" + }, + "dependencies": { + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + } + } + }, + "web3-providers-ws": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.0.0-beta.26.tgz", + "integrity": "sha1-z0ylFUpPsVok1GgtEJUO4Emku2E=", + "requires": { + "underscore": "1.8.3", + "web3-core-helpers": "1.0.0-beta.26", + "websocket": "git://github.com/frozeman/WebSocket-Node.git#7004c39c42ac98875ab61126e5b4a925430f592c" + }, + "dependencies": { + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + }, + "websocket": { + "version": "git://github.com/frozeman/WebSocket-Node.git#7004c39c42ac98875ab61126e5b4a925430f592c", + "requires": { + "debug": "2.6.9", + "nan": "2.8.0", + "typedarray-to-buffer": "3.1.2", + "yaeti": "0.0.6" + } + } + } + }, + "web3-shh": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/web3-shh/-/web3-shh-1.0.0-beta.26.tgz", + "integrity": "sha1-YMrff1V71rRRVHXd4z4uV7gKgg4=", + "requires": { + "web3-core": "1.0.0-beta.26", + "web3-core-method": "1.0.0-beta.26", + "web3-core-subscriptions": "1.0.0-beta.26", + "web3-net": "1.0.0-beta.26" + } + }, + "web3-utils": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.0.0-beta.26.tgz", + "integrity": "sha1-8ErYwUSxeBxrIMKBjgUyy55tyhU=", + "requires": { + "bn.js": "4.11.6", + "eth-lib": "0.1.27", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "underscore": "1.8.3", + "utf8": "2.1.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" + }, + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + }, + "utf8": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-2.1.1.tgz", + "integrity": "sha1-LgHbAvfY0JRPdxBPFgnrDDBM92g=" } } }, @@ -17988,7 +19046,7 @@ "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-5.0.6.tgz", "integrity": "sha512-dh2hPQFOPP0tLEYlFxtGI5vuQmRqkOdYni5wMKUHIx5I2dw0TJ1HdG7P+UechRWt6TvwPWhtbjVNQcQf1KXJmQ==", "requires": { - "rtcpeerconnection-shim": "1.2.2", + "rtcpeerconnection-shim": "1.2.4", "sdp": "2.5.0" } }, @@ -18150,6 +19208,16 @@ "integrity": "sha1-wlLXx8WxtAKJdjDjRTx7/mkNnKE=", "dev": true }, + "ws": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.2.tgz", + "integrity": "sha512-t+WGpsNxhMR4v6EClXS8r8km5ZljKJzyGhJf7goJz9k5Ye3+b5Bvno5rjqPuIBn5mnn5GBb7o8IrIWHxX1qOLQ==", + "requires": { + "async-limiter": "1.0.0", + "safe-buffer": "5.1.1", + "ultron": "1.1.1" + } + }, "x-is-function": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/x-is-function/-/x-is-function-1.0.4.tgz", @@ -18169,6 +19237,64 @@ "os-homedir": "1.0.2" } }, + "xhr": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.4.1.tgz", + "integrity": "sha512-pAIU5vBr9Hiy5cpFIbPnwf0C18ZF86DBsZKrlsf87N5De/JbA6RJ83UP/cv+aljl4S40iRVMqP4pr4sF9Dnj0A==", + "requires": { + "global": "4.3.2", + "is-function": "1.0.1", + "parse-headers": "2.0.1", + "xtend": "4.0.1" + } + }, + "xhr-request": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xhr-request/-/xhr-request-1.0.1.tgz", + "integrity": "sha1-g/CKSyC+7Geowcco6BAvTJ7svdo=", + "requires": { + "buffer-to-arraybuffer": "0.0.2", + "object-assign": "3.0.0", + "query-string": "2.4.2", + "simple-get": "1.4.3", + "timed-out": "2.0.0", + "url-set-query": "1.0.0", + "xhr": "2.4.1" + }, + "dependencies": { + "object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=" + }, + "query-string": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-2.4.2.tgz", + "integrity": "sha1-fbBmZCCAS6qSrp8miWKFWnYUPfs=", + "requires": { + "strict-uri-encode": "1.1.0" + } + }, + "timed-out": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-2.0.0.tgz", + "integrity": "sha1-84sK6B03R9YoAB9B2vxlKs5nHAo=" + } + } + }, + "xhr-request-promise": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/xhr-request-promise/-/xhr-request-promise-0.1.2.tgz", + "integrity": "sha1-NDxE0e53JrhkgGloLQ+EDIO0Jh0=", + "requires": { + "xhr-request": "1.0.1" + } + }, + "xhr2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.1.4.tgz", + "integrity": "sha1-f4dliEdxbbUCYyOBL4GMras4el8=" + }, "xml-char-classes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/xml-char-classes/-/xml-char-classes-1.0.0.tgz", @@ -18181,11 +19307,6 @@ "integrity": "sha1-TYuPHszTQZqjYgYb7O9RXh5VljU=", "dev": true }, - "xmlhttprequest": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", - "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" - }, "xss-filters": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/xss-filters/-/xss-filters-1.2.7.tgz", @@ -18205,8 +19326,7 @@ "yaeti": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=", - "dev": true + "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" }, "yallist": { "version": "2.1.2", diff --git a/js/package.json b/js/package.json index 89c9d453b..b75bb67c8 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "Parity", - "version": "1.9.22", + "version": "1.9.40", "main": "src/index.parity.js", "jsnext:main": "src/index.parity.js", "author": "Parity Team ", @@ -27,7 +27,7 @@ "build:embed": "cross-env EMBED=1 node webpack/embed", "build:i18n": "npm run clean && npm run build && babel-node ./scripts/build-i18n.js", "ci:build": "cross-env NODE_ENV=production npm run build", - "clean": "rimraf ./.build ./.coverage ./.happypack ./build", + "clean": "rimraf ./.build ./.coverage ./.happypack", "coveralls": "npm run testCoverage && coveralls < coverage/lcov.info", "lint": "npm run lint:css && npm run lint:js", "lint:cached": "npm run lint:css && npm run lint:js:cached", @@ -55,8 +55,6 @@ "@parity/dapp-signaturereg": "paritytech/dapp-signaturereg", "@parity/dapp-tokendeploy": "paritytech/dapp-tokendeploy", "@parity/dapp-tokenreg": "paritytech/dapp-tokenreg", - "@parity/dapp-vaults": "paritytech/dapp-vaults", - "@parity/dapp-web": "paritytech/dapp-web", "babel-cli": "6.26.0", "babel-core": "6.26.0", "babel-eslint": "7.1.1", @@ -70,7 +68,7 @@ "babel-plugin-transform-runtime": "6.23.0", "babel-plugin-webpack-alias": "2.1.2", "babel-polyfill": "6.26.0", - "babel-preset-env": "1.6.0", + "babel-preset-env": "1.6.1", "babel-preset-react": "6.24.1", "babel-preset-stage-0": "6.24.1", "babel-register": "6.26.0", @@ -142,14 +140,13 @@ "yargs": "6.6.0" }, "dependencies": { - "@parity/api": "2.1.x", - "@parity/dapp-accounts": "paritytech/dapp-accounts", + "@parity/api": "^2.1.14", "@parity/plugin-signer-account": "paritytech/plugin-signer-account", "@parity/plugin-signer-default": "paritytech/plugin-signer-default", "@parity/plugin-signer-hardware": "paritytech/plugin-signer-hardware", "@parity/plugin-signer-qr": "paritytech/plugin-signer-qr", - "@parity/shared": "2.2.x", - "@parity/ui": "~3.0.4", + "@parity/shared": "2.2.14", + "@parity/ui": "3.0.20", "keythereum": "1.0.2", "lodash.flatten": "4.4.0", "lodash.omitby": "4.6.0", @@ -173,6 +170,6 @@ "semantic-ui-react": "0.77.0", "solc": "ngotchac/solc-js", "store": "1.3.20", - "web3": "0.17.0-beta" + "web3": "1.0.0-beta.26" } } diff --git a/js/scripts/test.js b/js/scripts/test.js index 5bcb1b273..e62cd8fb9 100644 --- a/js/scripts/test.js +++ b/js/scripts/test.js @@ -1 +1 @@ -// test script 26 +// test script 27 diff --git a/js/src/Application/application.css b/js/src/Application/application.css index 82b3a7534..2022da923 100644 --- a/js/src/Application/application.css +++ b/js/src/Application/application.css @@ -19,39 +19,35 @@ box-sizing: border-box; display: flex; flex-direction: column; - min-height: 100vh; + height: 100vh; + width: 100%; .logo { - top: 0; - right: 0; - left: 0; - bottom: 0; - opacity: 0.2; - position: fixed; - padding: 7em; - text-align: center; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); z-index: 0; + opacity: 0.2; + } - img { - display: inline-block; - margin: 0 auto; + .content { + box-sizing: border-box; + display: flex; + flex-grow: 1; + position: relative; + + /* Show scrollbar for Homepage only, dapps' scrollbar are handled inside + * their iframe. + */ + & > div { + overflow-y: auto; } } -} -.container { - box-sizing: border-box; - display: flex; - flex-grow: 1; - flex-direction: column; -} - -.content { - padding: 0; -} - -.error { - padding: 2em; - background: red; - color: white; + .error { + padding: 2em; + background: red; + color: white; + } } diff --git a/js/src/Application/application.js b/js/src/Application/application.js index e8015c948..3060032b6 100644 --- a/js/src/Application/application.js +++ b/js/src/Application/application.js @@ -36,7 +36,6 @@ import Status from '../Status'; import UpgradeParity from '../UpgradeParity'; import { appLogoDark as parityLogo } from '../config'; -import Store from './store'; import styles from './application.css'; const inFrame = window.parent !== window && window.parent.frames.length !== 0; @@ -54,7 +53,6 @@ class Application extends Component { pending: PropTypes.array } - store = new Store(this.context.api); hwstore = HardwareStore.get(this.context.api); upgradeStore = UpgradeStore.get(this.context.api); @@ -77,6 +75,7 @@ class Application extends Component { return (
+ { blockNumber ? @@ -109,35 +108,29 @@ class Application extends Component { } renderApp () { - const { children } = this.props; - - return ( -
- - - - - -
- { children } -
-
- ); + return [ + , + , + , + , + , + this.renderContent() + ]; } renderMinimized () { + return [ + , + this.renderContent() + ]; + } + + renderContent () { const { children } = this.props; return ( -
-
- -
- - { children } +
+ {children}
); } diff --git a/js/src/Application/store.js b/js/src/Application/store.js deleted file mode 100644 index 9008997b8..000000000 --- a/js/src/Application/store.js +++ /dev/null @@ -1,95 +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 . - -import { action, observable } from 'mobx'; -import store from 'store'; - -const OLD_LS_FIRST_RUN_KEY = 'showFirstRun'; -const LS_FIRST_RUN_KEY = '_parity::showFirstRun'; - -export default class Store { - @observable firstrunVisible = false; - - constructor (api) { - // Migrate the old key to the new one - this._migrateStore(); - - this._api = api; - - // Show the first run the storage doesn't hold `false` value - const firstrunVisible = store.get(LS_FIRST_RUN_KEY) !== false; - - // Only check accounts if we might show the first run - if (firstrunVisible) { - api.transport.once('open', () => { - this._checkAccounts(); - }); - } else { - this.firstrunVisible = false; - } - } - - @action closeFirstrun = () => { - this.toggleFirstrun(false); - } - - @action toggleFirstrun = (visible = false) => { - this.firstrunVisible = visible; - - // There's no need to write to storage that the - // First Run should be visible - if (!visible) { - store.set(LS_FIRST_RUN_KEY, !!visible); - } - } - - /** - * Migrate the old LocalStorage key format - * to the new one - */ - _migrateStore () { - const oldValue = store.get(OLD_LS_FIRST_RUN_KEY); - const newValue = store.get(LS_FIRST_RUN_KEY); - - if (newValue === undefined && oldValue !== undefined) { - store.set(LS_FIRST_RUN_KEY, oldValue); - store.remove(OLD_LS_FIRST_RUN_KEY); - } - } - - _checkAccounts () { - return Promise - .all([ - this._api.parity.listVaults(), - this._api.parity.allAccountsInfo() - ]) - .then(([ vaults, info ]) => { - const accounts = Object.keys(info) - .filter((address) => info[address].uuid) - // In DEV mode, the empty phrase account is already added - .filter((address) => address.toLowerCase() !== '0x00a329c0648769a73afac7f9381e08fb43dbea72'); - - // Has accounts if any vaults or accounts - const hasAccounts = (accounts && accounts.length > 0) || (vaults && vaults.length > 0); - - // Show First Run if no accounts and no vaults - this.toggleFirstrun(!hasAccounts); - }) - .catch((error) => { - console.error('checkAccounts', error); - }); - } -} diff --git a/js/src/Connection/connection.css b/js/src/Connection/connection.css index 43e7da408..62aea01c6 100644 --- a/js/src/Connection/connection.css +++ b/js/src/Connection/connection.css @@ -53,9 +53,9 @@ } .form { - margin-top: 0.75em; + margin-top: 1em; padding: 0 4em; - text-align: left; + text-align: center; } .timestamp { @@ -71,14 +71,15 @@ } .icons { - padding-top: 1.5em; + margin-top: 2em; + margin-bottom: 0; } .icon, .iconSmall { display: inline-block; - padding: 1em; - padding-bottom: 1em; + margin-top: 2em; + padding: 0 1.5em; vertical-align: middle; } @@ -92,10 +93,13 @@ } .console { + width: 80%; font-family: 'Roboto Mono', monospace; - background: rgba(0, 0, 0, 0.25); + background: rgba(0, 0, 0, 0.65); font-size: 16px; - padding: 0 0.25em; + padding: 0.5em 0.25em; + margin: 1em auto; + border-radius: 3px; } @keyframes pulse { @@ -123,10 +127,11 @@ color: rgb(208, 208, 208); } -.formInput input { +.formInput input[type=text] { background: rgba(0, 0, 0, 0.25) !important; + text-align: center; } -.formInput input:focus { +.formInput input[type=text]:focus { background: rgba(0, 0, 0, 0.25) !important; } diff --git a/js/src/Connection/connection.js b/js/src/Connection/connection.js index d6501b45d..0817f25ce 100644 --- a/js/src/Connection/connection.js +++ b/js/src/Connection/connection.js @@ -21,7 +21,7 @@ import PropTypes from 'prop-types'; import GradientBg from '@parity/ui/lib/GradientBg'; import Input from '@parity/ui/lib/Form/Input'; -import { CompareIcon, ComputerIcon, DashboardIcon, VpnIcon } from '@parity/ui/lib/Icons'; +import { CompareIcon, ComputerIcon, DashboardIcon, KeyIcon } from '@parity/ui/lib/Icons'; import styles from './connection.css'; @@ -64,7 +64,7 @@ class Connection extends Component {
{ needsToken - ? + ? : }
@@ -90,18 +90,12 @@ class Connection extends Component {
parity signer new-token
+ newToken:
$ parity signer new-token
} } />
-
- -
} onChange={ this.onChangeToken } diff --git a/js/src/Dapp/dapp.css b/js/src/Dapp/dapp.css index 314d6c747..a458aa7a4 100644 --- a/js/src/Dapp/dapp.css +++ b/js/src/Dapp/dapp.css @@ -15,22 +15,20 @@ /* along with Parity. If not, see . */ -.frame { +.frame, +.full { background: white; box-sizing: border-box; border: 0; flex-grow: 1; + margin: 0; + padding: 0; opacity: 0; width: 100%; z-index: 1; } .full { - width: 100vw; - height: 100vh; - margin: 0; - padding: 0; - background: white; font-family: 'Roboto', sans-serif; font-size: 16px; font-weight: 300; diff --git a/js/src/Dapp/dapp.js b/js/src/Dapp/dapp.js index 6c5b8f74a..58b583d49 100644 --- a/js/src/Dapp/dapp.js +++ b/js/src/Dapp/dapp.js @@ -19,7 +19,6 @@ import { observer } from 'mobx-react'; import { FormattedMessage } from 'react-intl'; import PropTypes from 'prop-types'; -import Api from '@parity/api'; import builtinDapps from '@parity/shared/lib/config/dappsBuiltin.json'; import viewsDapps from '@parity/shared/lib/config/dappsViews.json'; import DappsStore from '@parity/shared/lib/mobx/dappsStore'; @@ -27,15 +26,7 @@ import HistoryStore from '@parity/shared/lib/mobx/historyStore'; import styles from './dapp.css'; -const internalDapps = [] - .concat(viewsDapps, builtinDapps) - .map((app) => { - if (app.id && app.id.substr(0, 2) !== '0x') { - app.id = Api.util.sha3(app.id); - } - - return app; - }); +const internalDapps = [].concat(viewsDapps, builtinDapps); @observer export default class Dapp extends Component { @@ -130,13 +121,9 @@ export default class Dapp extends Component { dapphost = ''; } - const appId = this.context.api.util.isHex(app.id) - ? app.id - : this.context.api.sha3(app.url); - src = window.location.protocol === 'file:' - ? `dapps/${appId}/index.html` - : `${dapphost}/dapps/${appId}/index.html`; + ? `dapps/${app.id}/index.html` + : `${dapphost}/dapps/${app.id}/index.html`; break; } diff --git a/js/src/DappRequests/README.md b/js/src/DappRequests/README.md index f4ed60b40..84e838789 100644 --- a/js/src/DappRequests/README.md +++ b/js/src/DappRequests/README.md @@ -6,4 +6,16 @@ To be clear with the terminology used in the code here: - a *methodGroup* is the grouping of similar methods (see `methodGroups.js`) - a *permission* is a boolean which tells if an app is allowed to call a method or not - a *request* is when an app prompts the shell to call a method -- a *requestGroup* is an array of *requests* whose methods are in the same *methodGroup* +- a *requestGroup* is a map of the following form +```javascript +{ + appId1: { + methodGroup1: [request1, request2] // This is a requestGroup sub-item + }, + appId2: { + methodGroup1: [request1] + methodGroup2: [request3] + }, + // ... +} +``` diff --git a/js/src/DappRequests/RequestGroups/RequestGroupSubItem/RequestGroupSubItem.css b/js/src/DappRequests/RequestGroups/RequestGroupSubItem/RequestGroupSubItem.css new file mode 100644 index 000000000..74837da45 --- /dev/null +++ b/js/src/DappRequests/RequestGroups/RequestGroupSubItem/RequestGroupSubItem.css @@ -0,0 +1,24 @@ +/* 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 . +*/ + +.requestGroupSubItem { + margin-top: 2px; + + .requestGroupSubItemTitle { + margin-right: 10px; + } +} diff --git a/js/src/DappRequests/RequestGroups/RequestGroupSubItem/RequestGroupSubItem.js b/js/src/DappRequests/RequestGroups/RequestGroupSubItem/RequestGroupSubItem.js new file mode 100644 index 000000000..1ed845a21 --- /dev/null +++ b/js/src/DappRequests/RequestGroups/RequestGroupSubItem/RequestGroupSubItem.js @@ -0,0 +1,76 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import { FormattedMessage } from 'react-intl'; + +import Popup from 'semantic-ui-react/dist/commonjs/modules/Popup'; +import Button from '@parity/ui/lib/Button'; + +import methodGroups from '../../methodGroups'; +import styles from './RequestGroupSubItem.css'; + +export default class RequestGroupSubItem extends PureComponent { + handleApprove = () => this.props.onApprove(this.props.requests, this.props.groupId) + + handleReject = () => this.props.onReject(this.props.requests) + + render () { + const { groupId } = this.props; + + return ( +
+ + Permission for{' '} + {groupId} } + content={ `Requested methods: ${methodGroups[groupId].methods.join(', ')}` } + /> + +
+ + ); + } +} + +RequestGroupSubItem.propTypes = { + className: PropTypes.string, + groupId: PropTypes.string, + onApprove: PropTypes.func.isRequired, + onReject: PropTypes.func.isRequired, + requests: PropTypes.array.isRequired +}; diff --git a/js/src/DappRequests/RequestGroups/RequestGroupSubItem/index.js b/js/src/DappRequests/RequestGroups/RequestGroupSubItem/index.js new file mode 100644 index 000000000..475d0a0f4 --- /dev/null +++ b/js/src/DappRequests/RequestGroups/RequestGroupSubItem/index.js @@ -0,0 +1,17 @@ +// 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 . + +export default from './RequestGroupSubItem'; diff --git a/js/src/DappRequests/RequestGroups/RequestGroups.css b/js/src/DappRequests/RequestGroups/RequestGroups.css index 65335e85e..7354c7870 100644 --- a/js/src/DappRequests/RequestGroups/RequestGroups.css +++ b/js/src/DappRequests/RequestGroups/RequestGroups.css @@ -28,12 +28,4 @@ $backgroundTwo: #e57a00; > span { margin-right: 30px; } - - .requestGroup { - margin-top: 2px; - - .requestGroupTitle { - margin-right: 10px; - } - } } diff --git a/js/src/DappRequests/RequestGroups/RequestGroups.js b/js/src/DappRequests/RequestGroups/RequestGroups.js index 42328ed2a..fd386370f 100644 --- a/js/src/DappRequests/RequestGroups/RequestGroups.js +++ b/js/src/DappRequests/RequestGroups/RequestGroups.js @@ -14,49 +14,25 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import React, { PureComponent } from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from 'react-intl'; -import Popup from 'semantic-ui-react/dist/commonjs/modules/Popup'; -import Button from '@parity/ui/lib/Button'; - import DappsStore from '@parity/shared/lib/mobx/dappsStore'; +import RequestGroupSubItem from './RequestGroupSubItem'; import styles from './RequestGroups.css'; -export default class RequestGroups extends PureComponent { - state = { - opened: false - }; - - handleApproveRequestGroup = groupId => { - const { requestGroups, onApproveRequestGroup } = this.props; - - onApproveRequestGroup(Object.values(requestGroups[groupId].map(({ requestId }) => requestId))); - } - - handleRejectRequestGroup = groupId => { - const { requestGroups, onRejectRequestGroup } = this.props; - - onRejectRequestGroup(Object.values(requestGroups[groupId].map(({ requestId }) => requestId))); - } - - renderPopupContent = groupId => { - const { requestGroups } = this.props; - // Get unique list of methods in that request group - const requestedMethods = [...new Set( - Object.values(requestGroups[groupId]) - .map(request => request.data.method || request.data.params[0]) - )]; - - return `Requested methods: ${requestedMethods.join(', ')}`; +export default class RequestGroups extends Component { + handleApproveRequestGroup = (requests, groupId) => { + this.props.onApproveRequestGroup(requests, groupId, this.props.appId); } render () { const { appId, - requestGroups + requestGroups, + onRejectRequestGroup } = this.props; const app = DappsStore.get().getAppById(appId); @@ -72,35 +48,13 @@ export default class RequestGroups extends PureComponent { } } /> {Object.keys(requestGroups).map(groupId => ( -
- - Permission for{' '} - {groupId} } - content={ this.renderPopupContent(groupId) } - /> - -
+ ))}
); diff --git a/js/src/DappRequests/dappRequests.js b/js/src/DappRequests/dappRequests.js index 275fbbb23..406e44078 100644 --- a/js/src/DappRequests/dappRequests.js +++ b/js/src/DappRequests/dappRequests.js @@ -17,6 +17,7 @@ import { observer } from 'mobx-react'; import React, { Component } from 'react'; +import methodGroups from './methodGroups'; import RequestGroups from './RequestGroups'; import Store from './store'; import styles from './dappRequests.css'; @@ -24,12 +25,16 @@ import styles from './dappRequests.css'; class DappRequests extends Component { store = Store.get(); - handleApproveRequestGroup = requestIds => { - requestIds.forEach(this.store.approveRequest); + // When we approve a requestGroup, when approve all the requests, and add permissions + // to all the other methods in the same methodGroup + handleApproveRequestGroup = (requests, groupId, appId) => { + requests.map(({ requestId }) => requestId).forEach(this.store.approveRequest); + methodGroups[groupId].methods.forEach(method => this.store.addAppPermission(method, appId)); } - handleRejectRequestGroup = requestIds => { - requestIds.forEach(this.store.rejectRequest); + // When we reject a requestGroup, we reject the requests in that group + handleRejectRequestGroup = requests => { + requests.map(({ requestId }) => requestId).forEach(this.store.rejectRequest); } render () { diff --git a/js/src/DappRequests/methodGroups.js b/js/src/DappRequests/methodGroups.js index c37357428..efcf6a227 100644 --- a/js/src/DappRequests/methodGroups.js +++ b/js/src/DappRequests/methodGroups.js @@ -17,16 +17,33 @@ const methodGroups = { shell: { methods: [ + 'shell_loadApp' + ] + }, + dapps: { + methods: [ + 'parity_dappsRefresh', + 'parity_dappsUrl', 'shell_getApps', - 'shell_getFilteredMethods', - 'shell_getMethodGroups', - 'shell_getMethodPermissions', + 'shell_getMethodPermissions' + ] + }, + dappsEdit: { + methods: [ + 'shell_setAppPinned', 'shell_setAppVisibility', 'shell_setMethodPermissions' ] }, - accountsView: { - methods: ['parity_accountsInfo', 'parity_allAccountsInfo'] + accounts: { + methods: [ + 'parity_accountsInfo', + 'parity_allAccountsInfo', + 'parity_getNewDappsAddresses', + 'parity_getNewDappsDefaultAddress', + 'parity_hardwareAccountsInfo', + 'parity_lockedHardwareAccountsInfo' + ] }, accountsCreate: { methods: [ @@ -40,35 +57,67 @@ const methodGroups = { ] }, accountsEdit: { - methods: ['parity_setAccountName', 'parity_setAccountMeta'] - }, - upgrade: { methods: [ - 'parity_consensusCapability', - 'parity_executeUpgrade', - 'parity_upgradeReady', - 'parity_versionInfo' + 'parity_setAccountName', + 'parity_setAccountMeta', + 'parity_hardwarePinMatrixAck', + 'parity_setNewDappsAddresses', + 'parity_setNewDappsDefaultAddress' + ] + }, + accountsDelete: { + methods: [ + 'parity_killAccount', + 'parity_removeAddress' ] }, vaults: { methods: [ - 'parity_changeVault', - 'parity_changeVaultPassword', 'parity_closeVault', 'parity_getVaultMeta', 'parity_listVaults', 'parity_listOpenedVaults', - 'parity_newVault', - 'parity_openVault', + 'parity_openVault' + ] + }, + vaultsCreate: { + methods: [ + 'parity_newVault' + ] + }, + vaultsEdit: { + methods: [ + 'parity_changeVault', + 'parity_changeVaultPassword', 'parity_setVaultMeta' ] }, - other: { + signerRequests: { methods: [ 'parity_checkRequest', - 'parity_hashContent', 'parity_localTransactions' ] + }, + signerConfirm: { + methods: [ + 'parity_confirmRequest', + 'parity_confirmRequestRaw', + 'parity_rejectRequest' + ] + }, + node: { + methods: [ + 'parity_hashContent', + 'parity_consensusCapability', + 'parity_upgradeReady', + 'parity_versionInfo', + 'parity_wsUrl' + ] + }, + nodeUpgrade: { + methods: [ + 'parity_executeUpgrade' + ] } }; diff --git a/js/src/DappRequests/store.js b/js/src/DappRequests/store.js index 5a89ad29a..ee9905288 100644 --- a/js/src/DappRequests/store.js +++ b/js/src/DappRequests/store.js @@ -52,7 +52,7 @@ export default class Store { accumulator[appId] = accumulator[appId] || {}; accumulator[appId][methodGroup] = accumulator[appId][methodGroup] || []; - accumulator[appId][methodGroup].push({ data, requestId }); // Append the requestId field in the request object + accumulator[appId][methodGroup].push({ data, requestId }); // Push request & append the requestId field in the request object return accumulator; }, {}); @@ -99,7 +99,7 @@ export default class Store { this.requests = { ...this.requests }; }; - getPermissionId = (method, appId) => `${method}:${appId}` // Create an id to identify permissions based on method and appId + getPermissionId = (method, appId) => `${method}:${appId}`; // Create an id to identify permissions based on method and appId getMethodFromRequest = requestId => { const { data: { method, params } } = this.requests[requestId]; @@ -131,13 +131,10 @@ export default class Store { }; setPermissions = _permissions => { - const permissions = {}; - - Object.keys(_permissions).forEach(id => { - permissions[id] = !!_permissions[id]; - }); - - this.permissions = permissions; + this.permissions = { + ...this.permissions, + ..._permissions + }; this.savePermissions(); return true; @@ -245,14 +242,9 @@ export default class Store { return; } - if ( - (method && - methodGroupFromMethod[method] && - !this.hasTokenPermission(method, token)) || - (api && - methodGroupFromMethod[params[0]] && - !this.hasTokenPermission(method, token)) - ) { + const _method = api ? params[0] : method; + + if (methodGroupFromMethod[_method] && !this.hasTokenPermission(_method, token)) { this.queueRequest(id, { // The requestId of a request is the id inside data data, source diff --git a/js/src/Dapps/DappCard/dappCard.css b/js/src/Dapps/DappCard/dappCard.css new file mode 100644 index 000000000..d802b7fe6 --- /dev/null +++ b/js/src/Dapps/DappCard/dappCard.css @@ -0,0 +1,54 @@ +/* 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 . +*/ + +.card { + position: relative; + margin-top: 3em; + + .pin { + display: none; + position: absolute; + top: 0; + right: 1.6em; + z-index: 1; + } + + .pin.pinned { + display: block; + transform: rotate(45deg); + } + + &:hover { + .pin { + display: block; + } + } + + .content { + display: block; + padding-top: 1em; + + .title { + margin-top: 0.8em; + } + + .image { + margin-left: auto; + margin-right: auto; + } + } +} diff --git a/js/src/Dapps/DappCard/dappCard.js b/js/src/Dapps/DappCard/dappCard.js new file mode 100644 index 000000000..a0fd8f3a8 --- /dev/null +++ b/js/src/Dapps/DappCard/dappCard.js @@ -0,0 +1,72 @@ +// 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 . + +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { Link } from 'react-router'; + +import DappIcon from '@parity/ui/lib/DappIcon'; +import Header from 'semantic-ui-react/dist/commonjs/elements/Header'; +import Button from 'semantic-ui-react/dist/commonjs/elements/Button'; + +import styles from './dappCard.css'; + +export default class DappCard extends Component { + static propTypes = { + app: PropTypes.object.isRequired, + availability: PropTypes.string.isRequired, + className: PropTypes.string, + onPin: PropTypes.func, + pinned: PropTypes.bool + }; + + handlePin = () => this.props.onPin(this.props.app.id) + + render () { + const { app, availability, className, pinned } = this.props; + + if (app.onlyPersonal && availability !== 'personal') { + return null; + } + + return ( +
+
+ ); + } +} diff --git a/js/src/Dapps/DappCard/index.js b/js/src/Dapps/DappCard/index.js new file mode 100644 index 000000000..eed38ca78 --- /dev/null +++ b/js/src/Dapps/DappCard/index.js @@ -0,0 +1,17 @@ +// 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 . + +export default from './dappCard'; diff --git a/js/src/Dapps/dapps.css b/js/src/Dapps/dapps.css index 8371fa0d0..6a4cc181d 100644 --- a/js/src/Dapps/dapps.css +++ b/js/src/Dapps/dapps.css @@ -16,13 +16,46 @@ */ .overlay { + background: rgba(40, 40, 40, 0.85); color: white; line-height: 1.5em; - margin: 0 auto; - text-align: left; - max-width: 980px; + padding: 4em 2em; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + text-align: center; + z-index: 100; - & > div:first-child { - padding-bottom: 1em; + div { + max-width: 640px; + margin: 1em auto; + text-align: left; + } + + .accept { + label { + color: white; + } + } +} + +.sectionTitle { + padding: 2em 0 0 4em !important; + margin-bottom: 0 !important; +} + +.dapps { + padding: 0 1.5em; + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + + .dapp { + width: 12em; + margin-left: 2em; + margin-right: 2em; } } diff --git a/js/src/Dapps/dapps.js b/js/src/Dapps/dapps.js index 1b96f4f1d..f73947a6b 100644 --- a/js/src/Dapps/dapps.js +++ b/js/src/Dapps/dapps.js @@ -14,20 +14,19 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import omitBy from 'lodash.omitby'; import { observer } from 'mobx-react'; import React, { Component } from 'react'; import { FormattedMessage } from 'react-intl'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; -import DappCard from '@parity/ui/lib/DappCard'; import Checkbox from '@parity/ui/lib/Form/Checkbox'; import Page from '@parity/ui/lib/Page'; -import SectionList from '@parity/ui/lib/SectionList'; import DappsStore from '@parity/shared/lib/mobx/dappsStore'; +import DappCard from './DappCard'; + import styles from './dapps.css'; @observer @@ -37,7 +36,6 @@ class Dapps extends Component { }; static propTypes = { - accounts: PropTypes.object.isRequired, availability: PropTypes.string.isRequired }; @@ -47,101 +45,76 @@ class Dapps extends Component { this.store.loadAllApps(); } - render () { - let externalOverlay = null; - - if (this.store.externalOverlayVisible) { - externalOverlay = ( -
-
- -
-
- - } - checked={ false } - onClick={ this.onClickAcceptExternal } - /> -
-
- ); + handlePin = (appId) => { + if (this.store.displayApps[appId].pinned) { + this.store.unpinApp(appId); + } else { + this.store.pinApp(appId); } + } - return ( - ( + apps && apps.length > 0 && +
+ { + apps.map((app, index) => ( + + )) + } +
+ ) + + render () { + return ( + + {this.renderSection(this.store.pinnedApps)} + {this.renderSection(this.store.visibleUnpinned)} + { + this.store.externalOverlayVisible && + ( +
+
+ +
+
+ + } + checked={ false } + onClick={ this.onClickAcceptExternal } + /> +
+
+ ) } - > - { this.renderList(this.store.visibleLocal) } - { this.renderList(this.store.visibleViews) } - { this.renderList(this.store.visibleBuiltin) } - { this.renderList(this.store.visibleNetwork, externalOverlay) }
); } - renderList (items, overlay) { - return ( - - ); - } - - renderApp = (app) => { - if (app.onlyPersonal && this.props.availability !== 'personal') { - return null; - } - - return ( - - ); - } - onClickAcceptExternal = () => { this.store.closeExternalOverlay(); } - - openPermissionsModal = () => { - const { accounts } = this.props; - - this.permissionStore.openModal(accounts); - } } function mapStateToProps (state) { - const { accounts } = state.personal; const { availability = 'unknown' } = state.nodeStatus.nodeKind || {}; - /** - * Do not show the Wallet Accounts in the Dapps - * Permissions Modal. This will come in v1.6, but - * for now it would break dApps using Web3... - */ - const _accounts = omitBy(accounts, (account) => account.wallet); - return { - accounts: _accounts, availability }; } diff --git a/js/src/FirstRun/TnC/tnc.js b/js/src/FirstRun/TnC/tnc.js index 93199b5c3..654970db4 100644 --- a/js/src/FirstRun/TnC/tnc.js +++ b/js/src/FirstRun/TnC/tnc.js @@ -21,19 +21,16 @@ import ReactMarkdown from 'react-markdown'; import Checkbox from '@parity/ui/lib/Form/Checkbox'; +import tnc from './tnc.md'; + import styles from '../firstRun.css'; -let tnc = ''; - -if (process.env.NODE_ENV !== 'test') { - tnc = require('./tnc.md'); -} - export default function TnC ({ hasAccepted, onAccept }) { return (
, - , - , - , - , - -]; -const BUTTON_LABEL_NEXT = ( - -); @observer -class FirstRun extends Component { - static contextTypes = { - api: PropTypes.object.isRequired - } - - static propTypes = { - hasAccounts: PropTypes.bool.isRequired, - newError: PropTypes.func.isRequired, - onClose: PropTypes.func.isRequired, - visible: PropTypes.bool.isRequired - } - - createStore = new CreateStore(this.context.api, {}, true, false); - +export default class FirstRun extends Component { state = { - stage: 0, hasAcceptedTnc: false } - render () { - const { visible } = this.props; - const { stage } = this.state; + store = new Store(); - if (!visible) { + render () { + const { hasAcceptedTnc } = this.state; + + if (!this.store.visible) { return null; } return ( - { this.renderStage() } - - ); - } - - renderStage () { - const { stage, hasAcceptedTnc } = this.state; - - switch (stage) { - case 0: - return ( - - ); - case 1: - return ( - - ); - case 2: - return ( - - ); - case 3: - return ( - - ); - case 4: - return ( - - ); - case 5: - return ( - - ); - } - } - - renderDialogActions () { - const { hasAccounts } = this.props; - const { stage, hasAcceptedTnc } = this.state; - const { canCreate, phraseBackedUpError } = this.createStore; - - switch (stage) { - case 0: - return ( -
+ } + content={ this.renderPopupContent() } + offset={ 8 } // Empirically looks better + on='click' + hideOnScroll + open={ this.state.isOpen } + onClose={ this.handleClose } + onOpen={ this.handleOpen } + position='bottom right' + /> + ); + } +} + +export default SignerPending; diff --git a/js/src/Status/status.css b/js/src/Status/status.css index e38792f9e..1046ab525 100644 --- a/js/src/Status/status.css +++ b/js/src/Status/status.css @@ -16,6 +16,7 @@ */ $textColor: #ccc; +$statusHeight: 2.75em; .home { color: $textColor !important; @@ -26,15 +27,9 @@ $textColor: #ccc; .container { flex: 0; box-sizing: border-box; - height: 2.75em; - .fixed { - box-sizing: border-box; - position: fixed; - top: 0; - right: 0; - left: 0; - height: 2.75em; + .bar { + height: $statusHeight; z-index: 1000; border-bottom: 1px solid #405161; color: $textColor; @@ -75,13 +70,8 @@ $textColor: #ccc; opacity: 0.75; } - .signerPending { - cursor: pointer; - } - .health { > span { - margin: -0.5rem 0 0.2rem 0 !important; width: auto; } } diff --git a/js/src/Status/status.js b/js/src/Status/status.js index 24513304b..fd39b1862 100644 --- a/js/src/Status/status.js +++ b/js/src/Status/status.js @@ -25,28 +25,26 @@ import GradientBg from '@parity/ui/lib/GradientBg'; import { HomeIcon } from '@parity/ui/lib/Icons'; import NetChain from '@parity/ui/lib/NetChain'; import NetPeers from '@parity/ui/lib/NetPeers'; -import SignerPending from '@parity/ui/lib/SignerPending'; import StatusIndicator from '@parity/ui/lib/StatusIndicator'; import Consensus from './Consensus'; import DefaultAccount from './DefaultAccount'; import AccountStore from '../ParityBar/accountStore'; -import ParityBarStore from '../ParityBar/store'; import SyncWarning from '../SyncWarning'; import PluginStore from './pluginStore'; +import SignerPending from './SignerPending'; import Upgrade from './Upgrade'; import styles from './status.css'; const pluginStore = PluginStore.get(); -const parityBarStore = ParityBarStore.get(); function Status ({ className = '', upgradeStore }, { api }) { const accountStore = AccountStore.get(api); return (
- + ); } diff --git a/js/src/embed.js b/js/src/embed.js index 459442825..76cb75c08 100644 --- a/js/src/embed.js +++ b/js/src/embed.js @@ -66,6 +66,8 @@ class FrameSecureApi extends SecureApi { () => transport, () => 'http:' ); + + this._isConnected = false; } connect () { @@ -73,6 +75,7 @@ class FrameSecureApi extends SecureApi { this.emit('connecting'); // Fire connected event with some delay. setTimeout(() => { + this._isConnected = true; this.emit('connected'); }); } @@ -86,7 +89,7 @@ class FrameSecureApi extends SecureApi { } isConnected () { - return true; + return this._isConnected; } } diff --git a/js/src/index.parity.js b/js/src/index.parity.js index e9059a0ea..82459e826 100644 --- a/js/src/index.parity.js +++ b/js/src/index.parity.js @@ -21,7 +21,6 @@ import ReactDOM from 'react-dom'; import injectTapEventPlugin from 'react-tap-event-plugin'; import { IndexRoute, Redirect, Route, Router, hashHistory } from 'react-router'; -import qs from 'querystring'; import ContractInstances from '@parity/shared/lib/contracts'; import { initStore } from '@parity/shared/lib/redux'; @@ -29,6 +28,9 @@ import ContextProvider from '@parity/ui/lib/ContextProvider'; import '@parity/shared/lib/environment'; +import { redirectLocalhost } from './util/host'; +import { retrieveToken } from './util/token'; + import Application from './Application'; import Dapp from './Dapp'; import Dapps from './Dapps'; @@ -52,45 +54,45 @@ if (process.env.NODE_ENV === 'development') { } */ -const AUTH_HASH = '#/auth?'; +function renderUI (token) { + const api = new SecureApi(window.location.host, token); -let token = null; + api.parity.registryAddress().then((address) => console.log('registryAddress', address)).catch((error) => console.error('registryAddress', error)); -if (window.location.hash && window.location.hash.indexOf(AUTH_HASH) === 0) { - token = qs.parse(window.location.hash.substr(AUTH_HASH.length)).token; + ContractInstances.get(api); + + setupProviderFilters(api.provider); + + const store = initStore(api, hashHistory); + + console.log('UI version', process.env.UI_VERSION); + + ReactDOM.render( + + + + + + + + + + , + document.querySelector('#container') + ); + + // testing, priceTicker gist + // injectExternalScript('https://cdn.rawgit.com/jacogr/396fc583e81b9404e21195a48dc862ca/raw/33e5058a4c0028cf9acf4b0662d75298e41ca6fa/priceTicker.js'); + + // testing, signer plugins + require('@parity/plugin-signer-account'); + require('@parity/plugin-signer-default'); + require('@parity/plugin-signer-hardware'); + require('@parity/plugin-signer-qr'); } -const api = new SecureApi(window.location.host, token); +const token = retrieveToken(); -api.parity.registryAddress().then((address) => console.log('registryAddress', address)).catch((error) => console.error('registryAddress', error)); - -ContractInstances.get(api); - -setupProviderFilters(api.provider); - -const store = initStore(api, hashHistory); - -console.log('UI version', process.env.UI_VERSION); - -ReactDOM.render( - - - - - - - - - - , - document.querySelector('#container') -); - -// testing, priceTicker gist -// injectExternalScript('https://cdn.rawgit.com/jacogr/396fc583e81b9404e21195a48dc862ca/raw/33e5058a4c0028cf9acf4b0662d75298e41ca6fa/priceTicker.js'); - -// testing, signer plugins -import '@parity/plugin-signer-account'; -import '@parity/plugin-signer-default'; -import '@parity/plugin-signer-hardware'; -import '@parity/plugin-signer-qr'; +if (!redirectLocalhost(token)) { + renderUI(token); +} diff --git a/js/src/inject.js b/js/src/inject.js index 7bde1233d..b06ffd396 100644 --- a/js/src/inject.js +++ b/js/src/inject.js @@ -18,8 +18,6 @@ import Api from '@parity/api'; import qs from 'query-string'; import Web3 from 'web3'; -import web3extensions from './web3.extensions'; - function initProvider () { const path = window.location.pathname.split('/'); const query = qs.parse(window.location.search); @@ -28,8 +26,6 @@ function initProvider () { if (appId === 'dapps') { appId = path[2]; - } else if (!Api.util.isHex(appId)) { - appId = Api.util.sha3(appId); } const ethereum = new Api.Provider.PostMessage(appId); @@ -69,8 +65,6 @@ function initWeb3 (ethereum) { web3.eth.defaultAccount = accounts[0]; }); - web3extensions(web3).map((extension) => web3._extend(extension)); - window.web3 = web3; } diff --git a/js/src/shellMiddleware.js b/js/src/shellMiddleware.js index 3cc6b1868..c128d8310 100644 --- a/js/src/shellMiddleware.js +++ b/js/src/shellMiddleware.js @@ -17,76 +17,93 @@ import * as mobx from 'mobx'; import flatten from 'lodash.flatten'; -import { sha3 } from '@parity/api/lib/util/sha3'; -import VisibleStore from '@parity/shared/lib/mobx/dappsStore'; - +import DappsStore from '@parity/shared/lib/mobx/dappsStore'; import RequestStore from './DappRequests/store'; import methodGroups from './DappRequests/methodGroups'; export default function execute (appId, method, params, callback) { - const visibleStore = VisibleStore.get(); + const dappsStore = DappsStore.get(); const requestStore = RequestStore.get(); switch (method) { - case 'shell_getApps': + case 'shell_getApps': { const [displayAll] = params; callback( null, displayAll - ? visibleStore.allApps.slice().map(mobx.toJS) - : visibleStore.visibleApps.slice().map(mobx.toJS) + ? dappsStore.allApps.slice().map(mobx.toJS) + : dappsStore.visibleApps.slice().map(mobx.toJS) ); return true; + } - case 'shell_getFilteredMethods': + case 'shell_getFilteredMethods': { callback( null, flatten(Object.keys(methodGroups).map(key => methodGroups[key].methods)) ); return true; + } - case 'shell_getMethodGroups': + case 'shell_getMethodGroups': { callback( null, methodGroups ); return true; + } - case 'shell_getMethodPermissions': + case 'shell_getMethodPermissions': { callback(null, mobx.toJS(requestStore.permissions)); return true; + } - case 'shell_loadApp': - const [_loadId, loadParams] = params; - const loadId = _loadId.substr(0, 2) !== '0x' ? sha3(_loadId) : _loadId; + case 'shell_loadApp': { + const [loadId, loadParams] = params; const loadUrl = `/${loadId}/${loadParams || ''}`; window.location.hash = loadUrl; callback(null, true); return true; + } - case 'shell_requestNewToken': + case 'shell_requestNewToken': { callback(null, requestStore.createToken(appId)); return true; + } - case 'shell_setAppVisibility': - const [changeId, visibility] = params; + case 'shell_setAppPinned': { + const [appId, pinned] = params; + + callback( + null, + pinned + ? dappsStore.pinApp(appId) + : dappsStore.unpinApp(appId) + ); + return true; + } + + case 'shell_setAppVisibility': { + const [appId, visibility] = params; callback( null, visibility - ? visibleStore.showApp(changeId) - : visibleStore.hideApp(changeId) + ? dappsStore.showApp(appId) + : dappsStore.hideApp(appId) ); return true; + } - case 'shell_setMethodPermissions': + case 'shell_setMethodPermissions': { const [permissions] = params; callback(null, requestStore.setPermissions(permissions)); return true; + } default: return false; diff --git a/js/src/util/host.js b/js/src/util/host.js new file mode 100644 index 000000000..09dc7601d --- /dev/null +++ b/js/src/util/host.js @@ -0,0 +1,40 @@ +// 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 . + +export function createLocation (token, location = window.location) { + const { hash, port, protocol } = location; + let query = ''; + + if (hash && hash.indexOf('?') !== -1) { + // TODO: currently no app uses query-params visible in the shell, this may need adjustment if they do + query = hash; + } else { + query = `${hash || '#/'}${token ? '?token=' : ''}${token || ''}`; + } + + return `${protocol}//127.0.0.1:${port}/${query}`; +} + +export function redirectLocalhost (token) { + // we don't want localhost, rather we want 127.0.0.1 + if (window.location.hostname !== 'localhost') { + return false; + } + + window.location.assign(createLocation(token, window.location)); + + return true; +} diff --git a/js/src/util/host.spec.js b/js/src/util/host.spec.js new file mode 100644 index 000000000..baa34eed1 --- /dev/null +++ b/js/src/util/host.spec.js @@ -0,0 +1,49 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import { createLocation } from './host'; + +describe('createLocation', () => { + it('only changes the host with no token', () => { + expect( + createLocation('', { protocol: 'http:', port: 3000, hostname: 'localhost' }) + ).to.equal('http://127.0.0.1:3000/#/'); + }); + + it('preserves hash when changing the host', () => { + expect( + createLocation('', { protocol: 'http:', port: 3000, hostname: 'localhost', hash: '#/accounts' }) + ).to.equal('http://127.0.0.1:3000/#/accounts'); + }); + + it('adds the token when required', () => { + expect( + createLocation('test', { protocol: 'http:', port: 3000, hostname: 'localhost' }) + ).to.equal('http://127.0.0.1:3000/#/?token=test'); + }); + + it('preserves hash when token adjusted', () => { + expect( + createLocation('test', { protocol: 'http:', port: 3000, hostname: 'localhost', hash: '#/accounts' }) + ).to.equal('http://127.0.0.1:3000/#/accounts?token=test'); + }); + + it('does not override already-passed parameters', () => { + expect( + createLocation('test', { protocol: 'http:', port: 3000, hostname: 'localhost', hash: '#/accounts?token=abc' }) + ).to.equal('http://127.0.0.1:3000/#/accounts?token=abc'); + }); +}); diff --git a/js/src/util/token.js b/js/src/util/token.js new file mode 100644 index 000000000..cf0683f5a --- /dev/null +++ b/js/src/util/token.js @@ -0,0 +1,54 @@ +// 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 . + +import qs from 'querystring'; +import store from 'store'; + +const TOKEN_QS = 'token='; + +function parseTokenQuery (query) { + try { + return qs.parse(query).token; + } catch (error) { + return null; + } +} + +export function retrieveToken (location = window.location) { + const hashIndex = location.hash + ? location.hash.indexOf(TOKEN_QS) + : -1; + const searchIndex = location.search + ? location.search.indexOf(TOKEN_QS) + : -1; + + let token = null; + + if (hashIndex !== -1) { + // extract from hash (e.g. http://127.0.0.1:8180/#/auth?token=...) + token = parseTokenQuery(location.hash.substr(hashIndex)); + } else if (searchIndex !== -1) { + // extract from query (e.g. http://127.0.0.1:3000/?token=...) + token = parseTokenQuery(location.search); + } + + if (!token) { + // we don't have a token, attempt from localStorage + token = store.get('sysuiToken'); + } + + return token; +} diff --git a/js/src/util/token.spec.js b/js/src/util/token.spec.js new file mode 100644 index 000000000..06ebe02b6 --- /dev/null +++ b/js/src/util/token.spec.js @@ -0,0 +1,55 @@ +// 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 . + +import { retrieveToken } from './token'; + +describe('retrieveToken', () => { + it('returns nothing when not found in hash, search or localStorage', () => { + expect(retrieveToken()).not.to.be.ok; + }); + + describe('localStorage', () => { + beforeEach(() => { + localStorage.setItem('sysuiToken', 'yQls-7TX1-t0Jb-0001'); + }); + + afterEach(() => { + localStorage.removeItem('sysuiToken'); + }); + + it('retrieves the token', () => { + expect(retrieveToken()).to.equal('yQls-7TX1-t0Jb-0001'); + }); + }); + + describe('URL', () => { + it('retrieves the token from search', () => { + expect( + retrieveToken({ + search: 'token=yQls-7TX1-t0Jb-0002' + }) + ).to.equal('yQls-7TX1-t0Jb-0002'); + }); + + it('retrieves the token from hash', () => { + expect( + retrieveToken({ + hash: '#/auth?token=yQls-7TX1-t0Jb-0003' + }) + ).to.equal('yQls-7TX1-t0Jb-0003'); + }); + }); +}); diff --git a/js/test/babel.js b/js/test/babel.js index 36f16cd18..c29d29ab9 100644 --- a/js/test/babel.js +++ b/js/test/babel.js @@ -14,13 +14,5 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -require('babel-register')({ - ignore: function (filename) { - if (filename.indexOf('node_modules') !== -1) { - return filename.indexOf('@parity') === -1; - } - - return false; - } -}); +require('babel-register')(); require('babel-polyfill'); diff --git a/js/webpack/app.js b/js/webpack/app.js index f30a54b21..1e0aeb5f6 100644 --- a/js/webpack/app.js +++ b/js/webpack/app.js @@ -15,7 +15,6 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -const Api = require('@parity/api'); const fs = require('fs'); const path = require('path'); const rimraf = require('rimraf'); @@ -54,7 +53,7 @@ module.exports = { cache: !isProd, devtool: isProd ? false - : '#eval', + : isEmbed ? '#source-map' : '#eval', context: path.join(__dirname, '../src'), entry, output: { @@ -213,16 +212,12 @@ module.exports = { return null; } - const destination = Api.util.isHex(dapp.id) - ? dapp.id - : Api.util.sha3(dapp.url); - if (!fs.existsSync(path.join(dir, 'dist'))) { rimraf.sync(path.join(dir, 'node_modules')); return { from: path.join(dir), - to: `dapps/${destination}/` + to: `dapps/${dapp.id}/` }; } @@ -236,11 +231,11 @@ module.exports = { .filter((from) => fs.existsSync(from)) .map((from) => ({ from, - to: `dapps/${destination}/` + to: `dapps/${dapp.id}/` })) .concat({ from: path.join(dir, 'dist'), - to: `dapps/${destination}/dist/` + to: `dapps/${dapp.id}/dist/` }); }) .filter((copy) => copy) diff --git a/json/Cargo.toml b/json/Cargo.toml index dbc365a1d..5d468a14f 100644 --- a/json/Cargo.toml +++ b/json/Cargo.toml @@ -9,5 +9,4 @@ rustc-hex = "1.0" serde = "1.0" serde_json = "1.0" serde_derive = "1.0" -clippy = { version = "0.0.103", optional = true} diff --git a/json/src/spec/authority_round.rs b/json/src/spec/authority_round.rs index b3e75a6b4..57e354c72 100644 --- a/json/src/spec/authority_round.rs +++ b/json/src/spec/authority_round.rs @@ -22,7 +22,7 @@ use super::ValidatorSet; /// Authority params deserialization. #[derive(Debug, PartialEq, Deserialize)] pub struct AuthorityRoundParams { - /// Block duration. + /// Block duration, in seconds. #[serde(rename="stepDuration")] pub step_duration: Uint, /// Valid authorities @@ -43,6 +43,10 @@ pub struct AuthorityRoundParams { /// Reward per block in wei. #[serde(rename="blockReward")] pub block_reward: Option, + /// Block at which maximum uncle count should be considered. + #[serde(rename="maximumUncleCountTransition")] + pub maximum_uncle_count_transition: Option, + /// Maximum number of accepted uncles. #[serde(rename="maximumUncleCount")] pub maximum_uncle_count: Option, } @@ -73,7 +77,9 @@ mod tests { }, "startStep" : 24, "validateStepTransition": 150, - "blockReward": 5000000 + "blockReward": 5000000, + "maximumUncleCountTransition": 10000000, + "maximumUncleCount": 5 } }"#; @@ -82,6 +88,8 @@ mod tests { assert_eq!(deserialized.params.validators, ValidatorSet::List(vec![Address(H160::from("0xc6d9d2cd449a754c494264e1809c50e34d64562b"))])); assert_eq!(deserialized.params.start_step, Some(Uint(U256::from(24)))); assert_eq!(deserialized.params.immediate_transitions, None); + assert_eq!(deserialized.params.maximum_uncle_count_transition, Some(Uint(10_000_000.into()))); + assert_eq!(deserialized.params.maximum_uncle_count, Some(Uint(5.into()))); } } diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index 330abe936..71bfdd4d8 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -290,7 +290,7 @@ usage! { ARG arg_chain: (String) = "foundation", or |c: &Config| otry!(c.parity).chain.clone(), "--chain=[CHAIN]", - "Specify the blockchain type. CHAIN may be either a JSON chain specification file or olympic, frontier, homestead, mainnet, morden, ropsten, classic, expanse, musicoin, testnet, kovan or dev.", + "Specify the blockchain type. CHAIN may be either a JSON chain specification file or olympic, frontier, homestead, mainnet, morden, ropsten, classic, expanse, musicoin, ellaism, testnet, kovan or dev.", ARG arg_keys_path: (String) = "$BASE/keys", or |c: &Config| otry!(c.parity).keys_path.clone(), "--keys-path=[PATH]", @@ -335,6 +335,10 @@ usage! { "--keys-iterations=[NUM]", "Specify the number of iterations to use when deriving key from the password (bigger is more secure)", + ARG arg_accounts_refresh: (u64) = 5u64, or |c: &Config| otry!(c.account).refresh_time.clone(), + "--accounts-refresh=[TIME]", + "Specify the cache time of accounts read from disk. If you manage thousands of accounts set this to 0 to disable refresh.", + ARG arg_unlock: (Option) = None, or |c: &Config| otry!(c.account).unlock.as_ref().map(|vec| vec.join(",")), "--unlock=[ACCOUNTS]", "Unlock ACCOUNTS for the duration of the execution. ACCOUNTS is a comma-delimited list of addresses. Implies --no-ui.", @@ -1013,6 +1017,7 @@ struct Account { unlock: Option>, password: Option>, keys_iterations: Option, + refresh_time: Option, disable_hardware: Option, fast_unlock: Option, } @@ -1433,6 +1438,7 @@ mod tests { arg_unlock: Some("0xdeadbeefcafe0000000000000000000000000000".into()), arg_password: vec!["~/.safe/password.file".into()], arg_keys_iterations: 10240u32, + arg_accounts_refresh: 5u64, flag_no_hardware_wallets: false, flag_fast_unlock: false, @@ -1671,6 +1677,7 @@ mod tests { unlock: Some(vec!["0x1".into(), "0x2".into(), "0x3".into()]), password: Some(vec!["passwdfile path".into()]), keys_iterations: None, + refresh_time: None, disable_hardware: None, fast_unlock: None, }), diff --git a/parity/cli/usage.rs b/parity/cli/usage.rs index 71ac01295..388afd1c1 100644 --- a/parity/cli/usage.rs +++ b/parity/cli/usage.rs @@ -152,7 +152,7 @@ macro_rules! usage { use toml; use std::{fs, io, process}; use std::io::{Read, Write}; - use util::version; + use parity_version::version; use clap::{Arg, App, SubCommand, AppSettings, ArgMatches as ClapArgMatches, Error as ClapError, ErrorKind as ClapErrorKind}; use helpers::replace_home; use std::ffi::OsStr; diff --git a/parity/configuration.rs b/parity/configuration.rs index b9a614c8b..1a707f031 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -25,7 +25,8 @@ use cli::{Args, ArgsError}; use hash::keccak; use bigint::prelude::U256; use bigint::hash::H256; -use util::{version_data, Address, version}; +use util::Address; +use parity_version::{version_data, version}; use bytes::Bytes; use ansi_term::Colour; use ethsync::{NetworkConfiguration, validate_node_url, self}; @@ -484,6 +485,7 @@ impl Configuration { fn accounts_config(&self) -> Result { let cfg = AccountsConfig { iterations: self.args.arg_keys_iterations, + refresh_time: self.args.arg_accounts_refresh, testnet: self.args.flag_testnet, password_files: self.args.arg_password.clone(), unlocked_accounts: to_addresses(&self.args.arg_unlock)?, @@ -567,7 +569,7 @@ impl Configuration { } fn dapps_config(&self) -> DappsConfiguration { - let dev_ui = if self.args.flag_ui_no_validation { vec![("localhost".to_owned(), 3000)] } else { vec![] }; + let dev_ui = if self.args.flag_ui_no_validation { vec![("127.0.0.1".to_owned(), 3000)] } else { vec![] }; let ui_port = self.ui_port(); DappsConfiguration { @@ -1587,7 +1589,7 @@ mod tests { port: 8180, hosts: Some(vec![]), }); - assert_eq!(conf1.dapps_config().extra_embed_on, vec![("localhost".to_owned(), 3000)]); + assert_eq!(conf1.dapps_config().extra_embed_on, vec![("127.0.0.1".to_owned(), 3000)]); assert_eq!(conf1.ws_config().unwrap().origins, None); assert_eq!(conf2.directories().signer, "signer".to_owned()); assert_eq!(conf2.ui_config(), UiConfiguration { diff --git a/parity/helpers.rs b/parity/helpers.rs index dc1644597..88ff333d9 100644 --- a/parity/helpers.rs +++ b/parity/helpers.rs @@ -213,11 +213,10 @@ pub fn default_network_config() -> ::ethsync::NetworkConfiguration { ip_filter: IpFilter::default(), reserved_nodes: Vec::new(), allow_non_reserved: true, - client_version: ::util::version(), + client_version: ::parity_version::version(), } } -#[cfg_attr(feature = "dev", allow(too_many_arguments))] pub fn to_client_config( cache_config: &CacheConfig, spec_name: String, @@ -453,7 +452,6 @@ but the first password is trimmed } #[test] - #[cfg_attr(feature = "dev", allow(float_cmp))] fn test_to_price() { assert_eq!(to_price("1").unwrap(), 1.0); assert_eq!(to_price("2.3").unwrap(), 2.3); diff --git a/parity/informant.rs b/parity/informant.rs index 56bb3ad4d..cd2205382 100644 --- a/parity/informant.rs +++ b/parity/informant.rs @@ -252,7 +252,6 @@ impl Informant { self.in_shutdown.store(true, ::std::sync::atomic::Ordering::SeqCst); } - #[cfg_attr(feature="dev", allow(match_bool))] pub fn tick(&self) { let elapsed = self.last_tick.read().elapsed(); if elapsed < Duration::from_secs(5) { diff --git a/parity/main.rs b/parity/main.rs index eae471dce..c8ea6ead8 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -17,10 +17,6 @@ //! Ethcore client application. #![warn(missing_docs)] -#![cfg_attr(feature="dev", feature(plugin))] -#![cfg_attr(feature="dev", plugin(clippy))] -#![cfg_attr(feature="dev", allow(useless_format))] -#![cfg_attr(feature="dev", allow(match_bool))] extern crate ansi_term; extern crate app_dirs; @@ -71,6 +67,7 @@ extern crate parity_local_store as local_store; extern crate parity_reactor; extern crate parity_rpc; extern crate parity_updater as updater; +extern crate parity_version; extern crate parity_whisper; extern crate path; extern crate rpc_cli; diff --git a/parity/params.rs b/parity/params.rs index 32353e168..2d5cb8a7d 100644 --- a/parity/params.rs +++ b/parity/params.rs @@ -17,7 +17,8 @@ use std::{str, fs, fmt}; use std::time::Duration; use bigint::prelude::U256; -use util::{Address, version_data}; +use util::Address; +use parity_version::version_data; use journaldb::Algorithm; use ethcore::spec::{Spec, SpecParams}; use ethcore::ethereum; @@ -36,6 +37,7 @@ pub enum SpecType { Classic, Expanse, Musicoin, + Ellaism, Dev, Custom(String), } @@ -59,6 +61,7 @@ impl str::FromStr for SpecType { "olympic" => SpecType::Olympic, "expanse" => SpecType::Expanse, "musicoin" => SpecType::Musicoin, + "ellaism" => SpecType::Ellaism, "dev" => SpecType::Dev, other => SpecType::Custom(other.into()), }; @@ -76,6 +79,7 @@ impl fmt::Display for SpecType { SpecType::Classic => "classic", SpecType::Expanse => "expanse", SpecType::Musicoin => "musicoin", + SpecType::Ellaism => "ellaism", SpecType::Kovan => "kovan", SpecType::Dev => "dev", SpecType::Custom(ref custom) => custom, @@ -94,6 +98,7 @@ impl SpecType { SpecType::Classic => Ok(ethereum::new_classic(params)), SpecType::Expanse => Ok(ethereum::new_expanse(params)), SpecType::Musicoin => Ok(ethereum::new_musicoin(params)), + SpecType::Ellaism => Ok(ethereum::new_ellaism(params)), SpecType::Kovan => Ok(ethereum::new_kovan(params)), SpecType::Dev => Ok(Spec::new_instant()), SpecType::Custom(ref filename) => { @@ -184,6 +189,7 @@ impl str::FromStr for ResealPolicy { #[derive(Debug, PartialEq)] pub struct AccountsConfig { pub iterations: u32, + pub refresh_time: u64, pub testnet: bool, pub password_files: Vec, pub unlocked_accounts: Vec
, @@ -195,6 +201,7 @@ impl Default for AccountsConfig { fn default() -> Self { AccountsConfig { iterations: 10240, + refresh_time: 5, testnet: false, password_files: Vec::new(), unlocked_accounts: Vec::new(), diff --git a/parity/run.rs b/parity/run.rs index fcad975c8..eb9521020 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -39,7 +39,7 @@ use parity_reactor::EventLoop; use parity_rpc::{NetworkSettings, informant, is_major_importing}; use updater::{UpdatePolicy, Updater}; use ansi_term::Colour; -use util::version; +use parity_version::version; use parking_lot::{Condvar, Mutex}; use node_filter::NodeFilter; use journaldb::Algorithm; @@ -910,9 +910,15 @@ fn prepare_account_provider(spec: &SpecType, dirs: &Directories, data_dir: &str, ], }, }; + + let ethstore = EthStore::open_with_iterations(dir, cfg.iterations).map_err(|e| format!("Could not open keys directory: {}", e))?; + if cfg.refresh_time > 0 { + ethstore.set_refresh_time(::std::time::Duration::from_secs(cfg.refresh_time)); + } let account_provider = AccountProvider::new( - Box::new(EthStore::open_with_iterations(dir, cfg.iterations).map_err(|e| format!("Could not open keys directory: {}", e))?), - account_settings); + Box::new(ethstore), + account_settings, + ); for a in cfg.unlocked_accounts { // Check if the account exists diff --git a/parity/secretstore.rs b/parity/secretstore.rs index 06772e74a..ee818fa62 100644 --- a/parity/secretstore.rs +++ b/parity/secretstore.rs @@ -135,7 +135,7 @@ mod server { // Attempt to sign in the engine signer. let password = deps.accounts_passwords.iter() .find(|p| deps.account_provider.sign(account.clone(), Some((*p).clone()), Default::default()).is_ok()) - .ok_or(format!("No valid password for the secret store node account {}", account))?; + .ok_or_else(|| format!("No valid password for the secret store node account {}", account))?; Arc::new(ethcore_secretstore::KeyStoreNodeKeyPair::new(deps.account_provider, account, password.clone()) .map_err(|e| format!("{}", e))?) }, diff --git a/parity/upgrade.rs b/parity/upgrade.rs index 99d2abdcb..49f18d571 100644 --- a/parity/upgrade.rs +++ b/parity/upgrade.rs @@ -27,7 +27,6 @@ use dir::{DatabaseDirectories, default_data_path}; use helpers::replace_home; use journaldb::Algorithm; -#[cfg_attr(feature="dev", allow(enum_variant_names))] #[derive(Debug)] pub enum Error { CannotCreateConfigPath, diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 9ed55bc63..17ac43bd4 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -54,19 +54,15 @@ fetch = { path = "../util/fetch" } node-health = { path = "../dapps/node-health" } parity-reactor = { path = "../util/reactor" } parity-updater = { path = "../updater" } +parity-version = { path = "../util/version" } rlp = { path = "../util/rlp" } stats = { path = "../util/stats" } vm = { path = "../ethcore/vm" } keccak-hash = { path = "../util/hash" } hardware-wallet = { path = "../hw" } -clippy = { version = "0.0.103", optional = true} - [dev-dependencies] pretty_assertions = "0.1" macros = { path = "../util/macros" } ethcore-network = { path = "../util/network" } kvdb-memorydb = { path = "../util/kvdb-memorydb" } - -[features] -dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev"] diff --git a/rpc/src/authcodes.rs b/rpc/src/authcodes.rs index f1b2a391d..b0bb02fdf 100644 --- a/rpc/src/authcodes.rs +++ b/rpc/src/authcodes.rs @@ -83,7 +83,6 @@ pub struct AuthCodes { impl AuthCodes { /// Reads `AuthCodes` from file and creates new instance using `DefaultTimeProvider`. - #[cfg_attr(feature="dev", allow(single_char_pattern))] pub fn from_file(file: &Path) -> io::Result { let content = { if let Ok(mut file) = fs::File::open(file) { @@ -154,7 +153,6 @@ impl AuthCodes { /// Checks if given hash is correct authcode of `SignerUI` /// Updates this hash last used field in case it's valid. - #[cfg_attr(feature="dev", allow(wrong_self_convention))] pub fn is_valid(&mut self, hash: &H256, time: u64) -> bool { let now = self.now.now(); // check time diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 2e689feb8..f326cb381 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -17,8 +17,6 @@ //! Parity RPC. #![warn(missing_docs)] -#![cfg_attr(feature="dev", feature(plugin))] -#![cfg_attr(feature="dev", plugin(clippy))] #[macro_use] extern crate futures; @@ -64,6 +62,7 @@ extern crate fetch; extern crate node_health; extern crate parity_reactor; extern crate parity_updater as updater; +extern crate parity_version as version; extern crate rlp; extern crate stats; extern crate keccak_hash as hash; diff --git a/rpc/src/v1/impls/eth_pubsub.rs b/rpc/src/v1/impls/eth_pubsub.rs index cc6e0d260..795ed760c 100644 --- a/rpc/src/v1/impls/eth_pubsub.rs +++ b/rpc/src/v1/impls/eth_pubsub.rs @@ -153,9 +153,9 @@ impl ChainNotificationHandler { self.remote.spawn(logs .map(move |logs| { let logs = logs.into_iter().flat_map(|log| log).collect(); - let logs = limit_logs(logs, limit); - if !logs.is_empty() { - Self::notify(&remote, &subscriber, pubsub::Result::Logs(logs)); + + for log in limit_logs(logs, limit) { + Self::notify(&remote, &subscriber, pubsub::Result::Log(log)) } }) .map_err(|e| warn!("Unable to fetch latest logs: {:?}", e)) diff --git a/rpc/src/v1/impls/light/parity.rs b/rpc/src/v1/impls/light/parity.rs index 6987f8082..1be734df4 100644 --- a/rpc/src/v1/impls/light/parity.rs +++ b/rpc/src/v1/impls/light/parity.rs @@ -18,7 +18,7 @@ use std::sync::Arc; use std::collections::{BTreeMap, HashSet}; -use util::misc::version_data; +use version::version_data; use crypto::{ecies, DEFAULT_MAC}; use ethkey::{Brain, Generator}; diff --git a/rpc/src/v1/impls/parity.rs b/rpc/src/v1/impls/parity.rs index b10659322..4cb02f9cb 100644 --- a/rpc/src/v1/impls/parity.rs +++ b/rpc/src/v1/impls/parity.rs @@ -20,7 +20,7 @@ use std::str::FromStr; use std::collections::{BTreeMap, HashSet}; use util::Address; -use util::misc::version_data; +use version::version_data; use crypto::{DEFAULT_MAC, ecies}; use ethkey::{Brain, Generator}; diff --git a/rpc/src/v1/impls/personal.rs b/rpc/src/v1/impls/personal.rs index 03e4ef573..849cb1413 100644 --- a/rpc/src/v1/impls/personal.rs +++ b/rpc/src/v1/impls/personal.rs @@ -30,7 +30,7 @@ use v1::helpers::errors; use v1::helpers::dispatch::{Dispatcher, SignWith}; use v1::helpers::accounts::unwrap_provider; use v1::traits::Personal; -use v1::types::{H160 as RpcH160, H256 as RpcH256, U128 as RpcU128, TransactionRequest}; +use v1::types::{H160 as RpcH160, H256 as RpcH256, U128 as RpcU128, TransactionRequest, RichRawTransaction as RpcRichRawTransaction}; use v1::metadata::Metadata; /// Account management (personal) rpc implementation. @@ -55,6 +55,35 @@ impl PersonalClient { } } +impl PersonalClient { + fn do_sign_transaction(&self, meta: Metadata, request: TransactionRequest, password: String) -> BoxFuture<(PendingTransaction, D)> { + let dispatcher = self.dispatcher.clone(); + let accounts = try_bf!(self.account_provider()); + + let default = match request.from.as_ref() { + Some(account) => Ok(account.clone().into()), + None => accounts + .dapp_default_address(meta.dapp_id().into()) + .map_err(|e| errors::account("Cannot find default account.", e)), + }; + + let default = match default { + Ok(default) => default, + Err(e) => return Box::new(future::err(e)), + }; + + Box::new(dispatcher.fill_optional_fields(request.into(), default, false) + .and_then(move |filled| { + let condition = filled.condition.clone().map(Into::into); + dispatcher.sign(accounts, filled, SignWith::Password(password)) + .map(|tx| tx.into_value()) + .map(move |tx| PendingTransaction::new(tx, condition)) + .map(move |tx| (tx, dispatcher)) + }) + ) + } +} + impl Personal for PersonalClient { type Metadata = Metadata; @@ -104,37 +133,21 @@ impl Personal for PersonalClient { } } + fn sign_transaction(&self, meta: Metadata, request: TransactionRequest, password: String) -> BoxFuture { + Box::new(self.do_sign_transaction(meta, request, password) + .map(|(pending_tx, dispatcher)| dispatcher.enrich(pending_tx.transaction))) + } + fn send_transaction(&self, meta: Metadata, request: TransactionRequest, password: String) -> BoxFuture { - let dispatcher = self.dispatcher.clone(); - let accounts = try_bf!(self.account_provider()); - - let default = match request.from.as_ref() { - Some(account) => Ok(account.clone().into()), - None => accounts - .dapp_default_address(meta.dapp_id().into()) - .map_err(|e| errors::account("Cannot find default account.", e)), - }; - - let default = match default { - Ok(default) => default, - Err(e) => return Box::new(future::err(e)), - }; - - Box::new(dispatcher.fill_optional_fields(request.into(), default, false) - .and_then(move |filled| { - let condition = filled.condition.clone().map(Into::into); - dispatcher.sign(accounts, filled, SignWith::Password(password)) - .map(|tx| tx.into_value()) - .map(move |tx| PendingTransaction::new(tx, condition)) - .map(move |tx| (tx, dispatcher)) - }) + Box::new(self.do_sign_transaction(meta, request, password) .and_then(|(pending_tx, dispatcher)| { let chain_id = pending_tx.chain_id(); trace!(target: "miner", "send_transaction: dispatching tx: {} for chain ID {:?}", ::rlp::encode(&*pending_tx).into_vec().pretty(), chain_id); dispatcher.dispatch_transaction(pending_tx).map(Into::into) - })) + }) + ) } fn sign_and_send_transaction(&self, meta: Metadata, request: TransactionRequest, password: String) -> BoxFuture { diff --git a/rpc/src/v1/impls/web3.rs b/rpc/src/v1/impls/web3.rs index 44055507f..6915af1d1 100644 --- a/rpc/src/v1/impls/web3.rs +++ b/rpc/src/v1/impls/web3.rs @@ -17,7 +17,7 @@ //! Web3 rpc implementation. use hash::keccak; use jsonrpc_core::Result; -use util::version; +use version::version; use v1::traits::Web3; use v1::types::{H256, Bytes}; diff --git a/rpc/src/v1/tests/mocked/eth_pubsub.rs b/rpc/src/v1/tests/mocked/eth_pubsub.rs index 199830025..56843ca62 100644 --- a/rpc/src/v1/tests/mocked/eth_pubsub.rs +++ b/rpc/src/v1/tests/mocked/eth_pubsub.rs @@ -127,17 +127,17 @@ fn should_subscribe_to_logs() { // Check notifications (enacted) handler.new_blocks(vec![], vec![], vec![h1], vec![], vec![], vec![], 0); let (res, receiver) = receiver.into_future().wait().unwrap(); - let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":[{"address":"0x0000000000000000000000000000000000000005","blockHash":"0x3457d2fa2e3dd33c78ac681cf542e429becf718859053448748383af67e23218","blockNumber":"0x1","data":"0x","logIndex":"0x0","topics":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000"],"transactionHash":""#.to_owned() + let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"address":"0x0000000000000000000000000000000000000005","blockHash":"0x3457d2fa2e3dd33c78ac681cf542e429becf718859053448748383af67e23218","blockNumber":"0x1","data":"0x","logIndex":"0x0","topics":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000"],"transactionHash":""#.to_owned() + &format!("0x{:?}", tx_hash) - + r#"","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"}],"subscription":"0x416d77337e24399d"}}"#; + + r#"","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"},"subscription":"0x416d77337e24399d"}}"#; assert_eq!(res, Some(response.into())); // Check notifications (retracted) handler.new_blocks(vec![], vec![], vec![], vec![h1], vec![], vec![], 0); let (res, receiver) = receiver.into_future().wait().unwrap(); - let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":[{"address":"0x0000000000000000000000000000000000000005","blockHash":"0x3457d2fa2e3dd33c78ac681cf542e429becf718859053448748383af67e23218","blockNumber":"0x1","data":"0x","logIndex":"0x0","topics":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000"],"transactionHash":""#.to_owned() + let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"address":"0x0000000000000000000000000000000000000005","blockHash":"0x3457d2fa2e3dd33c78ac681cf542e429becf718859053448748383af67e23218","blockNumber":"0x1","data":"0x","logIndex":"0x0","topics":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000"],"transactionHash":""#.to_owned() + &format!("0x{:?}", tx_hash) - + r#"","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"removed"}],"subscription":"0x416d77337e24399d"}}"#; + + r#"","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"removed"},"subscription":"0x416d77337e24399d"}}"#; assert_eq!(res, Some(response.into())); diff --git a/rpc/src/v1/tests/mocked/parity.rs b/rpc/src/v1/tests/mocked/parity.rs index 9153a17b3..e25f3e4d3 100644 --- a/rpc/src/v1/tests/mocked/parity.rs +++ b/rpc/src/v1/tests/mocked/parity.rs @@ -234,14 +234,14 @@ fn rpc_parity_chain_id() { #[test] fn rpc_parity_default_extra_data() { - use util::misc; + use version::version_data; use bytes::ToPretty; let deps = Dependencies::new(); let io = deps.default_client(); let request = r#"{"jsonrpc": "2.0", "method": "parity_defaultExtraData", "params": [], "id": 1}"#; - let response = format!(r#"{{"jsonrpc":"2.0","result":"0x{}","id":1}}"#, misc::version_data().to_hex()); + let response = format!(r#"{{"jsonrpc":"2.0","result":"0x{}","id":1}}"#, version_data().to_hex()); assert_eq!(io.handle_request_sync(request), Some(response)); } diff --git a/rpc/src/v1/tests/mocked/personal.rs b/rpc/src/v1/tests/mocked/personal.rs index 49e1e9db4..ce74ae686 100644 --- a/rpc/src/v1/tests/mocked/personal.rs +++ b/rpc/src/v1/tests/mocked/personal.rs @@ -96,28 +96,39 @@ fn new_account() { assert_eq!(res, Some(response)); } -#[test] -fn sign_and_send_transaction_with_invalid_password() { +fn invalid_password_test(method: &str) +{ let tester = setup(); let address = tester.accounts.new_account("password123").unwrap(); + let request = r#"{ "jsonrpc": "2.0", - "method": "personal_sendTransaction", + "method": ""#.to_owned() + method + r#"", "params": [{ - "from": ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", + "from": ""# + format!("0x{:?}", address).as_ref() + r#"", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "gas": "0x76c0", "gasPrice": "0x9184e72a000", "value": "0x9184e72a" }, "password321"], "id": 1 - }"#; + }"#; let response = r#"{"jsonrpc":"2.0","error":{"code":-32021,"message":"Account password is invalid or account does not exist.","data":"SStore(InvalidPassword)"},"id":1}"#; assert_eq!(tester.io.handle_request_sync(request.as_ref()), Some(response.into())); } +#[test] +fn sign_transaction_with_invalid_password() { + invalid_password_test("personal_signTransaction"); +} + +#[test] +fn sign_and_send_transaction_with_invalid_password() { + invalid_password_test("personal_sendTransaction"); +} + #[test] fn send_transaction() { sign_and_send_test("personal_sendTransaction"); diff --git a/rpc/src/v1/tests/mocked/web3.rs b/rpc/src/v1/tests/mocked/web3.rs index 46a83bb3d..aceb36e56 100644 --- a/rpc/src/v1/tests/mocked/web3.rs +++ b/rpc/src/v1/tests/mocked/web3.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . use jsonrpc_core::IoHandler; -use util::version; +use version::version; use v1::{Web3, Web3Client}; #[test] diff --git a/rpc/src/v1/traits/personal.rs b/rpc/src/v1/traits/personal.rs index 3d6d29b28..9eda7528b 100644 --- a/rpc/src/v1/traits/personal.rs +++ b/rpc/src/v1/traits/personal.rs @@ -17,7 +17,7 @@ //! Personal rpc interface. use jsonrpc_core::{BoxFuture, Result}; -use v1::types::{U128, H160, H256, TransactionRequest}; +use v1::types::{U128, H160, H256, TransactionRequest, RichRawTransaction as RpcRichRawTransaction}; build_rpc_trait! { /// Personal rpc interface. Safe (read-only) functions. @@ -37,6 +37,10 @@ build_rpc_trait! { #[rpc(name = "personal_unlockAccount")] fn unlock_account(&self, H160, String, Option) -> Result; + /// Signs transaction. The account is not unlocked in such case. + #[rpc(meta, name = "personal_signTransaction")] + fn sign_transaction(&self, Self::Metadata, TransactionRequest, String) -> BoxFuture; + /// Sends transaction and signs it in single call. The account is not unlocked in such case. #[rpc(meta, name = "personal_sendTransaction")] fn send_transaction(&self, Self::Metadata, TransactionRequest, String) -> BoxFuture; diff --git a/rpc/src/v1/types/pubsub.rs b/rpc/src/v1/types/pubsub.rs index e7deeba4f..608a1cbcc 100644 --- a/rpc/src/v1/types/pubsub.rs +++ b/rpc/src/v1/types/pubsub.rs @@ -26,8 +26,8 @@ use v1::types::{RichHeader, Filter, Log}; pub enum Result { /// New block header. Header(RichHeader), - /// Logs - Logs(Vec), + /// Log + Log(Log), } impl Serialize for Result { @@ -36,7 +36,7 @@ impl Serialize for Result { { match *self { Result::Header(ref header) => header.serialize(serializer), - Result::Logs(ref logs) => logs.serialize(serializer), + Result::Log(ref log) => log.serialize(serializer), } } } diff --git a/secret_store/src/key_server_cluster/admin_sessions/share_add_session.rs b/secret_store/src/key_server_cluster/admin_sessions/share_add_session.rs index 39fd70cd4..194138eda 100644 --- a/secret_store/src/key_server_cluster/admin_sessions/share_add_session.rs +++ b/secret_store/src/key_server_cluster/admin_sessions/share_add_session.rs @@ -257,7 +257,7 @@ impl SessionImpl where T: SessionTransport { let admin_public = self.core.admin_public.as_ref().cloned().ok_or(Error::ConsensusUnreachable)?; // key share version is required on ShareAdd master node - let key_share = self.core.key_share.as_ref().ok_or(Error::KeyStorage("key share is not found on master node".into()))?; + let key_share = self.core.key_share.as_ref().ok_or_else(|| Error::KeyStorage("key share is not found on master node".into()))?; let key_version = key_share.version(&version).map_err(|e| Error::KeyStorage(e.into()))?; // old nodes set is all non-isolated owners of version holders diff --git a/secret_store/src/key_server_cluster/admin_sessions/share_move_session.rs b/secret_store/src/key_server_cluster/admin_sessions/share_move_session.rs index d193e9efd..da75d095d 100644 --- a/secret_store/src/key_server_cluster/admin_sessions/share_move_session.rs +++ b/secret_store/src/key_server_cluster/admin_sessions/share_move_session.rs @@ -188,7 +188,7 @@ impl SessionImpl where T: SessionTransport { let is_consensus_pre_established = data.shares_to_move.is_some(); if !is_consensus_pre_established { let shares_to_move_reversed = shares_to_move_reversed.ok_or(Error::InvalidMessage)?; - let key_share = self.core.key_share.as_ref().ok_or(Error::KeyStorage("key share is not found on master node".into()))?; + let key_share = self.core.key_share.as_ref().ok_or_else(|| Error::KeyStorage("key share is not found on master node".into()))?; check_shares_to_move(&self.core.meta.self_node_id, &shares_to_move_reversed, Some(&key_share.id_numbers))?; let old_set_signature = old_set_signature.ok_or(Error::InvalidMessage)?; @@ -424,7 +424,7 @@ impl SessionImpl where T: SessionTransport { if !move_confirmations_to_receive.remove(sender) { return Err(Error::InvalidMessage); } - + if !move_confirmations_to_receive.is_empty() { return Ok(()); } @@ -818,7 +818,7 @@ mod tests { // check that session has completed on all nodes assert!(ml.nodes.values().all(|n| n.session.is_finished())); - + // check that secret is still the same as before adding the share check_secret_is_preserved(ml.original_key_pair.clone(), ml.nodes.iter() .filter(|&(k, _)| !shares_to_move.values().any(|v| v == k)) diff --git a/secret_store/src/key_storage.rs b/secret_store/src/key_storage.rs index 020dde700..798a2c553 100644 --- a/secret_store/src/key_storage.rs +++ b/secret_store/src/key_storage.rs @@ -154,7 +154,7 @@ impl PersistentKeyStorage { pub fn new(config: &ServiceConfiguration) -> Result { let mut db_path = PathBuf::from(&config.data_path); db_path.push("db"); - let db_path = db_path.to_str().ok_or(Error::Database("Invalid secretstore path".to_owned()))?; + let db_path = db_path.to_str().ok_or_else(|| Error::Database("Invalid secretstore path".to_owned()))?; let db = Database::open_default(&db_path)?; let db = upgrade_db(db)?; diff --git a/sync/Cargo.toml b/sync/Cargo.toml index c2095c032..fea5fbf1e 100644 --- a/sync/Cargo.toml +++ b/sync/Cargo.toml @@ -20,7 +20,6 @@ keccak-hash = { path = "../util/hash" } triehash = { path = "../util/triehash" } kvdb = { path = "../util/kvdb" } macros = { path = "../util/macros" } -clippy = { version = "0.0.103", optional = true} log = "0.3" env_logger = "0.4" time = "0.1.34" @@ -34,7 +33,3 @@ ipnetwork = "0.12.6" [dev-dependencies] ethkey = { path = "../ethkey" } kvdb-memorydb = { path = "../util/kvdb-memorydb" } - -[features] -default = [] -dev = ["clippy", "ethcore/dev", "ethcore-util/dev"] diff --git a/sync/src/chain.rs b/sync/src/chain.rs index 837fa4223..8cf21ef7b 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -469,7 +469,6 @@ impl ChainSync { self.peers.clear(); } - #[cfg_attr(feature="dev", allow(for_kv_map))] // Because it's not possible to get `values_mut()` /// Reset sync. Clear all downloaded data but keep the queue fn reset(&mut self, io: &mut SyncIo) { self.new_blocks.reset(); @@ -672,7 +671,6 @@ impl ChainSync { Ok(()) } - #[cfg_attr(feature="dev", allow(cyclomatic_complexity, needless_borrow))] /// Called by peer once it has new block headers during sync fn on_peer_block_headers(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> { let confirmed = match self.peers.get_mut(&peer_id) { @@ -883,7 +881,6 @@ impl ChainSync { } /// Called by peer once it has new block bodies - #[cfg_attr(feature="dev", allow(cyclomatic_complexity))] fn on_peer_new_block(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> { if !self.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { trace!(target: "sync", "Ignoring new block from unconfirmed peer {}", peer_id); @@ -1333,7 +1330,6 @@ impl ChainSync { } /// Checks if there are blocks fully downloaded that can be imported into the blockchain and does the import. - #[cfg_attr(feature="dev", allow(block_in_if_condition_stmt))] fn collect_blocks(&mut self, io: &mut SyncIo, block_set: BlockSet) { match block_set { BlockSet::NewBlocks => { @@ -1353,7 +1349,6 @@ impl ChainSync { } /// Request headers from a peer by block hash - #[cfg_attr(feature="dev", allow(too_many_arguments))] fn request_headers_by_hash(&mut self, sync: &mut SyncIo, peer_id: PeerId, h: &H256, count: u64, skip: u64, reverse: bool, set: BlockSet) { trace!(target: "sync", "{} <- GetBlockHeaders: {} entries starting from {}, set = {:?}", peer_id, count, h, set); let mut rlp = RlpStream::new_list(4); @@ -1368,7 +1363,6 @@ impl ChainSync { } /// Request headers from a peer by block number - #[cfg_attr(feature="dev", allow(too_many_arguments))] fn request_fork_header_by_number(&mut self, sync: &mut SyncIo, peer_id: PeerId, n: BlockNumber) { trace!(target: "sync", "{} <- GetForkHeader: at {}", peer_id, n); let mut rlp = RlpStream::new_list(4); @@ -1783,7 +1777,6 @@ impl ChainSync { }) } - #[cfg_attr(feature="dev", allow(match_same_arms))] pub fn maintain_peers(&mut self, io: &mut SyncIo) { let tick = time::precise_time_ns(); let mut aborting = Vec::new(); diff --git a/sync/src/lib.rs b/sync/src/lib.rs index a88ab1032..2d020e43d 100644 --- a/sync/src/lib.rs +++ b/sync/src/lib.rs @@ -15,12 +15,6 @@ // along with Parity. If not, see . #![warn(missing_docs)] -#![cfg_attr(feature="dev", feature(plugin))] -#![cfg_attr(feature="dev", plugin(clippy))] -// Keeps consistency (all lines with `.clone()`) and helpful when changing ref to non-ref. -#![cfg_attr(feature="dev", allow(clone_on_copy))] -// In most cases it expresses function flow better -#![cfg_attr(feature="dev", allow(if_not_else))] //! Blockchain sync module //! Implements ethereum protocol version 63 as specified here: diff --git a/transaction-pool/Cargo.toml b/transaction-pool/Cargo.toml new file mode 100644 index 000000000..3d53d3273 --- /dev/null +++ b/transaction-pool/Cargo.toml @@ -0,0 +1,12 @@ +[package] +description = "Generic transaction pool." +name = "transaction-pool" +version = "1.9.0" +license = "GPL-3.0" +authors = ["Parity Technologies "] + +[dependencies] +error-chain = "0.11" +log="0.3" +smallvec = "0.4" +ethcore-bigint = { features = ["heapsizeof"], version="0.2" } diff --git a/transaction-pool/src/error.rs b/transaction-pool/src/error.rs new file mode 100644 index 000000000..2adb75963 --- /dev/null +++ b/transaction-pool/src/error.rs @@ -0,0 +1,48 @@ +// 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 . + +use bigint::hash::H256; + +error_chain! { + errors { + AlreadyImported(hash: H256) { + description("transaction is already in the queue"), + display("[{:?}] transaction already imported", hash) + } + TooCheapToEnter(hash: H256) { + description("the pool is full and transaction is too cheap to replace any transaction"), + display("[{:?}] transaction too cheap to enter the pool", hash) + } + TooCheapToReplace(old_hash: H256, hash: H256) { + description("transaction is too cheap to replace existing transaction in the queue"), + display("[{:?}] transaction too cheap to replace: {:?}", hash, old_hash) + } + } +} + +#[cfg(test)] +impl PartialEq for ErrorKind { + fn eq(&self, other: &Self) -> bool { + use self::ErrorKind::*; + + match (self, other) { + (&AlreadyImported(ref h1), &AlreadyImported(ref h2)) => h1 == h2, + (&TooCheapToEnter(ref h1), &TooCheapToEnter(ref h2)) => h1 == h2, + (&TooCheapToReplace(ref old1, ref new1), &TooCheapToReplace(ref old2, ref new2)) => old1 == old2 && new1 == new2, + _ => false, + } + } +} diff --git a/transaction-pool/src/lib.rs b/transaction-pool/src/lib.rs new file mode 100644 index 000000000..047178dab --- /dev/null +++ b/transaction-pool/src/lib.rs @@ -0,0 +1,118 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Generic Transaction Pool +//! +//! An extensible and performant implementation of Ethereum Transaction Pool. +//! The pool stores ordered, verified transactions according to some pluggable +//! `Scoring` implementation. +//! The pool also allows you to construct a set of `pending` transactions according +//! to some notion of `Readiness` (pluggable). +//! +//! The pool is generic over transactions and should make no assumptions about them. +//! The only thing we can rely on is the `Scoring` that defines: +//! - the ordering of transactions from a single sender +//! - the priority of the transaction compared to other transactions from different senders +//! +//! NOTE: the transactions from a single sender are not ordered by priority, +//! but still when constructing pending set we always need to maintain the ordering +//! (i.e. `txs[1]` always needs to be included after `txs[0]` even if it has higher priority) +//! +//! ### Design Details +//! +//! Performance assumptions: +//! - Possibility to handle tens of thousands of transactions +//! - Fast insertions and replacements `O(per-sender + log(senders))` +//! - Reasonably fast removal of stalled transactions `O(per-sender)` +//! - Reasonably fast construction of pending set `O(txs * (log(senders) + log(per-sender))` +//! +//! The removal performance could be improved by trading some memory. Currently `SmallVec` is used +//! to store senders transactions, instead we could use `VecDeque` and efficiently `pop_front` +//! the best transactions. +//! +//! The pending set construction and insertion complexity could be reduced by introducing +//! a notion of `nonce` - an absolute, numeric ordering of transactions. +//! We don't do that because of possible implications of EIP208 where nonce might not be +//! explicitly available. +//! +//! 1. The pool groups transactions from particular sender together +//! and stores them ordered by `Scoring` within that group +//! i.e. `HashMap>`. +//! 2. Additionaly we maintain the best and the worst transaction from each sender +//! (by `Scoring` not `priority`) ordered by `priority`. +//! It means that we can easily identify the best transaction inside the entire pool +//! and the worst transaction. +//! 3. Whenever new transaction is inserted to the queue: +//! - first check all the limits (overall, memory, per-sender) +//! - retrieve all transactions from a sender +//! - binary search for position to insert the transaction +//! - decide if we are replacing existing transaction (3 outcomes: drop, replace, insert) +//! - update best and worst transaction from that sender if affected +//! 4. Pending List construction: +//! - Take the best transaction (by priority) from all senders to the List +//! - Replace the transaction with next transaction (by ordering) from that sender (if any) +//! - Repeat + +#![warn(missing_docs)] + +extern crate smallvec; +extern crate ethcore_bigint as bigint; + +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate log; + +#[cfg(test)] +mod tests; + +mod error; +mod listener; +mod options; +mod pool; +mod ready; +mod status; +mod transactions; +mod verifier; + +pub mod scoring; + +pub use self::listener::{Listener, NoopListener}; +pub use self::options::Options; +pub use self::pool::Pool; +pub use self::ready::{Ready, Readiness}; +pub use self::scoring::Scoring; +pub use self::status::{LightStatus, Status}; +pub use self::verifier::Verifier; + +use std::fmt; + +use self::bigint::prelude::{H256, H160 as Address}; + +/// Already verified transaction that can be safely queued. +pub trait VerifiedTransaction: fmt::Debug { + /// Transaction hash + fn hash(&self) -> &H256; + + /// Memory usage + fn mem_usage(&self) -> usize; + + /// Transaction sender + fn sender(&self) -> &Address; + + /// Unique index of insertion (lower = older). + fn insertion_id(&self) -> u64; +} diff --git a/transaction-pool/src/listener.rs b/transaction-pool/src/listener.rs new file mode 100644 index 000000000..36a5d9de2 --- /dev/null +++ b/transaction-pool/src/listener.rs @@ -0,0 +1,48 @@ +// 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 . + +use std::sync::Arc; + +/// Transaction pool listener. +/// +/// Listener is being notified about status of every transaction in the pool. +pub trait Listener { + /// The transaction has been successfuly added to the pool. + /// If second argument is `Some` the transaction has took place of some other transaction + /// which was already in pool. + /// NOTE: You won't be notified about drop of `old` transaction separately. + fn added(&mut self, _tx: &Arc, _old: Option<&Arc>) {} + + /// The transaction was rejected from the pool. + /// It means that it was too cheap to replace any transaction already in the pool. + fn rejected(&mut self, _tx: T) {} + + /// The transaction was dropped from the pool because of a limit. + fn dropped(&mut self, _tx: &Arc) {} + + /// The transaction was marked as invalid by executor. + fn invalid(&mut self, _tx: &Arc) {} + + /// The transaction has been cancelled. + fn cancelled(&mut self, _tx: &Arc) {} + + /// The transaction has been mined. + fn mined(&mut self, _tx: &Arc) {} +} + +/// A no-op implementation of `Listener`. +pub struct NoopListener; +impl Listener for NoopListener {} diff --git a/transaction-pool/src/options.rs b/transaction-pool/src/options.rs new file mode 100644 index 000000000..ddec91286 --- /dev/null +++ b/transaction-pool/src/options.rs @@ -0,0 +1,36 @@ +// 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 . + +/// Transaction Pool options. +#[derive(Debug)] +pub struct Options { + /// Maximal number of transactions in the pool. + pub max_count: usize, + /// Maximal number of transactions from single sender. + pub max_per_sender: usize, + /// Maximal memory usage. + pub max_mem_usage: usize, +} + +impl Default for Options { + fn default() -> Self { + Options { + max_count: 1024, + max_per_sender: 16, + max_mem_usage: 8 * 1024 * 1024, + } + } +} diff --git a/transaction-pool/src/pool.rs b/transaction-pool/src/pool.rs new file mode 100644 index 000000000..1c58da3fe --- /dev/null +++ b/transaction-pool/src/pool.rs @@ -0,0 +1,431 @@ +// 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 . + +use std::sync::Arc; +use std::collections::{HashMap, BTreeSet}; + +use bigint::hash::{H160, H256}; + +use error; +use listener::{Listener, NoopListener}; +use options::Options; +use ready::{Ready, Readiness}; +use scoring::{Scoring, ScoreWithRef}; +use status::{LightStatus, Status}; +use transactions::{AddResult, Transactions}; + +use {VerifiedTransaction}; + +type Sender = H160; + +/// A transaction pool. +#[derive(Debug)] +pub struct Pool, L = NoopListener> { + listener: L, + scoring: S, + options: Options, + mem_usage: usize, + + transactions: HashMap>, + by_hash: HashMap>, + + best_transactions: BTreeSet>, + worst_transactions: BTreeSet>, +} + +impl + Default> Default for Pool { + fn default() -> Self { + Self::with_scoring(S::default(), Options::default()) + } +} + +impl + Default> Pool { + /// Creates a new `Pool` with given options + /// and default `Scoring` and `Listener`. + pub fn with_options(options: Options) -> Self { + Self::with_scoring(S::default(), options) + } +} + +impl> Pool { + /// Creates a new `Pool` with given `Scoring` and options. + pub fn with_scoring(scoring: S, options: Options) -> Self { + Self::new(NoopListener, scoring, options) + } +} + + +const INITIAL_NUMBER_OF_SENDERS: usize = 16; + +impl Pool where + T: VerifiedTransaction, + S: Scoring, + L: Listener, +{ + /// Creates new `Pool` with given `Scoring`, `Listener` and options. + pub fn new(listener: L, scoring: S, options: Options) -> Self { + let transactions = HashMap::with_capacity(INITIAL_NUMBER_OF_SENDERS); + let by_hash = HashMap::with_capacity(options.max_count / 16); + + Pool { + listener, + scoring, + options, + mem_usage: 0, + transactions, + by_hash, + best_transactions: Default::default(), + worst_transactions: Default::default(), + } + + } + + /// Attempts to import new transaction to the pool, returns a `Arc` or an `Error`. + /// + /// NOTE: Since `Ready`ness is separate from the pool it's possible to import stalled transactions. + /// It's the caller responsibility to make sure that's not the case. + /// + /// NOTE: The transaction may push out some other transactions from the pool + /// either because of limits (see `Options`) or because `Scoring` decides that the transaction + /// replaces an existing transaction from that sender. + /// If any limit is reached the transaction with the lowest `Score` is evicted to make room. + /// + /// The `Listener` will be informed on any drops or rejections. + pub fn import(&mut self, mut transaction: T) -> error::Result> { + let mem_usage = transaction.mem_usage(); + + ensure!(!self.by_hash.contains_key(transaction.hash()), error::ErrorKind::AlreadyImported(*transaction.hash())); + + { + let remove_worst = |s: &mut Self, transaction| { + match s.remove_worst(&transaction) { + Err(err) => { + s.listener.rejected(transaction); + Err(err) + }, + Ok(removed) => { + s.listener.dropped(&removed); + s.finalize_remove(removed.hash()); + Ok(transaction) + }, + } + }; + + while self.by_hash.len() + 1 > self.options.max_count { + transaction = remove_worst(self, transaction)?; + } + + while self.mem_usage + mem_usage > self.options.max_mem_usage { + transaction = remove_worst(self, transaction)?; + } + } + + let (result, prev_state, current_state) = { + let transactions = self.transactions.entry(*transaction.sender()).or_insert_with(Transactions::default); + // get worst and best transactions for comparison + let prev = transactions.worst_and_best(); + let result = transactions.add(transaction, &self.scoring, self.options.max_per_sender); + let current = transactions.worst_and_best(); + (result, prev, current) + }; + + // update best and worst transactions from this sender (if required) + self.update_senders_worst_and_best(prev_state, current_state); + + match result { + AddResult::Ok(tx) => { + self.listener.added(&tx, None); + self.finalize_insert(&tx, None); + Ok(tx) + }, + AddResult::PushedOut { new, old } | + AddResult::Replaced { new, old } => { + self.listener.added(&new, Some(&old)); + self.finalize_insert(&new, Some(&old)); + Ok(new) + }, + AddResult::TooCheap { new, old } => { + let hash = *new.hash(); + self.listener.rejected(new); + bail!(error::ErrorKind::TooCheapToReplace(*old.hash(), hash)) + }, + AddResult::TooCheapToEnter(new) => { + let hash = *new.hash(); + self.listener.rejected(new); + bail!(error::ErrorKind::TooCheapToEnter(hash)) + } + } + } + + /// Updates state of the pool statistics if the transaction was added to a set. + fn finalize_insert(&mut self, new: &Arc, old: Option<&Arc>) { + self.mem_usage += new.mem_usage(); + self.by_hash.insert(*new.hash(), new.clone()); + + if let Some(old) = old { + self.finalize_remove(old.hash()); + } + } + + /// Updates the pool statistics if transaction was removed. + fn finalize_remove(&mut self, hash: &H256) -> Option> { + self.by_hash.remove(hash).map(|old| { + self.mem_usage -= old.mem_usage(); + old + }) + } + + /// Updates best and worst transactions from a sender. + fn update_senders_worst_and_best( + &mut self, + previous: Option<((S::Score, Arc), (S::Score, Arc))>, + current: Option<((S::Score, Arc), (S::Score, Arc))>, + ) { + let worst_collection = &mut self.worst_transactions; + let best_collection = &mut self.best_transactions; + + let is_same = |a: &(S::Score, Arc), b: &(S::Score, Arc)| { + a.0 == b.0 && a.1.hash() == b.1.hash() + }; + + let update = |collection: &mut BTreeSet<_>, (score, tx), remove| if remove { + collection.remove(&ScoreWithRef::new(score, tx)); + } else { + collection.insert(ScoreWithRef::new(score, tx)); + }; + + match (previous, current) { + (None, Some((worst, best))) => { + update(worst_collection, worst, false); + update(best_collection, best, false); + }, + (Some((worst, best)), None) => { + // all transactions from that sender has been removed. + // We can clear a hashmap entry. + self.transactions.remove(worst.1.sender()); + update(worst_collection, worst, true); + update(best_collection, best, true); + }, + (Some((w1, b1)), Some((w2, b2))) => { + if !is_same(&w1, &w2) { + update(worst_collection, w1, true); + update(worst_collection, w2, false); + } + if !is_same(&b1, &b2) { + update(best_collection, b1, true); + update(best_collection, b2, false); + } + }, + (None, None) => {}, + } + } + + /// Attempts to remove the worst transaction from the pool if it's worse than the given one. + fn remove_worst(&mut self, transaction: &T) -> error::Result> { + let to_remove = match self.worst_transactions.iter().next_back() { + // No elements to remove? and the pool is still full? + None => { + warn!("The pool is full but there are no transactions to remove."); + return Err(error::ErrorKind::TooCheapToEnter(*transaction.hash()).into()); + }, + Some(old) => if self.scoring.should_replace(&old.transaction, transaction) { + // New transaction is better than the worst one so we can replace it. + old.clone() + } else { + // otherwise fail + return Err(error::ErrorKind::TooCheapToEnter(*transaction.hash()).into()) + }, + }; + + // Remove from transaction set + self.remove_from_set(to_remove.transaction.sender(), |set, scoring| { + set.remove(&to_remove.transaction, scoring) + }); + Ok(to_remove.transaction) + } + + /// Removes transaction from sender's transaction `HashMap`. + fn remove_from_set, &S) -> R>(&mut self, sender: &Sender, f: F) -> Option { + let (prev, next, result) = if let Some(set) = self.transactions.get_mut(sender) { + let prev = set.worst_and_best(); + let result = f(set, &self.scoring); + (prev, set.worst_and_best(), result) + } else { + return None; + }; + + self.update_senders_worst_and_best(prev, next); + Some(result) + } + + /// Clears pool from all transactions. + /// This causes a listener notification that all transactions were dropped. + /// NOTE: the drop-notification order will be arbitrary. + pub fn clear(&mut self) { + self.mem_usage = 0; + self.transactions.clear(); + self.best_transactions.clear(); + self.worst_transactions.clear(); + + for (_hash, tx) in self.by_hash.drain() { + self.listener.dropped(&tx) + } + } + + /// Removes single transaction from the pool. + /// Depending on the `is_invalid` flag the listener + /// will either get a `cancelled` or `invalid` notification. + pub fn remove(&mut self, hash: &H256, is_invalid: bool) -> bool { + if let Some(tx) = self.finalize_remove(hash) { + self.remove_from_set(tx.sender(), |set, scoring| { + set.remove(&tx, scoring) + }); + if is_invalid { + self.listener.invalid(&tx); + } else { + self.listener.cancelled(&tx); + } + true + } else { + false + } + } + + /// Removes all stalled transactions from given sender. + fn remove_stalled>(&mut self, sender: &Sender, ready: &mut R) -> usize { + let removed_from_set = self.remove_from_set(sender, |transactions, scoring| { + transactions.cull(ready, scoring) + }); + + match removed_from_set { + Some(removed) => { + let len = removed.len(); + for tx in removed { + self.finalize_remove(tx.hash()); + self.listener.mined(&tx); + } + len + }, + None => 0, + } + } + + /// Removes all stalled transactions from given sender list (or from all senders). + pub fn cull>(&mut self, senders: Option<&[Sender]>, mut ready: R) -> usize { + let mut removed = 0; + match senders { + Some(senders) => { + for sender in senders { + removed += self.remove_stalled(sender, &mut ready); + } + }, + None => { + let senders = self.transactions.keys().cloned().collect::>(); + for sender in senders { + removed += self.remove_stalled(&sender, &mut ready); + } + }, + } + + removed + } + + /// Returns an iterator of pending (ready) transactions. + pub fn pending>(&self, ready: R) -> PendingIterator { + PendingIterator { + ready, + best_transactions: self.best_transactions.clone(), + pool: self, + } + } + + /// Computes the full status of the pool (including readiness). + pub fn status>(&self, mut ready: R) -> Status { + let mut status = Status::default(); + + for (_sender, transactions) in &self.transactions { + let len = transactions.len(); + for (idx, tx) in transactions.iter().enumerate() { + match ready.is_ready(tx) { + Readiness::Stalled => status.stalled += 1, + Readiness::Ready => status.pending += 1, + Readiness::Future => { + status.future += len - idx; + break; + } + } + } + } + + status + } + + /// Returns light status of the pool. + pub fn light_status(&self) -> LightStatus { + LightStatus { + mem_usage: self.mem_usage, + transaction_count: self.by_hash.len(), + senders: self.transactions.len(), + } + } +} + +/// An iterator over all pending (ready) transactions. +/// NOTE: the transactions are not removed from the queue. +/// You might remove them later by calling `cull`. +pub struct PendingIterator<'a, T, R, S, L> where + T: VerifiedTransaction + 'a, + S: Scoring + 'a, + L: 'a, +{ + ready: R, + best_transactions: BTreeSet>, + pool: &'a Pool, +} + +impl<'a, T, R, S, L> Iterator for PendingIterator<'a, T, R, S, L> where + T: VerifiedTransaction, + R: Ready, + S: Scoring, +{ + type Item = Arc; + + fn next(&mut self) -> Option { + while !self.best_transactions.is_empty() { + let best = { + let best = self.best_transactions.iter().next().expect("current_best is not empty; qed").clone(); + self.best_transactions.take(&best).expect("Just taken from iterator; qed") + }; + + match self.ready.is_ready(&best.transaction) { + Readiness::Ready => { + // retrieve next one from that sender. + let next = self.pool.transactions + .get(best.transaction.sender()) + .and_then(|s| s.find_next(&best.transaction, &self.pool.scoring)); + if let Some((score, tx)) = next { + self.best_transactions.insert(ScoreWithRef::new(score, tx)); + } + + return Some(best.transaction) + }, + state => warn!("[{:?}] Ignoring {:?} transaction.", best.transaction.hash(), state), + } + } + + None + } +} diff --git a/transaction-pool/src/ready.rs b/transaction-pool/src/ready.rs new file mode 100644 index 000000000..1d8e342db --- /dev/null +++ b/transaction-pool/src/ready.rs @@ -0,0 +1,42 @@ +// 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 . + +/// Transaction readiness. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Readiness { + /// The transaction is stalled (and should/will be removed from the pool). + Stalled, + /// The transaction is ready to be included in pending set. + Ready, + /// The transaction is not yet ready. + Future, +} + +/// A readiness indicator. +pub trait Ready { + /// Returns true if transaction is ready to be included in pending block, + /// given all previous transactions that were ready are already included. + /// + /// NOTE: readiness of transactions will be checked according to `Score` ordering, + /// the implementation should maintain a state of already checked transactions. + fn is_ready(&mut self, tx: &T) -> Readiness; +} + +impl Ready for F where F: FnMut(&T) -> Readiness { + fn is_ready(&mut self, tx: &T) -> Readiness { + (*self)(tx) + } +} diff --git a/transaction-pool/src/scoring.rs b/transaction-pool/src/scoring.rs new file mode 100644 index 000000000..e4f923c9b --- /dev/null +++ b/transaction-pool/src/scoring.rs @@ -0,0 +1,145 @@ +// 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 . + +//! A transactions ordering abstraction. + +use std::{cmp, fmt}; +use std::sync::Arc; + +use {VerifiedTransaction}; + +/// Represents a decision what to do with +/// a new transaction that tries to enter the pool. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Choice { + /// New transaction should be rejected + /// (i.e. the old transaction that occupies the same spot + /// is better). + RejectNew, + /// The old transaction should be dropped + /// in favour of the new one. + ReplaceOld, + /// The new transaction should be inserted + /// and both (old and new) should stay in the pool. + InsertNew, +} + +/// Describes a reason why the `Score` of transactions +/// should be updated. +/// The `Scoring` implementations can use this information +/// to update the `Score` table more efficiently. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Change { + /// New transaction has been inserted at given index. + /// The Score at that index is initialized with default value + /// and needs to be filled in. + InsertedAt(usize), + /// The transaction has been removed at given index and other transactions + /// shifted to it's place. + /// The scores were removed and shifted as well. + /// For simple scoring algorithms no action is required here. + RemovedAt(usize), + /// The transaction at given index has replaced a previous transaction. + /// The score at that index needs to be update (it contains value from previous transaction). + ReplacedAt(usize), + /// Given number of stalled transactions has been culled from the beginning. + /// Usually the score will have to be re-computed from scratch. + Culled(usize), +} + +/// A transaction ordering. +/// +/// The implementation should decide on order of transactions in the pool. +/// Each transaction should also get assigned a `Score` which is used to later +/// prioritize transactions in the pending set. +/// +/// Implementation notes: +/// - Returned `Score`s should match ordering of `compare` method. +/// - `compare` will be called only within a context of transactions from the same sender. +/// - `choose` will be called only if `compare` returns `Ordering::Equal` +/// - `should_replace` is used to decide if new transaction should push out an old transaction already in the queue. +/// - `Score`s and `compare` should align with `Ready` implementation. +/// +/// Example: Natural ordering of Ethereum transactions. +/// - `compare`: compares transaction `nonce` () +/// - `choose`: compares transactions `gasPrice` (decides if old transaction should be replaced) +/// - `update_scores`: score defined as `gasPrice` if `n==0` and `max(scores[n-1], gasPrice)` if `n>0` +/// - `should_replace`: compares `gasPrice` (decides if transaction from a different sender is more valuable) +/// +pub trait Scoring { + /// A score of a transaction. + type Score: cmp::Ord + Clone + Default + fmt::Debug; + + /// Decides on ordering of `T`s from a particular sender. + fn compare(&self, old: &T, other: &T) -> cmp::Ordering; + + /// Decides how to deal with two transactions from a sender that seem to occupy the same slot in the queue. + fn choose(&self, old: &T, new: &T) -> Choice; + + /// Updates the transaction scores given a list of transactions and a change to previous scoring. + /// NOTE: you can safely assume that both slices have the same length. + /// (i.e. score at index `i` represents transaction at the same index) + fn update_scores(&self, txs: &[Arc], scores: &mut [Self::Score], change: Change); + + /// Decides if `new` should push out `old` transaction from the pool. + fn should_replace(&self, old: &T, new: &T) -> bool; +} + +/// A score with a reference to the transaction. +#[derive(Debug)] +pub struct ScoreWithRef { + /// Score + pub score: S, + /// Shared transaction + pub transaction: Arc, +} + +impl Clone for ScoreWithRef { + fn clone(&self) -> Self { + ScoreWithRef { + score: self.score.clone(), + transaction: self.transaction.clone(), + } + } +} + +impl ScoreWithRef { + /// Creates a new `ScoreWithRef` + pub fn new(score: S, transaction: Arc) -> Self { + ScoreWithRef { score, transaction } + } +} + +impl Ord for ScoreWithRef { + fn cmp(&self, other: &Self) -> cmp::Ordering { + other.score.cmp(&self.score) + .then(other.transaction.insertion_id().cmp(&self.transaction.insertion_id())) + } +} + +impl PartialOrd for ScoreWithRef { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialEq for ScoreWithRef { + fn eq(&self, other: &Self) -> bool { + self.score == other.score && self.transaction.insertion_id() == other.transaction.insertion_id() + } +} + +impl Eq for ScoreWithRef {} diff --git a/transaction-pool/src/status.rs b/transaction-pool/src/status.rs new file mode 100644 index 000000000..5862f75a1 --- /dev/null +++ b/transaction-pool/src/status.rs @@ -0,0 +1,40 @@ +// 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 . + +/// Light pool status. +/// This status is cheap to compute and can be called frequently. +#[derive(Default, Debug, PartialEq, Eq)] +pub struct LightStatus { + /// Memory usage in bytes. + pub mem_usage: usize, + /// Total number of transactions in the pool. + pub transaction_count: usize, + /// Number of unique senders in the pool. + pub senders: usize, +} + +/// A full queue status. +/// To compute this status it is required to provide `Ready`. +/// NOTE: To compute the status we need to visit each transaction in the pool. +#[derive(Default, Debug, PartialEq, Eq)] +pub struct Status { + /// Number of stalled transactions. + pub stalled: usize, + /// Number of pending (ready) transactions. + pub pending: usize, + /// Number of future (not ready) transactions. + pub future: usize, +} diff --git a/transaction-pool/src/tests/helpers.rs b/transaction-pool/src/tests/helpers.rs new file mode 100644 index 000000000..faa9aba3e --- /dev/null +++ b/transaction-pool/src/tests/helpers.rs @@ -0,0 +1,80 @@ +// 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 . + +use std::cmp; +use std::collections::HashMap; + +use {scoring, Scoring, Ready, Readiness, Address as Sender}; +use super::{Transaction, SharedTransaction, U256}; + +#[derive(Default)] +pub struct DummyScoring; + +impl Scoring for DummyScoring { + type Score = U256; + + fn compare(&self, old: &Transaction, new: &Transaction) -> cmp::Ordering { + old.nonce.cmp(&new.nonce) + } + + fn choose(&self, old: &Transaction, new: &Transaction) -> scoring::Choice { + if old.nonce == new.nonce { + if new.gas_price > old.gas_price { + scoring::Choice::ReplaceOld + } else { + scoring::Choice::RejectNew + } + } else { + scoring::Choice::InsertNew + } + } + + fn update_scores(&self, txs: &[SharedTransaction], scores: &mut [Self::Score], _change: scoring::Change) { + for i in 0..txs.len() { + scores[i] = txs[i].gas_price; + } + } + + fn should_replace(&self, old: &Transaction, new: &Transaction) -> bool { + new.gas_price > old.gas_price + } +} + +#[derive(Default)] +pub struct NonceReady(HashMap, U256); + +impl NonceReady { + pub fn new>(min: T) -> Self { + let mut n = NonceReady::default(); + n.1 = min.into(); + n + } +} + +impl Ready for NonceReady { + fn is_ready(&mut self, tx: &Transaction) -> Readiness { + let min = self.1; + let nonce = self.0.entry(tx.sender).or_insert_with(|| min); + match tx.nonce.cmp(nonce) { + cmp::Ordering::Greater => Readiness::Future, + cmp::Ordering::Equal => { + *nonce = *nonce + 1.into(); + Readiness::Ready + }, + cmp::Ordering::Less => Readiness::Stalled, + } + } +} diff --git a/transaction-pool/src/tests/mod.rs b/transaction-pool/src/tests/mod.rs new file mode 100644 index 000000000..8dfb0e149 --- /dev/null +++ b/transaction-pool/src/tests/mod.rs @@ -0,0 +1,506 @@ +// 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 . + +mod helpers; +mod tx_builder; + +use self::helpers::{DummyScoring, NonceReady}; +use self::tx_builder::TransactionBuilder; + +use std::sync::Arc; + +use bigint::prelude::{H256, U256, H160 as Address}; +use super::*; + +#[derive(Debug, PartialEq)] +pub struct Transaction { + pub hash: H256, + pub nonce: U256, + pub gas_price: U256, + pub gas: U256, + pub sender: Address, + pub insertion_id: u64, + pub mem_usage: usize, +} + +impl VerifiedTransaction for Transaction { + fn hash(&self) -> &H256 { &self.hash } + fn mem_usage(&self) -> usize { self.mem_usage } + fn sender(&self) -> &Address { &self.sender } + fn insertion_id(&self) -> u64 { self.insertion_id } +} + +pub type SharedTransaction = Arc; + +type TestPool = Pool; + +#[test] +fn should_clear_queue() { + // given + let b = TransactionBuilder::default(); + let mut txq = TestPool::default(); + assert_eq!(txq.light_status(), LightStatus { + mem_usage: 0, + transaction_count: 0, + senders: 0, + }); + let tx1 = b.tx().nonce(0).new(); + let tx2 = b.tx().nonce(1).mem_usage(1).new(); + + // add + txq.import(tx1).unwrap(); + txq.import(tx2).unwrap(); + assert_eq!(txq.light_status(), LightStatus { + mem_usage: 1, + transaction_count: 2, + senders: 1, + }); + + // when + txq.clear(); + + // then + assert_eq!(txq.light_status(), LightStatus { + mem_usage: 0, + transaction_count: 0, + senders: 0, + }); +} + +#[test] +fn should_not_allow_same_transaction_twice() { + // given + let b = TransactionBuilder::default(); + let mut txq = TestPool::default(); + let tx1 = b.tx().nonce(0).new(); + let tx2 = b.tx().nonce(0).new(); + + // when + txq.import(tx1).unwrap(); + txq.import(tx2).unwrap_err(); + + // then + assert_eq!(txq.light_status().transaction_count, 1); +} + +#[test] +fn should_replace_transaction() { + // given + let b = TransactionBuilder::default(); + let mut txq = TestPool::default(); + let tx1 = b.tx().nonce(0).gas_price(1).new(); + let tx2 = b.tx().nonce(0).gas_price(2).new(); + + // when + txq.import(tx1).unwrap(); + txq.import(tx2).unwrap(); + + // then + assert_eq!(txq.light_status().transaction_count, 1); +} + +#[test] +fn should_reject_if_above_count() { + let b = TransactionBuilder::default(); + let mut txq = TestPool::with_options(Options { + max_count: 1, + ..Default::default() + }); + + // Reject second + let tx1 = b.tx().nonce(0).new(); + let tx2 = b.tx().nonce(1).new(); + let hash = *tx2.hash(); + txq.import(tx1).unwrap(); + assert_eq!(txq.import(tx2).unwrap_err().kind(), &error::ErrorKind::TooCheapToEnter(hash)); + assert_eq!(txq.light_status().transaction_count, 1); + + txq.clear(); + + // Replace first + let tx1 = b.tx().nonce(0).new(); + let tx2 = b.tx().nonce(0).sender(1).gas_price(2).new(); + txq.import(tx1).unwrap(); + txq.import(tx2).unwrap(); + assert_eq!(txq.light_status().transaction_count, 1); +} + +#[test] +fn should_reject_if_above_mem_usage() { + let b = TransactionBuilder::default(); + let mut txq = TestPool::with_options(Options { + max_mem_usage: 1, + ..Default::default() + }); + + // Reject second + let tx1 = b.tx().nonce(1).mem_usage(1).new(); + let tx2 = b.tx().nonce(2).mem_usage(2).new(); + let hash = *tx2.hash(); + txq.import(tx1).unwrap(); + assert_eq!(txq.import(tx2).unwrap_err().kind(), &error::ErrorKind::TooCheapToEnter(hash)); + assert_eq!(txq.light_status().transaction_count, 1); + + txq.clear(); + + // Replace first + let tx1 = b.tx().nonce(1).mem_usage(1).new(); + let tx2 = b.tx().nonce(1).sender(1).gas_price(2).mem_usage(1).new(); + txq.import(tx1).unwrap(); + txq.import(tx2).unwrap(); + assert_eq!(txq.light_status().transaction_count, 1); +} + +#[test] +fn should_reject_if_above_sender_count() { + let b = TransactionBuilder::default(); + let mut txq = TestPool::with_options(Options { + max_per_sender: 1, + ..Default::default() + }); + + // Reject second + let tx1 = b.tx().nonce(1).new(); + let tx2 = b.tx().nonce(2).new(); + let hash = *tx2.hash(); + txq.import(tx1).unwrap(); + assert_eq!(txq.import(tx2).unwrap_err().kind(), &error::ErrorKind::TooCheapToEnter(hash)); + assert_eq!(txq.light_status().transaction_count, 1); + + txq.clear(); + + // Replace first + let tx1 = b.tx().nonce(1).new(); + let tx2 = b.tx().nonce(2).gas_price(2).new(); + let hash = *tx2.hash(); + txq.import(tx1).unwrap(); + // This results in error because we also compare nonces + assert_eq!(txq.import(tx2).unwrap_err().kind(), &error::ErrorKind::TooCheapToEnter(hash)); + assert_eq!(txq.light_status().transaction_count, 1); +} + +#[test] +fn should_construct_pending() { + // given + let b = TransactionBuilder::default(); + let mut txq = TestPool::default(); + + let tx0 = txq.import(b.tx().nonce(0).gas_price(5).new()).unwrap(); + let tx1 = txq.import(b.tx().nonce(1).gas_price(5).new()).unwrap(); + let tx2 = txq.import(b.tx().nonce(2).new()).unwrap(); + // this transaction doesn't get to the block despite high gas price + // because of block gas limit and simplistic ordering algorithm. + txq.import(b.tx().nonce(3).gas_price(4).new()).unwrap(); + //gap + txq.import(b.tx().nonce(5).new()).unwrap(); + + let tx5 = txq.import(b.tx().sender(1).nonce(0).new()).unwrap(); + let tx6 = txq.import(b.tx().sender(1).nonce(1).new()).unwrap(); + let tx7 = txq.import(b.tx().sender(1).nonce(2).new()).unwrap(); + let tx8 = txq.import(b.tx().sender(1).nonce(3).gas_price(4).new()).unwrap(); + // gap + txq.import(b.tx().sender(1).nonce(5).new()).unwrap(); + + let tx9 = txq.import(b.tx().sender(2).nonce(0).new()).unwrap(); + assert_eq!(txq.light_status().transaction_count, 11); + assert_eq!(txq.status(NonceReady::default()), Status { + stalled: 0, + pending: 9, + future: 2, + }); + assert_eq!(txq.status(NonceReady::new(1)), Status { + stalled: 3, + pending: 6, + future: 2, + }); + + // when + let mut current_gas = U256::zero(); + let limit = (21_000 * 8).into(); + let mut pending = txq.pending(NonceReady::default()).take_while(|tx| { + let should_take = tx.gas + current_gas <= limit; + if should_take { + current_gas = current_gas + tx.gas + } + should_take + }); + + assert_eq!(pending.next(), Some(tx0)); + assert_eq!(pending.next(), Some(tx1)); + assert_eq!(pending.next(), Some(tx9)); + assert_eq!(pending.next(), Some(tx5)); + assert_eq!(pending.next(), Some(tx6)); + assert_eq!(pending.next(), Some(tx7)); + assert_eq!(pending.next(), Some(tx8)); + assert_eq!(pending.next(), Some(tx2)); + assert_eq!(pending.next(), None); +} + +#[test] +fn should_remove_transaction() { + // given + let b = TransactionBuilder::default(); + let mut txq = TestPool::default(); + + let tx1 = txq.import(b.tx().nonce(0).new()).unwrap(); + let tx2 = txq.import(b.tx().nonce(1).new()).unwrap(); + txq.import(b.tx().nonce(2).new()).unwrap(); + assert_eq!(txq.light_status().transaction_count, 3); + + // when + assert!(txq.remove(&tx2.hash(), false)); + + // then + assert_eq!(txq.light_status().transaction_count, 2); + let mut pending = txq.pending(NonceReady::default()); + assert_eq!(pending.next(), Some(tx1)); + assert_eq!(pending.next(), None); +} + +#[test] +fn should_cull_stalled_transactions() { + // given + let b = TransactionBuilder::default(); + let mut txq = TestPool::default(); + + txq.import(b.tx().nonce(0).gas_price(5).new()).unwrap(); + txq.import(b.tx().nonce(1).new()).unwrap(); + txq.import(b.tx().nonce(3).new()).unwrap(); + + txq.import(b.tx().sender(1).nonce(0).new()).unwrap(); + txq.import(b.tx().sender(1).nonce(1).new()).unwrap(); + txq.import(b.tx().sender(1).nonce(5).new()).unwrap(); + + assert_eq!(txq.status(NonceReady::new(1)), Status { + stalled: 2, + pending: 2, + future: 2, + }); + + // when + assert_eq!(txq.cull(None, NonceReady::new(1)), 2); + + // then + assert_eq!(txq.status(NonceReady::new(1)), Status { + stalled: 0, + pending: 2, + future: 2, + }); + assert_eq!(txq.light_status(), LightStatus { + transaction_count: 4, + senders: 2, + mem_usage: 0, + }); +} + +#[test] +fn should_cull_stalled_transactions_from_a_sender() { + // given + let b = TransactionBuilder::default(); + let mut txq = TestPool::default(); + + txq.import(b.tx().nonce(0).gas_price(5).new()).unwrap(); + txq.import(b.tx().nonce(1).new()).unwrap(); + + txq.import(b.tx().sender(1).nonce(0).new()).unwrap(); + txq.import(b.tx().sender(1).nonce(1).new()).unwrap(); + txq.import(b.tx().sender(1).nonce(2).new()).unwrap(); + + assert_eq!(txq.status(NonceReady::new(2)), Status { + stalled: 4, + pending: 1, + future: 0, + }); + + // when + let sender = 0.into(); + assert_eq!(txq.cull(Some(&[sender]), NonceReady::new(2)), 2); + + // then + assert_eq!(txq.status(NonceReady::new(2)), Status { + stalled: 2, + pending: 1, + future: 0, + }); + assert_eq!(txq.light_status(), LightStatus { + transaction_count: 3, + senders: 1, + mem_usage: 0, + }); +} + +#[test] +fn should_re_insert_after_cull() { + // given + let b = TransactionBuilder::default(); + let mut txq = TestPool::default(); + + txq.import(b.tx().nonce(0).gas_price(5).new()).unwrap(); + txq.import(b.tx().nonce(1).new()).unwrap(); + txq.import(b.tx().sender(1).nonce(0).new()).unwrap(); + txq.import(b.tx().sender(1).nonce(1).new()).unwrap(); + assert_eq!(txq.status(NonceReady::new(1)), Status { + stalled: 2, + pending: 2, + future: 0, + }); + + // when + assert_eq!(txq.cull(None, NonceReady::new(1)), 2); + assert_eq!(txq.status(NonceReady::new(1)), Status { + stalled: 0, + pending: 2, + future: 0, + }); + txq.import(b.tx().nonce(0).gas_price(5).new()).unwrap(); + txq.import(b.tx().sender(1).nonce(0).new()).unwrap(); + + assert_eq!(txq.status(NonceReady::new(1)), Status { + stalled: 2, + pending: 2, + future: 0, + }); +} + +mod listener { + use std::cell::RefCell; + use std::rc::Rc; + + use super::*; + + #[derive(Default)] + struct MyListener(pub Rc>>); + + impl Listener for MyListener { + fn added(&mut self, _tx: &SharedTransaction, old: Option<&SharedTransaction>) { + self.0.borrow_mut().push(if old.is_some() { "replaced" } else { "added" }); + } + + fn rejected(&mut self, _tx: Transaction) { + self.0.borrow_mut().push("rejected".into()); + } + + fn dropped(&mut self, _tx: &SharedTransaction) { + self.0.borrow_mut().push("dropped".into()); + } + + fn invalid(&mut self, _tx: &SharedTransaction) { + self.0.borrow_mut().push("invalid".into()); + } + + fn cancelled(&mut self, _tx: &SharedTransaction) { + self.0.borrow_mut().push("cancelled".into()); + } + + fn mined(&mut self, _tx: &SharedTransaction) { + self.0.borrow_mut().push("mined".into()); + } + } + + #[test] + fn insert_transaction() { + let b = TransactionBuilder::default(); + let listener = MyListener::default(); + let results = listener.0.clone(); + let mut txq = Pool::new(listener, DummyScoring, Options { + max_per_sender: 1, + max_count: 2, + ..Default::default() + }); + assert!(results.borrow().is_empty()); + + // Regular import + txq.import(b.tx().nonce(1).new()).unwrap(); + assert_eq!(*results.borrow(), &["added"]); + // Already present (no notification) + txq.import(b.tx().nonce(1).new()).unwrap_err(); + assert_eq!(*results.borrow(), &["added"]); + // Push out the first one + txq.import(b.tx().nonce(1).gas_price(1).new()).unwrap(); + assert_eq!(*results.borrow(), &["added", "replaced"]); + // Reject + txq.import(b.tx().nonce(1).new()).unwrap_err(); + assert_eq!(*results.borrow(), &["added", "replaced", "rejected"]); + results.borrow_mut().clear(); + // Different sender (accept) + txq.import(b.tx().sender(1).nonce(1).gas_price(2).new()).unwrap(); + assert_eq!(*results.borrow(), &["added"]); + // Third sender push out low gas price + txq.import(b.tx().sender(2).nonce(1).gas_price(4).new()).unwrap(); + assert_eq!(*results.borrow(), &["added", "dropped", "added"]); + // Reject (too cheap) + txq.import(b.tx().sender(2).nonce(1).gas_price(2).new()).unwrap_err(); + assert_eq!(*results.borrow(), &["added", "dropped", "added", "rejected"]); + + assert_eq!(txq.light_status().transaction_count, 2); + } + + #[test] + fn remove_transaction() { + let b = TransactionBuilder::default(); + let listener = MyListener::default(); + let results = listener.0.clone(); + let mut txq = Pool::new(listener, DummyScoring, Options::default()); + + // insert + let tx1 = txq.import(b.tx().nonce(1).new()).unwrap(); + let tx2 = txq.import(b.tx().nonce(2).new()).unwrap(); + + // then + txq.remove(&tx1.hash(), false); + assert_eq!(*results.borrow(), &["added", "added", "cancelled"]); + txq.remove(&tx2.hash(), true); + assert_eq!(*results.borrow(), &["added", "added", "cancelled", "invalid"]); + assert_eq!(txq.light_status().transaction_count, 0); + } + + #[test] + fn clear_queue() { + let b = TransactionBuilder::default(); + let listener = MyListener::default(); + let results = listener.0.clone(); + let mut txq = Pool::new(listener, DummyScoring, Options::default()); + + // insert + txq.import(b.tx().nonce(1).new()).unwrap(); + txq.import(b.tx().nonce(2).new()).unwrap(); + + // when + txq.clear(); + + // then + assert_eq!(*results.borrow(), &["added", "added", "dropped", "dropped"]); + } + + #[test] + fn cull_stalled() { + let b = TransactionBuilder::default(); + let listener = MyListener::default(); + let results = listener.0.clone(); + let mut txq = Pool::new(listener, DummyScoring, Options::default()); + + // insert + txq.import(b.tx().nonce(1).new()).unwrap(); + txq.import(b.tx().nonce(2).new()).unwrap(); + + // when + txq.cull(None, NonceReady::new(3)); + + // then + assert_eq!(*results.borrow(), &["added", "added", "mined", "mined"]); + } +} + diff --git a/transaction-pool/src/tests/tx_builder.rs b/transaction-pool/src/tests/tx_builder.rs new file mode 100644 index 000000000..cd50a4fd0 --- /dev/null +++ b/transaction-pool/src/tests/tx_builder.rs @@ -0,0 +1,74 @@ +// 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 . + +use std::rc::Rc; +use std::cell::Cell; + +use super::{Transaction, U256, Address}; + +#[derive(Debug, Default, Clone)] +pub struct TransactionBuilder { + nonce: U256, + gas_price: U256, + gas: U256, + sender: Address, + mem_usage: usize, + insertion_id: Rc>, +} + +impl TransactionBuilder { + pub fn tx(&self) -> Self { + self.clone() + } + + pub fn nonce>(mut self, nonce: T) -> Self { + self.nonce = nonce.into(); + self + } + + pub fn gas_price>(mut self, gas_price: T) -> Self { + self.gas_price = gas_price.into(); + self + } + + pub fn sender>(mut self, sender: T) -> Self { + self.sender = sender.into(); + self + } + + pub fn mem_usage(mut self, mem_usage: usize) -> Self { + self.mem_usage = mem_usage; + self + } + + pub fn new(self) -> Transaction { + let insertion_id = { + let id = self.insertion_id.get() + 1; + self.insertion_id.set(id); + id + }; + let hash = self.nonce ^ (U256::from(100) * self.gas_price) ^ (U256::from(100_000) * self.sender.low_u64().into()); + Transaction { + hash: hash.into(), + nonce: self.nonce, + gas_price: self.gas_price, + gas: 21_000.into(), + sender: self.sender, + insertion_id, + mem_usage: self.mem_usage, + } + } +} diff --git a/transaction-pool/src/transactions.rs b/transaction-pool/src/transactions.rs new file mode 100644 index 000000000..39fd08e93 --- /dev/null +++ b/transaction-pool/src/transactions.rs @@ -0,0 +1,216 @@ +// 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 . + +use std::{fmt, mem}; +use std::sync::Arc; + +use smallvec::SmallVec; + +use ready::{Ready, Readiness}; +use scoring::{self, Scoring}; + +#[derive(Debug)] +pub enum AddResult { + Ok(Arc), + TooCheapToEnter(T), + TooCheap { + old: Arc, + new: T, + }, + Replaced { + old: Arc, + new: Arc, + }, + PushedOut { + old: Arc, + new: Arc, + }, +} + +/// Represents all transactions from a particular sender ordered by nonce. +const PER_SENDER: usize = 8; +#[derive(Debug)] +pub struct Transactions> { + // TODO [ToDr] Consider using something that doesn't require shifting all records. + transactions: SmallVec<[Arc; PER_SENDER]>, + scores: SmallVec<[S::Score; PER_SENDER]>, +} + +impl> Default for Transactions { + fn default() -> Self { + Transactions { + transactions: Default::default(), + scores: Default::default(), + } + } +} + +impl> Transactions { + pub fn is_empty(&self) -> bool { + self.transactions.is_empty() + } + + pub fn len(&self) -> usize { + self.transactions.len() + } + + pub fn iter(&self) -> ::std::slice::Iter> { + self.transactions.iter() + } + + pub fn worst_and_best(&self) -> Option<((S::Score, Arc), (S::Score, Arc))> { + let len = self.scores.len(); + self.scores.get(0).cloned().map(|best| { + let worst = self.scores[len - 1].clone(); + let best_tx = self.transactions[0].clone(); + let worst_tx = self.transactions[len - 1].clone(); + + ((worst, worst_tx), (best, best_tx)) + }) + } + + pub fn find_next(&self, tx: &T, scoring: &S) -> Option<(S::Score, Arc)> { + self.transactions.binary_search_by(|old| scoring.compare(old, &tx)).ok().and_then(|index| { + let index = index + 1; + if index < self.scores.len() { + Some((self.scores[index].clone(), self.transactions[index].clone())) + } else { + None + } + }) + } + + fn push_cheapest_transaction(&mut self, tx: T, scoring: &S, max_count: usize) -> AddResult { + let index = self.transactions.len(); + if index == max_count { + AddResult::TooCheapToEnter(tx) + } else { + let shared = Arc::new(tx); + self.transactions.push(shared.clone()); + self.scores.push(Default::default()); + scoring.update_scores(&self.transactions, &mut self.scores, scoring::Change::InsertedAt(index)); + + AddResult::Ok(shared) + } + } + + pub fn add(&mut self, tx: T, scoring: &S, max_count: usize) -> AddResult { + let index = match self.transactions.binary_search_by(|old| scoring.compare(old, &tx)) { + Ok(index) => index, + Err(index) => index, + }; + + // Insert at the end. + if index == self.transactions.len() { + return self.push_cheapest_transaction(tx, scoring, max_count) + } + + // Decide if the transaction should replace some other. + match scoring.choose(&self.transactions[index], &tx) { + // New transaction should be rejected + scoring::Choice::RejectNew => AddResult::TooCheap { + old: self.transactions[index].clone(), + new: tx, + }, + // New transaction should be kept along with old ones. + scoring::Choice::InsertNew => { + let new = Arc::new(tx); + + self.transactions.insert(index, new.clone()); + self.scores.insert(index, Default::default()); + scoring.update_scores(&self.transactions, &mut self.scores, scoring::Change::InsertedAt(index)); + + if self.transactions.len() > max_count { + let old = self.transactions.pop().expect("len is non-zero"); + self.scores.pop(); + scoring.update_scores(&self.transactions, &mut self.scores, scoring::Change::RemovedAt(self.transactions.len())); + + AddResult::PushedOut { + old, + new, + } + } else { + AddResult::Ok(new) + } + }, + // New transaction is replacing some other transaction already in the queue. + scoring::Choice::ReplaceOld => { + let new = Arc::new(tx); + let old = mem::replace(&mut self.transactions[index], new.clone()); + scoring.update_scores(&self.transactions, &mut self.scores, scoring::Change::ReplacedAt(index)); + + AddResult::Replaced { + old, + new, + } + }, + } + } + + pub fn remove(&mut self, tx: &T, scoring: &S) -> bool { + let index = match self.transactions.binary_search_by(|old| scoring.compare(old, tx)) { + Ok(index) => index, + Err(_) => { + warn!("Attempting to remove non-existent transaction {:?}", tx); + return false; + }, + }; + + self.transactions.remove(index); + self.scores.remove(index); + // Update scoring + scoring.update_scores(&self.transactions, &mut self.scores, scoring::Change::RemovedAt(index)); + return true; + } + + pub fn cull>(&mut self, ready: &mut R, scoring: &S) -> SmallVec<[Arc; PER_SENDER]> { + let mut result = SmallVec::new(); + if self.is_empty() { + return result; + } + + let mut first_non_stalled = 0; + for tx in &self.transactions { + match ready.is_ready(tx) { + Readiness::Stalled => { + first_non_stalled += 1; + }, + Readiness::Ready | Readiness::Future => break, + } + } + + // reverse the vectors to easily remove first elements. + self.transactions.reverse(); + self.scores.reverse(); + + for _ in 0..first_non_stalled { + self.scores.pop(); + result.push( + self.transactions.pop().expect("first_non_stalled is never greater than transactions.len(); qed") + ); + } + + self.transactions.reverse(); + self.scores.reverse(); + + // update scoring + scoring.update_scores(&self.transactions, &mut self.scores, scoring::Change::Culled(result.len())); + + // reverse the result to maintain correct order. + result.reverse(); + result + } +} diff --git a/transaction-pool/src/verifier.rs b/transaction-pool/src/verifier.rs new file mode 100644 index 000000000..e55a17e91 --- /dev/null +++ b/transaction-pool/src/verifier.rs @@ -0,0 +1,31 @@ +// 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 . + +use {VerifiedTransaction}; + +/// Transaction verification. +/// +/// Verifier is responsible to decide if the transaction should even be considered for pool inclusion. +pub trait Verifier { + /// Verification error. + type Error; + + /// Verified transaction. + type VerifiedTransaction: VerifiedTransaction; + + /// Verifies a `UnverifiedTransaction` and produces `VerifiedTransaction` instance. + fn verify_transaction(&self, tx: U) -> Result; +} diff --git a/updater/Cargo.toml b/updater/Cargo.toml index a67559f68..ec1b99296 100644 --- a/updater/Cargo.toml +++ b/updater/Cargo.toml @@ -19,4 +19,5 @@ futures = "0.1" parking_lot = "0.4" parity-hash-fetch = { path = "../hash-fetch" } parity-reactor = { path = "../util/reactor" } +parity-version = { path = "../util/version" } path = { path = "../util/path" } diff --git a/updater/src/lib.rs b/updater/src/lib.rs index c0da3b6a1..73ebc7ec4 100644 --- a/updater/src/lib.rs +++ b/updater/src/lib.rs @@ -28,6 +28,7 @@ extern crate ethsync; extern crate futures; extern crate target_info; extern crate parity_reactor; +extern crate parity_version as version; extern crate path; extern crate semver; diff --git a/updater/src/types/version_info.rs b/updater/src/types/version_info.rs index 778e57087..ea9b44577 100644 --- a/updater/src/types/version_info.rs +++ b/updater/src/types/version_info.rs @@ -19,7 +19,7 @@ use std::fmt; use semver::{Version}; use bigint::hash::H160; -use util::misc::raw_package_info; +use version::raw_package_info; use types::ReleaseTrack; /// Version information of a particular release. diff --git a/updater/src/updater.rs b/updater/src/updater.rs index 91c9181f8..72ddaa7fe 100644 --- a/updater/src/updater.rs +++ b/updater/src/updater.rs @@ -34,7 +34,7 @@ use bigint::hash::{H160, H256}; use util::Address; use bytes::Bytes; use parking_lot::Mutex; -use util::misc; +use version; /// Filter for releases. #[derive(Debug, Eq, PartialEq, Clone)] @@ -115,7 +115,7 @@ fn platform() -> String { } else if cfg!(target_os = "linux") { format!("{}-unknown-linux-gnu", Target::arch()) } else { - misc::platform() + version::platform() } } diff --git a/util/Cargo.toml b/util/Cargo.toml index ed65a8c47..e60b3c8eb 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -5,41 +5,15 @@ license = "GPL-3.0" name = "ethcore-util" version = "1.9.0" authors = ["Parity Technologies "] -build = "build.rs" [dependencies] -log = "0.3" -env_logger = "0.4" -rustc-hex = "1.0" -eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1" } -elastic-array = "0.9" -rlp = { path = "rlp" } -heapsize = "0.4" -keccak-hash = { path = "hash" } -clippy = { version = "0.0.103", optional = true} -libc = "0.2.7" -target_info = "0.1" ethcore-bigint = { path = "bigint", features = ["heapsizeof"] } -parking_lot = "0.4" -tiny-keccak= "1.0" -ethcore-logger = { path = "../logger" } -triehash = { path = "triehash" } hashdb = { path = "hashdb" } -patricia-trie = { path = "patricia_trie" } -ethcore-bytes = { path = "bytes" } memorydb = { path = "memorydb" } -util-error = { path = "error" } -kvdb = { path = "kvdb" } -journaldb = { path = "journaldb" } [dev-dependencies] -kvdb-memorydb = { path = "kvdb-memorydb" } - -[features] -default = [] -dev = ["clippy"] -final = [] - -[build-dependencies] -vergen = "0.1" -rustc_version = "0.1.0" +ethcore-bytes = { path = "bytes" } +keccak-hash = { path = "hash" } +log = "0.3" +patricia-trie = { path = "patricia_trie" } +triehash = { path = "triehash" } diff --git a/util/bigint/src/hash.rs b/util/bigint/src/hash.rs index 212a1dd13..5581bbc20 100644 --- a/util/bigint/src/hash.rs +++ b/util/bigint/src/hash.rs @@ -179,7 +179,6 @@ macro_rules! impl_hash { } impl Copy for $from {} - #[cfg_attr(feature="dev", allow(expl_impl_clone_on_copy))] impl Clone for $from { fn clone(&self) -> $from { let mut ret = $from::new(); @@ -464,7 +463,6 @@ mod tests { } #[test] - #[cfg_attr(feature="dev", allow(eq_op))] fn hash() { let h = H64([0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]); assert_eq!(H64::from_str("0123456789abcdef").unwrap(), h); diff --git a/util/bloom/Cargo.toml b/util/bloom/Cargo.toml index 99464879f..8c366a852 100644 --- a/util/bloom/Cargo.toml +++ b/util/bloom/Cargo.toml @@ -1,4 +1,4 @@ -[project] +[package] name = "ethcore-bloom-journal" version = "0.1.0" authors = ["Parity Technologies "] diff --git a/util/journaldb/src/archivedb.rs b/util/journaldb/src/archivedb.rs index 5fa1277e4..bd5745900 100644 --- a/util/journaldb/src/archivedb.rs +++ b/util/journaldb/src/archivedb.rs @@ -196,8 +196,6 @@ impl JournalDB for ArchiveDB { #[cfg(test)] mod tests { - #![cfg_attr(feature="dev", allow(blacklisted_name))] - #![cfg_attr(feature="dev", allow(similar_names))] use keccak::keccak; use hashdb::{HashDB, DBValue}; diff --git a/util/journaldb/src/earlymergedb.rs b/util/journaldb/src/earlymergedb.rs index 5ca023cb0..e9a1b80f9 100644 --- a/util/journaldb/src/earlymergedb.rs +++ b/util/journaldb/src/earlymergedb.rs @@ -447,7 +447,6 @@ impl JournalDB for EarlyMergeDB { } } - #[cfg_attr(feature="dev", allow(cyclomatic_complexity))] fn mark_canonical(&mut self, batch: &mut DBTransaction, end_era: u64, canon_id: &H256) -> Result { let mut refs = self.refs.as_ref().unwrap().write(); @@ -544,8 +543,6 @@ impl JournalDB for EarlyMergeDB { #[cfg(test)] mod tests { - #![cfg_attr(feature="dev", allow(blacklisted_name))] - #![cfg_attr(feature="dev", allow(similar_names))] use keccak::keccak; use hashdb::{HashDB, DBValue}; diff --git a/util/journaldb/src/overlaydb.rs b/util/journaldb/src/overlaydb.rs index 6844eb801..220076eaa 100644 --- a/util/journaldb/src/overlaydb.rs +++ b/util/journaldb/src/overlaydb.rs @@ -202,7 +202,6 @@ impl HashDB for OverlayDB { } #[test] -#[cfg_attr(feature="dev", allow(blacklisted_name))] fn overlaydb_revert() { let mut m = OverlayDB::new_temp(); let foo = m.insert(b"foo"); // insert foo. diff --git a/util/journaldb/src/overlayrecentdb.rs b/util/journaldb/src/overlayrecentdb.rs index d57e172d5..97fec7604 100644 --- a/util/journaldb/src/overlayrecentdb.rs +++ b/util/journaldb/src/overlayrecentdb.rs @@ -452,8 +452,6 @@ impl HashDB for OverlayRecentDB { #[cfg(test)] mod tests { - #![cfg_attr(feature="dev", allow(blacklisted_name))] - #![cfg_attr(feature="dev", allow(similar_names))] use keccak::keccak; use super::*; diff --git a/util/journaldb/src/refcounteddb.rs b/util/journaldb/src/refcounteddb.rs index 6e114e476..e1d15383e 100644 --- a/util/journaldb/src/refcounteddb.rs +++ b/util/journaldb/src/refcounteddb.rs @@ -205,8 +205,6 @@ impl JournalDB for RefCountedDB { #[cfg(test)] mod tests { - #![cfg_attr(feature="dev", allow(blacklisted_name))] - #![cfg_attr(feature="dev", allow(similar_names))] use keccak::keccak; use hashdb::{HashDB, DBValue}; diff --git a/util/network/Cargo.toml b/util/network/Cargo.toml index c4718ba31..006bbc5ac 100644 --- a/util/network/Cargo.toml +++ b/util/network/Cargo.toml @@ -15,7 +15,6 @@ time = "0.1.34" tiny-keccak = "1.3" rust-crypto = "0.2.34" slab = "0.2" -clippy = { version = "0.0.103", optional = true} igd = "0.6" libc = "0.2.7" parking_lot = "0.4" @@ -41,4 +40,3 @@ tempdir = "0.3" [features] default = [] -dev = ["clippy"] diff --git a/util/network/src/discovery.rs b/util/network/src/discovery.rs index c2804f594..f75672bf5 100644 --- a/util/network/src/discovery.rs +++ b/util/network/src/discovery.rs @@ -281,7 +281,6 @@ impl Discovery { self.send_to(packet, address.clone()); } - #[cfg_attr(feature="dev", allow(map_clone))] fn nearest_node_entries(target: &NodeId, buckets: &[NodeBucket]) -> Vec { let mut found: BTreeMap> = BTreeMap::new(); let mut count = 0; diff --git a/util/network/src/host.rs b/util/network/src/host.rs index 9a5c4c0ca..5c770094d 100644 --- a/util/network/src/host.rs +++ b/util/network/src/host.rs @@ -705,7 +705,6 @@ impl Host { debug!(target: "network", "Connecting peers: {} sessions, {} pending, {} started", self.session_count(), self.handshake_count(), started); } - #[cfg_attr(feature="dev", allow(single_match))] fn connect_peer(&self, id: &NodeId, io: &IoContext) { if self.have_session(id) { trace!(target: "network", "Aborted connect. Node already connected."); @@ -744,7 +743,6 @@ impl Host { } } - #[cfg_attr(feature="dev", allow(block_in_if_condition_stmt))] fn create_connection(&self, socket: TcpStream, id: Option<&NodeId>, io: &IoContext) -> Result<(), Error> { let nonce = self.info.write().next_nonce(); let mut sessions = self.sessions.write(); @@ -805,7 +803,6 @@ impl Host { self.kill_connection(token, io, true); } - #[cfg_attr(feature="dev", allow(collapsible_if))] fn session_readable(&self, token: StreamToken, io: &IoContext) { let mut ready_data: Vec = Vec::new(); let mut packet_data: Vec<(ProtocolId, PacketId, Vec)> = Vec::new(); diff --git a/util/network/src/ip_utils.rs b/util/network/src/ip_utils.rs index d637dbee8..3767fbb15 100644 --- a/util/network/src/ip_utils.rs +++ b/util/network/src/ip_utils.rs @@ -365,7 +365,6 @@ fn can_map_external_address_or_fail() { #[test] fn ipv4_properties() { - #![cfg_attr(feature="dev", allow(too_many_arguments))] fn check(octets: &[u8; 4], unspec: bool, loopback: bool, private: bool, link_local: bool, global: bool, multicast: bool, broadcast: bool, documentation: bool) { diff --git a/util/patricia_trie/src/lib.rs b/util/patricia_trie/src/lib.rs index 07fd43bdf..83ab09073 100644 --- a/util/patricia_trie/src/lib.rs +++ b/util/patricia_trie/src/lib.rs @@ -268,7 +268,6 @@ impl<'db> Trie for TrieKinds<'db> { } } -#[cfg_attr(feature="dev", allow(wrong_self_convention))] impl TrieFactory { /// Creates new factory. pub fn new(spec: TrieSpec) -> Self { diff --git a/util/patricia_trie/src/triedb.rs b/util/patricia_trie/src/triedb.rs index 939957a65..39776d106 100644 --- a/util/patricia_trie/src/triedb.rs +++ b/util/patricia_trie/src/triedb.rs @@ -57,7 +57,6 @@ pub struct TrieDB<'db> { pub hash_count: usize, } -#[cfg_attr(feature="dev", allow(wrong_self_convention))] impl<'db> TrieDB<'db> { /// Create a new trie with the backing database `db` and `root` /// Returns an error if `root` does not exist diff --git a/util/patricia_trie/src/triedbmut.rs b/util/patricia_trie/src/triedbmut.rs index d0bef2181..497fe7284 100644 --- a/util/patricia_trie/src/triedbmut.rs +++ b/util/patricia_trie/src/triedbmut.rs @@ -436,7 +436,6 @@ impl<'a> TrieDBMut<'a> { } /// the insertion inspector. - #[cfg_attr(feature = "dev", allow(cyclomatic_complexity))] fn insert_inspector(&mut self, node: Node, partial: NibbleSlice, value: DBValue, old_val: &mut Option) -> super::Result { diff --git a/util/src/lib.rs b/util/src/lib.rs index a81680c91..4094ed967 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -15,23 +15,6 @@ // along with Parity. If not, see . #![warn(missing_docs)] -#![cfg_attr(feature="dev", feature(plugin))] -#![cfg_attr(feature="dev", plugin(clippy))] - -// Clippy settings -// Most of the time much more readable -#![cfg_attr(feature="dev", allow(needless_range_loop))] -// Shorter than if-else -#![cfg_attr(feature="dev", allow(match_bool))] -// We use that to be more explicit about handled cases -#![cfg_attr(feature="dev", allow(match_same_arms))] -// Keeps consistency (all lines with `.clone()`). -#![cfg_attr(feature="dev", allow(clone_on_copy))] -// Some false positives when doing pattern matching. -#![cfg_attr(feature="dev", allow(needless_borrow))] -// TODO [todr] a lot of warnings to be fixed -#![cfg_attr(feature="dev", allow(assign_op_pattern))] - //! Ethcore-util library //! @@ -87,33 +70,10 @@ //! cargo build --release //! ``` -extern crate rustc_hex; -extern crate env_logger; -extern crate secp256k1; -extern crate elastic_array; -extern crate libc; -extern crate target_info; extern crate ethcore_bigint as bigint; -extern crate ethcore_bytes as bytes; -extern crate parking_lot; -extern crate tiny_keccak; -extern crate rlp; -extern crate heapsize; -extern crate ethcore_logger; -extern crate keccak_hash as keccak; extern crate hashdb; extern crate memorydb; -extern crate patricia_trie as trie; -extern crate kvdb; -extern crate util_error as error; -#[cfg(test)] -extern crate kvdb_memorydb; - - -pub mod misc; - -pub use misc::*; pub use hashdb::*; pub use memorydb::MemoryDB; diff --git a/util/version/Cargo.toml b/util/version/Cargo.toml new file mode 100644 index 000000000..22bf07fb1 --- /dev/null +++ b/util/version/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "parity-version" +version = "0.1.0" +authors = ["Parity Technologies "] +build = "build.rs" + +[dependencies] +ethcore-bytes = { path = "../bytes" } +rlp = { path = "../rlp" } +target_info = "0.1" + +[build-dependencies] +vergen = "0.1" +rustc_version = "0.1.0" diff --git a/util/build.rs b/util/version/build.rs similarity index 97% rename from util/build.rs rename to util/version/build.rs index 08ef81697..b5d3dd81d 100644 --- a/util/build.rs +++ b/util/version/build.rs @@ -17,11 +17,11 @@ extern crate vergen; extern crate rustc_version; -use vergen::*; use std::env; use std::fs::File; use std::io::Write; use std::path::Path; +use vergen::{vergen, OutputFns}; fn main() { vergen(OutputFns::all()).unwrap(); diff --git a/util/src/misc.rs b/util/version/src/lib.rs similarity index 93% rename from util/src/misc.rs rename to util/version/src/lib.rs index 6a1a11708..236f11914 100644 --- a/util/src/misc.rs +++ b/util/version/src/lib.rs @@ -16,9 +16,13 @@ //! Diff misc. -use rlp::RlpStream; +extern crate target_info; +extern crate ethcore_bytes as bytes; +extern crate rlp; + use target_info::Target; use bytes::Bytes; +use rlp::RlpStream; include!(concat!(env!("OUT_DIR"), "/version.rs")); include!(concat!(env!("OUT_DIR"), "/rustc_version.rs")); @@ -31,15 +35,6 @@ const THIS_TRACK: &'static str = "nightly"; const THIS_TRACK: &'static str = "unstable"; // ^^^ This gets used when we're not building a final release; should stay as "unstable". -/// Boolean type for clean/dirty status. -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub enum Filth { - /// Data has not been changed. - Clean, - /// Data has been changed. - Dirty, -} - /// Get the platform identifier. pub fn platform() -> String { let env = Target::env(); diff --git a/whisper/src/net.rs b/whisper/src/net/mod.rs similarity index 98% rename from whisper/src/net.rs rename to whisper/src/net/mod.rs index 4a051b3df..1fc5f30b4 100644 --- a/whisper/src/net.rs +++ b/whisper/src/net/mod.rs @@ -30,6 +30,9 @@ use rlp::{DecoderError, RlpStream, UntrustedRlp}; use message::{Message, Error as MessageError}; +#[cfg(test)] +mod tests; + // how often periodic relays are. when messages are imported // we directly broadcast. const RALLY_TOKEN: TimerToken = 1; @@ -341,8 +344,8 @@ impl Peer { .map_or(true, |filter| &(filter & message.bloom()) == message.bloom()) } - // note a message as known. returns true if it was already - // known, false otherwise. + // note a message as known. returns false if it was already + // known, true otherwise. fn note_known(&mut self, message: &Message) -> bool { self.known_messages.insert(message.hash().clone()) } @@ -657,6 +660,22 @@ impl Network { io.send(*peer, packet::STATUS, ::rlp::EMPTY_LIST_RLP.to_vec()); } + fn on_packet(&self, io: &C, peer: &PeerId, packet_id: u8, data: &[u8]) { + let rlp = UntrustedRlp::new(data); + let res = match packet_id { + packet::STATUS => self.on_status(peer, rlp), + packet::MESSAGES => self.on_messages(peer, rlp), + packet::POW_REQUIREMENT => self.on_pow_requirement(peer, rlp), + packet::TOPIC_FILTER => self.on_topic_filter(peer, rlp), + _ => Ok(()), // ignore unknown packets. + }; + + if let Err(e) = res { + trace!(target: "whisper", "Disabling peer due to misbehavior: {}", e); + io.disable_peer(*peer); + } + } + fn on_disconnect(&self, peer: &PeerId) { trace!(target: "whisper", "Disconnecting peer {}", peer); let _ = self.peers.write().remove(peer); @@ -673,19 +692,7 @@ impl ::network::NetworkProtocolHandler for Network { } fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) { - let rlp = UntrustedRlp::new(data); - let res = match packet_id { - packet::STATUS => self.on_status(peer, rlp), - packet::MESSAGES => self.on_messages(peer, rlp), - packet::POW_REQUIREMENT => self.on_pow_requirement(peer, rlp), - packet::TOPIC_FILTER => self.on_topic_filter(peer, rlp), - _ => Ok(()), // ignore unknown packets. - }; - - if let Err(e) = res { - trace!(target: "whisper", "Disabling peer due to misbehavior: {}", e); - io.disable_peer(*peer); - } + self.on_packet(io, peer, packet_id, data) } fn connected(&self, io: &NetworkContext, peer: &PeerId) { diff --git a/whisper/src/net/tests.rs b/whisper/src/net/tests.rs new file mode 100644 index 000000000..51c9c00ce --- /dev/null +++ b/whisper/src/net/tests.rs @@ -0,0 +1,192 @@ +// 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 . + +//! Tests for the whisper network module. + +use std::collections::HashSet; +use std::sync::mpsc; + +use parking_lot::Mutex; +use network::{NodeId, PeerId}; + +use message::{CreateParams, Message}; +use super::*; + +struct TestHandler(Mutex>); + +impl MessageHandler for TestHandler { + fn handle_messages(&self, messages: &[Message]) { + let tx = self.0.lock(); + for message in messages { + let _ = tx.send(message.clone()); + } + } +} + +struct TestPeer { + network: Network, + recv: mpsc::Receiver, + disconnected: Mutex>, +} + +impl TestPeer { + fn create() -> Self { + let (tx, rx) = mpsc::channel(); + + TestPeer { + network: Network::new(10 * 1024 * 1024, TestHandler(Mutex::new(tx))), + recv: rx, + disconnected: Mutex::new(HashSet::new()), + } + } +} + +struct TestNetwork { + peers: Vec, +} + +impl TestNetwork { + fn new(n_peers: usize) -> Self { + let unconnected_peers: Vec<_> = (0..n_peers).map(|_| TestPeer::create()).collect(); + for i in 0..n_peers { + for j in (i + 1)..n_peers { + let (peer1, peer2) = (&unconnected_peers[i], &unconnected_peers[j]); + let ctx1 = TestContext::new(&unconnected_peers, i); + let ctx2 = TestContext::new(&unconnected_peers, j); + + peer1.network.on_connect(&ctx1, &j); + peer2.network.on_connect(&ctx2, &i); + } + } + + TestNetwork { + peers: unconnected_peers, + } + } + + fn post_message_from(&self, id: PeerId, msg: Message) { + self.peers[id].network.post_message(msg, &TestContext::new(&self.peers, id)); + } +} + +enum Event { + Disconnect(PeerId, PeerId), + Send(PeerId, PeerId, u8, Vec), +} + +struct TestContext<'a> { + peers: &'a [TestPeer], + local_id: PeerId, + events: Mutex>, +} + +impl<'a> TestContext<'a> { + fn new(peers: &'a [TestPeer], local_id: PeerId) -> Self { + TestContext { + peers, + local_id, + events: Mutex::new(Vec::new()), + } + } +} + +impl<'a> Context for TestContext<'a> { + fn disconnect_peer(&self, id: PeerId) { + self.events.lock().push(Event::Disconnect(self.local_id, id)); + } + + fn disable_peer(&self, id: PeerId) { + self.events.lock().push(Event::Disconnect(self.local_id, id)); + } + + fn node_key(&self, peer: PeerId) -> Option { + let mut id = NodeId::default(); + id[0] = peer as _; + Some(id) + } + + fn protocol_version(&self, id: ::network::ProtocolId, _peer: PeerId) -> Option { + if &id == b"shh" || &id == b"pwh" { + Some(PROTOCOL_VERSION as _) + } else { + None + } + } + + fn send(&self, peer: PeerId, packet: u8, data: Vec) { + self.events.lock().push(Event::Send(self.local_id, peer, packet, data)); + } +} + +impl<'a> Drop for TestContext<'a> { + fn drop(&mut self) { + let events = self.events.get_mut(); + while !events.is_empty() { + let mut deferred = Vec::new(); + for event in events.drain(..) { + match event { + Event::Disconnect(from, target) => { + self.peers[from].network.on_disconnect(&target); + self.peers[target].network.on_disconnect(&from); + + self.peers[from].disconnected.lock().insert(target); + self.peers[target].disconnected.lock().insert(from); + } + Event::Send(from, target, packet, data) => { + if self.peers[from].disconnected.lock().contains(&target) { + continue; + } + + let mut inner_ctx = TestContext::new(self.peers, target); + + self.peers[target].network.on_packet( + &inner_ctx, + &from, + packet, + &data[..] + ); + + // don't recursively apply disconnects or new messages + // from the receiver's actions yet. + let inner_events = ::std::mem::replace(inner_ctx.events.get_mut(), Vec::new()); + deferred.extend(inner_events); + } + } + } + + events.extend(deferred); + } + } +} + +#[test] +fn message_gets_relayed() { + let network = TestNetwork::new(5); + let message = Message::create(CreateParams { + ttl: 500, + payload: b"this is my payload, pal".to_vec(), + topics: vec![[0, 1, 2, 3].into()], + work: 25, + }).unwrap(); + + network.post_message_from(0, message.clone()); + + assert!(network.peers[0].recv.try_recv().is_err()); + + for i in 1..5 { + assert_eq!(network.peers[i].recv.try_recv().unwrap(), message); + } +}