Merge branch 'master' into pv63-receipts
Conflicts: ethcore/src/client/test_client.rs
This commit is contained in:
commit
8f4323f3bb
11
.travis.yml
11
.travis.yml
@ -14,11 +14,11 @@ matrix:
|
|||||||
- rust: nightly
|
- rust: nightly
|
||||||
include:
|
include:
|
||||||
- rust: stable
|
- rust: stable
|
||||||
env: FEATURES="--features travis-beta" KCOV_FEATURES="" TARGETS="-p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity" ARCHIVE_SUFFIX="-${TRAVIS_OS_NAME}-${TRAVIS_TAG}"
|
env: FEATURES="--features travis-beta" KCOV_FEATURES="" TARGETS="-p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethminer" ARCHIVE_SUFFIX="-${TRAVIS_OS_NAME}-${TRAVIS_TAG}"
|
||||||
- rust: beta
|
- rust: beta
|
||||||
env: FEATURES="--features travis-beta" KCOV_FEATURES="" TARGETS="-p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity" ARCHIVE_SUFFIX="-${TRAVIS_OS_NAME}-${TRAVIS_TAG}"
|
env: FEATURES="--features travis-beta" KCOV_FEATURES="" TARGETS="-p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethminer" ARCHIVE_SUFFIX="-${TRAVIS_OS_NAME}-${TRAVIS_TAG}"
|
||||||
- rust: nightly
|
- rust: nightly
|
||||||
env: FEATURES="--features travis-nightly" KCOV_FEATURES="" TARGETS="-p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity" ARCHIVE_SUFFIX="-${TRAVIS_OS_NAME}-${TRAVIS_TAG}"
|
env: FEATURES="--features travis-nightly" KCOV_FEATURES="" TARGETS="-p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethminer" ARCHIVE_SUFFIX="-${TRAVIS_OS_NAME}-${TRAVIS_TAG}"
|
||||||
cache:
|
cache:
|
||||||
apt: true
|
apt: true
|
||||||
directories:
|
directories:
|
||||||
@ -33,10 +33,6 @@ addons:
|
|||||||
- libcurl4-openssl-dev
|
- libcurl4-openssl-dev
|
||||||
- libelf-dev
|
- libelf-dev
|
||||||
- libdw-dev
|
- libdw-dev
|
||||||
before_script: |
|
|
||||||
sudo add-apt-repository "deb http://ppa.launchpad.net/giskou/librocksdb/ubuntu trusty main" &&
|
|
||||||
sudo apt-get update &&
|
|
||||||
sudo apt-get install -y --force-yes librocksdb
|
|
||||||
script:
|
script:
|
||||||
- cargo build --release --verbose ${FEATURES}
|
- cargo build --release --verbose ${FEATURES}
|
||||||
- cargo test --release --verbose ${FEATURES} ${TARGETS}
|
- cargo test --release --verbose ${FEATURES} ${TARGETS}
|
||||||
@ -51,6 +47,7 @@ after_success: |
|
|||||||
./kcov-master/tmp/usr/local/bin/kcov --exclude-pattern /usr/,/.cargo,/root/.multirust,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests target/kcov target/debug/deps/ethcore-* &&
|
./kcov-master/tmp/usr/local/bin/kcov --exclude-pattern /usr/,/.cargo,/root/.multirust,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests target/kcov target/debug/deps/ethcore-* &&
|
||||||
./kcov-master/tmp/usr/local/bin/kcov --exclude-pattern /usr/,/.cargo,/root/.multirust,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests target/kcov target/debug/deps/ethsync-* &&
|
./kcov-master/tmp/usr/local/bin/kcov --exclude-pattern /usr/,/.cargo,/root/.multirust,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests target/kcov target/debug/deps/ethsync-* &&
|
||||||
./kcov-master/tmp/usr/local/bin/kcov --exclude-pattern /usr/,/.cargo,/root/.multirust,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests target/kcov target/debug/deps/ethcore_rpc-* &&
|
./kcov-master/tmp/usr/local/bin/kcov --exclude-pattern /usr/,/.cargo,/root/.multirust,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests target/kcov target/debug/deps/ethcore_rpc-* &&
|
||||||
|
./kcov-master/tmp/usr/local/bin/kcov --exclude-pattern /usr/,/.cargo,/root/.multirust,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests target/kcov target/debug/deps/ethminer-* &&
|
||||||
./kcov-master/tmp/usr/local/bin/kcov --coveralls-id=${TRAVIS_JOB_ID} --exclude-pattern /usr/,/.cargo,/root/.multirust target/kcov target/debug/parity-* &&
|
./kcov-master/tmp/usr/local/bin/kcov --coveralls-id=${TRAVIS_JOB_ID} --exclude-pattern /usr/,/.cargo,/root/.multirust target/kcov target/debug/parity-* &&
|
||||||
[ $TRAVIS_BRANCH = master ] &&
|
[ $TRAVIS_BRANCH = master ] &&
|
||||||
[ $TRAVIS_PULL_REQUEST = false ] &&
|
[ $TRAVIS_PULL_REQUEST = false ] &&
|
||||||
|
45
Cargo.lock
generated
45
Cargo.lock
generated
@ -2,7 +2,7 @@
|
|||||||
name = "parity"
|
name = "parity"
|
||||||
version = "0.9.99"
|
version = "0.9.99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clippy 0.0.49 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clippy 0.0.50 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ctrlc 1.1.1 (git+https://github.com/tomusdrw/rust-ctrlc.git)",
|
"ctrlc 1.1.1 (git+https://github.com/tomusdrw/rust-ctrlc.git)",
|
||||||
"daemonize 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"daemonize 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"docopt 0.6.78 (registry+https://github.com/rust-lang/crates.io-index)",
|
"docopt 0.6.78 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -11,6 +11,7 @@ dependencies = [
|
|||||||
"ethcore-devtools 0.9.99",
|
"ethcore-devtools 0.9.99",
|
||||||
"ethcore-rpc 0.9.99",
|
"ethcore-rpc 0.9.99",
|
||||||
"ethcore-util 0.9.99",
|
"ethcore-util 0.9.99",
|
||||||
|
"ethminer 0.9.99",
|
||||||
"ethsync 0.9.99",
|
"ethsync 0.9.99",
|
||||||
"fdlimit 0.1.0",
|
"fdlimit 0.1.0",
|
||||||
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -94,7 +95,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clippy"
|
name = "clippy"
|
||||||
version = "0.0.49"
|
version = "0.0.50"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"regex-syntax 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"regex-syntax 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -207,7 +208,7 @@ dependencies = [
|
|||||||
name = "ethcore"
|
name = "ethcore"
|
||||||
version = "0.9.99"
|
version = "0.9.99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clippy 0.0.49 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clippy 0.0.50 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"crossbeam 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossbeam 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"env_logger 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethash 0.9.99",
|
"ethash 0.9.99",
|
||||||
@ -233,10 +234,11 @@ dependencies = [
|
|||||||
name = "ethcore-rpc"
|
name = "ethcore-rpc"
|
||||||
version = "0.9.99"
|
version = "0.9.99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clippy 0.0.49 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clippy 0.0.50 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethash 0.9.99",
|
"ethash 0.9.99",
|
||||||
"ethcore 0.9.99",
|
"ethcore 0.9.99",
|
||||||
"ethcore-util 0.9.99",
|
"ethcore-util 0.9.99",
|
||||||
|
"ethminer 0.9.99",
|
||||||
"ethsync 0.9.99",
|
"ethsync 0.9.99",
|
||||||
"jsonrpc-core 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-core 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"jsonrpc-http-server 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-http-server 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -256,7 +258,7 @@ dependencies = [
|
|||||||
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bigint 0.1.0",
|
"bigint 0.1.0",
|
||||||
"chrono 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
"chrono 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"clippy 0.0.49 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clippy 0.0.50 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"crossbeam 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossbeam 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"elastic-array 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"elastic-array 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"env_logger 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -271,7 +273,7 @@ dependencies = [
|
|||||||
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"mio 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"mio 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rocksdb 0.4.2 (git+https://github.com/arkpar/rust-rocksdb.git)",
|
"rocksdb 0.4.3 (git+https://github.com/arkpar/rust-rocksdb.git)",
|
||||||
"rust-crypto 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rust-crypto 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -285,18 +287,31 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethsync"
|
name = "ethminer"
|
||||||
version = "0.9.99"
|
version = "0.9.99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clippy 0.0.49 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clippy 0.0.50 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"env_logger 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore 0.9.99",
|
"ethcore 0.9.99",
|
||||||
"ethcore-util 0.9.99",
|
"ethcore-util 0.9.99",
|
||||||
|
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rayon 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ethsync"
|
||||||
|
version = "0.9.99"
|
||||||
|
dependencies = [
|
||||||
|
"clippy 0.0.50 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"env_logger 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"ethcore 0.9.99",
|
||||||
|
"ethcore-util 0.9.99",
|
||||||
|
"ethminer 0.9.99",
|
||||||
"heapsize 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"heapsize 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rayon 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -466,8 +481,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "librocksdb-sys"
|
name = "librocksdb-sys"
|
||||||
version = "0.2.2"
|
version = "0.2.3"
|
||||||
source = "git+https://github.com/arkpar/rust-rocksdb.git#a4f89fea20ee3ae92b692df65d56426a5c0b6fd5"
|
source = "git+https://github.com/arkpar/rust-rocksdb.git#ebb602fc74b4067f9f51310bdc0401b8e59b7156"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
@ -688,11 +703,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rocksdb"
|
name = "rocksdb"
|
||||||
version = "0.4.2"
|
version = "0.4.3"
|
||||||
source = "git+https://github.com/arkpar/rust-rocksdb.git#a4f89fea20ee3ae92b692df65d56426a5c0b6fd5"
|
source = "git+https://github.com/arkpar/rust-rocksdb.git#ebb602fc74b4067f9f51310bdc0401b8e59b7156"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"librocksdb-sys 0.2.2 (git+https://github.com/arkpar/rust-rocksdb.git)",
|
"librocksdb-sys 0.2.3 (git+https://github.com/arkpar/rust-rocksdb.git)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -19,18 +19,19 @@ ctrlc = { git = "https://github.com/tomusdrw/rust-ctrlc.git" }
|
|||||||
fdlimit = { path = "util/fdlimit" }
|
fdlimit = { path = "util/fdlimit" }
|
||||||
daemonize = "0.2"
|
daemonize = "0.2"
|
||||||
number_prefix = "0.2"
|
number_prefix = "0.2"
|
||||||
clippy = { version = "0.0.49", optional = true }
|
rpassword = "0.1"
|
||||||
|
clippy = { version = "0.0.50", optional = true }
|
||||||
ethcore = { path = "ethcore" }
|
ethcore = { path = "ethcore" }
|
||||||
ethcore-util = { path = "util" }
|
ethcore-util = { path = "util" }
|
||||||
ethsync = { path = "sync" }
|
ethsync = { path = "sync" }
|
||||||
|
ethminer = { path = "miner" }
|
||||||
ethcore-devtools = { path = "devtools" }
|
ethcore-devtools = { path = "devtools" }
|
||||||
ethcore-rpc = { path = "rpc", optional = true }
|
ethcore-rpc = { path = "rpc", optional = true }
|
||||||
rpassword = "0.1"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["rpc"]
|
default = ["rpc"]
|
||||||
rpc = ["ethcore-rpc"]
|
rpc = ["ethcore-rpc"]
|
||||||
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev"]
|
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "ethminer/dev"]
|
||||||
travis-beta = ["ethcore/json-tests"]
|
travis-beta = ["ethcore/json-tests"]
|
||||||
travis-nightly = ["ethcore/json-tests", "dev"]
|
travis-nightly = ["ethcore/json-tests", "dev"]
|
||||||
|
|
||||||
|
23
cov.sh
23
cov.sh
@ -15,12 +15,23 @@ if ! type kcov > /dev/null; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cargo test -p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity --no-run || exit $?
|
cargo test \
|
||||||
|
-p ethash \
|
||||||
|
-p ethcore-util \
|
||||||
|
-p ethcore \
|
||||||
|
-p ethsync \
|
||||||
|
-p ethcore-rpc \
|
||||||
|
-p parity \
|
||||||
|
-p ethminer \
|
||||||
|
--no-run || exit $?
|
||||||
rm -rf target/coverage
|
rm -rf target/coverage
|
||||||
mkdir -p target/coverage
|
mkdir -p target/coverage
|
||||||
kcov --exclude-pattern ~/.multirust,rocksdb,secp256k1,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests --include-pattern src --verify target/coverage target/debug/deps/ethcore-*
|
|
||||||
kcov --exclude-pattern ~/.multirust,rocksdb,secp256k1,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests --include-pattern src --verify target/coverage target/debug/deps/ethash-*
|
EXCLUDE="~/.multirust,rocksdb,secp256k1,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests"
|
||||||
kcov --exclude-pattern ~/.multirust,rocksdb,secp256k1,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests --include-pattern src --verify target/coverage target/debug/deps/ethcore_util-*
|
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore-*
|
||||||
kcov --exclude-pattern ~/.multirust,rocksdb,secp256k1,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests --include-pattern src --verify target/coverage target/debug/deps/ethsync-*
|
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethash-*
|
||||||
kcov --exclude-pattern ~/.multirust,rocksdb,secp256k1,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests --include-pattern src --verify target/coverage target/debug/deps/ethcore_rpc-*
|
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_util-*
|
||||||
|
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethsync-*
|
||||||
|
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_rpc-*
|
||||||
|
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethminer-*
|
||||||
xdg-open target/coverage/index.html
|
xdg-open target/coverage/index.html
|
||||||
|
9
doc.sh
9
doc.sh
@ -1,4 +1,11 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# generate documentation only for partiy and ethcore libraries
|
# generate documentation only for partiy and ethcore libraries
|
||||||
|
|
||||||
cargo doc --no-deps --verbose -p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity
|
cargo doc --no-deps --verbose \
|
||||||
|
-p ethash \
|
||||||
|
-p ethcore-util \
|
||||||
|
-p ethcore \
|
||||||
|
-p ethsync \
|
||||||
|
-p ethcore-rpc \
|
||||||
|
-p parity \
|
||||||
|
-p ethminer
|
||||||
|
@ -17,7 +17,7 @@ ethcore-util = { path = "../util" }
|
|||||||
evmjit = { path = "../evmjit", optional = true }
|
evmjit = { path = "../evmjit", optional = true }
|
||||||
ethash = { path = "../ethash" }
|
ethash = { path = "../ethash" }
|
||||||
num_cpus = "0.2"
|
num_cpus = "0.2"
|
||||||
clippy = { version = "0.0.49", optional = true }
|
clippy = { version = "0.0.50", optional = true }
|
||||||
crossbeam = "0.1.5"
|
crossbeam = "0.1.5"
|
||||||
lazy_static = "0.1"
|
lazy_static = "0.1"
|
||||||
ethcore-devtools = { path = "../devtools" }
|
ethcore-devtools = { path = "../devtools" }
|
||||||
|
@ -97,6 +97,9 @@ impl<'db> HashDB for AccountDBMut<'db>{
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn insert(&mut self, value: &[u8]) -> H256 {
|
fn insert(&mut self, value: &[u8]) -> H256 {
|
||||||
|
if value == &NULL_RLP {
|
||||||
|
return SHA3_NULL_RLP.clone();
|
||||||
|
}
|
||||||
let k = value.sha3();
|
let k = value.sha3();
|
||||||
let ak = combine_key(&self.address, &k);
|
let ak = combine_key(&self.address, &k);
|
||||||
self.db.emplace(ak, value.to_vec());
|
self.db.emplace(ak, value.to_vec());
|
||||||
@ -104,11 +107,17 @@ impl<'db> HashDB for AccountDBMut<'db>{
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn emplace(&mut self, key: H256, value: Bytes) {
|
fn emplace(&mut self, key: H256, value: Bytes) {
|
||||||
|
if key == SHA3_NULL_RLP {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let key = combine_key(&self.address, &key);
|
let key = combine_key(&self.address, &key);
|
||||||
self.db.emplace(key, value.to_vec())
|
self.db.emplace(key, value.to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kill(&mut self, key: &H256) {
|
fn kill(&mut self, key: &H256) {
|
||||||
|
if key == &SHA3_NULL_RLP {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let key = combine_key(&self.address, key);
|
let key = combine_key(&self.address, key);
|
||||||
self.db.kill(&key)
|
self.db.kill(&key)
|
||||||
}
|
}
|
||||||
|
@ -523,7 +523,7 @@ mod tests {
|
|||||||
let engine = spec.to_engine().unwrap();
|
let engine = spec.to_engine().unwrap();
|
||||||
let mut config = BlockQueueConfig::default();
|
let mut config = BlockQueueConfig::default();
|
||||||
config.max_mem_use = super::MIN_MEM_LIMIT; // empty queue uses about 15000
|
config.max_mem_use = super::MIN_MEM_LIMIT; // empty queue uses about 15000
|
||||||
let mut queue = BlockQueue::new(config, Arc::new(engine), IoChannel::disconnected());
|
let queue = BlockQueue::new(config, Arc::new(engine), IoChannel::disconnected());
|
||||||
assert!(!queue.queue_info().is_full());
|
assert!(!queue.queue_info().is_full());
|
||||||
let mut blocks = get_good_dummy_block_seq(50);
|
let mut blocks = get_good_dummy_block_seq(50);
|
||||||
for b in blocks.drain(..) {
|
for b in blocks.drain(..) {
|
||||||
|
@ -28,9 +28,15 @@ pub struct MemoryCache {
|
|||||||
blooms: HashMap<BloomIndex, H2048>,
|
blooms: HashMap<BloomIndex, H2048>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for MemoryCache {
|
||||||
|
fn default() -> Self {
|
||||||
|
MemoryCache::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl MemoryCache {
|
impl MemoryCache {
|
||||||
/// Default constructor for MemoryCache
|
/// Default constructor for MemoryCache
|
||||||
pub fn new() -> MemoryCache {
|
pub fn new() -> Self {
|
||||||
MemoryCache { blooms: HashMap::new() }
|
MemoryCache { blooms: HashMap::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
//! Blockchain database client.
|
//! Blockchain database client.
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::sync::atomic::AtomicBool;
|
|
||||||
use util::*;
|
use util::*;
|
||||||
use util::panics::*;
|
use util::panics::*;
|
||||||
use views::BlockView;
|
use views::BlockView;
|
||||||
@ -31,12 +30,12 @@ use service::{NetSyncMessage, SyncMessage};
|
|||||||
use env_info::LastHashes;
|
use env_info::LastHashes;
|
||||||
use verification::*;
|
use verification::*;
|
||||||
use block::*;
|
use block::*;
|
||||||
use transaction::LocalizedTransaction;
|
use transaction::{LocalizedTransaction, SignedTransaction};
|
||||||
use extras::TransactionAddress;
|
use extras::TransactionAddress;
|
||||||
use filter::Filter;
|
use filter::Filter;
|
||||||
use log_entry::LocalizedLogEntry;
|
use log_entry::LocalizedLogEntry;
|
||||||
use block_queue::{BlockQueue, BlockQueueInfo};
|
use block_queue::{BlockQueue, BlockQueueInfo};
|
||||||
use blockchain::{BlockChain, BlockProvider, TreeRoute};
|
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
|
||||||
use client::{BlockId, TransactionId, ClientConfig, BlockChainClient};
|
use client::{BlockId, TransactionId, ClientConfig, BlockChainClient};
|
||||||
pub use blockchain::CacheSize as BlockChainCacheSize;
|
pub use blockchain::CacheSize as BlockChainCacheSize;
|
||||||
|
|
||||||
@ -106,12 +105,6 @@ pub struct Client<V = CanonVerifier> where V: Verifier {
|
|||||||
report: RwLock<ClientReport>,
|
report: RwLock<ClientReport>,
|
||||||
import_lock: Mutex<()>,
|
import_lock: Mutex<()>,
|
||||||
panic_handler: Arc<PanicHandler>,
|
panic_handler: Arc<PanicHandler>,
|
||||||
|
|
||||||
// for sealing...
|
|
||||||
sealing_enabled: AtomicBool,
|
|
||||||
sealing_block: Mutex<Option<ClosedBlock>>,
|
|
||||||
author: RwLock<Address>,
|
|
||||||
extra_data: RwLock<Bytes>,
|
|
||||||
verifier: PhantomData<V>,
|
verifier: PhantomData<V>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,10 +152,6 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
report: RwLock::new(Default::default()),
|
report: RwLock::new(Default::default()),
|
||||||
import_lock: Mutex::new(()),
|
import_lock: Mutex::new(()),
|
||||||
panic_handler: panic_handler,
|
panic_handler: panic_handler,
|
||||||
sealing_enabled: AtomicBool::new(false),
|
|
||||||
sealing_block: Mutex::new(None),
|
|
||||||
author: RwLock::new(Address::new()),
|
|
||||||
extra_data: RwLock::new(Vec::new()),
|
|
||||||
verifier: PhantomData,
|
verifier: PhantomData,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@ -233,12 +222,39 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
Ok(closed_block)
|
Ok(closed_block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn calculate_enacted_retracted(&self, import_results: Vec<ImportRoute>) -> (Vec<H256>, Vec<H256>) {
|
||||||
|
fn map_to_vec(map: Vec<(H256, bool)>) -> Vec<H256> {
|
||||||
|
map.into_iter().map(|(k, _v)| k).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
// In ImportRoute we get all the blocks that have been enacted and retracted by single insert.
|
||||||
|
// Because we are doing multiple inserts some of the blocks that were enacted in import `k`
|
||||||
|
// could be retracted in import `k+1`. This is why to understand if after all inserts
|
||||||
|
// the block is enacted or retracted we iterate over all routes and at the end final state
|
||||||
|
// will be in the hashmap
|
||||||
|
let map = import_results.into_iter().fold(HashMap::new(), |mut map, route| {
|
||||||
|
for hash in route.enacted {
|
||||||
|
map.insert(hash, true);
|
||||||
|
}
|
||||||
|
for hash in route.retracted {
|
||||||
|
map.insert(hash, false);
|
||||||
|
}
|
||||||
|
map
|
||||||
|
});
|
||||||
|
|
||||||
|
// Split to enacted retracted (using hashmap value)
|
||||||
|
let (enacted, retracted) = map.into_iter().partition(|&(_k, v)| v);
|
||||||
|
// And convert tuples to keys
|
||||||
|
(map_to_vec(enacted), map_to_vec(retracted))
|
||||||
|
}
|
||||||
|
|
||||||
/// This is triggered by a message coming from a block queue when the block is ready for insertion
|
/// This is triggered by a message coming from a block queue when the block is ready for insertion
|
||||||
pub fn import_verified_blocks(&self, io: &IoChannel<NetSyncMessage>) -> usize {
|
pub fn import_verified_blocks(&self, io: &IoChannel<NetSyncMessage>) -> usize {
|
||||||
let max_blocks_to_import = 128;
|
let max_blocks_to_import = 128;
|
||||||
|
|
||||||
let mut good_blocks = Vec::with_capacity(max_blocks_to_import);
|
let mut imported_blocks = Vec::with_capacity(max_blocks_to_import);
|
||||||
let mut bad_blocks = HashSet::new();
|
let mut invalid_blocks = HashSet::new();
|
||||||
|
let mut import_results = Vec::with_capacity(max_blocks_to_import);
|
||||||
|
|
||||||
let _import_lock = self.import_lock.lock();
|
let _import_lock = self.import_lock.lock();
|
||||||
let blocks = self.block_queue.drain(max_blocks_to_import);
|
let blocks = self.block_queue.drain(max_blocks_to_import);
|
||||||
@ -248,16 +264,16 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
for block in blocks {
|
for block in blocks {
|
||||||
let header = &block.header;
|
let header = &block.header;
|
||||||
|
|
||||||
if bad_blocks.contains(&header.parent_hash) {
|
if invalid_blocks.contains(&header.parent_hash) {
|
||||||
bad_blocks.insert(header.hash());
|
invalid_blocks.insert(header.hash());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let closed_block = self.check_and_close_block(&block);
|
let closed_block = self.check_and_close_block(&block);
|
||||||
if let Err(_) = closed_block {
|
if let Err(_) = closed_block {
|
||||||
bad_blocks.insert(header.hash());
|
invalid_blocks.insert(header.hash());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
good_blocks.push(header.hash());
|
imported_blocks.push(header.hash());
|
||||||
|
|
||||||
// Are we committing an era?
|
// Are we committing an era?
|
||||||
let ancient = if header.number() >= HISTORY {
|
let ancient = if header.number() >= HISTORY {
|
||||||
@ -276,37 +292,41 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
|
|
||||||
// And update the chain after commit to prevent race conditions
|
// And update the chain after commit to prevent race conditions
|
||||||
// (when something is in chain but you are not able to fetch details)
|
// (when something is in chain but you are not able to fetch details)
|
||||||
self.chain.insert_block(&block.bytes, receipts);
|
let route = self.chain.insert_block(&block.bytes, receipts);
|
||||||
|
import_results.push(route);
|
||||||
|
|
||||||
self.report.write().unwrap().accrue_block(&block);
|
self.report.write().unwrap().accrue_block(&block);
|
||||||
trace!(target: "client", "Imported #{} ({})", header.number(), header.hash());
|
trace!(target: "client", "Imported #{} ({})", header.number(), header.hash());
|
||||||
}
|
}
|
||||||
|
|
||||||
let imported = good_blocks.len();
|
let imported = imported_blocks.len();
|
||||||
let bad_blocks = bad_blocks.into_iter().collect::<Vec<H256>>();
|
let invalid_blocks = invalid_blocks.into_iter().collect::<Vec<H256>>();
|
||||||
|
|
||||||
{
|
{
|
||||||
if !bad_blocks.is_empty() {
|
if !invalid_blocks.is_empty() {
|
||||||
self.block_queue.mark_as_bad(&bad_blocks);
|
self.block_queue.mark_as_bad(&invalid_blocks);
|
||||||
}
|
}
|
||||||
if !good_blocks.is_empty() {
|
if !imported_blocks.is_empty() {
|
||||||
self.block_queue.mark_as_good(&good_blocks);
|
self.block_queue.mark_as_good(&imported_blocks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
if !good_blocks.is_empty() && self.block_queue.queue_info().is_empty() {
|
if !imported_blocks.is_empty() && self.block_queue.queue_info().is_empty() {
|
||||||
|
let (enacted, retracted) = self.calculate_enacted_retracted(import_results);
|
||||||
io.send(NetworkIoMessage::User(SyncMessage::NewChainBlocks {
|
io.send(NetworkIoMessage::User(SyncMessage::NewChainBlocks {
|
||||||
good: good_blocks,
|
imported: imported_blocks,
|
||||||
bad: bad_blocks,
|
invalid: invalid_blocks,
|
||||||
// TODO [todr] were to take those from?
|
enacted: enacted,
|
||||||
retracted: vec![],
|
retracted: retracted,
|
||||||
})).unwrap();
|
})).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.chain_info().best_block_hash != original_best && self.sealing_enabled.load(atomic::Ordering::Relaxed) {
|
{
|
||||||
self.prepare_sealing();
|
if self.chain_info().best_block_hash != original_best {
|
||||||
|
io.send(NetworkIoMessage::User(SyncMessage::NewChainHead)).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
imported
|
imported
|
||||||
@ -357,52 +377,59 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
BlockId::Latest => Some(self.chain.best_block_number())
|
BlockId::Latest => Some(self.chain.best_block_number())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the author that we will seal blocks as.
|
|
||||||
pub fn author(&self) -> Address {
|
|
||||||
self.author.read().unwrap().clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the author that we will seal blocks as.
|
|
||||||
pub fn set_author(&self, author: Address) {
|
|
||||||
*self.author.write().unwrap() = author;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the extra_data that we will seal blocks wuth.
|
|
||||||
pub fn extra_data(&self) -> Bytes {
|
|
||||||
self.extra_data.read().unwrap().clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the extra_data that we will seal blocks with.
|
|
||||||
pub fn set_extra_data(&self, extra_data: Bytes) {
|
|
||||||
*self.extra_data.write().unwrap() = extra_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// New chain head event. Restart mining operation.
|
|
||||||
pub fn prepare_sealing(&self) {
|
|
||||||
let h = self.chain.best_block_hash();
|
|
||||||
let mut b = OpenBlock::new(
|
|
||||||
self.engine.deref().deref(),
|
|
||||||
self.state_db.lock().unwrap().spawn(),
|
|
||||||
match self.chain.block_header(&h) { Some(ref x) => x, None => {return;} },
|
|
||||||
self.build_last_hashes(h.clone()),
|
|
||||||
self.author(),
|
|
||||||
self.extra_data()
|
|
||||||
);
|
|
||||||
|
|
||||||
self.chain.find_uncle_headers(&h, self.engine.deref().deref().maximum_uncle_age()).unwrap().into_iter().take(self.engine.deref().deref().maximum_uncle_count()).foreach(|h| { b.push_uncle(h).unwrap(); });
|
|
||||||
|
|
||||||
// TODO: push transactions.
|
|
||||||
|
|
||||||
let b = b.close();
|
|
||||||
trace!("Sealing: number={}, hash={}, diff={}", b.hash(), b.block().header().difficulty(), b.block().header().number());
|
|
||||||
*self.sealing_block.lock().unwrap() = Some(b);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: need MinerService MinerIoHandler
|
|
||||||
|
|
||||||
impl<V> BlockChainClient for Client<V> where V: Verifier {
|
impl<V> BlockChainClient for Client<V> where V: Verifier {
|
||||||
|
|
||||||
|
|
||||||
|
// TODO [todr] Should be moved to miner crate eventually.
|
||||||
|
fn try_seal(&self, block: ClosedBlock, seal: Vec<Bytes>) -> Result<SealedBlock, ClosedBlock> {
|
||||||
|
block.try_seal(self.engine.deref().deref(), seal)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO [todr] Should be moved to miner crate eventually.
|
||||||
|
fn prepare_sealing(&self, author: Address, extra_data: Bytes, transactions: Vec<SignedTransaction>) -> Option<ClosedBlock> {
|
||||||
|
let engine = self.engine.deref().deref();
|
||||||
|
let h = self.chain.best_block_hash();
|
||||||
|
|
||||||
|
let mut b = OpenBlock::new(
|
||||||
|
engine,
|
||||||
|
self.state_db.lock().unwrap().spawn(),
|
||||||
|
match self.chain.block_header(&h) { Some(ref x) => x, None => {return None} },
|
||||||
|
self.build_last_hashes(h.clone()),
|
||||||
|
author,
|
||||||
|
extra_data,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add uncles
|
||||||
|
self.chain
|
||||||
|
.find_uncle_headers(&h, engine.maximum_uncle_age())
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.take(engine.maximum_uncle_count())
|
||||||
|
.foreach(|h| {
|
||||||
|
b.push_uncle(h).unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add transactions
|
||||||
|
let block_number = b.block().header().number();
|
||||||
|
for tx in transactions {
|
||||||
|
let import = b.push_transaction(tx, None);
|
||||||
|
if let Err(e) = import {
|
||||||
|
trace!("Error adding transaction to block: number={}. Error: {:?}", block_number, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// And close
|
||||||
|
let b = b.close();
|
||||||
|
trace!("Sealing: number={}, hash={}, diff={}",
|
||||||
|
b.block().header().number(),
|
||||||
|
b.hash(),
|
||||||
|
b.block().header().difficulty()
|
||||||
|
);
|
||||||
|
Some(b)
|
||||||
|
}
|
||||||
|
|
||||||
fn block_header(&self, id: BlockId) -> Option<Bytes> {
|
fn block_header(&self, id: BlockId) -> Option<Bytes> {
|
||||||
Self::block_hash(&self.chain, id).and_then(|hash| self.chain.block(&hash).map(|bytes| BlockView::new(&bytes).rlp().at(0).as_raw().to_vec()))
|
Self::block_hash(&self.chain, id).and_then(|hash| self.chain.block(&hash).map(|bytes| BlockView::new(&bytes).rlp().at(0).as_raw().to_vec()))
|
||||||
}
|
}
|
||||||
@ -449,6 +476,14 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
|
|||||||
self.state().code(address)
|
self.state().code(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn balance(&self, address: &Address) -> U256 {
|
||||||
|
self.state().balance(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn storage_at(&self, address: &Address, position: &H256) -> H256 {
|
||||||
|
self.state().storage_at(address, position)
|
||||||
|
}
|
||||||
|
|
||||||
fn transaction(&self, id: TransactionId) -> Option<LocalizedTransaction> {
|
fn transaction(&self, id: TransactionId) -> Option<LocalizedTransaction> {
|
||||||
match id {
|
match id {
|
||||||
TransactionId::Hash(ref hash) => self.chain.transaction_address(hash),
|
TransactionId::Hash(ref hash) => self.chain.transaction_address(hash),
|
||||||
@ -553,39 +588,6 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
|
|||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Grab the `ClosedBlock` that we want to be sealed. Comes as a mutex that you have to lock.
|
|
||||||
fn sealing_block(&self) -> &Mutex<Option<ClosedBlock>> {
|
|
||||||
if self.sealing_block.lock().unwrap().is_none() {
|
|
||||||
self.sealing_enabled.store(true, atomic::Ordering::Relaxed);
|
|
||||||
// TODO: Above should be on a timer that resets after two blocks have arrived without being asked for.
|
|
||||||
self.prepare_sealing();
|
|
||||||
}
|
|
||||||
&self.sealing_block
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Submit `seal` as a valid solution for the header of `pow_hash`.
|
|
||||||
/// Will check the seal, but not actually insert the block into the chain.
|
|
||||||
fn submit_seal(&self, pow_hash: H256, seal: Vec<Bytes>) -> Result<(), Error> {
|
|
||||||
let mut maybe_b = self.sealing_block.lock().unwrap();
|
|
||||||
match *maybe_b {
|
|
||||||
Some(ref b) if b.hash() == pow_hash => {}
|
|
||||||
_ => { return Err(Error::PowHashInvalid); }
|
|
||||||
}
|
|
||||||
|
|
||||||
let b = maybe_b.take();
|
|
||||||
match b.unwrap().try_seal(self.engine.deref().deref(), seal) {
|
|
||||||
Err(old) => {
|
|
||||||
*maybe_b = Some(old);
|
|
||||||
Err(Error::PowInvalid)
|
|
||||||
}
|
|
||||||
Ok(sealed) => {
|
|
||||||
// TODO: commit DB from `sealed.drain` and make a VerifiedBlock to skip running the transactions twice.
|
|
||||||
try!(self.import_block(sealed.rlp_bytes()));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MayPanic for Client {
|
impl MayPanic for Client {
|
||||||
|
@ -26,18 +26,17 @@ pub use self::config::{ClientConfig, BlockQueueConfig, BlockChainConfig};
|
|||||||
pub use self::ids::{BlockId, TransactionId};
|
pub use self::ids::{BlockId, TransactionId};
|
||||||
pub use self::test_client::{TestBlockChainClient, EachBlockWith};
|
pub use self::test_client::{TestBlockChainClient, EachBlockWith};
|
||||||
|
|
||||||
use std::sync::Mutex;
|
|
||||||
use util::bytes::Bytes;
|
use util::bytes::Bytes;
|
||||||
use util::hash::{Address, H256, H2048};
|
use util::hash::{Address, H256, H2048};
|
||||||
use util::numbers::U256;
|
use util::numbers::U256;
|
||||||
use blockchain::TreeRoute;
|
use blockchain::TreeRoute;
|
||||||
use block_queue::BlockQueueInfo;
|
use block_queue::BlockQueueInfo;
|
||||||
use block::ClosedBlock;
|
use block::{ClosedBlock, SealedBlock};
|
||||||
use header::BlockNumber;
|
use header::BlockNumber;
|
||||||
use transaction::LocalizedTransaction;
|
use transaction::{LocalizedTransaction, SignedTransaction};
|
||||||
use log_entry::LocalizedLogEntry;
|
use log_entry::LocalizedLogEntry;
|
||||||
use filter::Filter;
|
use filter::Filter;
|
||||||
use error::{ImportResult, Error};
|
use error::{ImportResult};
|
||||||
|
|
||||||
/// Blockchain database client. Owns and manages a blockchain and a block queue.
|
/// Blockchain database client. Owns and manages a blockchain and a block queue.
|
||||||
pub trait BlockChainClient : Sync + Send {
|
pub trait BlockChainClient : Sync + Send {
|
||||||
@ -66,6 +65,12 @@ pub trait BlockChainClient : Sync + Send {
|
|||||||
/// Get address code.
|
/// Get address code.
|
||||||
fn code(&self, address: &Address) -> Option<Bytes>;
|
fn code(&self, address: &Address) -> Option<Bytes>;
|
||||||
|
|
||||||
|
/// Get address balance.
|
||||||
|
fn balance(&self, address: &Address) -> U256;
|
||||||
|
|
||||||
|
/// Get value of the storage at given position.
|
||||||
|
fn storage_at(&self, address: &Address, position: &H256) -> H256;
|
||||||
|
|
||||||
/// Get transaction with given hash.
|
/// Get transaction with given hash.
|
||||||
fn transaction(&self, id: TransactionId) -> Option<LocalizedTransaction>;
|
fn transaction(&self, id: TransactionId) -> Option<LocalizedTransaction>;
|
||||||
|
|
||||||
@ -103,11 +108,13 @@ pub trait BlockChainClient : Sync + Send {
|
|||||||
/// Returns logs matching given filter.
|
/// Returns logs matching given filter.
|
||||||
fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry>;
|
fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry>;
|
||||||
|
|
||||||
/// Grab the `ClosedBlock` that we want to be sealed. Comes as a mutex that you have to lock.
|
// TODO [todr] Should be moved to miner crate eventually.
|
||||||
fn sealing_block(&self) -> &Mutex<Option<ClosedBlock>>;
|
/// Returns ClosedBlock prepared for sealing.
|
||||||
|
fn prepare_sealing(&self, author: Address, extra_data: Bytes, transactions: Vec<SignedTransaction>) -> Option<ClosedBlock>;
|
||||||
|
|
||||||
|
// TODO [todr] Should be moved to miner crate eventually.
|
||||||
|
/// Attempts to seal given block. Returns `SealedBlock` on success and the same block in case of error.
|
||||||
|
fn try_seal(&self, block: ClosedBlock, seal: Vec<Bytes>) -> Result<SealedBlock, ClosedBlock>;
|
||||||
|
|
||||||
/// Submit `seal` as a valid solution for the header of `pow_hash`.
|
|
||||||
/// Will check the seal, but not actually insert the block into the chain.
|
|
||||||
fn submit_seal(&self, pow_hash: H256, seal: Vec<Bytes>) -> Result<(), Error>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
//! Test client.
|
//! Test client.
|
||||||
|
|
||||||
use util::*;
|
use util::*;
|
||||||
use transaction::{Transaction, LocalizedTransaction, Action};
|
use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action};
|
||||||
use blockchain::TreeRoute;
|
use blockchain::TreeRoute;
|
||||||
use client::{BlockChainClient, BlockChainInfo, BlockStatus, BlockId, TransactionId};
|
use client::{BlockChainClient, BlockChainInfo, BlockStatus, BlockId, TransactionId};
|
||||||
use header::{Header as BlockHeader, BlockNumber};
|
use header::{Header as BlockHeader, BlockNumber};
|
||||||
@ -25,9 +25,10 @@ use filter::Filter;
|
|||||||
use log_entry::LocalizedLogEntry;
|
use log_entry::LocalizedLogEntry;
|
||||||
use receipt::Receipt;
|
use receipt::Receipt;
|
||||||
use extras::BlockReceipts;
|
use extras::BlockReceipts;
|
||||||
use error::{ImportResult, Error};
|
use error::{ImportResult};
|
||||||
|
|
||||||
use block_queue::BlockQueueInfo;
|
use block_queue::BlockQueueInfo;
|
||||||
use block::ClosedBlock;
|
use block::{SealedBlock, ClosedBlock};
|
||||||
|
|
||||||
/// Test client.
|
/// Test client.
|
||||||
pub struct TestBlockChainClient {
|
pub struct TestBlockChainClient {
|
||||||
@ -41,6 +42,12 @@ pub struct TestBlockChainClient {
|
|||||||
pub last_hash: RwLock<H256>,
|
pub last_hash: RwLock<H256>,
|
||||||
/// Difficulty.
|
/// Difficulty.
|
||||||
pub difficulty: RwLock<U256>,
|
pub difficulty: RwLock<U256>,
|
||||||
|
/// Balances.
|
||||||
|
pub balances: RwLock<HashMap<Address, U256>>,
|
||||||
|
/// Storage.
|
||||||
|
pub storage: RwLock<HashMap<(Address, H256), H256>>,
|
||||||
|
/// Code.
|
||||||
|
pub code: RwLock<HashMap<Address, Bytes>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -56,9 +63,15 @@ pub enum EachBlockWith {
|
|||||||
UncleAndTransaction
|
UncleAndTransaction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for TestBlockChainClient {
|
||||||
|
fn default() -> Self {
|
||||||
|
TestBlockChainClient::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TestBlockChainClient {
|
impl TestBlockChainClient {
|
||||||
/// Creates new test client.
|
/// Creates new test client.
|
||||||
pub fn new() -> TestBlockChainClient {
|
pub fn new() -> Self {
|
||||||
|
|
||||||
let mut client = TestBlockChainClient {
|
let mut client = TestBlockChainClient {
|
||||||
blocks: RwLock::new(HashMap::new()),
|
blocks: RwLock::new(HashMap::new()),
|
||||||
@ -66,12 +79,30 @@ impl TestBlockChainClient {
|
|||||||
genesis_hash: H256::new(),
|
genesis_hash: H256::new(),
|
||||||
last_hash: RwLock::new(H256::new()),
|
last_hash: RwLock::new(H256::new()),
|
||||||
difficulty: RwLock::new(From::from(0)),
|
difficulty: RwLock::new(From::from(0)),
|
||||||
|
balances: RwLock::new(HashMap::new()),
|
||||||
|
storage: RwLock::new(HashMap::new()),
|
||||||
|
code: RwLock::new(HashMap::new()),
|
||||||
};
|
};
|
||||||
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
|
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
|
||||||
client.genesis_hash = client.last_hash.read().unwrap().clone();
|
client.genesis_hash = client.last_hash.read().unwrap().clone();
|
||||||
client
|
client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the balance of account `address` to `balance`.
|
||||||
|
pub fn set_balance(&mut self, address: Address, balance: U256) {
|
||||||
|
self.balances.write().unwrap().insert(address, balance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set `code` at `address`.
|
||||||
|
pub fn set_code(&mut self, address: Address, code: Bytes) {
|
||||||
|
self.code.write().unwrap().insert(address, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set storage `position` to `value` for account `address`.
|
||||||
|
pub fn set_storage(&mut self, address: Address, position: H256, value: H256) {
|
||||||
|
self.storage.write().unwrap().insert((address, position), value);
|
||||||
|
}
|
||||||
|
|
||||||
/// Add blocks to test client.
|
/// Add blocks to test client.
|
||||||
pub fn add_blocks(&mut self, count: usize, with: EachBlockWith) {
|
pub fn add_blocks(&mut self, count: usize, with: EachBlockWith) {
|
||||||
let len = self.numbers.read().unwrap().len();
|
let len = self.numbers.read().unwrap().len();
|
||||||
@ -162,8 +193,16 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
U256::zero()
|
U256::zero()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn code(&self, _address: &Address) -> Option<Bytes> {
|
fn code(&self, address: &Address) -> Option<Bytes> {
|
||||||
unimplemented!();
|
self.code.read().unwrap().get(address).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn balance(&self, address: &Address) -> U256 {
|
||||||
|
self.balances.read().unwrap().get(address).cloned().unwrap_or_else(U256::zero)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn storage_at(&self, address: &Address, position: &H256) -> H256 {
|
||||||
|
self.storage.read().unwrap().get(&(address.clone(), position.clone())).cloned().unwrap_or_else(H256::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transaction(&self, _id: TransactionId) -> Option<LocalizedTransaction> {
|
fn transaction(&self, _id: TransactionId) -> Option<LocalizedTransaction> {
|
||||||
@ -178,12 +217,12 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sealing_block(&self) -> &Mutex<Option<ClosedBlock>> {
|
fn prepare_sealing(&self, _author: Address, _extra_data: Bytes, _transactions: Vec<SignedTransaction>) -> Option<ClosedBlock> {
|
||||||
unimplemented!();
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn submit_seal(&self, _pow_hash: H256, _seal: Vec<Bytes>) -> Result<(), Error> {
|
fn try_seal(&self, _block: ClosedBlock, _seal: Vec<Bytes>) -> Result<SealedBlock, ClosedBlock> {
|
||||||
unimplemented!();
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_header(&self, id: BlockId) -> Option<Bytes> {
|
fn block_header(&self, id: BlockId) -> Option<Bytes> {
|
||||||
|
@ -63,8 +63,15 @@ pub enum ExecutionError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
/// Errors concerning transaction proessing.
|
/// Errors concerning transaction processing.
|
||||||
pub enum TransactionError {
|
pub enum TransactionError {
|
||||||
|
/// Transaction's gas price is below threshold.
|
||||||
|
InsufficientGasPrice {
|
||||||
|
/// Minimal expected gas price
|
||||||
|
minimal: U256,
|
||||||
|
/// Transaction gas price
|
||||||
|
got: U256
|
||||||
|
},
|
||||||
/// Transaction's gas limit (aka gas) is invalid.
|
/// Transaction's gas limit (aka gas) is invalid.
|
||||||
InvalidGasLimit(OutOfBounds<U256>),
|
InvalidGasLimit(OutOfBounds<U256>),
|
||||||
}
|
}
|
||||||
|
@ -301,8 +301,14 @@ mod tests {
|
|||||||
env_info: EnvInfo
|
env_info: EnvInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for TestSetup {
|
||||||
|
fn default() -> Self {
|
||||||
|
TestSetup::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TestSetup {
|
impl TestSetup {
|
||||||
fn new() -> TestSetup {
|
fn new() -> Self {
|
||||||
TestSetup {
|
TestSetup {
|
||||||
state: get_temp_state(),
|
state: get_temp_state(),
|
||||||
engine: get_test_spec().to_engine().unwrap(),
|
engine: get_test_spec().to_engine().unwrap(),
|
||||||
|
@ -28,12 +28,16 @@ pub enum SyncMessage {
|
|||||||
/// New block has been imported into the blockchain
|
/// New block has been imported into the blockchain
|
||||||
NewChainBlocks {
|
NewChainBlocks {
|
||||||
/// Hashes of blocks imported to blockchain
|
/// Hashes of blocks imported to blockchain
|
||||||
good: Vec<H256>,
|
imported: Vec<H256>,
|
||||||
/// Hashes of blocks not imported to blockchain
|
/// Hashes of blocks not imported to blockchain (because were invalid)
|
||||||
bad: Vec<H256>,
|
invalid: Vec<H256>,
|
||||||
/// Hashes of blocks that were removed from canonical chain
|
/// Hashes of blocks that were removed from canonical chain
|
||||||
retracted: Vec<H256>,
|
retracted: Vec<H256>,
|
||||||
|
/// Hashes of blocks that are now included in cannonical chain
|
||||||
|
enacted: Vec<H256>,
|
||||||
},
|
},
|
||||||
|
/// Best Block Hash in chain has been changed
|
||||||
|
NewChainHead,
|
||||||
/// A block is ready
|
/// A block is ready
|
||||||
BlockVerified,
|
BlockVerified,
|
||||||
}
|
}
|
||||||
|
@ -143,16 +143,9 @@ fn can_mine() {
|
|||||||
let dummy_blocks = get_good_dummy_block_seq(2);
|
let dummy_blocks = get_good_dummy_block_seq(2);
|
||||||
let client_result = get_test_client_with_blocks(vec![dummy_blocks[0].clone()]);
|
let client_result = get_test_client_with_blocks(vec![dummy_blocks[0].clone()]);
|
||||||
let client = client_result.reference();
|
let client = client_result.reference();
|
||||||
let b = client.sealing_block();
|
|
||||||
let pow_hash = {
|
let b = client.prepare_sealing(Address::default(), vec![], vec![]).unwrap();
|
||||||
let u = b.lock().unwrap();
|
|
||||||
match *u {
|
assert_eq!(*b.block().header().parent_hash(), BlockView::new(&dummy_blocks[0]).header_view().sha3());
|
||||||
Some(ref b) => {
|
assert!(client.try_seal(b, vec![]).is_ok());
|
||||||
assert_eq!(*b.block().header().parent_hash(), BlockView::new(&dummy_blocks[0]).header_view().sha3());
|
|
||||||
b.hash()
|
|
||||||
}
|
|
||||||
None => { panic!(); }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
assert!(client.submit_seal(pow_hash, vec![]).is_ok());
|
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ use error::Error;
|
|||||||
use header::Header;
|
use header::Header;
|
||||||
use super::Verifier;
|
use super::Verifier;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub struct NoopVerifier;
|
pub struct NoopVerifier;
|
||||||
|
|
||||||
impl Verifier for NoopVerifier {
|
impl Verifier for NoopVerifier {
|
||||||
|
@ -255,8 +255,14 @@ mod tests {
|
|||||||
numbers: HashMap<BlockNumber, H256>,
|
numbers: HashMap<BlockNumber, H256>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for TestBlockChain {
|
||||||
|
fn default() -> Self {
|
||||||
|
TestBlockChain::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TestBlockChain {
|
impl TestBlockChain {
|
||||||
pub fn new() -> TestBlockChain {
|
pub fn new() -> Self {
|
||||||
TestBlockChain {
|
TestBlockChain {
|
||||||
blocks: HashMap::new(),
|
blocks: HashMap::new(),
|
||||||
numbers: HashMap::new(),
|
numbers: HashMap::new(),
|
||||||
|
2
hook.sh
2
hook.sh
@ -7,6 +7,6 @@ echo "set -e" >> $FILE
|
|||||||
echo "cargo build --release --features dev" >> $FILE
|
echo "cargo build --release --features dev" >> $FILE
|
||||||
# Build tests
|
# Build tests
|
||||||
echo "cargo test --no-run --features dev \\" >> $FILE
|
echo "cargo test --no-run --features dev \\" >> $FILE
|
||||||
echo " -p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity" >> $FILE
|
echo " -p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethminer" >> $FILE
|
||||||
echo "" >> $FILE
|
echo "" >> $FILE
|
||||||
chmod +x $FILE
|
chmod +x $FILE
|
||||||
|
24
miner/Cargo.toml
Normal file
24
miner/Cargo.toml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
description = "Ethminer library"
|
||||||
|
homepage = "http://ethcore.io"
|
||||||
|
license = "GPL-3.0"
|
||||||
|
name = "ethminer"
|
||||||
|
version = "0.9.99"
|
||||||
|
authors = ["Ethcore <admin@ethcore.io>"]
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
rustc_version = "0.1"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
ethcore-util = { path = "../util" }
|
||||||
|
ethcore = { path = "../ethcore" }
|
||||||
|
log = "0.3"
|
||||||
|
env_logger = "0.3"
|
||||||
|
rustc-serialize = "0.3"
|
||||||
|
rayon = "0.3.1"
|
||||||
|
clippy = { version = "0.0.50", optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
dev = ["clippy"]
|
25
miner/build.rs
Normal file
25
miner/build.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
extern crate rustc_version;
|
||||||
|
|
||||||
|
use rustc_version::{version_meta, Channel};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if let Channel::Nightly = version_meta().channel {
|
||||||
|
println!("cargo:rustc-cfg=nightly");
|
||||||
|
}
|
||||||
|
}
|
111
miner/src/lib.rs
Normal file
111
miner/src/lib.rs
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
#![cfg_attr(all(nightly, feature="dev"), feature(plugin))]
|
||||||
|
#![cfg_attr(all(nightly, feature="dev"), plugin(clippy))]
|
||||||
|
|
||||||
|
//! Miner module
|
||||||
|
//! Keeps track of transactions and mined block.
|
||||||
|
//!
|
||||||
|
//! Usage example:
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! extern crate ethcore_util as util;
|
||||||
|
//! extern crate ethcore;
|
||||||
|
//! extern crate ethminer;
|
||||||
|
//! use std::ops::Deref;
|
||||||
|
//! use std::env;
|
||||||
|
//! use std::sync::Arc;
|
||||||
|
//! use util::network::{NetworkService, NetworkConfiguration};
|
||||||
|
//! use ethcore::client::{Client, ClientConfig, BlockChainClient};
|
||||||
|
//! use ethcore::ethereum;
|
||||||
|
//! use ethminer::{Miner, MinerService};
|
||||||
|
//!
|
||||||
|
//! fn main() {
|
||||||
|
//! let mut service = NetworkService::start(NetworkConfiguration::new()).unwrap();
|
||||||
|
//! let dir = env::temp_dir();
|
||||||
|
//! let client = Client::new(ClientConfig::default(), ethereum::new_frontier(), &dir, service.io().channel()).unwrap();
|
||||||
|
//!
|
||||||
|
//! let miner: Miner = Miner::default();
|
||||||
|
//! // get status
|
||||||
|
//! assert_eq!(miner.status().transaction_queue_pending, 0);
|
||||||
|
//!
|
||||||
|
//! // Check block for sealing
|
||||||
|
//! miner.prepare_sealing(client.deref());
|
||||||
|
//! assert!(miner.sealing_block(client.deref()).lock().unwrap().is_some());
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate ethcore_util as util;
|
||||||
|
extern crate ethcore;
|
||||||
|
extern crate env_logger;
|
||||||
|
extern crate rayon;
|
||||||
|
|
||||||
|
mod miner;
|
||||||
|
mod transaction_queue;
|
||||||
|
|
||||||
|
pub use transaction_queue::TransactionQueue;
|
||||||
|
pub use miner::{Miner};
|
||||||
|
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use util::{H256, U256, Address, Bytes};
|
||||||
|
use ethcore::client::{BlockChainClient};
|
||||||
|
use ethcore::block::{ClosedBlock};
|
||||||
|
use ethcore::error::{Error};
|
||||||
|
use ethcore::transaction::SignedTransaction;
|
||||||
|
|
||||||
|
/// Miner client API
|
||||||
|
pub trait MinerService : Send + Sync {
|
||||||
|
|
||||||
|
/// Returns miner's status.
|
||||||
|
fn status(&self) -> MinerStatus;
|
||||||
|
|
||||||
|
/// Imports transactions to transaction queue.
|
||||||
|
fn import_transactions<T>(&self, transactions: Vec<SignedTransaction>, fetch_nonce: T) -> Result<(), Error>
|
||||||
|
where T: Fn(&Address) -> U256;
|
||||||
|
|
||||||
|
/// Returns hashes of transactions currently in pending
|
||||||
|
fn pending_transactions_hashes(&self) -> Vec<H256>;
|
||||||
|
|
||||||
|
/// Removes all transactions from the queue and restart mining operation.
|
||||||
|
fn clear_and_reset(&self, chain: &BlockChainClient);
|
||||||
|
|
||||||
|
/// Called when blocks are imported to chain, updates transactions queue.
|
||||||
|
fn chain_new_blocks(&self, chain: &BlockChainClient, imported: &[H256], invalid: &[H256], enacted: &[H256], retracted: &[H256]);
|
||||||
|
|
||||||
|
/// New chain head event. Restart mining operation.
|
||||||
|
fn prepare_sealing(&self, chain: &BlockChainClient);
|
||||||
|
|
||||||
|
/// Grab the `ClosedBlock` that we want to be sealed. Comes as a mutex that you have to lock.
|
||||||
|
fn sealing_block(&self, chain: &BlockChainClient) -> &Mutex<Option<ClosedBlock>>;
|
||||||
|
|
||||||
|
/// Submit `seal` as a valid solution for the header of `pow_hash`.
|
||||||
|
/// Will check the seal, but not actually insert the block into the chain.
|
||||||
|
fn submit_seal(&self, chain: &BlockChainClient, pow_hash: H256, seal: Vec<Bytes>) -> Result<(), Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mining status
|
||||||
|
pub struct MinerStatus {
|
||||||
|
/// Number of transactions in queue with state `pending` (ready to be included in block)
|
||||||
|
pub transaction_queue_pending: usize,
|
||||||
|
/// Number of transactions in queue with state `future` (not yet ready to be included in block)
|
||||||
|
pub transaction_queue_future: usize,
|
||||||
|
}
|
191
miner/src/miner.rs
Normal file
191
miner/src/miner.rs
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use rayon::prelude::*;
|
||||||
|
use std::sync::{Mutex, RwLock, Arc};
|
||||||
|
use std::sync::atomic;
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
|
|
||||||
|
use util::{H256, U256, Address, Bytes};
|
||||||
|
use ethcore::views::{BlockView};
|
||||||
|
use ethcore::client::{BlockChainClient, BlockId};
|
||||||
|
use ethcore::block::{ClosedBlock};
|
||||||
|
use ethcore::error::{Error};
|
||||||
|
use ethcore::transaction::SignedTransaction;
|
||||||
|
use super::{MinerService, MinerStatus, TransactionQueue};
|
||||||
|
|
||||||
|
/// Keeps track of transactions using priority queue and holds currently mined block.
|
||||||
|
pub struct Miner {
|
||||||
|
transaction_queue: Mutex<TransactionQueue>,
|
||||||
|
|
||||||
|
// for sealing...
|
||||||
|
sealing_enabled: AtomicBool,
|
||||||
|
sealing_block: Mutex<Option<ClosedBlock>>,
|
||||||
|
author: RwLock<Address>,
|
||||||
|
extra_data: RwLock<Bytes>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Miner {
|
||||||
|
fn default() -> Miner {
|
||||||
|
Miner {
|
||||||
|
transaction_queue: Mutex::new(TransactionQueue::new()),
|
||||||
|
sealing_enabled: AtomicBool::new(false),
|
||||||
|
sealing_block: Mutex::new(None),
|
||||||
|
author: RwLock::new(Address::default()),
|
||||||
|
extra_data: RwLock::new(Vec::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Miner {
|
||||||
|
/// Creates new instance of miner
|
||||||
|
pub fn new() -> Arc<Miner> {
|
||||||
|
Arc::new(Miner::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the author that we will seal blocks as.
|
||||||
|
fn author(&self) -> Address {
|
||||||
|
*self.author.read().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the extra_data that we will seal blocks wuth.
|
||||||
|
fn extra_data(&self) -> Bytes {
|
||||||
|
self.extra_data.read().unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the author that we will seal blocks as.
|
||||||
|
pub fn set_author(&self, author: Address) {
|
||||||
|
*self.author.write().unwrap() = author;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the extra_data that we will seal blocks with.
|
||||||
|
pub fn set_extra_data(&self, extra_data: Bytes) {
|
||||||
|
*self.extra_data.write().unwrap() = extra_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set minimal gas price of transaction to be accepted for mining.
|
||||||
|
pub fn set_minimal_gas_price(&self, min_gas_price: U256) {
|
||||||
|
self.transaction_queue.lock().unwrap().set_minimal_gas_price(min_gas_price);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MinerService for Miner {
|
||||||
|
|
||||||
|
fn clear_and_reset(&self, chain: &BlockChainClient) {
|
||||||
|
self.transaction_queue.lock().unwrap().clear();
|
||||||
|
self.prepare_sealing(chain);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn status(&self) -> MinerStatus {
|
||||||
|
let status = self.transaction_queue.lock().unwrap().status();
|
||||||
|
MinerStatus {
|
||||||
|
transaction_queue_pending: status.pending,
|
||||||
|
transaction_queue_future: status.future,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn import_transactions<T>(&self, transactions: Vec<SignedTransaction>, fetch_nonce: T) -> Result<(), Error>
|
||||||
|
where T: Fn(&Address) -> U256 {
|
||||||
|
let mut transaction_queue = self.transaction_queue.lock().unwrap();
|
||||||
|
transaction_queue.add_all(transactions, fetch_nonce)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pending_transactions_hashes(&self) -> Vec<H256> {
|
||||||
|
let transaction_queue = self.transaction_queue.lock().unwrap();
|
||||||
|
transaction_queue.pending_hashes()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_sealing(&self, chain: &BlockChainClient) {
|
||||||
|
let no_of_transactions = 128;
|
||||||
|
let transactions = self.transaction_queue.lock().unwrap().top_transactions(no_of_transactions);
|
||||||
|
|
||||||
|
let b = chain.prepare_sealing(
|
||||||
|
self.author(),
|
||||||
|
self.extra_data(),
|
||||||
|
transactions,
|
||||||
|
);
|
||||||
|
*self.sealing_block.lock().unwrap() = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sealing_block(&self, chain: &BlockChainClient) -> &Mutex<Option<ClosedBlock>> {
|
||||||
|
if self.sealing_block.lock().unwrap().is_none() {
|
||||||
|
self.sealing_enabled.store(true, atomic::Ordering::Relaxed);
|
||||||
|
// TODO: Above should be on a timer that resets after two blocks have arrived without being asked for.
|
||||||
|
self.prepare_sealing(chain);
|
||||||
|
}
|
||||||
|
&self.sealing_block
|
||||||
|
}
|
||||||
|
|
||||||
|
fn submit_seal(&self, chain: &BlockChainClient, pow_hash: H256, seal: Vec<Bytes>) -> Result<(), Error> {
|
||||||
|
let mut maybe_b = self.sealing_block.lock().unwrap();
|
||||||
|
match *maybe_b {
|
||||||
|
Some(ref b) if b.hash() == pow_hash => {}
|
||||||
|
_ => { return Err(Error::PowHashInvalid); }
|
||||||
|
}
|
||||||
|
|
||||||
|
let b = maybe_b.take();
|
||||||
|
match chain.try_seal(b.unwrap(), seal) {
|
||||||
|
Err(old) => {
|
||||||
|
*maybe_b = Some(old);
|
||||||
|
Err(Error::PowInvalid)
|
||||||
|
}
|
||||||
|
Ok(sealed) => {
|
||||||
|
// TODO: commit DB from `sealed.drain` and make a VerifiedBlock to skip running the transactions twice.
|
||||||
|
try!(chain.import_block(sealed.rlp_bytes()));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn chain_new_blocks(&self, chain: &BlockChainClient, imported: &[H256], invalid: &[H256], enacted: &[H256], retracted: &[H256]) {
|
||||||
|
fn fetch_transactions(chain: &BlockChainClient, hash: &H256) -> Vec<SignedTransaction> {
|
||||||
|
let block = chain
|
||||||
|
.block(BlockId::Hash(*hash))
|
||||||
|
// Client should send message after commit to db and inserting to chain.
|
||||||
|
.expect("Expected in-chain blocks.");
|
||||||
|
let block = BlockView::new(&block);
|
||||||
|
block.transactions()
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let in_chain = vec![imported, enacted, invalid];
|
||||||
|
let in_chain = in_chain
|
||||||
|
.par_iter()
|
||||||
|
.flat_map(|h| h.par_iter().map(|h| fetch_transactions(chain, h)));
|
||||||
|
let out_of_chain = retracted
|
||||||
|
.par_iter()
|
||||||
|
.map(|h| fetch_transactions(chain, h));
|
||||||
|
|
||||||
|
in_chain.for_each(|txs| {
|
||||||
|
let mut transaction_queue = self.transaction_queue.lock().unwrap();
|
||||||
|
let hashes = txs.iter().map(|tx| tx.hash()).collect::<Vec<H256>>();
|
||||||
|
transaction_queue.remove_all(&hashes, |a| chain.nonce(a));
|
||||||
|
});
|
||||||
|
out_of_chain.for_each(|txs| {
|
||||||
|
// populate sender
|
||||||
|
for tx in &txs {
|
||||||
|
let _sender = tx.sender();
|
||||||
|
}
|
||||||
|
let mut transaction_queue = self.transaction_queue.lock().unwrap();
|
||||||
|
let _ = transaction_queue.add_all(txs, |a| chain.nonce(a));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.sealing_enabled.load(atomic::Ordering::Relaxed) {
|
||||||
|
self.prepare_sealing(chain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -28,13 +28,13 @@
|
|||||||
//! ```rust
|
//! ```rust
|
||||||
//! extern crate ethcore_util as util;
|
//! extern crate ethcore_util as util;
|
||||||
//! extern crate ethcore;
|
//! extern crate ethcore;
|
||||||
//! extern crate ethsync;
|
//! extern crate ethminer;
|
||||||
//! extern crate rustc_serialize;
|
//! extern crate rustc_serialize;
|
||||||
//!
|
//!
|
||||||
//! use util::crypto::KeyPair;
|
//! use util::crypto::KeyPair;
|
||||||
//! use util::hash::Address;
|
//! use util::hash::Address;
|
||||||
//! use util::numbers::{Uint, U256};
|
//! use util::numbers::{Uint, U256};
|
||||||
//! use ethsync::TransactionQueue;
|
//! use ethminer::TransactionQueue;
|
||||||
//! use ethcore::transaction::*;
|
//! use ethcore::transaction::*;
|
||||||
//! use rustc_serialize::hex::FromHex;
|
//! use rustc_serialize::hex::FromHex;
|
||||||
//!
|
//!
|
||||||
@ -86,7 +86,7 @@ use util::numbers::{Uint, U256};
|
|||||||
use util::hash::{Address, H256};
|
use util::hash::{Address, H256};
|
||||||
use util::table::*;
|
use util::table::*;
|
||||||
use ethcore::transaction::*;
|
use ethcore::transaction::*;
|
||||||
use ethcore::error::Error;
|
use ethcore::error::{Error, TransactionError};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -245,6 +245,8 @@ pub struct TransactionQueueStatus {
|
|||||||
|
|
||||||
/// TransactionQueue implementation
|
/// TransactionQueue implementation
|
||||||
pub struct TransactionQueue {
|
pub struct TransactionQueue {
|
||||||
|
/// Gas Price threshold for transactions that can be imported to this queue (defaults to 0)
|
||||||
|
minimal_gas_price: U256,
|
||||||
/// Priority queue for transactions that can go to block
|
/// Priority queue for transactions that can go to block
|
||||||
current: TransactionSet,
|
current: TransactionSet,
|
||||||
/// Priority queue for transactions that has been received but are not yet valid to go to block
|
/// Priority queue for transactions that has been received but are not yet valid to go to block
|
||||||
@ -281,6 +283,7 @@ impl TransactionQueue {
|
|||||||
};
|
};
|
||||||
|
|
||||||
TransactionQueue {
|
TransactionQueue {
|
||||||
|
minimal_gas_price: U256::zero(),
|
||||||
current: current,
|
current: current,
|
||||||
future: future,
|
future: future,
|
||||||
by_hash: HashMap::new(),
|
by_hash: HashMap::new(),
|
||||||
@ -288,6 +291,12 @@ impl TransactionQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets new gas price threshold for incoming transactions.
|
||||||
|
/// Any transactions already imported to the queue are not affected.
|
||||||
|
pub fn set_minimal_gas_price(&mut self, min_gas_price: U256) {
|
||||||
|
self.minimal_gas_price = min_gas_price;
|
||||||
|
}
|
||||||
|
|
||||||
// Will be used when rpc merged
|
// Will be used when rpc merged
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
/// Returns current status for this queue
|
/// Returns current status for this queue
|
||||||
@ -310,6 +319,19 @@ impl TransactionQueue {
|
|||||||
/// Add signed transaction to queue to be verified and imported
|
/// Add signed transaction to queue to be verified and imported
|
||||||
pub fn add<T>(&mut self, tx: SignedTransaction, fetch_nonce: &T) -> Result<(), Error>
|
pub fn add<T>(&mut self, tx: SignedTransaction, fetch_nonce: &T) -> Result<(), Error>
|
||||||
where T: Fn(&Address) -> U256 {
|
where T: Fn(&Address) -> U256 {
|
||||||
|
|
||||||
|
if tx.gas_price < self.minimal_gas_price {
|
||||||
|
trace!(target: "sync",
|
||||||
|
"Dropping transaction below minimal gas price threshold: {:?} (gp: {} < {})",
|
||||||
|
tx.hash(), tx.gas_price, self.minimal_gas_price
|
||||||
|
);
|
||||||
|
|
||||||
|
return Err(Error::Transaction(TransactionError::InsufficientGasPrice{
|
||||||
|
minimal: self.minimal_gas_price,
|
||||||
|
got: tx.gas_price
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
self.import_tx(try!(VerifiedTransaction::new(tx)), fetch_nonce);
|
self.import_tx(try!(VerifiedTransaction::new(tx)), fetch_nonce);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -346,7 +368,7 @@ impl TransactionQueue {
|
|||||||
self.update_future(&sender, current_nonce);
|
self.update_future(&sender, current_nonce);
|
||||||
// And now lets check if there is some chain of transactions in future
|
// And now lets check if there is some chain of transactions in future
|
||||||
// that should be placed in current
|
// that should be placed in current
|
||||||
self.move_matching_future_to_current(sender.clone(), current_nonce, current_nonce);
|
self.move_matching_future_to_current(sender, current_nonce, current_nonce);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,7 +384,7 @@ impl TransactionQueue {
|
|||||||
self.move_all_to_future(&sender, current_nonce);
|
self.move_all_to_future(&sender, current_nonce);
|
||||||
// And now lets check if there is some chain of transactions in future
|
// And now lets check if there is some chain of transactions in future
|
||||||
// that should be placed in current. It should also update last_nonces.
|
// that should be placed in current. It should also update last_nonces.
|
||||||
self.move_matching_future_to_current(sender.clone(), current_nonce, current_nonce);
|
self.move_matching_future_to_current(sender, current_nonce, current_nonce);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -377,7 +399,7 @@ impl TransactionQueue {
|
|||||||
for k in all_nonces_from_sender {
|
for k in all_nonces_from_sender {
|
||||||
let order = self.future.drop(&sender, &k).unwrap();
|
let order = self.future.drop(&sender, &k).unwrap();
|
||||||
if k >= current_nonce {
|
if k >= current_nonce {
|
||||||
self.future.insert(sender.clone(), k, order.update_height(k, current_nonce));
|
self.future.insert(*sender, k, order.update_height(k, current_nonce));
|
||||||
} else {
|
} else {
|
||||||
// Remove the transaction completely
|
// Remove the transaction completely
|
||||||
self.by_hash.remove(&order.hash);
|
self.by_hash.remove(&order.hash);
|
||||||
@ -397,7 +419,7 @@ impl TransactionQueue {
|
|||||||
// Goes to future or is removed
|
// Goes to future or is removed
|
||||||
let order = self.current.drop(&sender, &k).unwrap();
|
let order = self.current.drop(&sender, &k).unwrap();
|
||||||
if k >= current_nonce {
|
if k >= current_nonce {
|
||||||
self.future.insert(sender.clone(), k, order.update_height(k, current_nonce));
|
self.future.insert(*sender, k, order.update_height(k, current_nonce));
|
||||||
} else {
|
} else {
|
||||||
self.by_hash.remove(&order.hash);
|
self.by_hash.remove(&order.hash);
|
||||||
}
|
}
|
||||||
@ -417,6 +439,14 @@ impl TransactionQueue {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns hashes of all transactions from current, ordered by priority.
|
||||||
|
pub fn pending_hashes(&self) -> Vec<H256> {
|
||||||
|
self.current.by_priority
|
||||||
|
.iter()
|
||||||
|
.map(|t| t.hash)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
/// Removes all elements (in any state) from the queue
|
/// Removes all elements (in any state) from the queue
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.current.clear();
|
self.current.clear();
|
||||||
@ -438,8 +468,8 @@ impl TransactionQueue {
|
|||||||
// remove also from priority and hash
|
// remove also from priority and hash
|
||||||
self.future.by_priority.remove(&order);
|
self.future.by_priority.remove(&order);
|
||||||
// Put to current
|
// Put to current
|
||||||
let order = order.update_height(current_nonce.clone(), first_nonce);
|
let order = order.update_height(current_nonce, first_nonce);
|
||||||
self.current.insert(address.clone(), current_nonce, order);
|
self.current.insert(address, current_nonce, order);
|
||||||
current_nonce = current_nonce + U256::one();
|
current_nonce = current_nonce + U256::one();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -487,10 +517,10 @@ impl TransactionQueue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let base_nonce = fetch_nonce(&address);
|
let base_nonce = fetch_nonce(&address);
|
||||||
Self::replace_transaction(tx, base_nonce.clone(), &mut self.current, &mut self.by_hash);
|
Self::replace_transaction(tx, base_nonce, &mut self.current, &mut self.by_hash);
|
||||||
self.last_nonces.insert(address.clone(), nonce);
|
self.last_nonces.insert(address, nonce);
|
||||||
// But maybe there are some more items waiting in future?
|
// But maybe there are some more items waiting in future?
|
||||||
self.move_matching_future_to_current(address.clone(), nonce + U256::one(), base_nonce);
|
self.move_matching_future_to_current(address, nonce + U256::one(), base_nonce);
|
||||||
self.current.enforce_limit(&mut self.by_hash);
|
self.current.enforce_limit(&mut self.by_hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -504,7 +534,7 @@ impl TransactionQueue {
|
|||||||
let address = tx.sender();
|
let address = tx.sender();
|
||||||
let nonce = tx.nonce();
|
let nonce = tx.nonce();
|
||||||
|
|
||||||
by_hash.insert(hash.clone(), tx);
|
by_hash.insert(hash, tx);
|
||||||
if let Some(old) = set.insert(address, nonce, order.clone()) {
|
if let Some(old) = set.insert(address, nonce, order.clone()) {
|
||||||
// There was already transaction in queue. Let's check which one should stay
|
// There was already transaction in queue. Let's check which one should stay
|
||||||
let old_fee = old.gas_price;
|
let old_fee = old.gas_price;
|
||||||
@ -620,6 +650,22 @@ mod test {
|
|||||||
assert_eq!(stats.pending, 1);
|
assert_eq!(stats.pending, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_not_import_transaction_below_min_gas_price_threshold() {
|
||||||
|
// given
|
||||||
|
let mut txq = TransactionQueue::new();
|
||||||
|
let tx = new_tx();
|
||||||
|
txq.set_minimal_gas_price(tx.gas_price + U256::one());
|
||||||
|
|
||||||
|
// when
|
||||||
|
txq.add(tx, &default_nonce).unwrap_err();
|
||||||
|
|
||||||
|
// then
|
||||||
|
let stats = txq.status();
|
||||||
|
assert_eq!(stats.pending, 0);
|
||||||
|
assert_eq!(stats.future, 0);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_reject_incorectly_signed_transaction() {
|
fn should_reject_incorectly_signed_transaction() {
|
||||||
// given
|
// given
|
||||||
@ -663,6 +709,24 @@ mod test {
|
|||||||
assert_eq!(top.len(), 2);
|
assert_eq!(top.len(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_return_pending_hashes() {
|
||||||
|
// given
|
||||||
|
let mut txq = TransactionQueue::new();
|
||||||
|
|
||||||
|
let (tx, tx2) = new_txs(U256::from(1));
|
||||||
|
|
||||||
|
// when
|
||||||
|
txq.add(tx.clone(), &default_nonce).unwrap();
|
||||||
|
txq.add(tx2.clone(), &default_nonce).unwrap();
|
||||||
|
|
||||||
|
// then
|
||||||
|
let top = txq.pending_hashes();
|
||||||
|
assert_eq!(top[0], tx.hash());
|
||||||
|
assert_eq!(top[1], tx2.hash());
|
||||||
|
assert_eq!(top.len(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_put_transaction_to_futures_if_gap_detected() {
|
fn should_put_transaction_to_futures_if_gap_detected() {
|
||||||
// given
|
// given
|
||||||
@ -831,7 +895,7 @@ mod test {
|
|||||||
fn should_drop_transactions_with_old_nonces() {
|
fn should_drop_transactions_with_old_nonces() {
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::new();
|
||||||
let tx = new_tx();
|
let tx = new_tx();
|
||||||
let last_nonce = tx.nonce.clone() + U256::one();
|
let last_nonce = tx.nonce + U256::one();
|
||||||
let fetch_last_nonce = |_a: &Address| last_nonce;
|
let fetch_last_nonce = |_a: &Address| last_nonce;
|
||||||
|
|
||||||
// when
|
// when
|
264
parity/main.rs
264
parity/main.rs
@ -24,6 +24,7 @@ extern crate rustc_serialize;
|
|||||||
extern crate ethcore_util as util;
|
extern crate ethcore_util as util;
|
||||||
extern crate ethcore;
|
extern crate ethcore;
|
||||||
extern crate ethsync;
|
extern crate ethsync;
|
||||||
|
extern crate ethminer;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log as rlog;
|
extern crate log as rlog;
|
||||||
extern crate env_logger;
|
extern crate env_logger;
|
||||||
@ -50,6 +51,7 @@ use ethcore::client::*;
|
|||||||
use ethcore::service::{ClientService, NetSyncMessage};
|
use ethcore::service::{ClientService, NetSyncMessage};
|
||||||
use ethcore::ethereum;
|
use ethcore::ethereum;
|
||||||
use ethsync::{EthSync, SyncConfig, SyncProvider};
|
use ethsync::{EthSync, SyncConfig, SyncProvider};
|
||||||
|
use ethminer::{Miner, MinerService};
|
||||||
use docopt::Docopt;
|
use docopt::Docopt;
|
||||||
use daemonize::Daemonize;
|
use daemonize::Daemonize;
|
||||||
use number_prefix::{binary_prefix, Standalone, Prefixed};
|
use number_prefix::{binary_prefix, Standalone, Prefixed};
|
||||||
@ -76,70 +78,86 @@ Usage:
|
|||||||
parity [options]
|
parity [options]
|
||||||
|
|
||||||
Protocol Options:
|
Protocol Options:
|
||||||
--chain CHAIN Specify the blockchain type. CHAIN may be either a JSON chain specification file
|
--chain CHAIN Specify the blockchain type. CHAIN may be either a
|
||||||
or olympic, frontier, homestead, mainnet, morden, or testnet [default: homestead].
|
JSON chain specification file or olympic, frontier,
|
||||||
--testnet Equivalent to --chain testnet (geth-compatible).
|
homestead, mainnet, morden, or testnet
|
||||||
--networkid INDEX Override the network identifier from the chain we are on.
|
[default: homestead].
|
||||||
--pruning METHOD Configure pruning of the state/storage trie. METHOD may be one of: archive,
|
-d --db-path PATH Specify the database & configuration directory path
|
||||||
light (experimental), fast (experimental) [default: archive].
|
[default: $HOME/.parity].
|
||||||
-d --datadir PATH Specify the database & configuration directory path [default: $HOME/.parity]
|
--keys-path PATH Specify the path for JSON key files to be found
|
||||||
--db-path PATH Specify the database & configuration directory path [default: $HOME/.parity]
|
[default: $HOME/.web3/keys].
|
||||||
--keys-path PATH Specify the path for JSON key files to be found [default: $HOME/.web3/keys]
|
|
||||||
--identity NAME Specify your node's name.
|
--identity NAME Specify your node's name.
|
||||||
|
|
||||||
Networking Options:
|
Networking Options:
|
||||||
--port PORT Override the port on which the node should listen [default: 30303].
|
--port PORT Override the port on which the node should listen
|
||||||
|
[default: 30303].
|
||||||
--peers NUM Try to maintain that many peers [default: 25].
|
--peers NUM Try to maintain that many peers [default: 25].
|
||||||
--nat METHOD Specify method to use for determining public address. Must be one of: any, none,
|
--nat METHOD Specify method to use for determining public
|
||||||
upnp, extip:(IP) [default: any].
|
address. Must be one of: any, none, upnp,
|
||||||
--bootnodes NODES Specify additional comma-separated bootnodes.
|
extip:<IP> [default: any].
|
||||||
--no-bootstrap Don't bother trying to connect to standard bootnodes.
|
--network-id INDEX Override the network identifier from the chain we
|
||||||
|
are on.
|
||||||
|
--bootnodes NODES Override the bootnodes from our chain. NODES should
|
||||||
|
be comma-delimited enodes.
|
||||||
--no-discovery Disable new peer discovery.
|
--no-discovery Disable new peer discovery.
|
||||||
--node-key KEY Specify node secret key, either as 64-character hex string or input to SHA3 operation.
|
--node-key KEY Specify node secret key, either as 64-character hex
|
||||||
|
string or input to SHA3 operation.
|
||||||
|
|
||||||
API and Console Options:
|
API and Console Options:
|
||||||
-j --jsonrpc Enable the JSON-RPC API sever.
|
-j --jsonrpc Enable the JSON-RPC API sever.
|
||||||
--jsonrpc-addr HOST Specify the hostname portion of the JSONRPC API server [default: 127.0.0.1].
|
--jsonrpc-addr HOST Specify the hostname portion of the JSONRPC API
|
||||||
--jsonrpc-port PORT Specify the port portion of the JSONRPC API server [default: 8545].
|
server [default: 127.0.0.1].
|
||||||
--jsonrpc-cors URL Specify CORS header for JSON-RPC API responses [default: null].
|
--jsonrpc-port PORT Specify the port portion of the JSONRPC API server
|
||||||
--jsonrpc-apis APIS Specify the APIs available through the JSONRPC interface. APIS is a comma-delimited
|
[default: 8545].
|
||||||
list of API name. Possible name are web3, eth and net. [default: web3,eth,net,personal].
|
--jsonrpc-cors URL Specify CORS header for JSON-RPC API responses
|
||||||
|
[default: null].
|
||||||
--rpc Equivalent to --jsonrpc (geth-compatible).
|
--jsonrpc-apis APIS Specify the APIs available through the JSONRPC
|
||||||
--rpcaddr HOST Equivalent to --jsonrpc-addr HOST (geth-compatible).
|
interface. APIS is a comma-delimited list of API
|
||||||
--rpcport PORT Equivalent to --jsonrpc-port PORT (geth-compatible).
|
name. Possible name are web3, eth and net.
|
||||||
--rpcapi APIS Equivalent to --jsonrpc-apis APIS (geth-compatible).
|
[default: web3,eth,net,personal].
|
||||||
--rpccorsdomain URL Equivalent to --jsonrpc-cors URL (geth-compatible).
|
|
||||||
|
|
||||||
Sealing/Mining Options:
|
Sealing/Mining Options:
|
||||||
--author ADDRESS Specify the block author (aka "coinbase") address for sending block rewards
|
--gas-price WEI Minimum amount of Wei to be paid for a transaction
|
||||||
from sealed blocks [default: 0037a6b811ffeb6e072da21179d11b1406371c63].
|
to be accepted for mining [default: 20000000000].
|
||||||
--extra-data STRING Specify a custom extra-data for authored blocks, no more than 32 characters.
|
--author ADDRESS Specify the block author (aka "coinbase") address
|
||||||
|
for sending block rewards from sealed blocks
|
||||||
|
[default: 0037a6b811ffeb6e072da21179d11b1406371c63].
|
||||||
|
--extra-data STRING Specify a custom extra-data for authored blocks, no
|
||||||
|
more than 32 characters.
|
||||||
|
|
||||||
Memory Footprint Options:
|
Footprint Options:
|
||||||
--cache-pref-size BYTES Specify the prefered size of the blockchain cache in bytes [default: 16384].
|
--pruning METHOD Configure pruning of the state/storage trie. METHOD
|
||||||
--cache-max-size BYTES Specify the maximum size of the blockchain cache in bytes [default: 262144].
|
may be one of: archive, basic (experimental), fast
|
||||||
--queue-max-size BYTES Specify the maximum size of memory to use for block queue [default: 52428800].
|
(experimental) [default: archive].
|
||||||
--cache MEGABYTES Set total amount of cache to use for the entire system, mutually exclusive with
|
--cache-pref-size BYTES Specify the prefered size of the blockchain cache in
|
||||||
other cache options (geth-compatible).
|
bytes [default: 16384].
|
||||||
|
--cache-max-size BYTES Specify the maximum size of the blockchain cache in
|
||||||
|
bytes [default: 262144].
|
||||||
|
--queue-max-size BYTES Specify the maximum size of memory to use for block
|
||||||
|
queue [default: 52428800].
|
||||||
|
--cache MEGABYTES Set total amount of discretionary memory to use for
|
||||||
|
the entire system, overrides other cache and queue
|
||||||
|
options.
|
||||||
|
|
||||||
Geth-Compatibility Options
|
Geth-compatibility Options:
|
||||||
--datadir PATH Equivalent to --db-path PATH.
|
--datadir PATH Equivalent to --db-path PATH.
|
||||||
--testnet Equivalent to --chain testnet.
|
--testnet Equivalent to --chain testnet.
|
||||||
--networkid INDEX Override the network identifier from the chain we are on.
|
--networkid INDEX Equivalent to --network-id INDEX.
|
||||||
|
--maxpeers COUNT Equivalent to --peers COUNT.
|
||||||
|
--nodekey KEY Equivalent to --node-key KEY.
|
||||||
|
--nodiscover Equivalent to --no-discovery.
|
||||||
--rpc Equivalent to --jsonrpc.
|
--rpc Equivalent to --jsonrpc.
|
||||||
--rpcaddr HOST Equivalent to --jsonrpc-addr HOST.
|
--rpcaddr HOST Equivalent to --jsonrpc-addr HOST.
|
||||||
--rpcport PORT Equivalent to --jsonrpc-port PORT.
|
--rpcport PORT Equivalent to --jsonrpc-port PORT.
|
||||||
--rpcapi APIS Equivalent to --jsonrpc-apis APIS.
|
--rpcapi APIS Equivalent to --jsonrpc-apis APIS.
|
||||||
--rpccorsdomain URL Equivalent to --jsonrpc-cors URL.
|
--rpccorsdomain URL Equivalent to --jsonrpc-cors URL.
|
||||||
--maxpeers COUNT Equivalent to --peers COUNT.
|
--gasprice WEI Equivalent to --gas-price WEI.
|
||||||
--nodekey KEY Equivalent to --node-key KEY.
|
|
||||||
--nodiscover Equivalent to --no-discovery.
|
|
||||||
--etherbase ADDRESS Equivalent to --author ADDRESS.
|
--etherbase ADDRESS Equivalent to --author ADDRESS.
|
||||||
--extradata STRING Equivalent to --extra-data STRING.
|
--extradata STRING Equivalent to --extra-data STRING.
|
||||||
|
|
||||||
Miscellaneous Options:
|
Miscellaneous Options:
|
||||||
-l --logging LOGGING Specify the logging level.
|
-l --logging LOGGING Specify the logging level. Must conform to the same
|
||||||
|
format as RUST_LOG.
|
||||||
-v --version Show information about version.
|
-v --version Show information about version.
|
||||||
-h --help Show this screen.
|
-h --help Show this screen.
|
||||||
"#;
|
"#;
|
||||||
@ -157,8 +175,8 @@ struct Args {
|
|||||||
flag_cache: Option<usize>,
|
flag_cache: Option<usize>,
|
||||||
flag_keys_path: String,
|
flag_keys_path: String,
|
||||||
flag_bootnodes: Option<String>,
|
flag_bootnodes: Option<String>,
|
||||||
|
flag_network_id: Option<String>,
|
||||||
flag_pruning: String,
|
flag_pruning: String,
|
||||||
flag_no_bootstrap: bool,
|
|
||||||
flag_port: u16,
|
flag_port: u16,
|
||||||
flag_peers: usize,
|
flag_peers: usize,
|
||||||
flag_no_discovery: bool,
|
flag_no_discovery: bool,
|
||||||
@ -172,17 +190,19 @@ struct Args {
|
|||||||
flag_jsonrpc_port: u16,
|
flag_jsonrpc_port: u16,
|
||||||
flag_jsonrpc_cors: String,
|
flag_jsonrpc_cors: String,
|
||||||
flag_jsonrpc_apis: String,
|
flag_jsonrpc_apis: String,
|
||||||
|
flag_author: String,
|
||||||
|
flag_gas_price: String,
|
||||||
|
flag_extra_data: Option<String>,
|
||||||
flag_logging: Option<String>,
|
flag_logging: Option<String>,
|
||||||
flag_version: bool,
|
flag_version: bool,
|
||||||
// geth-compatibility...
|
// geth-compatibility...
|
||||||
flag_nodekey: Option<String>,
|
flag_nodekey: Option<String>,
|
||||||
flag_nodiscover: bool,
|
flag_nodiscover: bool,
|
||||||
flag_maxpeers: Option<usize>,
|
flag_maxpeers: Option<usize>,
|
||||||
flag_author: String,
|
|
||||||
flag_extra_data: Option<String>,
|
|
||||||
flag_datadir: Option<String>,
|
flag_datadir: Option<String>,
|
||||||
flag_extradata: Option<String>,
|
flag_extradata: Option<String>,
|
||||||
flag_etherbase: Option<String>,
|
flag_etherbase: Option<String>,
|
||||||
|
flag_gasprice: Option<String>,
|
||||||
flag_rpc: bool,
|
flag_rpc: bool,
|
||||||
flag_rpcaddr: Option<String>,
|
flag_rpcaddr: Option<String>,
|
||||||
flag_rpcport: Option<u16>,
|
flag_rpcport: Option<u16>,
|
||||||
@ -219,7 +239,15 @@ fn setup_log(init: &Option<String>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rpc")]
|
#[cfg(feature = "rpc")]
|
||||||
fn setup_rpc_server(client: Arc<Client>, sync: Arc<EthSync>, secret_store: Arc<AccountService>, url: &str, cors_domain: &str, apis: Vec<&str>) -> Option<Arc<PanicHandler>> {
|
fn setup_rpc_server(
|
||||||
|
client: Arc<Client>,
|
||||||
|
sync: Arc<EthSync>,
|
||||||
|
secret_store: Arc<AccountService>,
|
||||||
|
miner: Arc<Miner>,
|
||||||
|
url: &str,
|
||||||
|
cors_domain: &str,
|
||||||
|
apis: Vec<&str>
|
||||||
|
) -> Option<Arc<PanicHandler>> {
|
||||||
use rpc::v1::*;
|
use rpc::v1::*;
|
||||||
|
|
||||||
let server = rpc::RpcServer::new();
|
let server = rpc::RpcServer::new();
|
||||||
@ -228,8 +256,8 @@ fn setup_rpc_server(client: Arc<Client>, sync: Arc<EthSync>, secret_store: Arc<A
|
|||||||
"web3" => server.add_delegate(Web3Client::new().to_delegate()),
|
"web3" => server.add_delegate(Web3Client::new().to_delegate()),
|
||||||
"net" => server.add_delegate(NetClient::new(&sync).to_delegate()),
|
"net" => server.add_delegate(NetClient::new(&sync).to_delegate()),
|
||||||
"eth" => {
|
"eth" => {
|
||||||
server.add_delegate(EthClient::new(&client, &sync, &secret_store).to_delegate());
|
server.add_delegate(EthClient::new(&client, &sync, &secret_store, &miner).to_delegate());
|
||||||
server.add_delegate(EthFilterClient::new(&client).to_delegate());
|
server.add_delegate(EthFilterClient::new(&client, &miner).to_delegate());
|
||||||
}
|
}
|
||||||
"personal" => server.add_delegate(PersonalClient::new(&secret_store).to_delegate()),
|
"personal" => server.add_delegate(PersonalClient::new(&secret_store).to_delegate()),
|
||||||
_ => {
|
_ => {
|
||||||
@ -241,7 +269,15 @@ fn setup_rpc_server(client: Arc<Client>, sync: Arc<EthSync>, secret_store: Arc<A
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "rpc"))]
|
#[cfg(not(feature = "rpc"))]
|
||||||
fn setup_rpc_server(_client: Arc<Client>, _sync: Arc<EthSync>, _url: &str) -> Option<Arc<PanicHandler>> {
|
fn setup_rpc_server(
|
||||||
|
_client: Arc<Client>,
|
||||||
|
_sync: Arc<EthSync>,
|
||||||
|
_secret_store: Arc<AccountService>,
|
||||||
|
_miner: Arc<Miner>,
|
||||||
|
_url: &str,
|
||||||
|
_cors_domain: &str,
|
||||||
|
_apis: Vec<&str>
|
||||||
|
) -> Option<Arc<PanicHandler>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,7 +312,16 @@ impl Configuration {
|
|||||||
|
|
||||||
fn author(&self) -> Address {
|
fn author(&self) -> Address {
|
||||||
let d = self.args.flag_etherbase.as_ref().unwrap_or(&self.args.flag_author);
|
let d = self.args.flag_etherbase.as_ref().unwrap_or(&self.args.flag_author);
|
||||||
Address::from_str(d).unwrap_or_else(|_| die!("{}: Invalid address for --author. Must be 40 hex characters, without the 0x at the beginning.", self.args.flag_author))
|
Address::from_str(d).unwrap_or_else(|_| {
|
||||||
|
die!("{}: Invalid address for --author. Must be 40 hex characters, without the 0x at the beginning.", d)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gas_price(&self) -> U256 {
|
||||||
|
let d = self.args.flag_gasprice.as_ref().unwrap_or(&self.args.flag_gas_price);
|
||||||
|
U256::from_dec_str(d).unwrap_or_else(|_| {
|
||||||
|
die!("{}: Invalid gas price given. Must be a decimal unsigned 256-bit number.", d)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extra_data(&self) -> Bytes {
|
fn extra_data(&self) -> Bytes {
|
||||||
@ -299,7 +344,9 @@ impl Configuration {
|
|||||||
"frontier" | "homestead" | "mainnet" => ethereum::new_frontier(),
|
"frontier" | "homestead" | "mainnet" => ethereum::new_frontier(),
|
||||||
"morden" | "testnet" => ethereum::new_morden(),
|
"morden" | "testnet" => ethereum::new_morden(),
|
||||||
"olympic" => ethereum::new_olympic(),
|
"olympic" => ethereum::new_olympic(),
|
||||||
f => Spec::from_json_utf8(contents(f).unwrap_or_else(|_| die!("{}: Couldn't read chain specification file. Sure it exists?", f)).as_ref()),
|
f => Spec::from_json_utf8(contents(f).unwrap_or_else(|_| {
|
||||||
|
die!("{}: Couldn't read chain specification file. Sure it exists?", f)
|
||||||
|
}).as_ref()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,11 +359,15 @@ impl Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn init_nodes(&self, spec: &Spec) -> Vec<String> {
|
fn init_nodes(&self, spec: &Spec) -> Vec<String> {
|
||||||
let mut r = if self.args.flag_no_bootstrap { Vec::new() } else { spec.nodes().clone() };
|
match self.args.flag_bootnodes {
|
||||||
if let Some(ref x) = self.args.flag_bootnodes {
|
Some(ref x) if x.len() > 0 => x.split(',').map(|s| {
|
||||||
r.extend(x.split(",").map(|s| Self::normalize_enode(s).unwrap_or_else(|| die!("{}: Invalid node address format given for a boot node.", s))));
|
Self::normalize_enode(s).unwrap_or_else(|| {
|
||||||
|
die!("{}: Invalid node address format given for a boot node.", s)
|
||||||
|
})
|
||||||
|
}).collect(),
|
||||||
|
Some(_) => Vec::new(),
|
||||||
|
None => spec.nodes().clone(),
|
||||||
}
|
}
|
||||||
r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(useless_format))]
|
#[cfg_attr(feature="dev", allow(useless_format))]
|
||||||
@ -327,7 +378,7 @@ impl Configuration {
|
|||||||
let host = IpAddr::from_str(host).unwrap_or_else(|_| die!("Invalid host given with `--nat extip:{}`", host));
|
let host = IpAddr::from_str(host).unwrap_or_else(|_| die!("Invalid host given with `--nat extip:{}`", host));
|
||||||
Some(SocketAddr::new(host, self.args.flag_port))
|
Some(SocketAddr::new(host, self.args.flag_port))
|
||||||
} else {
|
} else {
|
||||||
listen_address.clone()
|
listen_address
|
||||||
};
|
};
|
||||||
(listen_address, public_address)
|
(listen_address, public_address)
|
||||||
}
|
}
|
||||||
@ -348,6 +399,38 @@ impl Configuration {
|
|||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn client_config(&self) -> ClientConfig {
|
||||||
|
let mut client_config = ClientConfig::default();
|
||||||
|
match self.args.flag_cache {
|
||||||
|
Some(mb) => {
|
||||||
|
client_config.blockchain.max_cache_size = mb * 1024 * 1024;
|
||||||
|
client_config.blockchain.pref_cache_size = client_config.blockchain.max_cache_size * 3 / 4;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
client_config.blockchain.pref_cache_size = self.args.flag_cache_pref_size;
|
||||||
|
client_config.blockchain.max_cache_size = self.args.flag_cache_max_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
client_config.pruning = match self.args.flag_pruning.as_str() {
|
||||||
|
"archive" => journaldb::Algorithm::Archive,
|
||||||
|
"light" => journaldb::Algorithm::EarlyMerge,
|
||||||
|
"fast" => journaldb::Algorithm::OverlayRecent,
|
||||||
|
"basic" => journaldb::Algorithm::RefCounted,
|
||||||
|
_ => { die!("Invalid pruning method given."); }
|
||||||
|
};
|
||||||
|
client_config.name = self.args.flag_identity.clone();
|
||||||
|
client_config.queue.max_mem_use = self.args.flag_queue_max_size;
|
||||||
|
client_config
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sync_config(&self, spec: &Spec) -> SyncConfig {
|
||||||
|
let mut sync_config = SyncConfig::default();
|
||||||
|
sync_config.network_id = self.args.flag_network_id.as_ref().or(self.args.flag_networkid.as_ref()).map_or(spec.network_id(), |id| {
|
||||||
|
U256::from_str(id).unwrap_or_else(|_| die!("{}: Invalid index given with --network-id/--networkid", id))
|
||||||
|
});
|
||||||
|
sync_config
|
||||||
|
}
|
||||||
|
|
||||||
fn execute(&self) {
|
fn execute(&self) {
|
||||||
if self.args.flag_version {
|
if self.args.flag_version {
|
||||||
print_version();
|
print_version();
|
||||||
@ -388,12 +471,13 @@ impl Configuration {
|
|||||||
}
|
}
|
||||||
if self.args.cmd_list {
|
if self.args.cmd_list {
|
||||||
println!("Known addresses:");
|
println!("Known addresses:");
|
||||||
for &(addr, _) in secret_store.accounts().unwrap().iter() {
|
for &(addr, _) in &secret_store.accounts().unwrap() {
|
||||||
println!("{:?}", addr);
|
println!("{:?}", addr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature="dev", allow(useless_format))]
|
||||||
fn execute_client(&self) {
|
fn execute_client(&self) {
|
||||||
// Setup panic handler
|
// Setup panic handler
|
||||||
let panic_handler = PanicHandler::new_in_arc();
|
let panic_handler = PanicHandler::new_in_arc();
|
||||||
@ -405,39 +489,21 @@ impl Configuration {
|
|||||||
|
|
||||||
let spec = self.spec();
|
let spec = self.spec();
|
||||||
let net_settings = self.net_settings(&spec);
|
let net_settings = self.net_settings(&spec);
|
||||||
let mut sync_config = SyncConfig::default();
|
let sync_config = self.sync_config(&spec);
|
||||||
sync_config.network_id = self.args.flag_networkid.as_ref().map(|id| U256::from_str(id).unwrap_or_else(|_| die!("{}: Invalid index given with --networkid", id))).unwrap_or(spec.network_id());
|
|
||||||
|
|
||||||
// Build client
|
// Build client
|
||||||
let mut client_config = ClientConfig::default();
|
let mut service = ClientService::start(self.client_config(), spec, net_settings, &Path::new(&self.path())).unwrap();
|
||||||
match self.args.flag_cache {
|
|
||||||
Some(mb) => {
|
|
||||||
client_config.blockchain.max_cache_size = mb * 1024 * 1024;
|
|
||||||
client_config.blockchain.pref_cache_size = client_config.blockchain.max_cache_size / 2;
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
client_config.blockchain.pref_cache_size = self.args.flag_cache_pref_size;
|
|
||||||
client_config.blockchain.max_cache_size = self.args.flag_cache_max_size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
client_config.pruning = match self.args.flag_pruning.as_str() {
|
|
||||||
"" => journaldb::Algorithm::Archive,
|
|
||||||
"archive" => journaldb::Algorithm::Archive,
|
|
||||||
"pruned" => journaldb::Algorithm::EarlyMerge,
|
|
||||||
"fast" => journaldb::Algorithm::OverlayRecent,
|
|
||||||
// "slow" => journaldb::Algorithm::RefCounted, // TODO: @gavofyork uncomment this once ref-count algo is merged.
|
|
||||||
_ => { die!("Invalid pruning method given."); }
|
|
||||||
};
|
|
||||||
client_config.name = self.args.flag_identity.clone();
|
|
||||||
client_config.queue.max_mem_use = self.args.flag_queue_max_size;
|
|
||||||
let mut service = ClientService::start(client_config, spec, net_settings, &Path::new(&self.path())).unwrap();
|
|
||||||
panic_handler.forward_from(&service);
|
panic_handler.forward_from(&service);
|
||||||
let client = service.client().clone();
|
let client = service.client();
|
||||||
client.set_author(self.author());
|
|
||||||
client.set_extra_data(self.extra_data());
|
// Miner
|
||||||
|
let miner = Miner::new();
|
||||||
|
miner.set_author(self.author());
|
||||||
|
miner.set_extra_data(self.extra_data());
|
||||||
|
miner.set_minimal_gas_price(self.gas_price());
|
||||||
|
|
||||||
// Sync
|
// Sync
|
||||||
let sync = EthSync::register(service.network(), sync_config, client);
|
let sync = EthSync::register(service.network(), sync_config, client.clone(), miner.clone());
|
||||||
|
|
||||||
// Secret Store
|
// Secret Store
|
||||||
let account_service = Arc::new(AccountService::new());
|
let account_service = Arc::new(AccountService::new());
|
||||||
@ -452,11 +518,18 @@ impl Configuration {
|
|||||||
let cors = self.args.flag_rpccorsdomain.as_ref().unwrap_or(&self.args.flag_jsonrpc_cors);
|
let cors = self.args.flag_rpccorsdomain.as_ref().unwrap_or(&self.args.flag_jsonrpc_cors);
|
||||||
// TODO: use this as the API list.
|
// TODO: use this as the API list.
|
||||||
let apis = self.args.flag_rpcapi.as_ref().unwrap_or(&self.args.flag_jsonrpc_apis);
|
let apis = self.args.flag_rpcapi.as_ref().unwrap_or(&self.args.flag_jsonrpc_apis);
|
||||||
let server_handler = setup_rpc_server(service.client(), sync.clone(), account_service.clone(), &url, cors, apis.split(",").collect());
|
let server_handler = setup_rpc_server(
|
||||||
|
service.client(),
|
||||||
|
sync.clone(),
|
||||||
|
account_service.clone(),
|
||||||
|
miner.clone(),
|
||||||
|
&url,
|
||||||
|
cors,
|
||||||
|
apis.split(',').collect()
|
||||||
|
);
|
||||||
if let Some(handler) = server_handler {
|
if let Some(handler) = server_handler {
|
||||||
panic_handler.forward_from(handler.deref());
|
panic_handler.forward_from(handler.deref());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register IO handler
|
// Register IO handler
|
||||||
@ -464,6 +537,7 @@ impl Configuration {
|
|||||||
client: service.client(),
|
client: service.client(),
|
||||||
info: Default::default(),
|
info: Default::default(),
|
||||||
sync: sync.clone(),
|
sync: sync.clone(),
|
||||||
|
accounts: account_service.clone(),
|
||||||
});
|
});
|
||||||
service.io().register_handler(io_handler).expect("Error registering IO handler");
|
service.io().register_handler(io_handler).expect("Error registering IO handler");
|
||||||
|
|
||||||
@ -526,7 +600,11 @@ impl Informant {
|
|||||||
let report = client.report();
|
let report = client.report();
|
||||||
let sync_info = sync.status();
|
let sync_info = sync.status();
|
||||||
|
|
||||||
if let (_, _, &Some(ref last_report)) = (self.chain_info.read().unwrap().deref(), self.cache_info.read().unwrap().deref(), self.report.read().unwrap().deref()) {
|
if let (_, _, &Some(ref last_report)) = (
|
||||||
|
self.chain_info.read().unwrap().deref(),
|
||||||
|
self.cache_info.read().unwrap().deref(),
|
||||||
|
self.report.read().unwrap().deref()
|
||||||
|
) {
|
||||||
println!("[ #{} {} ]---[ {} blk/s | {} tx/s | {} gas/s //··· {}/{} peers, #{}, {}+{} queued ···// mem: {} db, {} chain, {} queue, {} sync ]",
|
println!("[ #{} {} ]---[ {} blk/s | {} tx/s | {} gas/s //··· {}/{} peers, #{}, {}+{} queued ···// mem: {} db, {} chain, {} queue, {} sync ]",
|
||||||
chain_info.best_block_number,
|
chain_info.best_block_number,
|
||||||
chain_info.best_block_hash,
|
chain_info.best_block_hash,
|
||||||
@ -555,20 +633,28 @@ impl Informant {
|
|||||||
|
|
||||||
const INFO_TIMER: TimerToken = 0;
|
const INFO_TIMER: TimerToken = 0;
|
||||||
|
|
||||||
|
const ACCOUNT_TICK_TIMER: TimerToken = 10;
|
||||||
|
const ACCOUNT_TICK_MS: u64 = 60000;
|
||||||
|
|
||||||
struct ClientIoHandler {
|
struct ClientIoHandler {
|
||||||
client: Arc<Client>,
|
client: Arc<Client>,
|
||||||
sync: Arc<EthSync>,
|
sync: Arc<EthSync>,
|
||||||
|
accounts: Arc<AccountService>,
|
||||||
info: Informant,
|
info: Informant,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IoHandler<NetSyncMessage> for ClientIoHandler {
|
impl IoHandler<NetSyncMessage> for ClientIoHandler {
|
||||||
fn initialize(&self, io: &IoContext<NetSyncMessage>) {
|
fn initialize(&self, io: &IoContext<NetSyncMessage>) {
|
||||||
io.register_timer(INFO_TIMER, 5000).expect("Error registering timer");
|
io.register_timer(INFO_TIMER, 5000).expect("Error registering timer");
|
||||||
|
io.register_timer(ACCOUNT_TICK_TIMER, ACCOUNT_TICK_MS).expect("Error registering account timer");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn timeout(&self, _io: &IoContext<NetSyncMessage>, timer: TimerToken) {
|
fn timeout(&self, _io: &IoContext<NetSyncMessage>, timer: TimerToken) {
|
||||||
if INFO_TIMER == timer {
|
match timer {
|
||||||
self.info.tick(&self.client, &self.sync);
|
INFO_TIMER => { self.info.tick(&self.client, &self.sync); }
|
||||||
|
ACCOUNT_TICK_TIMER => { self.accounts.tick(); },
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,11 @@ ethcore-util = { path = "../util" }
|
|||||||
ethcore = { path = "../ethcore" }
|
ethcore = { path = "../ethcore" }
|
||||||
ethash = { path = "../ethash" }
|
ethash = { path = "../ethash" }
|
||||||
ethsync = { path = "../sync" }
|
ethsync = { path = "../sync" }
|
||||||
clippy = { version = "0.0.49", optional = true }
|
ethminer = { path = "../miner" }
|
||||||
rustc-serialize = "0.3"
|
rustc-serialize = "0.3"
|
||||||
transient-hashmap = "0.1"
|
transient-hashmap = "0.1"
|
||||||
serde_macros = { version = "0.7.0", optional = true }
|
serde_macros = { version = "0.7.0", optional = true }
|
||||||
|
clippy = { version = "0.0.50", optional = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
serde_codegen = { version = "0.7.0", optional = true }
|
serde_codegen = { version = "0.7.0", optional = true }
|
||||||
@ -30,4 +31,4 @@ syntex = "0.29.0"
|
|||||||
[features]
|
[features]
|
||||||
default = ["serde_codegen"]
|
default = ["serde_codegen"]
|
||||||
nightly = ["serde_macros"]
|
nightly = ["serde_macros"]
|
||||||
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev"]
|
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethminer/dev"]
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
#![cfg_attr(feature="nightly", feature(custom_derive, custom_attribute, plugin))]
|
#![cfg_attr(feature="nightly", feature(custom_derive, custom_attribute, plugin))]
|
||||||
#![cfg_attr(feature="nightly", plugin(serde_macros, clippy))]
|
#![cfg_attr(feature="nightly", plugin(serde_macros, clippy))]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
extern crate rustc_serialize;
|
extern crate rustc_serialize;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
@ -27,6 +29,7 @@ extern crate jsonrpc_http_server;
|
|||||||
extern crate ethcore_util as util;
|
extern crate ethcore_util as util;
|
||||||
extern crate ethcore;
|
extern crate ethcore;
|
||||||
extern crate ethsync;
|
extern crate ethsync;
|
||||||
|
extern crate ethminer;
|
||||||
extern crate transient_hashmap;
|
extern crate transient_hashmap;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
//! Helper type with all filter possibilities.
|
//! Helper type with all filter possibilities.
|
||||||
|
|
||||||
|
use util::hash::H256;
|
||||||
use ethcore::filter::Filter;
|
use ethcore::filter::Filter;
|
||||||
|
|
||||||
|
pub type BlockNumber = u64;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum PollFilter {
|
pub enum PollFilter {
|
||||||
Block,
|
Block(BlockNumber),
|
||||||
PendingTransaction,
|
PendingTransaction(Vec<H256>),
|
||||||
Logs(Filter)
|
Logs(BlockNumber, Filter)
|
||||||
}
|
}
|
||||||
|
@ -22,28 +22,13 @@ use transient_hashmap::{TransientHashMap, Timer, StandardTimer};
|
|||||||
const POLL_LIFETIME: u64 = 60;
|
const POLL_LIFETIME: u64 = 60;
|
||||||
|
|
||||||
pub type PollId = usize;
|
pub type PollId = usize;
|
||||||
pub type BlockNumber = u64;
|
|
||||||
|
|
||||||
pub struct PollInfo<F> {
|
|
||||||
pub filter: F,
|
|
||||||
pub block_number: BlockNumber
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F> Clone for PollInfo<F> where F: Clone {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
PollInfo {
|
|
||||||
filter: self.filter.clone(),
|
|
||||||
block_number: self.block_number.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Indexes all poll requests.
|
/// Indexes all poll requests.
|
||||||
///
|
///
|
||||||
/// Lazily garbage collects unused polls info.
|
/// Lazily garbage collects unused polls info.
|
||||||
pub struct PollManager<F, T = StandardTimer> where T: Timer {
|
pub struct PollManager<F, T = StandardTimer> where T: Timer {
|
||||||
polls: TransientHashMap<PollId, PollInfo<F>, T>,
|
polls: TransientHashMap<PollId, F, T>,
|
||||||
next_available_id: PollId
|
next_available_id: PollId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F> PollManager<F, StandardTimer> {
|
impl<F> PollManager<F, StandardTimer> {
|
||||||
@ -54,6 +39,7 @@ impl<F> PollManager<F, StandardTimer> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<F, T> PollManager<F, T> where T: Timer {
|
impl<F, T> PollManager<F, T> where T: Timer {
|
||||||
|
|
||||||
pub fn new_with_timer(timer: T) -> Self {
|
pub fn new_with_timer(timer: T) -> Self {
|
||||||
PollManager {
|
PollManager {
|
||||||
polls: TransientHashMap::new_with_timer(POLL_LIFETIME, timer),
|
polls: TransientHashMap::new_with_timer(POLL_LIFETIME, timer),
|
||||||
@ -64,31 +50,30 @@ impl<F, T> PollManager<F, T> where T: Timer {
|
|||||||
/// Returns id which can be used for new poll.
|
/// Returns id which can be used for new poll.
|
||||||
///
|
///
|
||||||
/// Stores information when last poll happend.
|
/// Stores information when last poll happend.
|
||||||
pub fn create_poll(&mut self, filter: F, block: BlockNumber) -> PollId {
|
pub fn create_poll(&mut self, filter: F) -> PollId {
|
||||||
self.polls.prune();
|
self.polls.prune();
|
||||||
|
|
||||||
let id = self.next_available_id;
|
let id = self.next_available_id;
|
||||||
|
self.polls.insert(id, filter);
|
||||||
|
|
||||||
self.next_available_id += 1;
|
self.next_available_id += 1;
|
||||||
self.polls.insert(id, PollInfo {
|
|
||||||
filter: filter,
|
|
||||||
block_number: block,
|
|
||||||
});
|
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates information when last poll happend.
|
// Implementation is always using `poll_mut`
|
||||||
pub fn update_poll(&mut self, id: &PollId, block: BlockNumber) {
|
#[cfg(test)]
|
||||||
self.polls.prune();
|
/// Get a reference to stored poll filter
|
||||||
if let Some(info) = self.polls.get_mut(id) {
|
pub fn poll(&mut self, id: &PollId) -> Option<&F> {
|
||||||
info.block_number = block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns number of block when last poll happend.
|
|
||||||
pub fn poll_info(&mut self, id: &PollId) -> Option<&PollInfo<F>> {
|
|
||||||
self.polls.prune();
|
self.polls.prune();
|
||||||
self.polls.get(id)
|
self.polls.get(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to stored poll filter
|
||||||
|
pub fn poll_mut(&mut self, id: &PollId) -> Option<&mut F> {
|
||||||
|
self.polls.prune();
|
||||||
|
self.polls.get_mut(id)
|
||||||
|
}
|
||||||
|
|
||||||
/// Removes poll info.
|
/// Removes poll info.
|
||||||
pub fn remove_poll(&mut self, id: &PollId) {
|
pub fn remove_poll(&mut self, id: &PollId) {
|
||||||
self.polls.remove(id);
|
self.polls.remove(id);
|
||||||
@ -97,48 +82,46 @@ impl<F, T> PollManager<F, T> where T: Timer {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::cell::RefCell;
|
use std::cell::Cell;
|
||||||
use transient_hashmap::Timer;
|
use transient_hashmap::Timer;
|
||||||
use v1::helpers::PollManager;
|
use v1::helpers::PollManager;
|
||||||
|
|
||||||
struct TestTimer<'a> {
|
struct TestTimer<'a> {
|
||||||
time: &'a RefCell<i64>,
|
time: &'a Cell<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Timer for TestTimer<'a> {
|
impl<'a> Timer for TestTimer<'a> {
|
||||||
fn get_time(&self) -> i64 {
|
fn get_time(&self) -> i64 {
|
||||||
*self.time.borrow()
|
self.time.get()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_poll_indexer() {
|
fn test_poll_indexer() {
|
||||||
let time = RefCell::new(0);
|
let time = Cell::new(0);
|
||||||
let timer = TestTimer {
|
let timer = TestTimer {
|
||||||
time: &time,
|
time: &time,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut indexer = PollManager::new_with_timer(timer);
|
let mut indexer = PollManager::new_with_timer(timer);
|
||||||
assert_eq!(indexer.create_poll(false, 20), 0);
|
assert_eq!(indexer.create_poll(20), 0);
|
||||||
assert_eq!(indexer.create_poll(true, 20), 1);
|
assert_eq!(indexer.create_poll(20), 1);
|
||||||
|
|
||||||
*time.borrow_mut() = 10;
|
time.set(10);
|
||||||
indexer.update_poll(&0, 21);
|
*indexer.poll_mut(&0).unwrap() = 21;
|
||||||
assert_eq!(indexer.poll_info(&0).unwrap().filter, false);
|
assert_eq!(*indexer.poll(&0).unwrap(), 21);
|
||||||
assert_eq!(indexer.poll_info(&0).unwrap().block_number, 21);
|
assert_eq!(*indexer.poll(&1).unwrap(), 20);
|
||||||
|
|
||||||
*time.borrow_mut() = 30;
|
time.set(30);
|
||||||
indexer.update_poll(&1, 23);
|
*indexer.poll_mut(&1).unwrap() = 23;
|
||||||
assert_eq!(indexer.poll_info(&1).unwrap().filter, true);
|
assert_eq!(*indexer.poll(&1).unwrap(), 23);
|
||||||
assert_eq!(indexer.poll_info(&1).unwrap().block_number, 23);
|
|
||||||
|
|
||||||
*time.borrow_mut() = 75;
|
time.set(75);
|
||||||
indexer.update_poll(&0, 30);
|
assert!(indexer.poll(&0).is_none());
|
||||||
assert!(indexer.poll_info(&0).is_none());
|
assert_eq!(*indexer.poll(&1).unwrap(), 23);
|
||||||
assert_eq!(indexer.poll_info(&1).unwrap().filter, true);
|
|
||||||
assert_eq!(indexer.poll_info(&1).unwrap().block_number, 23);
|
|
||||||
|
|
||||||
indexer.remove_poll(&1);
|
indexer.remove_poll(&1);
|
||||||
assert!(indexer.poll_info(&1).is_none());
|
assert!(indexer.poll(&1).is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,11 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Eth rpc implementation.
|
//! Eth rpc implementation.
|
||||||
use std::collections::HashMap;
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::sync::{Arc, Weak, Mutex, RwLock};
|
use std::sync::{Arc, Weak, Mutex, RwLock};
|
||||||
|
use std::ops::Deref;
|
||||||
use ethsync::{SyncProvider, SyncState};
|
use ethsync::{SyncProvider, SyncState};
|
||||||
|
use ethminer::{MinerService};
|
||||||
use jsonrpc_core::*;
|
use jsonrpc_core::*;
|
||||||
use util::numbers::*;
|
use util::numbers::*;
|
||||||
use util::sha3::*;
|
use util::sha3::*;
|
||||||
@ -34,19 +36,29 @@ use v1::helpers::{PollFilter, PollManager};
|
|||||||
use util::keys::store::AccountProvider;
|
use util::keys::store::AccountProvider;
|
||||||
|
|
||||||
/// Eth rpc implementation.
|
/// Eth rpc implementation.
|
||||||
pub struct EthClient<C, S, A> where C: BlockChainClient, S: SyncProvider, A: AccountProvider {
|
pub struct EthClient<C, S, A, M>
|
||||||
|
where C: BlockChainClient,
|
||||||
|
S: SyncProvider,
|
||||||
|
A: AccountProvider,
|
||||||
|
M: MinerService {
|
||||||
client: Weak<C>,
|
client: Weak<C>,
|
||||||
sync: Weak<S>,
|
sync: Weak<S>,
|
||||||
accounts: Weak<A>,
|
accounts: Weak<A>,
|
||||||
|
miner: Weak<M>,
|
||||||
hashrates: RwLock<HashMap<H256, u64>>,
|
hashrates: RwLock<HashMap<H256, u64>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, S, A> EthClient<C, S, A> where C: BlockChainClient, S: SyncProvider, A: AccountProvider {
|
impl<C, S, A, M> EthClient<C, S, A, M>
|
||||||
|
where C: BlockChainClient,
|
||||||
|
S: SyncProvider,
|
||||||
|
A: AccountProvider,
|
||||||
|
M: MinerService {
|
||||||
/// Creates new EthClient.
|
/// Creates new EthClient.
|
||||||
pub fn new(client: &Arc<C>, sync: &Arc<S>, accounts: &Arc<A>) -> Self {
|
pub fn new(client: &Arc<C>, sync: &Arc<S>, accounts: &Arc<A>, miner: &Arc<M>) -> Self {
|
||||||
EthClient {
|
EthClient {
|
||||||
client: Arc::downgrade(client),
|
client: Arc::downgrade(client),
|
||||||
sync: Arc::downgrade(sync),
|
sync: Arc::downgrade(sync),
|
||||||
|
miner: Arc::downgrade(miner),
|
||||||
accounts: Arc::downgrade(accounts),
|
accounts: Arc::downgrade(accounts),
|
||||||
hashrates: RwLock::new(HashMap::new()),
|
hashrates: RwLock::new(HashMap::new()),
|
||||||
}
|
}
|
||||||
@ -98,10 +110,15 @@ impl<C, S, A> EthClient<C, S, A> where C: BlockChainClient, S: SyncProvider, A:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, S, A> Eth for EthClient<C, S, A> where C: BlockChainClient + 'static, S: SyncProvider + 'static, A: AccountProvider + 'static {
|
impl<C, S, A, M> Eth for EthClient<C, S, A, M>
|
||||||
|
where C: BlockChainClient + 'static,
|
||||||
|
S: SyncProvider + 'static,
|
||||||
|
A: AccountProvider + 'static,
|
||||||
|
M: MinerService + 'static {
|
||||||
|
|
||||||
fn protocol_version(&self, params: Params) -> Result<Value, Error> {
|
fn protocol_version(&self, params: Params) -> Result<Value, Error> {
|
||||||
match params {
|
match params {
|
||||||
Params::None => to_value(&U256::from(take_weak!(self.sync).status().protocol_version)),
|
Params::None => Ok(Value::String(format!("{}", take_weak!(self.sync).status().protocol_version).to_owned())),
|
||||||
_ => Err(Error::invalid_params())
|
_ => Err(Error::invalid_params())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -155,6 +172,14 @@ impl<C, S, A> Eth for EthClient<C, S, A> where C: BlockChainClient + 'static, S:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn accounts(&self, _: Params) -> Result<Value, Error> {
|
||||||
|
let store = take_weak!(self.accounts);
|
||||||
|
match store.accounts() {
|
||||||
|
Ok(account_list) => to_value(&account_list),
|
||||||
|
Err(_) => Err(Error::internal_error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn block_number(&self, params: Params) -> Result<Value, Error> {
|
fn block_number(&self, params: Params) -> Result<Value, Error> {
|
||||||
match params {
|
match params {
|
||||||
Params::None => to_value(&U256::from(take_weak!(self.client).chain_info().best_block_number)),
|
Params::None => to_value(&U256::from(take_weak!(self.client).chain_info().best_block_number)),
|
||||||
@ -162,37 +187,59 @@ impl<C, S, A> Eth for EthClient<C, S, A> where C: BlockChainClient + 'static, S:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn balance(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
from_params::<(Address, BlockNumber)>(params)
|
||||||
|
.and_then(|(address, _block_number)| to_value(&take_weak!(self.client).balance(&address)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn storage_at(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
from_params::<(Address, U256, BlockNumber)>(params)
|
||||||
|
.and_then(|(address, position, _block_number)|
|
||||||
|
to_value(&U256::from(take_weak!(self.client).storage_at(&address, &H256::from(position)))))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transaction_count(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
from_params::<(Address, BlockNumber)>(params)
|
||||||
|
.and_then(|(address, _block_number)| to_value(&take_weak!(self.client).nonce(&address)))
|
||||||
|
}
|
||||||
|
|
||||||
fn block_transaction_count_by_hash(&self, params: Params) -> Result<Value, Error> {
|
fn block_transaction_count_by_hash(&self, params: Params) -> Result<Value, Error> {
|
||||||
from_params::<(H256,)>(params)
|
from_params::<(H256,)>(params)
|
||||||
.and_then(|(hash,)| match take_weak!(self.client).block(BlockId::Hash(hash)) {
|
.and_then(|(hash,)| // match
|
||||||
Some(bytes) => to_value(&BlockView::new(&bytes).transactions_count()),
|
to_value(&take_weak!(self.client).block(BlockId::Hash(hash))
|
||||||
None => Ok(Value::Null)
|
.map_or_else(U256::zero, |bytes| U256::from(BlockView::new(&bytes).transactions_count()))))
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_transaction_count_by_number(&self, params: Params) -> Result<Value, Error> {
|
fn block_transaction_count_by_number(&self, params: Params) -> Result<Value, Error> {
|
||||||
from_params::<(BlockNumber,)>(params)
|
from_params::<(BlockNumber,)>(params)
|
||||||
.and_then(|(block_number,)| match block_number {
|
.and_then(|(block_number,)| match block_number {
|
||||||
BlockNumber::Pending => to_value(&take_weak!(self.sync).status().transaction_queue_pending),
|
BlockNumber::Pending => to_value(&U256::from(take_weak!(self.miner).status().transaction_queue_pending)),
|
||||||
_ => match take_weak!(self.client).block(block_number.into()) {
|
_ => to_value(&take_weak!(self.client).block(block_number.into())
|
||||||
Some(bytes) => to_value(&BlockView::new(&bytes).transactions_count()),
|
.map_or_else(U256::zero, |bytes| U256::from(BlockView::new(&bytes).transactions_count())))
|
||||||
None => Ok(Value::Null)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_uncles_count(&self, params: Params) -> Result<Value, Error> {
|
fn block_uncles_count_by_hash(&self, params: Params) -> Result<Value, Error> {
|
||||||
from_params::<(H256,)>(params)
|
from_params::<(H256,)>(params)
|
||||||
.and_then(|(hash,)| match take_weak!(self.client).block(BlockId::Hash(hash)) {
|
.and_then(|(hash,)|
|
||||||
Some(bytes) => to_value(&BlockView::new(&bytes).uncles_count()),
|
to_value(&take_weak!(self.client).block(BlockId::Hash(hash))
|
||||||
None => Ok(Value::Null)
|
.map_or_else(U256::zero, |bytes| U256::from(BlockView::new(&bytes).uncles_count()))))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_uncles_count_by_number(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
from_params::<(BlockNumber,)>(params)
|
||||||
|
.and_then(|(block_number,)| match block_number {
|
||||||
|
BlockNumber::Pending => to_value(&U256::from(0)),
|
||||||
|
_ => to_value(&take_weak!(self.client).block(block_number.into())
|
||||||
|
.map_or_else(U256::zero, |bytes| U256::from(BlockView::new(&bytes).uncles_count())))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: do not ignore block number param
|
// TODO: do not ignore block number param
|
||||||
fn code_at(&self, params: Params) -> Result<Value, Error> {
|
fn code_at(&self, params: Params) -> Result<Value, Error> {
|
||||||
from_params::<(Address, BlockNumber)>(params)
|
from_params::<(Address, BlockNumber)>(params)
|
||||||
.and_then(|(address, _block_number)| to_value(&take_weak!(self.client).code(&address).map_or_else(Bytes::default, Bytes::new)))
|
.and_then(|(address, _block_number)|
|
||||||
|
to_value(&take_weak!(self.client).code(&address).map_or_else(Bytes::default, Bytes::new)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_by_hash(&self, params: Params) -> Result<Value, Error> {
|
fn block_by_hash(&self, params: Params) -> Result<Value, Error> {
|
||||||
@ -220,6 +267,13 @@ impl<C, S, A> Eth for EthClient<C, S, A> where C: BlockChainClient + 'static, S:
|
|||||||
.and_then(|(number, index)| self.transaction(TransactionId::Location(number.into(), index.value())))
|
.and_then(|(number, index)| self.transaction(TransactionId::Location(number.into(), index.value())))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compilers(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
match params {
|
||||||
|
Params::None => to_value(&vec![] as &Vec<String>),
|
||||||
|
_ => Err(Error::invalid_params())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn logs(&self, params: Params) -> Result<Value, Error> {
|
fn logs(&self, params: Params) -> Result<Value, Error> {
|
||||||
from_params::<(Filter,)>(params)
|
from_params::<(Filter,)>(params)
|
||||||
.and_then(|(filter,)| {
|
.and_then(|(filter,)| {
|
||||||
@ -234,8 +288,9 @@ impl<C, S, A> Eth for EthClient<C, S, A> where C: BlockChainClient + 'static, S:
|
|||||||
fn work(&self, params: Params) -> Result<Value, Error> {
|
fn work(&self, params: Params) -> Result<Value, Error> {
|
||||||
match params {
|
match params {
|
||||||
Params::None => {
|
Params::None => {
|
||||||
let c = take_weak!(self.client);
|
let miner = take_weak!(self.miner);
|
||||||
let u = c.sealing_block().lock().unwrap();
|
let client = take_weak!(self.client);
|
||||||
|
let u = miner.sealing_block(client.deref()).lock().unwrap();
|
||||||
match *u {
|
match *u {
|
||||||
Some(ref b) => {
|
Some(ref b) => {
|
||||||
let pow_hash = b.hash();
|
let pow_hash = b.hash();
|
||||||
@ -253,9 +308,10 @@ impl<C, S, A> Eth for EthClient<C, S, A> where C: BlockChainClient + 'static, S:
|
|||||||
fn submit_work(&self, params: Params) -> Result<Value, Error> {
|
fn submit_work(&self, params: Params) -> Result<Value, Error> {
|
||||||
from_params::<(H64, H256, H256)>(params).and_then(|(nonce, pow_hash, mix_hash)| {
|
from_params::<(H64, H256, H256)>(params).and_then(|(nonce, pow_hash, mix_hash)| {
|
||||||
// trace!("Decoded: nonce={}, pow_hash={}, mix_hash={}", nonce, pow_hash, mix_hash);
|
// trace!("Decoded: nonce={}, pow_hash={}, mix_hash={}", nonce, pow_hash, mix_hash);
|
||||||
let c = take_weak!(self.client);
|
let miner = take_weak!(self.miner);
|
||||||
|
let client = take_weak!(self.client);
|
||||||
let seal = vec![encode(&mix_hash).to_vec(), encode(&nonce).to_vec()];
|
let seal = vec![encode(&mix_hash).to_vec(), encode(&nonce).to_vec()];
|
||||||
let r = c.submit_seal(pow_hash, seal);
|
let r = miner.submit_seal(client.deref(), pow_hash, seal);
|
||||||
to_value(&r.is_ok())
|
to_value(&r.is_ok())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -274,12 +330,21 @@ impl<C, S, A> Eth for EthClient<C, S, A> where C: BlockChainClient + 'static, S:
|
|||||||
let accounts = take_weak!(self.accounts);
|
let accounts = take_weak!(self.accounts);
|
||||||
match accounts.account_secret(&transaction_request.from) {
|
match accounts.account_secret(&transaction_request.from) {
|
||||||
Ok(secret) => {
|
Ok(secret) => {
|
||||||
let sync = take_weak!(self.sync);
|
let miner = take_weak!(self.miner);
|
||||||
|
let client = take_weak!(self.client);
|
||||||
|
|
||||||
let transaction: EthTransaction = transaction_request.into();
|
let transaction: EthTransaction = transaction_request.into();
|
||||||
let signed_transaction = transaction.sign(&secret);
|
let signed_transaction = transaction.sign(&secret);
|
||||||
let hash = signed_transaction.hash();
|
let hash = signed_transaction.hash();
|
||||||
sync.insert_transaction(signed_transaction);
|
|
||||||
to_value(&hash)
|
let import = miner.import_transactions(vec![signed_transaction], |a: &Address| client.nonce(a));
|
||||||
|
match import {
|
||||||
|
Ok(_) => to_value(&hash),
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Error sending transaction: {:?}", e);
|
||||||
|
to_value(&U256::zero())
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Err(_) => { to_value(&U256::zero()) }
|
Err(_) => { to_value(&U256::zero()) }
|
||||||
}
|
}
|
||||||
@ -288,27 +353,39 @@ impl<C, S, A> Eth for EthClient<C, S, A> where C: BlockChainClient + 'static, S:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Eth filter rpc implementation.
|
/// Eth filter rpc implementation.
|
||||||
pub struct EthFilterClient<C> where C: BlockChainClient {
|
pub struct EthFilterClient<C, M>
|
||||||
|
where C: BlockChainClient,
|
||||||
|
M: MinerService {
|
||||||
|
|
||||||
client: Weak<C>,
|
client: Weak<C>,
|
||||||
|
miner: Weak<M>,
|
||||||
polls: Mutex<PollManager<PollFilter>>,
|
polls: Mutex<PollManager<PollFilter>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C> EthFilterClient<C> where C: BlockChainClient {
|
impl<C, M> EthFilterClient<C, M>
|
||||||
|
where C: BlockChainClient,
|
||||||
|
M: MinerService {
|
||||||
|
|
||||||
/// Creates new Eth filter client.
|
/// Creates new Eth filter client.
|
||||||
pub fn new(client: &Arc<C>) -> Self {
|
pub fn new(client: &Arc<C>, miner: &Arc<M>) -> Self {
|
||||||
EthFilterClient {
|
EthFilterClient {
|
||||||
client: Arc::downgrade(client),
|
client: Arc::downgrade(client),
|
||||||
polls: Mutex::new(PollManager::new())
|
miner: Arc::downgrade(miner),
|
||||||
|
polls: Mutex::new(PollManager::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C> EthFilter for EthFilterClient<C> where C: BlockChainClient + 'static {
|
impl<C, M> EthFilter for EthFilterClient<C, M>
|
||||||
|
where C: BlockChainClient + 'static,
|
||||||
|
M: MinerService + 'static {
|
||||||
|
|
||||||
fn new_filter(&self, params: Params) -> Result<Value, Error> {
|
fn new_filter(&self, params: Params) -> Result<Value, Error> {
|
||||||
from_params::<(Filter,)>(params)
|
from_params::<(Filter,)>(params)
|
||||||
.and_then(|(filter,)| {
|
.and_then(|(filter,)| {
|
||||||
let mut polls = self.polls.lock().unwrap();
|
let mut polls = self.polls.lock().unwrap();
|
||||||
let id = polls.create_poll(PollFilter::Logs(filter.into()), take_weak!(self.client).chain_info().best_block_number);
|
let block_number = take_weak!(self.client).chain_info().best_block_number;
|
||||||
|
let id = polls.create_poll(PollFilter::Logs(block_number, filter.into()));
|
||||||
to_value(&U256::from(id))
|
to_value(&U256::from(id))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -317,7 +394,7 @@ impl<C> EthFilter for EthFilterClient<C> where C: BlockChainClient + 'static {
|
|||||||
match params {
|
match params {
|
||||||
Params::None => {
|
Params::None => {
|
||||||
let mut polls = self.polls.lock().unwrap();
|
let mut polls = self.polls.lock().unwrap();
|
||||||
let id = polls.create_poll(PollFilter::Block, take_weak!(self.client).chain_info().best_block_number);
|
let id = polls.create_poll(PollFilter::Block(take_weak!(self.client).chain_info().best_block_number));
|
||||||
to_value(&U256::from(id))
|
to_value(&U256::from(id))
|
||||||
},
|
},
|
||||||
_ => Err(Error::invalid_params())
|
_ => Err(Error::invalid_params())
|
||||||
@ -328,7 +405,9 @@ impl<C> EthFilter for EthFilterClient<C> where C: BlockChainClient + 'static {
|
|||||||
match params {
|
match params {
|
||||||
Params::None => {
|
Params::None => {
|
||||||
let mut polls = self.polls.lock().unwrap();
|
let mut polls = self.polls.lock().unwrap();
|
||||||
let id = polls.create_poll(PollFilter::PendingTransaction, take_weak!(self.client).chain_info().best_block_number);
|
let pending_transactions = take_weak!(self.miner).pending_transactions_hashes();
|
||||||
|
let id = polls.create_poll(PollFilter::PendingTransaction(pending_transactions));
|
||||||
|
|
||||||
to_value(&U256::from(id))
|
to_value(&U256::from(id))
|
||||||
},
|
},
|
||||||
_ => Err(Error::invalid_params())
|
_ => Err(Error::invalid_params())
|
||||||
@ -339,37 +418,47 @@ impl<C> EthFilter for EthFilterClient<C> where C: BlockChainClient + 'static {
|
|||||||
let client = take_weak!(self.client);
|
let client = take_weak!(self.client);
|
||||||
from_params::<(Index,)>(params)
|
from_params::<(Index,)>(params)
|
||||||
.and_then(|(index,)| {
|
.and_then(|(index,)| {
|
||||||
let info = self.polls.lock().unwrap().poll_info(&index.value()).cloned();
|
let mut polls = self.polls.lock().unwrap();
|
||||||
match info {
|
match polls.poll_mut(&index.value()) {
|
||||||
None => Ok(Value::Array(vec![] as Vec<Value>)),
|
None => Ok(Value::Array(vec![] as Vec<Value>)),
|
||||||
Some(info) => match info.filter {
|
Some(filter) => match *filter {
|
||||||
PollFilter::Block => {
|
PollFilter::Block(ref mut block_number) => {
|
||||||
// + 1, cause we want to return hashes including current block hash.
|
// + 1, cause we want to return hashes including current block hash.
|
||||||
let current_number = client.chain_info().best_block_number + 1;
|
let current_number = client.chain_info().best_block_number + 1;
|
||||||
let hashes = (info.block_number..current_number).into_iter()
|
let hashes = (*block_number..current_number).into_iter()
|
||||||
.map(BlockId::Number)
|
.map(BlockId::Number)
|
||||||
.filter_map(|id| client.block_hash(id))
|
.filter_map(|id| client.block_hash(id))
|
||||||
.collect::<Vec<H256>>();
|
.collect::<Vec<H256>>();
|
||||||
|
|
||||||
self.polls.lock().unwrap().update_poll(&index.value(), current_number);
|
*block_number = current_number;
|
||||||
|
|
||||||
to_value(&hashes)
|
to_value(&hashes)
|
||||||
},
|
},
|
||||||
PollFilter::PendingTransaction => {
|
PollFilter::PendingTransaction(ref mut previous_hashes) => {
|
||||||
// TODO: fix implementation once TransactionQueue is merged
|
let current_hashes = take_weak!(self.miner).pending_transactions_hashes();
|
||||||
to_value(&vec![] as &Vec<H256>)
|
// calculate diff
|
||||||
|
let previous_hashes_set = previous_hashes.into_iter().map(|h| h.clone()).collect::<HashSet<H256>>();
|
||||||
|
let diff = current_hashes
|
||||||
|
.iter()
|
||||||
|
.filter(|hash| previous_hashes_set.contains(&hash))
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<H256>>();
|
||||||
|
|
||||||
|
*previous_hashes = current_hashes;
|
||||||
|
|
||||||
|
to_value(&diff)
|
||||||
},
|
},
|
||||||
PollFilter::Logs(mut filter) => {
|
PollFilter::Logs(ref mut block_number, ref mut filter) => {
|
||||||
filter.from_block = BlockId::Number(info.block_number);
|
filter.from_block = BlockId::Number(*block_number);
|
||||||
filter.to_block = BlockId::Latest;
|
filter.to_block = BlockId::Latest;
|
||||||
let logs = client.logs(filter)
|
let logs = client.logs(filter.clone())
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(From::from)
|
.map(From::from)
|
||||||
.collect::<Vec<Log>>();
|
.collect::<Vec<Log>>();
|
||||||
|
|
||||||
let current_number = client.chain_info().best_block_number;
|
let current_number = client.chain_info().best_block_number;
|
||||||
self.polls.lock().unwrap().update_poll(&index.value(), current_number);
|
|
||||||
|
|
||||||
|
*block_number = current_number;
|
||||||
to_value(&logs)
|
to_value(&logs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,12 +39,7 @@ impl<A> Personal for PersonalClient<A> where A: AccountProvider + 'static {
|
|||||||
fn accounts(&self, _: Params) -> Result<Value, Error> {
|
fn accounts(&self, _: Params) -> Result<Value, Error> {
|
||||||
let store = take_weak!(self.accounts);
|
let store = take_weak!(self.accounts);
|
||||||
match store.accounts() {
|
match store.accounts() {
|
||||||
Ok(account_list) => {
|
Ok(account_list) => to_value(&account_list),
|
||||||
Ok(Value::Array(account_list.iter()
|
|
||||||
.map(|&account| Value::String(format!("{:?}", account)))
|
|
||||||
.collect::<Vec<Value>>())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Err(_) => Err(Error::internal_error())
|
Err(_) => Err(Error::internal_error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -54,7 +49,7 @@ impl<A> Personal for PersonalClient<A> where A: AccountProvider + 'static {
|
|||||||
|(pass, )| {
|
|(pass, )| {
|
||||||
let store = take_weak!(self.accounts);
|
let store = take_weak!(self.accounts);
|
||||||
match store.new_account(&pass) {
|
match store.new_account(&pass) {
|
||||||
Ok(address) => Ok(Value::String(format!("{:?}", address))),
|
Ok(address) => Ok(Value::String(format!("0x{:?}", address))),
|
||||||
Err(_) => Err(Error::internal_error())
|
Err(_) => Err(Error::internal_error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
279
rpc/src/v1/tests/eth.rs
Normal file
279
rpc/src/v1/tests/eth.rs
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use jsonrpc_core::IoHandler;
|
||||||
|
use util::hash::{Address, H256};
|
||||||
|
use util::numbers::U256;
|
||||||
|
use ethcore::client::{TestBlockChainClient, EachBlockWith};
|
||||||
|
use v1::{Eth, EthClient};
|
||||||
|
use v1::tests::helpers::{TestAccount, TestAccountProvider, TestSyncProvider, Config, TestMinerService};
|
||||||
|
|
||||||
|
fn blockchain_client() -> Arc<TestBlockChainClient> {
|
||||||
|
let mut client = TestBlockChainClient::new();
|
||||||
|
client.add_blocks(10, EachBlockWith::Nothing);
|
||||||
|
client.set_balance(Address::from(1), U256::from(5));
|
||||||
|
client.set_storage(Address::from(1), H256::from(4), H256::from(7));
|
||||||
|
client.set_code(Address::from(1), vec![0xff, 0x21]);
|
||||||
|
Arc::new(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn accounts_provider() -> Arc<TestAccountProvider> {
|
||||||
|
let mut accounts = HashMap::new();
|
||||||
|
accounts.insert(Address::from(1), TestAccount::new("test"));
|
||||||
|
let ap = TestAccountProvider::new(accounts);
|
||||||
|
Arc::new(ap)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sync_provider() -> Arc<TestSyncProvider> {
|
||||||
|
Arc::new(TestSyncProvider::new(Config {
|
||||||
|
protocol_version: 65,
|
||||||
|
num_peers: 120,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn miner_service() -> Arc<TestMinerService> {
|
||||||
|
Arc::new(TestMinerService)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EthTester {
|
||||||
|
_client: Arc<TestBlockChainClient>,
|
||||||
|
_sync: Arc<TestSyncProvider>,
|
||||||
|
_accounts_provider: Arc<TestAccountProvider>,
|
||||||
|
_miner: Arc<TestMinerService>,
|
||||||
|
pub io: IoHandler,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for EthTester {
|
||||||
|
fn default() -> Self {
|
||||||
|
let client = blockchain_client();
|
||||||
|
let sync = sync_provider();
|
||||||
|
let ap = accounts_provider();
|
||||||
|
let miner = miner_service();
|
||||||
|
let eth = EthClient::new(&client, &sync, &ap, &miner).to_delegate();
|
||||||
|
let io = IoHandler::new();
|
||||||
|
io.add_delegate(eth);
|
||||||
|
EthTester {
|
||||||
|
_client: client,
|
||||||
|
_sync: sync,
|
||||||
|
_accounts_provider: ap,
|
||||||
|
_miner: miner,
|
||||||
|
io: io
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rpc_eth_protocol_version() {
|
||||||
|
let request = r#"{"jsonrpc": "2.0", "method": "eth_protocolVersion", "params": [], "id": 1}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":"65","id":1}"#;
|
||||||
|
|
||||||
|
assert_eq!(EthTester::default().io.handle_request(request), Some(response.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn rpc_eth_syncing() {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn rpc_eth_hashrate() {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn rpc_eth_author() {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn rpc_eth_mining() {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rpc_eth_gas_price() {
|
||||||
|
let request = r#"{"jsonrpc": "2.0", "method": "eth_gasPrice", "params": [], "id": 1}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":"0x0ba43b7400","id":1}"#;
|
||||||
|
|
||||||
|
assert_eq!(EthTester::default().io.handle_request(request), Some(response.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rpc_eth_accounts() {
|
||||||
|
let request = r#"{"jsonrpc": "2.0", "method": "eth_accounts", "params": [], "id": 1}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":["0x0000000000000000000000000000000000000001"],"id":1}"#;
|
||||||
|
|
||||||
|
assert_eq!(EthTester::default().io.handle_request(request), Some(response.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rpc_eth_block_number() {
|
||||||
|
let request = r#"{"jsonrpc": "2.0", "method": "eth_blockNumber", "params": [], "id": 1}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":"0x0a","id":1}"#;
|
||||||
|
|
||||||
|
assert_eq!(EthTester::default().io.handle_request(request), Some(response.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rpc_eth_balance() {
|
||||||
|
let request = r#"{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "eth_getBalance",
|
||||||
|
"params": ["0x0000000000000000000000000000000000000001", "latest"],
|
||||||
|
"id": 1
|
||||||
|
}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":"0x05","id":1}"#;
|
||||||
|
|
||||||
|
assert_eq!(EthTester::default().io.handle_request(request), Some(response.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rpc_eth_storage_at() {
|
||||||
|
let request = r#"{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "eth_getStorageAt",
|
||||||
|
"params": ["0x0000000000000000000000000000000000000001", "0x4", "latest"],
|
||||||
|
"id": 1
|
||||||
|
}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":"0x07","id":1}"#;
|
||||||
|
|
||||||
|
assert_eq!(EthTester::default().io.handle_request(request), Some(response.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rpc_eth_transaction_count() {
|
||||||
|
let request = r#"{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "eth_getTransactionCount",
|
||||||
|
"params": ["0x0000000000000000000000000000000000000001", "latest"],
|
||||||
|
"id": 1
|
||||||
|
}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":"0x00","id":1}"#;
|
||||||
|
|
||||||
|
assert_eq!(EthTester::default().io.handle_request(request), Some(response.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rpc_eth_block_transaction_count_by_hash() {
|
||||||
|
let request = r#"{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "eth_getBlockTransactionCountByHash",
|
||||||
|
"params": ["0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"],
|
||||||
|
"id": 1
|
||||||
|
}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":"0x00","id":1}"#;
|
||||||
|
|
||||||
|
assert_eq!(EthTester::default().io.handle_request(request), Some(response.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rpc_eth_transaction_count_by_number() {
|
||||||
|
let request = r#"{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "eth_getBlockTransactionCountByNumber",
|
||||||
|
"params": ["latest"],
|
||||||
|
"id": 1
|
||||||
|
}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":"0x00","id":1}"#;
|
||||||
|
|
||||||
|
assert_eq!(EthTester::default().io.handle_request(request), Some(response.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rpc_eth_uncle_count_by_block_hash() {
|
||||||
|
let request = r#"{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "eth_getUncleCountByBlockHash",
|
||||||
|
"params": ["0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"],
|
||||||
|
"id": 1
|
||||||
|
}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":"0x00","id":1}"#;
|
||||||
|
|
||||||
|
assert_eq!(EthTester::default().io.handle_request(request), Some(response.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rpc_eth_uncle_count_by_block_number() {
|
||||||
|
let request = r#"{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "eth_getUncleCountByBlockNumber",
|
||||||
|
"params": ["latest"],
|
||||||
|
"id": 1
|
||||||
|
}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":"0x00","id":1}"#;
|
||||||
|
|
||||||
|
assert_eq!(EthTester::default().io.handle_request(request), Some(response.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rpc_eth_code() {
|
||||||
|
let request = r#"{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "eth_getCode",
|
||||||
|
"params": ["0x0000000000000000000000000000000000000001", "latest"],
|
||||||
|
"id": 1
|
||||||
|
}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":"0xff21","id":1}"#;
|
||||||
|
|
||||||
|
assert_eq!(EthTester::default().io.handle_request(request), Some(response.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn rpc_eth_call() {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn rpc_eth_send_transaction() {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn rpc_eth_send_raw_transaction() {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn rpc_eth_sign() {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn rpc_eth_estimate_gas() {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rpc_eth_compilers() {
|
||||||
|
let request = r#"{"jsonrpc": "2.0", "method": "eth_getCompilers", "params": [], "id": 1}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":[],"id":1}"#;
|
||||||
|
|
||||||
|
assert_eq!(EthTester::default().io.handle_request(request), Some(response.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
90
rpc/src/v1/tests/helpers/account_provider.rs
Normal file
90
rpc/src/v1/tests/helpers/account_provider.rs
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::sync::RwLock;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::io;
|
||||||
|
use util::hash::{Address, H256};
|
||||||
|
use util::crypto::{Secret, Signature};
|
||||||
|
use util::keys::store::{AccountProvider, SigningError, EncryptedHashMapError};
|
||||||
|
|
||||||
|
/// Account mock.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TestAccount {
|
||||||
|
/// True if account is unlocked.
|
||||||
|
pub unlocked: bool,
|
||||||
|
/// Account's password.
|
||||||
|
pub password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestAccount {
|
||||||
|
pub fn new(password: &str) -> Self {
|
||||||
|
TestAccount {
|
||||||
|
unlocked: false,
|
||||||
|
password: password.to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test account provider.
|
||||||
|
pub struct TestAccountProvider {
|
||||||
|
accounts: RwLock<HashMap<Address, TestAccount>>,
|
||||||
|
pub adds: RwLock<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestAccountProvider {
|
||||||
|
/// Basic constructor.
|
||||||
|
pub fn new(accounts: HashMap<Address, TestAccount>) -> Self {
|
||||||
|
TestAccountProvider {
|
||||||
|
accounts: RwLock::new(accounts),
|
||||||
|
adds: RwLock::new(vec![]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AccountProvider for TestAccountProvider {
|
||||||
|
fn accounts(&self) -> Result<Vec<Address>, io::Error> {
|
||||||
|
Ok(self.accounts.read().unwrap().keys().cloned().collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unlock_account(&self, account: &Address, pass: &str) -> Result<(), EncryptedHashMapError> {
|
||||||
|
match self.accounts.write().unwrap().get_mut(account) {
|
||||||
|
Some(ref mut acc) if acc.password == pass => {
|
||||||
|
acc.unlocked = true;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
Some(_) => Err(EncryptedHashMapError::InvalidPassword),
|
||||||
|
None => Err(EncryptedHashMapError::UnknownIdentifier),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_account(&self, pass: &str) -> Result<Address, io::Error> {
|
||||||
|
let mut adds = self.adds.write().unwrap();
|
||||||
|
let address = Address::from(adds.len() as u64 + 2);
|
||||||
|
adds.push(pass.to_owned());
|
||||||
|
Ok(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn account_secret(&self, _account: &Address) -> Result<Secret, SigningError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sign(&self, _account: &Address, _message: &H256) -> Result<Signature, SigningError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
53
rpc/src/v1/tests/helpers/miner_service.rs
Normal file
53
rpc/src/v1/tests/helpers/miner_service.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use util::{Address, H256, U256, Bytes};
|
||||||
|
use util::standard::*;
|
||||||
|
use ethcore::error::Error;
|
||||||
|
use ethcore::client::BlockChainClient;
|
||||||
|
use ethcore::block::ClosedBlock;
|
||||||
|
use ethcore::transaction::SignedTransaction;
|
||||||
|
use ethminer::{MinerService, MinerStatus};
|
||||||
|
|
||||||
|
pub struct TestMinerService;
|
||||||
|
|
||||||
|
impl MinerService for TestMinerService {
|
||||||
|
|
||||||
|
/// Returns miner's status.
|
||||||
|
fn status(&self) -> MinerStatus { unimplemented!(); }
|
||||||
|
|
||||||
|
/// Imports transactions to transaction queue.
|
||||||
|
fn import_transactions<T>(&self, _transactions: Vec<SignedTransaction>, _fetch_nonce: T) -> Result<(), Error> where T: Fn(&Address) -> U256 { unimplemented!(); }
|
||||||
|
|
||||||
|
/// Returns hashes of transactions currently in pending
|
||||||
|
fn pending_transactions_hashes(&self) -> Vec<H256> { unimplemented!(); }
|
||||||
|
|
||||||
|
/// Removes all transactions from the queue and restart mining operation.
|
||||||
|
fn clear_and_reset(&self, _chain: &BlockChainClient) { unimplemented!(); }
|
||||||
|
|
||||||
|
/// Called when blocks are imported to chain, updates transactions queue.
|
||||||
|
fn chain_new_blocks(&self, _chain: &BlockChainClient, _imported: &[H256], _invalid: &[H256], _enacted: &[H256], _retracted: &[H256]) { unimplemented!(); }
|
||||||
|
|
||||||
|
/// New chain head event. Restart mining operation.
|
||||||
|
fn prepare_sealing(&self, _chain: &BlockChainClient) { unimplemented!(); }
|
||||||
|
|
||||||
|
/// Grab the `ClosedBlock` that we want to be sealed. Comes as a mutex that you have to lock.
|
||||||
|
fn sealing_block(&self, _chain: &BlockChainClient) -> &Mutex<Option<ClosedBlock>> { unimplemented!(); }
|
||||||
|
|
||||||
|
/// Submit `seal` as a valid solution for the header of `pow_hash`.
|
||||||
|
/// Will check the seal, but not actually insert the block into the chain.
|
||||||
|
fn submit_seal(&self, _chain: &BlockChainClient, _pow_hash: H256, _seal: Vec<Bytes>) -> Result<(), Error> { unimplemented!(); }
|
||||||
|
}
|
@ -14,6 +14,10 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
mod account_provider;
|
||||||
mod sync_provider;
|
mod sync_provider;
|
||||||
|
mod miner_service;
|
||||||
|
|
||||||
|
pub use self::account_provider::{TestAccount, TestAccountProvider};
|
||||||
pub use self::sync_provider::{Config, TestSyncProvider};
|
pub use self::sync_provider::{Config, TestSyncProvider};
|
||||||
|
pub use self::miner_service::{TestMinerService};
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use ethcore::transaction::SignedTransaction;
|
|
||||||
use ethsync::{SyncProvider, SyncStatus, SyncState};
|
use ethsync::{SyncProvider, SyncStatus, SyncState};
|
||||||
|
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
@ -40,7 +39,6 @@ impl TestSyncProvider {
|
|||||||
num_peers: config.num_peers,
|
num_peers: config.num_peers,
|
||||||
num_active_peers: 0,
|
num_active_peers: 0,
|
||||||
mem_used: 0,
|
mem_used: 0,
|
||||||
transaction_queue_pending: 0,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,9 +48,5 @@ impl SyncProvider for TestSyncProvider {
|
|||||||
fn status(&self) -> SyncStatus {
|
fn status(&self) -> SyncStatus {
|
||||||
self.status.clone()
|
self.status.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_transaction(&self, _transaction: SignedTransaction) {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
//!TODO: load custom blockchain state and test
|
//!TODO: load custom blockchain state and test
|
||||||
|
|
||||||
|
mod eth;
|
||||||
mod net;
|
mod net;
|
||||||
mod web3;
|
mod web3;
|
||||||
mod helpers;
|
mod helpers;
|
||||||
|
mod personal;
|
||||||
|
59
rpc/src/v1/tests/personal.rs
Normal file
59
rpc/src/v1/tests/personal.rs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
use jsonrpc_core::IoHandler;
|
||||||
|
use v1::tests::helpers::{TestAccount, TestAccountProvider};
|
||||||
|
use v1::{PersonalClient, Personal};
|
||||||
|
use util::numbers::*;
|
||||||
|
use std::collections::*;
|
||||||
|
|
||||||
|
fn accounts_provider() -> Arc<TestAccountProvider> {
|
||||||
|
let mut accounts = HashMap::new();
|
||||||
|
accounts.insert(Address::from(1), TestAccount::new("test"));
|
||||||
|
let ap = TestAccountProvider::new(accounts);
|
||||||
|
Arc::new(ap)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup() -> (Arc<TestAccountProvider>, IoHandler) {
|
||||||
|
let test_provider = accounts_provider();
|
||||||
|
let personal = PersonalClient::new(&test_provider);
|
||||||
|
let io = IoHandler::new();
|
||||||
|
io.add_delegate(personal.to_delegate());
|
||||||
|
(test_provider, io)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn accounts() {
|
||||||
|
let (_test_provider, io) = setup();
|
||||||
|
|
||||||
|
let request = r#"{"jsonrpc": "2.0", "method": "personal_listAccounts", "params": [], "id": 1}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":["0x0000000000000000000000000000000000000001"],"id":1}"#;
|
||||||
|
|
||||||
|
assert_eq!(io.handle_request(request), Some(response.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_account() {
|
||||||
|
let (_test_provider, io) = setup();
|
||||||
|
|
||||||
|
let request = r#"{"jsonrpc": "2.0", "method": "personal_newAccount", "params": ["pass"], "id": 1}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000002","id":1}"#;
|
||||||
|
|
||||||
|
assert_eq!(io.handle_request(request), Some(response.to_owned()));
|
||||||
|
}
|
||||||
|
|
@ -59,21 +59,30 @@ pub trait Eth: Sized + Send + Sync + 'static {
|
|||||||
/// Returns the number of transactions sent from given address at given time (block number).
|
/// Returns the number of transactions sent from given address at given time (block number).
|
||||||
fn transaction_count(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
fn transaction_count(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||||
|
|
||||||
/// Returns the number of transactions in a block given block hash.
|
/// Returns the number of transactions in a block with given hash.
|
||||||
fn block_transaction_count_by_hash(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
fn block_transaction_count_by_hash(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||||
|
|
||||||
/// Returns the number of transactions in a block given block number.
|
/// Returns the number of transactions in a block with given block number.
|
||||||
fn block_transaction_count_by_number(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
fn block_transaction_count_by_number(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||||
|
|
||||||
/// Returns the number of uncles in a given block.
|
/// Returns the number of uncles in a block with given hash.
|
||||||
fn block_uncles_count(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
fn block_uncles_count_by_hash(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||||
|
|
||||||
|
/// Returns the number of uncles in a block with given block number.
|
||||||
|
fn block_uncles_count_by_number(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||||
|
|
||||||
/// Returns the code at given address at given time (block number).
|
/// Returns the code at given address at given time (block number).
|
||||||
fn code_at(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
fn code_at(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||||
|
|
||||||
|
/// Signs the data with given address signature.
|
||||||
|
fn sign(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||||
|
|
||||||
/// Sends transaction.
|
/// Sends transaction.
|
||||||
fn send_transaction(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
fn send_transaction(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||||
|
|
||||||
|
/// Sends signed transaction.
|
||||||
|
fn send_raw_transaction(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||||
|
|
||||||
/// Call contract.
|
/// Call contract.
|
||||||
fn call(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
fn call(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||||
|
|
||||||
@ -130,15 +139,17 @@ pub trait Eth: Sized + Send + Sync + 'static {
|
|||||||
delegate.add_method("eth_gasPrice", Eth::gas_price);
|
delegate.add_method("eth_gasPrice", Eth::gas_price);
|
||||||
delegate.add_method("eth_accounts", Eth::accounts);
|
delegate.add_method("eth_accounts", Eth::accounts);
|
||||||
delegate.add_method("eth_blockNumber", Eth::block_number);
|
delegate.add_method("eth_blockNumber", Eth::block_number);
|
||||||
delegate.add_method("eth_balance", Eth::balance);
|
delegate.add_method("eth_getBalance", Eth::balance);
|
||||||
delegate.add_method("eth_getStorageAt", Eth::storage_at);
|
delegate.add_method("eth_getStorageAt", Eth::storage_at);
|
||||||
delegate.add_method("eth_getTransactionCount", Eth::transaction_count);
|
delegate.add_method("eth_getTransactionCount", Eth::transaction_count);
|
||||||
delegate.add_method("eth_getBlockTransactionCountByHash", Eth::block_transaction_count_by_hash);
|
delegate.add_method("eth_getBlockTransactionCountByHash", Eth::block_transaction_count_by_hash);
|
||||||
delegate.add_method("eth_getBlockTransactionCountByNumber", Eth::block_transaction_count_by_number);
|
delegate.add_method("eth_getBlockTransactionCountByNumber", Eth::block_transaction_count_by_number);
|
||||||
delegate.add_method("eth_getUncleCountByBlockHash", Eth::block_uncles_count);
|
delegate.add_method("eth_getUncleCountByBlockHash", Eth::block_uncles_count_by_hash);
|
||||||
delegate.add_method("eth_getUncleCountByBlockNumber", Eth::block_uncles_count);
|
delegate.add_method("eth_getUncleCountByBlockNumber", Eth::block_uncles_count_by_number);
|
||||||
delegate.add_method("eth_code", Eth::code_at);
|
delegate.add_method("eth_getCode", Eth::code_at);
|
||||||
|
delegate.add_method("eth_sign", Eth::sign);
|
||||||
delegate.add_method("eth_sendTransaction", Eth::send_transaction);
|
delegate.add_method("eth_sendTransaction", Eth::send_transaction);
|
||||||
|
delegate.add_method("eth_sendRawTransaction", Eth::send_raw_transaction);
|
||||||
delegate.add_method("eth_call", Eth::call);
|
delegate.add_method("eth_call", Eth::call);
|
||||||
delegate.add_method("eth_estimateGas", Eth::estimate_gas);
|
delegate.add_method("eth_estimateGas", Eth::estimate_gas);
|
||||||
delegate.add_method("eth_getBlockByHash", Eth::block_by_hash);
|
delegate.add_method("eth_getBlockByHash", Eth::block_by_hash);
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
use util::numbers::*;
|
use util::numbers::*;
|
||||||
use ethcore::transaction::{LocalizedTransaction, Action};
|
use ethcore::transaction::{LocalizedTransaction, Action};
|
||||||
use v1::types::{Bytes, OptionalValue};
|
use v1::types::{Bytes, OptionalValue};
|
||||||
use serde::Error;
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize)]
|
#[derive(Debug, Default, Serialize)]
|
||||||
pub struct Transaction {
|
pub struct Transaction {
|
||||||
|
@ -10,15 +10,14 @@ authors = ["Ethcore <admin@ethcore.io"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
ethcore-util = { path = "../util" }
|
ethcore-util = { path = "../util" }
|
||||||
ethcore = { path = "../ethcore" }
|
ethcore = { path = "../ethcore" }
|
||||||
clippy = { version = "0.0.49", optional = true }
|
clippy = { version = "0.0.50", optional = true }
|
||||||
|
ethminer = { path = "../miner" }
|
||||||
log = "0.3"
|
log = "0.3"
|
||||||
env_logger = "0.3"
|
env_logger = "0.3"
|
||||||
time = "0.1.34"
|
time = "0.1.34"
|
||||||
rand = "0.3.13"
|
rand = "0.3.13"
|
||||||
heapsize = "0.3"
|
heapsize = "0.3"
|
||||||
rustc-serialize = "0.3"
|
|
||||||
rayon = "0.3.1"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
dev = ["clippy", "ethcore/dev", "ethcore-util/dev"]
|
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethminer/dev"]
|
||||||
|
@ -30,20 +30,18 @@
|
|||||||
///
|
///
|
||||||
|
|
||||||
use util::*;
|
use util::*;
|
||||||
use rayon::prelude::*;
|
|
||||||
use std::mem::{replace};
|
use std::mem::{replace};
|
||||||
use ethcore::views::{HeaderView, BlockView};
|
use ethcore::views::{HeaderView};
|
||||||
use ethcore::header::{BlockNumber, Header as BlockHeader};
|
use ethcore::header::{BlockNumber, Header as BlockHeader};
|
||||||
use ethcore::client::{BlockChainClient, BlockStatus, BlockId, BlockChainInfo};
|
use ethcore::client::{BlockChainClient, BlockStatus, BlockId, BlockChainInfo};
|
||||||
use range_collection::{RangeCollection, ToUsize, FromUsize};
|
use range_collection::{RangeCollection, ToUsize, FromUsize};
|
||||||
use ethcore::error::*;
|
use ethcore::error::*;
|
||||||
use ethcore::block::Block;
|
|
||||||
use ethcore::transaction::SignedTransaction;
|
use ethcore::transaction::SignedTransaction;
|
||||||
|
use ethcore::block::Block;
|
||||||
|
use ethminer::{Miner, MinerService};
|
||||||
use io::SyncIo;
|
use io::SyncIo;
|
||||||
use transaction_queue::TransactionQueue;
|
|
||||||
use time;
|
use time;
|
||||||
use super::SyncConfig;
|
use super::SyncConfig;
|
||||||
use ethcore;
|
|
||||||
|
|
||||||
known_heap_size!(0, PeerInfo, Header, HeaderId);
|
known_heap_size!(0, PeerInfo, Header, HeaderId);
|
||||||
|
|
||||||
@ -143,8 +141,6 @@ pub struct SyncStatus {
|
|||||||
pub num_active_peers: usize,
|
pub num_active_peers: usize,
|
||||||
/// Heap memory used in bytes
|
/// Heap memory used in bytes
|
||||||
pub mem_used: usize,
|
pub mem_used: usize,
|
||||||
/// Number of pending transactions in queue
|
|
||||||
pub transaction_queue_pending: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||||
@ -217,15 +213,15 @@ pub struct ChainSync {
|
|||||||
max_download_ahead_blocks: usize,
|
max_download_ahead_blocks: usize,
|
||||||
/// Network ID
|
/// Network ID
|
||||||
network_id: U256,
|
network_id: U256,
|
||||||
/// Transactions Queue
|
/// Miner
|
||||||
transaction_queue: Mutex<TransactionQueue>,
|
miner: Arc<Miner>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type RlpResponseResult = Result<Option<(PacketId, RlpStream)>, PacketDecodeError>;
|
type RlpResponseResult = Result<Option<(PacketId, RlpStream)>, PacketDecodeError>;
|
||||||
|
|
||||||
impl ChainSync {
|
impl ChainSync {
|
||||||
/// Create a new instance of syncing strategy.
|
/// Create a new instance of syncing strategy.
|
||||||
pub fn new(config: SyncConfig) -> ChainSync {
|
pub fn new(config: SyncConfig, miner: Arc<Miner>) -> ChainSync {
|
||||||
ChainSync {
|
ChainSync {
|
||||||
state: SyncState::NotSynced,
|
state: SyncState::NotSynced,
|
||||||
starting_block: 0,
|
starting_block: 0,
|
||||||
@ -244,7 +240,7 @@ impl ChainSync {
|
|||||||
last_sent_block_number: 0,
|
last_sent_block_number: 0,
|
||||||
max_download_ahead_blocks: max(MAX_HEADERS_TO_REQUEST, config.max_download_ahead_blocks),
|
max_download_ahead_blocks: max(MAX_HEADERS_TO_REQUEST, config.max_download_ahead_blocks),
|
||||||
network_id: config.network_id,
|
network_id: config.network_id,
|
||||||
transaction_queue: Mutex::new(TransactionQueue::new()),
|
miner: miner,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,7 +256,6 @@ impl ChainSync {
|
|||||||
blocks_total: match self.highest_block { Some(x) if x > self.starting_block => x - self.starting_block, _ => 0 },
|
blocks_total: match self.highest_block { Some(x) if x > self.starting_block => x - self.starting_block, _ => 0 },
|
||||||
num_peers: self.peers.len(),
|
num_peers: self.peers.len(),
|
||||||
num_active_peers: self.peers.values().filter(|p| p.asking != PeerAsking::Nothing).count(),
|
num_active_peers: self.peers.values().filter(|p| p.asking != PeerAsking::Nothing).count(),
|
||||||
transaction_queue_pending: self.transaction_queue.lock().unwrap().status().pending,
|
|
||||||
mem_used:
|
mem_used:
|
||||||
// TODO: https://github.com/servo/heapsize/pull/50
|
// TODO: https://github.com/servo/heapsize/pull/50
|
||||||
// self.downloading_hashes.heap_size_of_children()
|
// self.downloading_hashes.heap_size_of_children()
|
||||||
@ -299,12 +294,9 @@ impl ChainSync {
|
|||||||
/// Restart sync
|
/// Restart sync
|
||||||
pub fn restart(&mut self, io: &mut SyncIo) {
|
pub fn restart(&mut self, io: &mut SyncIo) {
|
||||||
self.reset();
|
self.reset();
|
||||||
self.last_imported_block = None;
|
|
||||||
self.last_imported_hash = None;
|
|
||||||
self.starting_block = 0;
|
self.starting_block = 0;
|
||||||
self.highest_block = None;
|
self.highest_block = None;
|
||||||
self.have_common_block = false;
|
self.have_common_block = false;
|
||||||
self.transaction_queue.lock().unwrap().clear();
|
|
||||||
self.starting_block = io.chain().chain_info().best_block_number;
|
self.starting_block = io.chain().chain_info().best_block_number;
|
||||||
self.state = SyncState::NotSynced;
|
self.state = SyncState::NotSynced;
|
||||||
}
|
}
|
||||||
@ -367,7 +359,7 @@ impl ChainSync {
|
|||||||
for i in 0..item_count {
|
for i in 0..item_count {
|
||||||
let info: BlockHeader = try!(r.val_at(i));
|
let info: BlockHeader = try!(r.val_at(i));
|
||||||
let number = BlockNumber::from(info.number);
|
let number = BlockNumber::from(info.number);
|
||||||
if number <= self.current_base_block() || self.headers.have_item(&number) {
|
if (number <= self.current_base_block() && self.have_common_block) || self.headers.have_item(&number) {
|
||||||
trace!(target: "sync", "Skipping existing block header");
|
trace!(target: "sync", "Skipping existing block header");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -377,11 +369,17 @@ impl ChainSync {
|
|||||||
}
|
}
|
||||||
let hash = info.hash();
|
let hash = info.hash();
|
||||||
match io.chain().block_status(BlockId::Hash(hash.clone())) {
|
match io.chain().block_status(BlockId::Hash(hash.clone())) {
|
||||||
BlockStatus::InChain => {
|
BlockStatus::InChain | BlockStatus::Queued => {
|
||||||
self.have_common_block = true;
|
if !self.have_common_block || self.current_base_block() < number {
|
||||||
self.last_imported_block = Some(number);
|
self.last_imported_block = Some(number);
|
||||||
self.last_imported_hash = Some(hash.clone());
|
self.last_imported_hash = Some(hash.clone());
|
||||||
trace!(target: "sync", "Found common header {} ({})", number, hash);
|
}
|
||||||
|
if !self.have_common_block {
|
||||||
|
self.have_common_block = true;
|
||||||
|
trace!(target: "sync", "Found common header {} ({})", number, hash);
|
||||||
|
} else {
|
||||||
|
trace!(target: "sync", "Header already in chain {} ({})", number, hash);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
if self.have_common_block {
|
if self.have_common_block {
|
||||||
@ -589,7 +587,7 @@ impl ChainSync {
|
|||||||
pub fn on_peer_connected(&mut self, io: &mut SyncIo, peer: PeerId) {
|
pub fn on_peer_connected(&mut self, io: &mut SyncIo, peer: PeerId) {
|
||||||
trace!(target: "sync", "== Connected {}", peer);
|
trace!(target: "sync", "== Connected {}", peer);
|
||||||
if let Err(e) = self.send_status(io) {
|
if let Err(e) = self.send_status(io) {
|
||||||
warn!(target:"sync", "Error sending status request: {:?}", e);
|
debug!(target:"sync", "Error sending status request: {:?}", e);
|
||||||
io.disable_peer(peer);
|
io.disable_peer(peer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -657,10 +655,7 @@ impl ChainSync {
|
|||||||
let mut needed_numbers: Vec<BlockNumber> = Vec::new();
|
let mut needed_numbers: Vec<BlockNumber> = Vec::new();
|
||||||
|
|
||||||
if self.have_common_block && !self.headers.is_empty() && self.headers.range_iter().next().unwrap().0 == self.current_base_block() + 1 {
|
if self.have_common_block && !self.headers.is_empty() && self.headers.range_iter().next().unwrap().0 == self.current_base_block() + 1 {
|
||||||
for (start, ref items) in self.headers.range_iter() {
|
if let Some((start, ref items)) = self.headers.range_iter().next() {
|
||||||
if needed_bodies.len() >= MAX_BODIES_TO_REQUEST {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let mut index: BlockNumber = 0;
|
let mut index: BlockNumber = 0;
|
||||||
while index != items.len() as BlockNumber && needed_bodies.len() < MAX_BODIES_TO_REQUEST {
|
while index != items.len() as BlockNumber && needed_bodies.len() < MAX_BODIES_TO_REQUEST {
|
||||||
let block = start + index;
|
let block = start + index;
|
||||||
@ -704,7 +699,10 @@ impl ChainSync {
|
|||||||
if !self.have_common_block {
|
if !self.have_common_block {
|
||||||
// download backwards until common block is found 1 header at a time
|
// download backwards until common block is found 1 header at a time
|
||||||
let chain_info = io.chain().chain_info();
|
let chain_info = io.chain().chain_info();
|
||||||
start = chain_info.best_block_number;
|
start = match self.last_imported_block {
|
||||||
|
Some(n) => n,
|
||||||
|
None => chain_info.best_block_number,
|
||||||
|
};
|
||||||
if !self.headers.is_empty() {
|
if !self.headers.is_empty() {
|
||||||
start = min(start, self.headers.range_iter().next().unwrap().0 - 1);
|
start = min(start, self.headers.range_iter().next().unwrap().0 - 1);
|
||||||
}
|
}
|
||||||
@ -845,18 +843,12 @@ impl ChainSync {
|
|||||||
/// Remove downloaded bocks/headers starting from specified number.
|
/// Remove downloaded bocks/headers starting from specified number.
|
||||||
/// Used to recover from an error and re-download parts of the chain detected as bad.
|
/// Used to recover from an error and re-download parts of the chain detected as bad.
|
||||||
fn remove_downloaded_blocks(&mut self, start: BlockNumber) {
|
fn remove_downloaded_blocks(&mut self, start: BlockNumber) {
|
||||||
for n in self.headers.get_tail(&start) {
|
let ids = self.header_ids.drain().filter(|&(_, v)| v < start).collect();
|
||||||
if let Some(ref header_data) = self.headers.find_item(&n) {
|
self.header_ids = ids;
|
||||||
let header_to_delete = HeaderView::new(&header_data.data);
|
let hdrs = self.downloading_headers.drain().filter(|v| *v < start).collect();
|
||||||
let header_id = HeaderId {
|
self.downloading_headers = hdrs;
|
||||||
transactions_root: header_to_delete.transactions_root(),
|
let bodies = self.downloading_bodies.drain().filter(|v| *v < start).collect();
|
||||||
uncles: header_to_delete.uncles_hash()
|
self.downloading_bodies = bodies;
|
||||||
};
|
|
||||||
self.header_ids.remove(&header_id);
|
|
||||||
}
|
|
||||||
self.downloading_bodies.remove(&n);
|
|
||||||
self.downloading_headers.remove(&n);
|
|
||||||
}
|
|
||||||
self.headers.remove_from(&start);
|
self.headers.remove_from(&start);
|
||||||
self.bodies.remove_from(&start);
|
self.bodies.remove_from(&start);
|
||||||
}
|
}
|
||||||
@ -934,16 +926,17 @@ impl ChainSync {
|
|||||||
}
|
}
|
||||||
/// Called when peer sends us new transactions
|
/// Called when peer sends us new transactions
|
||||||
fn on_peer_transactions(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> {
|
fn on_peer_transactions(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> {
|
||||||
let chain = io.chain();
|
|
||||||
let item_count = r.item_count();
|
let item_count = r.item_count();
|
||||||
trace!(target: "sync", "{} -> Transactions ({} entries)", peer_id, item_count);
|
trace!(target: "sync", "{} -> Transactions ({} entries)", peer_id, item_count);
|
||||||
let fetch_latest_nonce = |a : &Address| chain.nonce(a);
|
|
||||||
|
|
||||||
let mut transaction_queue = self.transaction_queue.lock().unwrap();
|
let mut transactions = Vec::with_capacity(item_count);
|
||||||
for i in 0..item_count {
|
for i in 0..item_count {
|
||||||
let tx: SignedTransaction = try!(r.val_at(i));
|
let tx: SignedTransaction = try!(r.val_at(i));
|
||||||
let _ = transaction_queue.add(tx, &fetch_latest_nonce);
|
transactions.push(tx);
|
||||||
}
|
}
|
||||||
|
let chain = io.chain();
|
||||||
|
let fetch_nonce = |a: &Address| chain.nonce(a);
|
||||||
|
let _ = self.miner.import_transactions(transactions, fetch_nonce);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1099,7 +1092,7 @@ impl ChainSync {
|
|||||||
let rlp = UntrustedRlp::new(data);
|
let rlp = UntrustedRlp::new(data);
|
||||||
|
|
||||||
if packet_id != STATUS_PACKET && !self.peers.contains_key(&peer) {
|
if packet_id != STATUS_PACKET && !self.peers.contains_key(&peer) {
|
||||||
warn!(target:"sync", "Unexpected packet from unregistered peer: {}:{}", peer, io.peer_info(peer));
|
debug!(target:"sync", "Unexpected packet from unregistered peer: {}:{}", peer, io.peer_info(peer));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let result = match packet_id {
|
let result = match packet_id {
|
||||||
@ -1274,48 +1267,16 @@ impl ChainSync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// called when block is imported to chain, updates transactions queue and propagates the blocks
|
/// called when block is imported to chain, updates transactions queue and propagates the blocks
|
||||||
pub fn chain_new_blocks(&mut self, io: &mut SyncIo, good: &[H256], bad: &[H256], _retracted: &[H256]) {
|
pub fn chain_new_blocks(&mut self, io: &mut SyncIo, imported: &[H256], invalid: &[H256], enacted: &[H256], retracted: &[H256]) {
|
||||||
fn fetch_transactions(chain: &BlockChainClient, hash: &H256) -> Vec<SignedTransaction> {
|
// Notify miner
|
||||||
let block = chain
|
self.miner.chain_new_blocks(io.chain(), imported, invalid, enacted, retracted);
|
||||||
.block(BlockId::Hash(hash.clone()))
|
|
||||||
// Client should send message after commit to db and inserting to chain.
|
|
||||||
.expect("Expected in-chain blocks.");
|
|
||||||
let block = BlockView::new(&block);
|
|
||||||
block.transactions()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
let chain = io.chain();
|
|
||||||
let good = good.par_iter().map(|h| fetch_transactions(chain, h));
|
|
||||||
let bad = bad.par_iter().map(|h| fetch_transactions(chain, h));
|
|
||||||
|
|
||||||
good.for_each(|txs| {
|
|
||||||
let mut transaction_queue = self.transaction_queue.lock().unwrap();
|
|
||||||
let hashes = txs.iter().map(|tx| tx.hash()).collect::<Vec<H256>>();
|
|
||||||
transaction_queue.remove_all(&hashes, |a| chain.nonce(a));
|
|
||||||
});
|
|
||||||
bad.for_each(|txs| {
|
|
||||||
// populate sender
|
|
||||||
for tx in &txs {
|
|
||||||
let _sender = tx.sender();
|
|
||||||
}
|
|
||||||
let mut transaction_queue = self.transaction_queue.lock().unwrap();
|
|
||||||
let _ = transaction_queue.add_all(txs, |a| chain.nonce(a));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Propagate latests blocks
|
// Propagate latests blocks
|
||||||
self.propagate_latest_blocks(io);
|
self.propagate_latest_blocks(io);
|
||||||
// TODO [todr] propagate transactions?
|
// TODO [todr] propagate transactions?
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add transaction to the transaction queue
|
pub fn chain_new_head(&mut self, io: &mut SyncIo) {
|
||||||
pub fn insert_transaction<T>(&self, transaction: ethcore::transaction::SignedTransaction, fetch_nonce: &T) -> Result<(), Error>
|
self.miner.prepare_sealing(io.chain());
|
||||||
where T: Fn(&Address) -> U256
|
|
||||||
{
|
|
||||||
let mut queue = self.transaction_queue.lock().unwrap();
|
|
||||||
queue.add(transaction, fetch_nonce)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1328,6 +1289,7 @@ mod tests {
|
|||||||
use super::{PeerInfo, PeerAsking};
|
use super::{PeerInfo, PeerAsking};
|
||||||
use ethcore::header::*;
|
use ethcore::header::*;
|
||||||
use ethcore::client::*;
|
use ethcore::client::*;
|
||||||
|
use ethminer::{Miner, MinerService};
|
||||||
|
|
||||||
fn get_dummy_block(order: u32, parent_hash: H256) -> Bytes {
|
fn get_dummy_block(order: u32, parent_hash: H256) -> Bytes {
|
||||||
let mut header = Header::new();
|
let mut header = Header::new();
|
||||||
@ -1437,7 +1399,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn dummy_sync_with_peer(peer_latest_hash: H256) -> ChainSync {
|
fn dummy_sync_with_peer(peer_latest_hash: H256) -> ChainSync {
|
||||||
let mut sync = ChainSync::new(SyncConfig::default());
|
let mut sync = ChainSync::new(SyncConfig::default(), Miner::new());
|
||||||
sync.peers.insert(0,
|
sync.peers.insert(0,
|
||||||
PeerInfo {
|
PeerInfo {
|
||||||
protocol_version: 0,
|
protocol_version: 0,
|
||||||
@ -1658,15 +1620,15 @@ mod tests {
|
|||||||
let mut io = TestIo::new(&mut client, &mut queue, None);
|
let mut io = TestIo::new(&mut client, &mut queue, None);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
sync.chain_new_blocks(&mut io, &[], &good_blocks, &[]);
|
sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks);
|
||||||
assert_eq!(sync.transaction_queue.lock().unwrap().status().future, 0);
|
assert_eq!(sync.miner.status().transaction_queue_future, 0);
|
||||||
assert_eq!(sync.transaction_queue.lock().unwrap().status().pending, 1);
|
assert_eq!(sync.miner.status().transaction_queue_pending, 1);
|
||||||
sync.chain_new_blocks(&mut io, &good_blocks, &retracted_blocks, &[]);
|
sync.chain_new_blocks(&mut io, &good_blocks, &[], &[], &retracted_blocks);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
let status = sync.transaction_queue.lock().unwrap().status();
|
let status = sync.miner.status();
|
||||||
assert_eq!(status.pending, 1);
|
assert_eq!(status.transaction_queue_pending, 1);
|
||||||
assert_eq!(status.future, 0);
|
assert_eq!(status.transaction_queue_future, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -32,18 +32,21 @@
|
|||||||
//! extern crate ethcore_util as util;
|
//! extern crate ethcore_util as util;
|
||||||
//! extern crate ethcore;
|
//! extern crate ethcore;
|
||||||
//! extern crate ethsync;
|
//! extern crate ethsync;
|
||||||
|
//! extern crate ethminer;
|
||||||
//! use std::env;
|
//! use std::env;
|
||||||
//! use std::sync::Arc;
|
//! use std::sync::Arc;
|
||||||
//! use util::network::{NetworkService, NetworkConfiguration};
|
//! use util::network::{NetworkService, NetworkConfiguration};
|
||||||
//! use ethcore::client::{Client, ClientConfig};
|
//! use ethcore::client::{Client, ClientConfig};
|
||||||
//! use ethsync::{EthSync, SyncConfig};
|
//! use ethsync::{EthSync, SyncConfig};
|
||||||
|
//! use ethminer::Miner;
|
||||||
//! use ethcore::ethereum;
|
//! use ethcore::ethereum;
|
||||||
//!
|
//!
|
||||||
//! fn main() {
|
//! fn main() {
|
||||||
//! let mut service = NetworkService::start(NetworkConfiguration::new()).unwrap();
|
//! let mut service = NetworkService::start(NetworkConfiguration::new()).unwrap();
|
||||||
//! let dir = env::temp_dir();
|
//! let dir = env::temp_dir();
|
||||||
//! let client = Client::new(ClientConfig::default(), ethereum::new_frontier(), &dir, service.io().channel()).unwrap();
|
//! let client = Client::new(ClientConfig::default(), ethereum::new_frontier(), &dir, service.io().channel()).unwrap();
|
||||||
//! EthSync::register(&mut service, SyncConfig::default(), client);
|
//! let miner = Miner::new();
|
||||||
|
//! EthSync::register(&mut service, SyncConfig::default(), client, miner);
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
@ -52,28 +55,27 @@ extern crate log;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate ethcore_util as util;
|
extern crate ethcore_util as util;
|
||||||
extern crate ethcore;
|
extern crate ethcore;
|
||||||
|
extern crate ethminer;
|
||||||
extern crate env_logger;
|
extern crate env_logger;
|
||||||
extern crate time;
|
extern crate time;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
extern crate rayon;
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate heapsize;
|
extern crate heapsize;
|
||||||
|
|
||||||
use std::ops::*;
|
use std::ops::*;
|
||||||
use std::sync::*;
|
use std::sync::*;
|
||||||
use ethcore::client::Client;
|
|
||||||
use util::network::{NetworkProtocolHandler, NetworkService, NetworkContext, PeerId};
|
use util::network::{NetworkProtocolHandler, NetworkService, NetworkContext, PeerId};
|
||||||
use util::TimerToken;
|
use util::TimerToken;
|
||||||
use util::{U256, ONE_U256};
|
use util::{U256, ONE_U256};
|
||||||
use chain::ChainSync;
|
use ethcore::client::Client;
|
||||||
use ethcore::service::SyncMessage;
|
use ethcore::service::SyncMessage;
|
||||||
|
use ethminer::Miner;
|
||||||
use io::NetSyncIo;
|
use io::NetSyncIo;
|
||||||
|
use chain::ChainSync;
|
||||||
|
|
||||||
mod chain;
|
mod chain;
|
||||||
mod io;
|
mod io;
|
||||||
mod range_collection;
|
mod range_collection;
|
||||||
mod transaction_queue;
|
|
||||||
pub use transaction_queue::TransactionQueue;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
@ -99,8 +101,6 @@ impl Default for SyncConfig {
|
|||||||
pub trait SyncProvider: Send + Sync {
|
pub trait SyncProvider: Send + Sync {
|
||||||
/// Get sync status
|
/// Get sync status
|
||||||
fn status(&self) -> SyncStatus;
|
fn status(&self) -> SyncStatus;
|
||||||
/// Insert transaction in the sync transaction queue
|
|
||||||
fn insert_transaction(&self, transaction: ethcore::transaction::SignedTransaction);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ethereum network protocol handler
|
/// Ethereum network protocol handler
|
||||||
@ -115,10 +115,10 @@ pub use self::chain::{SyncStatus, SyncState};
|
|||||||
|
|
||||||
impl EthSync {
|
impl EthSync {
|
||||||
/// Creates and register protocol with the network service
|
/// Creates and register protocol with the network service
|
||||||
pub fn register(service: &mut NetworkService<SyncMessage>, config: SyncConfig, chain: Arc<Client>) -> Arc<EthSync> {
|
pub fn register(service: &mut NetworkService<SyncMessage>, config: SyncConfig, chain: Arc<Client>, miner: Arc<Miner>) -> Arc<EthSync> {
|
||||||
let sync = Arc::new(EthSync {
|
let sync = Arc::new(EthSync {
|
||||||
chain: chain,
|
chain: chain,
|
||||||
sync: RwLock::new(ChainSync::new(config)),
|
sync: RwLock::new(ChainSync::new(config, miner)),
|
||||||
});
|
});
|
||||||
service.register_protocol(sync.clone(), "eth", &[62u8, 63u8]).expect("Error registering eth protocol handler");
|
service.register_protocol(sync.clone(), "eth", &[62u8, 63u8]).expect("Error registering eth protocol handler");
|
||||||
sync
|
sync
|
||||||
@ -140,16 +140,6 @@ impl SyncProvider for EthSync {
|
|||||||
fn status(&self) -> SyncStatus {
|
fn status(&self) -> SyncStatus {
|
||||||
self.sync.read().unwrap().status()
|
self.sync.read().unwrap().status()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert transaction in transaction queue
|
|
||||||
fn insert_transaction(&self, transaction: ethcore::transaction::SignedTransaction) {
|
|
||||||
use util::numbers::*;
|
|
||||||
|
|
||||||
let nonce_fn = |a: &Address| self.chain.state().nonce(a) + U256::one();
|
|
||||||
let sync = self.sync.write().unwrap();
|
|
||||||
sync.insert_transaction(transaction, &nonce_fn).unwrap_or_else(
|
|
||||||
|e| warn!(target: "sync", "Error inserting transaction to queue: {:?}", e));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NetworkProtocolHandler<SyncMessage> for EthSync {
|
impl NetworkProtocolHandler<SyncMessage> for EthSync {
|
||||||
@ -174,13 +164,16 @@ impl NetworkProtocolHandler<SyncMessage> for EthSync {
|
|||||||
self.sync.write().unwrap().maintain_sync(&mut NetSyncIo::new(io, self.chain.deref()));
|
self.sync.write().unwrap().maintain_sync(&mut NetSyncIo::new(io, self.chain.deref()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(single_match)]
|
|
||||||
fn message(&self, io: &NetworkContext<SyncMessage>, message: &SyncMessage) {
|
fn message(&self, io: &NetworkContext<SyncMessage>, message: &SyncMessage) {
|
||||||
match *message {
|
match *message {
|
||||||
SyncMessage::NewChainBlocks { ref good, ref bad, ref retracted } => {
|
SyncMessage::NewChainBlocks { ref imported, ref invalid, ref enacted, ref retracted } => {
|
||||||
let mut sync_io = NetSyncIo::new(io, self.chain.deref());
|
let mut sync_io = NetSyncIo::new(io, self.chain.deref());
|
||||||
self.sync.write().unwrap().chain_new_blocks(&mut sync_io, good, bad, retracted);
|
self.sync.write().unwrap().chain_new_blocks(&mut sync_io, imported, invalid, enacted, retracted);
|
||||||
},
|
},
|
||||||
|
SyncMessage::NewChainHead => {
|
||||||
|
let mut sync_io = NetSyncIo::new(io, self.chain.deref());
|
||||||
|
self.sync.write().unwrap().chain_new_head(&mut sync_io);
|
||||||
|
}
|
||||||
_ => {/* Ignore other messages */},
|
_ => {/* Ignore other messages */},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -300,12 +300,17 @@ fn test_range() {
|
|||||||
let mut r = ranges.clone();
|
let mut r = ranges.clone();
|
||||||
r.remove_from(&20);
|
r.remove_from(&20);
|
||||||
assert_eq!(r.range_iter().cmp(vec![(2, &['b', 'c', 'd'][..]), (16, &['p', 'q', 'r'][..])]), Ordering::Equal);
|
assert_eq!(r.range_iter().cmp(vec![(2, &['b', 'c', 'd'][..]), (16, &['p', 'q', 'r'][..])]), Ordering::Equal);
|
||||||
r.remove_from(&17);
|
r.remove_from(&18);
|
||||||
assert_eq!(r.range_iter().cmp(vec![(2, &['b', 'c', 'd'][..]), (16, &['p'][..])]), Ordering::Equal);
|
assert!(!r.have_item(&18));
|
||||||
r.remove_from(&15);
|
assert_eq!(r.range_iter().cmp(vec![(2, &['b', 'c', 'd'][..]), (16, &['p', 'q'][..])]), Ordering::Equal);
|
||||||
|
r.remove_from(&16);
|
||||||
|
assert!(!r.have_item(&16));
|
||||||
assert_eq!(r.range_iter().cmp(vec![(2, &['b', 'c', 'd'][..])]), Ordering::Equal);
|
assert_eq!(r.range_iter().cmp(vec![(2, &['b', 'c', 'd'][..])]), Ordering::Equal);
|
||||||
r.remove_from(&3);
|
r.remove_from(&3);
|
||||||
assert_eq!(r.range_iter().cmp(vec![(2, &['b'][..])]), Ordering::Equal);
|
assert_eq!(r.range_iter().cmp(vec![(2, &['b'][..])]), Ordering::Equal);
|
||||||
|
r.remove_from(&1);
|
||||||
|
assert_eq!(r.range_iter().next(), None);
|
||||||
|
let mut r = ranges.clone();
|
||||||
r.remove_from(&2);
|
r.remove_from(&2);
|
||||||
assert_eq!(r.range_iter().next(), None);
|
assert_eq!(r.range_iter().next(), None);
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ use util::*;
|
|||||||
use ethcore::client::{TestBlockChainClient, BlockChainClient};
|
use ethcore::client::{TestBlockChainClient, BlockChainClient};
|
||||||
use io::SyncIo;
|
use io::SyncIo;
|
||||||
use chain::ChainSync;
|
use chain::ChainSync;
|
||||||
|
use ethminer::Miner;
|
||||||
use ::SyncConfig;
|
use ::SyncConfig;
|
||||||
|
|
||||||
pub struct TestIo<'p> {
|
pub struct TestIo<'p> {
|
||||||
@ -92,7 +93,7 @@ impl TestNet {
|
|||||||
for _ in 0..n {
|
for _ in 0..n {
|
||||||
net.peers.push(TestPeer {
|
net.peers.push(TestPeer {
|
||||||
chain: TestBlockChainClient::new(),
|
chain: TestBlockChainClient::new(),
|
||||||
sync: ChainSync::new(SyncConfig::default()),
|
sync: ChainSync::new(SyncConfig::default(), Miner::new()),
|
||||||
queue: VecDeque::new(),
|
queue: VecDeque::new(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -167,6 +168,6 @@ impl TestNet {
|
|||||||
|
|
||||||
pub fn trigger_chain_new_blocks(&mut self, peer_id: usize) {
|
pub fn trigger_chain_new_blocks(&mut self, peer_id: usize) {
|
||||||
let mut peer = self.peer_mut(peer_id);
|
let mut peer = self.peer_mut(peer_id);
|
||||||
peer.sync.chain_new_blocks(&mut TestIo::new(&mut peer.chain, &mut peer.queue, None), &[], &[], &[]);
|
peer.sync.chain_new_blocks(&mut TestIo::new(&mut peer.chain, &mut peer.queue, None), &[], &[], &[], &[]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
test.sh
2
test.sh
@ -1,4 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Running Parity Full Test Sute
|
# Running Parity Full Test Sute
|
||||||
|
|
||||||
cargo test --features ethcore/json-tests $1 -p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity
|
cargo test --features ethcore/json-tests $1 -p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethminer
|
||||||
|
@ -27,7 +27,7 @@ crossbeam = "0.2"
|
|||||||
slab = "0.1"
|
slab = "0.1"
|
||||||
sha3 = { path = "sha3" }
|
sha3 = { path = "sha3" }
|
||||||
serde = "0.7.0"
|
serde = "0.7.0"
|
||||||
clippy = { version = "0.0.49", optional = true }
|
clippy = { version = "0.0.50", optional = true }
|
||||||
json-tests = { path = "json-tests" }
|
json-tests = { path = "json-tests" }
|
||||||
rustc_version = "0.1.0"
|
rustc_version = "0.1.0"
|
||||||
igd = "0.4.2"
|
igd = "0.4.2"
|
||||||
|
@ -36,7 +36,6 @@
|
|||||||
//! The functions here are designed to be fast.
|
//! The functions here are designed to be fast.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
|
|
||||||
#[cfg(all(asm_available, target_arch="x86_64"))]
|
#[cfg(all(asm_available, target_arch="x86_64"))]
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -21,12 +21,13 @@ use network::NetworkError;
|
|||||||
use rlp::DecoderError;
|
use rlp::DecoderError;
|
||||||
use io;
|
use io;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use hash::H256;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
/// Error in database subsystem.
|
/// Error in database subsystem.
|
||||||
pub enum BaseDataError {
|
pub enum BaseDataError {
|
||||||
/// An entry was removed more times than inserted.
|
/// An entry was removed more times than inserted.
|
||||||
NegativelyReferencedHash,
|
NegativelyReferencedHash(H256),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -35,6 +35,7 @@ use std::env;
|
|||||||
pub struct ArchiveDB {
|
pub struct ArchiveDB {
|
||||||
overlay: MemoryDB,
|
overlay: MemoryDB,
|
||||||
backing: Arc<Database>,
|
backing: Arc<Database>,
|
||||||
|
latest_era: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// all keys must be at least 12 bytes
|
// all keys must be at least 12 bytes
|
||||||
@ -60,9 +61,11 @@ impl ArchiveDB {
|
|||||||
backing.put(&VERSION_KEY, &encode(&DB_VERSION)).expect("Error writing version to database");
|
backing.put(&VERSION_KEY, &encode(&DB_VERSION)).expect("Error writing version to database");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let latest_era = backing.get(&LATEST_ERA_KEY).expect("Low-level database error.").map(|val| decode::<u64>(&val));
|
||||||
ArchiveDB {
|
ArchiveDB {
|
||||||
overlay: MemoryDB::new(),
|
overlay: MemoryDB::new(),
|
||||||
backing: Arc::new(backing),
|
backing: Arc::new(backing),
|
||||||
|
latest_era: latest_era,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,6 +132,7 @@ impl JournalDB for ArchiveDB {
|
|||||||
Box::new(ArchiveDB {
|
Box::new(ArchiveDB {
|
||||||
overlay: MemoryDB::new(),
|
overlay: MemoryDB::new(),
|
||||||
backing: self.backing.clone(),
|
backing: self.backing.clone(),
|
||||||
|
latest_era: self.latest_era,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,10 +141,10 @@ impl JournalDB for ArchiveDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn is_empty(&self) -> bool {
|
fn is_empty(&self) -> bool {
|
||||||
self.backing.get(&LATEST_ERA_KEY).expect("Low level database error").is_none()
|
self.latest_era.is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn commit(&mut self, _: u64, _: &H256, _: Option<(u64, H256)>) -> Result<u32, UtilError> {
|
fn commit(&mut self, now: u64, _: &H256, _: Option<(u64, H256)>) -> Result<u32, UtilError> {
|
||||||
let batch = DBTransaction::new();
|
let batch = DBTransaction::new();
|
||||||
let mut inserts = 0usize;
|
let mut inserts = 0usize;
|
||||||
let mut deletes = 0usize;
|
let mut deletes = 0usize;
|
||||||
@ -156,6 +160,10 @@ impl JournalDB for ArchiveDB {
|
|||||||
deletes += 1;
|
deletes += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if self.latest_era.map_or(true, |e| now > e) {
|
||||||
|
try!(batch.put(&LATEST_ERA_KEY, &encode(&now)));
|
||||||
|
self.latest_era = Some(now);
|
||||||
|
}
|
||||||
try!(self.backing.write(batch));
|
try!(self.backing.write(batch));
|
||||||
Ok((inserts + deletes) as u32)
|
Ok((inserts + deletes) as u32)
|
||||||
}
|
}
|
||||||
|
@ -64,6 +64,7 @@ pub struct EarlyMergeDB {
|
|||||||
overlay: MemoryDB,
|
overlay: MemoryDB,
|
||||||
backing: Arc<Database>,
|
backing: Arc<Database>,
|
||||||
refs: Option<Arc<RwLock<HashMap<H256, RefInfo>>>>,
|
refs: Option<Arc<RwLock<HashMap<H256, RefInfo>>>>,
|
||||||
|
latest_era: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// all keys must be at least 12 bytes
|
// all keys must be at least 12 bytes
|
||||||
@ -90,11 +91,13 @@ impl EarlyMergeDB {
|
|||||||
backing.put(&VERSION_KEY, &encode(&DB_VERSION)).expect("Error writing version to database");
|
backing.put(&VERSION_KEY, &encode(&DB_VERSION)).expect("Error writing version to database");
|
||||||
}
|
}
|
||||||
|
|
||||||
let refs = Some(Arc::new(RwLock::new(EarlyMergeDB::read_refs(&backing))));
|
let (latest_era, refs) = EarlyMergeDB::read_refs(&backing);
|
||||||
|
let refs = Some(Arc::new(RwLock::new(refs)));
|
||||||
EarlyMergeDB {
|
EarlyMergeDB {
|
||||||
overlay: MemoryDB::new(),
|
overlay: MemoryDB::new(),
|
||||||
backing: Arc::new(backing),
|
backing: Arc::new(backing),
|
||||||
refs: refs,
|
refs: refs,
|
||||||
|
latest_era: latest_era,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +171,7 @@ impl EarlyMergeDB {
|
|||||||
trace!(target: "jdb.fine", "replay_keys: (end) refs={:?}", refs);
|
trace!(target: "jdb.fine", "replay_keys: (end) refs={:?}", refs);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kill_keys(deletes: &Vec<H256>, refs: &mut HashMap<H256, RefInfo>, batch: &DBTransaction, from: RemoveFrom, trace: bool) {
|
fn kill_keys(deletes: &[H256], refs: &mut HashMap<H256, RefInfo>, batch: &DBTransaction, from: RemoveFrom, trace: bool) {
|
||||||
// with a kill on {queue_refs: 1, in_archive: true}, we have two options:
|
// with a kill on {queue_refs: 1, in_archive: true}, we have two options:
|
||||||
// - convert to {queue_refs: 1, in_archive: false} (i.e. remove it from the conceptual archive)
|
// - convert to {queue_refs: 1, in_archive: false} (i.e. remove it from the conceptual archive)
|
||||||
// - convert to {queue_refs: 0, in_archive: true} (i.e. remove it from the conceptual queue)
|
// - convert to {queue_refs: 0, in_archive: true} (i.e. remove it from the conceptual queue)
|
||||||
@ -225,9 +228,9 @@ impl EarlyMergeDB {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn can_reconstruct_refs(&self) -> bool {
|
fn can_reconstruct_refs(&self) -> bool {
|
||||||
let reconstructed = Self::read_refs(&self.backing);
|
let (latest_era, reconstructed) = Self::read_refs(&self.backing);
|
||||||
let refs = self.refs.as_ref().unwrap().write().unwrap();
|
let refs = self.refs.as_ref().unwrap().write().unwrap();
|
||||||
if *refs != reconstructed {
|
if *refs != reconstructed || latest_era != self.latest_era {
|
||||||
let clean_refs = refs.iter().filter_map(|(k, v)| if reconstructed.get(k) == Some(v) {None} else {Some((k.clone(), v.clone()))}).collect::<HashMap<_, _>>();
|
let clean_refs = refs.iter().filter_map(|(k, v)| if reconstructed.get(k) == Some(v) {None} else {Some((k.clone(), v.clone()))}).collect::<HashMap<_, _>>();
|
||||||
let clean_recon = reconstructed.into_iter().filter_map(|(k, v)| if refs.get(&k) == Some(&v) {None} else {Some((k.clone(), v.clone()))}).collect::<HashMap<_, _>>();
|
let clean_recon = reconstructed.into_iter().filter_map(|(k, v)| if refs.get(&k) == Some(&v) {None} else {Some((k.clone(), v.clone()))}).collect::<HashMap<_, _>>();
|
||||||
warn!(target: "jdb", "mem: {:?} != log: {:?}", clean_refs, clean_recon);
|
warn!(target: "jdb", "mem: {:?} != log: {:?}", clean_refs, clean_recon);
|
||||||
@ -241,10 +244,12 @@ impl EarlyMergeDB {
|
|||||||
self.backing.get(&key.bytes()).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec())
|
self.backing.get(&key.bytes()).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_refs(db: &Database) -> HashMap<H256, RefInfo> {
|
fn read_refs(db: &Database) -> (Option<u64>, HashMap<H256, RefInfo>) {
|
||||||
let mut refs = HashMap::new();
|
let mut refs = HashMap::new();
|
||||||
|
let mut latest_era = None;
|
||||||
if let Some(val) = db.get(&LATEST_ERA_KEY).expect("Low-level database error.") {
|
if let Some(val) = db.get(&LATEST_ERA_KEY).expect("Low-level database error.") {
|
||||||
let mut era = decode::<u64>(&val);
|
let mut era = decode::<u64>(&val);
|
||||||
|
latest_era = Some(era);
|
||||||
loop {
|
loop {
|
||||||
let mut index = 0usize;
|
let mut index = 0usize;
|
||||||
while let Some(rlp_data) = db.get({
|
while let Some(rlp_data) = db.get({
|
||||||
@ -265,7 +270,7 @@ impl EarlyMergeDB {
|
|||||||
era -= 1;
|
era -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
refs
|
(latest_era, refs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,6 +325,7 @@ impl JournalDB for EarlyMergeDB {
|
|||||||
overlay: MemoryDB::new(),
|
overlay: MemoryDB::new(),
|
||||||
backing: self.backing.clone(),
|
backing: self.backing.clone(),
|
||||||
refs: self.refs.clone(),
|
refs: self.refs.clone(),
|
||||||
|
latest_era: self.latest_era.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,6 +340,8 @@ impl JournalDB for EarlyMergeDB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
|
||||||
fn commit(&mut self, now: u64, id: &H256, end: Option<(u64, H256)>) -> Result<u32, UtilError> {
|
fn commit(&mut self, now: u64, id: &H256, end: Option<(u64, H256)>) -> Result<u32, UtilError> {
|
||||||
// journal format:
|
// journal format:
|
||||||
// [era, 0] => [ id, [insert_0, ...], [remove_0, ...] ]
|
// [era, 0] => [ id, [insert_0, ...], [remove_0, ...] ]
|
||||||
@ -435,7 +443,10 @@ impl JournalDB for EarlyMergeDB {
|
|||||||
trace!(target: "jdb.ops", " Deletes: {:?}", removes);
|
trace!(target: "jdb.ops", " Deletes: {:?}", removes);
|
||||||
}
|
}
|
||||||
try!(batch.put(&last, r.as_raw()));
|
try!(batch.put(&last, r.as_raw()));
|
||||||
try!(batch.put(&LATEST_ERA_KEY, &encode(&now)));
|
if self.latest_era.map_or(true, |e| now > e) {
|
||||||
|
try!(batch.put(&LATEST_ERA_KEY, &encode(&now)));
|
||||||
|
self.latest_era = Some(now);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply old commits' details
|
// apply old commits' details
|
||||||
@ -464,7 +475,7 @@ impl JournalDB for EarlyMergeDB {
|
|||||||
if trace {
|
if trace {
|
||||||
trace!(target: "jdb.ops", " Finalising: {:?}", inserts);
|
trace!(target: "jdb.ops", " Finalising: {:?}", inserts);
|
||||||
}
|
}
|
||||||
for k in inserts.iter() {
|
for k in &inserts {
|
||||||
match refs.get(k).cloned() {
|
match refs.get(k).cloned() {
|
||||||
None => {
|
None => {
|
||||||
// [in archive] -> SHIFT remove -> SHIFT insert None->Some{queue_refs: 1, in_archive: true} -> TAKE remove Some{queue_refs: 1, in_archive: true}->None -> TAKE insert
|
// [in archive] -> SHIFT remove -> SHIFT insert None->Some{queue_refs: 1, in_archive: true} -> TAKE remove Some{queue_refs: 1, in_archive: true}->None -> TAKE insert
|
||||||
@ -480,7 +491,7 @@ impl JournalDB for EarlyMergeDB {
|
|||||||
Self::set_already_in(&batch, k);
|
Self::set_already_in(&batch, k);
|
||||||
refs.insert(k.clone(), RefInfo{ queue_refs: x - 1, in_archive: true });
|
refs.insert(k.clone(), RefInfo{ queue_refs: x - 1, in_archive: true });
|
||||||
}
|
}
|
||||||
Some( RefInfo{queue_refs: _, in_archive: true} ) => {
|
Some( RefInfo{in_archive: true, ..} ) => {
|
||||||
// Invalid! Reinserted the same key twice.
|
// Invalid! Reinserted the same key twice.
|
||||||
warn!("Key {} inserted twice into same fork.", k);
|
warn!("Key {} inserted twice into same fork.", k);
|
||||||
}
|
}
|
||||||
@ -552,6 +563,26 @@ mod tests {
|
|||||||
assert!(jdb.exists(&x));
|
assert!(jdb.exists(&x));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn insert_older_era() {
|
||||||
|
let mut jdb = EarlyMergeDB::new_temp();
|
||||||
|
let foo = jdb.insert(b"foo");
|
||||||
|
jdb.commit(0, &b"0a".sha3(), None).unwrap();
|
||||||
|
assert!(jdb.can_reconstruct_refs());
|
||||||
|
|
||||||
|
let bar = jdb.insert(b"bar");
|
||||||
|
jdb.commit(1, &b"1".sha3(), Some((0, b"0a".sha3()))).unwrap();
|
||||||
|
assert!(jdb.can_reconstruct_refs());
|
||||||
|
|
||||||
|
jdb.remove(&bar);
|
||||||
|
jdb.commit(0, &b"0b".sha3(), None).unwrap();
|
||||||
|
assert!(jdb.can_reconstruct_refs());
|
||||||
|
jdb.commit(2, &b"2".sha3(), Some((1, b"1".sha3()))).unwrap();
|
||||||
|
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
assert!(jdb.exists(&bar));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn long_history() {
|
fn long_history() {
|
||||||
// history is 3
|
// history is 3
|
||||||
|
@ -23,6 +23,7 @@ pub mod traits;
|
|||||||
mod archivedb;
|
mod archivedb;
|
||||||
mod earlymergedb;
|
mod earlymergedb;
|
||||||
mod overlayrecentdb;
|
mod overlayrecentdb;
|
||||||
|
mod refcounteddb;
|
||||||
|
|
||||||
/// Export the JournalDB trait.
|
/// Export the JournalDB trait.
|
||||||
pub use self::traits::JournalDB;
|
pub use self::traits::JournalDB;
|
||||||
@ -75,6 +76,6 @@ pub fn new(path: &str, algorithm: Algorithm) -> Box<JournalDB> {
|
|||||||
Algorithm::Archive => Box::new(archivedb::ArchiveDB::new(path)),
|
Algorithm::Archive => Box::new(archivedb::ArchiveDB::new(path)),
|
||||||
Algorithm::EarlyMerge => Box::new(earlymergedb::EarlyMergeDB::new(path)),
|
Algorithm::EarlyMerge => Box::new(earlymergedb::EarlyMergeDB::new(path)),
|
||||||
Algorithm::OverlayRecent => Box::new(overlayrecentdb::OverlayRecentDB::new(path)),
|
Algorithm::OverlayRecent => Box::new(overlayrecentdb::OverlayRecentDB::new(path)),
|
||||||
_ => unimplemented!(),
|
Algorithm::RefCounted => Box::new(refcounteddb::RefCountedDB::new(path)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ use super::JournalDB;
|
|||||||
/// 6. For a canonical journal record that becomes ancient delete its removals from the disk only if
|
/// 6. For a canonical journal record that becomes ancient delete its removals from the disk only if
|
||||||
/// the removed key is not present in the history overlay.
|
/// the removed key is not present in the history overlay.
|
||||||
/// 7. Delete ancient record from memory and disk.
|
/// 7. Delete ancient record from memory and disk.
|
||||||
///
|
|
||||||
pub struct OverlayRecentDB {
|
pub struct OverlayRecentDB {
|
||||||
transaction_overlay: MemoryDB,
|
transaction_overlay: MemoryDB,
|
||||||
backing: Arc<Database>,
|
backing: Arc<Database>,
|
||||||
@ -66,7 +66,7 @@ pub struct OverlayRecentDB {
|
|||||||
struct JournalOverlay {
|
struct JournalOverlay {
|
||||||
backing_overlay: MemoryDB,
|
backing_overlay: MemoryDB,
|
||||||
journal: HashMap<u64, Vec<JournalEntry>>,
|
journal: HashMap<u64, Vec<JournalEntry>>,
|
||||||
latest_era: u64,
|
latest_era: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
@ -152,10 +152,10 @@ impl OverlayRecentDB {
|
|||||||
let mut journal = HashMap::new();
|
let mut journal = HashMap::new();
|
||||||
let mut overlay = MemoryDB::new();
|
let mut overlay = MemoryDB::new();
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
let mut latest_era = 0;
|
let mut latest_era = None;
|
||||||
if let Some(val) = db.get(&LATEST_ERA_KEY).expect("Low-level database error.") {
|
if let Some(val) = db.get(&LATEST_ERA_KEY).expect("Low-level database error.") {
|
||||||
latest_era = decode::<u64>(&val);
|
let mut era = decode::<u64>(&val);
|
||||||
let mut era = latest_era;
|
latest_era = Some(era);
|
||||||
loop {
|
loop {
|
||||||
let mut index = 0usize;
|
let mut index = 0usize;
|
||||||
while let Some(rlp_data) = db.get({
|
while let Some(rlp_data) = db.get({
|
||||||
@ -236,14 +236,14 @@ impl JournalDB for OverlayRecentDB {
|
|||||||
r.append(&removed_keys);
|
r.append(&removed_keys);
|
||||||
|
|
||||||
let mut k = RlpStream::new_list(3);
|
let mut k = RlpStream::new_list(3);
|
||||||
let index = journal_overlay.journal.get(&now).map(|j| j.len()).unwrap_or(0);
|
let index = journal_overlay.journal.get(&now).map_or(0, |j| j.len());
|
||||||
k.append(&now);
|
k.append(&now);
|
||||||
k.append(&index);
|
k.append(&index);
|
||||||
k.append(&&PADDING[..]);
|
k.append(&&PADDING[..]);
|
||||||
try!(batch.put(&k.drain(), r.as_raw()));
|
try!(batch.put(&k.drain(), r.as_raw()));
|
||||||
if now >= journal_overlay.latest_era {
|
if journal_overlay.latest_era.map_or(true, |e| now > e) {
|
||||||
try!(batch.put(&LATEST_ERA_KEY, &encode(&now)));
|
try!(batch.put(&LATEST_ERA_KEY, &encode(&now)));
|
||||||
journal_overlay.latest_era = now;
|
journal_overlay.latest_era = Some(now);
|
||||||
}
|
}
|
||||||
journal_overlay.journal.entry(now).or_insert_with(Vec::new).push(JournalEntry { id: id.clone(), insertions: inserted_keys, deletions: removed_keys });
|
journal_overlay.journal.entry(now).or_insert_with(Vec::new).push(JournalEntry { id: id.clone(), insertions: inserted_keys, deletions: removed_keys });
|
||||||
}
|
}
|
||||||
@ -870,4 +870,24 @@ mod tests {
|
|||||||
assert!(!jdb.exists(&bar));
|
assert!(!jdb.exists(&bar));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn insert_older_era() {
|
||||||
|
let mut jdb = OverlayRecentDB::new_temp();
|
||||||
|
let foo = jdb.insert(b"foo");
|
||||||
|
jdb.commit(0, &b"0a".sha3(), None).unwrap();
|
||||||
|
assert!(jdb.can_reconstruct_refs());
|
||||||
|
|
||||||
|
let bar = jdb.insert(b"bar");
|
||||||
|
jdb.commit(1, &b"1".sha3(), Some((0, b"0a".sha3()))).unwrap();
|
||||||
|
assert!(jdb.can_reconstruct_refs());
|
||||||
|
|
||||||
|
jdb.remove(&bar);
|
||||||
|
jdb.commit(0, &b"0b".sha3(), None).unwrap();
|
||||||
|
assert!(jdb.can_reconstruct_refs());
|
||||||
|
jdb.commit(2, &b"2".sha3(), Some((1, b"1".sha3()))).unwrap();
|
||||||
|
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
assert!(jdb.exists(&bar));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
285
util/src/journaldb/refcounteddb.rs
Normal file
285
util/src/journaldb/refcounteddb.rs
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Disk-backed, ref-counted JournalDB implementation.
|
||||||
|
|
||||||
|
use common::*;
|
||||||
|
use rlp::*;
|
||||||
|
use hashdb::*;
|
||||||
|
use overlaydb::*;
|
||||||
|
use super::traits::JournalDB;
|
||||||
|
use kvdb::{Database, DBTransaction, DatabaseConfig};
|
||||||
|
#[cfg(test)]
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
/// Implementation of the HashDB trait for a disk-backed database with a memory overlay
|
||||||
|
/// and latent-removal semantics.
|
||||||
|
///
|
||||||
|
/// Like OverlayDB, there is a memory overlay; `commit()` must be called in order to
|
||||||
|
/// write operations out to disk. Unlike OverlayDB, `remove()` operations do not take effect
|
||||||
|
/// immediately. Rather some age (based on a linear but arbitrary metric) must pass before
|
||||||
|
/// the removals actually take effect.
|
||||||
|
pub struct RefCountedDB {
|
||||||
|
forward: OverlayDB,
|
||||||
|
backing: Arc<Database>,
|
||||||
|
latest_era: Option<u64>,
|
||||||
|
inserts: Vec<H256>,
|
||||||
|
removes: Vec<H256>,
|
||||||
|
}
|
||||||
|
|
||||||
|
const LATEST_ERA_KEY : [u8; 12] = [ b'l', b'a', b's', b't', 0, 0, 0, 0, 0, 0, 0, 0 ];
|
||||||
|
const VERSION_KEY : [u8; 12] = [ b'j', b'v', b'e', b'r', 0, 0, 0, 0, 0, 0, 0, 0 ];
|
||||||
|
const DB_VERSION : u32 = 512;
|
||||||
|
const PADDING : [u8; 10] = [ 0u8; 10 ];
|
||||||
|
|
||||||
|
impl RefCountedDB {
|
||||||
|
/// Create a new instance given a `backing` database.
|
||||||
|
pub fn new(path: &str) -> RefCountedDB {
|
||||||
|
let opts = DatabaseConfig {
|
||||||
|
prefix_size: Some(12) //use 12 bytes as prefix, this must match account_db prefix
|
||||||
|
};
|
||||||
|
let backing = Database::open(&opts, path).unwrap_or_else(|e| {
|
||||||
|
panic!("Error opening state db: {}", e);
|
||||||
|
});
|
||||||
|
if !backing.is_empty() {
|
||||||
|
match backing.get(&VERSION_KEY).map(|d| d.map(|v| decode::<u32>(&v))) {
|
||||||
|
Ok(Some(DB_VERSION)) => {},
|
||||||
|
v => panic!("Incompatible DB version, expected {}, got {:?}", DB_VERSION, v)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
backing.put(&VERSION_KEY, &encode(&DB_VERSION)).expect("Error writing version to database");
|
||||||
|
}
|
||||||
|
|
||||||
|
let backing = Arc::new(backing);
|
||||||
|
let latest_era = backing.get(&LATEST_ERA_KEY).expect("Low-level database error.").map(|val| decode::<u64>(&val));
|
||||||
|
|
||||||
|
RefCountedDB {
|
||||||
|
forward: OverlayDB::new_with_arc(backing.clone()),
|
||||||
|
backing: backing,
|
||||||
|
inserts: vec![],
|
||||||
|
removes: vec![],
|
||||||
|
latest_era: latest_era,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new instance with an anonymous temporary database.
|
||||||
|
#[cfg(test)]
|
||||||
|
fn new_temp() -> RefCountedDB {
|
||||||
|
let mut dir = env::temp_dir();
|
||||||
|
dir.push(H32::random().hex());
|
||||||
|
Self::new(dir.to_str().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HashDB for RefCountedDB {
|
||||||
|
fn keys(&self) -> HashMap<H256, i32> { self.forward.keys() }
|
||||||
|
fn lookup(&self, key: &H256) -> Option<&[u8]> { self.forward.lookup(key) }
|
||||||
|
fn exists(&self, key: &H256) -> bool { self.forward.exists(key) }
|
||||||
|
fn insert(&mut self, value: &[u8]) -> H256 { let r = self.forward.insert(value); self.inserts.push(r.clone()); r }
|
||||||
|
fn emplace(&mut self, key: H256, value: Bytes) { self.inserts.push(key.clone()); self.forward.emplace(key, value); }
|
||||||
|
fn kill(&mut self, key: &H256) { self.removes.push(key.clone()); }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JournalDB for RefCountedDB {
|
||||||
|
fn spawn(&self) -> Box<JournalDB> {
|
||||||
|
Box::new(RefCountedDB {
|
||||||
|
forward: self.forward.clone(),
|
||||||
|
backing: self.backing.clone(),
|
||||||
|
latest_era: self.latest_era,
|
||||||
|
inserts: self.inserts.clone(),
|
||||||
|
removes: self.removes.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mem_used(&self) -> usize {
|
||||||
|
self.inserts.heap_size_of_children() + self.removes.heap_size_of_children()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_empty(&self) -> bool {
|
||||||
|
self.latest_era.is_none()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn commit(&mut self, now: u64, id: &H256, end: Option<(u64, H256)>) -> Result<u32, UtilError> {
|
||||||
|
// journal format:
|
||||||
|
// [era, 0] => [ id, [insert_0, ...], [remove_0, ...] ]
|
||||||
|
// [era, 1] => [ id, [insert_0, ...], [remove_0, ...] ]
|
||||||
|
// [era, n] => [ ... ]
|
||||||
|
|
||||||
|
// TODO: store last_era, reclaim_period.
|
||||||
|
|
||||||
|
// when we make a new commit, we journal the inserts and removes.
|
||||||
|
// for each end_era that we journaled that we are no passing by,
|
||||||
|
// we remove all of its removes assuming it is canonical and all
|
||||||
|
// of its inserts otherwise.
|
||||||
|
|
||||||
|
// record new commit's details.
|
||||||
|
let batch = DBTransaction::new();
|
||||||
|
{
|
||||||
|
let mut index = 0usize;
|
||||||
|
let mut last;
|
||||||
|
|
||||||
|
while try!(self.backing.get({
|
||||||
|
let mut r = RlpStream::new_list(3);
|
||||||
|
r.append(&now);
|
||||||
|
r.append(&index);
|
||||||
|
r.append(&&PADDING[..]);
|
||||||
|
last = r.drain();
|
||||||
|
&last
|
||||||
|
})).is_some() {
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut r = RlpStream::new_list(3);
|
||||||
|
r.append(id);
|
||||||
|
r.append(&self.inserts);
|
||||||
|
r.append(&self.removes);
|
||||||
|
try!(batch.put(&last, r.as_raw()));
|
||||||
|
|
||||||
|
trace!(target: "rcdb", "new journal for time #{}.{} => {}: inserts={:?}, removes={:?}", now, index, id, self.inserts, self.removes);
|
||||||
|
|
||||||
|
self.inserts.clear();
|
||||||
|
self.removes.clear();
|
||||||
|
|
||||||
|
if self.latest_era.map_or(true, |e| now > e) {
|
||||||
|
try!(batch.put(&LATEST_ERA_KEY, &encode(&now)));
|
||||||
|
self.latest_era = Some(now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply old commits' details
|
||||||
|
if let Some((end_era, canon_id)) = end {
|
||||||
|
let mut index = 0usize;
|
||||||
|
let mut last;
|
||||||
|
while let Some(rlp_data) = {
|
||||||
|
// trace!(target: "rcdb", "checking for journal #{}.{}", end_era, index);
|
||||||
|
try!(self.backing.get({
|
||||||
|
let mut r = RlpStream::new_list(3);
|
||||||
|
r.append(&end_era);
|
||||||
|
r.append(&index);
|
||||||
|
r.append(&&PADDING[..]);
|
||||||
|
last = r.drain();
|
||||||
|
&last
|
||||||
|
}))
|
||||||
|
} {
|
||||||
|
let rlp = Rlp::new(&rlp_data);
|
||||||
|
let our_id: H256 = rlp.val_at(0);
|
||||||
|
let to_remove: Vec<H256> = rlp.val_at(if canon_id == our_id {2} else {1});
|
||||||
|
trace!(target: "rcdb", "delete journal for time #{}.{}=>{}, (canon was {}): deleting {:?}", end_era, index, our_id, canon_id, to_remove);
|
||||||
|
for i in &to_remove {
|
||||||
|
self.forward.remove(i);
|
||||||
|
}
|
||||||
|
try!(batch.delete(&last));
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let r = try!(self.forward.commit_to_batch(&batch));
|
||||||
|
try!(self.backing.write(batch));
|
||||||
|
Ok(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use common::*;
|
||||||
|
use super::*;
|
||||||
|
use super::super::traits::JournalDB;
|
||||||
|
use hashdb::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn long_history() {
|
||||||
|
// history is 3
|
||||||
|
let mut jdb = RefCountedDB::new_temp();
|
||||||
|
let h = jdb.insert(b"foo");
|
||||||
|
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
||||||
|
assert!(jdb.exists(&h));
|
||||||
|
jdb.remove(&h);
|
||||||
|
jdb.commit(1, &b"1".sha3(), None).unwrap();
|
||||||
|
assert!(jdb.exists(&h));
|
||||||
|
jdb.commit(2, &b"2".sha3(), None).unwrap();
|
||||||
|
assert!(jdb.exists(&h));
|
||||||
|
jdb.commit(3, &b"3".sha3(), Some((0, b"0".sha3()))).unwrap();
|
||||||
|
assert!(jdb.exists(&h));
|
||||||
|
jdb.commit(4, &b"4".sha3(), Some((1, b"1".sha3()))).unwrap();
|
||||||
|
assert!(!jdb.exists(&h));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn complex() {
|
||||||
|
// history is 1
|
||||||
|
let mut jdb = RefCountedDB::new_temp();
|
||||||
|
|
||||||
|
let foo = jdb.insert(b"foo");
|
||||||
|
let bar = jdb.insert(b"bar");
|
||||||
|
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
assert!(jdb.exists(&bar));
|
||||||
|
|
||||||
|
jdb.remove(&foo);
|
||||||
|
jdb.remove(&bar);
|
||||||
|
let baz = jdb.insert(b"baz");
|
||||||
|
jdb.commit(1, &b"1".sha3(), Some((0, b"0".sha3()))).unwrap();
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
assert!(jdb.exists(&bar));
|
||||||
|
assert!(jdb.exists(&baz));
|
||||||
|
|
||||||
|
let foo = jdb.insert(b"foo");
|
||||||
|
jdb.remove(&baz);
|
||||||
|
jdb.commit(2, &b"2".sha3(), Some((1, b"1".sha3()))).unwrap();
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
assert!(!jdb.exists(&bar));
|
||||||
|
assert!(jdb.exists(&baz));
|
||||||
|
|
||||||
|
jdb.remove(&foo);
|
||||||
|
jdb.commit(3, &b"3".sha3(), Some((2, b"2".sha3()))).unwrap();
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
assert!(!jdb.exists(&bar));
|
||||||
|
assert!(!jdb.exists(&baz));
|
||||||
|
|
||||||
|
jdb.commit(4, &b"4".sha3(), Some((3, b"3".sha3()))).unwrap();
|
||||||
|
assert!(!jdb.exists(&foo));
|
||||||
|
assert!(!jdb.exists(&bar));
|
||||||
|
assert!(!jdb.exists(&baz));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fork() {
|
||||||
|
// history is 1
|
||||||
|
let mut jdb = RefCountedDB::new_temp();
|
||||||
|
|
||||||
|
let foo = jdb.insert(b"foo");
|
||||||
|
let bar = jdb.insert(b"bar");
|
||||||
|
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
assert!(jdb.exists(&bar));
|
||||||
|
|
||||||
|
jdb.remove(&foo);
|
||||||
|
let baz = jdb.insert(b"baz");
|
||||||
|
jdb.commit(1, &b"1a".sha3(), Some((0, b"0".sha3()))).unwrap();
|
||||||
|
|
||||||
|
jdb.remove(&bar);
|
||||||
|
jdb.commit(1, &b"1b".sha3(), Some((0, b"0".sha3()))).unwrap();
|
||||||
|
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
assert!(jdb.exists(&bar));
|
||||||
|
assert!(jdb.exists(&baz));
|
||||||
|
|
||||||
|
jdb.commit(2, &b"2b".sha3(), Some((1, b"1b".sha3()))).unwrap();
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
assert!(!jdb.exists(&baz));
|
||||||
|
assert!(!jdb.exists(&bar));
|
||||||
|
}
|
||||||
|
}
|
@ -542,6 +542,8 @@ impl KeyDirectory {
|
|||||||
if removes.is_empty() { return; }
|
if removes.is_empty() { return; }
|
||||||
let mut cache = self.cache.write().unwrap();
|
let mut cache = self.cache.write().unwrap();
|
||||||
for key in removes { cache.remove(&key); }
|
for key in removes { cache.remove(&key); }
|
||||||
|
|
||||||
|
cache.shrink_to_fit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reports how many keys are currently cached.
|
/// Reports how many keys are currently cached.
|
||||||
|
@ -161,6 +161,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(feature="heavy-tests")]
|
||||||
fn can_decrypt_with_imported() {
|
fn can_decrypt_with_imported() {
|
||||||
use keys::store::EncryptedHashMap;
|
use keys::store::EncryptedHashMap;
|
||||||
|
|
||||||
|
@ -120,17 +120,37 @@ impl AccountProvider for AccountService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for AccountService {
|
||||||
|
fn default() -> Self {
|
||||||
|
AccountService::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl AccountService {
|
impl AccountService {
|
||||||
/// New account service with the default location
|
/// New account service with the default location
|
||||||
pub fn new() -> AccountService {
|
pub fn new() -> Self {
|
||||||
let secret_store = RwLock::new(SecretStore::new());
|
let secret_store = RwLock::new(SecretStore::new());
|
||||||
secret_store.write().unwrap().try_import_existing();
|
secret_store.write().unwrap().try_import_existing();
|
||||||
AccountService {
|
AccountService {
|
||||||
secret_store: secret_store
|
secret_store: secret_store
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn new_test(temp: &::devtools::RandomTempPath) -> Self {
|
||||||
|
let secret_store = RwLock::new(SecretStore::new_test(temp));
|
||||||
|
AccountService {
|
||||||
|
secret_store: secret_store
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ticks the account service
|
||||||
|
pub fn tick(&self) {
|
||||||
|
self.secret_store.write().unwrap().collect_garbage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Default for SecretStore {
|
impl Default for SecretStore {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
SecretStore::new()
|
SecretStore::new()
|
||||||
@ -250,6 +270,20 @@ impl SecretStore {
|
|||||||
let unlock = try!(read_lock.get(account).ok_or(SigningError::AccountNotUnlocked));
|
let unlock = try!(read_lock.get(account).ok_or(SigningError::AccountNotUnlocked));
|
||||||
Ok(unlock.secret as crypto::Secret)
|
Ok(unlock.secret as crypto::Secret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Makes account unlocks expire and removes unused key files from memory
|
||||||
|
pub fn collect_garbage(&mut self) {
|
||||||
|
let mut garbage_lock = self.unlocks.write().unwrap();
|
||||||
|
self.directory.collect_garbage();
|
||||||
|
let utc = UTC::now();
|
||||||
|
let expired_addresses = garbage_lock.iter()
|
||||||
|
.filter(|&(_, unlock)| unlock.expires < utc)
|
||||||
|
.map(|(address, _)| address.clone()).collect::<Vec<Address>>();
|
||||||
|
|
||||||
|
for expired in expired_addresses { garbage_lock.remove(&expired); }
|
||||||
|
|
||||||
|
garbage_lock.shrink_to_fit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn derive_key_iterations(password: &str, salt: &H256, c: u32) -> (Bytes, Bytes) {
|
fn derive_key_iterations(password: &str, salt: &H256, c: u32) -> (Bytes, Bytes) {
|
||||||
@ -356,12 +390,11 @@ impl EncryptedHashMap<H128> for SecretStore {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(all(test, feature="heavy-tests"))]
|
||||||
mod vector_tests {
|
mod vector_tests {
|
||||||
use super::{derive_mac,derive_key_iterations};
|
use super::{derive_mac,derive_key_iterations};
|
||||||
use common::*;
|
use common::*;
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn mac_vector() {
|
fn mac_vector() {
|
||||||
let password = "testpassword";
|
let password = "testpassword";
|
||||||
@ -388,6 +421,7 @@ mod tests {
|
|||||||
use devtools::*;
|
use devtools::*;
|
||||||
use common::*;
|
use common::*;
|
||||||
use crypto::KeyPair;
|
use crypto::KeyPair;
|
||||||
|
use chrono::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_insert() {
|
fn can_insert() {
|
||||||
@ -464,6 +498,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(feature="heavy-tests")]
|
||||||
fn can_get() {
|
fn can_get() {
|
||||||
let temp = RandomTempPath::create_dir();
|
let temp = RandomTempPath::create_dir();
|
||||||
let key_id = {
|
let key_id = {
|
||||||
@ -568,9 +603,37 @@ mod tests {
|
|||||||
let temp = RandomTempPath::create_dir();
|
let temp = RandomTempPath::create_dir();
|
||||||
let mut sstore = SecretStore::new_test(&temp);
|
let mut sstore = SecretStore::new_test(&temp);
|
||||||
let addr = sstore.new_account("test").unwrap();
|
let addr = sstore.new_account("test").unwrap();
|
||||||
let _ok = sstore.unlock_account(&addr, "test").unwrap();
|
sstore.unlock_account(&addr, "test").unwrap();
|
||||||
let secret = sstore.account_secret(&addr).unwrap();
|
let secret = sstore.account_secret(&addr).unwrap();
|
||||||
let kp = KeyPair::from_secret(secret).unwrap();
|
let kp = KeyPair::from_secret(secret).unwrap();
|
||||||
assert_eq!(Address::from(kp.public().sha3()), addr);
|
assert_eq!(Address::from(kp.public().sha3()), addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_create_service() {
|
||||||
|
let temp = RandomTempPath::create_dir();
|
||||||
|
let svc = AccountService::new_test(&temp);
|
||||||
|
assert!(svc.accounts().unwrap().is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn accounts_expire() {
|
||||||
|
use std::collections::hash_map::*;
|
||||||
|
|
||||||
|
let temp = RandomTempPath::create_dir();
|
||||||
|
let svc = AccountService::new_test(&temp);
|
||||||
|
let address = svc.new_account("pass").unwrap();
|
||||||
|
svc.unlock_account(&address, "pass").unwrap();
|
||||||
|
assert!(svc.account_secret(&address).is_ok());
|
||||||
|
{
|
||||||
|
let ss_rw = svc.secret_store.write().unwrap();
|
||||||
|
let mut ua_rw = ss_rw.unlocks.write().unwrap();
|
||||||
|
let entry = ua_rw.entry(address);
|
||||||
|
if let Entry::Occupied(mut occupied) = entry { occupied.get_mut().expires = UTC::now() - Duration::minutes(1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
svc.tick();
|
||||||
|
|
||||||
|
assert!(svc.account_secret(&address).is_err());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -513,8 +513,14 @@ mod tests {
|
|||||||
buf_size: usize,
|
buf_size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for TestSocket {
|
||||||
|
fn default() -> Self {
|
||||||
|
TestSocket::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TestSocket {
|
impl TestSocket {
|
||||||
fn new() -> TestSocket {
|
fn new() -> Self {
|
||||||
TestSocket {
|
TestSocket {
|
||||||
read_buffer: vec![],
|
read_buffer: vec![],
|
||||||
write_buffer: vec![],
|
write_buffer: vec![],
|
||||||
@ -593,8 +599,14 @@ mod tests {
|
|||||||
|
|
||||||
type TestConnection = GenericConnection<TestSocket>;
|
type TestConnection = GenericConnection<TestSocket>;
|
||||||
|
|
||||||
|
impl Default for TestConnection {
|
||||||
|
fn default() -> Self {
|
||||||
|
TestConnection::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TestConnection {
|
impl TestConnection {
|
||||||
pub fn new() -> TestConnection {
|
pub fn new() -> Self {
|
||||||
TestConnection {
|
TestConnection {
|
||||||
token: 999998888usize,
|
token: 999998888usize,
|
||||||
socket: TestSocket::new(),
|
socket: TestSocket::new(),
|
||||||
@ -609,8 +621,14 @@ mod tests {
|
|||||||
|
|
||||||
type TestBrokenConnection = GenericConnection<TestBrokenSocket>;
|
type TestBrokenConnection = GenericConnection<TestBrokenSocket>;
|
||||||
|
|
||||||
|
impl Default for TestBrokenConnection {
|
||||||
|
fn default() -> Self {
|
||||||
|
TestBrokenConnection::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TestBrokenConnection {
|
impl TestBrokenConnection {
|
||||||
pub fn new() -> TestBrokenConnection {
|
pub fn new() -> Self {
|
||||||
TestBrokenConnection {
|
TestBrokenConnection {
|
||||||
token: 999998888usize,
|
token: 999998888usize,
|
||||||
socket: TestBrokenSocket { error: "test broken socket".to_owned() },
|
socket: TestBrokenSocket { error: "test broken socket".to_owned() },
|
||||||
|
@ -18,7 +18,6 @@ use bytes::Bytes;
|
|||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::collections::{HashSet, HashMap, BTreeMap, VecDeque};
|
use std::collections::{HashSet, HashMap, BTreeMap, VecDeque};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::cmp;
|
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use mio::*;
|
use mio::*;
|
||||||
use mio::udp::*;
|
use mio::udp::*;
|
||||||
@ -407,27 +406,34 @@ impl Discovery {
|
|||||||
let target: NodeId = try!(rlp.val_at(0));
|
let target: NodeId = try!(rlp.val_at(0));
|
||||||
let timestamp: u64 = try!(rlp.val_at(1));
|
let timestamp: u64 = try!(rlp.val_at(1));
|
||||||
try!(self.check_timestamp(timestamp));
|
try!(self.check_timestamp(timestamp));
|
||||||
let limit = (MAX_DATAGRAM_SIZE - 109) / 90;
|
|
||||||
let nearest = Discovery::nearest_node_entries(&target, &self.node_buckets);
|
let nearest = Discovery::nearest_node_entries(&target, &self.node_buckets);
|
||||||
if nearest.is_empty() {
|
if nearest.is_empty() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let mut rlp = RlpStream::new_list(1);
|
let mut packets = Discovery::prepare_neighbours_packets(&nearest);
|
||||||
rlp.begin_list(cmp::min(limit, nearest.len()));
|
for p in packets.drain(..) {
|
||||||
for n in 0 .. nearest.len() {
|
self.send_packet(PACKET_NEIGHBOURS, &from, &p);
|
||||||
rlp.begin_list(4);
|
|
||||||
nearest[n].endpoint.to_rlp(&mut rlp);
|
|
||||||
rlp.append(&nearest[n].id);
|
|
||||||
if (n + 1) % limit == 0 || n == nearest.len() - 1 {
|
|
||||||
self.send_packet(PACKET_NEIGHBOURS, &from, &rlp.drain());
|
|
||||||
trace!(target: "discovery", "Sent {} Neighbours to {:?}", n, &from);
|
|
||||||
rlp = RlpStream::new_list(1);
|
|
||||||
rlp.begin_list(cmp::min(limit, nearest.len() - n));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
trace!(target: "discovery", "Sent {} Neighbours to {:?}", nearest.len(), &from);
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn prepare_neighbours_packets(nearest: &[NodeEntry]) -> Vec<Bytes> {
|
||||||
|
let limit = (MAX_DATAGRAM_SIZE - 109) / 90;
|
||||||
|
let chunks = nearest.chunks(limit);
|
||||||
|
let packets = chunks.map(|c| {
|
||||||
|
let mut rlp = RlpStream::new_list(1);
|
||||||
|
rlp.begin_list(c.len());
|
||||||
|
for n in 0 .. c.len() {
|
||||||
|
rlp.begin_list(4);
|
||||||
|
c[n].endpoint.to_rlp(&mut rlp);
|
||||||
|
rlp.append(&c[n].id);
|
||||||
|
}
|
||||||
|
rlp.out()
|
||||||
|
});
|
||||||
|
packets.collect()
|
||||||
|
}
|
||||||
|
|
||||||
fn on_neighbours(&mut self, rlp: &UntrustedRlp, _node: &NodeId, from: &SocketAddr) -> Result<Option<TableUpdates>, NetworkError> {
|
fn on_neighbours(&mut self, rlp: &UntrustedRlp, _node: &NodeId, from: &SocketAddr) -> Result<Option<TableUpdates>, NetworkError> {
|
||||||
// TODO: validate packet
|
// TODO: validate packet
|
||||||
let mut added = HashMap::new();
|
let mut added = HashMap::new();
|
||||||
@ -506,6 +512,24 @@ mod tests {
|
|||||||
use crypto::KeyPair;
|
use crypto::KeyPair;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use rustc_serialize::hex::FromHex;
|
use rustc_serialize::hex::FromHex;
|
||||||
|
use rlp::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn find_node() {
|
||||||
|
let mut nearest = Vec::new();
|
||||||
|
let node = Node::from_str("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@127.0.0.1:7770").unwrap();
|
||||||
|
for _ in 0..1000 {
|
||||||
|
nearest.push( NodeEntry { id: node.id.clone(), endpoint: node.endpoint.clone() });
|
||||||
|
}
|
||||||
|
|
||||||
|
let packets = Discovery::prepare_neighbours_packets(&nearest);
|
||||||
|
assert_eq!(packets.len(), 77);
|
||||||
|
for p in &packets[0..76] {
|
||||||
|
assert!(p.len() > 1280/2);
|
||||||
|
assert!(p.len() <= 1280);
|
||||||
|
}
|
||||||
|
assert!(packets.last().unwrap().len() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn discovery() {
|
fn discovery() {
|
||||||
|
@ -541,7 +541,7 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
|
|||||||
match TcpStream::connect(&address) {
|
match TcpStream::connect(&address) {
|
||||||
Ok(socket) => socket,
|
Ok(socket) => socket,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Can't connect to address {:?}: {:?}", address, e);
|
debug!("Can't connect to address {:?}: {:?}", address, e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -695,6 +695,14 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
if !originated {
|
||||||
|
let session_count = sessions.count();
|
||||||
|
let ideal_peers = { self.info.read().unwrap().deref().config.ideal_peers };
|
||||||
|
if session_count >= ideal_peers as usize {
|
||||||
|
session.disconnect(DisconnectReason::TooManyPeers);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
let result = sessions.insert_with(move |session_token| {
|
let result = sessions.insert_with(move |session_token| {
|
||||||
session.set_token(session_token);
|
session.set_token(session_token);
|
||||||
io.deregister_stream(token).expect("Error deleting handshake registration");
|
io.deregister_stream(token).expect("Error deleting handshake registration");
|
||||||
|
@ -26,7 +26,7 @@ use std::ops::*;
|
|||||||
use std::sync::*;
|
use std::sync::*;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use kvdb::{Database};
|
use kvdb::{Database, DBTransaction};
|
||||||
|
|
||||||
/// Implementation of the HashDB trait for a disk-backed database with a memory overlay.
|
/// Implementation of the HashDB trait for a disk-backed database with a memory overlay.
|
||||||
///
|
///
|
||||||
@ -36,7 +36,7 @@ use kvdb::{Database};
|
|||||||
///
|
///
|
||||||
/// `lookup()` and `contains()` maintain normal behaviour - all `insert()` and `remove()`
|
/// `lookup()` and `contains()` maintain normal behaviour - all `insert()` and `remove()`
|
||||||
/// queries have an immediate effect in terms of these functions.
|
/// queries have an immediate effect in terms of these functions.
|
||||||
//#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct OverlayDB {
|
pub struct OverlayDB {
|
||||||
overlay: MemoryDB,
|
overlay: MemoryDB,
|
||||||
backing: Arc<Database>,
|
backing: Arc<Database>,
|
||||||
@ -58,6 +58,36 @@ impl OverlayDB {
|
|||||||
Self::new(Database::open_default(dir.to_str().unwrap()).unwrap())
|
Self::new(Database::open_default(dir.to_str().unwrap()).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Commit all operations to given batch.
|
||||||
|
pub fn commit_to_batch(&mut self, batch: &DBTransaction) -> Result<u32, UtilError> {
|
||||||
|
let mut ret = 0u32;
|
||||||
|
let mut deletes = 0usize;
|
||||||
|
for i in self.overlay.drain().into_iter() {
|
||||||
|
let (key, (value, rc)) = i;
|
||||||
|
if rc != 0 {
|
||||||
|
match self.payload(&key) {
|
||||||
|
Some(x) => {
|
||||||
|
let (back_value, back_rc) = x;
|
||||||
|
let total_rc: i32 = back_rc as i32 + rc;
|
||||||
|
if total_rc < 0 {
|
||||||
|
return Err(From::from(BaseDataError::NegativelyReferencedHash(key)));
|
||||||
|
}
|
||||||
|
deletes += if self.put_payload_in_batch(batch, &key, (back_value, total_rc as u32)) {1} else {0};
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
if rc < 0 {
|
||||||
|
return Err(From::from(BaseDataError::NegativelyReferencedHash(key)));
|
||||||
|
}
|
||||||
|
self.put_payload_in_batch(batch, &key, (value, rc as u32));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ret += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trace!("OverlayDB::commit() deleted {} nodes", deletes);
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
|
||||||
/// Commit all memory operations to the backing database.
|
/// Commit all memory operations to the backing database.
|
||||||
///
|
///
|
||||||
/// Returns either an error or the number of items changed in the backing database.
|
/// Returns either an error or the number of items changed in the backing database.
|
||||||
@ -96,13 +126,13 @@ impl OverlayDB {
|
|||||||
let (back_value, back_rc) = x;
|
let (back_value, back_rc) = x;
|
||||||
let total_rc: i32 = back_rc as i32 + rc;
|
let total_rc: i32 = back_rc as i32 + rc;
|
||||||
if total_rc < 0 {
|
if total_rc < 0 {
|
||||||
return Err(From::from(BaseDataError::NegativelyReferencedHash));
|
return Err(From::from(BaseDataError::NegativelyReferencedHash(key)));
|
||||||
}
|
}
|
||||||
deletes += if self.put_payload(&key, (back_value, total_rc as u32)) {1} else {0};
|
deletes += if self.put_payload(&key, (back_value, total_rc as u32)) {1} else {0};
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
if rc < 0 {
|
if rc < 0 {
|
||||||
return Err(From::from(BaseDataError::NegativelyReferencedHash));
|
return Err(From::from(BaseDataError::NegativelyReferencedHash(key)));
|
||||||
}
|
}
|
||||||
self.put_payload(&key, (value, rc as u32));
|
self.put_payload(&key, (value, rc as u32));
|
||||||
}
|
}
|
||||||
@ -137,6 +167,9 @@ impl OverlayDB {
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn revert(&mut self) { self.overlay.clear(); }
|
pub fn revert(&mut self) { self.overlay.clear(); }
|
||||||
|
|
||||||
|
/// Get the number of references that would be committed.
|
||||||
|
pub fn commit_refs(&self, key: &H256) -> i32 { self.overlay.raw(&key).map_or(0, |&(_, refs)| refs) }
|
||||||
|
|
||||||
/// Get the refs and value of the given key.
|
/// Get the refs and value of the given key.
|
||||||
fn payload(&self, key: &H256) -> Option<(Bytes, u32)> {
|
fn payload(&self, key: &H256) -> Option<(Bytes, u32)> {
|
||||||
self.backing.get(&key.bytes())
|
self.backing.get(&key.bytes())
|
||||||
@ -147,6 +180,20 @@ impl OverlayDB {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Put the refs and value of the given key, possibly deleting it from the db.
|
||||||
|
fn put_payload_in_batch(&self, batch: &DBTransaction, key: &H256, payload: (Bytes, u32)) -> bool {
|
||||||
|
if payload.1 > 0 {
|
||||||
|
let mut s = RlpStream::new_list(2);
|
||||||
|
s.append(&payload.1);
|
||||||
|
s.append(&payload.0);
|
||||||
|
batch.put(&key.bytes(), s.as_raw()).expect("Low-level database error. Some issue with your hard disk?");
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
batch.delete(&key.bytes()).expect("Low-level database error. Some issue with your hard disk?");
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Put the refs and value of the given key, possibly deleting it from the db.
|
/// Put the refs and value of the given key, possibly deleting it from the db.
|
||||||
fn put_payload(&self, key: &H256, payload: (Bytes, u32)) -> bool {
|
fn put_payload(&self, key: &H256, payload: (Bytes, u32)) -> bool {
|
||||||
if payload.1 > 0 {
|
if payload.1 > 0 {
|
||||||
|
@ -116,7 +116,7 @@ impl<'a, 'view> View<'a, 'view> for Rlp<'a> where 'a: 'view {
|
|||||||
impl <'a, 'view> Rlp<'a> where 'a: 'view {
|
impl <'a, 'view> Rlp<'a> where 'a: 'view {
|
||||||
fn view_as_val<T, R>(r: &R) -> T where R: View<'a, 'view>, T: RlpDecodable {
|
fn view_as_val<T, R>(r: &R) -> T where R: View<'a, 'view>, T: RlpDecodable {
|
||||||
let res: Result<T, DecoderError> = r.as_val();
|
let res: Result<T, DecoderError> = r.as_val();
|
||||||
res.unwrap_or_else(|_| panic!())
|
res.unwrap_or_else(|e| panic!("DecodeError: {}", e))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decode into an object
|
/// Decode into an object
|
||||||
|
@ -40,7 +40,7 @@ impl<Row, Col, Val> Default for Table<Row, Col, Val>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// There is default but clippy does not detect it?
|
// There is default but clippy does not detect it?
|
||||||
#[allow(new_without_default)]
|
#[cfg_attr(feature="dev", allow(new_without_default))]
|
||||||
impl<Row, Col, Val> Table<Row, Col, Val>
|
impl<Row, Col, Val> Table<Row, Col, Val>
|
||||||
where Row: Eq + Hash + Clone,
|
where Row: Eq + Hash + Clone,
|
||||||
Col: Eq + Hash {
|
Col: Eq + Hash {
|
||||||
|
Loading…
Reference in New Issue
Block a user