Compare commits

...

30 Commits

Author SHA1 Message Date
5chdn
6f92bcf6f5 ci: fix regex 🙄 2018-09-19 18:22:02 +02:00
Denis S. Soldatov aka General-Beck
4637215ab2 Remove snapcraft clean (#9585)
* Revert " add snapcraft package image (master) (#9584)"

This reverts commit ceaedbbd7f.

* Update package-snap.sh

* Update .gitlab-ci.yml
2018-09-18 15:34:24 +02:00
Denis S. Soldatov aka General-Beck
ceaedbbd7f add snapcraft package image (master) (#9584) 2018-09-18 14:39:27 +02:00
Andronik Ordian
52f8b1a1d7 docs(rpc): push the branch along with tags (#9578)
* docs(rpc): push the branch along with tags

* ci: remove old rpc docs script
2018-09-18 08:54:50 +02:00
Thibaut Sardan
e6d1250185 Fix typo for jsonrpc-threads flag (#9574) 2018-09-18 00:56:33 +08:00
Wei Tang
d04e5e49d0 Fix informant compile (#9571) 2018-09-17 16:16:49 +02:00
EOS Classic
a72436f330 Added ropsten bootnodes (#9569)
from @eosclassicteam with ❤️
2018-09-17 12:16:32 +02:00
EOS Classic
467403f437 Increase Gas-floor-target and Gas Cap (#9564)
+ Gas-floor-target increased to 8M by default

+ Gas-cap increased to 10M by default
2018-09-17 08:33:00 +02:00
gabriel klawitter
bbaac0c6a9 while working on the platform tests make them non-breaking (#9563)
* while working on the platform tests make them non-critical

* ci: unify test stage job names and torelate more failures

* ci: restore valid yaml

* ci: allow beta and nightly rust builds to fail
2018-09-15 13:43:19 +02:00
Nicolas Gotchac
753fd4bda3 Improve P2P discovery (#9526)
* Add `target` to Rust traces

* network-devp2p: Don't remove discovery peer in main sync

* network-p2p: Refresh discovery more often

* Update Peer discovery protocol

* Run discovery more often when not enough nodes connected

* Start the first discovery early

* Update fast discovery rate

* Fix tests

* Fix `ping` tests

* Fixing remote Node address ; adding PingPong round

* Fix tests: update new +1 PingPong round

* Increase slow Discovery rate
Check in flight FindNode before pings

* Add `deprecated` to deprecated_echo_hash

* Refactor `discovery_round` branching
2018-09-14 22:18:03 +02:00
gabriel klawitter
57d2c8c94a move dockerfile for android build container to scripts repo (#9560) 2018-09-14 16:16:36 +02:00
gabriel klawitter
870ec89e9a Simultaneous platform tests WIP (#9557)
* look into commit changes

* look into commit changes ii

* all on test

* build only_releaseable_branches for platforms

* allow failure for check during development

* windows test typo

* fix sh for windows

* remove check stage again

* debug macos platform
2018-09-14 15:55:14 +02:00
Marek Kotewicz
0c3b70f2fb update ethabi-derive, serde, serde_json, serde_derive, syn && quote (#9553) 2018-09-14 03:37:28 +08:00
Andronik Ordian
7a367698fe ci: fix rpc docs generation 2 (#9550) 2018-09-13 14:36:44 +02:00
Afri Schoedon
c77e99814b ci: always run build pipelines for win, mac, linux, and android (#9537)
* ci: always run build pipelines for win, mac, linux, and android

* ci: always run build pipelines for win, mac, linux, and android

* ci: disallow failure for publish scripts

* ci: enable tests on master

* ci: run tests in debug mode to speed things up

* ci: only build windows, darwin, and android targets on PRs

* ci: reenable darwin and android pipelines on PR

* ci: revert tests to run in release mode
2018-09-13 13:15:15 +02:00
Nicolas Gotchac
4ddd69cc55 Multithreaded snapshot creation (#9239)
* Add Progress to Snapshot Secondary chunks creation

* Use half of CPUs to multithread snapshot creation

* Use env var to define number of threads

* info to debug logs

* Add Snapshot threads as CLI option

* Randomize chunks per thread

* Remove randomness, add debugging

* Add warning

* Add tracing

* Use parity-common fix seek branch

* Fix log

* Fix tests

* Fix tests

* PR Grumbles

* PR Grumble II

* Update Cargo.lock

* PR Grumbles

* Default snapshot threads to half number of CPUs

* Fix default snapshot threads // min 1
2018-09-13 12:58:49 +02:00
Marek Kotewicz
ef4a61c769 new ethabi (#9511)
* new ethabi migration in progress

* parity migrated to new ethabi

* migrated secred-store to new ethabi

* bump ethabi to 6.0

* fixed review suggestions
2018-09-13 11:04:39 +02:00
Tomasz Drwięga
7dfb5ff5bd Remove initial token for WS. (#9545) 2018-09-13 10:58:52 +02:00
Marek Kotewicz
6b391312ab net_version caches network_id to avoid redundant aquire of sync read lock (#9544)
* net_version caches network_id to avoid redundant aquire of sync read lock, #8746

* use lower_hex display formatting for net_peerCount rpc method
2018-09-13 10:16:24 +02:00
gabriel klawitter
6e62d77e4d correct before_script for nightly build versions (#9543)
- fix gitlab array of strings syntax error
- get proper commit id
- avoid colon in stings
2018-09-12 17:46:56 +03:00
Afri Schoedon
0281cca9af deps: bump kvdb-rocksdb to 0.1.4 (#9539) 2018-09-12 13:23:09 +02:00
Wei Tang
018e2403b1 state: test when contract creation fails, old storage values should re-appear (#9532)
Because more tests won't hurt. :)

Add a test case for https://github.com/ethereum/py-evm/pull/1224#issuecomment-418775512 where if contract creation fails, old storage values (if ever existed) should re-appear.
2018-09-12 11:42:09 +01:00
cheme
61f4534e2a Allow dropping light client RPC query with no results (#9318)
* OnDemand no longer loop until there is a query.
All peer known at the time will be queried, and the query fail if all
return no reply.
Returning the failure is done through an empty Vec of reply (the type
of the oneshot channel remains unchanged).
Before this commit the query were send randomly to any peer until there
is a reply (for a query that got no result it was an issue, for other
queries it was quering multiple times the same peers).
After this commit the first query is random but next queries
follows hashmap iterator order.

Test no_capability was broken by this commit (the pending query was
removed).

* OnDemand no longer loop until there is a query.
All peer known at the time will be queried, and the query fail if all
return no reply.
Returning the failure is done through an empty Vec of reply (the type
of the oneshot channel remains unchanged).
Before this commit the query were send randomly to any peer until there
is a reply (for a query that got no result it was an issue, for other
queries it was quering multiple times the same peers).
After this commit the first query is random but next queries
follows hashmap iterator order.

Test no_capability was broken by this commit (the pending query was
removed). If adding some kind of timeout mechanism it could be restored.

* Comment plus better field names.

* No panick on dropped oneshot channel.

* Use Set to avoid counter heuristic

* Cli option `on_demand_nb_retry` for maximum number of retry when doing
on demand query in light client.

* Missing test update for previous commit

* Add a timeout (only when there is no peer to query), that way we do not
set number of query to minimum current number peer or configured number
of query : that way capability test was restored.

* Adding an error type for on_demand, it helps having variant of error
reported at rpc level : choice of rpc error code error might not be
right.

* Duration as constant is nice

* Switch to duration in main too

* Fix indentation (sorry for that).

* Fix error management (bad merge in previous commit)

* Lots of english corrections, major change on the new command parameters :
 - use standard '-' instead of '_'
 - renaming nb_retry params to 'on-demand-retry-count'
2018-09-12 11:47:01 +02:00
Afri Schoedon
69667317c1 Bump master to 2.2.0 (#9517)
* parity-version: bump master to 2.2.0

* ci: update branch version references

* docker: release master to latest

* deps: bump fs-swap to 0.2.4
2018-09-12 11:32:05 +02:00
Wei Tang
530aac0682 Enable all Constantinople hard fork changes in constantinople_test.json (#9505)
* Enable all Constantinople hard fork changes in constantinople_test.json

* Address grumbles

* Remove EIP-210 activation

* 8m -> 5m

* Temporarily add back eip210 transition so we can get test passed

* Add eip210_test and remove eip210 transition from const_test
2018-09-12 02:08:23 +08:00
Niklas Adolfsson
6e7d8f90b5 [light] Validate account balance before importing transactions (#9417)
* `light::verify_transaction` basic tx validation

* update wasm tests

* Provide `cached_nonce` in `parity_next_nonce` RPC

* nits

* Improve error handeling

Two separate errors for distinguishing between `account not found` and
`insufficient balance`. However, when `next_nonce()` is called and the
account is not found then provide `local_best_next_nonce`!

* Ensure only one n/w request is performed

Refactored to code again:
* Removed `fn cached_next_nonce`
* Removed extra n/w request in `sign` to check balance
* Refactored `fill_optional_field` to request nonce and check account balance

* nits

* grumbles needless clone

* Prevent integer overflow with saturating add & mul

* Call `sign_transaction()` directly from `sign()`

Because the change in `fill_optional_fields` always fill the nonce it is
now possible to call `sign_transaction` directly instead of creating a
`ProspectiveSigner` "object".
2018-09-11 19:20:59 +02:00
cheme
65bf1086a2 In create memory calculation is the same for create2 because the additional parameter was popped before. (#9522) 2018-09-11 23:47:26 +08:00
cheme
98220442b4 Update patricia trie to 0.2.2 (#9525)
* Update patricia trie to 0.2.2 crates. Default dependencies on minor
version only.

* Putting back ethereum tests to the right commit
2018-09-11 16:20:26 +02:00
Hernando Castano
3c3d2ef2b9 Replace hardcoded JSON with serde json! macro (#9489)
* Replace hardcoded JSON with serde json! macro

* Use "{:#x}" formatter instead of "0x{:x}"

* Sort fields of JSON test strings alphabetically

* Stop escaping new lines in evmbin JSON errors

* Remove unnecessary 'to_string()' calls

* Add test with non-empty storage values
2018-09-11 11:36:38 +02:00
gabriel klawitter
67066eb32a fix typo in version string (#9516) 2018-09-11 11:31:25 +02:00
86 changed files with 1956 additions and 1879 deletions

View File

@@ -31,7 +31,7 @@ cache:
.publishable_branches: # list of git refs for publishing builds to the "production" locations
only: &publishable_branches
- nightly # Our nightly builds from schedule, on `master`
- "v2*" # Our version tags
- /^v2.*$/ # Our version tags
.collect_artifacts: &collect_artifacts
artifacts:
@@ -41,44 +41,84 @@ cache:
paths:
- artifacts/
.determine_version:
before_script: &determine_version
- >
VERSION="$(sed -r -n '1,/^version/s/^version = "([^"]+)".*$/\1/p' < Cargo.toml)";
if [ "${CI_COMMIT_REF_NAME}" = "nightly" ]; then
COMMIT_REF_SHORT="echo ${CI_COMMIT_REF} | grep -oE '^.{7}')";
DATE_STRING="$(date +%Y%m%d)";
export VERSION="${VERSION}-${COMMIT_REF_SHORT}-${DATE_STRING}";
fi;
export VERSION;
echo "Version: $VERSION"
.determine_version: &determine_version
- VERSION="$(sed -r -n '1,/^version/s/^version = "([^"]+)".*$/\1/p' Cargo.toml)"
- DATE_STR="$(date +%Y%m%d)"
- ID_SHORT="$(echo ${CI_COMMIT_SHA} | cut -c 1-7)"
- test "${CI_COMMIT_REF_NAME}" = "nightly" && VERSION="${VERSION}-${ID_SHORT}-${DATE_STR}"
- export VERSION
- echo "Version = ${VERSION}"
#### stage: test
test-rust-stable: &test
test-linux-rust-stable: &test
stage: test
script:
- scripts/gitlab/test.sh stable
tags:
- rust-stable
test-linux-rust-beta:
stage: test
script:
- scripts/gitlab/test.sh beta
tags:
- rust-stable
allow_failure: true
test-linux-rust-nightly:
stage: test
script:
- scripts/gitlab/test.sh nightly
tags:
- rust-stable
allow_failure: true
test-darwin-rust-stable:
stage: test
variables:
CARGO_TARGET: x86_64-apple-darwin
CC: gcc
CXX: g++
script:
- scripts/gitlab/test.sh stable
tags:
- osx
allow_failure: true
test-android-rust-stable:
stage: test
image: parity/rust-android:gitlab-ci
variables:
CARGO_TARGET: armv7-linux-androideabi
script:
- scripts/gitlab/test.sh stable
tags:
- rust-arm
allow_failure: true
test-windows-rust-stable:
stage: test
cache:
key: "%CI_JOB_NAME%"
paths:
- "%CI_PROJECT_DIR%/target/"
- "%CI_PROJECT_DIR%/cargo/"
# No cargo caching, since fetch-locking on Windows gets stuck
variables:
CARGO_TARGET: x86_64-pc-windows-msvc
script:
- sh scripts/gitlab/test.sh stable
tags:
- rust-windows
allow_failure: true
.optional_test: &optional_test
<<: *test
allow_failure: true
only:
- master
test-rust-beta:
<<: *optional_test
script:
- scripts/gitlab/test.sh beta
test-rust-nightly:
<<: *optional_test
script:
- scripts/gitlab/test.sh nightly
test-lint-rustfmt:
<<: *optional_test
script:
@@ -90,15 +130,11 @@ test-lint-clippy:
- scripts/gitlab/clippy.sh
test-coverage-kcov:
stage: test
only:
- master
<<: *optional_test
script:
- scripts/gitlab/coverage.sh
tags:
- shell
allow_failure: true
#### stage: build
@@ -112,46 +148,58 @@ build-linux-ubuntu-amd64: &build
<<: *collect_artifacts
tags:
- rust-stable
allow_failure: true
build-linux-ubuntu-i386:
<<: *build
only: *releaseable_branches
image: parity/rust-i686:gitlab-ci
variables:
CARGO_TARGET: i686-unknown-linux-gnu
tags:
- rust-i686
allow_failure: true
build-linux-ubuntu-arm64:
<<: *build
only: *releaseable_branches
image: parity/rust-arm64:gitlab-ci
variables:
CARGO_TARGET: aarch64-unknown-linux-gnu
tags:
- rust-arm
allow_failure: true
build-linux-ubuntu-armhf:
<<: *build
only: *releaseable_branches
image: parity/rust-armv7:gitlab-ci
variables:
CARGO_TARGET: armv7-unknown-linux-gnueabihf
tags:
- rust-arm
allow_failure: true
build-linux-android-armhf:
<<: *build
stage: build
only: *releaseable_branches
image: parity/rust-android:gitlab-ci
variables:
CARGO_TARGET: armv7-linux-androideabi
script:
- scripts/gitlab/build-unix.sh
tags:
- rust-arm
allow_failure: true
build-darwin-macos-x86_64:
<<: *build
stage: build
only: *releaseable_branches
variables:
CARGO_TARGET: x86_64-apple-darwin
CC: gcc
CXX: g++
script:
- scripts/gitlab/build-unix.sh
tags:
- osx
<<: *collect_artifacts
@@ -173,12 +221,12 @@ build-windows-msvc-x86_64:
- rust-windows
<<: *collect_artifacts
#### stage: package
package-linux-snap-amd64: &package_snap
stage: package
only: *releaseable_branches
image: parity/snapcraft:gitlab-ci
cache: {}
before_script: *determine_version
variables:
@@ -215,13 +263,12 @@ package-linux-snap-armhf:
dependencies:
- build-linux-ubuntu-armhf
#### stage: publish
publish-linux-snap-amd64: &publish_snap
stage: publish
only: *publishable_branches
image: snapcore/snapcraft:stable
image: parity/snapcraft:gitlab-ci
cache: {}
before_script: *determine_version
variables:
@@ -256,13 +303,12 @@ publish-linux-snap-armhf:
publish-docker-parity-amd64: &publish_docker
stage: publish
only: *publishable_branches
only: *releaseable_branches
cache: {}
dependencies:
- build-linux-ubuntu-amd64
tags:
- shell
allow_failure: true
script:
- scripts/gitlab/publish-docker.sh parity
@@ -287,8 +333,6 @@ publish-github-and-s3:
- scripts/gitlab/push.sh
tags:
- shell
allow_failure: true
####stage: docs

792
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
description = "Parity Ethereum client"
name = "parity-ethereum"
# NOTE Make sure to update util/version/Cargo.toml as well
version = "2.1.0"
version = "2.2.0"
license = "GPL-3.0"
authors = ["Parity Technologies <admin@parity.io>"]

View File

@@ -1,61 +0,0 @@
FROM ubuntu:xenial
LABEL maintainer="Parity Technologies <devops@parity.io>"
RUN apt-get update && \
apt-get install -yq sudo curl file build-essential wget git g++ cmake pkg-config bison flex \
unzip lib32stdc++6 lib32z1 python autotools-dev automake autoconf libtool \
gperf xsltproc docbook-xsl
# Rust & Cargo
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
ENV PATH /root/.cargo/bin:$PATH
RUN rustup toolchain install stable
RUN rustup target add --toolchain stable arm-linux-androideabi
RUN rustup target add --toolchain stable armv7-linux-androideabi
# Android NDK and toolchain
RUN cd /usr/local && \
wget -q https://dl.google.com/android/repository/android-ndk-r16b-linux-x86_64.zip && \
unzip -q android-ndk-r16b-linux-x86_64.zip && \
rm android-ndk-r16b-linux-x86_64.zip
ENV NDK_HOME /usr/local/android-ndk-r16b
RUN /usr/local/android-ndk-r16b/build/tools/make-standalone-toolchain.sh \
--arch=arm --install-dir=/opt/ndk-standalone --stl=libc++ --platform=android-26
ENV PATH $PATH:/opt/ndk-standalone/bin
# Compiling libudev for Android
# This is the most hacky part of the process, as we need to apply a patch and pass specific
# options that the compiler environment doesn't define.
RUN cd /root && \
git clone https://github.com/gentoo/eudev.git
ADD libudev.patch /root
RUN cd /root/eudev && \
git checkout 83d918449f22720d84a341a05e24b6d109e6d3ae && \
./autogen.sh && \
./configure --disable-introspection --disable-programs --disable-hwdb \
--host=arm-linux-androideabi --prefix=/opt/ndk-standalone/sysroot/usr/ \
--enable-shared=false CC=arm-linux-androideabi-clang \
CFLAGS="-D LINE_MAX=2048 -D RLIMIT_NLIMITS=15 -D IPTOS_LOWCOST=2 -std=gnu99" \
CXX=arm-linux-androideabi-clang++ && \
git apply - < /root/libudev.patch && \
make && \
make install
RUN rm -rf /root/eudev
RUN rm /root/libudev.patch
# Rust-related configuration
ADD cargo-config.toml /root/.cargo/config
ENV ARM_LINUX_ANDROIDEABI_OPENSSL_DIR /opt/ndk-standalone/sysroot/usr
ENV ARMV7_LINUX_ANDROIDEABI_OPENSSL_DIR /opt/ndk-standalone/sysroot/usr
ENV CC_arm_linux_androideabi arm-linux-androideabi-clang
ENV CC_armv7_linux_androideabi arm-linux-androideabi-clang
ENV CXX_arm_linux_androideabi arm-linux-androideabi-clang++
ENV CXX_armv7_linux_androideabi arm-linux-androideabi-clang++
ENV AR_arm_linux_androideabi arm-linux-androideabi-ar
ENV AR_armv7_linux_androideabi arm-linux-androideabi-ar
ENV CFLAGS_arm_linux_androideabi -std=gnu11 -fPIC -D OS_ANDROID
ENV CFLAGS_armv7_linux_androideabi -std=gnu11 -fPIC -D OS_ANDROID
ENV CXXFLAGS_arm_linux_androideabi -std=gnu++11 -fPIC -fexceptions -frtti -static-libstdc++ -D OS_ANDROID
ENV CXXFLAGS_armv7_linux_androideabi -std=gnu++11 -fPIC -fexceptions -frtti -static-libstdc++ -D OS_ANDROID
ENV CXXSTDLIB_arm_linux_androideabi ""
ENV CXXSTDLIB_armv7_linux_androideabi ""

View File

@@ -1,9 +0,0 @@
[target.armv7-linux-androideabi]
linker = "arm-linux-androideabi-clang"
ar = "arm-linux-androideabi-ar"
rustflags = ["-C", "link-arg=-lc++_static", "-C", "link-arg=-lc++abi", "-C", "link-arg=-landroid_support"]
[target.arm-linux-androideabi]
linker = "arm-linux-androideabi-clang"
ar = "arm-linux-androideabi-ar"
rustflags = ["-C", "link-arg=-lc++_static", "-C", "link-arg=-lc++abi", "-C", "link-arg=-landroid_support"]

View File

@@ -1,216 +0,0 @@
diff --git a/src/collect/collect.c b/src/collect/collect.c
index 2cf1f00..b24f26b 100644
--- a/src/collect/collect.c
+++ b/src/collect/collect.c
@@ -84,7 +84,7 @@ static void usage(void)
" invoked for each ID in <idlist>) collect returns 0, the\n"
" number of missing IDs otherwise.\n"
" On error a negative number is returned.\n\n"
- , program_invocation_short_name);
+ , "parity");
}
/*
diff --git a/src/scsi_id/scsi_id.c b/src/scsi_id/scsi_id.c
index 8b76d87..7bf3948 100644
--- a/src/scsi_id/scsi_id.c
+++ b/src/scsi_id/scsi_id.c
@@ -321,7 +321,7 @@ static void help(void) {
" -u --replace-whitespace Replace all whitespace by underscores\n"
" -v --verbose Verbose logging\n"
" -x --export Print values as environment keys\n"
- , program_invocation_short_name);
+ , "parity");
}
diff --git a/src/shared/hashmap.h b/src/shared/hashmap.h
index a03ee58..a7c2005 100644
--- a/src/shared/hashmap.h
+++ b/src/shared/hashmap.h
@@ -98,10 +98,7 @@ extern const struct hash_ops uint64_hash_ops;
#if SIZEOF_DEV_T != 8
unsigned long devt_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_;
int devt_compare_func(const void *a, const void *b) _pure_;
-extern const struct hash_ops devt_hash_ops = {
- .hash = devt_hash_func,
- .compare = devt_compare_func
-};
+extern const struct hash_ops devt_hash_ops;
#else
#define devt_hash_func uint64_hash_func
#define devt_compare_func uint64_compare_func
diff --git a/src/shared/log.c b/src/shared/log.c
index 4a40996..1496984 100644
--- a/src/shared/log.c
+++ b/src/shared/log.c
@@ -335,7 +335,7 @@ static int write_to_syslog(
IOVEC_SET_STRING(iovec[0], header_priority);
IOVEC_SET_STRING(iovec[1], header_time);
- IOVEC_SET_STRING(iovec[2], program_invocation_short_name);
+ IOVEC_SET_STRING(iovec[2], "parity");
IOVEC_SET_STRING(iovec[3], header_pid);
IOVEC_SET_STRING(iovec[4], buffer);
@@ -383,7 +383,7 @@ static int write_to_kmsg(
char_array_0(header_pid);
IOVEC_SET_STRING(iovec[0], header_priority);
- IOVEC_SET_STRING(iovec[1], program_invocation_short_name);
+ IOVEC_SET_STRING(iovec[1], "parity");
IOVEC_SET_STRING(iovec[2], header_pid);
IOVEC_SET_STRING(iovec[3], buffer);
IOVEC_SET_STRING(iovec[4], "\n");
diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c
index 6af7163..3271e56 100644
--- a/src/udev/udevadm-control.c
+++ b/src/udev/udevadm-control.c
@@ -41,7 +41,7 @@ static void print_help(void) {
" -p --property=KEY=VALUE Set a global property for all events\n"
" -m --children-max=N Maximum number of children\n"
" --timeout=SECONDS Maximum time to block for a reply\n"
- , program_invocation_short_name);
+ , "parity");
}
static int adm_control(struct udev *udev, int argc, char *argv[]) {
diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c
index 0aec976..a31ac02 100644
--- a/src/udev/udevadm-info.c
+++ b/src/udev/udevadm-info.c
@@ -279,7 +279,7 @@ static void help(void) {
" -P --export-prefix Export the key name with a prefix\n"
" -e --export-db Export the content of the udev database\n"
" -c --cleanup-db Clean up the udev database\n"
- , program_invocation_short_name);
+ , "parity");
}
static int uinfo(struct udev *udev, int argc, char *argv[]) {
diff --git a/src/udev/udevadm-monitor.c b/src/udev/udevadm-monitor.c
index 15ded09..b58dd08 100644
--- a/src/udev/udevadm-monitor.c
+++ b/src/udev/udevadm-monitor.c
@@ -73,7 +73,7 @@ static void help(void) {
" -u --udev Print udev events\n"
" -s --subsystem-match=SUBSYSTEM[/DEVTYPE] Filter events by subsystem\n"
" -t --tag-match=TAG Filter events by tag\n"
- , program_invocation_short_name);
+ , "parity");
}
static int adm_monitor(struct udev *udev, int argc, char *argv[]) {
diff --git a/src/udev/udevadm-settle.c b/src/udev/udevadm-settle.c
index 33597bc..b36a504 100644
--- a/src/udev/udevadm-settle.c
+++ b/src/udev/udevadm-settle.c
@@ -43,7 +43,7 @@ static void help(void) {
" --version Show package version\n"
" -t --timeout=SECONDS Maximum time to wait for events\n"
" -E --exit-if-exists=FILE Stop waiting if file exists\n"
- , program_invocation_short_name);
+ , "parity");
}
static int adm_settle(struct udev *udev, int argc, char *argv[]) {
diff --git a/src/udev/udevadm-test-builtin.c b/src/udev/udevadm-test-builtin.c
index baaeca9..50ed812 100644
--- a/src/udev/udevadm-test-builtin.c
+++ b/src/udev/udevadm-test-builtin.c
@@ -39,7 +39,7 @@ static void help(struct udev *udev) {
" -h --help Print this message\n"
" --version Print version of the program\n\n"
"Commands:\n"
- , program_invocation_short_name);
+ , "parity");
udev_builtin_list(udev);
}
diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c
index 47fd924..a855412 100644
--- a/src/udev/udevadm-test.c
+++ b/src/udev/udevadm-test.c
@@ -39,7 +39,7 @@ static void help(void) {
" --version Show package version\n"
" -a --action=ACTION Set action string\n"
" -N --resolve-names=early|late|never When to resolve names\n"
- , program_invocation_short_name);
+ , "parity");
}
static int adm_test(struct udev *udev, int argc, char *argv[]) {
diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c
index 4dc756a..67787d3 100644
--- a/src/udev/udevadm-trigger.c
+++ b/src/udev/udevadm-trigger.c
@@ -92,7 +92,7 @@ static void help(void) {
" -y --sysname-match=NAME Trigger devices with this /sys path\n"
" --name-match=NAME Trigger devices with this /dev name\n"
" -b --parent-match=NAME Trigger devices with that parent device\n"
- , program_invocation_short_name);
+ , "parity");
}
static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c
index 3e57cf6..b03dfaa 100644
--- a/src/udev/udevadm.c
+++ b/src/udev/udevadm.c
@@ -62,7 +62,7 @@ static int adm_help(struct udev *udev, int argc, char *argv[]) {
printf("%s [--help] [--version] [--debug] COMMAND [COMMAND OPTIONS]\n\n"
"Send control commands or test the device manager.\n\n"
"Commands:\n"
- , program_invocation_short_name);
+ , "parity");
for (i = 0; i < ELEMENTSOF(udevadm_cmds); i++)
if (udevadm_cmds[i]->help != NULL)
@@ -128,7 +128,7 @@ int main(int argc, char *argv[]) {
goto out;
}
- fprintf(stderr, "%s: missing or unknown command\n", program_invocation_short_name);
+ fprintf(stderr, "%s: missing or unknown command\n", "parity");
rc = 2;
out:
mac_selinux_finish();
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index cf826c6..4eec0af 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -1041,7 +1041,7 @@ static void help(void) {
" -t --event-timeout=SECONDS Seconds to wait before terminating an event\n"
" -N --resolve-names=early|late|never\n"
" When to resolve users and groups\n"
- , program_invocation_short_name);
+ , "parity");
}
static int parse_argv(int argc, char *argv[]) {
diff --git a/src/v4l_id/v4l_id.c b/src/v4l_id/v4l_id.c
index 1dce0d5..f65badf 100644
--- a/src/v4l_id/v4l_id.c
+++ b/src/v4l_id/v4l_id.c
@@ -49,7 +49,7 @@ int main(int argc, char *argv[]) {
printf("%s [-h,--help] <device file>\n\n"
"Video4Linux device identification.\n\n"
" -h Print this message\n"
- , program_invocation_short_name);
+ , "parity");
return 0;
case '?':
return -EINVAL;
diff --git a/src/shared/path-util.c b/src/shared/path-util.c
index 0744563..7151356 100644
--- a/src/shared/path-util.c
+++ b/src/shared/path-util.c
@@ -109,7 +109,7 @@ char *path_make_absolute_cwd(const char *p) {
if (path_is_absolute(p))
return strdup(p);
- cwd = get_current_dir_name();
+ cwd = getcwd(malloc(128), 128);
if (!cwd)
return NULL;

View File

@@ -18,7 +18,7 @@ ethcore-bloom-journal = { path = "../util/bloom" }
parity-bytes = "0.1"
hashdb = "0.2.1"
memorydb = "0.2.1"
patricia-trie = "0.2.1"
patricia-trie = "0.2"
patricia-trie-ethereum = { path = "../util/patricia-trie-ethereum" }
parity-crypto = "0.1"
error-chain = { version = "0.12", default-features = false }
@@ -29,9 +29,9 @@ ethcore-stratum = { path = "./stratum", optional = true }
ethcore-transaction = { path = "./transaction" }
ethereum-types = "0.4"
memory-cache = { path = "../util/memory_cache" }
ethabi = "5.1.2"
ethabi-derive = "5.1.3"
ethabi-contract = "5.1.1"
ethabi = "6.0"
ethabi-derive = "6.0"
ethabi-contract = "6.0"
ethjson = { path = "../json" }
ethkey = { path = "../ethkey" }
ethstore = { path = "../ethstore" }

View File

@@ -45,7 +45,7 @@ macro_rules! enum_with_from_u8 {
enum_with_from_u8! {
#[doc = "Virtual machine bytecode instruction."]
#[repr(u8)]
#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug)]
#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug, Hash)]
pub enum Instruction {
#[doc = "halts execution"]
STOP = 0x00,

View File

@@ -233,11 +233,7 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
},
instructions::CREATE | instructions::CREATE2 => {
let gas = Gas::from(schedule.create_gas);
let mem = match instruction {
instructions::CREATE => mem_needed(stack.peek(1), stack.peek(2))?,
instructions::CREATE2 => mem_needed(stack.peek(2), stack.peek(3))?,
_ => unreachable!("instruction can only be CREATE/CREATE2 checked above; qed"),
};
let mem = mem_needed(stack.peek(1), stack.peek(2))?;
Request::GasMemProvide(gas, mem, None)
},

View File

@@ -42,7 +42,7 @@ mod inner {
use ethereum_types::U256;
use interpreter::stack::Stack;
use instructions::{Instruction, InstructionInfo, INSTRUCTIONS};
use instructions::{Instruction, InstructionInfo};
use CostType;
macro_rules! evm_debug {
@@ -97,7 +97,7 @@ mod inner {
&self.spacing,
pc,
Self::color(instruction, info.name),
instruction,
instruction as u8,
current_gas,
Self::as_micro(&time),
));
@@ -117,18 +117,16 @@ mod inner {
pub fn done(&mut self) {
// Print out stats
let infos = &*INSTRUCTIONS;
let mut stats: Vec<(_,_)> = self.stats.drain().collect();
stats.sort_by(|ref a, ref b| b.1.avg().cmp(&a.1.avg()));
print(format!("\n{}-------OPCODE STATS:", self.spacing));
for (instruction, stats) in stats.into_iter() {
let info = infos[instruction as usize];
let info = instruction.info();
print(format!("{}-------{:>19}(0x{:<2x}) count: {:4}, avg: {:10}μs",
self.spacing,
Self::color(instruction, info.name),
instruction,
instruction as u8,
stats.count,
stats.avg(),
));

View File

@@ -274,7 +274,7 @@ impl<Cost: CostType> Interpreter<Cost> {
self.gasometer.as_mut().expect(GASOMETER_PROOF).current_mem_gas = requirements.memory_total_gas;
self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas = self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas - requirements.gas_cost;
evm_debug!({ informant.before_instruction(reader.position, instruction, info, &self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas, &stack) });
evm_debug!({ self.informant.before_instruction(self.reader.position, instruction, info, &self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas, &self.stack) });
let (mem_written, store_written) = match self.do_trace {
true => (Self::mem_written(instruction, &self.stack), Self::store_written(instruction, &self.stack)),
@@ -287,7 +287,7 @@ impl<Cost: CostType> Interpreter<Cost> {
current_gas, ext, instruction, requirements.provide_gas
)?;
evm_debug!({ informant.after_instruction(instruction) });
evm_debug!({ self.informant.after_instruction(instruction) });
if let InstructionResult::UnusedGas(ref gas) = result {
self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas = self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas + *gas;

View File

@@ -13,7 +13,7 @@ parity-bytes = "0.1"
ethcore-transaction = { path = "../transaction" }
ethereum-types = "0.4"
memorydb = "0.2.1"
patricia-trie = "0.2.1"
patricia-trie = "0.2"
patricia-trie-ethereum = { path = "../../util/patricia-trie-ethereum" }
ethcore-network = { path = "../../util/network" }
ethcore-io = { path = "../../util/io" }

View File

@@ -19,17 +19,18 @@
//! will take the raw data received here and extract meaningful results from it.
use std::cmp;
use std::collections::HashMap;
use std::collections::{HashMap, BTreeSet};
use std::marker::PhantomData;
use std::sync::Arc;
use ethcore::executed::{Executed, ExecutionError};
use futures::{Poll, Future};
use futures::sync::oneshot::{self, Receiver, Canceled};
use futures::{Poll, Future, Async};
use futures::sync::oneshot::{self, Receiver};
use network::PeerId;
use parking_lot::{RwLock, Mutex};
use rand;
use std::time::{Duration, SystemTime};
use net::{
self, Handler, PeerStatus, Status, Capabilities,
@@ -49,7 +50,45 @@ pub mod request;
/// The result of execution
pub type ExecutionResult = Result<Executed, ExecutionError>;
/// The default number of retries for OnDemand queries to send to the other nodes
pub const DEFAULT_RETRY_COUNT: usize = 10;
/// The default time limit in milliseconds for inactive (no new peer to connect to) OnDemand queries (0 for unlimited)
pub const DEFAULT_QUERY_TIME_LIMIT: Duration = Duration::from_millis(10000);
const NULL_DURATION: Duration = Duration::from_secs(0);
/// OnDemand related errors
pub mod error {
use futures::sync::oneshot::Canceled;
error_chain! {
foreign_links {
ChannelCanceled(Canceled) #[doc = "Canceled oneshot channel"];
}
errors {
#[doc = "Max number of on-demand query attempts reached without result."]
MaxAttemptReach(query_index: usize) {
description("On-demand query limit reached")
display("On-demand query limit reached on query #{}", query_index)
}
#[doc = "No reply with current peer set, time out occured while waiting for new peers for additional query attempt."]
TimeoutOnNewPeers(query_index: usize, remaining_attempts: usize) {
description("Timeout for On-demand query")
display("Timeout for On-demand query; {} query attempts remain for query #{}", remaining_attempts, query_index)
}
}
}
}
// relevant peer info.
#[derive(Debug, Clone, PartialEq, Eq)]
struct Peer {
status: Status,
capabilities: Capabilities,
@@ -74,13 +113,21 @@ impl Peer {
}
}
/// Either an array of responses or a single error.
type PendingResponse = self::error::Result<Vec<Response>>;
// Attempted request info and sender to put received value.
struct Pending {
requests: basic_request::Batch<CheckedRequest>,
net_requests: basic_request::Batch<NetworkRequest>,
required_capabilities: Capabilities,
responses: Vec<Response>,
sender: oneshot::Sender<Vec<Response>>,
sender: oneshot::Sender<PendingResponse>,
base_query_index: usize,
remaining_query_count: usize,
query_id_history: BTreeSet<PeerId>,
inactive_time_limit: Option<SystemTime>,
}
impl Pending {
@@ -142,7 +189,9 @@ impl Pending {
// if the requests are complete, send the result and consume self.
fn try_complete(self) -> Option<Self> {
if self.requests.is_complete() {
let _ = self.sender.send(self.responses);
if self.sender.send(Ok(self.responses)).is_err() {
debug!(target: "on_demand", "Dropped oneshot channel receiver on complete request at query #{}", self.query_id_history.len());
}
None
} else {
Some(self)
@@ -177,6 +226,25 @@ impl Pending {
self.net_requests = builder.build();
self.required_capabilities = capabilities;
}
// returning no reponse, it will result in an error.
// self is consumed on purpose.
fn no_response(self) {
trace!(target: "on_demand", "Dropping a pending query (no reply) at query #{}", self.query_id_history.len());
let err = self::error::ErrorKind::MaxAttemptReach(self.requests.num_answered());
if self.sender.send(Err(err.into())).is_err() {
debug!(target: "on_demand", "Dropped oneshot channel receiver on no response");
}
}
// returning a peer discovery timeout during query attempts
fn time_out(self) {
trace!(target: "on_demand", "Dropping a pending query (no new peer time out) at query #{}", self.query_id_history.len());
let err = self::error::ErrorKind::TimeoutOnNewPeers(self.requests.num_answered(), self.query_id_history.len());
if self.sender.send(Err(err.into())).is_err() {
debug!(target: "on_demand", "Dropped oneshot channel receiver on time out");
}
}
}
// helper to guess capabilities required for a given batch of network requests.
@@ -230,16 +298,21 @@ fn guess_capabilities(requests: &[CheckedRequest]) -> Capabilities {
/// A future extracting the concrete output type of the generic adapter
/// from a vector of responses.
pub struct OnResponses<T: request::RequestAdapter> {
receiver: Receiver<Vec<Response>>,
receiver: Receiver<PendingResponse>,
_marker: PhantomData<T>,
}
impl<T: request::RequestAdapter> Future for OnResponses<T> {
type Item = T::Out;
type Error = Canceled;
type Error = self::error::Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
self.receiver.poll().map(|async| async.map(T::extract_from))
match self.receiver.poll() {
Ok(Async::Ready(Ok(v))) => Ok(Async::Ready(T::extract_from(v))),
Ok(Async::Ready(Err(e))) => Err(e),
Ok(Async::NotReady) => Ok(Async::NotReady),
Err(e) => Err(e.into()),
}
}
}
@@ -253,9 +326,12 @@ pub struct OnDemand {
in_transit: RwLock<HashMap<ReqId, Pending>>,
cache: Arc<Mutex<Cache>>,
no_immediate_dispatch: bool,
base_retry_count: usize,
query_inactive_time_limit: Option<Duration>,
}
impl OnDemand {
/// Create a new `OnDemand` service with the given cache.
pub fn new(cache: Arc<Mutex<Cache>>) -> Self {
OnDemand {
@@ -264,6 +340,8 @@ impl OnDemand {
in_transit: RwLock::new(HashMap::new()),
cache,
no_immediate_dispatch: false,
base_retry_count: DEFAULT_RETRY_COUNT,
query_inactive_time_limit: Some(DEFAULT_QUERY_TIME_LIMIT),
}
}
@@ -282,11 +360,11 @@ impl OnDemand {
/// Fails if back-references are not coherent.
/// The returned vector of responses will correspond to the requests exactly.
pub fn request_raw(&self, ctx: &BasicContext, requests: Vec<Request>)
-> Result<Receiver<Vec<Response>>, basic_request::NoSuchOutput>
-> Result<Receiver<PendingResponse>, basic_request::NoSuchOutput>
{
let (sender, receiver) = oneshot::channel();
if requests.is_empty() {
assert!(sender.send(Vec::new()).is_ok(), "receiver still in scope; qed");
assert!(sender.send(Ok(Vec::new())).is_ok(), "receiver still in scope; qed");
return Ok(receiver);
}
@@ -325,6 +403,10 @@ impl OnDemand {
required_capabilities: capabilities,
responses,
sender,
base_query_index: 0,
remaining_query_count: 0,
query_id_history: BTreeSet::new(),
inactive_time_limit: None,
});
Ok(receiver)
@@ -363,30 +445,68 @@ impl OnDemand {
let peers = self.peers.read();
*pending = ::std::mem::replace(&mut *pending, Vec::new()).into_iter()
.filter(|pending| !pending.sender.is_canceled())
.filter_map(|pending| {
.filter_map(|mut pending| {
// the peer we dispatch to is chosen randomly
let num_peers = peers.len();
let rng = rand::random::<usize>() % cmp::max(num_peers, 1);
for (peer_id, peer) in peers.iter().chain(peers.iter()).skip(rng).take(num_peers) {
let history_len = pending.query_id_history.len();
let offset = if history_len == 0 {
pending.remaining_query_count = self.base_retry_count;
let rand = rand::random::<usize>();
pending.base_query_index = rand;
rand
} else {
pending.base_query_index + history_len
} % cmp::max(num_peers, 1);
let init_remaining_query_count = pending.remaining_query_count; // to fail in case of big reduction of nb of peers
for (peer_id, peer) in peers.iter().chain(peers.iter())
.skip(offset).take(num_peers) {
// TODO: see which requests can be answered by the cache?
if !peer.can_fulfill(&pending.required_capabilities) {
continue
if pending.remaining_query_count == 0 {
break
}
match ctx.request_from(*peer_id, pending.net_requests.clone()) {
Ok(req_id) => {
trace!(target: "on_demand", "Dispatched request {} to peer {}", req_id, peer_id);
self.in_transit.write().insert(req_id, pending);
return None
if pending.query_id_history.insert(peer_id.clone()) {
if !peer.can_fulfill(&pending.required_capabilities) {
trace!(target: "on_demand", "Peer {} without required capabilities, skipping, {} remaining attempts", peer_id, pending.remaining_query_count);
continue
}
pending.remaining_query_count -= 1;
pending.inactive_time_limit = None;
match ctx.request_from(*peer_id, pending.net_requests.clone()) {
Ok(req_id) => {
trace!(target: "on_demand", "Dispatched request {} to peer {}, {} remaining attempts", req_id, peer_id, pending.remaining_query_count);
self.in_transit.write().insert(req_id, pending);
return None
}
Err(net::Error::NoCredits) | Err(net::Error::NotServer) => {}
Err(e) => debug!(target: "on_demand", "Error dispatching request to peer: {}", e),
}
Err(net::Error::NoCredits) | Err(net::Error::NotServer) => {}
Err(e) => debug!(target: "on_demand", "Error dispatching request to peer: {}", e),
}
}
// TODO: maximum number of failures _when we have peers_.
Some(pending)
if pending.remaining_query_count == 0 {
pending.no_response();
None
} else if init_remaining_query_count == pending.remaining_query_count {
if let Some(query_inactive_time_limit) = self.query_inactive_time_limit {
let now = SystemTime::now();
if let Some(inactive_time_limit) = pending.inactive_time_limit {
if now > inactive_time_limit {
pending.time_out();
return None
}
} else {
debug!(target: "on_demand", "No more peers to query, waiting for {} seconds until dropping query", query_inactive_time_limit.as_secs());
pending.inactive_time_limit = Some(now + query_inactive_time_limit);
}
}
Some(pending)
} else {
Some(pending)
}
})
.collect(); // `pending` now contains all requests we couldn't dispatch.
@@ -406,6 +526,21 @@ impl OnDemand {
self.attempt_dispatch(ctx);
}
}
/// Set the retry count for a query.
pub fn default_retry_number(&mut self, nb_retry: usize) {
self.base_retry_count = nb_retry;
}
/// Set the time limit for a query.
pub fn query_inactive_time_limit(&mut self, inactive_time_limit: Duration) {
self.query_inactive_time_limit = if inactive_time_limit == NULL_DURATION {
None
} else {
Some(inactive_time_limit)
};
}
}
impl Handler for OnDemand {
@@ -458,6 +593,16 @@ impl Handler for OnDemand {
None => return,
};
if responses.is_empty() {
if pending.remaining_query_count == 0 {
pending.no_response();
return;
}
} else {
// do not keep query counter for others elements of this batch
pending.query_id_history.clear();
}
// for each incoming response
// 1. ensure verification data filled.
// 2. pending.requests.supply_response

View File

@@ -13,9 +13,9 @@ ethcore-network-devp2p = { path = "../../util/network-devp2p" }
ethereum-types = "0.4"
log = "0.4"
parking_lot = "0.6"
ethabi = "5.1.2"
ethabi-derive = "5.1.3"
ethabi-contract = "5.1.1"
ethabi = "6.0"
ethabi-derive = "6.0"
ethabi-contract = "6.0"
lru-cache = "0.1"
[dev-dependencies]

View File

@@ -44,16 +44,16 @@ use parking_lot::Mutex;
use ethcore::client::{BlockChainClient, BlockId};
use ethereum_types::{H256, Address};
use ethabi::FunctionOutputDecoder;
use network::{ConnectionFilter, ConnectionDirection};
use devp2p::NodeId;
use_contract!(peer_set, "PeerSet", "res/peer_set.json");
use_contract!(peer_set, "res/peer_set.json");
const MAX_CACHE_SIZE: usize = 4096;
/// Connection filter that uses a contract to manage permissions.
pub struct NodeFilter {
contract: peer_set::PeerSet,
client: Weak<BlockChainClient>,
contract_address: Address,
permission_cache: Mutex<LruCache<(H256, NodeId), bool>>,
@@ -63,7 +63,6 @@ impl NodeFilter {
/// Create a new instance. Accepts a contract address.
pub fn new(client: Weak<BlockChainClient>, contract_address: Address) -> NodeFilter {
NodeFilter {
contract: peer_set::PeerSet::default(),
client,
contract_address,
permission_cache: Mutex::new(LruCache::new(MAX_CACHE_SIZE)),
@@ -96,9 +95,9 @@ impl ConnectionFilter for NodeFilter {
let id_low = H256::from_slice(&connecting_id[0..32]);
let id_high = H256::from_slice(&connecting_id[32..64]);
let allowed = self.contract.functions()
.connection_allowed()
.call(own_low, own_high, id_low, id_high, &|data| client.call_contract(BlockId::Latest, address, data))
let (data, decoder) = peer_set::functions::connection_allowed::call(own_low, own_high, id_low, id_high);
let allowed = client.call_contract(BlockId::Latest, address, data)
.and_then(|value| decoder.decode(&value).map_err(|e| e.to_string()))
.unwrap_or_else(|e| {
debug!("Error callling peer set contract: {:?}", e);
false

View File

@@ -7,9 +7,9 @@ authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
error-chain = { version = "0.12", default-features = false }
ethabi = "5.1.2"
ethabi-derive = "5.1.3"
ethabi-contract = "5.1.1"
ethabi = "6.0"
ethabi-derive = "6.0"
ethabi-contract = "6.0"
ethcore = { path = ".." }
parity-bytes = "0.1"
parity-crypto = "0.1"
@@ -26,7 +26,7 @@ heapsize = "0.4"
keccak-hash = "0.1.2"
log = "0.4"
parking_lot = "0.6"
patricia-trie = "0.2.1"
patricia-trie = "0.2"
patricia-trie-ethereum = { path = "../../util/patricia-trie-ethereum" }
rand = "0.3"
rlp = { version = "0.2.4", features = ["ethereum"] }

View File

@@ -89,11 +89,12 @@ use ethcore::miner::{self, Miner, MinerService, pool_client::NonceCache};
use ethcore::trace::{Tracer, VMTracer};
use rustc_hex::FromHex;
use ethkey::Password;
use ethabi::FunctionOutputDecoder;
// Source avaiable at https://github.com/parity-contracts/private-tx/blob/master/contracts/PrivateContract.sol
const DEFAULT_STUB_CONTRACT: &'static str = include_str!("../res/private.evm");
use_contract!(private, "PrivateContract", "res/private.json");
use_contract!(private_contract, "res/private.json");
/// Initialization vector length.
const INIT_VEC_LEN: usize = 16;
@@ -425,31 +426,23 @@ impl Provider where {
}
fn get_decrypted_state(&self, address: &Address, block: BlockId) -> Result<Bytes, Error> {
let contract = private::PrivateContract::default();
let state = contract.functions()
.state()
.call(&|data| self.client.call_contract(block, *address, data))
.map_err(|e| ErrorKind::Call(format!("Contract call failed {:?}", e)))?;
let (data, decoder) = private_contract::functions::state::call();
let value = self.client.call_contract(block, *address, data)?;
let state = decoder.decode(&value).map_err(|e| ErrorKind::Call(format!("Contract call failed {:?}", e)))?;
self.decrypt(address, &state)
}
fn get_decrypted_code(&self, address: &Address, block: BlockId) -> Result<Bytes, Error> {
let contract = private::PrivateContract::default();
let code = contract.functions()
.code()
.call(&|data| self.client.call_contract(block, *address, data))
.map_err(|e| ErrorKind::Call(format!("Contract call failed {:?}", e)))?;
self.decrypt(address, &code)
let (data, decoder) = private_contract::functions::code::call();
let value = self.client.call_contract(block, *address, data)?;
let state = decoder.decode(&value).map_err(|e| ErrorKind::Call(format!("Contract call failed {:?}", e)))?;
self.decrypt(address, &state)
}
pub fn get_contract_nonce(&self, address: &Address, block: BlockId) -> Result<U256, Error> {
let contract = private::PrivateContract::default();
Ok(contract.functions()
.nonce()
.call(&|data| self.client.call_contract(block, *address, data))
.map_err(|e| ErrorKind::Call(format!("Contract call failed {:?}", e)))?)
let (data, decoder) = private_contract::functions::nonce::call();
let value = self.client.call_contract(block, *address, data)?;
decoder.decode(&value).map_err(|e| ErrorKind::Call(format!("Contract call failed {:?}", e)).into())
}
fn snapshot_to_storage(raw: Bytes) -> HashMap<H256, H256> {
@@ -524,13 +517,11 @@ impl Provider where {
fn generate_constructor(validators: &[Address], code: Bytes, storage: Bytes) -> Bytes {
let constructor_code = DEFAULT_STUB_CONTRACT.from_hex().expect("Default contract code is valid");
let private = private::PrivateContract::default();
private.constructor(constructor_code, validators.iter().map(|a| *a).collect::<Vec<Address>>(), code, storage)
private_contract::constructor(constructor_code, validators.iter().map(|a| *a).collect::<Vec<Address>>(), code, storage)
}
fn generate_set_state_call(signatures: &[Signature], storage: Bytes) -> Bytes {
let private = private::PrivateContract::default();
private.functions().set_state().input(
private_contract::functions::set_state::encode_input(
storage,
signatures.iter().map(|s| {
let mut v: [u8; 32] = [0; 32];
@@ -604,11 +595,9 @@ impl Provider where {
/// Returns private validators for a contract.
pub fn get_validators(&self, block: BlockId, address: &Address) -> Result<Vec<Address>, Error> {
let contract = private::PrivateContract::default();
Ok(contract.functions()
.get_validators()
.call(&|data| self.client.call_contract(block, *address, data))
.map_err(|e| ErrorKind::Call(format!("Contract call failed {:?}", e)))?)
let (data, decoder) = private_contract::functions::get_validators::call();
let value = self.client.call_contract(block, *address, data)?;
decoder.decode(&value).map_err(|e| ErrorKind::Call(format!("Contract call failed {:?}", e)).into())
}
}

View File

@@ -1,16 +1,16 @@
{
"name": "Byzantium (Test)",
"name": "Constantinople (test)",
"engine": {
"Ethash": {
"params": {
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x29A2241AF62C0000",
"blockReward": "0x1BC16D674EC80000",
"homesteadTransition": "0x0",
"eip100bTransition": "0x0",
"difficultyBombDelays": {
"0": 3000000
"0": 5000000
}
}
}
@@ -30,11 +30,13 @@
"eip161abcTransition": "0x0",
"eip161dTransition": "0x0",
"eip140Transition": "0x0",
"eip210Transition": "0x0",
"eip211Transition": "0x0",
"eip214Transition": "0x0",
"eip155Transition": "0x0",
"eip658Transition": "0x0",
"eip145Transition": "0x0",
"eip1014Transition": "0x0",
"eip1052Transition": "0x0",
"eip1283Transition": "0x0"
},
"genesis": {

View File

@@ -0,0 +1,54 @@
{
"name": "EIP210 (test)",
"engine": {
"Ethash": {
"params": {
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"homesteadTransition": "0x0"
}
}
},
"params": {
"gasLimitBoundDivisor": "0x0400",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"accountStartNonce": "0x00",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1",
"maxCodeSize": 24576,
"maxCodeSizeTransition": "0x0",
"eip98Transition": "0xffffffffffffffff",
"eip150Transition": "0x0",
"eip160Transition": "0x0",
"eip161abcTransition": "0x0",
"eip161dTransition": "0x0",
"eip210Transition": "0x0"
},
"genesis": {
"seal": {
"ethereum": {
"nonce": "0x0000000000000042",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
},
"difficulty": "0x400000000",
"author": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
"gasLimit": "0x1388"
},
"accounts": {
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
"0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
"0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
"0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
"0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x00", "pricing": { "modexp": { "divisor": 100 } } } },
"0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": "0x00", "pricing": { "linear": { "base": 500, "word": 0 } } } },
"0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": "0x00", "pricing": { "linear": { "base": 2000, "word": 0 } } } },
"0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": "0x00", "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } }
}
}

View File

@@ -1978,7 +1978,8 @@
"enode://6332792c4a00e3e4ee0926ed89e0d27ef985424d97b6a45bf0f23e51f0dcb5e66b875777506458aea7af6f9e4ffb69f43f3778ee73c81ed9d34c51c4b16b0b0f@52.232.243.152:30303",
"enode://94c15d1b9e2fe7ce56e458b9a3b672ef11894ddedd0c6f247e0f1d3487f52b66208fb4aeb8179fce6e3a749ea93ed147c37976d67af557508d199d9594c35f09@192.81.208.223:30303",
"enode://30b7ab30a01c124a6cceca36863ece12c4f5fa68e3ba9b0b51407ccc002eeed3b3102d20a88f1c1d3c3154e2449317b8ef95090e77b312d5cc39354f86d5d606@52.176.7.10:30303",
"enode://865a63255b3bb68023b6bffd5095118fcc13e79dcf014fe4e47e065c350c7cc72af2e53eff895f11ba1bbb6a2b33271c1116ee870f266618eadfc2e78aa7349c@52.176.100.77:30303"
"enode://865a63255b3bb68023b6bffd5095118fcc13e79dcf014fe4e47e065c350c7cc72af2e53eff895f11ba1bbb6a2b33271c1116ee870f266618eadfc2e78aa7349c@52.176.100.77:30303",
"enode://691907d5a7dee24884b791e799183e5db01f4fe0b6e9b795ffaf5cf85a3023a637f2abadc82fc0da168405092df869126377c5f190794cd2d1c067245ae2b1ce@54.180.81.196:30303"
],
"accounts": {
"0000000000000000000000000000000000000000": { "balance": "1" },

View File

@@ -86,7 +86,7 @@ pub use types::block_status::BlockStatus;
pub use blockchain::CacheSize as BlockChainCacheSize;
pub use verification::QueueInfo as BlockQueueInfo;
use_contract!(registry, "Registry", "res/contracts/registrar.json");
use_contract!(registry, "res/contracts/registrar.json");
const MAX_ANCIENT_BLOCKS_QUEUE_SIZE: usize = 4096;
// Max number of blocks imported at once.
@@ -232,8 +232,6 @@ pub struct Client {
/// An action to be done if a mode/spec_name change happens
on_user_defaults_change: Mutex<Option<Box<FnMut(Option<Mode>) + 'static + Send>>>,
/// Link to a registry object useful for looking up names
registrar: registry::Registry,
registrar_address: Option<Address>,
/// A closure to call when we want to restart the client
@@ -776,7 +774,6 @@ impl Client {
factories: factories,
history: history,
on_user_defaults_change: Mutex::new(None),
registrar: registry::Registry::default(),
registrar_address,
exit_handler: Mutex::new(None),
importer,
@@ -1153,7 +1150,8 @@ impl Client {
},
};
snapshot::take_snapshot(&*self.engine, &self.chain.read(), start_hash, db.as_hashdb(), writer, p)?;
let processing_threads = self.config.snapshot.processing_threads;
snapshot::take_snapshot(&*self.engine, &self.chain.read(), start_hash, db.as_hashdb(), writer, p, processing_threads)?;
Ok(())
}
@@ -1365,17 +1363,17 @@ impl BlockChainTrait for Client {}
impl RegistryInfo for Client {
fn registry_address(&self, name: String, block: BlockId) -> Option<Address> {
use ethabi::FunctionOutputDecoder;
let address = self.registrar_address?;
self.registrar.functions()
.get_address()
.call(keccak(name.as_bytes()), "A", &|data| self.call_contract(block, address, data))
.ok()
.and_then(|a| if a.is_zero() {
None
} else {
Some(a)
})
let (data, decoder) = registry::functions::get_address::call(keccak(name.as_bytes()), "A");
let value = decoder.decode(&self.call_contract(block, address, data).ok()?).ok()?;
if value.is_zero() {
None
} else {
Some(value)
}
}
}

View File

@@ -19,6 +19,7 @@ use std::fmt::{Display, Formatter, Error as FmtError};
use verification::{VerifierType, QueueConfig};
use journaldb;
use snapshot::SnapshotConfiguration;
pub use std::time::Duration;
pub use blockchain::Config as BlockChainConfig;
@@ -120,6 +121,8 @@ pub struct ClientConfig {
pub check_seal: bool,
/// Maximal number of transactions queued for verification in a separate thread.
pub transaction_verification_queue_size: usize,
/// Snapshot configuration
pub snapshot: SnapshotConfiguration,
}
impl Default for ClientConfig {
@@ -144,6 +147,7 @@ impl Default for ClientConfig {
history_mem: 32 * mb,
check_seal: true,
transaction_verification_queue_size: 8192,
snapshot: Default::default(),
}
}
}

View File

@@ -30,7 +30,7 @@ use trace;
use types::BlockNumber;
use super::{SystemOrCodeCall, SystemOrCodeCallKind};
use_contract!(block_reward_contract, "BlockReward", "res/contracts/block_reward.json");
use_contract!(block_reward_contract, "res/contracts/block_reward.json");
/// The kind of block reward.
/// Depending on the consensus engine the allocated block reward might have
@@ -81,7 +81,6 @@ impl Into<trace::RewardType> for RewardKind {
#[derive(PartialEq, Debug)]
pub struct BlockRewardContract {
kind: SystemOrCodeCallKind,
block_reward_contract: block_reward_contract::BlockReward,
}
impl BlockRewardContract {
@@ -89,7 +88,6 @@ impl BlockRewardContract {
pub fn new(kind: SystemOrCodeCallKind) -> BlockRewardContract {
BlockRewardContract {
kind,
block_reward_contract: block_reward_contract::BlockReward::default(),
}
}
@@ -114,9 +112,7 @@ impl BlockRewardContract {
beneficiaries: &[(Address, RewardKind)],
caller: &mut SystemOrCodeCall,
) -> Result<Vec<(Address, U256)>, Error> {
let reward = self.block_reward_contract.functions().reward();
let input = reward.input(
let input = block_reward_contract::functions::reward::encode_input(
beneficiaries.iter().map(|&(address, _)| H160::from(address)),
beneficiaries.iter().map(|&(_, ref reward_kind)| u16::from(*reward_kind)),
);

View File

@@ -30,13 +30,12 @@ use machine::{AuxiliaryData, Call, EthereumMachine};
use super::{ValidatorSet, SimpleList, SystemCall};
use super::safe_contract::ValidatorSafeContract;
use_contract!(validator_report, "ValidatorReport", "res/contracts/validator_report.json");
use_contract!(validator_report, "res/contracts/validator_report.json");
/// A validator contract with reporting.
pub struct ValidatorContract {
contract_address: Address,
validators: ValidatorSafeContract,
provider: validator_report::ValidatorReport,
client: RwLock<Option<Weak<EngineClient>>>, // TODO [keorn]: remove
}
@@ -45,7 +44,6 @@ impl ValidatorContract {
ValidatorContract {
contract_address,
validators: ValidatorSafeContract::new(contract_address),
provider: validator_report::ValidatorReport::default(),
client: RwLock::new(None),
}
}
@@ -111,7 +109,7 @@ impl ValidatorSet for ValidatorContract {
}
fn report_malicious(&self, address: &Address, _set_block: BlockNumber, block: BlockNumber, proof: Bytes) {
let data = self.provider.functions().report_malicious().input(*address, block, proof);
let data = validator_report::functions::report_malicious::encode_input(*address, block, proof);
match self.transact(data) {
Ok(_) => warn!(target: "engine", "Reported malicious validator {}", address),
Err(s) => warn!(target: "engine", "Validator {} could not be reported {}", address, s),
@@ -119,7 +117,7 @@ impl ValidatorSet for ValidatorContract {
}
fn report_benign(&self, address: &Address, _set_block: BlockNumber, block: BlockNumber) {
let data = self.provider.functions().report_benign().input(*address, block);
let data = validator_report::functions::report_benign::encode_input(*address, block);
match self.transact(data) {
Ok(_) => warn!(target: "engine", "Reported benign validator misbehaviour {}", address),
Err(s) => warn!(target: "engine", "Validator {} could not be reported {}", address, s),

View File

@@ -33,8 +33,9 @@ use std::sync::{Weak, Arc};
use super::{SystemCall, ValidatorSet};
use super::simple_list::SimpleList;
use unexpected::Mismatch;
use ethabi::FunctionOutputDecoder;
use_contract!(validator_set, "ValidatorSet", "res/contracts/validator_set.json");
use_contract!(validator_set, "res/contracts/validator_set.json");
const MEMOIZE_CAPACITY: usize = 500;
@@ -50,12 +51,11 @@ lazy_static! {
struct StateProof {
contract_address: Address,
header: Header,
provider: validator_set::ValidatorSet,
}
impl ::engines::StateDependentProof<EthereumMachine> for StateProof {
fn generate_proof(&self, caller: &Call) -> Result<Vec<u8>, String> {
prove_initial(&self.provider, self.contract_address, &self.header, caller)
prove_initial(self.contract_address, &self.header, caller)
}
fn check_proof(&self, machine: &EthereumMachine, proof: &[u8]) -> Result<(), String> {
@@ -65,7 +65,7 @@ impl ::engines::StateDependentProof<EthereumMachine> for StateProof {
return Err("wrong header in proof".into());
}
check_first_proof(machine, &self.provider, self.contract_address, header, &state_items).map(|_| ())
check_first_proof(machine, self.contract_address, header, &state_items).map(|_| ())
}
}
@@ -73,7 +73,6 @@ impl ::engines::StateDependentProof<EthereumMachine> for StateProof {
pub struct ValidatorSafeContract {
contract_address: Address,
validators: RwLock<MemoryLruCache<H256, SimpleList>>,
provider: validator_set::ValidatorSet,
client: RwLock<Option<Weak<EngineClient>>>, // TODO [keorn]: remove
}
@@ -89,7 +88,7 @@ fn encode_first_proof(header: &Header, state_items: &[Vec<u8>]) -> Bytes {
}
// check a first proof: fetch the validator set at the given block.
fn check_first_proof(machine: &EthereumMachine, provider: &validator_set::ValidatorSet, contract_address: Address, old_header: Header, state_items: &[DBValue])
fn check_first_proof(machine: &EthereumMachine, contract_address: Address, old_header: Header, state_items: &[DBValue])
-> Result<Vec<Address>, String>
{
use transaction::{Action, Transaction};
@@ -114,31 +113,31 @@ fn check_first_proof(machine: &EthereumMachine, provider: &validator_set::Valida
// check state proof using given machine.
let number = old_header.number();
provider.functions().get_validators().call(&|data| {
let from = Address::default();
let tx = Transaction {
nonce: machine.account_start_nonce(number),
action: Action::Call(contract_address),
gas: PROVIDED_GAS.into(),
gas_price: U256::default(),
value: U256::default(),
data,
}.fake_sign(from);
let (data, decoder) = validator_set::functions::get_validators::call();
let res = ::state::check_proof(
state_items,
*old_header.state_root(),
&tx,
machine,
&env_info,
);
let from = Address::default();
let tx = Transaction {
nonce: machine.account_start_nonce(number),
action: Action::Call(contract_address),
gas: PROVIDED_GAS.into(),
gas_price: U256::default(),
value: U256::default(),
data,
}.fake_sign(from);
match res {
::state::ProvedExecution::BadProof => Err("Bad proof".into()),
::state::ProvedExecution::Failed(e) => Err(format!("Failed call: {}", e)),
::state::ProvedExecution::Complete(e) => Ok(e.output),
}
}).map_err(|err| err.to_string())
let res = ::state::check_proof(
state_items,
*old_header.state_root(),
&tx,
machine,
&env_info,
);
match res {
::state::ProvedExecution::BadProof => Err("Bad proof".into()),
::state::ProvedExecution::Failed(e) => Err(format!("Failed call: {}", e)),
::state::ProvedExecution::Complete(e) => decoder.decode(&e.output).map_err(|e| e.to_string()),
}
}
fn decode_first_proof(rlp: &Rlp) -> Result<(Header, Vec<DBValue>), ::error::Error> {
@@ -167,32 +166,26 @@ fn decode_proof(rlp: &Rlp) -> Result<(Header, Vec<Receipt>), ::error::Error> {
// given a provider and caller, generate proof. this will just be a state proof
// of `getValidators`.
fn prove_initial(provider: &validator_set::ValidatorSet, contract_address: Address, header: &Header, caller: &Call) -> Result<Vec<u8>, String> {
fn prove_initial(contract_address: Address, header: &Header, caller: &Call) -> Result<Vec<u8>, String> {
use std::cell::RefCell;
let epoch_proof = RefCell::new(None);
let res = {
let caller = |data| {
let (result, proof) = caller(contract_address, data)?;
*epoch_proof.borrow_mut() = Some(encode_first_proof(header, &proof));
Ok(result)
};
provider.functions().get_validators().call(&caller)
.map_err(|err| err.to_string())
let validators = {
let (data, decoder) = validator_set::functions::get_validators::call();
let (value, proof) = caller(contract_address, data)?;
*epoch_proof.borrow_mut() = Some(encode_first_proof(header, &proof));
decoder.decode(&value).map_err(|e| e.to_string())?
};
res.map(|validators| {
let proof = epoch_proof.into_inner().expect("epoch_proof always set after call; qed");
let proof = epoch_proof.into_inner().expect("epoch_proof always set after call; qed");
trace!(target: "engine", "obtained proof for initial set: {} validators, {} bytes",
validators.len(), proof.len());
trace!(target: "engine", "obtained proof for initial set: {} validators, {} bytes",
validators.len(), proof.len());
info!(target: "engine", "Signal for switch to contract-based validator set.");
info!(target: "engine", "Initial contract validators: {:?}", validators);
info!(target: "engine", "Signal for switch to contract-based validator set.");
info!(target: "engine", "Initial contract validators: {:?}", validators);
proof
})
Ok(proof)
}
impl ValidatorSafeContract {
@@ -200,7 +193,6 @@ impl ValidatorSafeContract {
ValidatorSafeContract {
contract_address,
validators: RwLock::new(MemoryLruCache::new(MEMOIZE_CAPACITY)),
provider: validator_set::ValidatorSet::default(),
client: RwLock::new(None),
}
}
@@ -208,8 +200,11 @@ impl ValidatorSafeContract {
/// Queries the state and gets the set of validators.
fn get_list(&self, caller: &Call) -> Option<SimpleList> {
let contract_address = self.contract_address;
let caller = move |data| caller(contract_address, data).map(|x| x.0);
match self.provider.functions().get_validators().call(&caller) {
let (data, decoder) = validator_set::functions::get_validators::call();
let value = caller(contract_address, data).and_then(|x| decoder.decode(&x.0).map_err(|e| e.to_string()));
match value {
Ok(new) => {
debug!(target: "engine", "Set of validators obtained: {:?}", new);
Some(SimpleList::new(new))
@@ -259,7 +254,6 @@ impl ValidatorSafeContract {
log.topics[1] == *header.parent_hash()
};
let event = self.provider.events().initiate_change();
//// iterate in reverse because only the _last_ change in a given
//// block actually has any effect.
//// the contract should only increment the nonce once.
@@ -269,7 +263,7 @@ impl ValidatorSafeContract {
.flat_map(|r| r.logs.iter())
.filter(move |l| check_log(l))
.filter_map(|log| {
event.parse_log((log.topics.clone(), log.data.clone()).into()).ok()
validator_set::events::initiate_change::parse_log((log.topics.clone(), log.data.clone()).into()).ok()
});
// only last log is taken into account
@@ -296,7 +290,7 @@ impl ValidatorSet for ValidatorSafeContract {
}
fn on_epoch_begin(&self, _first: bool, _header: &Header, caller: &mut SystemCall) -> Result<(), ::error::Error> {
let data = self.provider.functions().finalize_change().input();
let data = validator_set::functions::finalize_change::encode_input();
caller(self.contract_address, data)
.map(|_| ())
.map_err(::engines::EngineError::FailedSystemCall)
@@ -304,7 +298,7 @@ impl ValidatorSet for ValidatorSafeContract {
}
fn genesis_epoch_data(&self, header: &Header, call: &Call) -> Result<Vec<u8>, String> {
prove_initial(&self.provider, self.contract_address, header, call)
prove_initial(self.contract_address, header, call)
}
fn is_epoch_end(&self, _first: bool, _chain_head: &Header) -> Option<Vec<u8>> {
@@ -322,7 +316,6 @@ impl ValidatorSet for ValidatorSafeContract {
let state_proof = Arc::new(StateProof {
contract_address: self.contract_address,
header: header.clone(),
provider: validator_set::ValidatorSet::default(),
});
return ::engines::EpochChange::Yes(::engines::Proof::WithState(state_proof as Arc<_>));
}
@@ -361,7 +354,7 @@ impl ValidatorSet for ValidatorSafeContract {
let (old_header, state_items) = decode_first_proof(&rlp)?;
let number = old_header.number();
let old_hash = old_header.hash();
let addresses = check_first_proof(machine, &self.provider, self.contract_address, old_header, &state_items)
let addresses = check_first_proof(machine, self.contract_address, old_header, &state_items)
.map_err(::engines::EngineError::InsufficientProof)?;
trace!(target: "engine", "extracted epoch set at #{}: {} addresses",

View File

@@ -152,6 +152,9 @@ pub fn new_frontier_test_machine() -> EthereumMachine { load_machine(include_byt
/// Create a new Foundation Homestead-era chain spec as though it never changed from Frontier.
pub fn new_homestead_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/homestead_test.json")) }
/// Create a new Foundation Homestead-EIP210-era chain spec as though it never changed from Homestead/Frontier.
pub fn new_eip210_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/eip210_test.json")) }
/// Create a new Foundation Byzantium era spec.
pub fn new_byzantium_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/byzantium_test.json")) }

View File

@@ -18,16 +18,15 @@
use client::{RegistryInfo, CallContract, BlockId};
use transaction::SignedTransaction;
use ethabi::FunctionOutputDecoder;
use_contract!(service_transaction, "ServiceTransaction", "res/contracts/service_transaction.json");
use_contract!(service_transaction, "res/contracts/service_transaction.json");
const SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME: &'static str = "service_transaction_checker";
/// Service transactions checker.
#[derive(Default, Clone)]
pub struct ServiceTransactionChecker {
contract: service_transaction::ServiceTransaction,
}
pub struct ServiceTransactionChecker;
impl ServiceTransactionChecker {
/// Checks if given address is whitelisted to send service transactions.
@@ -45,9 +44,8 @@ impl ServiceTransactionChecker {
trace!(target: "txqueue", "[{:?}] Checking service transaction checker contract from {}", hash, sender);
self.contract.functions()
.certified()
.call(sender, &|data| client.call_contract(BlockId::Latest, address, data))
.map_err(|e| e.to_string())
let (data, decoder) = service_transaction::functions::certified::call(sender);
let value = client.call_contract(BlockId::Latest, address, data)?;
decoder.decode(&value).map_err(|e| e.to_string())
}
}

View File

@@ -30,7 +30,7 @@ use machine::EthereumMachine;
use ids::BlockId;
use header::Header;
use receipt::Receipt;
use snapshot::{Error, ManifestData};
use snapshot::{Error, ManifestData, Progress};
use itertools::{Position, Itertools};
use rlp::{RlpStream, Rlp};
@@ -59,6 +59,7 @@ impl SnapshotComponents for PoaSnapshot {
chain: &BlockChain,
block_at: H256,
sink: &mut ChunkSink,
_progress: &Progress,
preferred_size: usize,
) -> Result<(), Error> {
let number = chain.block_number(&block_at)

View File

@@ -22,7 +22,7 @@ use std::sync::Arc;
use blockchain::{BlockChain, BlockChainDB};
use engines::EthEngine;
use snapshot::{Error, ManifestData};
use snapshot::{Error, ManifestData, Progress};
use ethereum_types::H256;
@@ -49,6 +49,7 @@ pub trait SnapshotComponents: Send {
chain: &BlockChain,
block_at: H256,
chunk_sink: &mut ChunkSink,
progress: &Progress,
preferred_size: usize,
) -> Result<(), Error>;

View File

@@ -28,7 +28,7 @@ use std::sync::Arc;
use blockchain::{BlockChain, BlockChainDB, BlockProvider};
use engines::EthEngine;
use snapshot::{Error, ManifestData};
use snapshot::{Error, ManifestData, Progress};
use snapshot::block::AbridgedBlock;
use ethereum_types::H256;
use kvdb::KeyValueDB;
@@ -65,6 +65,7 @@ impl SnapshotComponents for PowSnapshot {
chain: &BlockChain,
block_at: H256,
chunk_sink: &mut ChunkSink,
progress: &Progress,
preferred_size: usize,
) -> Result<(), Error> {
PowWorker {
@@ -72,6 +73,7 @@ impl SnapshotComponents for PowSnapshot {
rlps: VecDeque::new(),
current_hash: block_at,
writer: chunk_sink,
progress: progress,
preferred_size: preferred_size,
}.chunk_all(self.blocks)
}
@@ -96,6 +98,7 @@ struct PowWorker<'a> {
rlps: VecDeque<Bytes>,
current_hash: H256,
writer: &'a mut ChunkSink<'a>,
progress: &'a Progress,
preferred_size: usize,
}
@@ -138,6 +141,7 @@ impl<'a> PowWorker<'a> {
last = self.current_hash;
self.current_hash = block.header_view().parent_hash();
self.progress.blocks.fetch_add(1, Ordering::SeqCst);
}
if loaded_size != 0 {

View File

@@ -20,6 +20,7 @@
//! https://wiki.parity.io/Warp-Sync-Snapshot-Format
use std::collections::{HashMap, HashSet};
use std::cmp;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use hash::{keccak, KECCAK_NULL_RLP, KECCAK_EMPTY};
@@ -43,6 +44,7 @@ use trie::{Trie, TrieMut};
use ethtrie::{TrieDB, TrieDBMut};
use rlp::{RlpStream, Rlp};
use bloom_journal::Bloom;
use num_cpus;
use self::io::SnapshotWriter;
@@ -88,6 +90,28 @@ const MAX_CHUNK_SIZE: usize = PREFERRED_CHUNK_SIZE / 4 * 5;
const MIN_SUPPORTED_STATE_CHUNK_VERSION: u64 = 1;
// current state chunk version.
const STATE_CHUNK_VERSION: u64 = 2;
/// number of snapshot subparts, must be a power of 2 in [1; 256]
const SNAPSHOT_SUBPARTS: usize = 16;
/// Maximum number of snapshot subparts (must be a multiple of `SNAPSHOT_SUBPARTS`)
const MAX_SNAPSHOT_SUBPARTS: usize = 256;
/// Configuration for the Snapshot service
#[derive(Debug, Clone, PartialEq)]
pub struct SnapshotConfiguration {
/// If `true`, no periodic snapshots will be created
pub no_periodic: bool,
/// Number of threads for creating snapshots
pub processing_threads: usize,
}
impl Default for SnapshotConfiguration {
fn default() -> Self {
SnapshotConfiguration {
no_periodic: false,
processing_threads: ::std::cmp::max(1, num_cpus::get() / 2),
}
}
}
/// A progress indicator for snapshots.
#[derive(Debug, Default)]
@@ -130,7 +154,8 @@ pub fn take_snapshot<W: SnapshotWriter + Send>(
block_at: H256,
state_db: &HashDB<KeccakHasher>,
writer: W,
p: &Progress
p: &Progress,
processing_threads: usize,
) -> Result<(), Error> {
let start_header = chain.block_header_data(&block_at)
.ok_or(Error::InvalidStartingBlock(BlockId::Hash(block_at)))?;
@@ -142,17 +167,45 @@ pub fn take_snapshot<W: SnapshotWriter + Send>(
let writer = Mutex::new(writer);
let chunker = engine.snapshot_components().ok_or(Error::SnapshotsUnsupported)?;
let snapshot_version = chunker.current_version();
let (state_hashes, block_hashes) = scope(|scope| {
let (state_hashes, block_hashes) = scope(|scope| -> Result<(Vec<H256>, Vec<H256>), Error> {
let writer = &writer;
let block_guard = scope.spawn(move || chunk_secondary(chunker, chain, block_at, writer, p));
let state_res = chunk_state(state_db, &state_root, writer, p);
state_res.and_then(|state_hashes| {
block_guard.join().map(|block_hashes| (state_hashes, block_hashes))
})
// The number of threads must be between 1 and SNAPSHOT_SUBPARTS
assert!(processing_threads >= 1, "Cannot use less than 1 threads for creating snapshots");
let num_threads: usize = cmp::min(processing_threads, SNAPSHOT_SUBPARTS);
info!(target: "snapshot", "Using {} threads for Snapshot creation.", num_threads);
let mut state_guards = Vec::with_capacity(num_threads as usize);
for thread_idx in 0..num_threads {
let state_guard = scope.spawn(move || -> Result<Vec<H256>, Error> {
let mut chunk_hashes = Vec::new();
for part in (thread_idx..SNAPSHOT_SUBPARTS).step_by(num_threads) {
debug!(target: "snapshot", "Chunking part {} in thread {}", part, thread_idx);
let mut hashes = chunk_state(state_db, &state_root, writer, p, Some(part))?;
chunk_hashes.append(&mut hashes);
}
Ok(chunk_hashes)
});
state_guards.push(state_guard);
}
let block_hashes = block_guard.join()?;
let mut state_hashes = Vec::new();
for guard in state_guards {
let part_state_hashes = guard.join()?;
state_hashes.extend(part_state_hashes);
}
debug!(target: "snapshot", "Took a snapshot of {} accounts", p.accounts.load(Ordering::SeqCst));
Ok((state_hashes, block_hashes))
})?;
info!("produced {} state chunks and {} block chunks.", state_hashes.len(), block_hashes.len());
info!(target: "snapshot", "produced {} state chunks and {} block chunks.", state_hashes.len(), block_hashes.len());
let manifest_data = ManifestData {
version: snapshot_version,
@@ -200,6 +253,7 @@ pub fn chunk_secondary<'a>(mut chunker: Box<SnapshotComponents>, chain: &'a Bloc
chain,
start_hash,
&mut chunk_sink,
progress,
PREFERRED_CHUNK_SIZE,
)?;
}
@@ -263,10 +317,12 @@ impl<'a> StateChunker<'a> {
/// Walk the given state database starting from the given root,
/// creating chunks and writing them out.
/// `part` is a number between 0 and 15, which describe which part of
/// the tree should be chunked.
///
/// Returns a list of hashes of chunks created, or any error it may
/// have encountered.
pub fn chunk_state<'a>(db: &HashDB<KeccakHasher>, root: &H256, writer: &Mutex<SnapshotWriter + 'a>, progress: &'a Progress) -> Result<Vec<H256>, Error> {
pub fn chunk_state<'a>(db: &HashDB<KeccakHasher>, root: &H256, writer: &Mutex<SnapshotWriter + 'a>, progress: &'a Progress, part: Option<usize>) -> Result<Vec<H256>, Error> {
let account_trie = TrieDB::new(db, &root)?;
let mut chunker = StateChunker {
@@ -281,11 +337,33 @@ pub fn chunk_state<'a>(db: &HashDB<KeccakHasher>, root: &H256, writer: &Mutex<Sn
let mut used_code = HashSet::new();
// account_key here is the address' hash.
for item in account_trie.iter()? {
let mut account_iter = account_trie.iter()?;
let mut seek_to = None;
if let Some(part) = part {
assert!(part < 16, "Wrong chunk state part number (must be <16) in snapshot creation.");
let part_offset = MAX_SNAPSHOT_SUBPARTS / SNAPSHOT_SUBPARTS;
let mut seek_from = vec![0; 32];
seek_from[0] = (part * part_offset) as u8;
account_iter.seek(&seek_from)?;
// Set the upper-bound, except for the last part
if part < SNAPSHOT_SUBPARTS - 1 {
seek_to = Some(((part + 1) * part_offset) as u8)
}
}
for item in account_iter {
let (account_key, account_data) = item?;
let account = ::rlp::decode(&*account_data)?;
let account_key_hash = H256::from_slice(&account_key);
if seek_to.map_or(false, |seek_to| account_key[0] >= seek_to) {
break;
}
let account = ::rlp::decode(&*account_data)?;
let account_db = AccountDB::from_hash(db, account_key_hash);
let fat_rlps = account::to_fat_rlps(&account_key_hash, &account, &account_db, &mut used_code, PREFERRED_CHUNK_SIZE - chunker.chunk_size(), PREFERRED_CHUNK_SIZE)?;

View File

@@ -32,7 +32,7 @@ use tempdir::TempDir;
use ethereum_types::Address;
use test_helpers;
use_contract!(test_validator_set, "ValidatorSet", "res/contracts/test_validator_set.json");
use_contract!(test_validator_set, "res/contracts/test_validator_set.json");
const PASS: &'static str = "";
const TRANSITION_BLOCK_1: usize = 2; // block at which the contract becomes activated.
@@ -135,8 +135,6 @@ fn make_chain(accounts: Arc<AccountProvider>, blocks_beyond: usize, transitions:
vec![transaction]
};
let contract = test_validator_set::ValidatorSet::default();
// apply all transitions.
for transition in transitions {
let (num, manual, new_set) = match transition {
@@ -163,7 +161,7 @@ fn make_chain(accounts: Arc<AccountProvider>, blocks_beyond: usize, transitions:
false => &CONTRACT_ADDR_1 as &Address,
};
let data = contract.functions().set_validators().input(new_set.clone());
let data = test_validator_set::functions::set_validators::encode_input(new_set.clone());
let mut nonce = nonce.borrow_mut();
let transaction = Transaction {
nonce: *nonce,

View File

@@ -22,7 +22,7 @@ use hash::{KECCAK_NULL_RLP, keccak};
use basic_account::BasicAccount;
use snapshot::account;
use snapshot::{chunk_state, Error as SnapshotError, Progress, StateRebuilder};
use snapshot::{chunk_state, Error as SnapshotError, Progress, StateRebuilder, SNAPSHOT_SUBPARTS};
use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter};
use super::helpers::{compare_dbs, StateProducer};
@@ -53,7 +53,11 @@ fn snap_and_restore() {
let state_root = producer.state_root();
let writer = Mutex::new(PackedWriter::new(&snap_file).unwrap());
let state_hashes = chunk_state(&old_db, &state_root, &writer, &Progress::default()).unwrap();
let mut state_hashes = Vec::new();
for part in 0..SNAPSHOT_SUBPARTS {
let mut hashes = chunk_state(&old_db, &state_root, &writer, &Progress::default(), Some(part)).unwrap();
state_hashes.append(&mut hashes);
}
writer.into_inner().finish(::snapshot::ManifestData {
version: 2,
@@ -164,7 +168,7 @@ fn checks_flag() {
let state_root = producer.state_root();
let writer = Mutex::new(PackedWriter::new(&snap_file).unwrap());
let state_hashes = chunk_state(&old_db, &state_root, &writer, &Progress::default()).unwrap();
let state_hashes = chunk_state(&old_db, &state_root, &writer, &Progress::default(), None).unwrap();
writer.into_inner().finish(::snapshot::ManifestData {
version: 2,

View File

@@ -2472,6 +2472,33 @@ mod tests {
assert_eq!(orig_root, state.root().clone());
}
#[test]
fn create_contract_fail_previous_storage() {
let mut state = get_temp_state();
let a: Address = 1000.into();
let k = H256::from(U256::from(0));
state.set_storage(&a, k, H256::from(U256::from(0xffff))).unwrap();
state.commit().unwrap();
state.clear();
let orig_root = state.root().clone();
assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(0xffff)));
state.clear();
state.checkpoint(); // c1
state.new_contract(&a, U256::zero(), U256::zero()).unwrap();
state.checkpoint(); // c2
state.set_storage(&a, k, H256::from(U256::from(2))).unwrap();
state.revert_to_checkpoint(); // revert to c2
assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(0)));
state.revert_to_checkpoint(); // revert to c1
assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(0xffff)));
state.commit().unwrap();
assert_eq!(orig_root, state.root().clone());
}
#[test]
fn create_empty() {
let mut state = get_temp_state();

View File

@@ -38,7 +38,7 @@ fn test_blockhash_eip210(factory: Factory) {
let test_blockhash_contract = "73fffffffffffffffffffffffffffffffffffffffe33141561007a57600143036020526000356101006020510755600061010060205107141561005057600035610100610100602051050761010001555b6000620100006020510714156100755760003561010062010000602051050761020001555b61014a565b4360003512151561009057600060405260206040f35b610100600035430312156100b357610100600035075460605260206060f3610149565b62010000600035430312156100d157600061010060003507146100d4565b60005b156100f6576101006101006000350507610100015460805260206080f3610148565b630100000060003543031215610116576000620100006000350714610119565b60005b1561013c57610100620100006000350507610200015460a052602060a0f3610147565b600060c052602060c0f35b5b5b5b5b";
let blockhash_contract_code = Arc::new(test_blockhash_contract.from_hex().unwrap());
let blockhash_contract_code_hash = keccak(blockhash_contract_code.as_ref());
let machine = ::ethereum::new_constantinople_test_machine();
let machine = ::ethereum::new_eip210_test_machine();
let mut env_info = EnvInfo::default();
// populate state with 256 last hashes

View File

@@ -18,6 +18,7 @@
use ethereum_types::{H256, U256, Address};
use lru_cache::LruCache;
use ethabi::FunctionOutputDecoder;
use client::{BlockInfo, CallContract, BlockId};
use parking_lot::Mutex;
@@ -26,8 +27,8 @@ use transaction::{Action, SignedTransaction};
use types::BlockNumber;
use hash::KECCAK_EMPTY;
use_contract!(transact_acl_deprecated, "TransactAclDeprecated", "res/contracts/tx_acl_deprecated.json");
use_contract!(transact_acl, "TransactAcl", "res/contracts/tx_acl.json");
use_contract!(transact_acl_deprecated, "res/contracts/tx_acl_deprecated.json");
use_contract!(transact_acl, "res/contracts/tx_acl.json");
const MAX_CACHE_SIZE: usize = 4096;
@@ -42,8 +43,6 @@ mod tx_permissions {
/// Connection filter that uses a contract to manage permissions.
pub struct TransactionFilter {
contract_deprecated: transact_acl_deprecated::TransactAclDeprecated,
contract: transact_acl::TransactAcl,
contract_address: Address,
transition_block: BlockNumber,
permission_cache: Mutex<LruCache<(H256, Address), u32>>,
@@ -55,8 +54,6 @@ impl TransactionFilter {
pub fn from_params(params: &CommonParams) -> Option<TransactionFilter> {
params.transaction_permission_contract.map(|address|
TransactionFilter {
contract_deprecated: transact_acl_deprecated::TransactAclDeprecated::default(),
contract: transact_acl::TransactAcl::default(),
contract_address: address,
transition_block: params.transaction_permission_contract_transition,
permission_cache: Mutex::new(LruCache::new(MAX_CACHE_SIZE)),
@@ -91,10 +88,8 @@ impl TransactionFilter {
let contract_address = self.contract_address;
let contract_version = contract_version_cache.get_mut(parent_hash).and_then(|v| *v).or_else(|| {
self.contract.functions()
.contract_version()
.call(&|data| client.call_contract(BlockId::Hash(*parent_hash), contract_address, data))
.ok()
let (data, decoder) = transact_acl::functions::contract_version::call();
decoder.decode(&client.call_contract(BlockId::Hash(*parent_hash), contract_address, data).ok()?).ok()
});
contract_version_cache.insert(*parent_hash, contract_version);
@@ -104,14 +99,16 @@ impl TransactionFilter {
let version_u64 = version.low_u64();
trace!(target: "tx_filter", "Version of tx permission contract: {}", version);
match version_u64 {
2 => self.contract.functions()
.allowed_tx_types()
.call(sender, to, value, &|data| client.call_contract(BlockId::Hash(*parent_hash), contract_address, data))
.map(|(p, f)| (p.low_u32(), f))
.unwrap_or_else(|e| {
error!(target: "tx_filter", "Error calling tx permissions contract: {:?}", e);
(tx_permissions::NONE, true)
}),
2 => {
let (data, decoder) = transact_acl::functions::allowed_tx_types::call(sender, to, value);
client.call_contract(BlockId::Hash(*parent_hash), contract_address, data)
.and_then(|value| decoder.decode(&value).map_err(|e| e.to_string()))
.map(|(p, f)| (p.low_u32(), f))
.unwrap_or_else(|e| {
error!(target: "tx_filter", "Error calling tx permissions contract: {:?}", e);
(tx_permissions::NONE, true)
})
},
_ => {
error!(target: "tx_filter", "Unknown version of tx permissions contract is used");
(tx_permissions::NONE, true)
@@ -120,14 +117,14 @@ impl TransactionFilter {
},
None => {
trace!(target: "tx_filter", "Fallback to the deprecated version of tx permission contract");
(self.contract_deprecated.functions()
.allowed_tx_types()
.call(sender, &|data| client.call_contract(BlockId::Hash(*parent_hash), contract_address, data))
.map(|p| p.low_u32())
.unwrap_or_else(|e| {
error!(target: "tx_filter", "Error calling tx permissions contract: {:?}", e);
tx_permissions::NONE
}), true)
let (data, decoder) = transact_acl_deprecated::functions::allowed_tx_types::call(sender);
(client.call_contract(BlockId::Hash(*parent_hash), contract_address, data)
.and_then(|value| decoder.decode(&value).map_err(|e| e.to_string()))
.map(|p| p.low_u32())
.unwrap_or_else(|e| {
error!(target: "tx_filter", "Error calling tx permissions contract: {:?}", e);
tx_permissions::NONE
}), true)
}
};

View File

@@ -200,7 +200,7 @@ impl SyncPropagator {
let appended = packet.append_raw_checked(&transaction.drain(), 1, MAX_TRANSACTION_PACKET_SIZE);
if !appended {
// Maximal packet size reached just proceed with sending
debug!("Transaction packet size limit reached. Sending incomplete set of {}/{} transactions.", pushed, to_send.len());
debug!(target: "sync", "Transaction packet size limit reached. Sending incomplete set of {}/{} transactions.", pushed, to_send.len());
to_send = to_send.into_iter().take(pushed).collect();
break;
}

View File

@@ -7,7 +7,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
byteorder = "1.0"
parity-bytes = "0.1"
ethereum-types = "0.4"
patricia-trie = "0.2.1"
patricia-trie = "0.2"
patricia-trie-ethereum = { path = "../../util/patricia-trie-ethereum" }
log = "0.4"
common-types = { path = "../types" }

View File

@@ -21,6 +21,7 @@ panic_hook = { path = "../util/panic_hook" }
rustc-hex = "1.0"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
vm = { path = "../ethcore/vm" }
[dev-dependencies]

View File

@@ -43,31 +43,9 @@ pub struct Informant {
unmatched: bool,
}
impl Informant {
fn memory(&self) -> String {
format!("\"0x{}\"", self.memory.to_hex())
}
fn stack(&self) -> String {
let items = self.stack.iter().map(|i| format!("\"0x{:x}\"", i)).collect::<Vec<_>>();
format!("[{}]", items.join(","))
}
fn storage(&self) -> String {
let vals = self.storage.iter()
.map(|(k, v)| format!("\"0x{:?}\": \"0x{:?}\"", k, v))
.collect::<Vec<_>>();
format!("{{{}}}", vals.join(","))
}
}
impl vm::Informant for Informant {
fn before_test(&mut self, name: &str, action: &str) {
println!(
"{{\"test\":\"{name}\",\"action\":\"{action}\"}}",
name = name,
action = action,
);
println!("{}", json!({"action": action, "test": name}));
}
fn set_gas(&mut self, gas: U256) {
@@ -81,24 +59,26 @@ impl vm::Informant for Informant {
println!("{}", trace);
}
println!(
"{{\"output\":\"0x{output}\",\"gasUsed\":\"{gas:x}\",\"time\":{time}}}",
output = success.output.to_hex(),
gas = success.gas_used,
time = display::as_micros(&success.time),
)
let success_msg = json!({
"output": format!("0x{}", success.output.to_hex()),
"gasUsed": format!("{:#x}", success.gas_used),
"time": display::as_micros(&success.time),
});
println!("{}", success_msg)
},
Err(failure) => {
for trace in failure.traces.unwrap_or_else(Vec::new) {
println!("{}", trace);
}
println!(
"{{\"error\":\"{error}\",\"gasUsed\":\"{gas:x}\",\"time\":{time}}}",
error = display::escape_newlines(&failure.error),
gas = failure.gas_used,
time = display::as_micros(&failure.time),
)
let failure_msg = json!({
"error": &failure.error.to_string(),
"gasUsed": format!("{:#x}", failure.gas_used),
"time": display::as_micros(&failure.time),
});
println!("{}", failure_msg)
},
}
}
@@ -123,19 +103,19 @@ impl trace::VMTracer for Informant {
fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem_diff: Option<(usize, &[u8])>, store_diff: Option<(U256, U256)>) {
let info = ::evm::Instruction::from_u8(self.instruction).map(|i| i.info());
let trace = format!(
"{{\"pc\":{pc},\"op\":{op},\"opName\":\"{name}\",\"gas\":\"0x{gas:x}\",\"gasCost\":\"0x{gas_cost:x}\",\"memory\":{memory},\"stack\":{stack},\"storage\":{storage},\"depth\":{depth}}}",
pc = self.pc,
op = self.instruction,
name = info.map(|i| i.name).unwrap_or(""),
gas = gas_used.saturating_add(self.gas_cost),
gas_cost = self.gas_cost,
memory = self.memory(),
stack = self.stack(),
storage = self.storage(),
depth = self.depth,
);
self.traces.push(trace);
let trace = json!({
"pc": self.pc,
"op": self.instruction,
"opName": info.map(|i| i.name).unwrap_or(""),
"gas": format!("{:#x}", gas_used.saturating_add(self.gas_cost)),
"gasCost": format!("{:#x}", self.gas_cost),
"memory": format!("0x{}", self.memory.to_hex()),
"stack": self.stack,
"storage": self.storage,
"depth": self.depth,
});
self.traces.push(trace.to_string());
self.unmatched = false;
self.gas_used = gas_used;
@@ -193,6 +173,23 @@ impl trace::VMTracer for Informant {
mod tests {
use super::*;
use info::tests::run_test;
use serde_json;
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
struct TestTrace {
pc: usize,
#[serde(rename = "op")]
instruction: u8,
op_name: String,
#[serde(rename = "gas")]
gas_used: U256,
gas_cost: U256,
memory: String,
stack: Vec<U256>,
storage: HashMap<H256, H256>,
depth: usize,
}
fn assert_traces_eq(
a: &[String],
@@ -204,7 +201,10 @@ mod tests {
loop {
match (ita.next(), itb.next()) {
(Some(a), Some(b)) => {
assert_eq!(a, b);
// Compare both without worrying about the order of the fields
let actual: TestTrace = serde_json::from_str(a).unwrap();
let expected: TestTrace = serde_json::from_str(b).unwrap();
assert_eq!(actual, expected);
println!("{}", a);
},
(None, None) => return,
@@ -280,7 +280,20 @@ mod tests {
{"pc":5,"op":88,"opName":"PC","gas":"0x2102","gasCost":"0x2","memory":"0x","stack":["0x0","0x0","0x0","0x0","0x0"],"storage":{},"depth":2}
{"pc":6,"op":48,"opName":"ADDRESS","gas":"0x2100","gasCost":"0x2","memory":"0x","stack":["0x0","0x0","0x0","0x0","0x0","0x5"],"storage":{},"depth":2}
{"pc":7,"op":241,"opName":"CALL","gas":"0x20fe","gasCost":"0x0","memory":"0x","stack":["0x0","0x0","0x0","0x0","0x0","0x5","0xbd770416a3345f91e4b34576cb804a576fa48eb1"],"storage":{},"depth":2}
"#,
"#,
);
run_test(
Informant::default(),
&compare_json,
"3260D85554",
0xffff,
r#"
{"pc":0,"op":50,"opName":"ORIGIN","gas":"0xffff","gasCost":"0x2","memory":"0x","stack":[],"storage":{},"depth":1}
{"pc":1,"op":96,"opName":"PUSH1","gas":"0xfffd","gasCost":"0x3","memory":"0x","stack":["0x0"],"storage":{},"depth":1}
{"pc":3,"op":85,"opName":"SSTORE","gas":"0xfffa","gasCost":"0x1388","memory":"0x","stack":["0x0","0xd8"],"storage":{},"depth":1}
{"pc":4,"op":84,"opName":"SLOAD","gas":"0xec72","gasCost":"0x0","memory":"0x","stack":[],"storage":{"0x00000000000000000000000000000000000000000000000000000000000000d8":"0x0000000000000000000000000000000000000000000000000000000000000000"},"depth":1}
"#,
)
}
}

View File

@@ -31,7 +31,3 @@ pub fn format_time(time: &Duration) -> String {
pub fn as_micros(time: &Duration) -> u64 {
time.as_secs() * 1_000_000 + time.subsec_nanos() as u64 / 1_000
}
fn escape_newlines<D: ::std::fmt::Display>(s: D) -> String {
format!("{}", s).replace("\r\n", "\n").replace('\n', "\\n")
}

View File

@@ -81,28 +81,14 @@ impl<Trace: Writer, Out: Writer> Informant<Trace, Out> {
}
}
impl<Trace: Writer, Out: Writer> Informant<Trace, Out> {
fn stack(&self) -> String {
let items = self.stack.iter().map(|i| format!("\"0x{:x}\"", i)).collect::<Vec<_>>();
format!("[{}]", items.join(","))
}
fn storage(&self) -> String {
let vals = self.storage.iter()
.map(|(k, v)| format!("\"0x{:?}\": \"0x{:?}\"", k, v))
.collect::<Vec<_>>();
format!("{{{}}}", vals.join(","))
}
}
impl<Trace: Writer, Out: Writer> vm::Informant for Informant<Trace, Out> {
fn before_test(&mut self, name: &str, action: &str) {
writeln!(
&mut self.out_sink,
"{{\"test\":\"{name}\",\"action\":\"{action}\"}}",
name = name,
action = action,
).expect("The sink must be writeable.");
let out_data = json!({
"action": action,
"test": name,
});
writeln!(&mut self.out_sink, "{}", out_data).expect("The sink must be writeable.");
}
fn set_gas(&mut self, _gas: U256) {}
@@ -113,26 +99,26 @@ impl<Trace: Writer, Out: Writer> vm::Informant for Informant<Trace, Out> {
match result {
Ok(success) => {
writeln!(
&mut trace_sink,
"{{\"stateRoot\":\"{:?}\"}}", success.state_root
).expect("The sink must be writeable.");
writeln!(
&mut out_sink,
"{{\"output\":\"0x{output}\",\"gasUsed\":\"0x{gas:x}\",\"time\":{time}}}",
output = success.output.to_hex(),
gas = success.gas_used,
time = display::as_micros(&success.time),
).expect("The sink must be writeable.");
let trace_data = json!({"stateRoot": success.state_root});
writeln!(&mut trace_sink, "{}", trace_data)
.expect("The sink must be writeable.");
let out_data = json!({
"output": format!("0x{}", success.output.to_hex()),
"gasUsed": format!("{:#x}", success.gas_used),
"time": display::as_micros(&success.time),
});
writeln!(&mut out_sink, "{}", out_data).expect("The sink must be writeable.");
},
Err(failure) => {
writeln!(
&mut out_sink,
"{{\"error\":\"{error}\",\"gasUsed\":\"0x{gas:x}\",\"time\":{time}}}",
error = display::escape_newlines(&failure.error),
gas = failure.gas_used,
time = display::as_micros(&failure.time),
).expect("The sink must be writeable.");
let out_data = json!({
"error": &failure.error.to_string(),
"gasUsed": format!("{:#x}", failure.gas_used),
"time": display::as_micros(&failure.time),
});
writeln!(&mut out_sink, "{}", out_data).expect("The sink must be writeable.");
},
}
}
@@ -144,20 +130,17 @@ impl<Trace: Writer, Out: Writer> trace::VMTracer for Informant<Trace, Out> {
fn trace_next_instruction(&mut self, pc: usize, instruction: u8, current_gas: U256) -> bool {
let info = ::evm::Instruction::from_u8(instruction).map(|i| i.info());
self.instruction = instruction;
let storage = self.storage();
let stack = self.stack();
let trace_data = json!({
"pc": pc,
"op": instruction,
"opName": info.map(|i| i.name).unwrap_or(""),
"gas": format!("{:#x}", current_gas),
"stack": self.stack,
"storage": self.storage,
"depth": self.depth,
});
writeln!(
&mut self.trace_sink,
"{{\"pc\":{pc},\"op\":{op},\"opName\":\"{name}\",\"gas\":\"0x{gas:x}\",\"stack\":{stack},\"storage\":{storage},\"depth\":{depth}}}",
pc = pc,
op = instruction,
name = info.map(|i| i.name).unwrap_or(""),
gas = current_gas,
stack = stack,
storage = storage,
depth = self.depth,
).expect("The sink must be writeable.");
writeln!(&mut self.trace_sink, "{}", trace_data).expect("The sink must be writeable.");
true
}
@@ -232,8 +215,8 @@ pub mod tests {
},
"60F8d6",
0xffff,
r#"{"pc":0,"op":96,"opName":"PUSH1","gas":"0xffff","stack":[],"storage":{},"depth":1}
{"pc":2,"op":214,"opName":"","gas":"0xfffc","stack":["0xf8"],"storage":{},"depth":1}
r#"{"depth":1,"gas":"0xffff","op":96,"opName":"PUSH1","pc":0,"stack":[],"storage":{}}
{"depth":1,"gas":"0xfffc","op":214,"opName":"","pc":2,"stack":["0xf8"],"storage":{}}
"#,
);
@@ -246,7 +229,7 @@ pub mod tests {
},
"F8d6",
0xffff,
r#"{"pc":0,"op":248,"opName":"","gas":"0xffff","stack":[],"storage":{},"depth":1}
r#"{"depth":1,"gas":"0xffff","op":248,"opName":"","pc":0,"stack":[],"storage":{}}
"#,
);
}
@@ -262,30 +245,30 @@ pub mod tests {
},
"32343434345830f138343438323439f0",
0xffff,
r#"{"pc":0,"op":50,"opName":"ORIGIN","gas":"0xffff","stack":[],"storage":{},"depth":1}
{"pc":1,"op":52,"opName":"CALLVALUE","gas":"0xfffd","stack":["0x0"],"storage":{},"depth":1}
{"pc":2,"op":52,"opName":"CALLVALUE","gas":"0xfffb","stack":["0x0","0x0"],"storage":{},"depth":1}
{"pc":3,"op":52,"opName":"CALLVALUE","gas":"0xfff9","stack":["0x0","0x0","0x0"],"storage":{},"depth":1}
{"pc":4,"op":52,"opName":"CALLVALUE","gas":"0xfff7","stack":["0x0","0x0","0x0","0x0"],"storage":{},"depth":1}
{"pc":5,"op":88,"opName":"PC","gas":"0xfff5","stack":["0x0","0x0","0x0","0x0","0x0"],"storage":{},"depth":1}
{"pc":6,"op":48,"opName":"ADDRESS","gas":"0xfff3","stack":["0x0","0x0","0x0","0x0","0x0","0x5"],"storage":{},"depth":1}
{"pc":7,"op":241,"opName":"CALL","gas":"0xfff1","stack":["0x0","0x0","0x0","0x0","0x0","0x5","0x0"],"storage":{},"depth":1}
{"pc":8,"op":56,"opName":"CODESIZE","gas":"0x9e21","stack":["0x1"],"storage":{},"depth":1}
{"pc":9,"op":52,"opName":"CALLVALUE","gas":"0x9e1f","stack":["0x1","0x10"],"storage":{},"depth":1}
{"pc":10,"op":52,"opName":"CALLVALUE","gas":"0x9e1d","stack":["0x1","0x10","0x0"],"storage":{},"depth":1}
{"pc":11,"op":56,"opName":"CODESIZE","gas":"0x9e1b","stack":["0x1","0x10","0x0","0x0"],"storage":{},"depth":1}
{"pc":12,"op":50,"opName":"ORIGIN","gas":"0x9e19","stack":["0x1","0x10","0x0","0x0","0x10"],"storage":{},"depth":1}
{"pc":13,"op":52,"opName":"CALLVALUE","gas":"0x9e17","stack":["0x1","0x10","0x0","0x0","0x10","0x0"],"storage":{},"depth":1}
{"pc":14,"op":57,"opName":"CODECOPY","gas":"0x9e15","stack":["0x1","0x10","0x0","0x0","0x10","0x0","0x0"],"storage":{},"depth":1}
{"pc":15,"op":240,"opName":"CREATE","gas":"0x9e0c","stack":["0x1","0x10","0x0","0x0"],"storage":{},"depth":1}
{"pc":0,"op":50,"opName":"ORIGIN","gas":"0x210c","stack":[],"storage":{},"depth":2}
{"pc":1,"op":52,"opName":"CALLVALUE","gas":"0x210a","stack":["0x0"],"storage":{},"depth":2}
{"pc":2,"op":52,"opName":"CALLVALUE","gas":"0x2108","stack":["0x0","0x0"],"storage":{},"depth":2}
{"pc":3,"op":52,"opName":"CALLVALUE","gas":"0x2106","stack":["0x0","0x0","0x0"],"storage":{},"depth":2}
{"pc":4,"op":52,"opName":"CALLVALUE","gas":"0x2104","stack":["0x0","0x0","0x0","0x0"],"storage":{},"depth":2}
{"pc":5,"op":88,"opName":"PC","gas":"0x2102","stack":["0x0","0x0","0x0","0x0","0x0"],"storage":{},"depth":2}
{"pc":6,"op":48,"opName":"ADDRESS","gas":"0x2100","stack":["0x0","0x0","0x0","0x0","0x0","0x5"],"storage":{},"depth":2}
{"pc":7,"op":241,"opName":"CALL","gas":"0x20fe","stack":["0x0","0x0","0x0","0x0","0x0","0x5","0xbd770416a3345f91e4b34576cb804a576fa48eb1"],"storage":{},"depth":2}
r#"{"depth":1,"gas":"0xffff","op":50,"opName":"ORIGIN","pc":0,"stack":[],"storage":{}}
{"depth":1,"gas":"0xfffd","op":52,"opName":"CALLVALUE","pc":1,"stack":["0x0"],"storage":{}}
{"depth":1,"gas":"0xfffb","op":52,"opName":"CALLVALUE","pc":2,"stack":["0x0","0x0"],"storage":{}}
{"depth":1,"gas":"0xfff9","op":52,"opName":"CALLVALUE","pc":3,"stack":["0x0","0x0","0x0"],"storage":{}}
{"depth":1,"gas":"0xfff7","op":52,"opName":"CALLVALUE","pc":4,"stack":["0x0","0x0","0x0","0x0"],"storage":{}}
{"depth":1,"gas":"0xfff5","op":88,"opName":"PC","pc":5,"stack":["0x0","0x0","0x0","0x0","0x0"],"storage":{}}
{"depth":1,"gas":"0xfff3","op":48,"opName":"ADDRESS","pc":6,"stack":["0x0","0x0","0x0","0x0","0x0","0x5"],"storage":{}}
{"depth":1,"gas":"0xfff1","op":241,"opName":"CALL","pc":7,"stack":["0x0","0x0","0x0","0x0","0x0","0x5","0x0"],"storage":{}}
{"depth":1,"gas":"0x9e21","op":56,"opName":"CODESIZE","pc":8,"stack":["0x1"],"storage":{}}
{"depth":1,"gas":"0x9e1f","op":52,"opName":"CALLVALUE","pc":9,"stack":["0x1","0x10"],"storage":{}}
{"depth":1,"gas":"0x9e1d","op":52,"opName":"CALLVALUE","pc":10,"stack":["0x1","0x10","0x0"],"storage":{}}
{"depth":1,"gas":"0x9e1b","op":56,"opName":"CODESIZE","pc":11,"stack":["0x1","0x10","0x0","0x0"],"storage":{}}
{"depth":1,"gas":"0x9e19","op":50,"opName":"ORIGIN","pc":12,"stack":["0x1","0x10","0x0","0x0","0x10"],"storage":{}}
{"depth":1,"gas":"0x9e17","op":52,"opName":"CALLVALUE","pc":13,"stack":["0x1","0x10","0x0","0x0","0x10","0x0"],"storage":{}}
{"depth":1,"gas":"0x9e15","op":57,"opName":"CODECOPY","pc":14,"stack":["0x1","0x10","0x0","0x0","0x10","0x0","0x0"],"storage":{}}
{"depth":1,"gas":"0x9e0c","op":240,"opName":"CREATE","pc":15,"stack":["0x1","0x10","0x0","0x0"],"storage":{}}
{"depth":2,"gas":"0x210c","op":50,"opName":"ORIGIN","pc":0,"stack":[],"storage":{}}
{"depth":2,"gas":"0x210a","op":52,"opName":"CALLVALUE","pc":1,"stack":["0x0"],"storage":{}}
{"depth":2,"gas":"0x2108","op":52,"opName":"CALLVALUE","pc":2,"stack":["0x0","0x0"],"storage":{}}
{"depth":2,"gas":"0x2106","op":52,"opName":"CALLVALUE","pc":3,"stack":["0x0","0x0","0x0"],"storage":{}}
{"depth":2,"gas":"0x2104","op":52,"opName":"CALLVALUE","pc":4,"stack":["0x0","0x0","0x0","0x0"],"storage":{}}
{"depth":2,"gas":"0x2102","op":88,"opName":"PC","pc":5,"stack":["0x0","0x0","0x0","0x0","0x0"],"storage":{}}
{"depth":2,"gas":"0x2100","op":48,"opName":"ADDRESS","pc":6,"stack":["0x0","0x0","0x0","0x0","0x0","0x5"],"storage":{}}
{"depth":2,"gas":"0x20fe","op":241,"opName":"CALL","pc":7,"stack":["0x0","0x0","0x0","0x0","0x0","0x5","0xbd770416a3345f91e4b34576cb804a576fa48eb1"],"storage":{}}
"#,
)
}

View File

@@ -225,16 +225,16 @@ pub mod tests {
assert_eq!(
&String::from_utf8_lossy(&**res.lock().unwrap()),
r#"{"pc":0,"op":98,"opName":"PUSH3","gas":"0xffff","stack":[],"storage":{},"depth":1}
{"pc":4,"op":96,"opName":"PUSH1","gas":"0xfffc","stack":["0xaaaaaa"],"storage":{},"depth":1}
{"pc":6,"op":96,"opName":"PUSH1","gas":"0xfff9","stack":["0xaaaaaa","0xaa"],"storage":{},"depth":1}
{"pc":8,"op":80,"opName":"POP","gas":"0xfff6","stack":["0xaaaaaa","0xaa","0xaa"],"storage":{},"depth":1}
{"pc":9,"op":96,"opName":"PUSH1","gas":"0xfff4","stack":["0xaaaaaa","0xaa"],"storage":{},"depth":1}
{"pc":11,"op":96,"opName":"PUSH1","gas":"0xfff1","stack":["0xaaaaaa","0xaa","0xaa"],"storage":{},"depth":1}
{"pc":13,"op":96,"opName":"PUSH1","gas":"0xffee","stack":["0xaaaaaa","0xaa","0xaa","0xaa"],"storage":{},"depth":1}
{"pc":15,"op":96,"opName":"PUSH1","gas":"0xffeb","stack":["0xaaaaaa","0xaa","0xaa","0xaa","0xaa"],"storage":{},"depth":1}
{"pc":17,"op":96,"opName":"PUSH1","gas":"0xffe8","stack":["0xaaaaaa","0xaa","0xaa","0xaa","0xaa","0xaa"],"storage":{},"depth":1}
{"pc":19,"op":96,"opName":"PUSH1","gas":"0xffe5","stack":["0xaaaaaa","0xaa","0xaa","0xaa","0xaa","0xaa","0xaa"],"storage":{},"depth":1}
r#"{"depth":1,"gas":"0xffff","op":98,"opName":"PUSH3","pc":0,"stack":[],"storage":{}}
{"depth":1,"gas":"0xfffc","op":96,"opName":"PUSH1","pc":4,"stack":["0xaaaaaa"],"storage":{}}
{"depth":1,"gas":"0xfff9","op":96,"opName":"PUSH1","pc":6,"stack":["0xaaaaaa","0xaa"],"storage":{}}
{"depth":1,"gas":"0xfff6","op":80,"opName":"POP","pc":8,"stack":["0xaaaaaa","0xaa","0xaa"],"storage":{}}
{"depth":1,"gas":"0xfff4","op":96,"opName":"PUSH1","pc":9,"stack":["0xaaaaaa","0xaa"],"storage":{}}
{"depth":1,"gas":"0xfff1","op":96,"opName":"PUSH1","pc":11,"stack":["0xaaaaaa","0xaa","0xaa"],"storage":{}}
{"depth":1,"gas":"0xffee","op":96,"opName":"PUSH1","pc":13,"stack":["0xaaaaaa","0xaa","0xaa","0xaa"],"storage":{}}
{"depth":1,"gas":"0xffeb","op":96,"opName":"PUSH1","pc":15,"stack":["0xaaaaaa","0xaa","0xaa","0xaa","0xaa"],"storage":{}}
{"depth":1,"gas":"0xffe8","op":96,"opName":"PUSH1","pc":17,"stack":["0xaaaaaa","0xaa","0xaa","0xaa","0xaa","0xaa"],"storage":{}}
{"depth":1,"gas":"0xffe5","op":96,"opName":"PUSH1","pc":19,"stack":["0xaaaaaa","0xaa","0xaa","0xaa","0xaa","0xaa","0xaa"],"storage":{}}
"#);
}
}

View File

@@ -24,6 +24,8 @@ extern crate rustc_hex;
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;
extern crate docopt;
extern crate ethcore_transaction as transaction;
extern crate parity_bytes as bytes;

View File

@@ -21,9 +21,9 @@ parity-reactor = { path = "../util/reactor" }
keccak-hash = "0.1"
registrar = { path = "../registrar" }
ethabi = "5.1.2"
ethabi-derive = "5.1.3"
ethabi-contract = "5.1.1"
ethabi = "6.0"
ethabi-derive = "6.0"
ethabi-contract = "6.0"
[dev-dependencies]
hyper = "0.11"

View File

@@ -26,7 +26,7 @@ use futures::future::Either;
use ethereum_types::{H256, Address};
use registrar::{Registrar, RegistrarClient, Asynchronous};
use_contract!(urlhint, "Urlhint", "res/urlhint.json");
use_contract!(urlhint, "res/urlhint.json");
const COMMIT_LEN: usize = 20;
const GITHUB_HINT: &'static str = "githubhint";
@@ -100,16 +100,14 @@ pub trait URLHint: Send + Sync {
/// `URLHintContract` API
pub struct URLHintContract {
urlhint: urlhint::Urlhint,
registrar: Registrar,
client: Arc<RegistrarClient<Call=Asynchronous>>,
}
impl URLHintContract {
/// Creates new `URLHintContract`
pub fn new(client: Arc<RegistrarClient<Call=Asynchronous>>) -> Self {
pub fn new(client: Arc<RegistrarClient<Call=Asynchronous>>) -> Self {
URLHintContract {
urlhint: urlhint::Urlhint::default(),
registrar: Registrar::new(client.clone()),
client: client,
}
@@ -162,14 +160,13 @@ fn decode_urlhint_output(output: (String, [u8; 20], Address)) -> Option<URLHintR
impl URLHint for URLHintContract {
fn resolve(&self, id: H256) -> Box<Future<Item = Option<URLHintResult>, Error = String> + Send> {
let entries = self.urlhint.functions().entries();
let client = self.client.clone();
let future = self.registrar.get_address(GITHUB_HINT)
.and_then(move |addr| if !addr.is_zero() {
let data = entries.input(id);
let data = urlhint::functions::entries::encode_input(id);
let result = client.call_contract(addr, data)
.and_then(move |output| entries.output(&output).map_err(|e| e.to_string()))
.and_then(move |output| urlhint::functions::entries::decode_output(&output).map_err(|e| e.to_string()))
.map(decode_urlhint_output);
Either::B(result)
} else {

View File

@@ -407,7 +407,7 @@ usage! {
"--port=[PORT]",
"Override the port on which the node should listen.",
ARG arg_interface: (String) = "all", or |c: &Config| c.network.as_ref()?.interface.clone(),
ARG arg_interface: (String) = "all", or |c: &Config| c.network.as_ref()?.interface.clone(),
"--interface=[IP]",
"Network interfaces. Valid values are 'all', 'local' or the ip of the interface you want parity to listen to.",
@@ -471,7 +471,7 @@ usage! {
"--jsonrpc-port=[PORT]",
"Specify the port portion of the HTTP JSON-RPC API server.",
ARG arg_jsonrpc_interface: (String) = "local", or |c: &Config| c.rpc.as_ref()?.interface.clone(),
ARG arg_jsonrpc_interface: (String) = "local", or |c: &Config| c.rpc.as_ref()?.interface.clone(),
"--jsonrpc-interface=[IP]",
"Specify the hostname portion of the HTTP JSON-RPC API server, IP should be an interface's IP address, or all (all interfaces) or local.",
@@ -485,7 +485,7 @@ usage! {
ARG arg_jsonrpc_threads: (usize) = 4usize, or |c: &Config| c.rpc.as_ref()?.processing_threads,
"--jsonrpc-threads=[THREADS]",
"Turn on additional processing threads in all HTTP JSON-RPC servers. Setting this to non-zero value allows parallel execution of cpu-heavy queries.",
"Turn on additional processing threads for JSON-RPC servers (all transports). Setting this to a non-zero value allows parallel execution of cpu-heavy queries.",
ARG arg_jsonrpc_cors: (String) = "none", or |c: &Config| c.rpc.as_ref()?.cors.as_ref().map(|vec| vec.join(",")),
"--jsonrpc-cors=[URL]",
@@ -508,7 +508,7 @@ usage! {
"--ws-port=[PORT]",
"Specify the port portion of the WebSockets JSON-RPC server.",
ARG arg_ws_interface: (String) = "local", or |c: &Config| c.websockets.as_ref()?.interface.clone(),
ARG arg_ws_interface: (String) = "local", or |c: &Config| c.websockets.as_ref()?.interface.clone(),
"--ws-interface=[IP]",
"Specify the hostname portion of the WebSockets JSON-RPC server, IP should be an interface's IP address, or all (all interfaces) or local.",
@@ -562,6 +562,15 @@ usage! {
"--ipfs-api-cors=[URL]",
"Specify CORS header for IPFS API responses. Special options: \"all\", \"none\".",
["Light Client Options"]
ARG arg_on_demand_retry_count: (Option<usize>) = None, or |c: &Config| c.light.as_ref()?.on_demand_retry_count,
"--on-demand-retry-count=[RETRIES]",
"Specify the query retry count.",
ARG arg_on_demand_inactive_time_limit: (Option<u64>) = None, or |c: &Config| c.light.as_ref()?.on_demand_inactive_time_limit,
"--on-demand-inactive-time-limit=[MS]",
"Specify light client query inactive time limit. O for no limit.",
["Secret Store Options"]
FLAG flag_no_secretstore: (bool) = false, or |c: &Config| c.secretstore.as_ref()?.disable.clone(),
"--no-secretstore",
@@ -704,11 +713,11 @@ usage! {
"--price-update-period=[T]",
"T will be allowed to pass between each gas price update. T may be daily, hourly, a number of seconds, or a time string of the form \"2 days\", \"30 minutes\" etc..",
ARG arg_gas_floor_target: (String) = "4700000", or |c: &Config| c.mining.as_ref()?.gas_floor_target.clone(),
ARG arg_gas_floor_target: (String) = "8000000", or |c: &Config| c.mining.as_ref()?.gas_floor_target.clone(),
"--gas-floor-target=[GAS]",
"Amount of gas per block to target when sealing a new block.",
ARG arg_gas_cap: (String) = "6283184", or |c: &Config| c.mining.as_ref()?.gas_cap.clone(),
ARG arg_gas_cap: (String) = "10000000", or |c: &Config| c.mining.as_ref()?.gas_cap.clone(),
"--gas-cap=[GAS]",
"A cap on how large we will raise the gas limit per block due to transaction volume.",
@@ -865,6 +874,10 @@ usage! {
"--no-periodic-snapshot",
"Disable automated snapshots which usually occur once every 10000 blocks.",
ARG arg_snapshot_threads: (Option<usize>) = None, or |c: &Config| c.snapshots.as_ref()?.processing_threads,
"--snapshot-threads=[NUM]",
"Enables multiple threads for snapshots creation.",
["Whisper Options"]
FLAG flag_whisper: (bool) = false, or |c: &Config| c.whisper.as_ref()?.enabled,
"--whisper",
@@ -875,7 +888,7 @@ usage! {
"Target size of the whisper message pool in megabytes.",
["Legacy Options"]
// Options that are hidden from config, but are still unique for its functionality.
// Options that are hidden from config, but are still unique for its functionality.
FLAG flag_geth: (bool) = false, or |_| None,
"--geth",
@@ -1100,6 +1113,7 @@ struct Config {
misc: Option<Misc>,
stratum: Option<Stratum>,
whisper: Option<Whisper>,
light: Option<Light>,
}
#[derive(Default, Debug, PartialEq, Deserialize)]
@@ -1345,6 +1359,7 @@ struct Footprint {
#[serde(deny_unknown_fields)]
struct Snapshots {
disable_periodic: Option<bool>,
processing_threads: Option<usize>,
}
#[derive(Default, Debug, PartialEq, Deserialize)]
@@ -1364,12 +1379,19 @@ struct Whisper {
pool_size: Option<usize>,
}
#[derive(Default, Debug, PartialEq, Deserialize)]
#[serde(deny_unknown_fields)]
struct Light {
on_demand_retry_count: Option<usize>,
on_demand_inactive_time_limit: Option<u64>,
}
#[cfg(test)]
mod tests {
use super::{
Args, ArgsError,
Config, Operating, Account, Ui, Network, Ws, Rpc, Ipc, Dapps, Ipfs, Mining, Footprint,
Snapshots, Misc, Whisper, SecretStore,
Snapshots, Misc, Whisper, SecretStore, Light,
};
use toml;
use clap::{ErrorKind as ClapErrorKind};
@@ -1712,7 +1734,7 @@ mod tests {
arg_reseal_max_period: 60000u64,
flag_reseal_on_uncle: false,
arg_work_queue_size: 20usize,
arg_tx_gas_limit: Some("6283184".into()),
arg_tx_gas_limit: Some("10000000".into()),
arg_tx_time_limit: Some(100u64),
arg_relay_set: "cheap".into(),
arg_min_gas_price: Some(0u64),
@@ -1721,8 +1743,8 @@ mod tests {
arg_poll_lifetime: 60u32,
arg_usd_per_eth: "auto".into(),
arg_price_update_period: "hourly".into(),
arg_gas_floor_target: "4700000".into(),
arg_gas_cap: "6283184".into(),
arg_gas_floor_target: "8000000".into(),
arg_gas_cap: "10000000".into(),
arg_extra_data: Some("Parity".into()),
flag_tx_queue_no_unfamiliar_locals: false,
flag_tx_queue_no_early_reject: false,
@@ -1771,6 +1793,11 @@ mod tests {
arg_export_state_at: "latest".into(),
arg_snapshot_at: "latest".into(),
flag_no_periodic_snapshot: false,
arg_snapshot_threads: None,
// -- Light options.
arg_on_demand_retry_count: Some(15),
arg_on_demand_inactive_time_limit: Some(15000),
// -- Whisper options.
flag_whisper: false,
@@ -2019,8 +2046,13 @@ mod tests {
scale_verifiers: Some(false),
num_verifiers: None,
}),
light: Some(Light {
on_demand_retry_count: Some(12),
on_demand_inactive_time_limit: Some(20000),
}),
snapshots: Some(Snapshots {
disable_periodic: Some(true),
processing_threads: None,
}),
misc: Some(Misc {
logging: Some("own_tx=trace".into()),

View File

@@ -125,13 +125,13 @@ min_gas_price = 0
usd_per_tx = "0.0001"
usd_per_eth = "auto"
price_update_period = "hourly"
gas_floor_target = "4700000"
gas_cap = "6283184"
gas_floor_target = "8000000"
gas_cap = "10000000"
tx_queue_size = 8192
tx_queue_strategy = "gas_factor"
tx_queue_ban_count = 1
tx_queue_ban_time = 180 #s
tx_gas_limit = "6283184"
tx_gas_limit = "10000000"
tx_time_limit = 100 #ms
tx_queue_no_unfamiliar_locals = false
tx_queue_no_early_reject = false
@@ -155,6 +155,10 @@ fat_db = "auto"
scale_verifiers = true
num_verifiers = 6
[light]
on_demand_retry_count = 15
on_demand_inactive_time_limit = 15000
[snapshots]
disable_periodic = false

View File

@@ -70,6 +70,10 @@ db_compaction = "ssd"
fat_db = "off"
scale_verifiers = false
[light]
on_demand_retry_count = 12
on_demand_inactive_time_limit = 20000
[snapshots]
disable_periodic = true

View File

@@ -30,8 +30,10 @@ use sync::{NetworkConfiguration, validate_node_url, self};
use ethcore::ethstore::ethkey::{Secret, Public};
use ethcore::client::{VMType};
use ethcore::miner::{stratum, MinerOptions};
use ethcore::snapshot::SnapshotConfiguration;
use ethcore::verification::queue::VerifierSettings;
use miner::pool;
use num_cpus;
use rpc::{IpcConfiguration, HttpConfiguration, WsConfiguration};
use parity_rpc::NetworkSettings;
@@ -125,6 +127,7 @@ impl Configuration {
let update_policy = self.update_policy()?;
let logger_config = self.logger_config();
let ws_conf = self.ws_config()?;
let snapshot_conf = self.snapshot_config()?;
let http_conf = self.http_config()?;
let ipc_conf = self.ipc_config()?;
let net_conf = self.net_config()?;
@@ -156,13 +159,13 @@ impl Configuration {
port: ws_conf.port,
authfile: authfile,
}
} else if self.args.cmd_signer_reject {
} else if self.args.cmd_signer_reject {
Cmd::SignerReject {
id: self.args.arg_signer_reject_id,
port: ws_conf.port,
authfile: authfile,
}
} else if self.args.cmd_signer_list {
} else if self.args.cmd_signer_list {
Cmd::SignerList {
port: ws_conf.port,
authfile: authfile,
@@ -205,7 +208,7 @@ impl Configuration {
};
Cmd::Account(account_cmd)
} else if self.args.flag_import_geth_keys {
let account_cmd = AccountCmd::ImportFromGeth(
let account_cmd = AccountCmd::ImportFromGeth(
ImportFromGethAccounts {
spec: spec,
to: dirs.keys,
@@ -298,6 +301,7 @@ impl Configuration {
file_path: self.args.arg_snapshot_file.clone(),
kind: snapshot::Kind::Take,
block_at: to_block_id(&self.args.arg_snapshot_at)?,
snapshot_conf: snapshot_conf,
};
Cmd::Snapshot(snapshot_cmd)
} else if self.args.cmd_restore {
@@ -314,6 +318,7 @@ impl Configuration {
file_path: self.args.arg_restore_file.clone(),
kind: snapshot::Kind::Restore,
block_at: to_block_id("latest")?, // unimportant.
snapshot_conf: snapshot_conf,
};
Cmd::Snapshot(restore_cmd)
} else if self.args.cmd_export_hardcoded_sync {
@@ -349,6 +354,7 @@ impl Configuration {
gas_price_percentile: self.args.arg_gas_price_percentile,
poll_lifetime: self.args.arg_poll_lifetime,
ws_conf: ws_conf,
snapshot_conf: snapshot_conf,
http_conf: http_conf,
ipc_conf: ipc_conf,
net_conf: net_conf,
@@ -374,7 +380,6 @@ impl Configuration {
private_tx_enabled,
name: self.args.arg_identity,
custom_bootnodes: self.args.arg_bootnodes.is_some(),
no_periodic_snapshot: self.args.flag_no_periodic_snapshot,
check_seal: !self.args.flag_no_seal_check,
download_old_blocks: !self.args.flag_no_ancient_blocks,
verifier_settings: verifier_settings,
@@ -383,6 +388,8 @@ impl Configuration {
no_persistent_txqueue: self.args.flag_no_persistent_txqueue,
whisper: whisper_config,
no_hardcoded_sync: self.args.flag_no_hardcoded_sync,
on_demand_retry_count: self.args.arg_on_demand_retry_count,
on_demand_inactive_time_limit: self.args.arg_on_demand_inactive_time_limit,
};
Cmd::Run(run_cmd)
};
@@ -890,6 +897,18 @@ impl Configuration {
Ok((provider_conf, encryptor_conf, self.args.flag_private_enabled))
}
fn snapshot_config(&self) -> Result<SnapshotConfiguration, String> {
let conf = SnapshotConfiguration {
no_periodic: self.args.flag_no_periodic_snapshot,
processing_threads: match self.args.arg_snapshot_threads {
Some(threads) if threads > 0 => threads,
_ => ::std::cmp::max(1, num_cpus::get() / 2),
},
};
Ok(conf)
}
fn network_settings(&self) -> Result<NetworkSettings, String> {
let http_conf = self.http_config()?;
let net_addresses = self.net_addresses()?;
@@ -1331,10 +1350,10 @@ mod tests {
support_token_api: true,
max_connections: 100,
}, LogConfig {
color: true,
mode: None,
file: None,
} ));
color: true,
mode: None,
file: None,
} ));
}
#[test]
@@ -1398,7 +1417,7 @@ mod tests {
name: "".into(),
custom_bootnodes: false,
fat_db: Default::default(),
no_periodic_snapshot: false,
snapshot_conf: Default::default(),
stratum: None,
check_seal: true,
download_old_blocks: true,
@@ -1408,6 +1427,8 @@ mod tests {
no_hardcoded_sync: false,
no_persistent_txqueue: false,
whisper: Default::default(),
on_demand_retry_count: None,
on_demand_inactive_time_limit: None,
};
expected.secretstore_conf.enabled = cfg!(feature = "secretstore");
expected.secretstore_conf.http_enabled = cfg!(feature = "secretstore");
@@ -1516,11 +1537,11 @@ mod tests {
"--jsonrpc-apis", "web3,eth"
]);
let conf2 = parse(&["parity", "--rpc",
"--rpcport", "8000",
"--rpcaddr", "all",
"--rpccorsdomain", "*",
"--rpcapi", "web3,eth"
]);
"--rpcport", "8000",
"--rpcaddr", "all",
"--rpccorsdomain", "*",
"--rpcapi", "web3,eth"
]);
// then
assert(conf1);

View File

@@ -286,7 +286,7 @@ impl Default for MinerExtras {
author: Default::default(),
engine_signer: Default::default(),
extra_data: version_data(),
gas_range_target: (4_700_000.into(), 6_283_184.into()),
gas_range_target: (8_000_000.into(), 10_000_000.into()),
work_notify: Default::default(),
}
}

View File

@@ -25,7 +25,7 @@ use ethcore::account_provider::{AccountProvider, AccountProviderSettings};
use ethcore::client::{BlockId, CallContract, Client, Mode, DatabaseCompactionProfile, VMType, BlockChainClient, BlockInfo};
use ethcore::ethstore::ethkey;
use ethcore::miner::{stratum, Miner, MinerService, MinerOptions};
use ethcore::snapshot;
use ethcore::snapshot::{self, SnapshotConfiguration};
use ethcore::spec::{SpecParams, OptimizeFor};
use ethcore::verification::queue::VerifierSettings;
use ethcore_logger::{Config as LogConfig, RotatingLogger};
@@ -119,7 +119,7 @@ pub struct RunCmd {
pub name: String,
pub custom_bootnodes: bool,
pub stratum: Option<stratum::Options>,
pub no_periodic_snapshot: bool,
pub snapshot_conf: SnapshotConfiguration,
pub check_seal: bool,
pub download_old_blocks: bool,
pub verifier_settings: VerifierSettings,
@@ -128,6 +128,8 @@ pub struct RunCmd {
pub no_persistent_txqueue: bool,
pub whisper: ::whisper::Config,
pub no_hardcoded_sync: bool,
pub on_demand_retry_count: Option<usize>,
pub on_demand_inactive_time_limit: Option<u64>,
}
// node info fetcher for the local store.
@@ -206,7 +208,13 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<Runnin
config.queue.verifier_settings = cmd.verifier_settings;
// start on_demand service.
let on_demand = Arc::new(::light::on_demand::OnDemand::new(cache.clone()));
let on_demand = Arc::new({
let mut on_demand = ::light::on_demand::OnDemand::new(cache.clone());
on_demand.default_retry_number(cmd.on_demand_retry_count.unwrap_or(::light::on_demand::DEFAULT_RETRY_COUNT));
on_demand.query_inactive_time_limit(cmd.on_demand_inactive_time_limit.map(Duration::from_millis)
.unwrap_or(::light::on_demand::DEFAULT_QUERY_TIME_LIMIT));
on_demand
});
let sync_handle = Arc::new(RwLock::new(Weak::new()));
let fetch = ::light_helpers::EpochFetch {
@@ -352,7 +360,7 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<Runnin
fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq: Cr,
on_updater_rq: Rr) -> Result<RunningClient, String>
where Cr: Fn(String) + 'static + Send,
Rr: Fn() + 'static + Send
Rr: Fn() + 'static + Send
{
// load spec
let spec = cmd.spec.spec(&cmd.dirs.cache)?;
@@ -531,6 +539,7 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
client_config.queue.verifier_settings = cmd.verifier_settings;
client_config.transaction_verification_queue_size = ::std::cmp::max(2048, txpool_size / 4);
client_config.snapshot = cmd.snapshot_conf.clone();
// set up bootnodes
let mut net_conf = cmd.net_conf;
@@ -778,7 +787,7 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
});
// the watcher must be kept alive.
let watcher = match cmd.no_periodic_snapshot {
let watcher = match cmd.snapshot_conf.no_periodic {
true => None,
false => {
let sync = sync_provider.clone();
@@ -900,7 +909,7 @@ impl RunningClient {
pub fn execute<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>,
on_client_rq: Cr, on_updater_rq: Rr) -> Result<RunningClient, String>
where Cr: Fn(String) + 'static + Send,
Rr: Fn() + 'static + Send
Rr: Fn() + 'static + Send
{
if cmd.light {
execute_light_impl(cmd, logger)

View File

@@ -22,7 +22,7 @@ use std::sync::Arc;
use hash::keccak;
use ethcore::account_provider::AccountProvider;
use ethcore::snapshot::{Progress, RestorationStatus, SnapshotService as SS};
use ethcore::snapshot::{Progress, RestorationStatus, SnapshotConfiguration, SnapshotService as SS};
use ethcore::snapshot::io::{SnapshotReader, PackedReader, PackedWriter};
use ethcore::snapshot::service::Service as SnapshotService;
use ethcore::client::{Mode, DatabaseCompactionProfile, VMType};
@@ -62,6 +62,7 @@ pub struct SnapshotCommand {
pub file_path: Option<String>,
pub kind: Kind,
pub block_at: BlockId,
pub snapshot_conf: SnapshotConfiguration,
}
// helper for reading chunks from arbitrary reader and feeding them into the
@@ -165,7 +166,7 @@ impl SnapshotCommand {
execute_upgrades(&self.dirs.base, &db_dirs, algorithm, &self.compaction)?;
// prepare client config
let client_config = to_client_config(
let mut client_config = to_client_config(
&self.cache_config,
spec.name.to_lowercase(),
Mode::Active,
@@ -180,6 +181,8 @@ impl SnapshotCommand {
true,
);
client_config.snapshot = self.snapshot_conf;
let restoration_db_handler = db::restoration_db_handler(&client_path, &client_config);
let client_db = restoration_db_handler.open(&client_path)
.map_err(|e| format!("Failed to open database {:?}", e))?;

View File

@@ -7,7 +7,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
futures = "0.1"
ethabi = "5.1.2"
ethabi-derive = "5.1.3"
ethabi-contract = "5.1.1"
ethabi = "6.0"
ethabi-derive = "6.0"
ethabi-contract = "6.0"
keccak-hash = "0.1"

View File

@@ -19,7 +19,7 @@ use ethabi::{Address, Bytes};
use std::sync::Arc;
use keccak_hash::keccak;
use_contract!(registry, "Registry", "res/registrar.json");
use_contract!(registrar, "res/registrar.json");
// Maps a domain name to an Ethereum address
const DNS_A_RECORD: &'static str = "A";
@@ -30,7 +30,6 @@ pub type Synchronous = Result<Bytes, String>;
/// Registrar is dedicated interface to access the registrar contract
/// which in turn generates an address when a client requests one
pub struct Registrar {
registrar: registry::Registry,
client: Arc<RegistrarClient<Call=Asynchronous>>,
}
@@ -38,7 +37,6 @@ impl Registrar {
/// Registrar constructor
pub fn new(client: Arc<RegistrarClient<Call=Asynchronous>>) -> Self {
Self {
registrar: registry::Registry::default(),
client: client,
}
}
@@ -51,14 +49,11 @@ impl Registrar {
Err(e) => return Box::new(future::err(e)),
};
let address_fetcher = self.registrar.functions().get_address();
let hashed_key: [u8; 32] = keccak(key).into();
let id = address_fetcher.input(hashed_key, DNS_A_RECORD);
let id = registrar::functions::get_address::encode_input(hashed_key, DNS_A_RECORD);
let future = self.client.call_contract(registrar_address, id).and_then(move |address| {
address_fetcher.output(&address)
}
.map_err(|e| e.to_string()));
let future = self.client.call_contract(registrar_address, id)
.and_then(move |address| registrar::functions::get_address::decode_output(&address).map_err(|e| e.to_string()));
Box::new(future)
}

View File

@@ -58,7 +58,7 @@ keccak-hash = "0.1.2"
parity-reactor = { path = "../util/reactor" }
parity-updater = { path = "../updater" }
parity-version = { path = "../util/version" }
patricia-trie = "0.2.1"
patricia-trie = "0.2"
rlp = { version = "0.2.4", features = ["ethereum"] }
stats = { path = "../util/stats" }
vm = { path = "../ethcore/vm" }

View File

@@ -50,8 +50,6 @@ impl TimeProvider for DefaultTimeProvider {
const TIME_THRESHOLD: u64 = 7;
/// minimal length of hash
const TOKEN_LENGTH: usize = 16;
/// special "initial" token used for authorization when there are no tokens yet.
const INITIAL_TOKEN: &'static str = "initial";
/// Separator between fields in serialized tokens file.
const SEPARATOR: &'static str = ";";
/// Number of seconds to keep unused tokens.
@@ -163,16 +161,6 @@ impl<T: TimeProvider> AuthCodes<T> {
let as_token = |code| keccak(format!("{}:{}", code, time));
// Check if it's the initial token.
if self.is_empty() {
let initial = &as_token(INITIAL_TOKEN) == hash;
// Initial token can be used only once.
if initial {
let _ = self.generate_new();
}
return initial;
}
// look for code
for code in &mut self.codes {
if &as_token(&code.code) == hash {
@@ -239,7 +227,7 @@ mod tests {
}
#[test]
fn should_return_true_if_code_is_initial_and_store_is_empty() {
fn should_return_false_even_if_code_is_initial_and_store_is_empty() {
// given
let code = "initial";
let time = 99;
@@ -250,7 +238,7 @@ mod tests {
let res2 = codes.is_valid(&generate_hash(code, time), time);
// then
assert_eq!(res1, true);
assert_eq!(res1, false);
assert_eq!(res2, false);
}

View File

@@ -136,7 +136,7 @@ mod testing {
}
#[test]
fn should_allow_initial_connection_but_only_once() {
fn should_not_allow_initial_connection_even_once() {
// given
let (server, port, authcodes) = serve();
let code = "initial";
@@ -160,26 +160,9 @@ mod testing {
timestamp,
)
);
let response2 = http_client::request(server.addr(),
&format!("\
GET / HTTP/1.1\r\n\
Host: 127.0.0.1:{}\r\n\
Connection: Close\r\n\
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n\
Sec-WebSocket-Protocol:{:?}_{}\r\n\
Sec-WebSocket-Version: 13\r\n\
\r\n\
{{}}
",
port,
keccak(format!("{}:{}", code, timestamp)),
timestamp,
)
);
// then
assert_eq!(response1.status, "HTTP/1.1 101 Switching Protocols".to_owned());
assert_eq!(response2.status, "HTTP/1.1 403 Forbidden".to_owned());
http_client::assert_security_headers_present(&response2.headers, None);
assert_eq!(response1.status, "HTTP/1.1 403 Forbidden".to_owned());
http_client::assert_security_headers_present(&response1.headers, None);
}
}

View File

@@ -30,14 +30,15 @@ use bytes::Bytes;
use parking_lot::{Mutex, RwLock};
use stats::Corpus;
use crypto::DEFAULT_MAC;
use ethcore::account_provider::AccountProvider;
use ethcore::basic_account::BasicAccount;
use ethcore::client::BlockChainClient;
use ethcore::ids::BlockId;
use ethcore::miner::{self, MinerService};
use ethkey::{Password, Signature};
use sync::LightSync;
use ethcore::ids::BlockId;
use ethcore::client::BlockChainClient;
use ethcore::miner::{self, MinerService};
use ethcore::account_provider::AccountProvider;
use crypto::DEFAULT_MAC;
use transaction::{Action, SignedTransaction, PendingTransaction, Transaction};
use transaction::{Action, SignedTransaction, PendingTransaction, Transaction, Error as TransactionError};
use jsonrpc_core::{BoxFuture, Result, Error};
use jsonrpc_core::futures::{future, Future, Poll, Async};
@@ -218,7 +219,6 @@ pub fn fetch_gas_price_corpus(
for t in block.transaction_views().iter() {
v.push(t.gas_price())
}
v
})
})
@@ -302,40 +302,42 @@ impl LightDispatcher {
)
}
/// Get an account's next nonce.
pub fn next_nonce(&self, addr: Address) -> BoxFuture<U256> {
// fast path where we don't go to network; nonce provided or can be gotten from queue.
let maybe_nonce = self.transaction_queue.read().next_nonce(&addr);
if let Some(nonce) = maybe_nonce {
return Box::new(future::ok(nonce))
}
/// Get an account's state
fn account(&self, addr: Address) -> BoxFuture<Option<BasicAccount>> {
let best_header = self.client.best_block_header();
let account_start_nonce = self.client.engine().account_start_nonce(best_header.number());
let nonce_future = self.sync.with_context(|ctx| self.on_demand.request(ctx, request::Account {
let account_future = self.sync.with_context(|ctx| self.on_demand.request(ctx, request::Account {
header: best_header.into(),
address: addr,
}).expect("no back-references; therefore all back-references valid; qed"));
match nonce_future {
Some(x) => Box::new(
x.map(move |acc| acc.map_or(account_start_nonce, |acc| acc.nonce))
.map_err(|_| errors::no_light_peers())
),
None => Box::new(future::err(errors::network_disabled()))
match account_future {
Some(response) => Box::new(response.map_err(|_| errors::no_light_peers())),
None => Box::new(future::err(errors::network_disabled())),
}
}
/// Get an account's next nonce.
pub fn next_nonce(&self, addr: Address) -> BoxFuture<U256> {
let account_start_nonce = self.client.engine().account_start_nonce(self.client.best_block_header().number());
Box::new(self.account(addr)
.and_then(move |maybe_account| {
future::ok(maybe_account.map_or(account_start_nonce, |account| account.nonce))
})
)
}
}
impl Dispatcher for LightDispatcher {
fn fill_optional_fields(&self, request: TransactionRequest, default_sender: Address, force_nonce: bool)
// Ignore the `force_nonce` flag in order to always query the network when fetching the nonce and
// the account state. If the nonce is specified in the transaction use that nonce instead but do the
// network request anyway to the account state (balance)
fn fill_optional_fields(&self, request: TransactionRequest, default_sender: Address, _force_nonce: bool)
-> BoxFuture<FilledTransactionRequest>
{
const DEFAULT_GAS_PRICE: U256 = U256([0, 0, 0, 21_000_000]);
let gas_limit = self.client.best_block_header().gas_limit();
let request_gas_price = request.gas_price.clone();
let request_nonce = request.nonce.clone();
let from = request.from.unwrap_or(default_sender);
let with_gas_price = move |gas_price| {
@@ -368,39 +370,37 @@ impl Dispatcher for LightDispatcher {
}).map(with_gas_price))
};
match (request_nonce, force_nonce) {
(_, false) | (Some(_), true) => Box::new(gas_price),
(None, true) => {
let next_nonce = self.next_nonce(from);
Box::new(gas_price.and_then(move |mut filled| next_nonce
.map_err(|_| errors::no_light_peers())
.map(move |nonce| {
filled.nonce = Some(nonce);
filled
})
))
},
}
let future_account = self.account(from);
Box::new(gas_price.and_then(move |mut filled| {
future_account
.and_then(move |maybe_account| {
let cost = filled.value.saturating_add(filled.gas.saturating_mul(filled.gas_price));
match maybe_account {
Some(ref account) if cost > account.balance => {
Err(errors::transaction(TransactionError::InsufficientBalance {
balance: account.balance,
cost,
}))
}
Some(account) => {
if filled.nonce.is_none() {
filled.nonce = Some(account.nonce);
}
Ok(filled)
}
None => Err(errors::account("Account not found", "")),
}
})
}))
}
fn sign(&self, accounts: Arc<AccountProvider>, filled: FilledTransactionRequest, password: SignWith)
-> BoxFuture<WithToken<SignedTransaction>>
{
let chain_id = self.client.signing_chain_id();
// fast path for pre-filled nonce.
if let Some(nonce) = filled.nonce {
return Box::new(future::done(sign_transaction(&*accounts, filled, chain_id, nonce, password)))
}
let nonces = self.nonces.clone();
Box::new(self.next_nonce(filled.from)
.map_err(|_| errors::no_light_peers())
.and_then(move |nonce| {
let reserved = nonces.lock().reserve(filled.from, nonce);
ProspectiveSigner::new(accounts, filled, chain_id, reserved, password)
}))
let nonce = filled.nonce.expect("nonce is always provided; qed");
Box::new(future::done(sign_transaction(&*accounts, filled, chain_id, nonce, password)))
}
fn enrich(&self, signed_transaction: SignedTransaction) -> RpcRichRawTransaction {

View File

@@ -26,6 +26,7 @@ use rlp::DecoderError;
use transaction::Error as TransactionError;
use ethcore_private_tx::Error as PrivateTransactionError;
use vm::Error as VMError;
use light::on_demand::error::{Error as OnDemandError, ErrorKind as OnDemandErrorKind};
mod codes {
// NOTE [ToDr] Codes from [-32099, -32000]
@@ -444,7 +445,41 @@ pub fn filter_block_not_found(id: BlockId) -> Error {
}
}
pub fn on_demand_error(err: OnDemandError) -> Error {
match err {
OnDemandError(OnDemandErrorKind::ChannelCanceled(e), _) => on_demand_cancel(e),
OnDemandError(OnDemandErrorKind::MaxAttemptReach(_), _) => max_attempts_reached(&err),
OnDemandError(OnDemandErrorKind::TimeoutOnNewPeers(_,_), _) => timeout_new_peer(&err),
_ => on_demand_others(&err),
}
}
// on-demand sender cancelled.
pub fn on_demand_cancel(_cancel: futures::sync::oneshot::Canceled) -> Error {
internal("on-demand sender cancelled", "")
}
pub fn max_attempts_reached(err: &OnDemandError) -> Error {
Error {
code: ErrorCode::ServerError(codes::REQUEST_NOT_FOUND),
message: err.to_string(),
data: None,
}
}
pub fn timeout_new_peer(err: &OnDemandError) -> Error {
Error {
code: ErrorCode::ServerError(codes::NO_LIGHT_PEERS),
message: err.to_string(),
data: None,
}
}
pub fn on_demand_others(err: &OnDemandError) -> Error {
Error {
code: ErrorCode::ServerError(codes::UNKNOWN_ERROR),
message: err.to_string(),
data: None,
}
}

View File

@@ -19,6 +19,7 @@
use std::cmp;
use std::sync::Arc;
use light::on_demand::error::Error as OnDemandError;
use ethcore::basic_account::BasicAccount;
use ethcore::encoded;
use ethcore::filter::Filter as EthcoreFilter;
@@ -49,7 +50,9 @@ use transaction::{Action, Transaction as EthTransaction, SignedTransaction, Loca
use v1::helpers::{CallRequest as CallRequestHelper, errors, dispatch};
use v1::types::{BlockNumber, CallRequest, Log, Transaction};
const NO_INVALID_BACK_REFS: &str = "Fails only on invalid back-references; back-references here known to be valid; qed";
const NO_INVALID_BACK_REFS_PROOF: &str = "Fails only on invalid back-references; back-references here known to be valid; qed";
const WRONG_RESPONSE_AMOUNT_TYPE_PROOF: &str = "responses correspond directly with requests in amount and type; qed";
/// Helper for fetching blockchain data either from the light client or the network
/// as necessary.
@@ -148,7 +151,7 @@ impl LightFetch {
Either::B(self.send_requests(reqs, |res|
extract_header(&res, header_ref)
.expect("these responses correspond to requests that header_ref belongs to \
therefore it will not fail; qed")
therefore it will not fail; qed")
))
}
@@ -166,7 +169,7 @@ impl LightFetch {
Either::B(self.send_requests(reqs, |mut res| match res.pop() {
Some(OnDemandResponse::Code(code)) => code,
_ => panic!("responses correspond directly with requests in amount and type; qed"),
_ => panic!(WRONG_RESPONSE_AMOUNT_TYPE_PROOF),
}))
}
@@ -183,7 +186,7 @@ impl LightFetch {
Either::B(self.send_requests(reqs, |mut res|match res.pop() {
Some(OnDemandResponse::Account(acc)) => acc,
_ => panic!("responses correspond directly with requests in amount and type; qed"),
_ => panic!(WRONG_RESPONSE_AMOUNT_TYPE_PROOF),
}))
}
@@ -276,7 +279,7 @@ impl LightFetch {
Either::B(self.send_requests(reqs, |mut res| match res.pop() {
Some(OnDemandResponse::Body(b)) => b,
_ => panic!("responses correspond directly with requests in amount and type; qed"),
_ => panic!(WRONG_RESPONSE_AMOUNT_TYPE_PROOF),
}))
}
@@ -292,7 +295,7 @@ impl LightFetch {
Either::B(self.send_requests(reqs, |mut res| match res.pop() {
Some(OnDemandResponse::Receipts(b)) => b,
_ => panic!("responses correspond directly with requests in amount and type; qed"),
_ => panic!(WRONG_RESPONSE_AMOUNT_TYPE_PROOF),
}))
}
@@ -322,7 +325,7 @@ impl LightFetch {
bit_combos.iter().any(|bloom| hdr_bloom.contains_bloom(bloom))
})
.map(|hdr| (hdr.number(), hdr.hash(), request::BlockReceipts(hdr.into())))
.map(|(num, hash, req)| on_demand.request(ctx, req).expect(NO_INVALID_BACK_REFS).map(move |x| (num, hash, x)))
.map(|(num, hash, req)| on_demand.request(ctx, req).expect(NO_INVALID_BACK_REFS_PROOF).map(move |x| (num, hash, x)))
.collect();
// as the receipts come in, find logs within them which match the filter.
@@ -351,10 +354,10 @@ impl LightFetch {
block_index += 1;
}
}
future::ok(matches)
future::ok::<_,OnDemandError>(matches)
}) // and then collect them into a vector.
.map(|matches| matches.into_iter().map(|(_, v)| v).collect())
.map_err(errors::on_demand_cancel)
.map_err(errors::on_demand_error)
});
match maybe_future {
@@ -379,7 +382,7 @@ impl LightFetch {
});
let eventual_index = match maybe_future {
Some(e) => e.expect(NO_INVALID_BACK_REFS).map_err(errors::on_demand_cancel),
Some(e) => e.expect(NO_INVALID_BACK_REFS_PROOF).map_err(errors::on_demand_error),
None => return Either::A(future::err(errors::network_disabled())),
};
@@ -429,9 +432,15 @@ impl LightFetch {
{
let maybe_future = self.sync.with_context(move |ctx| {
Box::new(self.on_demand.request_raw(ctx, reqs)
.expect(NO_INVALID_BACK_REFS)
.map(parse_response)
.map_err(errors::on_demand_cancel))
.expect(NO_INVALID_BACK_REFS_PROOF)
.map_err(errors::on_demand_cancel)
.and_then(|responses| {
match responses {
Ok(responses) => Ok(parse_response(responses)),
Err(e) => Err(errors::on_demand_error(e)),
}
})
)
});
match maybe_future {
@@ -638,7 +647,7 @@ fn execute_tx(gas_known: bool, params: ExecuteParams) -> impl Future<Item = Exec
on_demand
.request(ctx, request)
.expect("no back-references; therefore all back-refs valid; qed")
.map_err(errors::on_demand_cancel)
.map_err(errors::on_demand_error)
});
match proved_future {

View File

@@ -216,7 +216,7 @@ impl<T: LightChainClient + 'static> EthClient<T> {
};
fill_rich(block, score)
}).map_err(errors::on_demand_cancel)),
}).map_err(errors::on_demand_error)),
None => Either::A(future::err(errors::network_disabled())),
}
}
@@ -316,7 +316,7 @@ impl<T: LightChainClient + 'static> Eth for EthClient<T> {
sync.with_context(|ctx| on_demand.request(ctx, request::Body(hdr.into())))
.map(|x| x.expect(NO_INVALID_BACK_REFS))
.map(|x| x.map(|b| Some(U256::from(b.transactions_count()).into())))
.map(|x| Either::B(x.map_err(errors::on_demand_cancel)))
.map(|x| Either::B(x.map_err(errors::on_demand_error)))
.unwrap_or_else(|| Either::A(future::err(errors::network_disabled())))
}
}))
@@ -332,7 +332,7 @@ impl<T: LightChainClient + 'static> Eth for EthClient<T> {
sync.with_context(|ctx| on_demand.request(ctx, request::Body(hdr.into())))
.map(|x| x.expect(NO_INVALID_BACK_REFS))
.map(|x| x.map(|b| Some(U256::from(b.transactions_count()).into())))
.map(|x| Either::B(x.map_err(errors::on_demand_cancel)))
.map(|x| Either::B(x.map_err(errors::on_demand_error)))
.unwrap_or_else(|| Either::A(future::err(errors::network_disabled())))
}
}))
@@ -348,7 +348,7 @@ impl<T: LightChainClient + 'static> Eth for EthClient<T> {
sync.with_context(|ctx| on_demand.request(ctx, request::Body(hdr.into())))
.map(|x| x.expect(NO_INVALID_BACK_REFS))
.map(|x| x.map(|b| Some(U256::from(b.uncles_count()).into())))
.map(|x| Either::B(x.map_err(errors::on_demand_cancel)))
.map(|x| Either::B(x.map_err(errors::on_demand_error)))
.unwrap_or_else(|| Either::A(future::err(errors::network_disabled())))
}
}))
@@ -364,7 +364,7 @@ impl<T: LightChainClient + 'static> Eth for EthClient<T> {
sync.with_context(|ctx| on_demand.request(ctx, request::Body(hdr.into())))
.map(|x| x.expect(NO_INVALID_BACK_REFS))
.map(|x| x.map(|b| Some(U256::from(b.uncles_count()).into())))
.map(|x| Either::A(x.map_err(errors::on_demand_cancel)))
.map(|x| Either::A(x.map_err(errors::on_demand_error)))
.unwrap_or_else(|| Either::B(future::err(errors::network_disabled())))
}
}))

View File

@@ -22,7 +22,12 @@ use v1::traits::Net;
/// Net rpc implementation.
pub struct NetClient<S: ?Sized> {
sync: Arc<S>
sync: Arc<S>,
/// Cached `network_id`.
///
/// We cache it to avoid redundant aquire of sync read lock.
/// https://github.com/paritytech/parity-ethereum/issues/8746
network_id: u64,
}
impl<S: ?Sized> NetClient<S> where S: SyncProvider {
@@ -30,17 +35,18 @@ impl<S: ?Sized> NetClient<S> where S: SyncProvider {
pub fn new(sync: &Arc<S>) -> Self {
NetClient {
sync: sync.clone(),
network_id: sync.status().network_id,
}
}
}
impl<S: ?Sized> Net for NetClient<S> where S: SyncProvider + 'static {
fn version(&self) -> Result<String> {
Ok(format!("{}", self.sync.status().network_id).to_owned())
Ok(format!("{}", self.network_id))
}
fn peer_count(&self) -> Result<String> {
Ok(format!("0x{:x}", self.sync.status().num_peers as u64).to_owned())
Ok(format!("{:#x}", self.sync.status().num_peers as u64))
}
fn is_listening(&self) -> Result<bool> {

View File

@@ -1,53 +0,0 @@
#!/usr/bin/env bash
clone_repos() {
git clone https://github.com/parity-js/jsonrpc.git jsonrpc
git clone https://github.com/paritytech/wiki.git wiki
}
build_docs() {
npm install
npm run build:markdown
}
update_wiki_docs() {
for file in $(ls jsonrpc/docs); do
module_name=${file:0:-3}
mv jsonrpc/docs/$file wiki/JSONRPC-$module_name-module.md
done
}
set_remote_wiki() {
git config remote.origin.url "https://${GITHUB_TOKEN}@github.com/paritytech/wiki.git"
}
setup_git() {
git config --global user.email "devops@parity.com"
git config --global user.name "Devops Parity"
}
commit_files() {
git checkout -b rpcdoc-update-${CI_COMMIT_REF_NAME}
git add .
git commit -m "Update docs to ${CI_COMMIT_REF_NAME}"
git tag -a "${CI_COMMIT_REF_NAME}" -m "Updated to ${CI_COMMIT_REF_NAME}"
}
upload_files() {
git push --tags
}
PROJECT_DIR=$(pwd)
setup_git
cd ..
clone_repos
cp -r $PROJECT_DIR jsonrpc/.parity
cd jsonrpc
build_docs
cd ..
update_wiki_docs
cd wiki
set_remote_wiki
commit_files
upload_files

View File

@@ -3,15 +3,14 @@
set -e # fail on any error
set -u # treat unset variables as error
case ${CI_COMMIT_REF_NAME} in
nightly|*v2.1*) export GRADE="devel";;
beta|*v2.0*) export GRADE="stable";;
stable|*v1.11*) export GRADE="stable";;
nightly|*v2.2*) export GRADE="devel";;
beta|*v2.1*) export GRADE="stable";;
stable|*v2.0*) export GRADE="stable";;
*) echo "No release" exit 0;;
esac
SNAP_PACKAGE="parity_"$VERSION"_"$BUILD_ARCH".snap"
echo "__________Create snap package__________"
echo "Release channel :" $GRADE " Branch/tag: " $CI_COMMIT_REF_NAME
snapcraft clean
echo $VERSION:$GRADE:$BUILD_ARCH
cat scripts/gitlab/templates/snapcraft.template.yaml | envsubst '$VERSION:$GRADE:$BUILD_ARCH:$CARGO_TARGET' > snapcraft.yaml
cat snapcraft.yaml

View File

@@ -3,7 +3,7 @@
set -e # fail on any error
set -u # treat unset variables as error
if [ "$CI_COMMIT_REF_NAME" == "beta" ];
if [ "$CI_COMMIT_REF_NAME" == "master" ];
then export DOCKER_BUILD_TAG="latest";
else export DOCKER_BUILD_TAG=$CI_COMMIT_REF_NAME;
fi

View File

@@ -4,9 +4,9 @@ set -e # fail on any error
set -u # treat unset variables as error
case ${CI_COMMIT_REF_NAME} in
nightly|*v2.1*) export CHANNEL="edge";;
beta|*v2.0*) export CHANNEL="beta";;
stable|*v1.11*) export CHANNEL="stable";;
nightly|*v2.2*) export CHANNEL="edge";;
beta|*v2.1*) export CHANNEL="beta";;
stable|*v2.0*) export CHANNEL="stable";;
*) echo "No release" exit 0;;
esac
echo "Release channel :" $CHANNEL " Branch/tag: " $CI_COMMIT_REF_NAME

View File

@@ -14,9 +14,9 @@ RELEASE_TABLE="$(echo "${RELEASE_TABLE//\$VERSION/${VERSION}}")"
#The text in the file CANGELOG.md before which the table with links is inserted. Must be present in this file necessarily
REPLACE_TEXT="The full list of included changes:"
case ${CI_COMMIT_REF_NAME} in
nightly|*v2.1*) NAME="Parity "$VERSION" nightly";;
beta|*v2.0*) NAME="Parity "$VERSION" beta";;
stable|*v1.11*) NAME="Parity "$VERSION" stable";;
nightly|*v2.2*) NAME="Parity "$VERSION" nightly";;
beta|*v2.1*) NAME="Parity "$VERSION" beta";;
stable|*v2.0*) NAME="Parity "$VERSION" stable";;
*) echo "No release" exit 0;;
esac
cd artifacts

View File

@@ -39,15 +39,16 @@ commit_files() {
upload_files() {
echo "__________Upload files__________"
git push origin HEAD
git push --tags
}
RPC_TRAITS_DIR="rpc/src/v1/traits/"
RPC_TRAITS_DIR="rpc/src/v1/traits"
setup_git
clone_repos
mkdir -p "jsonrpc/.parity/$RPC_TRAITS_DIR"
cp -r "$RPC_TRAITS_DIR" "jsonrpc/.parity/$RPC_TRAITS_DIR"
cp $RPC_TRAITS_DIR/*.rs "jsonrpc/.parity/$RPC_TRAITS_DIR"
cd jsonrpc
build_docs
cd ..

View File

@@ -4,25 +4,41 @@
set -e # fail on any error
set -u # treat unset variables as error
rustup default $1
if [[ "$CI_COMMIT_REF_NAME" = "beta" || "$CI_COMMIT_REF_NAME" = "stable" ]]; then
export GIT_COMPARE=$CI_COMMIT_REF_NAME~;
else
export GIT_COMPARE=master;
set -x # full command output for development
git log --graph --oneline --all --decorate=short -n 10
case $CI_COMMIT_REF_NAME in
(master|beta|stable)
export GIT_COMPARE=$CI_COMMIT_REF_NAME~
;;
(*)
export GIT_COMPARE=master
;;
esac
export RUST_FILES_MODIFIED="$(git --no-pager diff --name-only $GIT_COMPARE...$CI_COMMIT_SHA | grep -v -e ^\\. -e ^LICENSE -e ^README.md -e ^test.sh -e ^scripts/ | wc -l | tr -d ' ')"
echo "RUST_FILES_MODIFIED: $RUST_FILES_MODIFIED"
if [ "${RUST_FILES_MODIFIED}" = "0" ]
then
echo "__________Skipping Rust tests since no Rust files modified__________";
exit 0
fi
export RUST_FILES_MODIFIED="$(git --no-pager diff --name-only $GIT_COMPARE...$CI_COMMIT_SHA | grep -v -e ^\\. -e ^LICENSE -e ^README.md -e ^test.sh -e ^windows/ -e ^scripts/ -e ^mac/ -e ^nsis/ | wc -l)"
echo "RUST_FILES_MODIFIED: $RUST_FILES_MODIFIED"
rustup default $1
git submodule update --init --recursive
rustup show
if [[ "${RUST_FILES_MODIFIED}" == "0" ]];
then echo "__________Skipping Rust tests since no Rust files modified__________";
else ./test.sh || exit $?;
fi
exec ./test.sh
# if [[ "$CI_COMMIT_REF_NAME" == "nightly" ]];
# ### @TODO re-enable fail after https://github.com/paritytech/parity-import-tests/issues/3
# then sh scripts/aura-test.sh; # || exit $?;
# fi

View File

@@ -34,9 +34,9 @@ kvdb = "0.1"
keccak-hash = "0.1"
ethkey = { path = "../ethkey" }
lazy_static = "1.0"
ethabi = "5.1.2"
ethabi-derive = "5.1.3"
ethabi-contract = "5.1.1"
ethabi = "6.0"
ethabi-derive = "6.0"
ethabi-contract = "6.0"
[dev-dependencies]
ethcore = { path = "../ethcore", features = ["test-helpers"] }

View File

@@ -20,11 +20,12 @@ use std::time::Duration;
use parking_lot::{Mutex, RwLock};
use ethcore::client::{BlockId, ChainNotify, ChainRoute, CallContract};
use ethereum_types::{H256, Address};
use ethabi::FunctionOutputDecoder;
use bytes::Bytes;
use trusted_client::TrustedClient;
use types::{Error, ServerKeyId, ContractAddress};
use_contract!(acl_storage, "AclStorage", "res/acl_storage.json");
use_contract!(acl_storage, "res/acl_storage.json");
const ACL_CHECKER_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_acl_checker";
@@ -48,8 +49,6 @@ struct CachedContract {
address_source: ContractAddress,
/// Current contract address.
contract_address: Option<Address>,
/// Contract at given address.
contract: acl_storage::AclStorage,
}
/// Dummy ACL storage implementation (check always passed).
@@ -91,7 +90,6 @@ impl CachedContract {
client,
address_source,
contract_address: None,
contract: acl_storage::AclStorage::default(),
};
contract.update_contract_address();
contract
@@ -112,10 +110,10 @@ impl CachedContract {
// call contract to check accesss
match self.contract_address {
Some(contract_address) => {
let do_call = |data| client.call_contract(BlockId::Latest, contract_address, data);
self.contract.functions()
.check_permissions()
.call(requester, document.clone(), &do_call)
let (encoded, decoder) = acl_storage::functions::check_permissions::call(requester, document.clone());
let d = client.call_contract(BlockId::Latest, contract_address, encoded)
.map_err(|e| Error::Internal(format!("ACL checker call error: {}", e.to_string())))?;
decoder.decode(&d)
.map_err(|e| Error::Internal(format!("ACL checker call error: {}", e.to_string())))
},
None => Err(Error::Internal("ACL checker contract is not configured".to_owned())),

View File

@@ -19,15 +19,16 @@ use std::net::SocketAddr;
use std::collections::{BTreeMap, HashSet};
use std::time::Duration;
use parking_lot::Mutex;
use ethabi::FunctionOutputDecoder;
use ethcore::client::{Client, BlockChainClient, BlockId, ChainNotify, ChainRoute, CallContract};
use ethkey::public_to_address;
use ethereum_types::{H256, Address};
use ethkey::public_to_address;
use bytes::Bytes;
use types::{Error, Public, NodeAddress, NodeId};
use trusted_client::TrustedClient;
use {NodeKeyPair, ContractAddress};
use_contract!(key_server, "KeyServerSet", "res/key_server_set.json");
use_contract!(key_server, "res/key_server_set.json");
/// Name of KeyServerSet contract in registry.
const KEY_SERVER_SET_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_server_set";
@@ -104,8 +105,6 @@ struct CachedContract {
contract_address_source: Option<ContractAddress>,
/// Current contract address.
contract_address: Option<Address>,
/// Contract interface.
contract: key_server::KeyServerSet,
/// Is auto-migrate enabled?
auto_migrate_enabled: bool,
/// Current contract state.
@@ -169,66 +168,60 @@ trait KeyServerSubset<F: Fn(Vec<u8>) -> Result<Vec<u8>, String>> {
fn read_address(&self, address: Address, f: &F) -> Result<String, String>;
}
#[derive(Default)]
struct CurrentKeyServerSubset {
read_list: key_server::functions::GetCurrentKeyServers,
read_public: key_server::functions::GetCurrentKeyServerPublic,
read_address: key_server::functions::GetCurrentKeyServerAddress,
}
struct CurrentKeyServerSubset;
impl <F: Fn(Vec<u8>) -> Result<Vec<u8>, String>> KeyServerSubset<F> for CurrentKeyServerSubset {
fn read_list(&self, f: &F) -> Result<Vec<Address>, String> {
self.read_list.call(f).map_err(|e| e.to_string())
let (encoded, decoder) = key_server::functions::get_current_key_servers::call();
decoder.decode(&f(encoded)?).map_err(|e| e.to_string())
}
fn read_public(&self, address: Address, f: &F) -> Result<Bytes, String> {
self.read_public.call(address, f).map_err(|e| e.to_string())
let (encoded, decoder) = key_server::functions::get_current_key_server_public::call(address);
decoder.decode(&f(encoded)?).map_err(|e| e.to_string())
}
fn read_address(&self, address: Address, f: &F) -> Result<String, String> {
self.read_address.call(address, f).map_err(|e| e.to_string())
let (encoded, decoder) = key_server::functions::get_current_key_server_address::call(address);
decoder.decode(&f(encoded)?).map_err(|e| e.to_string())
}
}
#[derive(Default)]
struct MigrationKeyServerSubset {
read_list: key_server::functions::GetMigrationKeyServers,
read_public: key_server::functions::GetMigrationKeyServerPublic,
read_address: key_server::functions::GetMigrationKeyServerAddress,
}
struct MigrationKeyServerSubset;
impl <F: Fn(Vec<u8>) -> Result<Vec<u8>, String>> KeyServerSubset<F> for MigrationKeyServerSubset {
fn read_list(&self, f: &F) -> Result<Vec<Address>, String> {
self.read_list.call(f).map_err(|e| e.to_string())
let (encoded, decoder) = key_server::functions::get_migration_key_servers::call();
decoder.decode(&f(encoded)?).map_err(|e| e.to_string())
}
fn read_public(&self, address: Address, f: &F) -> Result<Bytes, String> {
self.read_public.call(address, f).map_err(|e| e.to_string())
let (encoded, decoder) = key_server::functions::get_migration_key_server_public::call(address);
decoder.decode(&f(encoded)?).map_err(|e| e.to_string())
}
fn read_address(&self, address: Address, f: &F) -> Result<String, String> {
self.read_address.call(address, f).map_err(|e| e.to_string())
let (encoded, decoder) = key_server::functions::get_migration_key_server_address::call(address);
decoder.decode(&f(encoded)?).map_err(|e| e.to_string())
}
}
#[derive(Default)]
struct NewKeyServerSubset {
read_list: key_server::functions::GetNewKeyServers,
read_public: key_server::functions::GetNewKeyServerPublic,
read_address: key_server::functions::GetNewKeyServerAddress,
}
struct NewKeyServerSubset;
impl <F: Fn(Vec<u8>) -> Result<Vec<u8>, String>> KeyServerSubset<F> for NewKeyServerSubset {
fn read_list(&self, f: &F) -> Result<Vec<Address>, String> {
self.read_list.call(f).map_err(|e| e.to_string())
let (encoded, decoder) = key_server::functions::get_new_key_servers::call();
decoder.decode(&f(encoded)?).map_err(|e| e.to_string())
}
fn read_public(&self, address: Address, f: &F) -> Result<Bytes, String> {
self.read_public.call(address, f).map_err(|e| e.to_string())
let (encoded, decoder) = key_server::functions::get_new_key_server_public::call(address);
decoder.decode(&f(encoded)?).map_err(|e| e.to_string())
}
fn read_address(&self, address: Address, f: &F) -> Result<String, String> {
self.read_address.call(address, f).map_err(|e| e.to_string())
let (encoded, decoder) = key_server::functions::get_new_key_server_address::call(address);
decoder.decode(&f(encoded)?).map_err(|e| e.to_string())
}
}
@@ -249,7 +242,6 @@ impl CachedContract {
client: client,
contract_address_source: contract_address_source,
contract_address: None,
contract: key_server::KeyServerSet::default(),
auto_migrate_enabled: auto_migrate_enabled,
future_new_set: None,
confirm_migration_tx: None,
@@ -313,7 +305,7 @@ impl CachedContract {
}
// prepare transaction data
let transaction_data = self.contract.functions().start_migration().input(migration_id);
let transaction_data = key_server::functions::start_migration::encode_input(migration_id);
// send transaction
match self.client.transact_contract(*contract_address, transaction_data) {
@@ -334,7 +326,7 @@ impl CachedContract {
}
// prepare transaction data
let transaction_data = self.contract.functions().confirm_migration().input(migration_id);
let transaction_data = key_server::functions::confirm_migration::encode_input(migration_id);
// send transaction
match self.client.transact_contract(contract_address, transaction_data) {
@@ -362,36 +354,50 @@ impl CachedContract {
let do_call = |data| client.call_contract(BlockId::Latest, contract_address, data);
let current_set = Self::read_key_server_set(CurrentKeyServerSubset::default(), &do_call);
let current_set = Self::read_key_server_set(CurrentKeyServerSubset, &do_call);
// read migration-related data if auto migration is enabled
let (new_set, migration) = match self.auto_migrate_enabled {
true => {
let new_set = Self::read_key_server_set(NewKeyServerSubset::default(), &do_call);
let migration_set = Self::read_key_server_set(MigrationKeyServerSubset::default(), &do_call);
let new_set = Self::read_key_server_set(NewKeyServerSubset, &do_call);
let migration_set = Self::read_key_server_set(MigrationKeyServerSubset, &do_call);
let migration_id = match migration_set.is_empty() {
false => self.contract.functions().get_migration_id().call(&do_call)
.map_err(|err| { trace!(target: "secretstore", "Error {} reading migration id from contract", err); err })
.ok(),
false => {
let (encoded, decoder) = key_server::functions::get_migration_id::call();
do_call(encoded)
.map_err(|e| e.to_string())
.and_then(|data| decoder.decode(&data).map_err(|e| e.to_string()))
.map_err(|err| { trace!(target: "secretstore", "Error {} reading migration id from contract", err); err })
.ok()
},
true => None,
};
let migration_master = match migration_set.is_empty() {
false => self.contract.functions().get_migration_master().call(&do_call)
.map_err(|err| { trace!(target: "secretstore", "Error {} reading migration master from contract", err); err })
.ok()
.and_then(|address| current_set.keys().chain(migration_set.keys())
.find(|public| public_to_address(public) == address)
.cloned()),
false => {
let (encoded, decoder) = key_server::functions::get_migration_master::call();
do_call(encoded)
.map_err(|e| e.to_string())
.and_then(|data| decoder.decode(&data).map_err(|e| e.to_string()))
.map_err(|err| { trace!(target: "secretstore", "Error {} reading migration master from contract", err); err })
.ok()
.and_then(|address| current_set.keys().chain(migration_set.keys())
.find(|public| public_to_address(public) == address)
.cloned())
},
true => None,
};
let is_migration_confirmed = match migration_set.is_empty() {
false if current_set.contains_key(self.self_key_pair.public()) || migration_set.contains_key(self.self_key_pair.public()) =>
self.contract.functions().is_migration_confirmed().call(self.self_key_pair.address(), &do_call)
false if current_set.contains_key(self.self_key_pair.public()) || migration_set.contains_key(self.self_key_pair.public()) => {
let (encoded, decoder) = key_server::functions::is_migration_confirmed::call(self.self_key_pair.address());
do_call(encoded)
.map_err(|e| e.to_string())
.and_then(|data| decoder.decode(&data).map_err(|e| e.to_string()))
.map_err(|err| { trace!(target: "secretstore", "Error {} reading migration confirmation from contract", err); err })
.ok(),
.ok()
},
_ => None,
};

View File

@@ -17,8 +17,9 @@
use std::sync::Arc;
use parking_lot::RwLock;
use ethabi::RawLog;
use ethcore::filter::Filter;
use ethabi::FunctionOutputDecoder;
use ethcore::client::{Client, BlockChainClient, BlockId, CallContract};
use ethcore::filter::Filter;
use ethkey::{Public, public_to_address};
use hash::keccak;
use bytes::Bytes;
@@ -29,7 +30,7 @@ use trusted_client::TrustedClient;
use helpers::{get_confirmed_block_hash, REQUEST_CONFIRMATIONS_REQUIRED};
use {ServerKeyId, NodeKeyPair, ContractAddress};
use_contract!(service, "Service", "res/service.json");
use_contract!(service, "res/service.json");
/// Name of the general SecretStore contract in the registry.
pub const SERVICE_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_service";
@@ -102,8 +103,6 @@ pub struct OnChainServiceContract {
/// Contract address source.
address_source: ContractAddress,
/// Contract.
contract: service::Service,
/// Contract.
data: RwLock<ServiceData>,
}
@@ -143,7 +142,6 @@ impl OnChainServiceContract {
self_key_pair: self_key_pair,
name: name,
address_source: address_source,
contract: service::Service::default(),
data: RwLock::new(ServiceData {
contract_address: None,
last_log_block: None,
@@ -156,8 +154,8 @@ impl OnChainServiceContract {
/// Send transaction to the service contract.
fn send_contract_transaction<C, P>(&self, tx_name: &str, origin: &Address, server_key_id: &ServerKeyId, is_response_required: C, prepare_tx: P) -> Result<(), String>
where C: FnOnce(&Client, &Address, &service::Service, &ServerKeyId, &Address) -> bool,
P: FnOnce(&Client, &Address, &service::Service) -> Result<Bytes, String> {
where C: FnOnce(&Client, &Address, &ServerKeyId, &Address) -> bool,
P: FnOnce(&Client, &Address) -> Result<Bytes, String> {
// only publish if contract address is set && client is online
let client = match self.client.get() {
Some(client) => client,
@@ -168,12 +166,12 @@ impl OnChainServiceContract {
// failing is ok here - it could be that enough confirmations have been recevied
// or key has been requested using HTTP API
let self_address = public_to_address(self.self_key_pair.public());
if !is_response_required(&*client, origin, &self.contract, server_key_id, &self_address) {
if !is_response_required(&*client, origin, server_key_id, &self_address) {
return Ok(());
}
// prepare transaction data
let transaction_data = prepare_tx(&*client, origin, &self.contract)?;
let transaction_data = prepare_tx(&*client, origin)?;
// send transaction
self.client.transact_contract(
@@ -189,18 +187,17 @@ impl OnChainServiceContract {
/// Create task-specific pending requests iterator.
fn create_pending_requests_iterator<
C: 'static + Fn(&Client, &Address, &service::Service, &BlockId) -> Result<U256, String>,
R: 'static + Fn(&NodeKeyPair, &Client, &Address, &service::Service, &BlockId, U256) -> Result<(bool, ServiceTask), String>
C: 'static + Fn(&Client, &Address, &BlockId) -> Result<U256, String>,
R: 'static + Fn(&NodeKeyPair, &Client, &Address, &BlockId, U256) -> Result<(bool, ServiceTask), String>
>(&self, client: Arc<Client>, contract_address: &Address, block: &BlockId, get_count: C, read_item: R) -> Box<Iterator<Item=(bool, ServiceTask)>> {
let contract = service::Service::default();
get_count(&*client, contract_address, &contract, block)
get_count(&*client, contract_address, block)
.map(|count| {
let client = client.clone();
let self_key_pair = self.self_key_pair.clone();
let contract_address = contract_address.clone();
let block = block.clone();
Box::new(PendingRequestsIterator {
read_request: move |index| read_item(&*self_key_pair, &*client, &contract_address, &contract, &block, index)
read_request: move |index| read_item(&*self_key_pair, &*client, &contract_address, &block, index)
.map_err(|error| {
warn!(target: "secretstore", "{}: reading pending request failed: {}",
self_key_pair.public(), error);
@@ -289,15 +286,15 @@ impl ServiceContract for OnChainServiceContract {
.filter_map(|log| {
let raw_log: RawLog = (log.entry.topics.into_iter().map(|t| t.0.into()).collect(), log.entry.data).into();
if raw_log.topics[0] == *SERVER_KEY_GENERATION_REQUESTED_EVENT_NAME_HASH {
ServerKeyGenerationService::parse_log(&address, &self.contract, raw_log)
ServerKeyGenerationService::parse_log(&address, raw_log)
} else if raw_log.topics[0] == *SERVER_KEY_RETRIEVAL_REQUESTED_EVENT_NAME_HASH {
ServerKeyRetrievalService::parse_log(&address, &self.contract, raw_log)
ServerKeyRetrievalService::parse_log(&address, raw_log)
} else if raw_log.topics[0] == *DOCUMENT_KEY_STORE_REQUESTED_EVENT_NAME_HASH {
DocumentKeyStoreService::parse_log(&address, &self.contract, raw_log)
DocumentKeyStoreService::parse_log(&address, raw_log)
} else if raw_log.topics[0] == *DOCUMENT_KEY_COMMON_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH {
DocumentKeyShadowRetrievalService::parse_common_request_log(&address, &self.contract, raw_log)
DocumentKeyShadowRetrievalService::parse_common_request_log(&address, raw_log)
} else if raw_log.topics[0] == *DOCUMENT_KEY_PERSONAL_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH {
DocumentKeyShadowRetrievalService::parse_personal_request_log(&address, &self.contract, raw_log)
DocumentKeyShadowRetrievalService::parse_personal_request_log(&address, raw_log)
} else {
Err("unknown type of log entry".into())
}
@@ -357,58 +354,58 @@ impl ServiceContract for OnChainServiceContract {
fn publish_generated_server_key(&self, origin: &Address, server_key_id: &ServerKeyId, server_key: Public) -> Result<(), String> {
self.send_contract_transaction("publish_generated_server_key", origin, server_key_id, ServerKeyGenerationService::is_response_required,
|_, _, service| Ok(ServerKeyGenerationService::prepare_pubish_tx_data(service, server_key_id, &server_key)))
|_, _| Ok(ServerKeyGenerationService::prepare_pubish_tx_data(server_key_id, &server_key)))
}
fn publish_server_key_generation_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> {
self.send_contract_transaction("publish_server_key_generation_error", origin, server_key_id, ServerKeyGenerationService::is_response_required,
|_, _, service| Ok(ServerKeyGenerationService::prepare_error_tx_data(service, server_key_id)))
|_, _| Ok(ServerKeyGenerationService::prepare_error_tx_data(server_key_id)))
}
fn publish_retrieved_server_key(&self, origin: &Address, server_key_id: &ServerKeyId, server_key: Public, threshold: usize) -> Result<(), String> {
let threshold = serialize_threshold(threshold)?;
self.send_contract_transaction("publish_retrieved_server_key", origin, server_key_id, ServerKeyRetrievalService::is_response_required,
|_, _, service| Ok(ServerKeyRetrievalService::prepare_pubish_tx_data(service, server_key_id, server_key, threshold)))
|_, _| Ok(ServerKeyRetrievalService::prepare_pubish_tx_data(server_key_id, server_key, threshold)))
}
fn publish_server_key_retrieval_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> {
self.send_contract_transaction("publish_server_key_retrieval_error", origin, server_key_id, ServerKeyRetrievalService::is_response_required,
|_, _, service| Ok(ServerKeyRetrievalService::prepare_error_tx_data(service, server_key_id)))
|_, _| Ok(ServerKeyRetrievalService::prepare_error_tx_data(server_key_id)))
}
fn publish_stored_document_key(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> {
self.send_contract_transaction("publish_stored_document_key", origin, server_key_id, DocumentKeyStoreService::is_response_required,
|_, _, service| Ok(DocumentKeyStoreService::prepare_pubish_tx_data(service, server_key_id)))
|_, _| Ok(DocumentKeyStoreService::prepare_pubish_tx_data(server_key_id)))
}
fn publish_document_key_store_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> {
self.send_contract_transaction("publish_document_key_store_error", origin, server_key_id, DocumentKeyStoreService::is_response_required,
|_, _, service| Ok(DocumentKeyStoreService::prepare_error_tx_data(service, server_key_id)))
|_, _| Ok(DocumentKeyStoreService::prepare_error_tx_data(server_key_id)))
}
fn publish_retrieved_document_key_common(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address, common_point: Public, threshold: usize) -> Result<(), String> {
let threshold = serialize_threshold(threshold)?;
self.send_contract_transaction("publish_retrieved_document_key_common", origin, server_key_id,
|client, contract_address, contract, server_key_id, key_server|
DocumentKeyShadowRetrievalService::is_response_required(client, contract_address, contract, server_key_id, requester, key_server),
|_, _, service|
Ok(DocumentKeyShadowRetrievalService::prepare_pubish_common_tx_data(service, server_key_id, requester, common_point, threshold))
|client, contract_address, server_key_id, key_server|
DocumentKeyShadowRetrievalService::is_response_required(client, contract_address, server_key_id, requester, key_server),
|_, _|
Ok(DocumentKeyShadowRetrievalService::prepare_pubish_common_tx_data(server_key_id, requester, common_point, threshold))
)
}
fn publish_retrieved_document_key_personal(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address, participants: &[Address], decrypted_secret: Public, shadow: Bytes) -> Result<(), String> {
self.send_contract_transaction("publish_retrieved_document_key_personal", origin, server_key_id, |_, _, _, _, _| true,
move |client, address, service|
DocumentKeyShadowRetrievalService::prepare_pubish_personal_tx_data(client, address, service, server_key_id, requester, participants, decrypted_secret, shadow)
self.send_contract_transaction("publish_retrieved_document_key_personal", origin, server_key_id, |_, _, _, _| true,
move |client, address|
DocumentKeyShadowRetrievalService::prepare_pubish_personal_tx_data(client, address, server_key_id, requester, participants, decrypted_secret, shadow)
)
}
fn publish_document_key_retrieval_error(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address) -> Result<(), String> {
self.send_contract_transaction("publish_document_key_retrieval_error", origin, server_key_id,
|client, contract_address, contract, server_key_id, key_server|
DocumentKeyShadowRetrievalService::is_response_required(client, contract_address, contract, server_key_id, requester, key_server),
|_, _, service|
Ok(DocumentKeyShadowRetrievalService::prepare_error_tx_data(service, server_key_id, requester))
|client, contract_address, server_key_id, key_server|
DocumentKeyShadowRetrievalService::is_response_required(client, contract_address, server_key_id, requester, key_server),
|_, _|
Ok(DocumentKeyShadowRetrievalService::prepare_error_tx_data(server_key_id, requester))
)
}
}
@@ -449,318 +446,280 @@ pub fn mask_topics(mask: &ApiMask) -> Vec<H256> {
impl ServerKeyGenerationService {
/// Parse request log entry.
pub fn parse_log(origin: &Address, contract: &service::Service, raw_log: RawLog) -> Result<ServiceTask, String> {
let event = contract.events().server_key_generation_requested();
match event.parse_log(raw_log) {
pub fn parse_log(origin: &Address, raw_log: RawLog) -> Result<ServiceTask, String> {
match service::events::server_key_generation_requested::parse_log(raw_log) {
Ok(l) => Ok(ServiceTask::GenerateServerKey(origin.clone(), l.server_key_id, l.author, parse_threshold(l.threshold)?)),
Err(e) => Err(format!("{}", e)),
}
}
/// Check if response from key server is required.
pub fn is_response_required(client: &Client, contract_address: &Address, contract: &service::Service, server_key_id: &ServerKeyId, key_server: &Address) -> bool {
pub fn is_response_required(client: &Client, contract_address: &Address, server_key_id: &ServerKeyId, key_server: &Address) -> bool {
// we're checking confirmation in Latest block, because we're interested in latest contract state here
let do_call = |data| client.call_contract(BlockId::Latest, *contract_address, data);
contract.functions()
.is_server_key_generation_response_required()
.call(*server_key_id, key_server.clone(), &do_call)
.unwrap_or(true)
let (encoded, decoder) = service::functions::is_server_key_generation_response_required::call(*server_key_id, *key_server);
match client.call_contract(BlockId::Latest, *contract_address, encoded) {
Err(_) => true,
Ok(data) => decoder.decode(&data).unwrap_or(true)
}
}
/// Prepare publish key transaction data.
pub fn prepare_pubish_tx_data(contract: &service::Service, server_key_id: &ServerKeyId, server_key_public: &Public) -> Bytes {
contract.functions()
.server_key_generated()
.input(*server_key_id, server_key_public.to_vec())
pub fn prepare_pubish_tx_data(server_key_id: &ServerKeyId, server_key_public: &Public) -> Bytes {
service::functions::server_key_generated::encode_input(*server_key_id, server_key_public.to_vec())
}
/// Prepare error transaction data.
pub fn prepare_error_tx_data(contract: &service::Service, server_key_id: &ServerKeyId) -> Bytes {
contract.functions()
.server_key_generation_error()
.input(*server_key_id)
pub fn prepare_error_tx_data(server_key_id: &ServerKeyId) -> Bytes {
service::functions::server_key_generation_error::encode_input(*server_key_id)
}
/// Read pending requests count.
fn read_pending_requests_count(client: &Client, contract_address: &Address, _contract: &service::Service, block: &BlockId) -> Result<U256, String> {
let do_call = |data| client.call_contract(block.clone(), contract_address.clone(), data);
let contract = service::Service::default();
contract.functions()
.server_key_generation_requests_count()
.call(&do_call)
.map_err(|error| format!("{}", error))
fn read_pending_requests_count(client: &Client, contract_address: &Address, block: &BlockId) -> Result<U256, String> {
let (encoded, decoder) = service::functions::server_key_generation_requests_count::call();
decoder.decode(&client.call_contract(*block, *contract_address, encoded)?)
.map_err(|e| e.to_string())
}
/// Read pending request.
fn read_pending_request(self_key_pair: &NodeKeyPair, client: &Client, contract_address: &Address, contract: &service::Service, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> {
fn read_pending_request(self_key_pair: &NodeKeyPair, client: &Client, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> {
let self_address = public_to_address(self_key_pair.public());
let do_call = |d| client.call_contract(block.clone(), contract_address.clone(), d);
contract.functions()
.get_server_key_generation_request()
.call(index, &do_call)
.map_err(|error| format!("{}", error))
.and_then(|(server_key_id, author, threshold)| parse_threshold(threshold)
.map(|threshold| (server_key_id, author, threshold)))
.and_then(|(server_key_id, author, threshold)| contract.functions()
.is_server_key_generation_response_required()
.call(server_key_id.clone(), self_address, &do_call)
.map(|not_confirmed| (
not_confirmed,
ServiceTask::GenerateServerKey(
contract_address.clone(),
server_key_id,
author,
threshold,
)))
.map_err(|error| format!("{}", error)))
let (encoded, decoder) = service::functions::get_server_key_generation_request::call(index);
let (server_key_id, author, threshold) = decoder.decode(&client.call_contract(*block, *contract_address, encoded)?)
.map_err(|e| e.to_string())?;
let threshold = parse_threshold(threshold)?;
let (encoded, decoder) = service::functions::is_server_key_generation_response_required::call(server_key_id, self_address);
let not_confirmed = decoder.decode(&client.call_contract(*block, *contract_address, encoded)?)
.map_err(|e| e.to_string())?;
let task = ServiceTask::GenerateServerKey(
contract_address.clone(),
server_key_id,
author,
threshold,
);
Ok((not_confirmed, task))
}
}
impl ServerKeyRetrievalService {
/// Parse request log entry.
pub fn parse_log(origin: &Address, contract: &service::Service, raw_log: RawLog) -> Result<ServiceTask, String> {
let event = contract.events().server_key_retrieval_requested();
match event.parse_log(raw_log) {
Ok(l) => Ok(ServiceTask::RetrieveServerKey(origin.clone(), l.server_key_id)),
Err(e) => Err(format!("{}", e)),
pub fn parse_log(origin: &Address, raw_log: RawLog) -> Result<ServiceTask, String> {
match service::events::server_key_retrieval_requested::parse_log(raw_log) {
Ok(l) => Ok(ServiceTask::RetrieveServerKey(*origin, l.server_key_id)),
Err(e) => Err(e.to_string())
}
}
/// Check if response from key server is required.
pub fn is_response_required(client: &Client, contract_address: &Address, contract: &service::Service, server_key_id: &ServerKeyId, key_server: &Address) -> bool {
pub fn is_response_required(client: &Client, contract_address: &Address, server_key_id: &ServerKeyId, key_server: &Address) -> bool {
// we're checking confirmation in Latest block, because we're interested in latest contract state here
let do_call = |data| client.call_contract(BlockId::Latest, *contract_address, data);
contract.functions()
.is_server_key_retrieval_response_required()
.call(*server_key_id, key_server.clone(), &do_call)
.unwrap_or(true)
let (encoded, decoder) = service::functions::is_server_key_retrieval_response_required::call(*server_key_id, *key_server);
match client.call_contract(BlockId::Latest, *contract_address, encoded) {
Err(_) => true,
Ok(data) => decoder.decode(&data).unwrap_or(true)
}
}
/// Prepare publish key transaction data.
pub fn prepare_pubish_tx_data(contract: &service::Service, server_key_id: &ServerKeyId, server_key_public: Public, threshold: U256) -> Bytes {
contract.functions()
.server_key_retrieved()
.input(*server_key_id, server_key_public.to_vec(), threshold)
pub fn prepare_pubish_tx_data(server_key_id: &ServerKeyId, server_key_public: Public, threshold: U256) -> Bytes {
service::functions::server_key_retrieved::encode_input(*server_key_id, server_key_public.to_vec(), threshold)
}
/// Prepare error transaction data.
pub fn prepare_error_tx_data(contract: &service::Service, server_key_id: &ServerKeyId) -> Bytes {
contract.functions()
.server_key_retrieval_error()
.input(*server_key_id)
pub fn prepare_error_tx_data(server_key_id: &ServerKeyId) -> Bytes {
service::functions::server_key_retrieval_error::encode_input(*server_key_id)
}
/// Read pending requests count.
fn read_pending_requests_count(client: &Client, contract_address: &Address, _contract: &service::Service, block: &BlockId) -> Result<U256, String> {
let do_call = |data| client.call_contract(block.clone(), contract_address.clone(), data);
let contract = service::Service::default();
contract.functions()
.server_key_retrieval_requests_count()
.call(&do_call)
.map_err(|error| format!("{}", error))
fn read_pending_requests_count(client: &Client, contract_address: &Address, block: &BlockId) -> Result<U256, String> {
let (encoded, decoder) = service::functions::server_key_retrieval_requests_count::call();
decoder.decode(&client.call_contract(*block, *contract_address, encoded)?)
.map_err(|e| e.to_string())
}
/// Read pending request.
fn read_pending_request(self_key_pair: &NodeKeyPair, client: &Client, contract_address: &Address, contract: &service::Service, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> {
fn read_pending_request(self_key_pair: &NodeKeyPair, client: &Client, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> {
let self_address = public_to_address(self_key_pair.public());
let do_call = |d| client.call_contract(block.clone(), contract_address.clone(), d);
contract.functions()
.get_server_key_retrieval_request()
.call(index, &do_call)
.map_err(|error| format!("{}", error))
.and_then(|server_key_id| contract.functions()
.is_server_key_retrieval_response_required()
.call(server_key_id.clone(), self_address, &do_call)
.map(|not_confirmed| (
not_confirmed,
ServiceTask::RetrieveServerKey(
contract_address.clone(),
server_key_id,
)))
.map_err(|error| format!("{}", error)))
let (encoded, decoder) = service::functions::get_server_key_retrieval_request::call(index);
let server_key_id = decoder.decode(&client.call_contract(*block, *contract_address, encoded)?)
.map_err(|e| e.to_string())?;
let (encoded, decoder) = service::functions::is_server_key_retrieval_response_required::call(server_key_id, self_address);
let not_confirmed = decoder.decode(&client.call_contract(*block, *contract_address, encoded)?)
.map_err(|e| e.to_string())?;
let task = ServiceTask::RetrieveServerKey(
*contract_address,
server_key_id,
);
Ok((not_confirmed, task))
}
}
impl DocumentKeyStoreService {
/// Parse request log entry.
pub fn parse_log(origin: &Address, contract: &service::Service, raw_log: RawLog) -> Result<ServiceTask, String> {
let event = contract.events().document_key_store_requested();
match event.parse_log(raw_log) {
pub fn parse_log(origin: &Address, raw_log: RawLog) -> Result<ServiceTask, String> {
match service::events::document_key_store_requested::parse_log(raw_log) {
Ok(l) => Ok(ServiceTask::StoreDocumentKey(origin.clone(), l.server_key_id, l.author, (*l.common_point).into(), (*l.encrypted_point).into())),
Err(e) => Err(format!("{}", e)),
}
}
/// Check if response from key server is required.
pub fn is_response_required(client: &Client, contract_address: &Address, contract: &service::Service, server_key_id: &ServerKeyId, key_server: &Address) -> bool {
pub fn is_response_required(client: &Client, contract_address: &Address, server_key_id: &ServerKeyId, key_server: &Address) -> bool {
// we're checking confirmation in Latest block, because we're interested in latest contract state here
let do_call = |data| client.call_contract(BlockId::Latest, *contract_address, data);
contract.functions()
.is_document_key_store_response_required()
.call(*server_key_id, key_server.clone(), &do_call)
.unwrap_or(true)
let (encoded, decoder) = service::functions::is_document_key_store_response_required::call(*server_key_id, *key_server);
match client.call_contract(BlockId::Latest, *contract_address, encoded) {
Err(_) => true,
Ok(data) => decoder.decode(&data).unwrap_or(true)
}
}
/// Prepare publish key transaction data.
pub fn prepare_pubish_tx_data(contract: &service::Service, server_key_id: &ServerKeyId) -> Bytes {
contract.functions()
.document_key_stored()
.input(*server_key_id)
pub fn prepare_pubish_tx_data(server_key_id: &ServerKeyId) -> Bytes {
service::functions::document_key_stored::encode_input(*server_key_id)
}
/// Prepare error transaction data.
pub fn prepare_error_tx_data(contract: &service::Service, server_key_id: &ServerKeyId) -> Bytes {
contract.functions()
.document_key_store_error()
.input(*server_key_id)
pub fn prepare_error_tx_data(server_key_id: &ServerKeyId) -> Bytes {
service::functions::document_key_store_error::encode_input(*server_key_id)
}
/// Read pending requests count.
fn read_pending_requests_count(client: &Client, contract_address: &Address, _contract: &service::Service, block: &BlockId) -> Result<U256, String> {
let do_call = |data| client.call_contract(block.clone(), contract_address.clone(), data);
let contract = service::Service::default();
contract.functions()
.document_key_store_requests_count()
.call(&do_call)
.map_err(|error| format!("{}", error))
fn read_pending_requests_count(client: &Client, contract_address: &Address, block: &BlockId) -> Result<U256, String> {
let (encoded, decoder) = service::functions::document_key_store_requests_count::call();
decoder.decode(&client.call_contract(*block, *contract_address, encoded)?)
.map_err(|e| e.to_string())
}
/// Read pending request.
fn read_pending_request(self_key_pair: &NodeKeyPair, client: &Client, contract_address: &Address, contract: &service::Service, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> {
fn read_pending_request(self_key_pair: &NodeKeyPair, client: &Client, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> {
let self_address = public_to_address(self_key_pair.public());
let do_call = |d| client.call_contract(block.clone(), contract_address.clone(), d);
contract.functions()
.get_document_key_store_request()
.call(index, &do_call)
.map_err(|error| format!("{}", error))
.and_then(|(server_key_id, author, common_point, encrypted_point)| contract.functions()
.is_document_key_store_response_required()
.call(server_key_id.clone(), self_address, &do_call)
.map(|not_confirmed| (
not_confirmed,
ServiceTask::StoreDocumentKey(
contract_address.clone(),
server_key_id,
author,
Public::from_slice(&common_point),
Public::from_slice(&encrypted_point),
)))
.map_err(|error| format!("{}", error)))
let (encoded, decoder) = service::functions::get_document_key_store_request::call(index);
let (server_key_id, author, common_point, encrypted_point) = decoder.decode(&client.call_contract(*block, *contract_address, encoded)?)
.map_err(|e| e.to_string())?;
let (encoded, decoder) = service::functions::is_document_key_store_response_required::call(server_key_id, self_address);
let not_confirmed = decoder.decode(&client.call_contract(*block, *contract_address, encoded)?)
.map_err(|e| e.to_string())?;
let task = ServiceTask::StoreDocumentKey(
*contract_address,
server_key_id,
author,
Public::from_slice(&common_point),
Public::from_slice(&encrypted_point),
);
Ok((not_confirmed, task))
}
}
impl DocumentKeyShadowRetrievalService {
/// Parse common request log entry.
pub fn parse_common_request_log(origin: &Address, contract: &service::Service, raw_log: RawLog) -> Result<ServiceTask, String> {
let event = contract.events().document_key_common_retrieval_requested();
match event.parse_log(raw_log) {
pub fn parse_common_request_log(origin: &Address, raw_log: RawLog) -> Result<ServiceTask, String> {
match service::events::document_key_common_retrieval_requested::parse_log(raw_log) {
Ok(l) => Ok(ServiceTask::RetrieveShadowDocumentKeyCommon(origin.clone(), l.server_key_id, l.requester)),
Err(e) => Err(format!("{}", e)),
Err(e) => Err(e.to_string())
}
}
/// Parse personal request log entry.
pub fn parse_personal_request_log(origin: &Address, contract: &service::Service, raw_log: RawLog) -> Result<ServiceTask, String> {
let event = contract.events().document_key_personal_retrieval_requested();
match event.parse_log(raw_log) {
pub fn parse_personal_request_log(origin: &Address, raw_log: RawLog) -> Result<ServiceTask, String> {
match service::events::document_key_personal_retrieval_requested::parse_log(raw_log) {
Ok(l) => Ok(ServiceTask::RetrieveShadowDocumentKeyPersonal(origin.clone(), l.server_key_id, (*l.requester_public).into())),
Err(e) => Err(format!("{}", e)),
Err(e) => Err(e.to_string())
}
}
/// Check if response from key server is required.
pub fn is_response_required(client: &Client, contract_address: &Address, contract: &service::Service, server_key_id: &ServerKeyId, requester: &Address, key_server: &Address) -> bool {
pub fn is_response_required(client: &Client, contract_address: &Address, server_key_id: &ServerKeyId, requester: &Address, key_server: &Address) -> bool {
// we're checking confirmation in Latest block, because we're interested in latest contract state here
let do_call = |data| client.call_contract(BlockId::Latest, *contract_address, data);
contract.functions()
.is_document_key_shadow_retrieval_response_required()
.call(*server_key_id, *requester, key_server.clone(), &do_call)
.unwrap_or(true)
let (encoded, decoder) = service::functions::is_document_key_shadow_retrieval_response_required::call(*server_key_id, *requester, *key_server);
match client.call_contract(BlockId::Latest, *contract_address, encoded) {
Err(_) => true,
Ok(data) => decoder.decode(&data).unwrap_or(true)
}
}
/// Prepare publish common key transaction data.
pub fn prepare_pubish_common_tx_data(contract: &service::Service, server_key_id: &ServerKeyId, requester: &Address, common_point: Public, threshold: U256) -> Bytes {
contract.functions()
.document_key_common_retrieved()
.input(*server_key_id, *requester, common_point.to_vec(), threshold)
pub fn prepare_pubish_common_tx_data(server_key_id: &ServerKeyId, requester: &Address, common_point: Public, threshold: U256) -> Bytes {
service::functions::document_key_common_retrieved::encode_input(*server_key_id, *requester, common_point.to_vec(), threshold)
}
/// Prepare publish personal key transaction data.
pub fn prepare_pubish_personal_tx_data(client: &Client, contract_address: &Address, contract: &service::Service, server_key_id: &ServerKeyId, requester: &Address, participants: &[Address], decrypted_secret: Public, shadow: Bytes) -> Result<Bytes, String> {
pub fn prepare_pubish_personal_tx_data(client: &Client, contract_address: &Address, server_key_id: &ServerKeyId, requester: &Address, participants: &[Address], decrypted_secret: Public, shadow: Bytes) -> Result<Bytes, String> {
let mut participants_mask = U256::default();
for participant in participants {
let participant_index = Self::map_key_server_address(client, contract_address, contract, participant.clone())
let participant_index = Self::map_key_server_address(client, contract_address, participant.clone())
.map_err(|e| format!("Error searching for {} participant: {}", participant, e))?;
participants_mask = participants_mask | (U256::one() << participant_index);
}
Ok(contract.functions()
.document_key_personal_retrieved()
.input(*server_key_id, *requester, participants_mask, decrypted_secret.to_vec(), shadow))
Ok(service::functions::document_key_personal_retrieved::encode_input(
*server_key_id, *requester, participants_mask, decrypted_secret.to_vec(), shadow
))
}
/// Prepare error transaction data.
pub fn prepare_error_tx_data(contract: &service::Service, server_key_id: &ServerKeyId, requester: &Address) -> Bytes {
contract.functions()
.document_key_shadow_retrieval_error()
.input(*server_key_id, *requester)
pub fn prepare_error_tx_data(server_key_id: &ServerKeyId, requester: &Address) -> Bytes {
service::functions::document_key_shadow_retrieval_error::encode_input(*server_key_id, *requester)
}
/// Read pending requests count.
fn read_pending_requests_count(client: &Client, contract_address: &Address, _contract: &service::Service, block: &BlockId) -> Result<U256, String> {
let do_call = |data| client.call_contract(block.clone(), contract_address.clone(), data);
let contract = service::Service::default();
contract.functions()
.document_key_shadow_retrieval_requests_count()
.call(&do_call)
.map_err(|error| format!("{}", error))
fn read_pending_requests_count(client: &Client, contract_address: &Address, block: &BlockId) -> Result<U256, String> {
let (encoded, decoder) = service::functions::document_key_shadow_retrieval_requests_count::call();
decoder.decode(&client.call_contract(*block, *contract_address, encoded)?)
.map_err(|e| e.to_string())
}
/// Read pending request.
fn read_pending_request(self_key_pair: &NodeKeyPair, client: &Client, contract_address: &Address, contract: &service::Service, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> {
fn read_pending_request(self_key_pair: &NodeKeyPair, client: &Client, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> {
let self_address = public_to_address(self_key_pair.public());
let do_call = |d| client.call_contract(block.clone(), contract_address.clone(), d);
contract.functions()
.get_document_key_shadow_retrieval_request()
.call(index, &do_call)
.map_err(|error| format!("{}", error))
.and_then(|(server_key_id, requester, is_common_retrieval_completed)| {
let requester = Public::from_slice(&requester);
contract.functions()
.is_document_key_shadow_retrieval_response_required()
.call(server_key_id.clone(), public_to_address(&requester), self_address, &do_call)
.map(|not_confirmed| (
not_confirmed,
match is_common_retrieval_completed {
true => ServiceTask::RetrieveShadowDocumentKeyPersonal(
contract_address.clone(),
server_key_id,
requester,
),
false => ServiceTask::RetrieveShadowDocumentKeyCommon(
contract_address.clone(),
server_key_id,
public_to_address(&requester),
),
},
))
.map_err(|error| format!("{}", error))
})
let (encoded, decoder) = service::functions::get_document_key_shadow_retrieval_request::call(index);
let (server_key_id, requester, is_common_retrieval_completed) =
decoder.decode(&client.call_contract(*block, *contract_address, encoded)?)
.map_err(|e| e.to_string())?;
let requester = Public::from_slice(&requester);
let (encoded, decoder) = service::functions::is_document_key_shadow_retrieval_response_required::call(server_key_id, public_to_address(&requester), self_address);
let not_confirmed = decoder.decode(&client.call_contract(*block, *contract_address, encoded)?)
.map_err(|e| e.to_string())?;
let task = match is_common_retrieval_completed {
true => ServiceTask::RetrieveShadowDocumentKeyPersonal(
*contract_address,
server_key_id,
requester,
),
false => ServiceTask::RetrieveShadowDocumentKeyCommon(
*contract_address,
server_key_id,
public_to_address(&requester),
),
};
Ok((not_confirmed, task))
}
/// Map from key server address to key server index.
fn map_key_server_address(client: &Client, contract_address: &Address, contract: &service::Service, key_server: Address) -> Result<u8, String> {
fn map_key_server_address(client: &Client, contract_address: &Address, key_server: Address) -> Result<u8, String> {
// we're checking confirmation in Latest block, because tx ,ust be appended to the latest state
let do_call = |data| client.call_contract(BlockId::Latest, *contract_address, data);
contract.functions()
.require_key_server()
.call(key_server, &do_call)
.map_err(|e| format!("{}", e))
.and_then(|index| if index > ::std::u8::MAX.into() {
Err(format!("key server index is too big: {}", index))
} else {
let index: u32 = index.into();
Ok(index as u8)
})
let (encoded, decoder) = service::functions::require_key_server::call(key_server);
let index = decoder.decode(&client.call_contract(BlockId::Latest, *contract_address, encoded)?)
.map_err(|e| e.to_string())?;
if index > u8::max_value().into() {
Err(format!("key server index is too big: {}", index))
} else {
let index: u32 = index.into();
Ok(index as u8)
}
}
}

View File

@@ -9,9 +9,9 @@ authors = ["Parity Technologies <admin@parity.io>"]
keccak-hash = "0.1"
lazy_static = "1.0"
log = "0.4"
ethabi = "5.1.2"
ethabi-derive = "5.1.3"
ethabi-contract = "5.1.1"
ethabi = "6.0"
ethabi-derive = "6.0"
ethabi-contract = "6.0"
target_info = "0.1"
semver = "0.9"
ethcore = { path = "../ethcore" }

View File

@@ -37,8 +37,9 @@ use sync::{SyncProvider};
use types::{ReleaseInfo, OperationsInfo, CapState, VersionInfo, ReleaseTrack};
use version;
use semver::Version;
use ethabi::FunctionOutputDecoder;
use_contract!(operations_contract, "Operations", "res/operations.json");
use_contract!(operations, "res/operations.json");
/// Filter for releases.
#[derive(Debug, Eq, PartialEq, Clone)]
@@ -192,41 +193,35 @@ pub trait OperationsClient: Send + Sync + 'static {
/// `OperationsClient` that delegates calls to the operations contract.
pub struct OperationsContractClient {
operations_contract: operations_contract::Operations,
client: Weak<BlockChainClient>,
}
impl OperationsContractClient {
fn new(
operations_contract: operations_contract::Operations,
client: Weak<BlockChainClient>,
) -> OperationsContractClient {
OperationsContractClient { operations_contract, client }
fn new(client: Weak<BlockChainClient>) -> Self {
OperationsContractClient {
client
}
}
/// Get the hash of the latest release for the given track
fn latest_hash<F>(&self, track: ReleaseTrack, do_call: &F) -> Result<H256, String>
where F: Fn(Vec<u8>) -> Result<Vec<u8>, String> {
self.operations_contract.functions()
.latest_in_track()
.call(*CLIENT_ID_HASH, u8::from(track), do_call)
.map_err(|e| format!("{:?}", e))
let (data, decoder) = operations::functions::latest_in_track::call(*CLIENT_ID_HASH, u8::from(track));
let value = do_call(data)?;
decoder.decode(&value).map_err(|e| e.to_string())
}
/// Get release info for the given release
fn release_info<F>(&self, release_id: H256, do_call: &F) -> Result<ReleaseInfo, String>
where F: Fn(Vec<u8>) -> Result<Vec<u8>, String> {
let (fork, track, semver, is_critical) = self.operations_contract.functions()
.release()
.call(*CLIENT_ID_HASH, release_id, &do_call)
.map_err(|e| format!("{:?}", e))?;
let (data, decoder) = operations::functions::release::call(*CLIENT_ID_HASH, release_id);
let (fork, track, semver, is_critical) = decoder.decode(&do_call(data)?).map_err(|e| e.to_string())?;
let (fork, track, semver) = (fork.low_u64(), track.low_u32(), semver.low_u32());
let latest_binary = self.operations_contract.functions()
.checksum()
.call(*CLIENT_ID_HASH, release_id, *PLATFORM_ID_HASH, &do_call)
.map_err(|e| format!("{:?}", e))?;
let (data, decoder) = operations::functions::checksum::call(*CLIENT_ID_HASH, release_id, *PLATFORM_ID_HASH);
let latest_binary = decoder.decode(&do_call(data)?).map_err(|e| e.to_string())?;
Ok(ReleaseInfo {
version: VersionInfo::from_raw(semver, track as u8, release_id.into()),
@@ -250,9 +245,9 @@ impl OperationsClient for OperationsContractClient {
trace!(target: "updater", "Looking up this_fork for our release: {}/{:?}", CLIENT_ID, this.hash);
// get the fork number of this release
let this_fork = self.operations_contract.functions()
.release()
.call(*CLIENT_ID_HASH, this.hash, &do_call)
let (data, decoder) = operations::functions::release::call(*CLIENT_ID_HASH, this.hash);
let this_fork = do_call(data)
.and_then(|value| decoder.decode(&value).map_err(|e| e.to_string()))
.ok()
.and_then(|(fork, track, _, _)| {
let this_track: ReleaseTrack = (track.low_u64() as u8).into();
@@ -282,10 +277,10 @@ impl OperationsClient for OperationsContractClient {
in_minor = Some(self.release_info(latest_in_track, &do_call)?);
}
let fork = self.operations_contract.functions()
.latest_fork()
.call(&do_call)
.map_err(|e| format!("{:?}", e))?.low_u64();
let (data, decoder) = operations::functions::latest_fork::call();
let fork = do_call(data)
.and_then(|value| decoder.decode(&value).map_err(|e| e.to_string()))?
.low_u64();
Ok(OperationsInfo {
fork,
@@ -299,9 +294,7 @@ impl OperationsClient for OperationsContractClient {
let client = self.client.upgrade()?;
let address = client.registry_address("operations".into(), BlockId::Latest)?;
let event = self.operations_contract.events().release_added();
let topics = event.create_filter(Some(*CLIENT_ID_HASH), Some(release.fork.into()), Some(release.is_critical));
let topics = operations::events::release_added::filter(Some(*CLIENT_ID_HASH), Some(release.fork.into()), Some(release.is_critical));
let topics = vec![topics.topic0, topics.topic1, topics.topic2, topics.topic3];
let topics = topics.into_iter().map(Into::into).map(Some).collect();
@@ -317,7 +310,7 @@ impl OperationsClient for OperationsContractClient {
.unwrap_or_default()
.iter()
.filter_map(|log| {
let event = event.parse_log((log.topics.clone(), log.data.clone()).into()).ok()?;
let event = operations::events::release_added::parse_log((log.topics.clone(), log.data.clone()).into()).ok()?;
let version_info = VersionInfo::from_raw(event.semver.low_u32(), event.track.low_u32() as u8, event.release.into());
if version_info == release.version {
Some(log.block_number)
@@ -375,7 +368,6 @@ impl Updater {
sync: Some(sync.clone()),
fetcher,
operations_client: OperationsContractClient::new(
operations_contract::Operations::default(),
client.clone()),
exit_handler: Mutex::new(None),
this: if cfg!(feature = "test-updater") {

View File

@@ -42,9 +42,9 @@ const PACKET_PONG: u8 = 2;
const PACKET_FIND_NODE: u8 = 3;
const PACKET_NEIGHBOURS: u8 = 4;
const PING_TIMEOUT: Duration = Duration::from_millis(300);
const PING_TIMEOUT: Duration = Duration::from_millis(500);
const FIND_NODE_TIMEOUT: Duration = Duration::from_secs(2);
const EXPIRY_TIME: Duration = Duration::from_secs(60);
const EXPIRY_TIME: Duration = Duration::from_secs(20);
const MAX_NODES_PING: usize = 32; // Max nodes to add/ping at once
const REQUEST_BACKOFF: [Duration; 4] = [
Duration::from_secs(1),
@@ -80,15 +80,29 @@ impl BucketEntry {
}
}
pub struct NodeBucket {
nodes: VecDeque<BucketEntry>, //sorted by last active
struct FindNodeRequest {
// Time when the request was sent
sent_at: Instant,
// Number of items sent by the node
response_count: usize,
// Whether the request have been answered yet
answered: bool,
}
struct PendingRequest {
packet_id: u8,
struct PingRequest {
// Time when the request was sent
sent_at: Instant,
packet_hash: H256,
response_count: usize, // Some requests (eg. FIND_NODE) have multi-packet responses
// The node to which the request was sent
node: NodeEntry,
// The hash sent in the Ping request
echo_hash: H256,
// The hash Parity used to respond with (until rev 01f825b0e1f1c4c420197b51fc801cbe89284b29)
#[deprecated()]
deprecated_echo_hash: H256,
}
pub struct NodeBucket {
nodes: VecDeque<BucketEntry>, //sorted by last active
}
impl Default for NodeBucket {
@@ -115,13 +129,13 @@ pub struct Discovery<'a> {
id_hash: H256,
secret: Secret,
public_endpoint: NodeEndpoint,
discovery_round: u16,
discovery_initiated: bool,
discovery_round: Option<u16>,
discovery_id: NodeId,
discovery_nodes: HashSet<NodeId>,
node_buckets: Vec<NodeBucket>,
in_flight_requests: HashMap<NodeId, PendingRequest>,
expiring_pings: VecDeque<(NodeId, Instant)>,
expiring_finds: VecDeque<(NodeId, Instant)>,
in_flight_pings: HashMap<NodeId, PingRequest>,
in_flight_find_nodes: HashMap<NodeId, FindNodeRequest>,
send_queue: VecDeque<Datagram>,
check_timestamps: bool,
adding_nodes: Vec<NodeEntry>,
@@ -141,13 +155,13 @@ impl<'a> Discovery<'a> {
id_hash: keccak(key.public()),
secret: key.secret().clone(),
public_endpoint: public,
discovery_round: 0,
discovery_initiated: false,
discovery_round: None,
discovery_id: NodeId::new(),
discovery_nodes: HashSet::new(),
node_buckets: (0..ADDRESS_BITS).map(|_| NodeBucket::new()).collect(),
in_flight_requests: HashMap::new(),
expiring_pings: VecDeque::new(),
expiring_finds: VecDeque::new(),
in_flight_pings: HashMap::new(),
in_flight_find_nodes: HashMap::new(),
send_queue: VecDeque::new(),
check_timestamps: true,
adding_nodes: Vec::new(),
@@ -175,15 +189,6 @@ impl<'a> Discovery<'a> {
}
}
/// Add a list of known nodes to the table.
pub fn init_node_list(&mut self, nodes: Vec<NodeEntry>) {
for n in nodes {
if self.is_allowed(&n) {
self.update_node(n);
}
}
}
fn update_node(&mut self, e: NodeEntry) -> Option<TableUpdates> {
trace!(target: "discovery", "Inserting {:?}", &e);
let id_hash = keccak(e.id);
@@ -224,13 +229,20 @@ impl<'a> Discovery<'a> {
/// Starts the discovery process at round 0
fn start(&mut self) {
trace!(target: "discovery", "Starting discovery");
self.discovery_round = 0;
self.discovery_round = Some(0);
self.discovery_id.randomize(); //TODO: use cryptographic nonce
self.discovery_nodes.clear();
}
/// Complete the discovery process
fn stop(&mut self) {
trace!(target: "discovery", "Completing discovery");
self.discovery_round = None;
self.discovery_nodes.clear();
}
fn update_new_nodes(&mut self) {
while self.in_flight_requests.len() < MAX_NODES_PING {
while self.in_flight_pings.len() < MAX_NODES_PING {
match self.adding_nodes.pop() {
Some(next) => self.try_ping(next),
None => break,
@@ -239,8 +251,12 @@ impl<'a> Discovery<'a> {
}
fn discover(&mut self) {
self.update_new_nodes();
if self.discovery_round == DISCOVERY_MAX_STEPS {
let discovery_round = match self.discovery_round {
Some(r) => r,
None => return,
};
if discovery_round == DISCOVERY_MAX_STEPS {
self.stop();
return;
}
trace!(target: "discovery", "Starting round {:?}", self.discovery_round);
@@ -263,12 +279,10 @@ impl<'a> Discovery<'a> {
}
if tried_count == 0 {
trace!(target: "discovery", "Completing discovery");
self.discovery_round = DISCOVERY_MAX_STEPS;
self.discovery_nodes.clear();
self.stop();
return;
}
self.discovery_round += 1;
self.discovery_round = Some(discovery_round + 1);
}
/// The base 2 log of the distance between a and b using the XOR metric.
@@ -285,14 +299,20 @@ impl<'a> Discovery<'a> {
}
fn try_ping(&mut self, node: NodeEntry) {
if !self.is_allowed(&node) ||
self.in_flight_requests.contains_key(&node.id) ||
self.adding_nodes.iter().any(|n| n.id == node.id)
{
if !self.is_allowed(&node) {
trace!(target: "discovery", "Node {:?} not allowed", node);
return;
}
if self.in_flight_pings.contains_key(&node.id) || self.in_flight_find_nodes.contains_key(&node.id) {
trace!(target: "discovery", "Node {:?} in flight requests", node);
return;
}
if self.adding_nodes.iter().any(|n| n.id == node.id) {
trace!(target: "discovery", "Node {:?} in adding nodes", node);
return;
}
if self.in_flight_requests.len() < MAX_NODES_PING {
if self.in_flight_pings.len() < MAX_NODES_PING {
self.ping(&node)
.unwrap_or_else(|e| {
warn!(target: "discovery", "Error sending Ping packet: {:?}", e);
@@ -308,18 +328,17 @@ impl<'a> Discovery<'a> {
self.public_endpoint.to_rlp_list(&mut rlp);
node.endpoint.to_rlp_list(&mut rlp);
append_expiration(&mut rlp);
let old_parity_hash = keccak(rlp.as_raw());
let hash = self.send_packet(PACKET_PING, &node.endpoint.udp_address(), &rlp.drain())?;
let request_info = PendingRequest {
packet_id: PACKET_PING,
self.in_flight_pings.insert(node.id, PingRequest {
sent_at: Instant::now(),
packet_hash: hash,
response_count: 0,
};
self.expiring_pings.push_back((node.id, request_info.sent_at));
self.in_flight_requests.insert(node.id, request_info);
node: node.clone(),
echo_hash: hash,
deprecated_echo_hash: old_parity_hash,
});
trace!(target: "discovery", "Sent Ping to {:?}", &node.endpoint);
trace!(target: "discovery", "Sent Ping to {:?} ; node_id={:#x}", &node.endpoint, node.id);
Ok(())
}
@@ -327,16 +346,13 @@ impl<'a> Discovery<'a> {
let mut rlp = RlpStream::new_list(2);
rlp.append(target);
append_expiration(&mut rlp);
let hash = self.send_packet(PACKET_FIND_NODE, &node.endpoint.udp_address(), &rlp.drain())?;
self.send_packet(PACKET_FIND_NODE, &node.endpoint.udp_address(), &rlp.drain())?;
let request_info = PendingRequest {
packet_id: PACKET_FIND_NODE,
self.in_flight_find_nodes.insert(node.id, FindNodeRequest {
sent_at: Instant::now(),
packet_hash: hash,
response_count: 0,
};
self.expiring_finds.push_back((node.id, request_info.sent_at));
self.in_flight_requests.insert(node.id, request_info);
answered: false,
});
trace!(target: "discovery", "Sent FindNode to {:?}", &node.endpoint);
Ok(())
@@ -448,20 +464,31 @@ impl<'a> Discovery<'a> {
entry.endpoint.is_allowed(&self.ip_filter) && entry.id != self.id
}
fn on_ping(&mut self, rlp: &Rlp, node: &NodeId, from: &SocketAddr, echo_hash: &[u8]) -> Result<Option<TableUpdates>, Error> {
fn on_ping(&mut self, rlp: &Rlp, node_id: &NodeId, from: &SocketAddr, echo_hash: &[u8]) -> Result<Option<TableUpdates>, Error> {
trace!(target: "discovery", "Got Ping from {:?}", &from);
let source = NodeEndpoint::from_rlp(&rlp.at(1)?)?;
let dest = NodeEndpoint::from_rlp(&rlp.at(2)?)?;
let ping_from = NodeEndpoint::from_rlp(&rlp.at(1)?)?;
let ping_to = NodeEndpoint::from_rlp(&rlp.at(2)?)?;
let timestamp: u64 = rlp.val_at(3)?;
self.check_timestamp(timestamp)?;
let mut response = RlpStream::new_list(3);
dest.to_rlp_list(&mut response);
let pong_to = NodeEndpoint {
address: from.clone(),
udp_port: ping_from.udp_port
};
// Here the PONG's `To` field should be the node we are
// sending the request to
// WARNING: this field _should not be used_, but old Parity versions
// use it in order to get the node's address.
// So this is a temporary fix so that older Parity versions don't brake completely.
ping_to.to_rlp_list(&mut response);
// pong_to.to_rlp_list(&mut response);
response.append(&echo_hash);
append_expiration(&mut response);
self.send_packet(PACKET_PONG, from, &response.drain())?;
let entry = NodeEntry { id: *node, endpoint: source.clone() };
let entry = NodeEntry { id: *node_id, endpoint: pong_to.clone() };
if !entry.endpoint.is_valid() {
debug!(target: "discovery", "Got bad address: {:?}", entry);
} else if !self.is_allowed(&entry) {
@@ -469,40 +496,45 @@ impl<'a> Discovery<'a> {
} else {
self.add_node(entry.clone());
}
Ok(None)
}
fn on_pong(&mut self, rlp: &Rlp, node_id: &NodeId, from: &SocketAddr) -> Result<Option<TableUpdates>, Error> {
trace!(target: "discovery", "Got Pong from {:?}", &from);
let dest = NodeEndpoint::from_rlp(&rlp.at(0)?)?;
trace!(target: "discovery", "Got Pong from {:?} ; node_id={:#x}", &from, node_id);
let _pong_to = NodeEndpoint::from_rlp(&rlp.at(0)?)?;
let echo_hash: H256 = rlp.val_at(1)?;
let timestamp: u64 = rlp.val_at(2)?;
self.check_timestamp(timestamp)?;
let mut node = NodeEntry { id: *node_id, endpoint: dest };
if !node.endpoint.is_valid() {
debug!(target: "discovery", "Bad address: {:?}", node);
node.endpoint.address = *from;
}
let is_expected = match self.in_flight_requests.entry(*node_id) {
let expected_node = match self.in_flight_pings.entry(*node_id) {
Entry::Occupied(entry) => {
let is_expected = {
let expected_node = {
let request = entry.get();
request.packet_id == PACKET_PING && request.packet_hash == echo_hash
if request.echo_hash != echo_hash && request.deprecated_echo_hash != echo_hash {
debug!(target: "discovery", "Got unexpected Pong from {:?} ; packet_hash={:#x} ; expected_hash={:#x}", &from, request.echo_hash, echo_hash);
None
} else {
if request.deprecated_echo_hash == echo_hash {
trace!(target: "discovery", "Got Pong from an old parity-ethereum version.");
}
Some(request.node.clone())
}
};
if is_expected {
if expected_node.is_some() {
entry.remove();
}
is_expected
expected_node
},
Entry::Vacant(_) => {
None
},
Entry::Vacant(_) => false
};
if is_expected {
if let Some(node) = expected_node {
Ok(self.update_node(node))
} else {
debug!(target: "discovery", "Got unexpected Pong from {:?}", &from);
debug!(target: "discovery", "Got unexpected Pong from {:?} ; request not found", &from);
Ok(None)
}
}
@@ -544,29 +576,32 @@ impl<'a> Discovery<'a> {
fn on_neighbours(&mut self, rlp: &Rlp, node_id: &NodeId, from: &SocketAddr) -> Result<Option<TableUpdates>, Error> {
let results_count = rlp.at(0)?.item_count()?;
let is_expected = match self.in_flight_requests.entry(*node_id) {
let is_expected = match self.in_flight_find_nodes.entry(*node_id) {
Entry::Occupied(mut entry) => {
let result = {
let expected = {
let request = entry.get_mut();
if request.packet_id == PACKET_FIND_NODE &&
request.response_count + results_count <= BUCKET_SIZE
{
// Mark the request as answered
request.answered = true;
if request.response_count + results_count <= BUCKET_SIZE {
request.response_count += results_count;
true
} else {
debug!(target: "discovery", "Got unexpected Neighbors from {:?} ; oversized packet ({} + {}) node_id={:#x}", &from, request.response_count, results_count, node_id);
false
}
};
if entry.get().response_count == BUCKET_SIZE {
entry.remove();
}
result
expected
}
Entry::Vacant(_) => false,
Entry::Vacant(_) => {
debug!(target: "discovery", "Got unexpected Neighbors from {:?} ; couldn't find node_id={:#x}", &from, node_id);
false
},
};
if !is_expected {
debug!(target: "discovery", "Got unexpected Neighbors from {:?}", &from);
return Ok(None);
}
@@ -591,65 +626,74 @@ impl<'a> Discovery<'a> {
Ok(None)
}
fn check_expired(&mut self, time: Instant) -> HashSet<NodeId> {
let mut removed: HashSet<NodeId> = HashSet::new();
while let Some((node_id, sent_at)) = self.expiring_pings.pop_front() {
if time.duration_since(sent_at) <= PING_TIMEOUT {
self.expiring_pings.push_front((node_id, sent_at));
break;
fn check_expired(&mut self, time: Instant) {
let mut nodes_to_expire = Vec::new();
self.in_flight_pings.retain(|node_id, ping_request| {
if time.duration_since(ping_request.sent_at) > PING_TIMEOUT {
debug!(target: "discovery", "Removing expired PING request for node_id={:#x}", node_id);
nodes_to_expire.push(*node_id);
false
} else {
true
}
self.expire_in_flight_request(node_id, sent_at, &mut removed);
}
while let Some((node_id, sent_at)) = self.expiring_finds.pop_front() {
if time.duration_since(sent_at) <= FIND_NODE_TIMEOUT {
self.expiring_finds.push_front((node_id, sent_at));
break;
}
self.expire_in_flight_request(node_id, sent_at, &mut removed);
}
removed
}
fn expire_in_flight_request(&mut self, node_id: NodeId, sent_at: Instant, removed: &mut HashSet<NodeId>) {
if let Entry::Occupied(entry) = self.in_flight_requests.entry(node_id) {
if entry.get().sent_at == sent_at {
entry.remove();
// Attempt to remove from bucket if in one.
let id_hash = keccak(&node_id);
let dist = Discovery::distance(&self.id_hash, &id_hash)
.expect("distance is None only if id hashes are equal; will never send request to self; qed");
let bucket = &mut self.node_buckets[dist];
if let Some(index) = bucket.nodes.iter().position(|n| n.id_hash == id_hash) {
if bucket.nodes[index].fail_count < self.request_backoff.len() {
let node = &mut bucket.nodes[index];
node.backoff_until = Instant::now() + self.request_backoff[node.fail_count];
node.fail_count += 1;
trace!(
target: "discovery",
"Requests to node {:?} timed out {} consecutive time(s)",
&node.address, node.fail_count
);
} else {
removed.insert(node_id);
let node = bucket.nodes.remove(index).expect("index was located in if condition");
debug!(target: "discovery", "Removed expired node {:?}", &node.address);
}
});
self.in_flight_find_nodes.retain(|node_id, find_node_request| {
if time.duration_since(find_node_request.sent_at) > FIND_NODE_TIMEOUT {
if !find_node_request.answered {
debug!(target: "discovery", "Removing expired FIND NODE request for node_id={:#x}", node_id);
nodes_to_expire.push(*node_id);
}
false
} else {
true
}
});
for node_id in nodes_to_expire {
self.expire_node_request(node_id);
}
}
fn expire_node_request(&mut self, node_id: NodeId) {
// Attempt to remove from bucket if in one.
let id_hash = keccak(&node_id);
let dist = Discovery::distance(&self.id_hash, &id_hash)
.expect("distance is None only if id hashes are equal; will never send request to self; qed");
let bucket = &mut self.node_buckets[dist];
if let Some(index) = bucket.nodes.iter().position(|n| n.id_hash == id_hash) {
if bucket.nodes[index].fail_count < self.request_backoff.len() {
let node = &mut bucket.nodes[index];
node.backoff_until = Instant::now() + self.request_backoff[node.fail_count];
node.fail_count += 1;
trace!(
target: "discovery",
"Requests to node {:?} timed out {} consecutive time(s)",
&node.address, node.fail_count
);
} else {
let node = bucket.nodes.remove(index).expect("index was located in if condition");
debug!(target: "discovery", "Removed expired node {:?}", &node.address);
}
}
}
pub fn round(&mut self) -> Option<TableUpdates> {
let removed = self.check_expired(Instant::now());
self.discover();
if !removed.is_empty() {
Some(TableUpdates { added: HashMap::new(), removed })
} else { None }
pub fn round(&mut self) {
self.check_expired(Instant::now());
self.update_new_nodes();
if self.discovery_round.is_some() {
self.discover();
// Start discovering if the first pings have been sent (or timed out)
} else if self.in_flight_pings.len() == 0 && !self.discovery_initiated {
self.discovery_initiated = true;
self.refresh();
}
}
pub fn refresh(&mut self) {
self.start();
if self.discovery_round.is_none() {
self.start();
}
}
pub fn any_sends_queued(&self) -> bool {
@@ -663,6 +707,16 @@ impl<'a> Discovery<'a> {
pub fn requeue_send(&mut self, datagram: Datagram) {
self.send_queue.push_front(datagram)
}
/// Add a list of known nodes to the table.
#[cfg(test)]
pub fn init_node_list(&mut self, nodes: Vec<NodeEntry>) {
for n in nodes {
if self.is_allowed(&n) {
self.update_node(n);
}
}
}
}
fn append_expiration(rlp: &mut RlpStream) {
@@ -738,13 +792,13 @@ mod tests {
for i in 1..(MAX_NODES_PING+1) {
discovery.add_node(NodeEntry { id: NodeId::random(), endpoint: ep.clone() });
assert_eq!(discovery.in_flight_requests.len(), i);
assert_eq!(discovery.in_flight_pings.len(), i);
assert_eq!(discovery.send_queue.len(), i);
assert_eq!(discovery.adding_nodes.len(), 0);
}
for i in 1..20 {
discovery.add_node(NodeEntry { id: NodeId::random(), endpoint: ep.clone() });
assert_eq!(discovery.in_flight_requests.len(), MAX_NODES_PING);
assert_eq!(discovery.in_flight_pings.len(), MAX_NODES_PING);
assert_eq!(discovery.send_queue.len(), MAX_NODES_PING);
assert_eq!(discovery.adding_nodes.len(), i);
}
@@ -821,23 +875,29 @@ mod tests {
assert_eq!(total_bucket_nodes(&discovery.node_buckets), 1200);
// Requests have not expired yet.
let removed = discovery.check_expired(Instant::now()).len();
let num_nodes = total_bucket_nodes(&discovery.node_buckets);
discovery.check_expired(Instant::now());
let removed = num_nodes - total_bucket_nodes(&discovery.node_buckets);
assert_eq!(removed, 0);
// Expiring pings to bucket nodes removes them from bucket.
let removed = discovery.check_expired(Instant::now() + PING_TIMEOUT).len();
let num_nodes = total_bucket_nodes(&discovery.node_buckets);
discovery.check_expired(Instant::now() + PING_TIMEOUT);
let removed = num_nodes - total_bucket_nodes(&discovery.node_buckets);
assert!(removed > 0);
assert_eq!(total_bucket_nodes(&discovery.node_buckets), 1200 - removed);
for _ in 0..100 {
discovery.add_node(NodeEntry { id: NodeId::random(), endpoint: ep.clone() });
}
assert!(discovery.in_flight_requests.len() > 0);
assert!(discovery.in_flight_pings.len() > 0);
// Expire pings to nodes that are not in buckets.
let removed = discovery.check_expired(Instant::now() + PING_TIMEOUT).len();
let num_nodes = total_bucket_nodes(&discovery.node_buckets);
discovery.check_expired(Instant::now() + PING_TIMEOUT);
let removed = num_nodes - total_bucket_nodes(&discovery.node_buckets);
assert_eq!(removed, 0);
assert_eq!(discovery.in_flight_requests.len(), 0);
assert_eq!(discovery.in_flight_pings.len(), 0);
let from = SocketAddr::from_str("99.99.99.99:40445").unwrap();
@@ -849,7 +909,9 @@ mod tests {
discovery.on_packet(&packet, from.clone()).unwrap();
}
let removed = discovery.check_expired(Instant::now() + FIND_NODE_TIMEOUT).len();
let num_nodes = total_bucket_nodes(&discovery.node_buckets);
discovery.check_expired(Instant::now() + FIND_NODE_TIMEOUT);
let removed = num_nodes - total_bucket_nodes(&discovery.node_buckets);
assert!(removed > 0);
// FIND_NODE does not time out because it receives k results.
@@ -859,7 +921,9 @@ mod tests {
discovery.on_packet(&packet, from.clone()).unwrap();
}
let removed = discovery.check_expired(Instant::now() + FIND_NODE_TIMEOUT).len();
let num_nodes = total_bucket_nodes(&discovery.node_buckets);
discovery.check_expired(Instant::now() + FIND_NODE_TIMEOUT);
let removed = num_nodes - total_bucket_nodes(&discovery.node_buckets);
assert_eq!(removed, 0);
// Test bucket evictions with retries.
@@ -868,12 +932,16 @@ mod tests {
for _ in 0..2 {
discovery.ping(&node_entries[101]).unwrap();
let removed = discovery.check_expired(Instant::now() + PING_TIMEOUT).len();
let num_nodes = total_bucket_nodes(&discovery.node_buckets);
discovery.check_expired(Instant::now() + PING_TIMEOUT);
let removed = num_nodes - total_bucket_nodes(&discovery.node_buckets);
assert_eq!(removed, 0);
}
discovery.ping(&node_entries[101]).unwrap();
let removed = discovery.check_expired(Instant::now() + PING_TIMEOUT).len();
let num_nodes = total_bucket_nodes(&discovery.node_buckets);
discovery.check_expired(Instant::now() + PING_TIMEOUT);
let removed = num_nodes - total_bucket_nodes(&discovery.node_buckets);
assert_eq!(removed, 1);
}
@@ -1066,9 +1134,11 @@ mod tests {
assert_eq!(ep1, NodeEndpoint::from_rlp(&rlp.at(1).unwrap()).unwrap());
assert_eq!(ep2, NodeEndpoint::from_rlp(&rlp.at(2).unwrap()).unwrap());
// `discovery1` should be added to node table on ping received
if let Some(_) = discovery2.on_packet(&ping_data.payload, ep1.address.clone()).unwrap() {
panic!("Expected no changes to discovery2's table");
}
let pong_data = discovery2.dequeue_send().unwrap();
let data = &pong_data.payload[(32 + 65)..];
assert_eq!(data[0], PACKET_PONG);

View File

@@ -59,8 +59,9 @@ const TCP_ACCEPT: StreamToken = SYS_TIMER + 1;
const IDLE: TimerToken = SYS_TIMER + 2;
const DISCOVERY: StreamToken = SYS_TIMER + 3;
const DISCOVERY_REFRESH: TimerToken = SYS_TIMER + 4;
const DISCOVERY_ROUND: TimerToken = SYS_TIMER + 5;
const NODE_TABLE: TimerToken = SYS_TIMER + 6;
const FAST_DISCOVERY_REFRESH: TimerToken = SYS_TIMER + 5;
const DISCOVERY_ROUND: TimerToken = SYS_TIMER + 6;
const NODE_TABLE: TimerToken = SYS_TIMER + 7;
const FIRST_SESSION: StreamToken = 0;
const LAST_SESSION: StreamToken = FIRST_SESSION + MAX_SESSIONS - 1;
const USER_TIMER: TimerToken = LAST_SESSION + 256;
@@ -71,6 +72,8 @@ const SYS_TIMER: TimerToken = LAST_SESSION + 1;
const MAINTENANCE_TIMEOUT: Duration = Duration::from_secs(1);
// for DISCOVERY_REFRESH TimerToken
const DISCOVERY_REFRESH_TIMEOUT: Duration = Duration::from_secs(60);
// for FAST_DISCOVERY_REFRESH TimerToken
const FAST_DISCOVERY_REFRESH_TIMEOUT: Duration = Duration::from_secs(10);
// for DISCOVERY_ROUND TimerToken
const DISCOVERY_ROUND_TIMEOUT: Duration = Duration::from_millis(300);
// for NODE_TABLE TimerToken
@@ -478,10 +481,10 @@ impl Host {
let socket = UdpSocket::bind(&udp_addr).expect("Error binding UDP socket");
*self.udp_socket.lock() = Some(socket);
discovery.init_node_list(self.nodes.read().entries());
discovery.add_node_list(self.nodes.read().entries());
*self.discovery.lock() = Some(discovery);
io.register_stream(DISCOVERY)?;
io.register_timer(FAST_DISCOVERY_REFRESH, FAST_DISCOVERY_REFRESH_TIMEOUT)?;
io.register_timer(DISCOVERY_REFRESH, DISCOVERY_REFRESH_TIMEOUT)?;
io.register_timer(DISCOVERY_ROUND, DISCOVERY_ROUND_TIMEOUT)?;
}
@@ -533,6 +536,18 @@ impl Host {
}
}
fn has_enough_peers(&self) -> bool {
let min_peers = {
let info = self.info.read();
let config = &info.config;
config.min_peers
};
let (_, egress_count, ingress_count) = self.session_count();
return egress_count + ingress_count >= min_peers as usize;
}
fn connect_peers(&self, io: &IoContext<NetworkIoMessage>) {
let (min_peers, mut pin, max_handshakes, allow_ips, self_id) = {
let info = self.info.read();
@@ -1014,16 +1029,23 @@ impl IoHandler<NetworkIoMessage> for Host {
IDLE => self.maintain_network(io),
FIRST_SESSION ... LAST_SESSION => self.connection_timeout(token, io),
DISCOVERY_REFRESH => {
if let Some(d) = self.discovery.lock().as_mut() {
d.refresh();
}
// Run the _slow_ discovery if enough peers are connected
if !self.has_enough_peers() {
return;
}
self.discovery.lock().as_mut().map(|d| d.refresh());
io.update_registration(DISCOVERY).unwrap_or_else(|e| debug!("Error updating discovery registration: {:?}", e));
},
FAST_DISCOVERY_REFRESH => {
// Run the fast discovery if not enough peers are connected
if self.has_enough_peers() {
return;
}
self.discovery.lock().as_mut().map(|d| d.refresh());
io.update_registration(DISCOVERY).unwrap_or_else(|e| debug!("Error updating discovery registration: {:?}", e));
},
DISCOVERY_ROUND => {
let node_changes = { self.discovery.lock().as_mut().and_then(|d| d.round()) };
if let Some(node_changes) = node_changes {
self.update_nodes(io, node_changes);
}
self.discovery.lock().as_mut().map(|d| d.round());
io.update_registration(DISCOVERY).unwrap_or_else(|e| debug!("Error updating discovery registration: {:?}", e));
},
NODE_TABLE => {

View File

@@ -385,7 +385,7 @@ impl NodeTable {
None => return,
};
if let Err(e) = fs::create_dir_all(&path) {
warn!("Error creating node table directory: {:?}", e);
warn!(target: "network", "Error creating node table directory: {:?}", e);
return;
}
path.push(NODES_FILE);
@@ -400,11 +400,11 @@ impl NodeTable {
match fs::File::create(&path) {
Ok(file) => {
if let Err(e) = serde_json::to_writer_pretty(file, &table) {
warn!("Error writing node table file: {:?}", e);
warn!(target: "network", "Error writing node table file: {:?}", e);
}
},
Err(e) => {
warn!("Error creating node table file: {:?}", e);
warn!(target: "network", "Error creating node table file: {:?}", e);
}
}
}
@@ -418,7 +418,7 @@ impl NodeTable {
let file = match fs::File::open(&path) {
Ok(file) => file,
Err(e) => {
debug!("Error opening node table file: {:?}", e);
debug!(target: "network", "Error opening node table file: {:?}", e);
return Default::default();
},
};
@@ -431,7 +431,7 @@ impl NodeTable {
.collect()
},
Err(e) => {
warn!("Error reading node table file: {:?}", e);
warn!(target: "network", "Error reading node table file: {:?}", e);
Default::default()
},
}

View File

@@ -6,7 +6,7 @@ description = "Merkle-Patricia Trie (Ethereum Style)"
license = "GPL-3.0"
[dependencies]
patricia-trie = "0.2.1"
patricia-trie = "0.2"
keccak-hasher = { version = "0.1.1", path = "../keccak-hasher" }
hashdb = "0.2"
rlp = { version = "0.2.4", features = ["ethereum"] }

View File

@@ -8,8 +8,9 @@ name = "rlp_derive"
proc-macro = true
[dependencies]
syn = "0.13"
quote = "0.5"
syn = "0.15"
quote = "0.6"
proc-macro2 = "0.4"
[dev-dependencies]
rlp = "0.2.4"

View File

@@ -14,11 +14,12 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use {syn, quote};
use syn;
use proc_macro2::{TokenStream, Span};
struct ParseQuotes {
single: quote::Tokens,
list: quote::Tokens,
single: TokenStream,
list: TokenStream,
takes_index: bool,
}
@@ -38,7 +39,7 @@ fn decodable_wrapper_parse_quotes() -> ParseQuotes {
}
}
pub fn impl_decodable(ast: &syn::DeriveInput) -> quote::Tokens {
pub fn impl_decodable(ast: &syn::DeriveInput) -> TokenStream {
let body = match ast.data {
syn::Data::Struct(ref s) => s,
_ => panic!("#[derive(RlpDecodable)] is only defined for structs."),
@@ -47,7 +48,7 @@ pub fn impl_decodable(ast: &syn::DeriveInput) -> quote::Tokens {
let stmts: Vec<_> = body.fields.iter().enumerate().map(decodable_field_map).collect();
let name = &ast.ident;
let dummy_const: syn::Ident = format!("_IMPL_RLP_DECODABLE_FOR_{}", name).into();
let dummy_const = syn::Ident::new(&format!("_IMPL_RLP_DECODABLE_FOR_{}", name), Span::call_site());
let impl_block = quote! {
impl rlp::Decodable for #name {
fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
@@ -69,7 +70,7 @@ pub fn impl_decodable(ast: &syn::DeriveInput) -> quote::Tokens {
}
}
pub fn impl_decodable_wrapper(ast: &syn::DeriveInput) -> quote::Tokens {
pub fn impl_decodable_wrapper(ast: &syn::DeriveInput) -> TokenStream {
let body = match ast.data {
syn::Data::Struct(ref s) => s,
_ => panic!("#[derive(RlpDecodableWrapper)] is only defined for structs."),
@@ -87,7 +88,7 @@ pub fn impl_decodable_wrapper(ast: &syn::DeriveInput) -> quote::Tokens {
let name = &ast.ident;
let dummy_const: syn::Ident = format!("_IMPL_RLP_DECODABLE_FOR_{}", name).into();
let dummy_const = syn::Ident::new(&format!("_IMPL_RLP_DECODABLE_FOR_{}", name), Span::call_site());
let impl_block = quote! {
impl rlp::Decodable for #name {
fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
@@ -109,11 +110,11 @@ pub fn impl_decodable_wrapper(ast: &syn::DeriveInput) -> quote::Tokens {
}
}
fn decodable_field_map(tuple: (usize, &syn::Field)) -> quote::Tokens {
fn decodable_field_map(tuple: (usize, &syn::Field)) -> TokenStream {
decodable_field(tuple.0, tuple.1, decodable_parse_quotes())
}
fn decodable_field(index: usize, field: &syn::Field, quotes: ParseQuotes) -> quote::Tokens {
fn decodable_field(index: usize, field: &syn::Field, quotes: ParseQuotes) -> TokenStream {
let id = match field.ident {
Some(ref ident) => quote! { #ident },
None => {

View File

@@ -14,9 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use {syn, quote};
use syn;
use proc_macro2::{TokenStream, Span};
pub fn impl_encodable(ast: &syn::DeriveInput) -> quote::Tokens {
pub fn impl_encodable(ast: &syn::DeriveInput) -> TokenStream {
let body = match ast.data {
syn::Data::Struct(ref s) => s,
_ => panic!("#[derive(RlpEncodable)] is only defined for structs."),
@@ -27,7 +28,7 @@ pub fn impl_encodable(ast: &syn::DeriveInput) -> quote::Tokens {
let stmts_len = stmts.len();
let stmts_len = quote! { #stmts_len };
let dummy_const: syn::Ident = format!("_IMPL_RLP_ENCODABLE_FOR_{}", name).into();
let dummy_const = syn::Ident::new(&format!("_IMPL_RLP_ENCODABLE_FOR_{}", name), Span::call_site());
let impl_block = quote! {
impl rlp::Encodable for #name {
fn rlp_append(&self, stream: &mut rlp::RlpStream) {
@@ -46,7 +47,7 @@ pub fn impl_encodable(ast: &syn::DeriveInput) -> quote::Tokens {
}
}
pub fn impl_encodable_wrapper(ast: &syn::DeriveInput) -> quote::Tokens {
pub fn impl_encodable_wrapper(ast: &syn::DeriveInput) -> TokenStream {
let body = match ast.data {
syn::Data::Struct(ref s) => s,
_ => panic!("#[derive(RlpEncodableWrapper)] is only defined for structs."),
@@ -64,7 +65,7 @@ pub fn impl_encodable_wrapper(ast: &syn::DeriveInput) -> quote::Tokens {
let name = &ast.ident;
let dummy_const: syn::Ident = format!("_IMPL_RLP_ENCODABLE_FOR_{}", name).into();
let dummy_const = syn::Ident::new(&format!("_IMPL_RLP_ENCODABLE_FOR_{}", name), Span::call_site());
let impl_block = quote! {
impl rlp::Encodable for #name {
fn rlp_append(&self, stream: &mut rlp::RlpStream) {
@@ -82,11 +83,11 @@ pub fn impl_encodable_wrapper(ast: &syn::DeriveInput) -> quote::Tokens {
}
}
fn encodable_field_map(tuple: (usize, &syn::Field)) -> quote::Tokens {
fn encodable_field_map(tuple: (usize, &syn::Field)) -> TokenStream {
encodable_field(tuple.0, tuple.1)
}
fn encodable_field(index: usize, field: &syn::Field) -> quote::Tokens {
fn encodable_field(index: usize, field: &syn::Field) -> TokenStream {
let ident = match field.ident {
Some(ref ident) => quote! { #ident },
None => {

View File

@@ -15,6 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
extern crate proc_macro;
extern crate proc_macro2;
extern crate syn;
#[macro_use]
extern crate quote;

View File

@@ -3,7 +3,7 @@
[package]
name = "parity-version"
# NOTE: this value is used for Parity Ethereum version string (via env CARGO_PKG_VERSION)
version = "2.1.0"
version = "2.2.0"
authors = ["Parity Technologies <admin@parity.io>"]
build = "build.rs"