Compare commits

...

35 Commits

Author SHA1 Message Date
Tomasz Drwięga
8b749367fd AuRa fix for 1.7.x series (#7666)
* Fix Temporarily Invalid blocks handling (#7613)

* Handle temporarily invalid blocks in sync.

* Fix tests.

* Bump rustc-serialize

* Bump version.

* Update .gitlab-ci.yml

fix lint

* Remove slash from gitlab ci script to fix builds

* Start build.
2018-01-22 23:44:59 +01:00
Afri Schoedon
f96d152548 Fix stable builds for rustc 1.23.0 (#7504) 2018-01-09 10:39:00 +01:00
Tomasz Drwięga
c8f57315a8 [stable] Missing AuRa backports (#7499)
* Merge pull request #7368 from paritytech/td-future-blocks

Wait for future blocks in AuRa

* Advance AuRa step as far as we can and prevent invalid blocks. (#7451)

* Advance AuRa step as far as we can.

* Wait for future blocks.

* Problem: AuRa's unsafeties around step duration (#7282)

Firstly, `Step.duration_remaining` casts it to u32, unnecesarily
limiting it to 2^32. While theoretically this is "good enough" (at 3
seconds steps it provides room for a little over 400 years), it is
still a lossy way to calculate the remaining time until the next step.

Secondly, step duration might be zero, triggering division by zero
in `Step.calibrate`

Solution: rework the code around the fact that duration is
typically in single digits and never grows, hence, it can be represented
by a much narrower range (u16) and this highlights the fact that
multiplying u64 by u16 will only result in an overflow in even further
future, at which point we should panic informatively (if anybody's
still around)

Similarly, panic when it is detected that incrementing the step
counter wrapped around on the overflow of usize.

As for the division by zero, prevent it by making zero an invalid
value for step duration. This will make AuRa log the constraint
mismatch and panic (after all, what purpose would zero step duration
serve? it makes no sense within the definition of the protocol,
as finality can only be achieved as per the specification
if messages are received within the step duration, which would violate
the speed of light and other physical laws in this case).

* Fix tests.

* detect different node, same-key signing in aura (#7245)

* detect different node, same-key signing in aura

* reduce scope of warning
2018-01-08 23:09:11 +01:00
Tomasz Drwięga
e1925015c8 [stable] backports (#7496)
* Advance AuRa step as far as we can.

* fixed panic when io is not available for export block, closes #7486 (#7495)

* Update Parity Mainnet Bootnodes

* Replace the Azure HDD bootnodes with the new ones :)

* Bump version.
2018-01-08 16:07:53 +01:00
Afri Schoedon
a5ed4cfc4b Stable Bootnodes and Warpnodes (#7298)
* new warp enodes (#7287)

* new warp enodes

* added one more warp enode; replaced spaces with tabs

* Bump stable to 1.7.11

* Update kovan boot nodes (#7296)

* Fix Cargo.lock

* Updating mainnet bootnodes.

* Update bootnodes (#7363)

* Updating mainnet bootnodes.

* Add additional parity-beta bootnodes.

* Restore old parity bootnodes and update foudation bootnodes
2017-12-28 15:18:40 +01:00
Tomasz Drwięga
cfa197061e Ethstore optimizations (#6827) (#6844) (#7347) 2017-12-27 23:36:01 +01:00
Tomasz Drwięga
9075ff883f Fix default CORS. (#7389) 2017-12-27 18:56:35 +01:00
Tomasz Drwięga
39317136c7 [stable] Backports + HF block update (#7243)
* Reduce max block timestamp drift to 15 seconds (#7240)

* reduce max block timestamp drift to 15 seconds

* add test for block timestamp validation within allowed drift

* Update kovan HF block number.
2017-12-11 14:09:46 +01:00
Tomasz Drwięga
f4a496ac8c [stable] Backports + Kovan HF (#7235)
* Merge pull request #7075 from miyao-gmo/feature/estimate_gas_limit

escape inifinite loop in estimte_gas

* Merge pull request #7006 from paritytech/no-uncles

Disable uncles by default

* Maximum uncle count transition (#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)
2017-12-08 13:08:01 +01:00
Robert Habermeier
12940e4ed7 Merge pull request #7047 from paritytech/a5-version-179
Bumb 1.7.9
2017-11-13 21:00:21 +01:00
5chdn
2c2587e44c Bumb to v1.7.9 2017-11-13 16:50:41 +01:00
GitLab Build Bot
d219ebc9ae [ci skip] js-precompiled 20171112-191643 2017-11-12 19:23:36 +00:00
Marek Kotewicz
412d1cb578 Merge pull request #7017 from paritytech/jg-stable-multisig-no-deploy
[stable] Disallow builtin multisig deploy (only watch)
2017-11-12 20:08:40 +01:00
jacogr
6638b01b46 Disallow builtin multisig deploy (only watch) 2017-11-10 17:25:57 +01:00
arkpar
d5fcf3b8ed Fixed build 2017-10-25 12:06:04 +02:00
Tomasz Drwięga
6bb3edeb8a [stable] Refactor static context check in CREATE (#6889)
* Refactor static context check in CREATE.

* Fix evm tests.
2017-10-25 11:52:32 +02:00
Arkadiy Paronyan
16c091590f v1.7.8 (#6890) 2017-10-25 11:34:41 +02:00
Arkadiy Paronyan
eb7c648250 [stable] Fix auto-update (#6769)
* v1.7.7

* Updated ethabi to fix auto-update
2017-10-15 11:53:40 +02:00
Arkadiy Paronyan
be23afd1cd Bumped fork block for the auto updater (#6754) 2017-10-14 15:05:52 +02:00
Arkadiy Paronyan
19535333c9 [stable] Fixed modexp gas calculation overflow (#6741) (#6746)
* Fixed modexp gas calculation overflow (#6741)

* v1.7.6
2017-10-13 16:20:27 +02:00
GitLab Build Bot
5a1a7f6f45 js-precompiled 20171012-203250 2017-10-12 23:18:56 +02:00
Nicolas Gotchac
148b20978c Backport stable #6730 - Fixes Badges (#6731)
* Fix badges not showing up

* Always fetch meta data first [badges]
2017-10-12 22:23:06 +02:00
GitLab Build Bot
75b4bea521 js-precompiled 20171012-183503 2017-10-12 21:48:12 +02:00
Jaco Greeff
69f53ffb2b Backport #6719 & #6725 (#6726)
* Check vouch status on appId in addition to contentHash (#6719)

* Check vouch status on appId in addition to contentHash

* Simplify var expansion

* Merge #6725

* Add updated MethodDecoding from master
2017-10-12 20:26:41 +02:00
arkpar
eee0e6f295 v1.7.5 stabilized 2017-10-12 20:25:29 +02:00
Marek Kotewicz
822fa82056 Merge pull request #6724 from paritytech/backporting
Backport #6694 and #6718
2017-10-12 18:53:01 +02:00
arkpar
83825befea Fixed RETURNDATA out of bounds check 2017-10-12 18:21:48 +02:00
arkpar
4e93537e45 Prevent going offline when restoring or taking snapshot 2017-10-12 18:16:23 +02:00
arkpar
c0e32bac0e v1.7.5 2017-10-12 18:11:51 +02:00
GitLab Build Bot
8b459b7bd3 [ci skip] js-precompiled 20171012-142240 2017-10-12 14:28:14 +00:00
Jaco Greeff
51f830e922 Trigger beta js build & release (#6721) 2017-10-12 15:56:11 +02:00
Jaco Greeff
6d0dfa6dab Backport #6710 & #6714 (#6715)
* [ci skip] js-precompiled 20171011-183908

* Fix estimate gas if from is not provided. (#6714)

* Display vouched overlay on dapps (#6710)

* Remove .only

* Add vouch overlays to dapps

* Cleanup address

* Only run where we have a contentHash

* GitLab kickstart
2017-10-11 22:21:12 +02:00
GitLab Build Bot
d30c715134 js-precompiled 20171011-183908 2017-10-11 21:23:12 +02:00
Arkadiy Paronyan
a21db69686 Backporting #6686 and #6691 (#6712)
* v1.7.4

* Fixed potential exp len overflow (#6686)

* Fix warp sync blockers detection
2017-10-11 20:34:36 +02:00
Jaco Greeff
9afd1006ed Backport #6707, #6702 & #6701 (#6713)
* #6707

* Fix default values for address input (#6701)

* #6702

* Remove .only
2017-10-11 20:34:25 +02:00
83 changed files with 1833 additions and 695 deletions

View File

@@ -11,7 +11,7 @@ variables:
CARGOFLAGS: ""
CI_SERVER_NAME: "GitLab CI"
cache:
key: "$CI_BUILD_STAGE/$CI_BUILD_REF_NAME"
key: "$CI_BUILD_STAGE-$CI_BUILD_REF_NAME"
untracked: true
linux-stable:
stage: build
@@ -95,7 +95,7 @@ linux-snap:
paths:
- scripts/parity_*_amd64.snap
name: "stable-x86_64-unknown-snap-gnu_parity"
allow_failure: true
allow_failure: true
linux-stable-debian:
stage: build
image: parity/rust-debian:gitlab-ci
@@ -246,7 +246,7 @@ linux-i686:
- strip target/$PLATFORM/release/parity
- strip target/$PLATFORM/release/parity-evm
- strip target/$PLATFORM/release/ethstore
- strip target/$PLATFORM/release/ethkey
- strip target/$PLATFORM/release/ethkey
- strip target/$PLATFORM/release/parity
- md5sum target/$PLATFORM/release/parity > parity.md5
- export SHA3=$(target/$PLATFORM/release/parity tools hash target/$PLATFORM/release/parity)
@@ -488,7 +488,7 @@ darwin:
name: "x86_64-apple-darwin_parity"
windows:
cache:
key: "%CI_BUILD_STAGE%/%CI_BUILD_REF_NAME%"
key: "%CI_BUILD_STAGE%-%CI_BUILD_REF_NAME%"
untracked: true
stage: build
only:

114
Cargo.lock generated
View File

@@ -1,7 +1,3 @@
[root]
name = "using_queue"
version = "0.1.0"
[[package]]
name = "advapi32-sys"
version = "0.2.0"
@@ -190,7 +186,7 @@ source = "git+https://github.com/paritytech/bn#c9831a8d10d55045692394cbc10efe032
dependencies = [
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -258,7 +254,7 @@ dependencies = [
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"quine-mc_cluskey 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (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.28 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -277,7 +273,7 @@ dependencies = [
name = "common-types"
version = "0.1.0"
dependencies = [
"ethcore-util 1.7.3",
"ethcore-util 1.7.13",
"ethjson 0.1.0",
"rlp 0.2.0",
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -429,12 +425,12 @@ dependencies = [
"gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ethabi"
version = "2.0.0"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -466,7 +462,7 @@ dependencies = [
"common-types 0.1.0",
"crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethabi 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ethash 1.7.0",
"ethcore-bloom-journal 0.1.0",
"ethcore-devtools 1.7.0",
@@ -476,7 +472,7 @@ dependencies = [
"ethcore-ipc-nano 1.7.0",
"ethcore-logger 1.7.0",
"ethcore-stratum 1.7.0",
"ethcore-util 1.7.3",
"ethcore-util 1.7.13",
"ethjson 0.1.0",
"ethkey 0.2.0",
"ethstore 0.1.0",
@@ -497,7 +493,7 @@ dependencies = [
"rlp 0.2.0",
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (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.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"stats 0.1.0",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -545,7 +541,7 @@ name = "ethcore-ipc"
version = "1.7.0"
dependencies = [
"ethcore-devtools 1.7.0",
"ethcore-util 1.7.3",
"ethcore-util 1.7.13",
"nanomsg 0.5.1 (git+https://github.com/paritytech/nanomsg.rs.git?branch=parity-1.7)",
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -594,7 +590,7 @@ dependencies = [
"ethcore-ipc 1.7.0",
"ethcore-ipc-codegen 1.7.0",
"ethcore-ipc-nano 1.7.0",
"ethcore-util 1.7.3",
"ethcore-util 1.7.13",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"nanomsg 0.5.1 (git+https://github.com/paritytech/nanomsg.rs.git?branch=parity-1.7)",
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -611,7 +607,7 @@ dependencies = [
"ethcore-ipc 1.7.0",
"ethcore-ipc-codegen 1.7.0",
"ethcore-network 1.7.0",
"ethcore-util 1.7.3",
"ethcore-util 1.7.13",
"evm 0.1.0",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -650,7 +646,7 @@ dependencies = [
"ethcore-devtools 1.7.0",
"ethcore-io 1.7.0",
"ethcore-logger 1.7.0",
"ethcore-util 1.7.3",
"ethcore-util 1.7.13",
"ethcrypto 0.1.0",
"ethkey 0.2.0",
"igd 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -663,7 +659,7 @@ dependencies = [
"rlp 0.2.0",
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"tiny-keccak 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -674,14 +670,14 @@ name = "ethcore-secretstore"
version = "1.0.0"
dependencies = [
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethabi 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.7.0",
"ethcore-devtools 1.7.0",
"ethcore-ipc 1.7.0",
"ethcore-ipc-codegen 1.7.0",
"ethcore-ipc-nano 1.7.0",
"ethcore-logger 1.7.0",
"ethcore-util 1.7.3",
"ethcore-util 1.7.13",
"ethcrypto 0.1.0",
"ethkey 0.2.0",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -711,7 +707,7 @@ dependencies = [
"ethcore-ipc-codegen 1.7.0",
"ethcore-ipc-nano 1.7.0",
"ethcore-logger 1.7.0",
"ethcore-util 1.7.3",
"ethcore-util 1.7.13",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-macros 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
@@ -724,7 +720,7 @@ dependencies = [
[[package]]
name = "ethcore-util"
version = "1.7.3"
version = "1.7.13"
dependencies = [
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -774,7 +770,7 @@ name = "ethjson"
version = "0.1.0"
dependencies = [
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-util 1.7.3",
"ethcore-util 1.7.13",
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -855,7 +851,7 @@ dependencies = [
"ethcore-ipc-nano 1.7.0",
"ethcore-light 1.7.0",
"ethcore-network 1.7.0",
"ethcore-util 1.7.3",
"ethcore-util 1.7.13",
"ethkey 0.2.0",
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -874,7 +870,7 @@ dependencies = [
"bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"common-types 0.1.0",
"ethcore-util 1.7.3",
"ethcore-util 1.7.13",
"ethjson 0.1.0",
"evmjit 1.7.0",
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -891,7 +887,7 @@ version = "0.1.0"
dependencies = [
"docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.7.0",
"ethcore-util 1.7.3",
"ethcore-util 1.7.13",
"ethjson 0.1.0",
"evm 0.1.0",
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1057,7 +1053,7 @@ dependencies = [
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rotor 0.6.3 (git+https://github.com/tailhook/rotor)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
"spmc 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1075,7 +1071,7 @@ dependencies = [
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1136,7 +1132,7 @@ version = "1.7.0"
dependencies = [
"ethcore-ipc 1.7.0",
"ethcore-ipc-codegen 1.7.0",
"ethcore-util 1.7.3",
"ethcore-util 1.7.13",
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1520,7 +1516,7 @@ dependencies = [
name = "native-contract-generator"
version = "0.1.0"
dependencies = [
"ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethabi 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"heck 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1529,8 +1525,8 @@ name = "native-contracts"
version = "0.1.0"
dependencies = [
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-util 1.7.3",
"ethabi 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-util 1.7.13",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"native-contract-generator 0.1.0",
]
@@ -1621,7 +1617,7 @@ dependencies = [
"num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1630,7 +1626,7 @@ version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1658,7 +1654,7 @@ dependencies = [
"num-bigint 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
"num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1735,7 +1731,7 @@ dependencies = [
[[package]]
name = "parity"
version = "1.7.3"
version = "1.7.13"
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)",
@@ -1755,7 +1751,7 @@ dependencies = [
"ethcore-logger 1.7.0",
"ethcore-secretstore 1.0.0",
"ethcore-stratum 1.7.0",
"ethcore-util 1.7.3",
"ethcore-util 1.7.13",
"ethkey 0.2.0",
"ethsync 1.7.0",
"fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1801,7 +1797,7 @@ dependencies = [
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-devtools 1.7.0",
"ethcore-util 1.7.3",
"ethcore-util 1.7.13",
"fetch 0.1.0",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
@@ -1844,8 +1840,8 @@ dependencies = [
name = "parity-hash-fetch"
version = "1.7.0"
dependencies = [
"ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-util 1.7.3",
"ethabi 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-util 1.7.13",
"fetch 0.1.0",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1863,7 +1859,7 @@ version = "1.7.0"
dependencies = [
"cid 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.7.0",
"ethcore-util 1.7.3",
"ethcore-util 1.7.13",
"jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"multihash 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1876,7 +1872,7 @@ version = "0.1.0"
dependencies = [
"ethcore 1.7.0",
"ethcore-io 1.7.0",
"ethcore-util 1.7.3",
"ethcore-util 1.7.13",
"ethkey 0.2.0",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.2.0",
@@ -1906,7 +1902,7 @@ dependencies = [
"ethcore-ipc 1.7.0",
"ethcore-light 1.7.0",
"ethcore-logger 1.7.0",
"ethcore-util 1.7.3",
"ethcore-util 1.7.13",
"ethcrypto 0.1.0",
"ethjson 0.1.0",
"ethkey 0.2.0",
@@ -1948,7 +1944,7 @@ dependencies = [
name = "parity-rpc-client"
version = "1.4.0"
dependencies = [
"ethcore-util 1.7.3",
"ethcore-util 1.7.13",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-ws-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
@@ -1984,7 +1980,7 @@ name = "parity-ui"
version = "1.7.0"
dependencies = [
"parity-ui-dev 1.7.0",
"parity-ui-precompiled 1.4.0 (git+https://github.com/paritytech/js-precompiled.git?branch=beta)",
"parity-ui-precompiled 1.4.0 (git+https://github.com/paritytech/js-precompiled.git?branch=stable)",
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1998,7 +1994,7 @@ dependencies = [
[[package]]
name = "parity-ui-precompiled"
version = "1.4.0"
source = "git+https://github.com/paritytech/js-precompiled.git?branch=beta#657a34702890778823bbd0f2aeb6e015b2053744"
source = "git+https://github.com/paritytech/js-precompiled.git?branch=stable#bbc5b6c3e3a43ab21b94d37922cdbed766c7b5c8"
dependencies = [
"parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -2007,11 +2003,11 @@ dependencies = [
name = "parity-updater"
version = "1.7.0"
dependencies = [
"ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethabi 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.7.0",
"ethcore-ipc 1.7.0",
"ethcore-ipc-codegen 1.7.0",
"ethcore-util 1.7.3",
"ethcore-util 1.7.13",
"ethsync 1.7.0",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"ipc-common-types 1.7.0",
@@ -2367,7 +2363,7 @@ name = "rpc-cli"
version = "1.4.0"
dependencies = [
"bigint 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-util 1.7.3",
"ethcore-util 1.7.13",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-rpc 1.7.0",
"parity-rpc-client 1.4.0",
@@ -2382,7 +2378,7 @@ dependencies = [
"gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -2398,7 +2394,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustc-serialize"
version = "0.3.19"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -2668,7 +2664,7 @@ version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_pos 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
"term 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2679,7 +2675,7 @@ name = "syntex_pos"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -2689,7 +2685,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_errors 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_pos 0.58.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2901,7 +2897,7 @@ name = "toml"
version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -2997,6 +2993,10 @@ dependencies = [
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "using_queue"
version = "0.1.0"
[[package]]
name = "utf8-ranges"
version = "1.0.0"
@@ -3163,7 +3163,7 @@ dependencies = [
"checksum env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e3856f1697098606fc6cb97a93de88ca3f3bc35bb878c725920e6e82ecf05e83"
"checksum error-chain 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd5c82c815138e278b8dcdeffc49f27ea6ffb528403e9dea4194f2e3dd40b143"
"checksum eth-secp256k1 0.5.6 (git+https://github.com/paritytech/rust-secp256k1)" = "<none>"
"checksum ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0c3d62319ee0f35abf20afe8859dd2668195912614346447bb2dee9fb8da7c62"
"checksum ethabi 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8a6af84a622f9169efc6ead7f70043ca2058ef261c50e5e1e2ac60ccd381e386"
"checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa"
"checksum flate2 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "3eeb481e957304178d2e782f2da1257f1434dfecbae883bafb61ada2a9fea3bb"
"checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344"
@@ -3250,7 +3250,7 @@ dependencies = [
"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
"checksum parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1d06f6ee0fda786df3784a96ee3f0629f529b91cbfb7d142f6410e6bcd1ce2c"
"checksum parity-tokio-ipc 0.1.5 (git+https://github.com/nikvolf/parity-tokio-ipc)" = "<none>"
"checksum parity-ui-precompiled 1.4.0 (git+https://github.com/paritytech/js-precompiled.git?branch=beta)" = "<none>"
"checksum parity-ui-precompiled 1.4.0 (git+https://github.com/paritytech/js-precompiled.git?branch=stable)" = "<none>"
"checksum parity-wasm 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "51104c8b8da5cd0ebe0ab765dfab37bc1927b4a01a3d870b0fe09d9ee65e35ea"
"checksum parity-wordlist 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "52142d717754f7ff7ef0fc8da1bdce4f302dd576fb9bf8b727d6a5fdef33348d"
"checksum parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aebb68eebde2c99f89592d925288600fde220177e46b5c9a91ca218d245aeedf"
@@ -3291,7 +3291,7 @@ dependencies = [
"checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a"
"checksum rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3058a43ada2c2d0b92b3ae38007a2d0fa5e9db971be260e0171408a4ff471c95"
"checksum rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0ceb8ce7a5e520de349e1fa172baeba4a9e8d5ef06c47471863530bc4972ee1e"
"checksum rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "6159e4e6e559c81bd706afe9c8fd68f547d3e851ce12e76b1de7914bab61691b"
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
"checksum rustc_version 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1e114e275f7c9b5d50bb52b28f9aac1921209f02aa6077c8b255e21eefaf8ffa"
"checksum schannel 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4e45ac5e9e4698c1c138d2972bedcd90b81fe1efeba805449d2bdd54512de5f9"

View File

@@ -1,7 +1,7 @@
[package]
description = "Parity Ethereum client"
name = "parity"
version = "1.7.3"
version = "1.7.13"
license = "GPL-3.0"
authors = ["Parity Technologies <admin@parity.io>"]
build = "build.rs"

View File

@@ -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;
#[derive(Debug, Clone)]
/// A time checker.

View File

@@ -12,7 +12,7 @@ rustc_version = "0.1"
[dependencies]
parity-ui-dev = { path = "../../js", optional = true }
# This is managed by the js/scripts/release.sh script on CI - keep it in a single line
parity-ui-precompiled = { git = "https://github.com/paritytech/js-precompiled.git", optional = true, branch = "beta" }
parity-ui-precompiled = { git = "https://github.com/paritytech/js-precompiled.git", optional = true, branch = "stable" }
[features]
no-precompiled-js = ["parity-ui-dev"]

View File

@@ -30,9 +30,6 @@ pub enum ContractCreateResult {
/// Returned when contract creation failed.
/// VM doesn't have to know the reason.
Failed,
/// Returned when contract creation failed.
/// VM doesn't have to know the reason.
FailedInStaticCall,
/// Reverted with REVERT.
Reverted(U256, ReturnData),
}

View File

@@ -313,20 +313,24 @@ impl<Cost: CostType> Interpreter<Cost> {
let init_off = stack.pop_back();
let init_size = stack.pop_back();
let address_scheme = if instruction == instructions::CREATE { CreateContractAddress::FromSenderAndNonce } else { CreateContractAddress::FromSenderAndCodeHash };
let create_gas = provided.expect("`provided` comes through Self::exec from `Gasometer::get_gas_cost_mem`; `gas_gas_mem_cost` guarantees `Some` when instruction is `CALL`/`CALLCODE`/`DELEGATECALL`/`CREATE`; this is `CREATE`; qed");
let contract_code = self.mem.read_slice(init_off, init_size);
let can_create = ext.balance(&params.address)? >= endowment && ext.depth() < ext.schedule().max_depth;
if ext.is_static() {
return Err(evm::Error::MutableCallInStaticContext);
}
// clear return data buffer before creating new call frame.
self.return_data = ReturnData::empty();
let can_create = ext.balance(&params.address)? >= endowment && ext.depth() < ext.schedule().max_depth;
if !can_create {
stack.push(U256::zero());
return Ok(InstructionResult::UnusedGas(create_gas));
}
let contract_code = self.mem.read_slice(init_off, init_size);
let address_scheme = if instruction == instructions::CREATE { CreateContractAddress::FromSenderAndNonce } else { CreateContractAddress::FromSenderAndCodeHash };
let create_result = ext.create(&create_gas.as_u256(), &endowment, contract_code, address_scheme);
return match create_result {
ContractCreateResult::Created(address, gas_left) => {
@@ -342,9 +346,6 @@ impl<Cost: CostType> Interpreter<Cost> {
stack.push(U256::zero());
Ok(InstructionResult::Ok)
},
ContractCreateResult::FailedInStaticCall => {
Err(evm::Error::MutableCallInStaticContext)
},
};
},
instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL | instructions::STATICCALL => {
@@ -565,7 +566,7 @@ impl<Cost: CostType> Interpreter<Cost> {
let source_offset = stack.peek(1);
let size = stack.peek(2);
let return_data_len = U256::from(self.return_data.len());
if source_offset.overflow_add(*size).0 > return_data_len {
if source_offset.saturating_add(*size) > return_data_len {
return Err(evm::Error::OutOfBounds);
}
}
@@ -906,8 +907,8 @@ mod tests {
use rustc_hex::FromHex;
use vmtype::VMType;
use factory::Factory;
use vm::{self, ActionParams, ActionValue};
use vm::tests::{FakeExt, test_finalize};
use action_params::{ActionParams, ActionValue};
use ::tests::{FakeExt, test_finalize};
#[test]
fn should_not_fail_on_tracing_mem() {
@@ -931,4 +932,25 @@ mod tests {
assert_eq!(ext.calls.len(), 1);
assert_eq!(gas_left, 248_212.into());
}
#[test]
fn should_not_overflow_returndata() {
let code = "6001600160000360003e00".from_hex().unwrap();
let mut params = ActionParams::default();
params.address = 5.into();
params.gas = 300_000.into();
params.gas_price = 1.into();
params.code = Some(Arc::new(code));
let mut ext = FakeExt::new_byzantium();
ext.balances.insert(5.into(), 1_000_000_000.into());
ext.tracing = true;
let err = {
let mut vm = Factory::new(VMType::Interpreter, 1).create(params.gas);
test_finalize(vm.exec(params, &mut ext)).err().unwrap()
};
assert_eq!(err, ::Error::OutOfBounds);
}
}

View File

@@ -55,18 +55,20 @@ pub struct FakeExt {
pub store: HashMap<H256, H256>,
pub suicides: HashSet<Address>,
pub calls: HashSet<FakeCall>,
sstore_clears: usize,
depth: usize,
blockhashes: HashMap<U256, H256>,
codes: HashMap<Address, Arc<Bytes>>,
logs: Vec<FakeLogEntry>,
info: EnvInfo,
schedule: Schedule,
balances: HashMap<Address, U256>,
pub sstore_clears: usize,
pub depth: usize,
pub blockhashes: HashMap<U256, H256>,
pub codes: HashMap<Address, Arc<Bytes>>,
pub logs: Vec<FakeLogEntry>,
pub info: EnvInfo,
pub schedule: Schedule,
pub balances: HashMap<Address, U256>,
pub is_static: bool,
pub tracing: bool,
}
// similar to the normal `finalize` function, but ignoring NeedsReturn.
fn test_finalize(res: Result<GasLeft, evm::Error>) -> Result<U256, evm::Error> {
pub fn test_finalize(res: Result<GasLeft, evm::Error>) -> Result<U256, evm::Error> {
match res {
Ok(GasLeft::Known(gas)) => Ok(gas),
Ok(GasLeft::NeedsReturn{..}) => unimplemented!(), // since ret is unimplemented.
@@ -78,6 +80,12 @@ impl FakeExt {
pub fn new() -> Self {
FakeExt::default()
}
pub fn new_byzantium() -> Self {
let mut ext = FakeExt::new();
ext.schedule = Schedule::new_byzantium();
ext
}
}
impl Default for Schedule {
@@ -168,7 +176,7 @@ impl Ext for FakeExt {
Ok(())
}
fn ret(self, _gas: &U256, _data: &ReturnData) -> evm::Result<U256> {
fn ret(self, _gas: &U256, _data: &ReturnData, _apply_state: bool) -> evm::Result<U256> {
unimplemented!();
}
@@ -192,6 +200,10 @@ impl Ext for FakeExt {
fn inc_sstore_clears(&mut self) {
self.sstore_clears += 1;
}
fn is_static(&self) -> bool {
self.is_static
}
}
#[test]
@@ -917,7 +929,6 @@ fn test_jumps(factory: super::Factory) {
assert_eq!(gas_left, U256::from(54_117));
}
evm_test!{test_calls: test_calls_jit, test_calls_int}
fn test_calls(factory: super::Factory) {
let code = "600054602d57600160005560006000600060006050610998610100f160006000600060006050610998610100f25b".from_hex().unwrap();
@@ -962,6 +973,27 @@ fn test_calls(factory: super::Factory) {
assert_eq!(ext.calls.len(), 2);
}
evm_test!{test_create_in_staticcall: test_create_in_staticcall_jit, test_create_in_staticcall_int}
fn test_create_in_staticcall(factory: super::Factory) {
let code = "600060006064f000".from_hex().unwrap();
let address = Address::from(0x155);
let mut params = ActionParams::default();
params.gas = U256::from(100_000);
params.code = Some(Arc::new(code));
params.address = address.clone();
let mut ext = FakeExt::new_byzantium();
ext.is_static = true;
let err = {
let mut vm = factory.create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap_err()
};
assert_eq!(err, ::Error::MutableCallInStaticContext);
assert_eq!(ext.calls.len(), 0);
}
fn assert_set_contains<T : Debug + Eq + PartialEq + Hash>(set: &HashSet<T>, val: &T) {
let contains = set.contains(val);
if !contains {

View File

@@ -169,10 +169,6 @@ impl<'a> Runtime<'a> {
self.gas_counter = self.gas_limit - gas_left.low_u64();
Ok(Some((-1i32).into()))
},
ext::ContractCreateResult::FailedInStaticCall => {
trace!(target: "wasm", "runtime: create contract called in static context");
Err(interpreter::Error::Trap("CREATE in static context".to_owned()))
},
}
}

View File

@@ -173,24 +173,38 @@
"stateRoot": "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544"
},
"nodes": [
"enode://efe4f2493f4aff2d641b1db8366b96ddacfe13e7a6e9c8f8f8cf49f9cdba0fdf3258d8c8f8d0c5db529f8123c8f1d95f36d54d590ca1bb366a5818b9a4ba521c@163.172.187.252:30303",
"enode://6a868ced2dec399c53f730261173638a93a40214cf299ccf4d42a76e3fa54701db410669e8006347a4b3a74fa090bb35af0320e4bc8d04cf5b7f582b1db285f5@163.172.131.191:30303",
"enode://66a483383882a518fcc59db6c017f9cd13c71261f13c8d7e67ed43adbbc82a932d88d2291f59be577e9425181fc08828dc916fdd053af935a9491edf9d6006ba@212.47.247.103:30303",
"enode://cd6611461840543d5b9c56fbf088736154c699c43973b3a1a32390cf27106f87e58a818a606ccb05f3866de95a4fe860786fea71bf891ea95f234480d3022aa3@163.172.157.114:30303",
"enode://78b094cb27ceeecbe311bc278f4fde8b9a265db42d268c88484c94d7a2d19b82a1bd22dfd6c2bd4d90f9b05e6d42255e6eb85de15f73848ff82ed0be9cdf5202@52.233.198.218:30303",
"enode://00526537cb7e1aa6cf49714f0635fd0f608904d8d0693b949eea2dcdfdb0abbe4c794003a5fe57aa662d0a9215e8dfa4d2deb6ef0101c5e185e2617721813d43@40.65.122.44:30303",
"enode://4a456b4b6e6ee1f51389763e51b80fe04782c762445d96c32a96ebd34bd9178c1894924d5101123eacfd4f0fc4da25b5e1ee7f18832ac0bf4c6d6ac81442d698@40.71.6.49:3030",
"enode://68f85e7403976aa92318eff804cbe9bc988e0f5230d9d07ae4def030cbae16603262638e272d19875b7e5c54e296ba88ab6ec6e98face9e2537346c4dce78882@52.243.47.211:30303",
"enode://dc72806c3aa8fda207c8c018aba8d6cf143728b3628b6ded8d5e8cdeb8aa05cbd53f710ecd014c9a8f0d1e98f2874bff8afb15a229202f510a9c0258d1f6d109@159.203.210.80:30303",
"enode://5a62f19d35c0da8b576c9414568c728d4744e6e9d436c0f9db27456400011414f515871f13a6b8e0468534b5116cfe765d7630f680f1707a38467940a9f62511@45.55.33.62:30303",
"enode://605e04a43b1156966b3a3b66b980c87b7f18522f7f712035f84576016be909a2798a438b2b17b1a8c58db314d88539a77419ca4be36148c086900fba487c9d39@188.166.255.12:30303",
"enode://1d1f7bcb159d308eb2f3d5e32dc5f8786d714ec696bb2f7e3d982f9bcd04c938c139432f13aadcaf5128304a8005e8606aebf5eebd9ec192a1471c13b5e31d49@138.201.223.35:30303",
"enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303",
"enode://3f1d12044546b76342d59d4a05532c14b85aa669704bfe1f864fe079415aa2c02d743e03218e57a33fb94523adb54032871a6c51b2cc5514cb7c7e35b3ed0a99@13.93.211.84:30303",
"enode://78de8a0916848093c73790ead81d1928bec737d565119932b98c6b100d944b7a95e94f847f689fc723399d2e31129d182f7ef3863f2b4c820abbf3ab2722344d@191.235.84.50:30303",
"enode://158f8aab45f6d19c6cbf4a089c2670541a8da11978a2f90dbf6a502a4a3bab80d288afdbeb7ec0ef6d92de563767f3b1ea9e8e334ca711e9f8e2df5a0385e8e6@13.75.154.138:30303",
"enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303",
"enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303",
"enode://0cc5f5ffb5d9098c8b8c62325f3797f56509bff942704687b6530992ac706e2cb946b90a34f1f19548cd3c7baccbcaea354531e5983c7d1bc0dee16ce4b6440b@40.118.3.223:30305",
"enode://1c7a64d76c0334b0418c004af2f67c50e36a3be60b5e4790bdac0439d21603469a85fad36f2473c9a80eb043ae60936df905fa28f1ff614c3e5dc34f15dcd2dc@40.118.3.223:30308",
"enode://85c85d7143ae8bb96924f2b54f1b3e70d8c4d367af305325d30a61385a432f247d2c75c45c6b4a60335060d072d7f5b35dd1d4c45f76941f62a4f83b6e75daaf@40.118.3.223:30309",
"enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303",
"enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303",
"enode://4cd540b2c3292e17cff39922e864094bf8b0741fcc8c5dcea14957e389d7944c70278d872902e3d0345927f621547efa659013c400865485ab4bfa0c6596936f@138.201.144.135:30303",
"enode://01f76fa0561eca2b9a7e224378dd854278735f1449793c46ad0c4e79e8775d080c21dcc455be391e90a98153c3b05dcc8935c8440de7b56fe6d67251e33f4e3c@51.15.42.252:30303",
"enode://2c9059f05c352b29d559192fe6bca272d965c9f2290632a2cfda7f83da7d2634f3ec45ae3a72c54dd4204926fb8082dcf9686e0d7504257541c86fc8569bcf4b@163.172.171.38:30303",
"enode://efe4f2493f4aff2d641b1db8366b96ddacfe13e7a6e9c8f8f8cf49f9cdba0fdf3258d8c8f8d0c5db529f8123c8f1d95f36d54d590ca1bb366a5818b9a4ba521c@163.172.187.252:30303",
"enode://bcc7240543fe2cf86f5e9093d05753dd83343f8fda7bf0e833f65985c73afccf8f981301e13ef49c4804491eab043647374df1c4adf85766af88a624ecc3330e@136.243.154.244:30303",
"enode://ed4227681ca8c70beb2277b9e870353a9693f12e7c548c35df6bca6a956934d6f659999c2decb31f75ce217822eefca149ace914f1cbe461ed5a2ebaf9501455@88.212.206.70:30303",
"enode://cadc6e573b6bc2a9128f2f635ac0db3353e360b56deef239e9be7e7fce039502e0ec670b595f6288c0d2116812516ad6b6ff8d5728ff45eba176989e40dead1e@37.128.191.230:30303",
"enode://595a9a06f8b9bc9835c8723b6a82105aea5d55c66b029b6d44f229d6d135ac3ecdd3e9309360a961ea39d7bee7bac5d03564077a4e08823acc723370aace65ec@46.20.235.22:30303",
"enode://029178d6d6f9f8026fc0bc17d5d1401aac76ec9d86633bba2320b5eed7b312980c0a210b74b20c4f9a8b0b2bf884b111fa9ea5c5f916bb9bbc0e0c8640a0f56c@216.158.85.185:30303",
"enode://fdd1b9bb613cfbc200bba17ce199a9490edc752a833f88d4134bf52bb0d858aa5524cb3ec9366c7a4ef4637754b8b15b5dc913e4ed9fdb6022f7512d7b63f181@212.47.247.103:30303",
"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://fdd1b9bb613cfbc200bba17ce199a9490edc752a833f88d4134bf52bb0d858aa5524cb3ec9366c7a4ef4637754b8b15b5dc913e4ed9fdb6022f7512d7b63f181@212.47.247.103:30303"
],
"accounts": {
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },

View File

@@ -26,15 +26,21 @@
},
"validateScoreTransition": 1000000,
"eip155Transition": 1000000,
"validateStepTransition": 1500000
}
"validateStepTransition": 1500000,
"maximumUncleCountTransition": 5067000,
"maximumUncleCount": 0
}
}
},
"params": {
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x2A",
"validateReceiptsTransition" : 1000000
"validateReceiptsTransition" : 1000000,
"eip140Transition": 5067000,
"eip211Transition": 5067000,
"eip214Transition": 5067000,
"eip658Transition": 5067000
},
"genesis": {
"seal": {
@@ -49,15 +55,19 @@
"accounts": {
"0x0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
"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 } } } },
"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"
]
}

View File

@@ -268,7 +268,7 @@ impl AccountProvider {
/// Checks whether an account with a given address is present.
pub fn has_account(&self, address: Address) -> Result<bool, Error> {
Ok(self.accounts()?.iter().any(|&a| a == address))
Ok(self.sstore.account_ref(&address).is_ok() && !self.blacklisted_accounts.contains(&address))
}
/// Returns addresses of all accounts.

View File

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

View File

@@ -98,18 +98,17 @@ impl Pricer for ModexpPricer {
let exp_len = read_len();
let mod_len = read_len();
let max_len = U256::from(u32::max_value() / 2);
if base_len > max_len || mod_len > max_len {
return U256::max_value();
if mod_len.is_zero() && base_len.is_zero() {
return U256::zero()
}
let base_len = base_len.low_u64();
let exp_len = exp_len.low_u64();
let mod_len = mod_len.low_u64();
let m = max(mod_len, base_len);
if m == 0 {
return U256::zero();
let max_len = U256::from(u32::max_value() / 2);
if base_len > max_len || mod_len > max_len || exp_len > max_len {
return U256::max_value();
}
let (base_len, exp_len, mod_len) = (base_len.low_u64(), exp_len.low_u64(), mod_len.low_u64());
let m = max(mod_len, base_len);
// read fist 32-byte word of the exponent.
let exp_low = if base_len + 96 >= input.len() as u64 { U256::zero() } else {
let mut buf = [0; 32];
@@ -121,7 +120,11 @@ impl Pricer for ModexpPricer {
let adjusted_exp_len = Self::adjusted_exp_len(exp_len, exp_low);
(Self::mult_complexity(m) * max(adjusted_exp_len, 1) / self.divisor as u64).into()
let (gas, overflow) = Self::mult_complexity(m).overflowing_mul(max(adjusted_exp_len, 1));
if overflow {
return U256::max_value();
}
(gas / self.divisor as u64).into()
}
}
@@ -130,8 +133,7 @@ impl ModexpPricer {
let bit_index = if exp_low.is_zero() { 0 } else { (255 - exp_low.leading_zeros()) as u64 };
if len <= 32 {
bit_index
}
else {
} else {
8 * (len - 32) + bit_index
}
}
@@ -712,6 +714,32 @@ mod tests {
native: ethereum_builtin("modexp"),
activate_at: 0,
};
// test for potential gas cost multiplication overflow
{
let input = FromHex::from_hex("0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000003b27bafd00000000000000000000000000000000000000000000000000000000503c8ac3").unwrap();
let expected_cost = U256::max_value();
assert_eq!(f.cost(&input[..]), expected_cost.into());
}
// test for potential exp len overflow
{
let input = FromHex::from_hex("\
00000000000000000000000000000000000000000000000000000000000000ff\
2a1e530000000000000000000000000000000000000000000000000000000000\
0000000000000000000000000000000000000000000000000000000000000000"
).unwrap();
let mut output = vec![0u8; 32];
let expected = FromHex::from_hex("0000000000000000000000000000000000000000000000000000000000000000").unwrap();
let expected_cost = U256::max_value();
f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should fail");
assert_eq!(output, expected);
assert_eq!(f.cost(&input[..]), expected_cost.into());
}
// fermat's little theorem example.
{
let input = FromHex::from_hex("\

View File

@@ -970,9 +970,11 @@ impl Client {
/// Tick the client.
// TODO: manage by real events.
pub fn tick(&self) {
pub fn tick(&self, prevent_sleep: bool) {
self.check_garbage();
self.check_snooze();
if !prevent_sleep {
self.check_snooze();
}
}
fn check_garbage(&self) {
@@ -1079,7 +1081,7 @@ impl Client {
if !self.liveness.load(AtomicOrdering::Relaxed) {
self.liveness.store(true, AtomicOrdering::Relaxed);
self.notify(|n| n.start());
trace!(target: "mode", "wake_up: Waking.");
info!(target: "mode", "wake_up: Waking.");
}
}
@@ -1089,11 +1091,11 @@ impl Client {
if self.queue_info().total_queue_size() <= MAX_QUEUE_SIZE_TO_SLEEP_ON {
self.liveness.store(false, AtomicOrdering::Relaxed);
self.notify(|n| n.stop());
trace!(target: "mode", "sleep: Sleeping.");
info!(target: "mode", "sleep: Sleeping.");
} else {
trace!(target: "mode", "sleep: Cannot sleep - syncing ongoing.");
info!(target: "mode", "sleep: Cannot sleep - syncing ongoing.");
// TODO: Consider uncommenting.
//*self.last_activity.lock() = Some(Instant::now());
//(*self.sleep_state.lock()).last_activity = Some(Instant::now());
}
}
}
@@ -1190,12 +1192,12 @@ impl BlockChainClient for Client {
}
fn estimate_gas(&self, t: &SignedTransaction, block: BlockId) -> Result<U256, CallError> {
const UPPER_CEILING: u64 = 1_000_000_000_000u64;
let (mut upper, env_info) = {
let (mut upper, max_upper, env_info) = {
let mut env_info = self.env_info(block).ok_or(CallError::StatePruned)?;
let initial_upper = env_info.gas_limit;
env_info.gas_limit = UPPER_CEILING.into();
(initial_upper, env_info)
let init = env_info.gas_limit;
let max = init * U256::from(10);
env_info.gas_limit = max;
(init, max, env_info)
};
// that's just a copy of the state.
@@ -1216,9 +1218,7 @@ impl BlockChainClient for Client {
};
if !cond(upper)? {
// impossible at block gas limit - try `UPPER_CEILING` instead.
// TODO: consider raising limit by powers of two.
upper = UPPER_CEILING.into();
upper = max_upper;
if !cond(upper)? {
trace!(target: "estimate_gas", "estimate_gas failed with {}", upper);
let err = ExecutionError::Internal(format!("Requires higher than upper limit of {}", upper));
@@ -1786,7 +1786,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;
@@ -1801,7 +1801,7 @@ impl MiningBlockChainClient for Client {
fn reopen_block(&self, block: ClosedBlock) -> OpenBlock {
let engine = &*self.engine;
let mut block = block.reopen(engine);
let max_uncles = engine.maximum_uncle_count();
let max_uncles = engine.maximum_uncle_count(block.header().number());
if block.uncles().len() < max_uncles {
let chain = self.chain.read();
let h = chain.best_block_hash();

View File

@@ -49,12 +49,16 @@ mod finality;
pub struct AuthorityRoundParams {
/// Gas limit divisor.
pub gas_limit_bound_divisor: U256,
/// Time to wait before next block or authority switching.
pub step_duration: Duration,
/// Block reward.
pub block_reward: U256,
/// Namereg contract address.
pub registrar: Address,
/// 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<u64>,
/// Valid validators.
@@ -67,13 +71,24 @@ pub struct AuthorityRoundParams {
pub validate_step_transition: u64,
/// Immediate transitions.
pub immediate_transitions: bool,
/// 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<ethjson::spec::AuthorityRoundParams> 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 {
gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(),
step_duration: Duration::from_secs(p.step_duration.into()),
step_duration: step_duration_usize as u16,
validators: new_validator_set(p.validators),
block_reward: p.block_reward.map_or_else(U256::zero, Into::into),
registrar: p.registrar.map_or_else(Address::new, Into::into),
@@ -82,6 +97,8 @@ impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
eip155_transition: p.eip155_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),
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),
}
}
}
@@ -91,40 +108,83 @@ impl From<ethjson::spec::AuthorityRoundParams> 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))
.map(Duration::from_secs);
match expected_seconds {
Some(step_end) if step_end > now => step_end - now,
Some(_) => 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);
}
}
fn is_future(&self, given: usize) -> bool {
if given > self.load() + 1 {
// Make absolutely sure that the given step is correct.
self.calibrate();
given > self.load() + 1
fn check_future(&self, given: usize) -> Result<(), Option<OutOfBounds<u64>>> {
const REJECTED_STEP_DRIFT: usize = 4;
// Verify if the step is correct.
if given <= self.load() {
return Ok(());
}
// Make absolutely sure that the given step is incorrect.
self.calibrate();
let current = self.load();
// reject blocks too far in the future
if given > current + REJECTED_STEP_DRIFT {
Err(None)
// wait a bit for blocks in near future
} else if given > current {
let d = self.duration as u64;
Err(Some(OutOfBounds {
min: None,
max: Some(d * current as u64),
found: d * given as u64,
}))
} else {
false
Ok(())
}
}
}
// 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,
@@ -229,6 +289,8 @@ pub struct AuthorityRound {
validate_step_transition: u64,
epoch_manager: Mutex<EpochManager>,
immediate_transitions: bool,
maximum_uncle_count_transition: u64,
maximum_uncle_count: usize,
}
// header-chain validator.
@@ -239,9 +301,11 @@ struct EpochVerifier {
impl super::EpochVerifier for EpochVerifier {
fn verify_light(&self, header: &Header) -> Result<(), Error> {
// Validate the timestamp
verify_timestamp(&*self.step, header_step(header)?)?;
// always check the seal since it's fast.
// nothing heavier to do.
verify_external(header, &self.subchain_validators, &*self.step, |_| {})
verify_external(header, &self.subchain_validators)
}
fn check_finality_proof(&self, proof: &[u8]) -> Option<Vec<H256>> {
@@ -266,7 +330,7 @@ impl super::EpochVerifier for EpochVerifier {
//
// `verify_external` checks that signature is correct and author == signer.
if header.seal().len() != 2 { return None }
otry!(verify_external(header, &self.subchain_validators, &*self.step, |_| {}).ok());
otry!(verify_external(header, &self.subchain_validators).ok());
let newly_finalized = otry!(finality_checker.push_hash(header.hash(), header.author().clone()).ok());
finalized.extend(newly_finalized);
@@ -276,16 +340,6 @@ impl super::EpochVerifier for EpochVerifier {
}
}
// Report misbehavior
#[derive(Debug)]
#[allow(dead_code)]
enum Report {
// Malicious behavior
Malicious(Address, BlockNumber, Bytes),
// benign misbehavior
Benign(Address, BlockNumber),
}
fn header_step(header: &Header) -> Result<usize, ::rlp::DecoderError> {
UntrustedRlp::new(&header.seal().get(0).expect("was either checked with verify_block_basic or is genesis; has 2 fields; qed (Make sure the spec file has a correct genesis seal)")).as_val()
}
@@ -304,28 +358,35 @@ fn is_step_proposer(validators: &ValidatorSet, bh: &H256, step: usize, address:
step_proposer(validators, bh, step) == *address
}
fn verify_external<F: Fn(Report)>(header: &Header, validators: &ValidatorSet, step: &Step, report: F)
-> Result<(), Error>
{
fn verify_timestamp(step: &Step, header_step: usize) -> Result<(), BlockError> {
match step.check_future(header_step) {
Err(None) => {
trace!(target: "engine", "verify_timestamp: block from the future");
Err(BlockError::InvalidSeal.into())
},
Err(Some(oob)) => {
// NOTE This error might be returned only in early stage of verification (Stage 1).
// Returning it further won't recover the sync process.
trace!(target: "engine", "verify_timestamp: block too early");
Err(BlockError::TemporarilyInvalid(oob).into())
},
Ok(_) => Ok(()),
}
}
fn verify_external(header: &Header, validators: &ValidatorSet) -> Result<(), Error> {
let header_step = header_step(header)?;
// Give one step slack if step is lagging, double vote is still not possible.
if step.is_future(header_step) {
trace!(target: "engine", "verify_block_external: block from the future");
report(Report::Benign(*header.author(), header.number()));
Err(BlockError::InvalidSeal)?
} else {
let proposer_signature = header_signature(header)?;
let correct_proposer = validators.get(header.parent_hash(), header_step);
let is_invalid_proposer = *header.author() != correct_proposer ||
!verify_address(&correct_proposer, &proposer_signature, &header.bare_hash())?;
let proposer_signature = header_signature(header)?;
let correct_proposer = validators.get(header.parent_hash(), header_step);
let is_invalid_proposer = *header.author() != correct_proposer ||
!verify_address(&correct_proposer, &proposer_signature, &header.bare_hash())?;
if is_invalid_proposer {
trace!(target: "engine", "verify_block_external: bad proposer for step: {}", header_step);
Err(EngineError::NotProposer(Mismatch { expected: correct_proposer, found: header.author().clone() }))?
} else {
Ok(())
}
if is_invalid_proposer {
trace!(target: "engine", "verify_block_external: bad proposer for step: {}", header_step);
Err(EngineError::NotProposer(Mismatch { expected: correct_proposer, found: header.author().clone() }))?
} else {
Ok(())
}
}
@@ -357,8 +418,12 @@ impl AsMillis for Duration {
impl AuthorityRound {
/// Create a new instance of AuthorityRound engine.
pub fn new(params: CommonParams, our_params: AuthorityRoundParams, builtins: BTreeMap<Address, Builtin>) -> Result<Arc<Self>, 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 {
params: params,
@@ -381,6 +446,8 @@ impl AuthorityRound {
validate_step_transition: our_params.validate_step_transition,
epoch_manager: Mutex::new(EpochManager::blank()),
immediate_transitions: our_params.immediate_transitions,
maximum_uncle_count_transition: our_params.maximum_uncle_count_transition,
maximum_uncle_count: our_params.maximum_uncle_count,
});
// Do not initialize timeouts for tests.
@@ -414,9 +481,15 @@ impl IoHandler<()> for TransitionHandler {
fn timeout(&self, io: &IoContext<()>, timer: TimerToken) {
if timer == ENGINE_TIMEOUT_TOKEN {
if let Some(engine) = self.engine.upgrade() {
engine.step();
let remaining = engine.step.duration_remaining();
io.register_timer_once(ENGINE_TIMEOUT_TOKEN, remaining.as_millis())
// NOTE we might be lagging by couple of steps in case the timeout
// has not been called fast enough.
// Make sure to advance up to the actual step.
while engine.step.duration_remaining().as_millis() == 0 {
engine.step();
}
let next_run_at = engine.step.duration_remaining().as_millis() >> 2;
io.register_timer_once(ENGINE_TIMEOUT_TOKEN, next_run_at)
.unwrap_or_else(|e| warn!(target: "engine", "Failed to restart consensus step timer: {}.", e))
}
}
@@ -455,10 +528,19 @@ impl Engine for AuthorityRound {
]
}
fn maximum_uncle_count(&self, block: BlockNumber) -> usize {
if block >= self.maximum_uncle_count_transition {
self.maximum_uncle_count
} else {
// fallback to default value
2
}
}
fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) {
// 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);
header.set_gas_limit({
let gas_limit = parent.gas_limit().clone();
let bound_divisor = self.gas_limit_bound_divisor;
@@ -471,21 +553,38 @@ impl Engine for AuthorityRound {
}
fn seals_internally(&self) -> Option<bool> {
// TODO: accept a `&Call` here so we can query the validator set.
Some(self.signer.read().is_some())
}
/// Attempt to seal the block internally.
///
/// This operation is synchronous and may (quite reasonably) not be available, in which `false` will
/// be returned.
fn generate_seal(&self, block: &ExecutedBlock) -> Seal {
/// 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, 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 {
debug!(target: "engine", "Aborting seal generation. The step has changed in the meantime. {:?} != {:?}",
header.difficulty(), expected_diff);
return Seal::None;
}
if parent_step > step.into() {
warn!(target: "engine", "Aborting seal generation for invalid step: {} > {}", parent_step, step);
return Seal::None;
}
// fetch correct validator set for current epoch, taking into account
// finality of previous transitions.
let active_set;
@@ -512,6 +611,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);
@@ -526,6 +632,7 @@ impl Engine for AuthorityRound {
trace!(target: "engine", "generate_seal: {} not a proposer for step {}.",
header.author(), step);
}
Seal::None
}
@@ -580,15 +687,28 @@ impl Engine for AuthorityRound {
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
if header.seal().len() != self.seal_fields() {
trace!(target: "engine", "verify_block_basic: wrong number of seal fields");
Err(From::from(BlockError::InvalidSealArity(
return Err(From::from(BlockError::InvalidSealArity(
Mismatch { expected: self.seal_fields(), found: header.seal().len() }
)))
} else if header.number() >= self.validate_score_transition && *header.difficulty() >= U256::from(U128::max_value()) {
Err(From::from(BlockError::DifficultyOutOfBounds(
)));
}
if header.number() >= self.validate_score_transition && *header.difficulty() >= U256::from(U128::max_value()) {
return Err(From::from(BlockError::DifficultyOutOfBounds(
OutOfBounds { min: None, max: Some(U256::from(U128::max_value())), found: *header.difficulty() }
)))
} else {
Ok(())
)));
}
// TODO [ToDr] Should this go from epoch manager?
// If yes then probably benign reporting needs to be moved further in the verification.
let set_number = header.number();
match verify_timestamp(&*self.step, header_step(header)?) {
Err(BlockError::InvalidSeal) => {
self.validators.report_benign(header.author(), set_number, header.number());
Err(BlockError::InvalidSeal.into())
}
Err(e) => Err(e.into()),
Ok(()) => Ok(()),
}
}
@@ -606,13 +726,15 @@ impl Engine for AuthorityRound {
}
let parent_step = header_step(parent)?;
// TODO [ToDr] Should this go from epoch manager?
let set_number = header.number();
// Ensure header is from the step after parent.
if step == parent_step
|| (header.number() >= self.validate_step_transition && step <= parent_step) {
trace!(target: "engine", "Multiple blocks proposed for step {}.", parent_step);
self.validators.report_malicious(header.author(), header.number(), header.number(), Default::default());
self.validators.report_malicious(header.author(), set_number, header.number(), Default::default());
Err(EngineError::DoubleVote(header.author().clone()))?;
}
// Report skipped primaries.
@@ -624,7 +746,7 @@ impl Engine for AuthorityRound {
let skipped_primary = step_proposer(&*self.validators, &parent.hash(), s);
// Do not report this signer.
if skipped_primary != me {
self.validators.report_benign(&skipped_primary, header.number(), header.number());
self.validators.report_benign(&skipped_primary, set_number, header.number());
}
// Stop reporting once validators start repeating.
if !reported.insert(skipped_primary) { break; }
@@ -645,9 +767,8 @@ impl Engine for AuthorityRound {
// fetch correct validator set for current epoch, taking into account
// finality of previous transitions.
let active_set;
let (validators, set_number) = if self.immediate_transitions {
(&*self.validators, header.number())
let validators = if self.immediate_transitions {
&*self.validators
} else {
// get correct validator set for epoch.
let client = match self.client.read().as_ref().and_then(|weak| weak.upgrade()) {
@@ -665,19 +786,12 @@ impl Engine for AuthorityRound {
}
active_set = epoch_manager.validators().clone();
(&active_set as &_, epoch_manager.epoch_transition_number)
};
let report = |report| match report {
Report::Benign(address, block_number) =>
self.validators.report_benign(&address, set_number, block_number),
Report::Malicious(address, block_number, proof) =>
self.validators.report_malicious(&address, set_number, block_number, proof),
&active_set as &_
};
// verify signature against fixed list, but reports should go to the
// contract itself.
verify_external(header, validators, &*self.step, report)
verify_external(header, validators)
}
fn genesis_epoch_data(&self, header: &Header, call: &Call) -> Result<Vec<u8>, String> {
@@ -924,17 +1038,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("1".sha3().into(), "1").unwrap();
let addr2 = tap.insert_account("0".sha3().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 => {}
}
}
}
}
@@ -985,8 +1133,7 @@ mod tests {
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
assert!(engine.verify_block_external(&header, None).is_ok());
header.set_seal(vec![encode(&5usize).into_vec(), encode(&(&*signature as &[u8])).into_vec()]);
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
assert!(engine.verify_block_external(&header, None).is_err());
assert!(engine.verify_block_basic(&header, None).is_err());
}
#[test]
@@ -1018,16 +1165,19 @@ mod tests {
let last_benign = Arc::new(AtomicUsize::new(0));
let params = AuthorityRoundParams {
gas_limit_bound_divisor: U256::from_str("400").unwrap(),
step_duration: Default::default(),
block_reward: Default::default(),
registrar: 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,
eip155_transition: 0,
immediate_transitions: true,
maximum_uncle_count_transition: 0,
maximum_uncle_count: 0,
};
let aura = AuthorityRound::new(Default::default(), params, Default::default()).unwrap();
let mut parent_header: Header = Header::default();
@@ -1047,4 +1197,76 @@ mod tests {
assert!(aura.verify_block_family(&header, &parent_header, None).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 {
gas_limit_bound_divisor: 5.into(),
block_reward: Default::default(),
registrar: 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,
eip155_transition: 0,
immediate_transitions: true,
maximum_uncle_count_transition: 1,
maximum_uncle_count: 0,
};
let aura = AuthorityRound::new(Default::default(), params, Default::default()).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(),
eip155_transition: 0,
registrar: Default::default(),
gas_limit_bound_divisor: 5.into(),
};
AuthorityRound::new(Default::default(), params, Default::default()).unwrap();
}
}

View File

@@ -133,7 +133,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) {
@@ -320,7 +320,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());
}
}

View File

@@ -18,6 +18,7 @@ use std::collections::BTreeMap;
use util::{Address, HashMap};
use builtin::Builtin;
use engines::{Engine, Seal};
use header::Header;
use spec::CommonParams;
use block::ExecutedBlock;
@@ -58,8 +59,8 @@ impl Engine for InstantSeal {
fn seals_internally(&self) -> Option<bool> { Some(true) }
fn generate_seal(&self, _block: &ExecutedBlock) -> Seal {
Seal::Regular(Vec::new())
fn generate_seal(&self, block: &ExecutedBlock, _parent: &Header) -> Seal {
if block.fields().transactions.is_empty() { Seal::None } else { Seal::Regular(Vec::new()) }
}
}
@@ -81,7 +82,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());
}
}

View File

@@ -203,7 +203,8 @@ pub trait Engine : Sync + Send {
/// Some intrinsic operation parameters; by default they take their value from the `spec()`'s `engine_params`.
fn maximum_extra_data_size(&self) -> usize { self.params().maximum_extra_data_size }
/// Maximum number of uncles a block is allowed to declare.
fn maximum_uncle_count(&self) -> usize { 2 }
fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 0 }
/// The number of generations back that uncles can be.
fn maximum_uncle_age(&self) -> usize { 6 }
/// The nonce with which accounts begin at given block.
@@ -242,7 +243,10 @@ pub trait Engine : Sync + Send {
///
/// This operation is synchronous and may (quite reasonably) not be available, in which None will
/// be returned.
fn generate_seal(&self, _block: &ExecutedBlock) -> Seal { Seal::None }
///
/// 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: &ExecutedBlock, _parent: &Header) -> Seal { Seal::None }
/// Phase 1 quick block verification. Only does checks that are cheap. `block` (the header's full block)
/// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import.

View File

@@ -57,6 +57,8 @@ impl Engine for NullEngine {
&self.builtins
}
fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 2 }
fn schedule(&self, _block_number: BlockNumber) -> Schedule {
Schedule::new_homestead()
}

View File

@@ -391,7 +391,7 @@ impl Engine for Tendermint {
fn builtins(&self) -> &BTreeMap<Address, Builtin> { &self.builtins }
fn maximum_uncle_count(&self) -> usize { 0 }
fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 0 }
fn maximum_uncle_age(&self) -> usize { 0 }
@@ -427,7 +427,10 @@ impl Engine for Tendermint {
}
/// Attempt to seal generate a proposal seal.
fn generate_seal(&self, block: &ExecutedBlock) -> Seal {
///
/// 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, _parent: &Header) -> Seal {
let header = block.header();
let author = header.author();
// Only proposer can generate seal if None was generated.
@@ -681,7 +684,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!()

View File

@@ -159,22 +159,36 @@ mod tests {
// Make sure reporting can be done.
client.miner().set_gas_floor_target(1_000_000.into());
client.miner().set_engine_signer(v1, "".into()).unwrap();
// Check a block that is a bit in future, reject it but don't report the validator.
let mut header = Header::default();
let seal = vec![encode(&5u8).into_vec(), encode(&(&H520::default() as &[u8])).into_vec()];
header.set_seal(seal);
header.set_author(v1);
header.set_number(2);
header.set_parent_hash(client.chain_info().best_block_hash);
// `reportBenign` when the designated proposer releases block from the future (bad clock).
assert!(client.engine().verify_block_external(&header, None).is_err());
client.engine().step();
assert_eq!(client.chain_info().best_block_number, 0);
// Now create one that is more in future. That one should be rejected and validator should be reported.
let mut header = Header::default();
let seal = vec![encode(&8u8).into_vec(), encode(&(&H520::default() as &[u8])).into_vec()];
header.set_seal(seal);
header.set_author(v1);
header.set_number(2);
header.set_parent_hash(client.chain_info().best_block_hash);
// `reportBenign` when the designated proposer releases block from the future (bad clock).
assert!(client.engine().verify_block_basic(&header, None).is_err());
// Seal a block.
client.engine().step();
assert_eq!(client.chain_info().best_block_number, 1);
// Check if the unresponsive validator is `disliked`.
assert_eq!(client.call_contract(BlockId::Latest, validator_contract, "d8f2e0bf".from_hex().unwrap()).unwrap().to_hex(), "0000000000000000000000007d577a597b2742b498cb5cf0c26cdcd726d39e6e");
assert_eq!(
client.call_contract(BlockId::Latest, validator_contract, "d8f2e0bf".from_hex().unwrap()).unwrap().to_hex(),
"0000000000000000000000007d577a597b2742b498cb5cf0c26cdcd726d39e6e"
);
// Simulate a misbehaving validator by handling a double proposal.
let header = client.best_block_header().decode();
assert!(client.engine().verify_block_family(&header, &header, None).is_err());

View File

@@ -157,6 +157,8 @@ pub enum BlockError {
InvalidReceiptsRoot(Mismatch<H256>),
/// Timestamp header field is invalid.
InvalidTimestamp(OutOfBounds<u64>),
/// Timestamp header field is too far in future.
TemporarilyInvalid(OutOfBounds<u64>),
/// Log bloom header field is invalid.
InvalidLogBloom(Mismatch<LogBloom>),
/// Parent hash field of header is invalid; this is an invalid error indicating a logic flaw in the codebase.
@@ -202,6 +204,7 @@ impl fmt::Display for BlockError {
InvalidGasLimit(ref oob) => format!("Invalid gas limit: {}", oob),
InvalidReceiptsRoot(ref mis) => format!("Invalid receipts trie root in header: {}", mis),
InvalidTimestamp(ref oob) => format!("Invalid timestamp in header: {}", oob),
TemporarilyInvalid(ref oob) => format!("Future timestamp in header: {}", oob),
InvalidLogBloom(ref oob) => format!("Invalid log bloom in header: {}", oob),
InvalidParentHash(ref mis) => format!("Invalid parent hash: {}", mis),
InvalidNumber(ref mis) => format!("Invalid number in header: {}", mis),

View File

@@ -217,7 +217,7 @@ impl Engine for Arc<Ethash> {
} else if block_number < self.ethash_params.eip150_transition {
Schedule::new_homestead()
} else {
/// There's no max_code_size transition so we tie it to eip161abc
// There's no max_code_size transition so we tie it to eip161abc
let max_code_size = if block_number >= self.ethash_params.eip161abc_transition { self.ethash_params.max_code_size as usize } else { usize::max_value() };
let mut schedule = Schedule::new_post_eip150(
max_code_size,
@@ -238,6 +238,8 @@ impl Engine for Arc<Ethash> {
}
}
fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 2 }
fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, mut gas_ceil_target: U256) {
let difficulty = self.calculate_difficulty(header, parent);
if header.number() >= self.ethash_params.max_gas_limit_transition && gas_ceil_target > self.ethash_params.max_gas_limit {

View File

@@ -31,7 +31,7 @@ use std::path::Path;
use super::spec::*;
/// Most recent fork block that we support on Mainnet.
pub const FORK_SUPPORTED_FOUNDATION: u64 = 2675000;
pub const FORK_SUPPORTED_FOUNDATION: u64 = 4370000;
/// Most recent fork block that we support on Ropsten.
pub const FORK_SUPPORTED_ROPSTEN: u64 = 10;

View File

@@ -234,7 +234,6 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E>
Ok(FinalizationResult{ gas_left, apply_state: false, return_data }) => {
ContractCreateResult::Reverted(gas_left, return_data)
},
Err(evm::Error::MutableCallInStaticContext) => ContractCreateResult::FailedInStaticCall,
_ => ContractCreateResult::Failed,
}
}

View File

@@ -499,7 +499,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.");
@@ -621,10 +627,6 @@ impl Miner {
condition: Option<TransactionCondition>,
transaction_queue: &mut BanningTransactionQueue,
) -> Vec<Result<TransactionImportResult, Error>> {
let accounts = self.accounts.as_ref()
.and_then(|provider| provider.accounts().ok())
.map(|accounts| accounts.into_iter().collect::<HashSet<_>>());
let best_block_header = client.best_block_header().decode();
let insertion_time = client.chain_info().best_block_number;
@@ -643,8 +645,8 @@ impl Miner {
Err(e)
},
Ok(transaction) => {
let origin = accounts.as_ref().and_then(|accounts| {
match accounts.contains(&transaction.sender()) {
let origin = self.accounts.as_ref().and_then(|accounts| {
match accounts.has_account(transaction.sender()).unwrap_or(false) {
true => Some(TransactionOrigin::Local),
false => None,
}

View File

@@ -23,7 +23,7 @@ use error::*;
use client::{Client, ClientConfig, ChainNotify};
use miner::Miner;
use snapshot::ManifestData;
use snapshot::{ManifestData, RestorationStatus};
use snapshot::service::{Service as SnapshotService, ServiceParams as SnapServiceParams};
use std::sync::atomic::AtomicBool;
@@ -175,7 +175,11 @@ impl IoHandler<ClientIoMessage> for ClientIoHandler {
fn timeout(&self, _io: &IoContext<ClientIoMessage>, timer: TimerToken) {
match timer {
CLIENT_TICK_TIMER => self.client.tick(),
CLIENT_TICK_TIMER => {
use snapshot::SnapshotService;
let snapshot_restoration = if let RestorationStatus::Ongoing{..} = self.snapshot.status() { true } else { false };
self.client.tick(snapshot_restoration)
},
SNAPSHOT_TICK_TIMER => self.snapshot.tick(),
_ => warn!("IO service triggered unregistered timer '{}'", timer),
}

View File

@@ -190,7 +190,7 @@ fn imports_block_sequence() {
#[test]
fn can_collect_garbage() {
let client = generate_dummy_client(100);
client.tick();
client.tick(true);
assert!(client.blockchain_cache_info().blocks < 100 * 1024);
}

View File

@@ -500,7 +500,7 @@ impl<K: Kind> VerificationQueue<K> {
Err(err) => {
match err {
// Don't mark future blocks as bad.
Error::Block(BlockError::InvalidTimestamp(ref e)) if e.max.is_some() => {},
Error::Block(BlockError::TemporarilyInvalid(_)) => {},
_ => {
self.verification.bad.lock().insert(h.clone());
}

View File

@@ -110,9 +110,14 @@ pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &
engine.verify_block_family(&header, &parent, Some(bytes))?;
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();
@@ -225,11 +230,20 @@ pub fn verify_header_params(header: &Header, engine: &Engine, is_full: bool) ->
return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: Some(maximum_extra_data_size), found: header.extra_data().len() })));
}
if is_full {
let max_time = get_time().sec as u64 + 30;
if header.timestamp() > max_time {
return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: Some(max_time), min: None, found: header.timestamp() })))
const ACCEPTABLE_DRIFT_SECS: u64 = 15;
let max_time = get_time().sec as u64 + ACCEPTABLE_DRIFT_SECS;
let invalid_threshold = max_time + ACCEPTABLE_DRIFT_SECS * 9;
let timestamp = header.timestamp();
if timestamp > invalid_threshold {
return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: Some(max_time), min: None, found: timestamp })))
}
if timestamp > max_time {
return Err(From::from(BlockError::TemporarilyInvalid(OutOfBounds { max: Some(max_time), min: None, found: timestamp })))
}
}
Ok(())
}
@@ -293,11 +307,13 @@ mod tests {
}
}
fn check_fail_timestamp(result: Result<(), Error>) {
fn check_fail_timestamp(result: Result<(), Error>, temp: bool) {
let name = if temp { "TemporarilyInvalid" } else { "InvalidTimestamp" };
match result {
Err(Error::Block(BlockError::InvalidTimestamp(_))) => (),
Err(other) => panic!("Block verification failed.\nExpected: InvalidTimestamp\nGot: {:?}", other),
Ok(_) => panic!("Block verification failed.\nExpected: InvalidTimestamp\nGot: Ok"),
Err(Error::Block(BlockError::InvalidTimestamp(_))) if !temp => (),
Err(Error::Block(BlockError::TemporarilyInvalid(_))) if temp => (),
Err(other) => panic!("Block verification failed.\nExpected: {}\nGot: {:?}", name, other),
Ok(_) => panic!("Block verification failed.\nExpected: {}\nGot: Ok", name),
}
}
@@ -556,11 +572,17 @@ mod tests {
header = good.clone();
header.set_timestamp(2450000000);
check_fail_timestamp(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine));
check_fail_timestamp(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine), false);
header = good.clone();
header.set_timestamp(get_time().sec as u64 + 40);
check_fail_timestamp(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine));
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), true);
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);
@@ -571,7 +593,7 @@ mod tests {
let mut bad_uncles = good_uncles.clone();
bad_uncles.push(good_uncle1.clone());
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &bad_uncles), engine, &bc),
TooManyUncles(OutOfBounds { max: Some(engine.maximum_uncle_count()), min: None, found: bad_uncles.len() }));
TooManyUncles(OutOfBounds { max: Some(engine.maximum_uncle_count(header.number())), min: None, found: bad_uncles.len() }));
header = good.clone();
bad_uncles = vec![ good_uncle1.clone(), good_uncle1.clone() ];

View File

@@ -122,6 +122,13 @@ impl<T> DiskDirectory<T> where T: KeyFileManager {
Ok(hasher.finish())
}
fn last_modification_date(&self) -> Result<u64, Error> {
use std::time::{Duration, UNIX_EPOCH};
let duration = fs::metadata(&self.path)?.modified()?.duration_since(UNIX_EPOCH).unwrap_or(Duration::default());
let timestamp = duration.as_secs() ^ (duration.subsec_nanos() as u64);
Ok(timestamp)
}
/// all accounts found in keys directory
fn files_content(&self) -> Result<HashMap<PathBuf, SafeAccount>, Error> {
// it's not done using one iterator cause
@@ -226,7 +233,7 @@ impl<T> KeyDirectory for DiskDirectory<T> where T: KeyFileManager {
}
fn unique_repr(&self) -> Result<u64, Error> {
self.files_hash()
self.last_modification_date()
}
}

View File

@@ -18,6 +18,7 @@ use std::collections::{BTreeMap, HashMap};
use std::mem;
use std::path::PathBuf;
use parking_lot::{Mutex, RwLock};
use std::time::{Instant, Duration};
use crypto::KEY_ITERATIONS;
use random::Random;
@@ -28,6 +29,8 @@ 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,
@@ -241,7 +244,12 @@ pub struct EthMultiStore {
// order lock: cache, then vaults
cache: RwLock<BTreeMap<StoreAccountRef, Vec<SafeAccount>>>,
vaults: Mutex<HashMap<String, Box<VaultKeyDirectory>>>,
dir_hash: Mutex<Option<u64>>,
timestamp: Mutex<Timestamp>,
}
struct Timestamp {
dir_hash: Option<u64>,
last_checked: Instant,
}
impl EthMultiStore {
@@ -257,20 +265,27 @@ impl EthMultiStore {
vaults: Mutex::new(HashMap::new()),
iterations: iterations,
cache: Default::default(),
dir_hash: Default::default(),
timestamp: Mutex::new(Timestamp {
dir_hash: None,
last_checked: Instant::now(),
}),
};
store.reload_accounts()?;
Ok(store)
}
fn reload_if_changed(&self) -> Result<(), Error> {
let mut last_dir_hash = self.dir_hash.lock();
let dir_hash = Some(self.dir.unique_repr()?);
if *last_dir_hash == dir_hash {
return Ok(())
let mut last_timestamp = self.timestamp.lock();
let now = Instant::now();
if (now - last_timestamp.last_checked) > Duration::from_secs(REFRESH_TIME_SEC) {
let dir_hash = Some(self.dir.unique_repr()?);
last_timestamp.last_checked = now;
if last_timestamp.dir_hash == dir_hash {
return Ok(())
}
self.reload_accounts()?;
last_timestamp.dir_hash = dir_hash;
}
self.reload_accounts()?;
*last_dir_hash = dir_hash;
Ok(())
}
@@ -451,11 +466,11 @@ impl SimpleSecretStore for EthMultiStore {
}
fn account_ref(&self, address: &Address) -> Result<StoreAccountRef, Error> {
use std::collections::Bound;
self.reload_if_changed()?;
self.cache.read().keys()
.find(|r| &r.address == address)
.cloned()
.ok_or(Error::InvalidAccount)
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())
}
fn accounts(&self) -> Result<Vec<StoreAccountRef>, Error> {

View File

@@ -16,6 +16,7 @@
use std::hash::{Hash, Hasher};
use std::path::PathBuf;
use std::cmp::Ordering;
use ethkey::{Address, Message, Signature, Secret, Public};
use Error;
use json::{Uuid, OpaqueKeyFile};
@@ -32,12 +33,24 @@ pub enum SecretVaultRef {
}
/// Stored account reference
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, Clone, PartialEq, Eq, Ord)]
pub struct StoreAccountRef {
/// Vault reference
pub vault: SecretVaultRef,
/// Account address
pub address: Address,
/// Vault reference
pub vault: SecretVaultRef,
}
impl PartialOrd for StoreAccountRef {
fn partial_cmp(&self, other: &StoreAccountRef) -> Option<Ordering> {
Some(self.address.cmp(&other.address).then_with(|| self.vault.cmp(&other.vault)))
}
}
impl ::std::borrow::Borrow<Address> for StoreAccountRef {
fn borrow(&self) -> &Address {
&self.address
}
}
/// Simple Secret Store API

12
js/package-lock.json generated
View File

@@ -428,7 +428,7 @@
"glob": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
"integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=",
"dev": true,
"requires": {
"fs.realpath": "1.0.0",
@@ -810,7 +810,7 @@
"glob": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
"integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=",
"dev": true,
"requires": {
"fs.realpath": "1.0.0",
@@ -4414,7 +4414,7 @@
"glob": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
"integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=",
"dev": true,
"requires": {
"fs.realpath": "1.0.0",
@@ -11958,7 +11958,7 @@
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=",
"dev": true,
"requires": {
"is-fullwidth-code-point": "2.0.0",
@@ -12923,7 +12923,7 @@
"async": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz",
"integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==",
"integrity": "sha1-hDGQ/WtzV6C54clW7d3V7IRitU0=",
"dev": true,
"requires": {
"lodash": "4.17.2"
@@ -13328,7 +13328,7 @@
"commander": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz",
"integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ=="
"integrity": "sha1-FXFS/R56bI2YpbcVzzdt+SgARWM="
},
"detect-indent": {
"version": "5.0.0",

View File

@@ -1 +1,2 @@
// test script 10
// test script 11
// beta trigger 01

View File

@@ -75,7 +75,13 @@ export function bytesToAscii (bytes) {
}
export function asciiToHex (string) {
return '0x' + string.split('').map((s) => s.charCodeAt(0).toString(16)).join('');
let result = '0x';
for (let i = 0; i < string.length; ++i) {
result += ('0' + string.charCodeAt(i).toString(16)).substr(-2);
}
return result;
}
export function padRight (input, length) {

View File

@@ -68,6 +68,14 @@ describe('api/util/format', () => {
it('correctly converts a non-empty string', () => {
expect(asciiToHex('abc')).to.equal('0x616263');
});
it('correctly converts where charCode < 0x10', () => {
expect(
asciiToHex(
[32, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0].map((v) => String.fromCharCode(v)).join('')
)
).to.equal('0x20100f0e0d0c0b0a09080706050403020100');
});
});
describe('hexToAscii', () => {

View File

@@ -29,3 +29,4 @@ export signaturereg from './signaturereg.json';
export smsverification from './sms-verification.json';
export tokenreg from './tokenreg.json';
export foundationWallet from './foundation-multisig-wallet.json';
export vouchfor from './vouchfor.json';

View File

@@ -0,0 +1 @@
[{"constant":true,"inputs":[],"name":"certifier","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_what","type":"bytes32"}],"name":"vouch","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_what","type":"bytes32"},{"name":"_index","type":"uint256"}],"name":"vouched","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_what","type":"bytes32"},{"name":"_index","type":"uint256"}],"name":"unvouch","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"},{"name":"","type":"uint256"}],"name":"vouchers","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_certifier","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"who","type":"address"},{"indexed":false,"name":"what","type":"bytes32"}],"name":"Vouched","type":"event"}]

View File

@@ -105,7 +105,7 @@ export default class BadgeReg {
]);
})
.then(([ title, icon ]) => {
title = bytesToHex(title);
title = bytesToHex(title).replace(/(00)+$/, '');
title = title === ZERO32 ? null : hexToAscii(title);
if (bytesToHex(icon) === ZERO32) {

View File

@@ -29,6 +29,7 @@
left: 0;
opacity: 1;
padding: 1.5em;
cursor: pointer;
position: fixed;
right: 50%;
z-index: 100;

View File

@@ -43,8 +43,13 @@ export default class Application extends Component {
contract: PropTypes.object
};
state = {
hideWarning: false
};
render () {
const { isLoading, contract } = this.props;
const { hideWarning } = this.state;
if (isLoading) {
return (
@@ -62,9 +67,15 @@ export default class Application extends Component {
<Actions />
<Tokens />
<div className={ styles.warning }>
WARNING: The token registry is experimental. Please ensure that you understand the steps, risks, benefits & consequences of registering a token before doing so. A non-refundable fee of { api.util.fromWei(contract.fee).toFormat(3) }<small>ETH</small> is required for all registrations.
</div>
{
hideWarning
? null
: (
<div className={ styles.warning } onClick={ this.handleHideWarning }>
WARNING: The token registry is experimental. Please ensure that you understand the steps, risks, benefits & consequences of registering a token before doing so. A non-refundable fee of { api.util.fromWei(contract.fee).toFormat(3) }<small>ETH</small> is required for all registrations.
</div>
)
}
</div>
);
}
@@ -74,4 +85,8 @@ export default class Application extends Component {
muiTheme
};
}
handleHideWarning = () => {
this.setState({ hideWarning: true });
}
}

View File

@@ -17,35 +17,35 @@
import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl';
import { walletSourceURL } from '~/contracts/code/wallet';
// import { walletSourceURL } from '~/contracts/code/wallet';
import { RadioButtons } from '~/ui';
const TYPES = [
{
label: (
<FormattedMessage
id='createWallet.type.multisig.label'
defaultMessage='Multi-Sig wallet'
/>
),
key: 'MULTISIG',
description: (
<FormattedMessage
id='createWallet.type.multisig.description'
defaultMessage='Create/Deploy a {link} Wallet'
values={ {
link: (
<a href={ walletSourceURL } target='_blank'>
<FormattedMessage
id='createWallet.type.multisig.link'
defaultMessage='standard multi-signature'
/>
</a>
)
} }
/>
)
},
// {
// label: (
// <FormattedMessage
// id='createWallet.type.multisig.label'
// defaultMessage='Multi-Sig wallet'
// />
// ),
// key: 'MULTISIG',
// description: (
// <FormattedMessage
// id='createWallet.type.multisig.description'
// defaultMessage='Create/Deploy a {link} Wallet'
// values={ {
// link: (
// <a href={ walletSourceURL } target='_blank'>
// <FormattedMessage
// id='createWallet.type.multisig.link'
// defaultMessage='standard multi-signature'
// />
// </a>
// )
// } }
// />
// )
// },
{
label: (
<FormattedMessage

View File

@@ -59,7 +59,7 @@ const STEPS = {
export default class CreateWalletStore {
@observable step = null;
@observable txhash = null;
@observable walletType = 'MULTISIG';
@observable walletType = 'WATCH'; // 'MULTISIG';
@observable wallet = {
account: '',

View File

@@ -14,14 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export const fetchCertifiers = () => ({
type: 'fetchCertifiers'
});
export const fetchCertifications = (address) => ({
type: 'fetchCertifications', address
});
export const addCertification = (address, id, name, title, icon) => ({
type: 'addCertification', address, id, name, title, icon
});

View File

@@ -0,0 +1,343 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { range } from 'lodash';
import { addCertification, removeCertification } from './actions';
import { getLogger, LOG_KEYS } from '~/config';
import Contract from '~/api/contract';
import { bytesToHex, hexToAscii } from '~/api/util/format';
import Contracts from '~/contracts';
import CertifierABI from '~/contracts/abi/certifier.json';
import { querier } from './enhanced-querier';
const log = getLogger(LOG_KEYS.CertificationsMiddleware);
let self = null;
export default class CertifiersMonitor {
constructor (api, store) {
this._api = api;
this._name = 'Certifiers';
this._store = store;
this._contract = new Contract(this.api, CertifierABI);
this._contractEvents = [ 'Confirmed', 'Revoked' ]
.map((name) => this.contract.events.find((e) => e.name === name));
this.certifiers = {};
this.fetchedAccounts = {};
this.load();
}
static get () {
if (self) {
return self;
}
self = new CertifiersMonitor();
return self;
}
static init (api, store) {
if (!self) {
self = new CertifiersMonitor(api, store);
}
}
get api () {
return this._api;
}
get contract () {
return this._contract;
}
get contractEvents () {
return this._contractEvents;
}
get name () {
return this._name;
}
get store () {
return this._store;
}
get registry () {
return this._registry;
}
get registryEvents () {
return this._registryEvents;
}
checkFilters () {
this.checkCertifiersFilter();
this.checkRegistryFilter();
}
checkCertifiersFilter () {
if (!this.certifiersFilter) {
return;
}
this.api.eth.getFilterChanges(this.certifiersFilter)
.then((logs) => {
if (logs.length === 0) {
return;
}
const parsedLogs = this.contract.parseEventLogs(logs).filter((log) => log.params);
log.debug('received certifiers logs', parsedLogs);
const promises = parsedLogs.map((log) => {
const account = log.params.who.value;
const certifier = Object.values(this.certifiers).find((c) => c.address === log.address);
if (!certifier) {
log.warn('could not find the certifier', { certifiers: this.certifiers, log });
return Promise.resolve();
}
return this.fetchAccount(account, { ids: [ certifier.id ] });
});
return Promise.all(promises);
})
.catch((error) => {
console.error(error);
});
}
checkRegistryFilter () {
if (!this.registryFilter) {
return;
}
this.api.eth.getFilterChanges(this.registryFilter)
.then((logs) => {
if (logs.length === 0) {
return;
}
const parsedLogs = this.contract.parseEventLogs(logs).filter((log) => log.params);
const indexes = parsedLogs.map((log) => log.params && log.params.id.value.toNumber());
log.debug('received registry logs', parsedLogs);
return this.fetchElements(indexes);
})
.catch((error) => {
console.error(error);
});
}
/**
* Initial load of the Monitor.
* Fetch the contract from the Registry, and
* load the elements addresses
*/
load () {
const badgeReg = Contracts.get().badgeReg;
log.debug(`loading the ${this.name} monitor...`);
return badgeReg.getContract()
.then((registryContract) => {
this._registry = registryContract;
this._registryEvents = [ 'Registered', 'Unregistered', 'MetaChanged', 'AddressChanged' ]
.map((name) => this.registry.events.find((e) => e.name === name));
return this.registry.instance.badgeCount.call({});
})
.then((count) => {
log.debug(`found ${count.toFormat()} registered contracts for ${this.name}`);
return this.fetchElements(range(count.toNumber()));
})
.then(() => {
return this.setRegistryFilter();
})
.then(() => {
// Listen for new blocks
return this.api.subscribe('eth_blockNumber', (err) => {
if (err) {
return;
}
this.checkFilters();
});
})
.then(() => {
log.debug(`loaded the ${this.name} monitor!`, this.certifiers);
})
.catch((error) => {
log.error(error);
});
}
/**
* Fetch the given registered element
*/
fetchElements (indexes) {
const badgeReg = Contracts.get().badgeReg;
const { instance } = this.registry;
const sorted = indexes.sort();
const from = sorted[0];
const last = sorted[sorted.length - 1];
const limit = last - from + 1;
// Fetch the address, name and owner in one batch
return querier(this.api, { address: instance.address, from, limit }, instance.badge)
.then((results) => {
const certifiers = results
.map(([ address, name, owner ], index) => ({
address, owner,
id: index + from,
name: hexToAscii(bytesToHex(name).replace(/(00)+$/, ''))
}))
.reduce((certifiers, certifier) => {
const { id } = certifier;
if (!/^(0x)?0+$/.test(certifier.address)) {
certifiers[id] = certifier;
} else if (certifiers[id]) {
delete certifiers[id];
}
return certifiers;
}, {});
// Fetch the meta-data in serie
return Object.values(certifiers).reduce((promise, certifier) => {
return promise.then(() => badgeReg.fetchMeta(certifier.id))
.then((meta) => {
this.certifiers[certifier.id] = { ...certifier, ...meta };
});
}, Promise.resolve());
})
.then(() => log.debug('fetched certifiers', { certifiers: this.certifiers }))
// Fetch the know accounts in case it's an update of the certifiers
.then(() => this.fetchAccounts(Object.keys(this.fetchedAccounts), { ids: indexes, force: true }));
}
fetchAccounts (addresses, { ids = null, force = false } = {}) {
const newAddresses = force
? addresses
: addresses.filter((address) => !this.fetchedAccounts[address]);
if (newAddresses.length === 0) {
return Promise.resolve();
}
log.debug(`fetching values for "${addresses.join(' ; ')}" in ${this.name}...`);
return newAddresses
.reduce((promise, address) => {
return promise.then(() => this.fetchAccount(address, { ids }));
}, Promise.resolve())
.then(() => {
log.debug(`fetched values for "${addresses.join(' ; ')}" in ${this.name}!`);
})
.then(() => this.setCertifiersFilter());
}
fetchAccount (address, { ids = null } = {}) {
let certifiers = Object.values(this.certifiers);
// Only fetch values for the givens ids, if any
if (ids) {
certifiers = certifiers.filter((certifier) => ids.includes(certifier.id));
}
certifiers
.reduce((promise, certifier) => {
return promise
.then(() => {
return this.contract.at(certifier.address).instance.certified.call({}, [ address ]);
})
.then((certified) => {
const { id, title, icon, name } = certifier;
if (!certified) {
return this.store.dispatch(removeCertification(address, id));
}
log.debug('seen as certified', { address, id, name, icon });
this.store.dispatch(addCertification(address, id, name, title, icon));
});
}, Promise.resolve())
.then(() => {
this.fetchedAccounts[address] = true;
});
}
setCertifiersFilter () {
const accounts = Object.keys(this.fetchedAccounts);
const addresses = Object.values(this.certifiers).map((c) => c.address);
// The events have as first indexed data the account address
const topics = [
this.contractEvents.map((event) => '0x' + event.signature),
accounts
];
if (accounts.length === 0 || addresses.length === 0) {
return;
}
const promise = this.certifiersFilter
? this.api.eth.uninstallFilter(this.certifiersFilter)
: Promise.resolve();
log.debug('setting up registry filter', { topics, accounts, addresses });
return promise
.then(() => this.api.eth.newFilter({
fromBlock: 'latest',
toBlock: 'latest',
address: addresses,
topics
}))
.then((filterId) => {
this.certifiersFilter = filterId;
})
.catch((error) => {
console.error(error);
});
}
setRegistryFilter () {
const { address } = this.registry.instance;
const topics = [ this.registryEvents.map((event) => '0x' + event.signature) ];
log.debug('setting up registry filter', { topics, address });
return this.api.eth
.newFilter({
fromBlock: 'latest',
toBlock: 'latest',
address, topics
})
.then((filterId) => {
this.registryFilter = filterId;
})
.catch((error) => {
console.error(error);
});
}
}

View File

@@ -0,0 +1,96 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { padRight, padLeft } from '~/api/util/format';
/**
* Bytecode of this contract:
*
*
pragma solidity ^0.4.10;
contract Querier {
function Querier
(address addr, bytes32 sign, uint out_size, uint from, uint limit)
public
{
// The size is 32 bytes for each
// value, plus 32 bytes for the count
uint m_size = out_size * limit + 32;
bytes32 p_return;
uint p_in;
uint p_out;
assembly {
p_return := mload(0x40)
mstore(0x40, add(p_return, m_size))
mstore(p_return, limit)
p_in := mload(0x40)
mstore(0x40, add(p_in, 0x24))
mstore(p_in, sign)
p_out := add(p_return, 0x20)
}
for (uint i = from; i < from + limit; i++) {
assembly {
mstore(add(p_in, 0x4), i)
call(gas, addr, 0x0, p_in, 0x24, p_out, out_size)
p_out := add(p_out, out_size)
pop
}
}
assembly {
return (p_return, m_size)
}
}
}
*/
export const bytecode = '0x60606040523415600e57600080fd5b60405160a0806099833981016040528080519190602001805191906020018051919060200180519190602001805191505082810260200160008080806040519350848401604052858452604051602481016040528981529250505060208201855b858701811015609457806004840152878260248560008e5af15090870190600101606f565b8484f300';
export const querier = (api, { address, from, limit }, method) => {
const { outputs, signature } = method;
const outLength = 32 * outputs.length;
const callargs = [
padLeft(address, 32),
padRight(signature, 32),
padLeft(outLength, 32),
padLeft(from, 32),
padLeft(limit, 32)
].map((v) => v.slice(2)).join('');
const calldata = bytecode + callargs;
return api.eth.call({ data: calldata })
.then((result) => {
const data = result.slice(2);
const results = [];
for (let i = 0; i < limit; i++) {
const datum = data.substr(2 * (32 + i * outLength), 2 * outLength);
const decoded = method.decodeOutput('0x' + datum).map((t) => t.value);
results.push(decoded);
}
return results;
});
};

View File

@@ -14,222 +14,22 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { uniq, range, debounce } from 'lodash';
import { addCertification, removeCertification } from './actions';
import { getLogger, LOG_KEYS } from '~/config';
import Contract from '~/api/contract';
import Contracts from '~/contracts';
import CertifierABI from '~/contracts/abi/certifier.json';
const log = getLogger(LOG_KEYS.CertificationsMiddleware);
// TODO: move this to a more general place
const updatableFilter = (api, onFilter) => {
let filter = null;
const update = (address, topics) => {
if (filter) {
filter = filter.then((filterId) => {
api.eth.uninstallFilter(filterId);
});
}
filter = (filter || Promise.resolve())
.then(() => api.eth.newFilter({
fromBlock: 'latest',
toBlock: 'latest',
address,
topics
}))
.then((filterId) => {
onFilter(filterId);
return filterId;
})
.catch((err) => {
console.error('Failed to create certifications filter:', err);
});
return filter;
};
return update;
};
import Monitor from './certifiers.monitor';
export default class CertificationsMiddleware {
toMiddleware () {
const api = Contracts.get()._api;
const badgeReg = Contracts.get().badgeReg;
const contract = new Contract(api, CertifierABI);
const Confirmed = contract.events.find((e) => e.name === 'Confirmed');
const Revoked = contract.events.find((e) => e.name === 'Revoked');
return (store) => {
let certifiers = [];
let addresses = [];
let filterChanged = false;
let filter = null;
let badgeRegFilter = null;
let fetchCertifiersPromise = null;
const updateFilter = updatableFilter(api, (filterId) => {
filterChanged = true;
filter = filterId;
});
const badgeRegUpdateFilter = updatableFilter(api, (filterId) => {
filterChanged = true;
badgeRegFilter = filterId;
});
badgeReg
.getContract()
.then((badgeRegContract) => {
return badgeRegUpdateFilter(badgeRegContract.address, [ [
badgeRegContract.instance.Registered.signature,
badgeRegContract.instance.Unregistered.signature,
badgeRegContract.instance.MetaChanged.signature,
badgeRegContract.instance.AddressChanged.signature
] ]);
})
.then(() => {
shortFetchChanges();
api.subscribe('eth_blockNumber', (err) => {
if (err) {
return;
}
fetchChanges();
});
});
function onLogs (logs) {
logs = contract.parseEventLogs(logs);
logs.forEach((log) => {
const certifier = certifiers.find((c) => c.address === log.address);
if (!certifier) {
throw new Error(`Could not find certifier at ${log.address}.`);
}
const { id, name, title, icon } = certifier;
if (log.event === 'Revoked') {
store.dispatch(removeCertification(log.params.who.value, id));
} else {
store.dispatch(addCertification(log.params.who.value, id, name, title, icon));
}
});
}
function onBadgeRegLogs (logs) {
return badgeReg.getContract()
.then((badgeRegContract) => {
logs = badgeRegContract.parseEventLogs(logs);
const ids = logs.map((log) => log.params && log.params.id.value.toNumber());
return fetchCertifiers(uniq(ids));
});
}
function _fetchChanges () {
const method = filterChanged
? 'getFilterLogs'
: 'getFilterChanges';
filterChanged = false;
api.eth[method](badgeRegFilter)
.then(onBadgeRegLogs)
.catch((err) => {
console.error('Failed to fetch badge reg events:', err);
})
.then(() => api.eth[method](filter))
.then(onLogs)
.catch((err) => {
console.error('Failed to fetch new certifier events:', err);
});
}
const shortFetchChanges = debounce(_fetchChanges, 0.5 * 1000, { leading: true });
const fetchChanges = debounce(shortFetchChanges, 10 * 1000, { leading: true });
function fetchConfirmedEvents () {
return updateFilter(certifiers.map((c) => c.address), [
[ Confirmed.signature, Revoked.signature ],
addresses
]).then(() => shortFetchChanges());
}
function fetchCertifiers (ids = []) {
if (fetchCertifiersPromise) {
return fetchCertifiersPromise;
}
let fetchEvents = false;
const idsPromise = (certifiers.length === 0)
? badgeReg.certifierCount().then((count) => {
return range(count);
})
: Promise.resolve(ids);
fetchCertifiersPromise = idsPromise
.then((ids) => {
const promises = ids.map((id) => {
return badgeReg.fetchCertifier(id)
.then((cert) => {
if (!certifiers.some((c) => c.id === cert.id)) {
certifiers = certifiers.concat(cert);
fetchEvents = true;
}
})
.catch((err) => {
if (/does not exist/.test(err.toString())) {
return log.info(err.toString());
}
log.warn(`Could not fetch certifier ${id}:`, err);
});
});
return Promise
.all(promises)
.then(() => {
fetchCertifiersPromise = null;
if (fetchEvents) {
return fetchConfirmedEvents();
}
});
});
return fetchCertifiersPromise;
}
Monitor.init(api, store);
return (next) => (action) => {
switch (action.type) {
case 'fetchCertifiers':
fetchConfirmedEvents();
break;
case 'fetchCertifications':
const { address } = action;
if (!addresses.includes(address)) {
addresses = addresses.concat(address);
fetchConfirmedEvents();
}
break;
case 'setVisibleAccounts':
const _addresses = action.addresses || [];
const { addresses = [] } = action;
addresses = uniq(addresses.concat(_addresses));
fetchConfirmedEvents();
Monitor.get().fetchAccounts(addresses);
next(action);
break;

View File

@@ -20,24 +20,32 @@ export default (state = initialState, action) => {
if (action.type === 'addCertification') {
const { address, id, name, icon, title } = action;
const certifications = state[address] || [];
const certifierIndex = certifications.findIndex((c) => c.id === id);
const data = { id, name, icon, title };
const nextCertifications = certifications.slice();
if (certifications.some((c) => c.id === id)) {
return state;
if (certifierIndex >= 0) {
nextCertifications[certifierIndex] = data;
} else {
nextCertifications.push(data);
}
const newCertifications = certifications.concat({
id, name, icon, title
});
return { ...state, [address]: newCertifications };
return { ...state, [address]: nextCertifications };
}
if (action.type === 'removeCertification') {
const { address, id } = action;
const certifications = state[address] || [];
const certifierIndex = certifications.findIndex((c) => c.id === id);
const newCertifications = certifications.filter((c) => c.id !== id);
// Don't remove if not there
if (certifierIndex < 0) {
return state;
}
const newCertifications = certifications.slice();
newCertifications.splice(certifierIndex, 1);
return { ...state, [address]: newCertifications };
}

View File

@@ -54,13 +54,24 @@ export const watchRequest = (request) => (dispatch, getState) => {
dispatch(trackRequest(requestId, request));
};
export const trackRequest = (requestId, { transactionHash = null } = {}) => (dispatch, getState) => {
export const trackRequest = (requestId, { transactionHash = null, retries = 0 } = {}) => (dispatch, getState) => {
const { api } = getState();
trackRequestUtil(api, { requestId, transactionHash }, (error, _data = {}) => {
const data = { ..._data };
if (error) {
// Retry in 500ms if request not found, max 5 times
if (error.type === 'REQUEST_NOT_FOUND') {
if (retries > 5) {
return dispatch(deleteRequest(requestId));
}
return setTimeout(() => {
trackRequest(requestId, { transactionHash, retries: retries + 1 })(dispatch, getState);
}, 500);
}
console.error(error);
return dispatch(setRequest(requestId, { error }));
}

View File

@@ -115,9 +115,11 @@ export function loadTokensBasics (_tokenIndexes, options) {
const prevTokensIndexes = Object.values(tokens).map((t) => t.index);
// Only fetch tokens we don't have yet
const tokenIndexes = _tokenIndexes.filter((tokenIndex) => {
return !prevTokensIndexes.includes(tokenIndex);
});
const tokenIndexes = _tokenIndexes
.filter((tokenIndex) => {
return !prevTokensIndexes.includes(tokenIndex);
})
.sort();
const count = tokenIndexes.length;
@@ -130,10 +132,15 @@ export function loadTokensBasics (_tokenIndexes, options) {
return tokenReg.getContract()
.then((tokenRegContract) => {
let promise = Promise.resolve();
const first = tokenIndexes[0];
const last = tokenIndexes[tokenIndexes.length - 1];
for (let from = first; from <= last; from += limit) {
// No need to fetch `limit` elements
const lowerLimit = Math.min(limit, last - from + 1);
for (let start = 0; start < count; start += limit) {
promise = promise
.then(() => fetchTokensBasics(api, tokenRegContract, start, limit))
.then(() => fetchTokensBasics(api, tokenRegContract, from, lowerLimit))
.then((results) => {
results
.forEach((token) => {

View File

@@ -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);

View File

@@ -15,11 +15,12 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import BigNumber from 'bignumber.js';
import { pick } from 'lodash';
import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import TokenImage from '~/ui/TokenImage';
import TokenValue from './tokenValue';
import styles from './balance.css';
@@ -69,58 +70,19 @@ export class Balance extends Component {
const balanceValue = balance[tokenId];
const isEthToken = token.native;
const isFullToken = !showOnlyEth || isEthToken;
const hasBalance = (balanceValue instanceof BigNumber) && balanceValue.gt(0);
if (!hasBalance && !isEthToken) {
return null;
}
const bnf = new BigNumber(token.format || 1);
let decimals = 0;
if (bnf.gte(1000)) {
decimals = 3;
} else if (bnf.gte(100)) {
decimals = 2;
} else if (bnf.gte(10)) {
decimals = 1;
}
const rawValue = new BigNumber(balanceValue).div(bnf);
const value = rawValue.toFormat(decimals);
const classNames = [styles.balance];
let details = null;
if (isFullToken) {
classNames.push(styles.full);
details = [
<div
className={ styles.value }
key='value'
>
<span title={ `${rawValue.toFormat()} ${token.tag}` }>
{ value }
</span>
</div>,
<div
className={ styles.tag }
key='tag'
>
{ token.tag }
</div>
];
}
return (
<div
className={ classNames.join(' ') }
<TokenValue
key={ tokenId }
>
<TokenImage token={ token } />
{ details }
</div>
showOnlyEth={ showOnlyEth }
token={ token }
value={ balanceValue }
/>
);
})
.filter((node) => node);
@@ -155,11 +117,15 @@ export class Balance extends Component {
}
function mapStateToProps (state, props) {
const { balances, tokens } = state;
const { balances, tokens: allTokens } = state;
const { address } = props;
const balance = balances[address] || props.balance || {};
const tokenIds = Object.keys(balance);
const tokens = pick(allTokens, tokenIds);
return {
balance: balances[address] || props.balance || {},
balance,
tokens
};
}

View File

@@ -84,13 +84,13 @@ describe('ui/Balance', () => {
});
it('renders all the non-zero balances', () => {
expect(component.find('Connect(TokenImage)')).to.have.length(2);
expect(component.find('Connect(TokenValue)')).to.have.length(2);
});
describe('render specifiers', () => {
it('renders all the tokens with showZeroValues', () => {
render({ showZeroValues: true });
expect(component.find('Connect(TokenImage)')).to.have.length(2);
expect(component.find('Connect(TokenValue)')).to.have.length(2);
});
});
});

View File

@@ -0,0 +1,109 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import BigNumber from 'bignumber.js';
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { fetchTokens } from '~/redux/providers/tokensActions';
import TokenImage from '~/ui/TokenImage';
import styles from './balance.css';
class TokenValue extends Component {
static propTypes = {
token: PropTypes.object.isRequired,
value: PropTypes.object.isRequired,
// Redux injection
fetchTokens: PropTypes.func.isRequired,
showOnlyEth: PropTypes.bool
};
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 { token, showOnlyEth, value } = this.props;
const isEthToken = token.native;
const isFullToken = !showOnlyEth || isEthToken;
const bnf = new BigNumber(token.format || 1);
let decimals = 0;
if (bnf.gte(1000)) {
decimals = 3;
} else if (bnf.gte(100)) {
decimals = 2;
} else if (bnf.gte(10)) {
decimals = 1;
}
const rawValue = new BigNumber(value).div(bnf);
const classNames = [styles.balance];
if (isFullToken) {
classNames.push(styles.full);
}
return (
<div className={ classNames.join(' ') }>
<TokenImage token={ token } />
{
isFullToken
? [
<div className={ styles.value } key='value'>
<span title={ `${rawValue.toFormat()} ${token.tag}` }>
{ rawValue.toFormat(decimals) }
</span>
</div>,
<div className={ styles.tag } key='tag'>
{ token.tag }
</div>
]
: null
}
</div>
);
}
}
function mapDispatchToProps (dispatch) {
return bindActionCreators({
fetchTokens
}, dispatch);
}
export default connect(
null,
mapDispatchToProps
)(TokenValue);

View File

@@ -19,6 +19,7 @@ import React, { Component, PropTypes } from 'react';
import Container, { Title as ContainerTitle } from '~/ui/Container';
import DappIcon from '~/ui/DappIcon';
import Tags from '~/ui/Tags';
import DappVouchFor from '../DappVouchFor';
import styles from './dappCard.css';
@@ -61,6 +62,7 @@ export default class DappCard extends Component {
app={ app }
className={ styles.image }
/>
<DappVouchFor app={ app } />
<Tags
className={ styles.tags }
tags={

View File

@@ -0,0 +1,43 @@
/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
/* This file is part of Parity.
/*
/* Parity is free software: you can redistribute it and/or modify
/* it under the terms of the GNU General Public License as published by
/* the Free Software Foundation, either version 3 of the License, or
/* (at your option) any later version.
/*
/* Parity is distributed in the hope that it will be useful,
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/* GNU General Public License for more details.
/*
/* You should have received a copy of the GNU General Public License
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
*/
.tag {
color: inherit;
position: absolute;
top: 1em;
right: 1em;
.image {
position: absolute;
right: 0;
top: 0;
width: 32px;
height: 32px;
}
.bubble {
background: red;
border-radius: 0.25em;
color: white;
font-size: 0.75em;
padding: 0.1em 0.5em;
position: absolute;
right: 0;
top: 0;
z-index: 100;
}
}

View File

@@ -0,0 +1,57 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component, PropTypes } from 'react';
import { observer } from 'mobx-react';
import IdentityIcon from '../IdentityIcon';
import Store from './store';
import styles from './dappVouchFor.css';
@observer
export default class DappVouchFor extends Component {
static contextTypes = {
api: PropTypes.object.isRequired
};
static propTypes = {
app: PropTypes.object.isRequired
};
store = new Store(this.context.api, this.props.app);
render () {
const count = this.store.vouchers.length;
if (!count) {
return null;
}
return (
<div className={ styles.tag }>
<IdentityIcon
address={ this.store.vouchers[0] }
className={ styles.image }
alt={ `${count} identities vouch for this dapp` }
/>
<div className={ styles.bubble }>
{ count }
</div>
</div>
);
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
export default from './dappVouchFor';

View File

@@ -0,0 +1,87 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { action, observable } from 'mobx';
import { uniq } from 'lodash';
import Contracts from '~/contracts';
import { vouchfor as vouchForAbi } from '~/contracts/abi';
let contractPromise = null;
export default class Store {
@observable vouchers = [];
constructor (api, app) {
this._api = api;
this.findVouchers(app);
}
async attachContract () {
const address = await Contracts.get().registry.lookupAddress('vouchfor');
if (!address || /^0x0*$/.test(address)) {
return null;
}
const contract = await this._api.newContract(vouchForAbi, address);
return contract;
}
async findVouchers ({ contentHash, id }) {
if (!contentHash) {
return;
}
if (!contractPromise) {
contractPromise = this.attachContract();
}
const contract = await contractPromise;
if (!contract) {
return;
}
const vouchHash = await this.lookupHash(contract, `0x${contentHash}`);
const vouchId = await this.lookupHash(contract, id);
this.addVouchers(vouchHash, vouchId);
}
async lookupHash (contract, hash) {
const vouchers = [];
let lastItem = false;
for (let index = 0; !lastItem; index++) {
const voucher = await contract.instance.vouched.call({}, [hash, index]);
if (/^0x0*$/.test(voucher)) {
lastItem = true;
} else {
vouchers.push(voucher);
}
}
return vouchers;
}
@action addVouchers = (vouchHash, vouchId) => {
this.vouchers = uniq([].concat(this.vouchers.peek(), vouchHash, vouchId));
}
}

View File

@@ -53,11 +53,11 @@ class InputAddressSelect extends Component {
const filteredContacts = nextAllowedValues
? pick(contacts, nextAllowedValues)
: accounts;
: contacts;
const filteredContracts = nextAllowedValues
? pick(contracts, nextAllowedValues)
: accounts;
: contracts;
return (
<AddressSelect

View File

@@ -30,6 +30,7 @@ class IdentityIcon extends Component {
static propTypes = {
address: PropTypes.string,
alt: PropTypes.string,
button: PropTypes.bool,
center: PropTypes.bool,
className: PropTypes.string,
@@ -84,7 +85,7 @@ class IdentityIcon extends Component {
}
render () {
const { address, button, className, center, disabled, inline, padded, tiny } = this.props;
const { address, alt, button, className, center, disabled, inline, padded, tiny } = this.props;
const { iconsrc } = this.state;
const classes = [
styles.icon,
@@ -135,6 +136,7 @@ class IdentityIcon extends Component {
return (
<img
alt={ alt || address }
className={ classes }
data-address-img
height={ size }

View File

@@ -23,6 +23,7 @@ import { connect } from 'react-redux';
import { TypedInput, InputAddress } from '../Form';
import MethodDecodingStore from './methodDecodingStore';
import TokenValue from './tokenValue';
import styles from './methodDecoding.css';
@@ -602,9 +603,10 @@ class MethodDecoding extends Component {
const { token } = this.props;
return (
<span className={ styles.tokenValue }>
{ value.div(token.format).toFormat(5) }<small> { token.tag }</small>
</span>
<TokenValue
id={ token.id }
value={ value }
/>
);
}

View File

@@ -0,0 +1,102 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { fetchTokens } from '~/redux/providers/tokensActions';
import styles from './methodDecoding.css';
class TokenValue extends Component {
static propTypes = {
id: PropTypes.string.isRequired,
value: PropTypes.object.isRequired,
fetchTokens: PropTypes.func,
token: PropTypes.object
};
componentWillMount () {
const { token } = this.props;
if (!token.fetched) {
this.props.fetchTokens([ token.index ]);
}
}
render () {
const { token, value } = this.props;
if (!token.format) {
console.warn('token with no format', token);
}
const format = token.format
? token.format
: 1;
const precision = token.format
? 5
: 0;
const tag = token.format
? token.tag
: 'TOKENS';
return (
<span className={ styles.tokenValue }>
{ value.div(format).toFormat(precision) }<small> { tag }</small>
</span>
);
}
}
function mapStateToProps (initState, initProps) {
const { id } = initProps;
let token = Object.assign({}, initState.tokens[id]);
if (token.fetched) {
return () => ({ token });
}
let update = true;
return (state) => {
if (update) {
const { tokens } = state;
const nextToken = tokens[id];
if (nextToken.fetched) {
token = Object.assign({}, nextToken);
update = false;
}
}
return { token };
};
}
function mapDispatchToProps (dispatch) {
return bindActionCreators({
fetchTokens
}, dispatch);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(TokenValue);

View File

@@ -81,12 +81,11 @@ export function getTxOptions (api, func, _options, values = []) {
options.to = options.to || func.contract.address;
}
if (!address) {
return Promise.resolve({ func, options, values });
}
const promise = (!address)
? Promise.resolve(false)
: WalletsUtils.isWallet(api, address);
return WalletsUtils
.isWallet(api, address)
return promise
.then((isWallet) => {
if (!isWallet) {
return { func, options, values };

View File

@@ -26,7 +26,6 @@ import HardwareStore from '~/mobx/hardwareStore';
import ExportStore from '~/modals/ExportAccount/exportStore';
import { DeleteAccount, EditMeta, Faucet, PasswordManager, Shapeshift, Transfer, Verification } from '~/modals';
import { setVisibleAccounts } from '~/redux/providers/personalActions';
import { fetchCertifiers, fetchCertifications } from '~/redux/providers/certifications/actions';
import { Actionbar, Button, ConfirmDialog, Input, Page, Portal } from '~/ui';
import { DeleteIcon, DialIcon, EditIcon, LockedIcon, SendIcon, VerifyIcon, FileDownloadIcon } from '~/ui/Icons';
@@ -45,8 +44,6 @@ class Account extends Component {
static propTypes = {
accounts: PropTypes.object.isRequired,
fetchCertifiers: PropTypes.func.isRequired,
fetchCertifications: PropTypes.func.isRequired,
setVisibleAccounts: PropTypes.func.isRequired,
account: PropTypes.object,
@@ -67,7 +64,6 @@ class Account extends Component {
}
componentDidMount () {
this.props.fetchCertifiers();
this.setVisibleAccounts();
}
@@ -90,11 +86,10 @@ class Account extends Component {
}
setVisibleAccounts (props = this.props) {
const { params, setVisibleAccounts, fetchCertifications } = props;
const { params, setVisibleAccounts } = props;
const addresses = [params.address];
setVisibleAccounts(addresses);
fetchCertifications(params.address);
}
render () {
@@ -524,8 +519,6 @@ function mapStateToProps (state, props) {
function mapDispatchToProps (dispatch) {
return bindActionCreators({
fetchCertifiers,
fetchCertifications,
newError,
setVisibleAccounts
}, dispatch);

View File

@@ -17,10 +17,8 @@
import { pick } from 'lodash';
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { Container, SectionList } from '~/ui';
import { fetchCertifiers, fetchCertifications } from '~/redux/providers/certifications/actions';
import { ETH_TOKEN } from '~/util/tokens';
import Summary from '../Summary';
@@ -38,20 +36,9 @@ class List extends Component {
orderFallback: PropTypes.string,
search: PropTypes.array,
fetchCertifiers: PropTypes.func.isRequired,
fetchCertifications: PropTypes.func.isRequired,
handleAddSearchToken: PropTypes.func
};
componentWillMount () {
const { accounts, fetchCertifiers, fetchCertifications } = this.props;
fetchCertifiers();
for (let address in accounts) {
fetchCertifications(address);
}
}
render () {
const { accounts, disabled, empty } = this.props;
@@ -264,14 +251,7 @@ function mapStateToProps (state, props) {
return { balances, certifications };
}
function mapDispatchToProps (dispatch) {
return bindActionCreators({
fetchCertifiers,
fetchCertifications
}, dispatch);
}
export default connect(
mapStateToProps,
mapDispatchToProps
null
)(List);

View File

@@ -28,12 +28,12 @@
}
.signData {
background: rgba(255,255,255, 0.25);
border: 0.25em solid red;
margin-left: 2em;
padding: 0.5em;
font-size: 0.75em;
padding: 0.5rem;
overflow: auto;
max-height: 6em;
max-width: calc(100% - 2em);
max-height: 10em;
}
.signData > p {

View File

@@ -18,7 +18,9 @@ import { observer } from 'mobx-react';
import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import ReactMarkdown from 'react-markdown';
import { hexToAscii } from '~/api/util/format';
import HardwareStore from '~/mobx/hardwareStore';
import Account from '../Account';
@@ -28,16 +30,39 @@ import RequestOrigin from '../RequestOrigin';
import styles from './signRequest.css';
function isAscii (data) {
for (var i = 2; i < data.length; i += 2) {
for (let i = 2; i < data.length; i += 2) {
let n = parseInt(data.substr(i, 2), 16);
if (n < 32 || n >= 128) {
return false;
}
}
return true;
}
function decodeMarkdown (data) {
return decodeURIComponent(escape(hexToAscii(data)));
}
export function isMarkdown (data) {
try {
const decoded = decodeMarkdown(data);
for (let i = 0; i < decoded.length; i++) {
const code = decoded.charCodeAt(i);
if (code < 32 && code !== 10) {
return false;
}
}
return decoded.indexOf('#') !== -1 || decoded.indexOf('*') !== -1;
} catch (error) {
return false;
}
}
@observer
class SignRequest extends Component {
static contextTypes = {
@@ -89,29 +114,26 @@ class SignRequest extends Component {
);
}
renderAsciiDetails (ascii) {
return (
<div className={ styles.signData }>
<p>{ascii}</p>
</div>
);
}
renderData (data) {
if (isAscii(data)) {
return hexToAscii(data);
}
if (isMarkdown(data)) {
return (
<ReactMarkdown source={ decodeMarkdown(data) } />
);
}
renderBinaryDetails (data) {
return (
<div className={ styles.signData }>
<p>
<FormattedMessage
id='signer.signRequest.unknownBinary'
defaultMessage='(Unknown binary data)'
/>
</p>
</div>
<FormattedMessage
id='signer.signRequest.unknownBinary'
defaultMessage='(Unknown binary data)'
/>
);
}
renderDetails () {
const { api } = this.context;
const { address, data, netVersion, origin, signerStore } = this.props;
const { balances, externalLink } = signerStore;
@@ -121,6 +143,8 @@ class SignRequest extends Component {
return <div />;
}
const hashToSign = this.context.api.util.sha3(data);
return (
<div className={ styles.signDetails }>
<div className={ styles.address }>
@@ -133,18 +157,16 @@ class SignRequest extends Component {
/>
<RequestOrigin origin={ origin } />
</div>
<div className={ styles.info } title={ api.util.sha3(data) }>
<div className={ styles.info } title={ hashToSign }>
<p>
<FormattedMessage
id='signer.signRequest.request'
defaultMessage='A request to sign data using your account:'
/>
</p>
{
isAscii(data)
? this.renderAsciiDetails(api.util.hexToAscii(data))
: this.renderBinaryDetails(data)
}
<div className={ styles.signData }>
<p>{ this.renderData(data) }</p>
</div>
<p>
<strong>
<FormattedMessage

View File

@@ -18,7 +18,9 @@ import { shallow } from 'enzyme';
import React from 'react';
import sinon from 'sinon';
import SignRequest from './';
import { asciiToHex } from '~/api/util/format';
import SignRequest, { isMarkdown } from './signRequest';
let component;
let reduxStore;
@@ -81,4 +83,24 @@ describe('views/Signer/components/SignRequest', () => {
it('renders', () => {
expect(component).to.be.ok;
});
describe('isMarkdown', () => {
it('returns true for markdown', () => {
const testMd = '# this is some\n\n*markdown*';
const encodedMd = asciiToHex(unescape(encodeURIComponent(testMd)));
expect(isMarkdown(encodedMd)).to.be.true;
});
it('return true with utf-8 markdown', () => {
const testMd = '# header\n\n(n) you are not a citizen of, or resident in, or located in, or incorporated or otherwise established in, the People\'s Republic of China 参与方并非中华人民共和国公民,或不常住中华人民共和国,或不位于中华人民共和国境内,或并非在中华人民共和国设立或以其他方式组建; and';
const encodedMd = asciiToHex(unescape(encodeURIComponent(testMd)));
expect(isMarkdown(encodedMd)).to.be.true;
});
it('returns false for random data', () => {
expect(isMarkdown('0x1234')).to.be.false;
});
});
});

View File

@@ -26,7 +26,7 @@ pub struct AuthorityRoundParams {
/// Gas limit divisor.
#[serde(rename="gasLimitBoundDivisor")]
pub gas_limit_bound_divisor: Uint,
/// Block duration.
/// Block duration, in seconds.
#[serde(rename="stepDuration")]
pub step_duration: Uint,
/// Valid authorities
@@ -52,6 +52,12 @@ pub struct AuthorityRoundParams {
/// Whether transitions should be immediate.
#[serde(rename="immediateTransitions")]
pub immediate_transitions: Option<bool>,
/// Block at which maximum uncle count should be considered.
#[serde(rename="maximumUncleCountTransition")]
pub maximum_uncle_count_transition: Option<Uint>,
/// Maximum number of accepted uncles.
#[serde(rename="maximumUncleCount")]
pub maximum_uncle_count: Option<Uint>,
}
/// Authority engine deserialization.
@@ -84,6 +90,8 @@ mod tests {
"startStep" : 24,
"eip155Transition": "0x42",
"validateStepTransition": 150
"maximumUncleCountTransition": 10000000,
"maximumUncleCount": 5
}
}"#;
@@ -96,5 +104,8 @@ mod tests {
assert_eq!(deserialized.params.start_step, Some(Uint(U256::from(24))));
assert_eq!(deserialized.params.eip155_transition, Some(Uint(U256::from(0x42))));
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())));
}
}

View File

@@ -462,7 +462,7 @@
<key>OVERWRITE_PERMISSIONS</key>
<false/>
<key>VERSION</key>
<string>1.7.3</string>
<string>1.7.12</string>
</dict>
<key>UUID</key>
<string>2DCD5B81-7BAF-4DA1-9251-6274B089FD36</string>

View File

@@ -10,7 +10,7 @@
!define DESCRIPTION "Fast, light, robust Ethereum implementation"
!define VERSIONMAJOR 1
!define VERSIONMINOR 7
!define VERSIONBUILD 3
!define VERSIONBUILD 12
!define ARGS "--warp"
!define FIRST_START_ARGS "ui --warp --mode=passive"

View File

@@ -584,8 +584,12 @@ fn execute_export(cmd: ExportBlockchain) -> Result<(), String> {
}
let b = client.block(BlockId::Number(i)).ok_or("Error exporting incomplete chain")?.into_inner();
match format {
DataFormat::Binary => { out.write(&b).expect("Couldn't write to stream."); }
DataFormat::Hex => { out.write_fmt(format_args!("{}", b.pretty())).expect("Couldn't write to stream."); }
DataFormat::Binary => {
out.write(&b).map_err(|e| format!("Couldn't write to stream. Cause: {}", e))?;
}
DataFormat::Hex => {
out.write_fmt(format_args!("{}", b.pretty())).map_err(|e| format!("Couldn't write to stream. Cause: {}", e))?;
}
}
}

View File

@@ -23,7 +23,6 @@ use std::cmp::max;
use std::str::FromStr;
use cli::{Args, ArgsError};
use util::{Hashable, H256, U256, Bytes, version_data, Address};
use util::journaldb::Algorithm;
use util::Colour;
use ethsync::{NetworkConfiguration, is_valid_node_url, AllowIP};
use ethcore::ethstore::ethkey::{Secret, Public};
@@ -37,7 +36,7 @@ use parity_rpc::NetworkSettings;
use cache::CacheConfig;
use helpers::{to_duration, to_mode, to_block_id, to_u256, to_pending_set, to_price, replace_home, replace_home_and_local,
geth_ipc_path, parity_ipc_path, to_bootnodes, to_addresses, to_address, to_gas_limit, to_queue_strategy};
use params::{SpecType, ResealPolicy, AccountsConfig, GasPricerConfig, MinerExtras, Pruning, Switch};
use params::{SpecType, ResealPolicy, AccountsConfig, GasPricerConfig, MinerExtras};
use ethcore_logger::Config as LogConfig;
use dir::{self, Directories, default_hypervisor_path, default_local_path, default_data_path};
use dapps::Configuration as DappsConfiguration;
@@ -123,15 +122,8 @@ impl Configuration {
let fat_db = self.args.flag_fat_db.parse()?;
let compaction = self.args.flag_db_compaction.parse()?;
let wal = !self.args.flag_fast_and_loose;
match self.args.flag_warp {
// Logging is not initialized yet, so we print directly to stderr
Some(true) if fat_db == Switch::On => writeln!(&mut stderr(), "Warning: Warp Sync is disabled because Fat DB is turned on").expect("Error writing to stderr"),
Some(true) if tracing == Switch::On => writeln!(&mut stderr(), "Warning: Warp Sync is disabled because tracing is turned on").expect("Error writing to stderr"),
Some(true) if pruning == Pruning::Specific(Algorithm::Archive) => writeln!(&mut stderr(), "Warning: Warp Sync is disabled because pruning mode is set to archive").expect("Error writing to stderr"),
_ => {},
};
let public_node = self.args.flag_public_node;
let warp_sync = !self.args.flag_no_warp && fat_db != Switch::On && tracing != Switch::On && pruning != Pruning::Specific(Algorithm::Archive);
let warp_sync = !self.args.flag_no_warp;
let geth_compatibility = self.args.flag_geth;
let mut dapps_conf = self.dapps_config();
let ipfs_conf = self.ipfs_config();
@@ -769,7 +761,8 @@ impl Configuration {
}
fn cors(cors: Option<&String>) -> Option<Vec<String>> {
cors.map(|ref c| c.split(',').map(Into::into).collect())
// Never return `None` here (enables CORS for all hosts).
Some(cors.map(|ref c| c.split(',').map(Into::into).collect()).unwrap_or_default())
}
fn rpc_cors(&self) -> Option<Vec<String>> {
@@ -1498,7 +1491,7 @@ mod tests {
let conf2 = parse(&["parity", "--ipfs-api-cors", "http://parity.io,http://something.io"]);
// then
assert_eq!(conf0.ipfs_cors(), None);
assert_eq!(conf0.ipfs_cors(), Some(vec![]));
assert_eq!(conf1.ipfs_cors(), Some(vec!["*".into()]));
assert_eq!(conf2.ipfs_cors(), Some(vec!["http://parity.io".into(),"http://something.io".into()]));
}

View File

@@ -34,8 +34,8 @@ impl Default for Configuration {
enabled: false,
port: 5001,
interface: "127.0.0.1".into(),
cors: None,
hosts: Some(Vec::new()),
cors: Some(vec![]),
hosts: Some(vec![]),
}
}
}

View File

@@ -63,8 +63,8 @@ impl Default for HttpConfiguration {
interface: "127.0.0.1".into(),
port: 8545,
apis: ApiSet::UnsafeContext,
cors: None,
hosts: Some(Vec::new()),
cors: Some(vec![]),
hosts: Some(vec![]),
server_threads: None,
processing_threads: 0,
}
@@ -95,7 +95,7 @@ impl From<UiConfiguration> for HttpConfiguration {
interface: conf.interface,
port: conf.port,
apis: rpc_apis::ApiSet::UnsafeContext,
cors: None,
cors: Some(vec![]),
hosts: conf.hosts,
server_threads: None,
processing_threads: 0,

View File

@@ -1,6 +1,7 @@
// 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
@@ -38,6 +39,7 @@ use parity_reactor::EventLoop;
use parity_rpc::{NetworkSettings, informant, is_major_importing};
use updater::{UpdatePolicy, Updater};
use util::{Colour, version, Mutex, Condvar};
use util::journaldb::Algorithm;
use params::{
SpecType, Pruning, AccountsConfig, GasPricerConfig, MinerExtras, Switch,
@@ -474,7 +476,21 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
}
sync_config.fork_block = spec.fork_block();
sync_config.warp_sync = spec.engine.supports_warp() && cmd.warp_sync;
let mut warp_sync = cmd.warp_sync;
if warp_sync {
// Logging is not initialized yet, so we print directly to stderr
if fat_db {
warn!("Warning: Warp Sync is disabled because Fat DB is turned on.");
warp_sync = false;
} else if tracing {
warn!("Warning: Warp Sync is disabled because tracing is turned on.");
warp_sync = false;
} else if algorithm != Algorithm::OverlayRecent {
warn!("Warning: Warp Sync is disabled because of non-default pruning mode.");
warp_sync = false;
}
}
sync_config.warp_sync = spec.engine.supports_warp() && warp_sync;
sync_config.download_old_blocks = cmd.download_old_blocks;
sync_config.serve_light = cmd.serve_light;

View File

@@ -19,4 +19,5 @@ export TARGETS="
-p ethcore-ipc-tests \
-p ethcore-ipc-nano \
-p ethcore-light \
-p evm \
-p parity"

View File

@@ -519,6 +519,10 @@ impl BlockDownloader {
trace!(target: "sync", "Unknown new block parent, restarting sync");
break;
},
Err(BlockImportError::Block(BlockError::TemporarilyInvalid(_))) => {
debug!(target: "sync", "Block temporarily invalid, restarting sync");
break;
},
Err(e) => {
debug!(target: "sync", "Bad block {:?} : {:?}", h, e);
bad = true;

View File

@@ -3,7 +3,7 @@ description = "Ethcore utility library"
homepage = "http://parity.io"
license = "GPL-3.0"
name = "ethcore-util"
version = "1.7.3"
version = "1.7.13"
authors = ["Parity Technologies <admin@parity.io>"]
build = "build.rs"

View File

@@ -24,7 +24,7 @@ include!(concat!(env!("OUT_DIR"), "/version.rs"));
include!(concat!(env!("OUT_DIR"), "/rustc_version.rs"));
#[cfg(feature = "final")]
const THIS_TRACK: &'static str = "beta";
const THIS_TRACK: &'static str = "stable";
// ^^^ should be reset to "stable" or "beta" according to the release branch.
#[cfg(not(feature = "final"))]