Compare commits
30 Commits
v2.1.9
...
v2.2.0-rc0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f92bcf6f5 | ||
|
|
4637215ab2 | ||
|
|
ceaedbbd7f | ||
|
|
52f8b1a1d7 | ||
|
|
e6d1250185 | ||
|
|
d04e5e49d0 | ||
|
|
a72436f330 | ||
|
|
467403f437 | ||
|
|
bbaac0c6a9 | ||
|
|
753fd4bda3 | ||
|
|
57d2c8c94a | ||
|
|
870ec89e9a | ||
|
|
0c3b70f2fb | ||
|
|
7a367698fe | ||
|
|
c77e99814b | ||
|
|
4ddd69cc55 | ||
|
|
ef4a61c769 | ||
|
|
7dfb5ff5bd | ||
|
|
6b391312ab | ||
|
|
6e62d77e4d | ||
|
|
0281cca9af | ||
|
|
018e2403b1 | ||
|
|
61f4534e2a | ||
|
|
69667317c1 | ||
|
|
530aac0682 | ||
|
|
6e7d8f90b5 | ||
|
|
65bf1086a2 | ||
|
|
98220442b4 | ||
|
|
3c3d2ef2b9 | ||
|
|
67066eb32a |
122
.gitlab-ci.yml
122
.gitlab-ci.yml
@@ -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
792
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -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>"]
|
||||
|
||||
|
||||
@@ -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 ""
|
||||
@@ -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"]
|
||||
@@ -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;
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
},
|
||||
|
||||
@@ -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(),
|
||||
));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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": {
|
||||
|
||||
54
ethcore/res/ethereum/eip210_test.json
Normal file
54
ethcore/res/ethereum/eip210_test.json
Normal 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 } } } }
|
||||
}
|
||||
}
|
||||
@@ -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" },
|
||||
|
||||
Submodule ethcore/res/wasm-tests updated: d17bfb6962...0edbf860ff
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)),
|
||||
);
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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")) }
|
||||
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>;
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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":{}}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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":{}}
|
||||
"#);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()),
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))?;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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())))
|
||||
}
|
||||
}))
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ..
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
@@ -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())),
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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()
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user