Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
080ec8043f | ||
|
|
7972c27b61 | ||
|
|
17c133cd01 | ||
|
|
6a4408cebc | ||
|
|
78d3f5fce9 | ||
|
|
93327e8c73 | ||
|
|
54da6aef33 | ||
|
|
22dc6e9ee9 | ||
|
|
ee0f1250ac | ||
|
|
8c01766d90 | ||
|
|
eb15c82fff | ||
|
|
e9f4819640 | ||
|
|
5618fa7a09 | ||
|
|
2f2614e0eb | ||
|
|
91903ac9e7 | ||
|
|
3327dc9aef | ||
|
|
b80aa9c3e4 | ||
|
|
d5ecd8f20f | ||
|
|
e9987c448e | ||
|
|
aac7815712 | ||
|
|
b3097c8036 | ||
|
|
e5ae24dfb6 | ||
|
|
1b36a2cd87 | ||
|
|
b078d71293 | ||
|
|
1df84a8e28 | ||
|
|
e0778fc069 | ||
|
|
2ce43434f1 | ||
|
|
18062f11dc | ||
|
|
d66bd3c83a | ||
|
|
935e8dcf56 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -30,3 +30,5 @@
|
||||
|
||||
# Build artifacts
|
||||
out/
|
||||
|
||||
.vscode
|
||||
|
||||
@@ -5,6 +5,7 @@ variables:
|
||||
GIT_DEPTH: "3"
|
||||
SIMPLECOV: "true"
|
||||
RUST_BACKTRACE: "1"
|
||||
RUSTFLAGS: "-D warnings"
|
||||
cache:
|
||||
key: "$CI_BUILD_NAME/$CI_BUILD_REF_NAME"
|
||||
untracked: true
|
||||
@@ -20,10 +21,17 @@ linux-stable:
|
||||
- cargo build --release --verbose
|
||||
- strip target/release/parity
|
||||
- md5sum target/release/parity >> parity.md5
|
||||
- sh scripts/deb-build.sh amd64
|
||||
- cp target/release/parity deb/usr/bin/parity
|
||||
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||
- dpkg-deb -b deb "parity_"$VER"_amd64.deb"
|
||||
- md5sum "parity_"$VER"_amd64.deb" >> "parity_"$VER"_amd64.deb.md5"
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/parity --body target/release/parity
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/parity.md5 --body parity.md5
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/"parity_"$VER"_amd64.deb" --body "parity_"$VER"_amd64.deb"
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/"parity_"$VER"_amd64.deb.md5" --body "parity_"$VER"_amd64.deb.md5"
|
||||
tags:
|
||||
- rust
|
||||
- rust-stable
|
||||
@@ -43,10 +51,17 @@ linux-stable-14.04:
|
||||
- cargo build --release --verbose
|
||||
- strip target/release/parity
|
||||
- md5sum target/release/parity >> parity.md5
|
||||
- sh scripts/deb-build.sh amd64
|
||||
- cp target/release/parity deb/usr/bin/parity
|
||||
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||
- dpkg-deb -b deb "parity_"$VER"_amd64.deb"
|
||||
- md5sum "parity_"$VER"_amd64.deb" >> "parity_"$VER"_amd64.deb.md5"
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-ubuntu_14_04-gnu/parity --body target/release/parity
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-ubuntu_14_04-gnu/parity.md5 --body parity.md5
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-ubuntu_14_04-gnu/"parity_"$VER"_amd64.deb" --body "parity_"$VER"_amd64.deb"
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-ubuntu_14_04-gnu/"parity_"$VER"_amd64.deb.md5" --body "parity_"$VER"_amd64.deb.md5"
|
||||
tags:
|
||||
- rust
|
||||
- rust-14.04
|
||||
@@ -133,10 +148,17 @@ linux-armv7:
|
||||
- cargo build --target armv7-unknown-linux-gnueabihf --release --verbose
|
||||
- arm-linux-gnueabihf-strip target/armv7-unknown-linux-gnueabihf/release/parity
|
||||
- md5sum target/armv7-unknown-linux-gnueabihf/release/parity >> parity.md5
|
||||
- sh scripts/deb-build.sh armhf
|
||||
- cp target/armv7-unknown-linux-gnueabihf/release/parity deb/usr/bin/parity
|
||||
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||
- dpkg-deb -b deb "parity_"$VER"_armhf.deb"
|
||||
- md5sum "parity_"$VER"_armhf.deb" >> "parity_"$VER"_armhf.deb.md5"
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/parity --body target/armv7-unknown-linux-gnueabihf/release/parity
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/parity.md5 --body parity.md5
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/"parity_"$VER"_armhf.deb" --body "parity_"$VER"_armhf.deb"
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/"parity_"$VER"_armhf.deb.md5" --body "parity_"$VER"_armhf.deb.md5"
|
||||
tags:
|
||||
- rust
|
||||
- rust-arm
|
||||
@@ -161,10 +183,17 @@ linux-arm:
|
||||
- cargo build --target arm-unknown-linux-gnueabihf --release --verbose
|
||||
- arm-linux-gnueabihf-strip target/arm-unknown-linux-gnueabihf/release/parity
|
||||
- md5sum target/arm-unknown-linux-gnueabihf/release/parity >> parity.md5
|
||||
- sh scripts/deb-build.sh armhf
|
||||
- cp target/arm-unknown-linux-gnueabihf/release/parity deb/usr/bin/parity
|
||||
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||
- dpkg-deb -b deb "parity_"$VER"_armhf.deb"
|
||||
- md5sum "parity_"$VER"_armhf.deb" >> "parity_"$VER"_armhf.deb.md5"
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/parity --body target/arm-unknown-linux-gnueabihf/release/parity
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/parity.md5 --body parity.md5
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/"parity_"$VER"_armhf.deb" --body "parity_"$VER"_armhf.deb"
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/"parity_"$VER"_armhf.deb.md5" --body "parity_"$VER"_armhf.deb.md5"
|
||||
tags:
|
||||
- rust
|
||||
- rust-arm
|
||||
@@ -217,10 +246,17 @@ linux-aarch64:
|
||||
- cargo build --target aarch64-unknown-linux-gnu --release --verbose
|
||||
- aarch64-linux-gnu-strip target/aarch64-unknown-linux-gnu/release/parity
|
||||
- md5sum target/aarch64-unknown-linux-gnu/release/parity >> parity.md5
|
||||
- sh scripts/deb-build.sh arm64
|
||||
- cp target/aarch64-unknown-linux-gnu/release/parity deb/usr/bin/parity
|
||||
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||
- dpkg-deb -b deb "parity_"$VER"_arm64.deb"
|
||||
- md5sum "parity_"$VER"_arm64.deb" >> "parity_"$VER"_arm64.deb.md5"
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/parity --body target/aarch64-unknown-linux-gnu/release/parity
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/parity.md5 --body parity.md5
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/"parity_"$VER"_arm64.deb" --body "parity_"$VER"_arm64.deb"
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/"parity_"$VER"_arm64.deb.md5" --body "parity_"$VER"_arm64.deb.md5"
|
||||
tags:
|
||||
- rust
|
||||
- rust-arm
|
||||
@@ -238,6 +274,7 @@ darwin:
|
||||
- stable
|
||||
script:
|
||||
- cargo build --release --verbose
|
||||
- rm -rf parity.md5
|
||||
- md5sum target/release/parity >> parity.md5
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
|
||||
40
Cargo.lock
generated
40
Cargo.lock
generated
@@ -1,6 +1,6 @@
|
||||
[root]
|
||||
name = "parity"
|
||||
version = "1.3.7"
|
||||
version = "1.3.11"
|
||||
dependencies = [
|
||||
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clippy 0.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -20,7 +20,7 @@ dependencies = [
|
||||
"ethcore-logger 1.3.0",
|
||||
"ethcore-rpc 1.3.0",
|
||||
"ethcore-signer 1.3.0",
|
||||
"ethcore-util 1.3.7",
|
||||
"ethcore-util 1.3.11",
|
||||
"ethsync 1.3.0",
|
||||
"fdlimit 0.1.0",
|
||||
"hyper 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -270,7 +270,7 @@ dependencies = [
|
||||
"ethcore-ipc 1.3.0",
|
||||
"ethcore-ipc-codegen 1.3.0",
|
||||
"ethcore-ipc-nano 1.3.0",
|
||||
"ethcore-util 1.3.7",
|
||||
"ethcore-util 1.3.11",
|
||||
"ethjson 0.1.0",
|
||||
"ethstore 0.1.0",
|
||||
"evmjit 1.3.0",
|
||||
@@ -294,10 +294,10 @@ version = "1.3.0"
|
||||
dependencies = [
|
||||
"clippy 0.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-rpc 1.3.0",
|
||||
"ethcore-util 1.3.7",
|
||||
"ethcore-util 1.3.11",
|
||||
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
||||
"jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
|
||||
"jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git?branch=beta)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||
@@ -336,7 +336,7 @@ name = "ethcore-ipc"
|
||||
version = "1.3.0"
|
||||
dependencies = [
|
||||
"ethcore-devtools 1.3.0",
|
||||
"ethcore-util 1.3.7",
|
||||
"ethcore-util 1.3.11",
|
||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -381,7 +381,7 @@ dependencies = [
|
||||
"ethcore-ipc 1.3.0",
|
||||
"ethcore-ipc-codegen 1.3.0",
|
||||
"ethcore-ipc-nano 1.3.0",
|
||||
"ethcore-util 1.3.7",
|
||||
"ethcore-util 1.3.11",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -393,7 +393,7 @@ name = "ethcore-logger"
|
||||
version = "1.3.0"
|
||||
dependencies = [
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-util 1.3.7",
|
||||
"ethcore-util 1.3.11",
|
||||
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -408,7 +408,7 @@ dependencies = [
|
||||
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-devtools 1.3.0",
|
||||
"ethcore-io 1.3.0",
|
||||
"ethcore-util 1.3.7",
|
||||
"ethcore-util 1.3.11",
|
||||
"igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -432,12 +432,12 @@ dependencies = [
|
||||
"ethcore-devtools 1.3.0",
|
||||
"ethcore-io 1.3.0",
|
||||
"ethcore-ipc 1.3.0",
|
||||
"ethcore-util 1.3.7",
|
||||
"ethcore-util 1.3.11",
|
||||
"ethjson 0.1.0",
|
||||
"ethsync 1.3.0",
|
||||
"json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git?branch=beta)",
|
||||
"jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
|
||||
"jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git?branch=beta)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -455,7 +455,7 @@ dependencies = [
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-io 1.3.0",
|
||||
"ethcore-rpc 1.3.0",
|
||||
"ethcore-util 1.3.7",
|
||||
"ethcore-util 1.3.11",
|
||||
"jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-dapps-signer 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||
@@ -466,7 +466,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ethcore-util"
|
||||
version = "1.3.7"
|
||||
version = "1.3.11"
|
||||
dependencies = [
|
||||
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -499,7 +499,7 @@ dependencies = [
|
||||
name = "ethjson"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ethcore-util 1.3.7",
|
||||
"ethcore-util 1.3.11",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -547,7 +547,7 @@ dependencies = [
|
||||
"ethcore-ipc-codegen 1.3.0",
|
||||
"ethcore-ipc-nano 1.3.0",
|
||||
"ethcore-network 1.3.0",
|
||||
"ethcore-util 1.3.7",
|
||||
"ethcore-util 1.3.11",
|
||||
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -710,7 +710,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
[[package]]
|
||||
name = "json-ipc-server"
|
||||
version = "0.2.4"
|
||||
source = "git+https://github.com/ethcore/json-ipc-server.git?branch=beta#42d0dacc9cd203757ba894d7ba3690e12ef9f934"
|
||||
source = "git+https://github.com/ethcore/json-ipc-server.git?branch=beta#d9c0811fb0c4aaa5b8b98905b2d72d277675d60f"
|
||||
dependencies = [
|
||||
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -736,7 +736,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "jsonrpc-http-server"
|
||||
version = "6.1.0"
|
||||
source = "git+https://github.com/ethcore/jsonrpc-http-server.git#4e3f93eb79125e91a46e04d77c25ff8885498b86"
|
||||
source = "git+https://github.com/ethcore/jsonrpc-http-server.git?branch=beta#ab0e2cdc8856a4ee26f65c395583e69fd4609c88"
|
||||
dependencies = [
|
||||
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
||||
"jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1225,7 +1225,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
[[package]]
|
||||
name = "rocksdb"
|
||||
version = "0.4.5"
|
||||
source = "git+https://github.com/ethcore/rust-rocksdb#ffc7c82380fe8569f85ae6743f7f620af2d4a679"
|
||||
source = "git+https://github.com/ethcore/rust-rocksdb#64c63ccbe1f62c2e2b39262486f9ba813793af58"
|
||||
dependencies = [
|
||||
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)",
|
||||
@@ -1234,7 +1234,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rocksdb-sys"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/ethcore/rust-rocksdb#ffc7c82380fe8569f85ae6743f7f620af2d4a679"
|
||||
source = "git+https://github.com/ethcore/rust-rocksdb#64c63ccbe1f62c2e2b39262486f9ba813793af58"
|
||||
dependencies = [
|
||||
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1687,7 +1687,7 @@ dependencies = [
|
||||
"checksum itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "086e1fa5fe48840b1cfdef3a20c7e3115599f8d5c4c87ef32a794a7cdd184d76"
|
||||
"checksum json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git?branch=beta)" = "<none>"
|
||||
"checksum jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ec4477e4e8218da23caa5dd31f4eb39999aa0ea9035660617eccfb19a23bf5ad"
|
||||
"checksum jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)" = "<none>"
|
||||
"checksum jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git?branch=beta)" = "<none>"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
|
||||
"checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
description = "Ethcore client."
|
||||
name = "parity"
|
||||
version = "1.3.7"
|
||||
version = "1.3.11"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Ethcore <admin@ethcore.io>"]
|
||||
build = "build.rs"
|
||||
|
||||
@@ -11,7 +11,7 @@ build = "build.rs"
|
||||
[dependencies]
|
||||
log = "0.3"
|
||||
jsonrpc-core = "2.1"
|
||||
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git" }
|
||||
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git", branch = "beta" }
|
||||
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
|
||||
unicase = "1.3"
|
||||
url = "1.0"
|
||||
|
||||
@@ -18,6 +18,6 @@ extern crate ethcore_ipc_codegen;
|
||||
|
||||
fn main() {
|
||||
ethcore_ipc_codegen::derive_binary("src/types/mod.rs.in").unwrap();
|
||||
ethcore_ipc_codegen::derive_ipc("src/client/traits.rs").unwrap();
|
||||
ethcore_ipc_codegen::derive_ipc("src/client/chain_notify.rs").unwrap();
|
||||
ethcore_ipc_codegen::derive_ipc_cond("src/client/traits.rs", cfg!(feature="ipc")).unwrap();
|
||||
ethcore_ipc_codegen::derive_ipc_cond("src/client/chain_notify.rs", cfg!(feature="ipc")).unwrap();
|
||||
}
|
||||
|
||||
@@ -10,7 +10,12 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0x118c30"
|
||||
"homesteadTransition": "0x118c30",
|
||||
"eip150Transition": "0x2625a0",
|
||||
"eip155Transition": "0x7fffffffffffffff",
|
||||
"eip160Transition": "0x7fffffffffffffff",
|
||||
"eip161abcTransition": "0x7fffffffffffffff",
|
||||
"eip161dTransition": "0x7fffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
47
ethcore/res/ethereum/eip150_test.json
Normal file
47
ethcore/res/ethereum/eip150_test.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "Homestead (Test)",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"homesteadTransition": "0x0",
|
||||
"eip150Transition": "0x0",
|
||||
"eip155Transition": "0x7fffffffffffffff",
|
||||
"eip160Transition": "0x7fffffffffffffff",
|
||||
"eip161abcTransition": "0x7fffffffffffffff",
|
||||
"eip161dTransition": "0x7fffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x0000000000000042",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x400000000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
|
||||
"gasLimit": "0x1388"
|
||||
},
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
"0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
|
||||
"0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
|
||||
"0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }
|
||||
}
|
||||
}
|
||||
47
ethcore/res/ethereum/eip161_test.json
Normal file
47
ethcore/res/ethereum/eip161_test.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "Homestead (Test)",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"homesteadTransition": "0x0",
|
||||
"eip150Transition": "0x0",
|
||||
"eip155Transition": "0x7fffffffffffffff",
|
||||
"eip160Transition": "0x0",
|
||||
"eip161abcTransition": "0x0",
|
||||
"eip161dTransition": "0x0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x0000000000000042",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x400000000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
|
||||
"gasLimit": "0x1388"
|
||||
},
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
"0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
|
||||
"0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
|
||||
"0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }
|
||||
}
|
||||
}
|
||||
74
ethcore/res/ethereum/expanse.json
Normal file
74
ethcore/res/ethereum/expanse.json
Normal file
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"name": "Expanse",
|
||||
"forkName": "expanse",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"difficultyIncrementDivisor": "60",
|
||||
"durationLimit": "0x3C",
|
||||
"blockReward": "0x6f05b59d3b200000",
|
||||
"registrar" : "0x6c221ca53705f3497ec90ca7b84c59ae7382fc21",
|
||||
"homesteadTransition": "0x30d40",
|
||||
"difficultyHardforkTransition": "0x59d9",
|
||||
"difficultyHardforkBoundDivisor": "0x0200",
|
||||
"bombDefuseTransition": "0x30d40",
|
||||
"eip150Transition": "0x7fffffffffffffff",
|
||||
"eip155Transition": "0x7fffffffffffffff",
|
||||
"eip160Transition": "0x7fffffffffffffff",
|
||||
"eip161abcTransition": "0x7fffffffffffffff",
|
||||
"eip161dTransition": "0x7fffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID": "0x1",
|
||||
"subprotocolName": "exp"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x214652414e4b4f21",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x40000000",
|
||||
"author": "0x93decab0cd745598860f782ac1e8f046cb99e898",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x4672616e6b6f497346726565646f6d",
|
||||
"gasLimit": "0x1388"
|
||||
},
|
||||
"nodes": [
|
||||
"enode://7f335a047654f3e70d6f91312a7cf89c39704011f1a584e2698250db3d63817e74b88e26b7854111e16b2c9d0c7173c05419aeee2d0321850227b126d8b1be3f@46.101.156.249:42786",
|
||||
"enode://df872f81e25f72356152b44cab662caf1f2e57c3a156ecd20e9ac9246272af68a2031b4239a0bc831f2c6ab34733a041464d46b3ea36dce88d6c11714446e06b@178.62.208.109:42786",
|
||||
"enode://96d3919b903e7f5ad59ac2f73c43be9172d9d27e2771355db03fd194732b795829a31fe2ea6de109d0804786c39a807e155f065b4b94c6fce167becd0ac02383@45.55.22.34:42786",
|
||||
"enode://5f6c625bf287e3c08aad568de42d868781e961cbda805c8397cfb7be97e229419bef9a5a25a75f97632787106bba8a7caf9060fab3887ad2cfbeb182ab0f433f@46.101.182.53:42786",
|
||||
"enode://d33a8d4c2c38a08971ed975b750f21d54c927c0bf7415931e214465a8d01651ecffe4401e1db913f398383381413c78105656d665d83f385244ab302d6138414@128.199.183.48:42786",
|
||||
"enode://df872f81e25f72356152b44cab662caf1f2e57c3a156ecd20e9ac9246272af68a2031b4239a0bc831f2c6ab34733a041464d46b3ea36dce88d6c11714446e06b@178.62.208.109:42786",
|
||||
"enode://f6f0d6b9b7d02ec9e8e4a16e38675f3621ea5e69860c739a65c1597ca28aefb3cec7a6d84e471ac927d42a1b64c1cbdefad75e7ce8872d57548ddcece20afdd1@159.203.64.95:42786"
|
||||
],
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
"0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
|
||||
"0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
|
||||
"0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
|
||||
"bb94f0ceb32257275b2a7a9c094c13e469b4563e": {
|
||||
"balance": "10000000000000000000000000"
|
||||
},
|
||||
"15656715068ab0dbdf0ab00748a8a19e40f28192": {
|
||||
"balance": "1000000000000000000000000"
|
||||
},
|
||||
"c075fa11f85bda3aaba67106226aaf086ac16f4e": {
|
||||
"balance": "100000000000000000000000"
|
||||
},
|
||||
"93decab0cd745598860f782ac1e8f046cb99e898": {
|
||||
"balance": "10000000000000000000000"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,10 +9,10 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0x118c30",
|
||||
"homesteadTransition": "0x118c30",
|
||||
"daoHardforkTransition": "0x1d4c00",
|
||||
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
||||
"daoHardforkAccounts": [
|
||||
"daoHardforkAccounts": [
|
||||
"0xd4fe7bc31cedb7bfb8a345f31e668033056b2728",
|
||||
"0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425",
|
||||
"0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f",
|
||||
@@ -129,7 +129,12 @@
|
||||
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
||||
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
||||
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
||||
]
|
||||
],
|
||||
"eip150Transition": "0x259518",
|
||||
"eip155Transition": "0x7fffffffffffffff",
|
||||
"eip160Transition": "0x7fffffffffffffff",
|
||||
"eip161abcTransition": "0x7fffffffffffffff",
|
||||
"eip161dTransition": "0x7fffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -160,7 +165,7 @@
|
||||
"enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303",
|
||||
"enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303",
|
||||
"enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303",
|
||||
"enode://248f12bc8b18d5289358085520ac78cd8076485211e6d96ab0bc93d6cd25442db0ce3a937dc404f64f207b0b9aed50e25e98ce32af5ac7cb321ff285b97de485@zero.parity.io:30303"
|
||||
"enode://4cd540b2c3292e17cff39922e864094bf8b0741fcc8c5dcea14957e389d7944c70278d872902e3d0345927f621547efa659013c400865485ab4bfa0c6596936f@138.201.144.135:30303"
|
||||
],
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0x118c30",
|
||||
"homesteadTransition": "0x118c30",
|
||||
"daoHardforkTransition": "0x1d4c00",
|
||||
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
||||
"daoHardforkAccounts": [
|
||||
@@ -129,7 +129,12 @@
|
||||
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
||||
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
||||
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
||||
]
|
||||
],
|
||||
"eip150Transition": "0x7fffffffffffffff",
|
||||
"eip155Transition": "0x7fffffffffffffff",
|
||||
"eip160Transition": "0x7fffffffffffffff",
|
||||
"eip161abcTransition": "0x7fffffffffffffff",
|
||||
"eip161dTransition": "0x7fffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -9,7 +9,12 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0xffffffffffffffff"
|
||||
"homesteadTransition": "0x7fffffffffffffff",
|
||||
"eip150Transition": "0x7fffffffffffffff",
|
||||
"eip155Transition": "0x7fffffffffffffff",
|
||||
"eip160Transition": "0x7fffffffffffffff",
|
||||
"eip161abcTransition": "0x7fffffffffffffff",
|
||||
"eip161dTransition": "0x7fffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -9,7 +9,12 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0x0"
|
||||
"homesteadTransition": "0x0",
|
||||
"eip150Transition": "0x7fffffffffffffff",
|
||||
"eip155Transition": "0x7fffffffffffffff",
|
||||
"eip160Transition": "0x7fffffffffffffff",
|
||||
"eip161abcTransition": "0x7fffffffffffffff",
|
||||
"eip161dTransition": "0x7fffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -9,7 +9,12 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar": "0x8e4e9b13d4b45cb0befc93c3061b1408f67316b2",
|
||||
"frontierCompatibilityModeLimit": "0x789b0"
|
||||
"homesteadTransition": "0x789b0",
|
||||
"eip150Transition": "0x1b34d8",
|
||||
"eip155Transition": "0x7fffffffffffffff",
|
||||
"eip160Transition": "0x7fffffffffffffff",
|
||||
"eip161abcTransition": "0x7fffffffffffffff",
|
||||
"eip161dTransition": "0x7fffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -17,7 +22,9 @@
|
||||
"accountStartNonce": "0x0100000",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x2"
|
||||
"networkID" : "0x2",
|
||||
"forkBlock": "0x1b34d8",
|
||||
"forkCanonHash": "0xf376243aeff1f256d970714c3de9fd78fa4e63cf63e32a51fe1169e375d98145"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
|
||||
@@ -8,7 +8,13 @@
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x08",
|
||||
"blockReward": "0x14D1120D7B160000",
|
||||
"registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050"
|
||||
"registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050",
|
||||
"homesteadTransition": "0x7fffffffffffffff",
|
||||
"eip150Transition": "0x7fffffffffffffff",
|
||||
"eip155Transition": "0x7fffffffffffffff",
|
||||
"eip160Transition": "0x7fffffffffffffff",
|
||||
"eip161abcTransition": "0x7fffffffffffffff",
|
||||
"eip161dTransition": "0x7fffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Submodule ethcore/res/ethereum/tests updated: ac5475d676...853333e7da
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "DAO hard-fork consensus test",
|
||||
"name": "EIP150.1b hard-fork consensus test",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
@@ -9,7 +9,7 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0x5",
|
||||
"homesteadTransition": "0x5",
|
||||
"daoHardforkTransition": "0x8",
|
||||
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
||||
"daoHardforkAccounts": [
|
||||
@@ -129,7 +129,12 @@
|
||||
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
||||
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
||||
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
||||
]
|
||||
],
|
||||
"eip150Transition": "0xa",
|
||||
"eip155Transition": "0x7fffffffffffffff",
|
||||
"eip160Transition": "0x7fffffffffffffff",
|
||||
"eip161abcTransition": "0x7fffffffffffffff",
|
||||
"eip161dTransition": "0x7fffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -288,6 +288,21 @@ impl Account {
|
||||
/// Determine whether there are any un-`commit()`-ed storage-setting operations.
|
||||
pub fn storage_is_clean(&self) -> bool { self.storage_changes.is_empty() }
|
||||
|
||||
/// Check if account has zero nonce, balance, no code and no storage.
|
||||
///
|
||||
/// NOTE: Will panic if `!self.storage_is_clean()`
|
||||
pub fn is_empty(&self) -> bool {
|
||||
assert!(self.storage_is_clean(), "Account::is_empty() may only legally be called when storage is clean.");
|
||||
self.is_null() && self.storage_root == SHA3_NULL_RLP
|
||||
}
|
||||
|
||||
/// Check if account has zero nonce, balance, no code.
|
||||
pub fn is_null(&self) -> bool {
|
||||
self.balance.is_zero() &&
|
||||
self.nonce.is_zero() &&
|
||||
self.code_hash == SHA3_EMPTY
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/// return the storage root associated with this account or None if it has been altered via the overlay.
|
||||
pub fn storage_root(&self) -> Option<&H256> { if self.storage_is_clean() {Some(&self.storage_root)} else {None} }
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
//! A queue of blocks. Sits between network or other I/O and the `BlockChain`.
|
||||
//! Sorts them ready for blockchain insertion.
|
||||
use std::thread::{JoinHandle, self};
|
||||
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrdering};
|
||||
use std::sync::{Condvar as SCondvar, Mutex as SMutex};
|
||||
use util::*;
|
||||
use io::*;
|
||||
@@ -76,6 +76,13 @@ impl BlockQueueInfo {
|
||||
}
|
||||
}
|
||||
|
||||
// the internal queue sizes.
|
||||
struct Sizes {
|
||||
unverified: AtomicUsize,
|
||||
verifying: AtomicUsize,
|
||||
verified: AtomicUsize,
|
||||
}
|
||||
|
||||
/// A queue of blocks. Sits between network or other I/O and the `BlockChain`.
|
||||
/// Sorts them ready for blockchain insertion.
|
||||
pub struct BlockQueue {
|
||||
@@ -110,7 +117,21 @@ struct QueueSignal {
|
||||
|
||||
impl QueueSignal {
|
||||
#[cfg_attr(feature="dev", allow(bool_comparison))]
|
||||
fn set(&self) {
|
||||
fn set_sync(&self) {
|
||||
// Do not signal when we are about to close
|
||||
if self.deleting.load(AtomicOrdering::Relaxed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.signalled.compare_and_swap(false, true, AtomicOrdering::Relaxed) == false {
|
||||
if let Err(e) = self.message_channel.send_sync(ClientIoMessage::BlockVerified) {
|
||||
debug!("Error sending BlockVerified message: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature="dev", allow(bool_comparison))]
|
||||
fn set_async(&self) {
|
||||
// Do not signal when we are about to close
|
||||
if self.deleting.load(AtomicOrdering::Relaxed) {
|
||||
return;
|
||||
@@ -131,11 +152,12 @@ impl QueueSignal {
|
||||
struct Verification {
|
||||
// All locks must be captured in the order declared here.
|
||||
unverified: Mutex<VecDeque<UnverifiedBlock>>,
|
||||
verified: Mutex<VecDeque<PreverifiedBlock>>,
|
||||
verifying: Mutex<VecDeque<VerifyingBlock>>,
|
||||
verified: Mutex<VecDeque<PreverifiedBlock>>,
|
||||
bad: Mutex<HashSet<H256>>,
|
||||
more_to_verify: SMutex<()>,
|
||||
empty: SMutex<()>,
|
||||
sizes: Sizes,
|
||||
}
|
||||
|
||||
impl BlockQueue {
|
||||
@@ -143,12 +165,16 @@ impl BlockQueue {
|
||||
pub fn new(config: BlockQueueConfig, engine: Arc<Engine>, message_channel: IoChannel<ClientIoMessage>) -> BlockQueue {
|
||||
let verification = Arc::new(Verification {
|
||||
unverified: Mutex::new(VecDeque::new()),
|
||||
verified: Mutex::new(VecDeque::new()),
|
||||
verifying: Mutex::new(VecDeque::new()),
|
||||
verified: Mutex::new(VecDeque::new()),
|
||||
bad: Mutex::new(HashSet::new()),
|
||||
more_to_verify: SMutex::new(()),
|
||||
empty: SMutex::new(()),
|
||||
|
||||
sizes: Sizes {
|
||||
unverified: AtomicUsize::new(0),
|
||||
verifying: AtomicUsize::new(0),
|
||||
verified: AtomicUsize::new(0),
|
||||
}
|
||||
});
|
||||
let more_to_verify = Arc::new(SCondvar::new());
|
||||
let deleting = Arc::new(AtomicBool::new(false));
|
||||
@@ -221,16 +247,18 @@ impl BlockQueue {
|
||||
}
|
||||
let mut verifying = verification.verifying.lock();
|
||||
let block = unverified.pop_front().unwrap();
|
||||
verification.sizes.unverified.fetch_sub(block.heap_size_of_children(), AtomicOrdering::SeqCst);
|
||||
verifying.push_back(VerifyingBlock{ hash: block.header.hash(), block: None });
|
||||
block
|
||||
};
|
||||
|
||||
let block_hash = block.header.hash();
|
||||
match verify_block_unordered(block.header, block.bytes, &*engine) {
|
||||
let is_ready = match verify_block_unordered(block.header, block.bytes, &*engine) {
|
||||
Ok(verified) => {
|
||||
let mut verifying = verification.verifying.lock();
|
||||
for e in verifying.iter_mut() {
|
||||
if e.hash == block_hash {
|
||||
verification.sizes.verifying.fetch_add(verified.heap_size_of_children(), AtomicOrdering::SeqCst);
|
||||
e.block = Some(verified);
|
||||
break;
|
||||
}
|
||||
@@ -239,8 +267,10 @@ impl BlockQueue {
|
||||
// we're next!
|
||||
let mut verified = verification.verified.lock();
|
||||
let mut bad = verification.bad.lock();
|
||||
BlockQueue::drain_verifying(&mut verifying, &mut verified, &mut bad);
|
||||
ready.set();
|
||||
BlockQueue::drain_verifying(&mut verifying, &mut verified, &mut bad, &verification.sizes);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
@@ -250,23 +280,39 @@ impl BlockQueue {
|
||||
warn!(target: "client", "Stage 2 block verification failed for {}\nError: {:?}", block_hash, err);
|
||||
bad.insert(block_hash.clone());
|
||||
verifying.retain(|e| e.hash != block_hash);
|
||||
BlockQueue::drain_verifying(&mut verifying, &mut verified, &mut bad);
|
||||
ready.set();
|
||||
if !verifying.is_empty() && verifying.front().unwrap().hash == block_hash {
|
||||
BlockQueue::drain_verifying(&mut verifying, &mut verified, &mut bad, &verification.sizes);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
};
|
||||
if is_ready {
|
||||
// Import the block immediately
|
||||
ready.set_sync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn drain_verifying(verifying: &mut VecDeque<VerifyingBlock>, verified: &mut VecDeque<PreverifiedBlock>, bad: &mut HashSet<H256>) {
|
||||
fn drain_verifying(verifying: &mut VecDeque<VerifyingBlock>, verified: &mut VecDeque<PreverifiedBlock>, bad: &mut HashSet<H256>, sizes: &Sizes) {
|
||||
let mut removed_size = 0;
|
||||
let mut inserted_size = 0;
|
||||
while !verifying.is_empty() && verifying.front().unwrap().block.is_some() {
|
||||
let block = verifying.pop_front().unwrap().block.unwrap();
|
||||
if bad.contains(&block.header.parent_hash) {
|
||||
let size = block.heap_size_of_children();
|
||||
removed_size += size;
|
||||
|
||||
if bad.contains(&block.header.parent_hash()) {
|
||||
bad.insert(block.header.hash());
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
inserted_size += size;
|
||||
verified.push_back(block);
|
||||
}
|
||||
}
|
||||
|
||||
sizes.verifying.fetch_sub(removed_size, AtomicOrdering::SeqCst);
|
||||
sizes.verified.fetch_add(inserted_size, AtomicOrdering::SeqCst);
|
||||
}
|
||||
|
||||
/// Clear the queue and stop verification activity.
|
||||
@@ -277,6 +323,12 @@ impl BlockQueue {
|
||||
unverified.clear();
|
||||
verifying.clear();
|
||||
verified.clear();
|
||||
|
||||
let sizes = &self.verification.sizes;
|
||||
sizes.unverified.store(0, AtomicOrdering::Release);
|
||||
sizes.verifying.store(0, AtomicOrdering::Release);
|
||||
sizes.verified.store(0, AtomicOrdering::Release);
|
||||
|
||||
self.processing.write().clear();
|
||||
}
|
||||
|
||||
@@ -322,7 +374,9 @@ impl BlockQueue {
|
||||
match verify_block_basic(&header, &bytes, &*self.engine) {
|
||||
Ok(()) => {
|
||||
self.processing.write().insert(h.clone());
|
||||
self.verification.unverified.lock().push_back(UnverifiedBlock { header: header, bytes: bytes });
|
||||
let block = UnverifiedBlock { header: header, bytes: bytes };
|
||||
self.verification.sizes.unverified.fetch_add(block.heap_size_of_children(), AtomicOrdering::SeqCst);
|
||||
self.verification.unverified.lock().push_back(block);
|
||||
self.more_to_verify.notify_all();
|
||||
Ok(h)
|
||||
},
|
||||
@@ -350,26 +404,32 @@ impl BlockQueue {
|
||||
}
|
||||
|
||||
let mut new_verified = VecDeque::new();
|
||||
let mut removed_size = 0;
|
||||
for block in verified.drain(..) {
|
||||
if bad.contains(&block.header.parent_hash) {
|
||||
removed_size += block.heap_size_of_children();
|
||||
bad.insert(block.header.hash());
|
||||
processing.remove(&block.header.hash());
|
||||
} else {
|
||||
new_verified.push_back(block);
|
||||
}
|
||||
}
|
||||
|
||||
self.verification.sizes.verified.fetch_sub(removed_size, AtomicOrdering::SeqCst);
|
||||
*verified = new_verified;
|
||||
}
|
||||
|
||||
/// Mark given block as processed
|
||||
pub fn mark_as_good(&self, block_hashes: &[H256]) {
|
||||
if block_hashes.is_empty() {
|
||||
return;
|
||||
/// Mark given item as processed.
|
||||
/// Returns true if the queue becomes empty.
|
||||
pub fn mark_as_good(&self, hashes: &[H256]) -> bool {
|
||||
if hashes.is_empty() {
|
||||
return self.processing.read().is_empty();
|
||||
}
|
||||
let mut processing = self.processing.write();
|
||||
for hash in block_hashes {
|
||||
for hash in hashes {
|
||||
processing.remove(hash);
|
||||
}
|
||||
processing.is_empty()
|
||||
}
|
||||
|
||||
/// Removes up to `max` verified blocks from the queue
|
||||
@@ -379,28 +439,34 @@ impl BlockQueue {
|
||||
let mut result = Vec::with_capacity(count);
|
||||
for _ in 0..count {
|
||||
let block = verified.pop_front().unwrap();
|
||||
self.verification.sizes.verified.fetch_sub(block.heap_size_of_children(), AtomicOrdering::SeqCst);
|
||||
result.push(block);
|
||||
}
|
||||
self.ready_signal.reset();
|
||||
if !verified.is_empty() {
|
||||
self.ready_signal.set();
|
||||
self.ready_signal.set_async();
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Get queue status.
|
||||
pub fn queue_info(&self) -> BlockQueueInfo {
|
||||
use std::mem::size_of;
|
||||
let (unverified_len, unverified_bytes) = {
|
||||
let v = self.verification.unverified.lock();
|
||||
(v.len(), v.heap_size_of_children())
|
||||
let len = self.verification.unverified.lock().len();
|
||||
let size = self.verification.sizes.unverified.load(AtomicOrdering::Acquire);
|
||||
|
||||
(len, size + len * size_of::<UnverifiedBlock>())
|
||||
};
|
||||
let (verifying_len, verifying_bytes) = {
|
||||
let v = self.verification.verifying.lock();
|
||||
(v.len(), v.heap_size_of_children())
|
||||
let len = self.verification.verifying.lock().len();
|
||||
let size = self.verification.sizes.verifying.load(AtomicOrdering::Acquire);
|
||||
(len, size + len * size_of::<VerifyingBlock>())
|
||||
};
|
||||
let (verified_len, verified_bytes) = {
|
||||
let v = self.verification.verified.lock();
|
||||
(v.len(), v.heap_size_of_children())
|
||||
let len = self.verification.verified.lock().len();
|
||||
let size = self.verification.sizes.verified.load(AtomicOrdering::Acquire);
|
||||
(len, size + len * size_of::<PreverifiedBlock>())
|
||||
};
|
||||
BlockQueueInfo {
|
||||
unverified_queue_size: unverified_len,
|
||||
@@ -408,12 +474,9 @@ impl BlockQueue {
|
||||
verified_queue_size: verified_len,
|
||||
max_queue_size: self.max_queue_size,
|
||||
max_mem_use: self.max_mem_use,
|
||||
mem_used:
|
||||
unverified_bytes
|
||||
+ verifying_bytes
|
||||
+ verified_bytes
|
||||
// TODO: https://github.com/servo/heapsize/pull/50
|
||||
//+ self.processing.read().heap_size_of_children(),
|
||||
mem_used: unverified_bytes
|
||||
+ verifying_bytes
|
||||
+ verified_bytes
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1279,7 +1279,7 @@ mod tests {
|
||||
action: Action::Create,
|
||||
value: 100.into(),
|
||||
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
|
||||
let b1a = canon_chain
|
||||
@@ -1343,7 +1343,7 @@ mod tests {
|
||||
action: Action::Create,
|
||||
value: 100.into(),
|
||||
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
let t2 = Transaction {
|
||||
nonce: 1.into(),
|
||||
@@ -1352,7 +1352,7 @@ mod tests {
|
||||
action: Action::Create,
|
||||
value: 100.into(),
|
||||
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
let t3 = Transaction {
|
||||
nonce: 2.into(),
|
||||
@@ -1361,7 +1361,7 @@ mod tests {
|
||||
action: Action::Create,
|
||||
value: 100.into(),
|
||||
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
let b1a = canon_chain
|
||||
.with_transaction(t1.clone())
|
||||
|
||||
@@ -15,12 +15,10 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use util::numbers::*;
|
||||
use ipc::{IpcConfig, BinaryConvertError};
|
||||
use std::collections::VecDeque;
|
||||
use std::mem;
|
||||
use ipc::{IpcConfig};
|
||||
|
||||
/// Represents what has to be handled by actor listening to chain events
|
||||
#[derive(Ipc)]
|
||||
#[ipc]
|
||||
pub trait ChainNotify : Send + Sync {
|
||||
/// fires when chain has new blocks
|
||||
fn new_blocks(&self,
|
||||
|
||||
@@ -34,7 +34,7 @@ use io::*;
|
||||
use views::{BlockView, HeaderView, BodyView};
|
||||
use error::{ImportError, ExecutionError, CallError, BlockError, ImportResult};
|
||||
use header::BlockNumber;
|
||||
use state::State;
|
||||
use state::{State, CleanupMode};
|
||||
use spec::Spec;
|
||||
use basic_types::Seal;
|
||||
use engines::Engine;
|
||||
@@ -259,6 +259,22 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
/// The env info as of the best block.
|
||||
fn latest_env_info(&self) -> EnvInfo {
|
||||
let header_data = self.best_block_header();
|
||||
let view = HeaderView::new(&header_data);
|
||||
|
||||
EnvInfo {
|
||||
number: view.number(),
|
||||
author: view.author(),
|
||||
timestamp: view.timestamp(),
|
||||
difficulty: view.difficulty(),
|
||||
last_hashes: self.build_last_hashes(view.hash()),
|
||||
gas_used: U256::default(),
|
||||
gas_limit: view.gas_limit(),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_last_hashes(&self, parent_hash: H256) -> Arc<LastHashes> {
|
||||
{
|
||||
let hashes = self.last_hashes.read();
|
||||
@@ -355,16 +371,19 @@ impl Client {
|
||||
|
||||
/// This is triggered by a message coming from a block queue when the block is ready for insertion
|
||||
pub fn import_verified_blocks(&self) -> usize {
|
||||
let max_blocks_to_import = 64;
|
||||
let (imported_blocks, import_results, invalid_blocks, imported, duration) = {
|
||||
let max_blocks_to_import = 4;
|
||||
let (imported_blocks, import_results, invalid_blocks, imported, duration, is_empty) = {
|
||||
let mut imported_blocks = Vec::with_capacity(max_blocks_to_import);
|
||||
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 blocks = self.block_queue.drain(max_blocks_to_import);
|
||||
if blocks.is_empty() {
|
||||
return 0;
|
||||
}
|
||||
let _timer = PerfTimer::new("import_verified_blocks");
|
||||
let start = precise_time_ns();
|
||||
let blocks = self.block_queue.drain(max_blocks_to_import);
|
||||
|
||||
for block in blocks {
|
||||
let header = &block.header;
|
||||
@@ -393,23 +412,19 @@ impl Client {
|
||||
let imported = imported_blocks.len();
|
||||
let invalid_blocks = invalid_blocks.into_iter().collect::<Vec<H256>>();
|
||||
|
||||
{
|
||||
if !invalid_blocks.is_empty() {
|
||||
self.block_queue.mark_as_bad(&invalid_blocks);
|
||||
}
|
||||
if !imported_blocks.is_empty() {
|
||||
self.block_queue.mark_as_good(&imported_blocks);
|
||||
}
|
||||
if !invalid_blocks.is_empty() {
|
||||
self.block_queue.mark_as_bad(&invalid_blocks);
|
||||
}
|
||||
let is_empty = self.block_queue.mark_as_good(&imported_blocks);
|
||||
let duration_ns = precise_time_ns() - start;
|
||||
(imported_blocks, import_results, invalid_blocks, imported, duration_ns)
|
||||
(imported_blocks, import_results, invalid_blocks, imported, duration_ns, is_empty)
|
||||
};
|
||||
|
||||
{
|
||||
if !imported_blocks.is_empty() && self.block_queue.queue_info().is_empty() {
|
||||
if !imported_blocks.is_empty() && is_empty {
|
||||
let (enacted, retracted) = self.calculate_enacted_retracted(&import_results);
|
||||
|
||||
if self.queue_info().is_empty() {
|
||||
if is_empty {
|
||||
self.miner.chain_new_blocks(self, &imported_blocks, &invalid_blocks, &enacted, &retracted);
|
||||
}
|
||||
|
||||
@@ -715,7 +730,7 @@ impl BlockChainClient for Client {
|
||||
let needed_balance = t.value + t.gas * t.gas_price;
|
||||
if balance < needed_balance {
|
||||
// give the sender a sufficient balance
|
||||
state.add_balance(&sender, &(needed_balance - balance));
|
||||
state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty);
|
||||
}
|
||||
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
|
||||
let mut ret = try!(Executive::new(&mut state, &env_info, &*self.engine, &self.vm_factory).transact(t, options));
|
||||
@@ -1052,6 +1067,10 @@ impl BlockChainClient for Client {
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction> {
|
||||
self.miner.pending_transactions(self.chain.best_block_number())
|
||||
}
|
||||
|
||||
fn signing_network_id(&self) -> Option<u8> {
|
||||
self.engine.signing_network_id(&self.latest_env_info())
|
||||
}
|
||||
}
|
||||
|
||||
impl MiningBlockChainClient for Client {
|
||||
|
||||
@@ -30,13 +30,17 @@ pub use self::test_client::{TestBlockChainClient, EachBlockWith};
|
||||
pub use types::trace_filter::Filter as TraceFilter;
|
||||
pub use executive::{Executed, Executive, TransactOptions};
|
||||
pub use env_info::{LastHashes, EnvInfo};
|
||||
pub use self::chain_notify::{ChainNotify, ChainNotifyClient};
|
||||
pub use self::chain_notify::ChainNotify;
|
||||
#[cfg(feature="ipc")]
|
||||
pub use self::chain_notify::ChainNotifyClient;
|
||||
#[cfg(feature="ipc")]
|
||||
pub use self::traits::RemoteClient;
|
||||
|
||||
pub use types::call_analytics::CallAnalytics;
|
||||
pub use block_import_error::BlockImportError;
|
||||
pub use transaction_import::TransactionImportResult;
|
||||
pub use transaction_import::TransactionImportError;
|
||||
pub use self::traits::{BlockChainClient, MiningBlockChainClient, RemoteClient};
|
||||
pub use self::traits::{BlockChainClient, MiningBlockChainClient};
|
||||
|
||||
mod traits {
|
||||
#![allow(dead_code, unused_assignments, unused_variables, missing_docs)] // codegen issues
|
||||
|
||||
@@ -202,7 +202,7 @@ impl TestBlockChainClient {
|
||||
gas_price: U256::one(),
|
||||
nonce: U256::zero()
|
||||
};
|
||||
let signed_tx = tx.sign(keypair.secret());
|
||||
let signed_tx = tx.sign(keypair.secret(), None);
|
||||
txs.append(&signed_tx);
|
||||
txs.out()
|
||||
},
|
||||
@@ -569,4 +569,6 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction> {
|
||||
self.miner.pending_transactions(self.chain_info().best_block_number)
|
||||
}
|
||||
|
||||
fn signing_network_id(&self) -> Option<u8> { None }
|
||||
}
|
||||
|
||||
@@ -37,13 +37,10 @@ use executive::Executed;
|
||||
use env_info::LastHashes;
|
||||
use types::call_analytics::CallAnalytics;
|
||||
use block_import_error::BlockImportError;
|
||||
use std::mem;
|
||||
use std::collections::VecDeque;
|
||||
use ipc::{IpcConfig, BinaryConvertError};
|
||||
use ipc::{IpcConfig};
|
||||
use types::blockchain_info::BlockChainInfo;
|
||||
use types::block_status::BlockStatus;
|
||||
|
||||
#[derive(Ipc)]
|
||||
#[ipc(client_ident="RemoteClient")]
|
||||
/// Blockchain database client. Owns and manages a blockchain and a block queue.
|
||||
pub trait BlockChainClient : Sync + Send {
|
||||
@@ -215,6 +212,9 @@ pub trait BlockChainClient : Sync + Send {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the preferred network ID to sign on
|
||||
fn signing_network_id(&self) -> Option<u8> { None }
|
||||
}
|
||||
|
||||
/// Extended client interface used for mining
|
||||
|
||||
@@ -98,6 +98,9 @@ pub trait Engine : Sync + Send {
|
||||
/// Verify a particular transaction is valid.
|
||||
fn verify_transaction(&self, _t: &SignedTransaction, _header: &Header) -> Result<(), Error> { Ok(()) }
|
||||
|
||||
/// The network ID that transactions should be signed with.
|
||||
fn signing_network_id(&self, _env_info: &EnvInfo) -> Option<u8> { None }
|
||||
|
||||
/// Verify the seal of a block. This is an auxilliary method that actually just calls other `verify_` methods
|
||||
/// to get the job done. By default it must pass `verify_basic` and `verify_block_unordered`. If more or fewer
|
||||
/// methods are needed for an Engine, this may be overridden.
|
||||
|
||||
@@ -62,6 +62,8 @@ pub enum TransactionError {
|
||||
},
|
||||
/// Transaction's gas limit (aka gas) is invalid.
|
||||
InvalidGasLimit(OutOfBounds<U256>),
|
||||
/// Invalid network ID given.
|
||||
InvalidNetworkId,
|
||||
}
|
||||
|
||||
impl fmt::Display for TransactionError {
|
||||
@@ -80,6 +82,7 @@ impl fmt::Display for TransactionError {
|
||||
GasLimitExceeded { limit, got } =>
|
||||
format!("Gas limit exceeded. Limit={}, Given={}", limit, got),
|
||||
InvalidGasLimit(ref err) => format!("Invalid gas limit. {}", err),
|
||||
InvalidNetworkId => "Transaction of this network ID is not allowed on this chain.".into(),
|
||||
};
|
||||
|
||||
f.write_fmt(format_args!("Transaction error ({})", msg))
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
use ethash::{quick_get_difficulty, EthashManager, H256 as EH256};
|
||||
use common::*;
|
||||
use block::*;
|
||||
use state::CleanupMode;
|
||||
use spec::CommonParams;
|
||||
use engines::Engine;
|
||||
use evm::Schedule;
|
||||
@@ -38,13 +39,29 @@ pub struct EthashParams {
|
||||
/// Namereg contract address.
|
||||
pub registrar: Address,
|
||||
/// Homestead transition block number.
|
||||
pub frontier_compatibility_mode_limit: u64,
|
||||
pub homestead_transition: u64,
|
||||
/// DAO hard-fork transition block (X).
|
||||
pub dao_hardfork_transition: u64,
|
||||
/// DAO hard-fork refund contract address (C).
|
||||
pub dao_hardfork_beneficiary: Address,
|
||||
/// DAO hard-fork DAO accounts list (L)
|
||||
pub dao_hardfork_accounts: Vec<Address>,
|
||||
/// Transition block for a change of difficulty params (currently just bound_divisor).
|
||||
pub difficulty_hardfork_transition: u64,
|
||||
/// Difficulty param after the difficulty transition.
|
||||
pub difficulty_hardfork_bound_divisor: U256,
|
||||
/// Block on which there is no additional difficulty from the exponential bomb.
|
||||
pub bomb_defuse_transition: u64,
|
||||
/// Number of first block where EIP-150 rules begin.
|
||||
pub eip150_transition: u64,
|
||||
/// Number of first block where EIP-155 rules begin.
|
||||
pub eip155_transition: u64,
|
||||
/// Number of first block where EIP-160 rules begin.
|
||||
pub eip160_transition: u64,
|
||||
/// Number of first block where EIP-161.abc begin.
|
||||
pub eip161abc_transition: u64,
|
||||
/// Number of first block where EIP-161.d begins.
|
||||
pub eip161d_transition: u64,
|
||||
}
|
||||
|
||||
impl From<ethjson::spec::EthashParams> for EthashParams {
|
||||
@@ -56,10 +73,18 @@ impl From<ethjson::spec::EthashParams> for EthashParams {
|
||||
duration_limit: p.duration_limit.into(),
|
||||
block_reward: p.block_reward.into(),
|
||||
registrar: p.registrar.map_or_else(Address::new, Into::into),
|
||||
frontier_compatibility_mode_limit: p.frontier_compatibility_mode_limit.map_or(0, Into::into),
|
||||
homestead_transition: p.homestead_transition.map_or(0, Into::into),
|
||||
dao_hardfork_transition: p.dao_hardfork_transition.map_or(0x7fffffffffffffff, Into::into),
|
||||
dao_hardfork_beneficiary: p.dao_hardfork_beneficiary.map_or_else(Address::new, Into::into),
|
||||
dao_hardfork_accounts: p.dao_hardfork_accounts.unwrap_or_else(Vec::new).into_iter().map(Into::into).collect(),
|
||||
difficulty_hardfork_transition: p.difficulty_hardfork_transition.map_or(0x7fffffffffffffff, Into::into),
|
||||
difficulty_hardfork_bound_divisor: p.difficulty_hardfork_bound_divisor.map_or(p.difficulty_bound_divisor.into(), Into::into),
|
||||
bomb_defuse_transition: p.bomb_defuse_transition.map_or(0x7fffffffffffffff, Into::into),
|
||||
eip150_transition: p.eip150_transition.map_or(0, Into::into),
|
||||
eip155_transition: p.eip155_transition.map_or(0, Into::into),
|
||||
eip160_transition: p.eip160_transition.map_or(0, Into::into),
|
||||
eip161abc_transition: p.eip161abc_transition.map_or(0, Into::into),
|
||||
eip161d_transition: p.eip161d_transition.map_or(0x7fffffffffffffff, Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,12 +129,26 @@ impl Engine for Ethash {
|
||||
}
|
||||
|
||||
fn schedule(&self, env_info: &EnvInfo) -> Schedule {
|
||||
trace!(target: "client", "Creating schedule. fCML={}", self.ethash_params.frontier_compatibility_mode_limit);
|
||||
trace!(target: "client", "Creating schedule. fCML={}, bGCML={}", self.ethash_params.homestead_transition, self.ethash_params.eip150_transition);
|
||||
|
||||
if env_info.number < self.ethash_params.frontier_compatibility_mode_limit {
|
||||
if env_info.number < self.ethash_params.homestead_transition {
|
||||
Schedule::new_frontier()
|
||||
} else {
|
||||
} else if env_info.number < self.ethash_params.eip150_transition {
|
||||
Schedule::new_homestead()
|
||||
} else {
|
||||
Schedule::new_post_eip150(
|
||||
env_info.number >= self.ethash_params.eip160_transition,
|
||||
env_info.number >= self.ethash_params.eip161abc_transition,
|
||||
env_info.number >= self.ethash_params.eip161d_transition
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn signing_network_id(&self, env_info: &EnvInfo) -> Option<u8> {
|
||||
if env_info.number >= self.ethash_params.eip155_transition && self.params().network_id < 127 {
|
||||
Some(self.params().network_id as u8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +183,7 @@ impl Engine for Ethash {
|
||||
let mut state = block.fields_mut().state;
|
||||
for child in &self.ethash_params.dao_hardfork_accounts {
|
||||
let b = state.balance(child);
|
||||
state.transfer_balance(child, &self.ethash_params.dao_hardfork_beneficiary, &b);
|
||||
state.transfer_balance(child, &self.ethash_params.dao_hardfork_beneficiary, &b, CleanupMode::NoEmpty);
|
||||
}
|
||||
// }
|
||||
}
|
||||
@@ -157,12 +196,12 @@ impl Engine for Ethash {
|
||||
let fields = block.fields_mut();
|
||||
|
||||
// Bestow block reward
|
||||
fields.state.add_balance(&fields.header.author, &(reward + reward / U256::from(32) * U256::from(fields.uncles.len())));
|
||||
fields.state.add_balance(&fields.header.author, &(reward + reward / U256::from(32) * U256::from(fields.uncles.len())), CleanupMode::NoEmpty);
|
||||
|
||||
// Bestow uncle rewards
|
||||
let current_number = fields.header.number();
|
||||
for u in fields.uncles.iter() {
|
||||
fields.state.add_balance(u.author(), &(reward * U256::from(8 + u.number() - current_number) / U256::from(8)));
|
||||
fields.state.add_balance(u.author(), &(reward * U256::from(8 + u.number() - current_number) / U256::from(8)), CleanupMode::NoEmpty);
|
||||
}
|
||||
if let Err(e) = fields.state.commit() {
|
||||
warn!("Encountered error on state commit: {}", e);
|
||||
@@ -246,9 +285,16 @@ impl Engine for Ethash {
|
||||
}
|
||||
|
||||
fn verify_transaction_basic(&self, t: &SignedTransaction, header: &Header) -> result::Result<(), Error> {
|
||||
if header.number() >= self.ethash_params.frontier_compatibility_mode_limit {
|
||||
if header.number() >= self.ethash_params.homestead_transition {
|
||||
try!(t.check_low_s());
|
||||
}
|
||||
|
||||
if let Some(n) = t.network_id() {
|
||||
if header.number() < self.ethash_params.eip155_transition || n as usize != self.params().network_id {
|
||||
return Err(TransactionError::InvalidNetworkId.into())
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -268,7 +314,7 @@ impl Ethash {
|
||||
let min_difficulty = self.ethash_params.minimum_difficulty;
|
||||
let difficulty_bound_divisor = self.ethash_params.difficulty_bound_divisor;
|
||||
let duration_limit = self.ethash_params.duration_limit;
|
||||
let frontier_limit = self.ethash_params.frontier_compatibility_mode_limit;
|
||||
let frontier_limit = self.ethash_params.homestead_transition;
|
||||
|
||||
let mut target = if header.number < frontier_limit {
|
||||
if header.timestamp >= parent.timestamp + duration_limit {
|
||||
|
||||
@@ -44,8 +44,14 @@ pub fn new_frontier_test() -> Spec { Spec::load(include_bytes!("../../res/ethere
|
||||
/// Create a new Homestead chain spec as though it never changed from Frontier.
|
||||
pub fn new_homestead_test() -> Spec { Spec::load(include_bytes!("../../res/ethereum/homestead_test.json")) }
|
||||
|
||||
/// Create a new Homestead-EIP150 chain spec as though it never changed from Homestead/Frontier.
|
||||
pub fn new_eip150_test() -> Spec { Spec::load(include_bytes!("../../res/ethereum/eip150_test.json")) }
|
||||
|
||||
/// Create a new Homestead-EIP150 chain spec as though it never changed from Homestead/Frontier.
|
||||
pub fn new_eip161_test() -> Spec { Spec::load(include_bytes!("../../res/ethereum/eip161_test.json")) }
|
||||
|
||||
/// Create a new Frontier/Homestead/DAO chain spec with transition points at #5 and #8.
|
||||
pub fn new_daohardfork_test() -> Spec { Spec::load(include_bytes!("../../res/ethereum/daohardfork_test.json")) }
|
||||
pub fn new_transition_test() -> Spec { Spec::load(include_bytes!("../../res/ethereum/transition_test.json")) }
|
||||
|
||||
/// Create a new Frontier main net chain spec without genesis accounts.
|
||||
pub fn new_mainnet_like() -> Spec { Spec::load(include_bytes!("../../res/ethereum/frontier_like_test.json")) }
|
||||
|
||||
@@ -52,6 +52,12 @@ pub trait Ext {
|
||||
/// Determine whether an account exists.
|
||||
fn exists(&self, address: &Address) -> bool;
|
||||
|
||||
/// Determine whether an account exists and is not null (zero balance/nonce, no code).
|
||||
fn exists_and_not_null(&self, address: &Address) -> bool;
|
||||
|
||||
/// Balance of the origin account.
|
||||
fn origin_balance(&self) -> U256;
|
||||
|
||||
/// Returns address balance.
|
||||
fn balance(&self, address: &Address) -> U256;
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ use super::u256_to_address;
|
||||
use evm::{self, CostType};
|
||||
use evm::instructions::{self, Instruction, InstructionInfo};
|
||||
use evm::interpreter::stack::Stack;
|
||||
use evm::schedule::Schedule;
|
||||
|
||||
macro_rules! overflowing {
|
||||
($x: expr) => {{
|
||||
@@ -31,7 +32,7 @@ macro_rules! overflowing {
|
||||
#[cfg_attr(feature="dev", allow(enum_variant_names))]
|
||||
enum InstructionCost<Cost: CostType> {
|
||||
Gas(Cost),
|
||||
GasMem(Cost, Cost),
|
||||
GasMem(Cost, Cost, Option<Cost>),
|
||||
GasMemCopy(Cost, Cost, Cost)
|
||||
}
|
||||
|
||||
@@ -56,7 +57,37 @@ impl<Gas: CostType> Gasometer<Gas> {
|
||||
}
|
||||
}
|
||||
|
||||
/// How much gas is provided to a CALL/CREATE, given that we need to deduct `needed` for this operation
|
||||
/// and that we `requested` some.
|
||||
pub fn gas_provided(&self, schedule: &Schedule, needed: Gas, requested: Option<evm::Result<Gas>>) -> evm::Result<Gas> {
|
||||
match schedule.sub_gas_cap_divisor {
|
||||
Some(cap_divisor) if self.current_gas >= needed => {
|
||||
let gas_remaining = self.current_gas - needed;
|
||||
let max_gas_provided = gas_remaining - gas_remaining / Gas::from(cap_divisor);
|
||||
if let Some(Ok(r)) = requested {
|
||||
Ok(min(r, max_gas_provided))
|
||||
} else {
|
||||
Ok(max_gas_provided)
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
if let Some(r) = requested {
|
||||
r
|
||||
} else if self.current_gas >= needed {
|
||||
Ok(self.current_gas - needed)
|
||||
} else {
|
||||
Ok(0.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
|
||||
/// Determine how much gas is used by the given instruction, given the machine's state.
|
||||
///
|
||||
/// We guarantee that the final element of the returned tuple (`provided`) will be `Some`
|
||||
/// iff the `instruction` is one of `CREATE`, or any of the `CALL` variants. In this case,
|
||||
/// it will be the amount of gas that the current context provides to the child context.
|
||||
pub fn get_gas_cost_mem(
|
||||
&mut self,
|
||||
ext: &evm::Ext,
|
||||
@@ -64,7 +95,7 @@ impl<Gas: CostType> Gasometer<Gas> {
|
||||
info: &InstructionInfo,
|
||||
stack: &Stack<U256>,
|
||||
current_mem_size: usize,
|
||||
) -> evm::Result<(Gas, Gas, usize)> {
|
||||
) -> evm::Result<(Gas, Gas, usize, Option<Gas>)> {
|
||||
let schedule = ext.schedule();
|
||||
let tier = instructions::get_tier_idx(info.tier);
|
||||
let default_gas = Gas::from(schedule.tier_step_gas[tier]);
|
||||
@@ -90,26 +121,47 @@ impl<Gas: CostType> Gasometer<Gas> {
|
||||
instructions::SLOAD => {
|
||||
InstructionCost::Gas(Gas::from(schedule.sload_gas))
|
||||
},
|
||||
instructions::BALANCE => {
|
||||
InstructionCost::Gas(Gas::from(schedule.balance_gas))
|
||||
},
|
||||
instructions::EXTCODESIZE => {
|
||||
InstructionCost::Gas(Gas::from(schedule.extcodesize_gas))
|
||||
},
|
||||
instructions::SUICIDE => {
|
||||
let mut gas = Gas::from(schedule.suicide_gas);
|
||||
|
||||
let is_value_transfer = !ext.origin_balance().is_zero();
|
||||
let address = u256_to_address(stack.peek(0));
|
||||
if (
|
||||
!schedule.no_empty && !ext.exists(&address)
|
||||
) || (
|
||||
schedule.no_empty && is_value_transfer && !ext.exists_and_not_null(&address)
|
||||
) {
|
||||
gas = overflowing!(gas.overflow_add(schedule.suicide_to_new_account_cost.into()));
|
||||
}
|
||||
|
||||
InstructionCost::Gas(gas)
|
||||
},
|
||||
instructions::MSTORE | instructions::MLOAD => {
|
||||
InstructionCost::GasMem(default_gas, try!(mem_needed_const(stack.peek(0), 32)))
|
||||
InstructionCost::GasMem(default_gas, try!(mem_needed_const(stack.peek(0), 32)), None)
|
||||
},
|
||||
instructions::MSTORE8 => {
|
||||
InstructionCost::GasMem(default_gas, try!(mem_needed_const(stack.peek(0), 1)))
|
||||
InstructionCost::GasMem(default_gas, try!(mem_needed_const(stack.peek(0), 1)), None)
|
||||
},
|
||||
instructions::RETURN => {
|
||||
InstructionCost::GasMem(default_gas, try!(mem_needed(stack.peek(0), stack.peek(1))))
|
||||
InstructionCost::GasMem(default_gas, try!(mem_needed(stack.peek(0), stack.peek(1))), None)
|
||||
},
|
||||
instructions::SHA3 => {
|
||||
let w = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(1))), 31));
|
||||
let words = w >> 5;
|
||||
let gas = Gas::from(schedule.sha3_gas) + (Gas::from(schedule.sha3_word_gas) * words);
|
||||
InstructionCost::GasMem(gas, try!(mem_needed(stack.peek(0), stack.peek(1))))
|
||||
InstructionCost::GasMem(gas, try!(mem_needed(stack.peek(0), stack.peek(1))), None)
|
||||
},
|
||||
instructions::CALLDATACOPY | instructions::CODECOPY => {
|
||||
InstructionCost::GasMemCopy(default_gas, try!(mem_needed(stack.peek(0), stack.peek(2))), try!(Gas::from_u256(*stack.peek(2))))
|
||||
},
|
||||
instructions::EXTCODECOPY => {
|
||||
InstructionCost::GasMemCopy(default_gas, try!(mem_needed(stack.peek(1), stack.peek(3))), try!(Gas::from_u256(*stack.peek(3))))
|
||||
InstructionCost::GasMemCopy(schedule.extcodecopy_base_gas.into(), try!(mem_needed(stack.peek(1), stack.peek(3))), try!(Gas::from_u256(*stack.peek(3))))
|
||||
},
|
||||
instructions::LOG0...instructions::LOG4 => {
|
||||
let no_of_topics = instructions::get_log_topics(instruction);
|
||||
@@ -117,39 +169,68 @@ impl<Gas: CostType> Gasometer<Gas> {
|
||||
|
||||
let data_gas = overflowing!(try!(Gas::from_u256(*stack.peek(1))).overflow_mul(Gas::from(schedule.log_data_gas)));
|
||||
let gas = overflowing!(data_gas.overflow_add(Gas::from(log_gas)));
|
||||
InstructionCost::GasMem(gas, try!(mem_needed(stack.peek(0), stack.peek(1))))
|
||||
InstructionCost::GasMem(gas, try!(mem_needed(stack.peek(0), stack.peek(1))), None)
|
||||
},
|
||||
instructions::CALL | instructions::CALLCODE => {
|
||||
let mut gas = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(0))), schedule.call_gas));
|
||||
let mut gas = Gas::from(schedule.call_gas);
|
||||
let mem = cmp::max(
|
||||
try!(mem_needed(stack.peek(5), stack.peek(6))),
|
||||
try!(mem_needed(stack.peek(3), stack.peek(4)))
|
||||
);
|
||||
|
||||
let address = u256_to_address(stack.peek(1));
|
||||
let is_value_transfer = !stack.peek(2).is_zero();
|
||||
|
||||
if instruction == instructions::CALL && !ext.exists(&address) {
|
||||
gas = overflowing!(gas.overflow_add(Gas::from(schedule.call_new_account_gas)));
|
||||
if instruction == instructions::CALL {
|
||||
if (
|
||||
!schedule.no_empty && !ext.exists(&address)
|
||||
) || (
|
||||
schedule.no_empty && is_value_transfer && !ext.exists_and_not_null(&address)
|
||||
) {
|
||||
gas = overflowing!(gas.overflow_add(schedule.call_new_account_gas.into()));
|
||||
}
|
||||
};
|
||||
|
||||
if !stack.peek(2).is_zero() {
|
||||
gas = overflowing!(gas.overflow_add(Gas::from(schedule.call_value_transfer_gas)));
|
||||
if is_value_transfer {
|
||||
gas = overflowing!(gas.overflow_add(schedule.call_value_transfer_gas.into()));
|
||||
};
|
||||
|
||||
InstructionCost::GasMem(gas,mem)
|
||||
// TODO: refactor to avoid duplicate calculation here and later on.
|
||||
let (mem_gas_cost, _, _) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem));
|
||||
let cost_so_far = overflowing!(gas.overflow_add(mem_gas_cost.into()));
|
||||
let requested = Gas::from_u256(*stack.peek(0));
|
||||
let provided = try!(self.gas_provided(schedule, cost_so_far, Some(requested)));
|
||||
gas = overflowing!(gas.overflow_add(provided));
|
||||
|
||||
InstructionCost::GasMem(gas, mem, Some(provided))
|
||||
},
|
||||
instructions::DELEGATECALL => {
|
||||
let gas = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(0))), schedule.call_gas));
|
||||
let mut gas = Gas::from(schedule.call_gas);
|
||||
let mem = cmp::max(
|
||||
try!(mem_needed(stack.peek(4), stack.peek(5))),
|
||||
try!(mem_needed(stack.peek(2), stack.peek(3)))
|
||||
);
|
||||
InstructionCost::GasMem(gas, mem)
|
||||
|
||||
// TODO: refactor to avoid duplicate calculation here and later on.
|
||||
let (mem_gas_cost, _, _) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem));
|
||||
let cost_so_far = overflowing!(gas.overflow_add(mem_gas_cost.into()));
|
||||
let requested = Gas::from_u256(*stack.peek(0));
|
||||
let provided = try!(self.gas_provided(schedule, cost_so_far, Some(requested)));
|
||||
gas = overflowing!(gas.overflow_add(provided));
|
||||
|
||||
InstructionCost::GasMem(gas, mem, Some(provided))
|
||||
},
|
||||
instructions::CREATE => {
|
||||
let gas = Gas::from(schedule.create_gas);
|
||||
let mut gas = Gas::from(schedule.create_gas);
|
||||
let mem = try!(mem_needed(stack.peek(1), stack.peek(2)));
|
||||
InstructionCost::GasMem(gas, mem)
|
||||
|
||||
// TODO: refactor to avoid duplicate calculation here and later on.
|
||||
let (mem_gas_cost, _, _) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem));
|
||||
let cost_so_far = overflowing!(gas.overflow_add(mem_gas_cost.into()));
|
||||
let provided = try!(self.gas_provided(schedule, cost_so_far, None));
|
||||
gas = overflowing!(gas.overflow_add(provided));
|
||||
|
||||
InstructionCost::GasMem(gas, mem, Some(provided))
|
||||
},
|
||||
instructions::EXP => {
|
||||
let expon = stack.peek(1);
|
||||
@@ -157,17 +238,17 @@ impl<Gas: CostType> Gasometer<Gas> {
|
||||
let gas = Gas::from(schedule.exp_gas + schedule.exp_byte_gas * bytes);
|
||||
InstructionCost::Gas(gas)
|
||||
},
|
||||
_ => InstructionCost::Gas(default_gas)
|
||||
_ => InstructionCost::Gas(default_gas),
|
||||
};
|
||||
|
||||
match cost {
|
||||
InstructionCost::Gas(gas) => {
|
||||
Ok((gas, self.current_mem_gas, 0))
|
||||
Ok((gas, self.current_mem_gas, 0, None))
|
||||
},
|
||||
InstructionCost::GasMem(gas, mem_size) => {
|
||||
InstructionCost::GasMem(gas, mem_size, provided) => {
|
||||
let (mem_gas_cost, new_mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem_size));
|
||||
let gas = overflowing!(gas.overflow_add(mem_gas_cost));
|
||||
Ok((gas, new_mem_gas, new_mem_size))
|
||||
Ok((gas, new_mem_gas, new_mem_size, provided))
|
||||
},
|
||||
InstructionCost::GasMemCopy(gas, mem_size, copy) => {
|
||||
let (mem_gas_cost, new_mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem_size));
|
||||
@@ -175,7 +256,7 @@ impl<Gas: CostType> Gasometer<Gas> {
|
||||
let copy_gas = Gas::from(schedule.copy_gas) * copy;
|
||||
let gas = overflowing!(gas.overflow_add(copy_gas));
|
||||
let gas = overflowing!(gas.overflow_add(mem_gas_cost));
|
||||
Ok((gas, new_mem_gas, new_mem_size))
|
||||
Ok((gas, new_mem_gas, new_mem_size, None))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,8 +89,6 @@ impl<'a> CodeReader<'a> {
|
||||
|
||||
enum InstructionResult<Gas> {
|
||||
Ok,
|
||||
UseAllGas,
|
||||
GasLeft(Gas),
|
||||
UnusedGas(Gas),
|
||||
JumpToPosition(U256),
|
||||
// gas left, init_orf, init_size
|
||||
@@ -129,7 +127,7 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
||||
try!(self.verify_instruction(ext, instruction, &info, &stack));
|
||||
|
||||
// Calculate gas cost
|
||||
let (gas_cost, mem_gas, mem_size) = try!(gasometer.get_gas_cost_mem(ext, instruction, &info, &stack, self.mem.size()));
|
||||
let (gas_cost, mem_gas, mem_size, provided) = try!(gasometer.get_gas_cost_mem(ext, instruction, &info, &stack, self.mem.size()));
|
||||
// TODO: make compile-time removable if too much of a performance hit.
|
||||
let trace_executed = ext.trace_prepare_execute(reader.position - 1, instruction, &gas_cost.as_u256());
|
||||
|
||||
@@ -155,25 +153,19 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
||||
|
||||
// Execute instruction
|
||||
let result = try!(self.exec_instruction(
|
||||
gasometer.current_gas, ¶ms, ext, instruction, &mut reader, &mut stack
|
||||
gasometer.current_gas, ¶ms, ext, instruction, &mut reader, &mut stack, provided
|
||||
));
|
||||
|
||||
if let InstructionResult::UnusedGas(ref gas) = result {
|
||||
gasometer.current_gas = gasometer.current_gas + *gas;
|
||||
}
|
||||
|
||||
if trace_executed {
|
||||
ext.trace_executed(gasometer.current_gas.as_u256(), stack.peek_top(info.ret), mem_written.map(|(o, s)| (o, &(self.mem[o..(o + s)]))), store_written);
|
||||
}
|
||||
|
||||
// Advance
|
||||
match result {
|
||||
InstructionResult::Ok => {},
|
||||
InstructionResult::UnusedGas(gas) => {
|
||||
gasometer.current_gas = gasometer.current_gas + gas;
|
||||
},
|
||||
InstructionResult::UseAllGas => {
|
||||
gasometer.current_gas = Cost::from(0);
|
||||
},
|
||||
InstructionResult::GasLeft(gas_left) => {
|
||||
gasometer.current_gas = gas_left;
|
||||
},
|
||||
InstructionResult::JumpToPosition(position) => {
|
||||
let pos = try!(self.verify_jump(position, &valid_jump_destinations));
|
||||
reader.position = pos;
|
||||
@@ -182,6 +174,7 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
||||
return Ok(GasLeft::NeedsReturn(gas.as_u256(), self.mem.read_slice(off, size)));
|
||||
},
|
||||
InstructionResult::StopExecution => break,
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,7 +257,8 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
ext: &mut evm::Ext,
|
||||
instruction: Instruction,
|
||||
code: &mut CodeReader,
|
||||
stack: &mut Stack<U256>
|
||||
stack: &mut Stack<U256>,
|
||||
provided: Option<Cost>
|
||||
) -> evm::Result<InstructionResult<Cost>> {
|
||||
match instruction {
|
||||
instructions::JUMP => {
|
||||
@@ -289,31 +283,32 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
let endowment = stack.pop_back();
|
||||
let init_off = stack.pop_back();
|
||||
let init_size = stack.pop_back();
|
||||
let create_gas = provided.expect("`provided` comes through Self::exec from `Gasometer::get_gas_cost_mem`; `gas_gas_mem_cost` guarantees `Some` when instruction is `CALL`/`CALLCODE`/`DELEGATECALL`/`CREATE`; this is `CREATE`; qed");
|
||||
|
||||
let contract_code = self.mem.read_slice(init_off, init_size);
|
||||
let can_create = ext.balance(¶ms.address) >= endowment && ext.depth() < ext.schedule().max_depth;
|
||||
|
||||
if !can_create {
|
||||
stack.push(U256::zero());
|
||||
return Ok(InstructionResult::Ok);
|
||||
return Ok(InstructionResult::UnusedGas(create_gas));
|
||||
}
|
||||
|
||||
let create_result = ext.create(&gas.as_u256(), &endowment, contract_code);
|
||||
let create_result = ext.create(&create_gas.as_u256(), &endowment, contract_code);
|
||||
return match create_result {
|
||||
ContractCreateResult::Created(address, gas_left) => {
|
||||
stack.push(address_to_u256(address));
|
||||
Ok(InstructionResult::GasLeft(Cost::from_u256(gas_left).expect("Gas left cannot be greater.")))
|
||||
Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater.")))
|
||||
},
|
||||
ContractCreateResult::Failed => {
|
||||
stack.push(U256::zero());
|
||||
// TODO [todr] Should we just StopExecution here?
|
||||
Ok(InstructionResult::UseAllGas)
|
||||
Ok(InstructionResult::Ok)
|
||||
}
|
||||
};
|
||||
},
|
||||
instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL => {
|
||||
assert!(ext.schedule().call_value_transfer_gas > ext.schedule().call_stipend, "overflow possible");
|
||||
let call_gas = Cost::from_u256(stack.pop_back()).expect("Gas is already validated.");
|
||||
stack.pop_back();
|
||||
let call_gas = provided.expect("`provided` comes through Self::exec from `Gasometer::get_gas_cost_mem`; `gas_gas_mem_cost` guarantees `Some` when instruction is `CALL`/`CALLCODE`/`DELEGATECALL`/`CREATE`; this is one of `CALL`/`CALLCODE`/`DELEGATECALL`; qed");
|
||||
let code_address = stack.pop_back();
|
||||
let code_address = u256_to_address(&code_address);
|
||||
|
||||
@@ -331,7 +326,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
// Add stipend (only CALL|CALLCODE when value > 0)
|
||||
let call_gas = call_gas + value.map_or_else(|| Cost::from(0), |val| match val.is_zero() {
|
||||
false => Cost::from(ext.schedule().call_stipend),
|
||||
true => Cost::from(0)
|
||||
true => Cost::from(0),
|
||||
});
|
||||
|
||||
// Get sender & receive addresses, check if we have balance
|
||||
|
||||
@@ -80,6 +80,23 @@ pub struct Schedule {
|
||||
pub tx_data_non_zero_gas: usize,
|
||||
/// Gas price for copying memory
|
||||
pub copy_gas: usize,
|
||||
/// Price of EXTCODESIZE
|
||||
pub extcodesize_gas: usize,
|
||||
/// Base price of EXTCODECOPY
|
||||
pub extcodecopy_base_gas: usize,
|
||||
/// Price of BALANCE
|
||||
pub balance_gas: usize,
|
||||
/// Price of SUICIDE
|
||||
pub suicide_gas: usize,
|
||||
/// Amount of additional gas to pay when SUICIDE credits a non-existant account
|
||||
pub suicide_to_new_account_cost: usize,
|
||||
/// If Some(x): let limit = GAS * (x - 1) / x; let CALL's gas = min(requested, limit). let CREATE's gas = limit.
|
||||
/// If None: let CALL's gas = (requested > GAS ? [OOG] : GAS). let CREATE's gas = GAS
|
||||
pub sub_gas_cap_divisor: Option<usize>,
|
||||
/// Don't ever make empty accounts; contracts start with nonce=1. Also, don't charge 25k when sending/suicide zero-value.
|
||||
pub no_empty: bool,
|
||||
/// Kill empty accounts if touched.
|
||||
pub kill_empty: bool,
|
||||
}
|
||||
|
||||
impl Schedule {
|
||||
@@ -93,8 +110,53 @@ impl Schedule {
|
||||
Self::new(true, true, 53000)
|
||||
}
|
||||
|
||||
/// Schedule for the post-EIP-150-era of the Ethereum main net.
|
||||
pub fn new_post_eip150(fix_exp: bool, no_empty: bool, kill_empty: bool) -> Schedule {
|
||||
Schedule {
|
||||
exceptional_failed_code_deposit: true,
|
||||
have_delegate_call: true,
|
||||
stack_limit: 1024,
|
||||
max_depth: 1024,
|
||||
tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0],
|
||||
exp_gas: 10,
|
||||
exp_byte_gas: if fix_exp {50} else {10},
|
||||
sha3_gas: 30,
|
||||
sha3_word_gas: 6,
|
||||
sload_gas: 200,
|
||||
sstore_set_gas: 20000,
|
||||
sstore_reset_gas: 5000,
|
||||
sstore_refund_gas: 15000,
|
||||
jumpdest_gas: 1,
|
||||
log_gas: 375,
|
||||
log_data_gas: 8,
|
||||
log_topic_gas: 375,
|
||||
create_gas: 32000,
|
||||
call_gas: 700,
|
||||
call_stipend: 2300,
|
||||
call_value_transfer_gas: 9000,
|
||||
call_new_account_gas: 25000,
|
||||
suicide_refund_gas: 24000,
|
||||
memory_gas: 3,
|
||||
quad_coeff_div: 512,
|
||||
create_data_gas: 200,
|
||||
tx_gas: 21000,
|
||||
tx_create_gas: 53000,
|
||||
tx_data_zero_gas: 4,
|
||||
tx_data_non_zero_gas: 68,
|
||||
copy_gas: 3,
|
||||
extcodesize_gas: 700,
|
||||
extcodecopy_base_gas: 700,
|
||||
balance_gas: 400,
|
||||
suicide_gas: 5000,
|
||||
suicide_to_new_account_cost: 25000,
|
||||
sub_gas_cap_divisor: Some(64),
|
||||
no_empty: no_empty,
|
||||
kill_empty: kill_empty,
|
||||
}
|
||||
}
|
||||
|
||||
fn new(efcd: bool, hdc: bool, tcg: usize) -> Schedule {
|
||||
Schedule{
|
||||
Schedule {
|
||||
exceptional_failed_code_deposit: efcd,
|
||||
have_delegate_call: hdc,
|
||||
stack_limit: 1024,
|
||||
@@ -126,6 +188,14 @@ impl Schedule {
|
||||
tx_data_zero_gas: 4,
|
||||
tx_data_non_zero_gas: 68,
|
||||
copy_gas: 3,
|
||||
extcodesize_gas: 20,
|
||||
extcodecopy_base_gas: 20,
|
||||
balance_gas: 20,
|
||||
suicide_gas: 0,
|
||||
suicide_to_new_account_cost: 0,
|
||||
sub_gas_cap_divisor: None,
|
||||
no_empty: false,
|
||||
kill_empty: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +92,14 @@ impl Ext for FakeExt {
|
||||
self.balances.contains_key(address)
|
||||
}
|
||||
|
||||
fn exists_and_not_null(&self, address: &Address) -> bool {
|
||||
self.balances.get(address).map_or(false, |b| !b.is_zero())
|
||||
}
|
||||
|
||||
fn origin_balance(&self) -> U256 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn balance(&self, address: &Address) -> U256 {
|
||||
*self.balances.get(address).unwrap()
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
//! Transaction Execution environment.
|
||||
use common::*;
|
||||
use state::*;
|
||||
use state::{State, CleanupMode};
|
||||
use engines::Engine;
|
||||
use types::executed::CallType;
|
||||
use evm::{self, Ext, Factory, Finalize};
|
||||
@@ -252,9 +252,11 @@ impl<'a> Executive<'a> {
|
||||
// backup used in case of running out of gas
|
||||
self.state.snapshot();
|
||||
|
||||
let schedule = self.engine.schedule(self.info);
|
||||
|
||||
// at first, transfer value to destination
|
||||
if let ActionValue::Transfer(val) = params.value {
|
||||
self.state.transfer_balance(¶ms.sender, ¶ms.address, &val);
|
||||
self.state.transfer_balance(¶ms.sender, ¶ms.address, &val, substate.to_cleanup_mode(&schedule));
|
||||
}
|
||||
trace!("Executive::call(params={:?}) self.env_info={:?}", params, self.info);
|
||||
|
||||
@@ -360,12 +362,14 @@ impl<'a> Executive<'a> {
|
||||
let mut unconfirmed_substate = Substate::new();
|
||||
|
||||
// create contract and transfer value to it if necessary
|
||||
let schedule = self.engine.schedule(self.info);
|
||||
let nonce_offset = if schedule.no_empty {1} else {0}.into();
|
||||
let prev_bal = self.state.balance(¶ms.address);
|
||||
if let ActionValue::Transfer(val) = params.value {
|
||||
self.state.sub_balance(¶ms.sender, &val);
|
||||
self.state.new_contract(¶ms.address, val + prev_bal);
|
||||
self.state.new_contract(¶ms.address, val + prev_bal, nonce_offset);
|
||||
} else {
|
||||
self.state.new_contract(¶ms.address, prev_bal);
|
||||
self.state.new_contract(¶ms.address, prev_bal, nonce_offset);
|
||||
}
|
||||
|
||||
let trace_info = tracer.prepare_trace_create(¶ms);
|
||||
@@ -401,7 +405,7 @@ impl<'a> Executive<'a> {
|
||||
fn finalize(
|
||||
&mut self,
|
||||
t: &SignedTransaction,
|
||||
substate: Substate,
|
||||
mut substate: Substate,
|
||||
result: evm::Result<U256>,
|
||||
output: Bytes,
|
||||
trace: Vec<FlatTrace>,
|
||||
@@ -427,16 +431,32 @@ impl<'a> Executive<'a> {
|
||||
trace!("exec::finalize: t.gas={}, sstore_refunds={}, suicide_refunds={}, refunds_bound={}, gas_left_prerefund={}, refunded={}, gas_left={}, gas_used={}, refund_value={}, fees_value={}\n",
|
||||
t.gas, sstore_refunds, suicide_refunds, refunds_bound, gas_left_prerefund, refunded, gas_left, gas_used, refund_value, fees_value);
|
||||
|
||||
trace!("exec::finalize: Refunding refund_value={}, sender={}\n", refund_value, t.sender().unwrap());
|
||||
self.state.add_balance(&t.sender().unwrap(), &refund_value);
|
||||
let sender = match t.sender() {
|
||||
Ok(sender) => sender,
|
||||
Err(e) => {
|
||||
debug!(target: "executive", "attempted to finalize transaction without sender: {}", e);
|
||||
return Err(ExecutionError::Internal);
|
||||
}
|
||||
};
|
||||
|
||||
trace!("exec::finalize: Refunding refund_value={}, sender={}\n", refund_value, sender);
|
||||
// Below: NoEmpty is safe since the sender must already be non-null to have sent this transaction
|
||||
self.state.add_balance(&sender, &refund_value, CleanupMode::NoEmpty);
|
||||
trace!("exec::finalize: Compensating author: fees_value={}, author={}\n", fees_value, &self.info.author);
|
||||
self.state.add_balance(&self.info.author, &fees_value);
|
||||
self.state.add_balance(&self.info.author, &fees_value, substate.to_cleanup_mode(&schedule));
|
||||
|
||||
// perform suicides
|
||||
for address in &substate.suicides {
|
||||
self.state.kill_account(address);
|
||||
}
|
||||
|
||||
// perform garbage-collection
|
||||
for address in &substate.garbage {
|
||||
if self.state.exists(address) && !self.state.exists_and_not_null(address) {
|
||||
self.state.kill_account(address);
|
||||
}
|
||||
}
|
||||
|
||||
match result {
|
||||
Err(evm::Error::Internal) => Err(ExecutionError::Internal),
|
||||
Err(_) => {
|
||||
@@ -499,6 +519,7 @@ mod tests {
|
||||
use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer};
|
||||
use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer};
|
||||
use types::executed::CallType;
|
||||
use state::{CleanupMode};
|
||||
|
||||
#[test]
|
||||
fn test_contract_address() {
|
||||
@@ -520,7 +541,7 @@ mod tests {
|
||||
params.value = ActionValue::Transfer(U256::from(0x7));
|
||||
let mut state_result = get_temp_state();
|
||||
let mut state = state_result.reference_mut();
|
||||
state.add_balance(&sender, &U256::from(0x100u64));
|
||||
state.add_balance(&sender, &U256::from(0x100u64), CleanupMode::NoEmpty);
|
||||
let info = EnvInfo::default();
|
||||
let engine = TestEngine::new(0);
|
||||
let mut substate = Substate::new();
|
||||
@@ -579,7 +600,7 @@ mod tests {
|
||||
params.value = ActionValue::Transfer(U256::from(100));
|
||||
let mut state_result = get_temp_state();
|
||||
let mut state = state_result.reference_mut();
|
||||
state.add_balance(&sender, &U256::from(100));
|
||||
state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty);
|
||||
let info = EnvInfo::default();
|
||||
let engine = TestEngine::new(0);
|
||||
let mut substate = Substate::new();
|
||||
@@ -635,7 +656,7 @@ mod tests {
|
||||
params.call_type = CallType::Call;
|
||||
let mut state_result = get_temp_state();
|
||||
let mut state = state_result.reference_mut();
|
||||
state.add_balance(&sender, &U256::from(100));
|
||||
state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty);
|
||||
let info = EnvInfo::default();
|
||||
let engine = TestEngine::new(5);
|
||||
let mut substate = Substate::new();
|
||||
@@ -693,7 +714,7 @@ mod tests {
|
||||
VMOperation { pc: 33, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99985.into(), stack_push: vec_into![29], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 35, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99982.into(), stack_push: vec_into![3], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 37, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99979.into(), stack_push: vec_into![23], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 39, instruction: 240, gas_cost: 32000.into(), executed: Some(VMExecutedOperation { gas_used: 67979.into(), stack_push: vec_into![U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap()], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 39, instruction: 240, gas_cost: 99979.into(), executed: Some(VMExecutedOperation { gas_used: 64755.into(), stack_push: vec_into![U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap()], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 40, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 64752.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) },
|
||||
VMOperation { pc: 42, instruction: 85, gas_cost: 20000.into(), executed: Some(VMExecutedOperation { gas_used: 44752.into(), stack_push: vec_into![], mem_diff: None, store_diff: Some(StorageDiff { location: 0.into(), value: U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap() }) }) }
|
||||
],
|
||||
@@ -744,7 +765,7 @@ mod tests {
|
||||
params.value = ActionValue::Transfer(100.into());
|
||||
let mut state_result = get_temp_state();
|
||||
let mut state = state_result.reference_mut();
|
||||
state.add_balance(&sender, &U256::from(100));
|
||||
state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty);
|
||||
let info = EnvInfo::default();
|
||||
let engine = TestEngine::new(5);
|
||||
let mut substate = Substate::new();
|
||||
@@ -832,7 +853,7 @@ mod tests {
|
||||
params.value = ActionValue::Transfer(U256::from(100));
|
||||
let mut state_result = get_temp_state();
|
||||
let mut state = state_result.reference_mut();
|
||||
state.add_balance(&sender, &U256::from(100));
|
||||
state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty);
|
||||
let info = EnvInfo::default();
|
||||
let engine = TestEngine::new(0);
|
||||
let mut substate = Substate::new();
|
||||
@@ -884,7 +905,7 @@ mod tests {
|
||||
params.value = ActionValue::Transfer(U256::from(100));
|
||||
let mut state_result = get_temp_state();
|
||||
let mut state = state_result.reference_mut();
|
||||
state.add_balance(&sender, &U256::from(100));
|
||||
state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty);
|
||||
let info = EnvInfo::default();
|
||||
let engine = TestEngine::new(1024);
|
||||
let mut substate = Substate::new();
|
||||
@@ -944,7 +965,7 @@ mod tests {
|
||||
let mut state = state_result.reference_mut();
|
||||
state.init_code(&address_a, code_a.clone());
|
||||
state.init_code(&address_b, code_b.clone());
|
||||
state.add_balance(&sender, &U256::from(100_000));
|
||||
state.add_balance(&sender, &U256::from(100_000), CleanupMode::NoEmpty);
|
||||
|
||||
let info = EnvInfo::default();
|
||||
let engine = TestEngine::new(0);
|
||||
@@ -1017,13 +1038,13 @@ mod tests {
|
||||
gas: U256::from(100_000),
|
||||
gas_price: U256::zero(),
|
||||
nonce: U256::zero()
|
||||
}.sign(keypair.secret());
|
||||
}.sign(keypair.secret(), None);
|
||||
let sender = t.sender().unwrap();
|
||||
let contract = contract_address(&sender, &U256::zero());
|
||||
|
||||
let mut state_result = get_temp_state();
|
||||
let mut state = state_result.reference_mut();
|
||||
state.add_balance(&sender, &U256::from(18));
|
||||
state.add_balance(&sender, &U256::from(18), CleanupMode::NoEmpty);
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = U256::from(100_000);
|
||||
let engine = TestEngine::new(0);
|
||||
@@ -1084,12 +1105,12 @@ mod tests {
|
||||
gas: U256::from(100_000),
|
||||
gas_price: U256::zero(),
|
||||
nonce: U256::one()
|
||||
}.sign(keypair.secret());
|
||||
}.sign(keypair.secret(), None);
|
||||
let sender = t.sender().unwrap();
|
||||
|
||||
let mut state_result = get_temp_state();
|
||||
let mut state = state_result.reference_mut();
|
||||
state.add_balance(&sender, &U256::from(17));
|
||||
state.add_balance(&sender, &U256::from(17), CleanupMode::NoEmpty);
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = U256::from(100_000);
|
||||
let engine = TestEngine::new(0);
|
||||
@@ -1117,12 +1138,12 @@ mod tests {
|
||||
gas: U256::from(80_001),
|
||||
gas_price: U256::zero(),
|
||||
nonce: U256::zero()
|
||||
}.sign(keypair.secret());
|
||||
}.sign(keypair.secret(), None);
|
||||
let sender = t.sender().unwrap();
|
||||
|
||||
let mut state_result = get_temp_state();
|
||||
let mut state = state_result.reference_mut();
|
||||
state.add_balance(&sender, &U256::from(17));
|
||||
state.add_balance(&sender, &U256::from(17), CleanupMode::NoEmpty);
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_used = U256::from(20_000);
|
||||
info.gas_limit = U256::from(100_000);
|
||||
@@ -1152,12 +1173,12 @@ mod tests {
|
||||
gas: U256::from(100_000),
|
||||
gas_price: U256::one(),
|
||||
nonce: U256::zero()
|
||||
}.sign(keypair.secret());
|
||||
}.sign(keypair.secret(), None);
|
||||
let sender = t.sender().unwrap();
|
||||
|
||||
let mut state_result = get_temp_state();
|
||||
let mut state = state_result.reference_mut();
|
||||
state.add_balance(&sender, &U256::from(100_017));
|
||||
state.add_balance(&sender, &U256::from(100_017), CleanupMode::NoEmpty);
|
||||
let mut info = EnvInfo::default();
|
||||
info.gas_limit = U256::from(100_000);
|
||||
let engine = TestEngine::new(0);
|
||||
@@ -1192,7 +1213,7 @@ mod tests {
|
||||
params.value = ActionValue::Transfer(U256::from_str("0de0b6b3a7640000").unwrap());
|
||||
let mut state_result = get_temp_state();
|
||||
let mut state = state_result.reference_mut();
|
||||
state.add_balance(&sender, &U256::from_str("152d02c7e14af6800000").unwrap());
|
||||
state.add_balance(&sender, &U256::from_str("152d02c7e14af6800000").unwrap(), CleanupMode::NoEmpty);
|
||||
let info = EnvInfo::default();
|
||||
let engine = TestEngine::new(0);
|
||||
let mut substate = Substate::new();
|
||||
|
||||
@@ -113,6 +113,12 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
|
||||
self.state.exists(address)
|
||||
}
|
||||
|
||||
fn exists_and_not_null(&self, address: &Address) -> bool {
|
||||
self.state.exists_and_not_null(address)
|
||||
}
|
||||
|
||||
fn origin_balance(&self) -> U256 { self.balance(&self.origin_info.address) }
|
||||
|
||||
fn balance(&self, address: &Address) -> U256 {
|
||||
self.state.balance(address)
|
||||
}
|
||||
@@ -266,11 +272,11 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
|
||||
let address = self.origin_info.address.clone();
|
||||
let balance = self.balance(&address);
|
||||
if &address == refund_address {
|
||||
// TODO [todr] To be consisted with CPP client we set balance to 0 in that case.
|
||||
// TODO [todr] To be consistent with CPP client we set balance to 0 in that case.
|
||||
self.state.sub_balance(&address, &balance);
|
||||
} else {
|
||||
trace!("Suiciding {} -> {} (xfer: {})", address, refund_address, balance);
|
||||
self.state.transfer_balance(&address, refund_address, &balance);
|
||||
trace!(target: "ext", "Suiciding {} -> {} (xfer: {})", address, refund_address, balance);
|
||||
self.state.transfer_balance(&address, refund_address, &balance, self.substate.to_cleanup_mode(&self.schedule));
|
||||
}
|
||||
|
||||
self.tracer.trace_suicide(address, balance, refund_address.clone());
|
||||
|
||||
@@ -48,7 +48,9 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
|
||||
let mut spec = match era {
|
||||
ChainEra::Frontier => ethereum::new_frontier_test(),
|
||||
ChainEra::Homestead => ethereum::new_homestead_test(),
|
||||
ChainEra::DaoHardfork => ethereum::new_daohardfork_test(),
|
||||
ChainEra::Eip150 => ethereum::new_eip150_test(),
|
||||
ChainEra::Eip161 => ethereum::new_eip161_test(),
|
||||
ChainEra::TransitionTest => ethereum::new_transition_test(),
|
||||
};
|
||||
spec.set_genesis_state(state);
|
||||
spec.overwrite_genesis_params(genesis);
|
||||
@@ -114,14 +116,38 @@ mod frontier_era_tests {
|
||||
declare_test!{BlockchainTests_RandomTests_bl201507071825GO, "BlockchainTests/RandomTests/bl201507071825GO"}
|
||||
}
|
||||
|
||||
mod daohardfork_tests {
|
||||
mod transition_tests {
|
||||
use tests::helpers::*;
|
||||
use super::json_chain_test;
|
||||
|
||||
fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
||||
json_chain_test(json_data, ChainEra::DaoHardfork)
|
||||
json_chain_test(json_data, ChainEra::TransitionTest)
|
||||
}
|
||||
|
||||
declare_test!{BlockchainTests_TestNetwork_bcSimpleTransitionTest, "BlockchainTests/TestNetwork/bcSimpleTransitionTest"}
|
||||
declare_test!{BlockchainTests_TestNetwork_bcTheDaoTest, "BlockchainTests/TestNetwork/bcTheDaoTest"}
|
||||
declare_test!{BlockchainTests_TestNetwork_bcEIP150Test, "BlockchainTests/TestNetwork/bcEIP150Test"}
|
||||
}
|
||||
|
||||
mod eip150_blockchain_tests {
|
||||
use tests::helpers::*;
|
||||
use super::json_chain_test;
|
||||
|
||||
fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
||||
json_chain_test(json_data, ChainEra::Eip150)
|
||||
}
|
||||
|
||||
declare_test!{BlockchainTests_EIP150_bcBlockGasLimitTest, "BlockchainTests/EIP150/bcBlockGasLimitTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcForkStressTest, "BlockchainTests/EIP150/bcForkStressTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcGasPricerTest, "BlockchainTests/EIP150/bcGasPricerTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcInvalidHeaderTest, "BlockchainTests/EIP150/bcInvalidHeaderTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcInvalidRLPTest, "BlockchainTests/EIP150/bcInvalidRLPTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcMultiChainTest, "BlockchainTests/EIP150/bcMultiChainTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcRPC_API_Test, "BlockchainTests/EIP150/bcRPC_API_Test"}
|
||||
declare_test!{BlockchainTests_EIP150_bcStateTest, "BlockchainTests/EIP150/bcStateTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcTotalDifficultyTest, "BlockchainTests/EIP150/bcTotalDifficultyTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcUncleHeaderValiditiy, "BlockchainTests/EIP150/bcUncleHeaderValiditiy"}
|
||||
declare_test!{BlockchainTests_EIP150_bcUncleTest, "BlockchainTests/EIP150/bcUncleTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcValidBlockTest, "BlockchainTests/EIP150/bcValidBlockTest"}
|
||||
declare_test!{BlockchainTests_EIP150_bcWalletTest, "BlockchainTests/EIP150/bcWalletTest"}
|
||||
}
|
||||
|
||||
43
ethcore/src/json_tests/eip150_state.rs
Normal file
43
ethcore/src/json_tests/eip150_state.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
// 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 super::test_common::*;
|
||||
use tests::helpers::*;
|
||||
use super::state::json_chain_test;
|
||||
|
||||
fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
||||
json_chain_test(json_data, ChainEra::Eip150)
|
||||
}
|
||||
|
||||
declare_test!{StateTests_EIP150_stEIPSpecificTest, "StateTests/EIP150/stEIPSpecificTest"}
|
||||
declare_test!{StateTests_EIP150_stEIPsingleCodeGasPrices, "StateTests/EIP150/stEIPsingleCodeGasPrices"}
|
||||
declare_test!{StateTests_EIP150_stMemExpandingEIPCalls, "StateTests/EIP150/stMemExpandingEIPCalls"}
|
||||
|
||||
declare_test!{StateTests_EIP150_stCallCodes, "StateTests/EIP150/Homestead/stCallCodes"}
|
||||
declare_test!{StateTests_EIP150_stCallCreateCallCodeTest, "StateTests/EIP150/Homestead/stCallCreateCallCodeTest"}
|
||||
declare_test!{StateTests_EIP150_stDelegatecallTest, "StateTests/EIP150/Homestead/stDelegatecallTest"}
|
||||
declare_test!{StateTests_EIP150_stInitCodeTest, "StateTests/EIP150/Homestead/stInitCodeTest"}
|
||||
declare_test!{StateTests_EIP150_stLogTests, "StateTests/EIP150/Homestead/stLogTests"}
|
||||
declare_test!{heavy => StateTests_EIP150_stMemoryStressTest, "StateTests/EIP150/Homestead/stMemoryStressTest"}
|
||||
declare_test!{heavy => StateTests_EIP150_stMemoryTest, "StateTests/EIP150/Homestead/stMemoryTest"}
|
||||
declare_test!{StateTests_EIP150_stPreCompiledContracts, "StateTests/EIP150/Homestead/stPreCompiledContracts"}
|
||||
declare_test!{heavy => StateTests_EIP150_stQuadraticComplexityTest, "StateTests/EIP150/Homestead/stQuadraticComplexityTest"}
|
||||
declare_test!{StateTests_EIP150_stRecursiveCreate, "StateTests/EIP150/Homestead/stRecursiveCreate"}
|
||||
declare_test!{StateTests_EIP150_stRefundTest, "StateTests/EIP150/Homestead/stRefundTest"}
|
||||
declare_test!{StateTests_EIP150_stSpecialTest, "StateTests/EIP150/Homestead/stSpecialTest"}
|
||||
declare_test!{StateTests_EIP150_stSystemOperationsTest, "StateTests/EIP150/Homestead/stSystemOperationsTest"}
|
||||
declare_test!{StateTests_EIP150_stTransactionTest, "StateTests/EIP150/Homestead/stTransactionTest"}
|
||||
declare_test!{StateTests_EIP150_stWalletTest, "StateTests/EIP150/Homestead/stWalletTest"}
|
||||
51
ethcore/src/json_tests/eip161_state.rs
Normal file
51
ethcore/src/json_tests/eip161_state.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
// 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 super::test_common::*;
|
||||
use tests::helpers::*;
|
||||
use super::state::json_chain_test;
|
||||
|
||||
fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
||||
json_chain_test(json_data, ChainEra::Eip161)
|
||||
}
|
||||
|
||||
declare_test!{StateTests_EIP158_stEIP158SpecificTest, "StateTests/EIP158/stEIP158SpecificTest"}
|
||||
declare_test!{StateTests_EIP158_stNonZeroCallsTest, "StateTests/EIP158/stNonZeroCallsTest"}
|
||||
declare_test!{StateTests_EIP158_stZeroCallsTest, "StateTests/EIP158/stZeroCallsTest"}
|
||||
|
||||
declare_test!{StateTests_EIP158_EIP150_stMemExpandingEIPCalls, "StateTests/EIP158/EIP150/stMemExpandingEIPCalls"}
|
||||
declare_test!{StateTests_EIP158_EIP150_stEIPSpecificTest, "StateTests/EIP158/EIP150/stEIPSpecificTest"}
|
||||
declare_test!{StateTests_EIP158_EIP150_stEIPsingleCodeGasPrices, "StateTests/EIP158/EIP150/stEIPsingleCodeGasPrices"}
|
||||
declare_test!{StateTests_EIP158_EIP150_stChangedTests, "StateTests/EIP158/EIP150/stChangedTests"}
|
||||
|
||||
declare_test!{StateTests_EIP158_Homestead_stBoundsTest, "StateTests/EIP158/Homestead/stBoundsTest"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stCallCodes, "StateTests/EIP158/Homestead/stCallCodes"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stCallCreateCallCodeTest, "StateTests/EIP158/Homestead/stCallCreateCallCodeTest"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stCallDelegateCodes, "StateTests/EIP158/Homestead/stCallDelegateCodes"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stCallDelegateCodesCallCode, "StateTests/EIP158/Homestead/stCallDelegateCodesCallCode"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stDelegatecallTest, "StateTests/EIP158/Homestead/stDelegatecallTest"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stHomeSteadSpecific, "StateTests/EIP158/Homestead/stHomeSteadSpecific"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stInitCodeTest, "StateTests/EIP158/Homestead/stInitCodeTest"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stLogTests, "StateTests/EIP158/Homestead/stLogTests"}
|
||||
declare_test!{heavy => StateTests_EIP158_Homestead_stMemoryTest, "StateTests/EIP158/Homestead/stMemoryTest"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stPreCompiledContracts, "StateTests/EIP158/Homestead/stPreCompiledContracts"}
|
||||
declare_test!{heavy => StateTests_EIP158_Homestead_stQuadraticComplexityTest, "StateTests/EIP158/Homestead/stQuadraticComplexityTest"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stRecursiveCreate, "StateTests/EIP158/Homestead/stRecursiveCreate"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stRefundTest, "StateTests/EIP158/Homestead/stRefundTest"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stSpecialTest, "StateTests/EIP158/Homestead/stSpecialTest"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stSystemOperationsTest, "StateTests/EIP158/Homestead/stSystemOperationsTest"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stTransactionTest, "StateTests/EIP158/Homestead/stTransactionTest"}
|
||||
declare_test!{StateTests_EIP158_Homestead_stWalletTest, "StateTests/EIP158/Homestead/stWalletTest"}
|
||||
@@ -91,10 +91,18 @@ impl<'a, T, V> Ext for TestExt<'a, T, V> where T: Tracer, V: VMTracer {
|
||||
self.ext.exists(address)
|
||||
}
|
||||
|
||||
fn exists_and_not_null(&self, address: &Address) -> bool {
|
||||
self.ext.exists_and_not_null(address)
|
||||
}
|
||||
|
||||
fn balance(&self, address: &Address) -> U256 {
|
||||
self.ext.balance(address)
|
||||
}
|
||||
|
||||
fn origin_balance(&self) -> U256 {
|
||||
self.ext.origin_balance()
|
||||
}
|
||||
|
||||
fn blockhash(&self, number: &U256) -> H256 {
|
||||
self.ext.blockhash(number)
|
||||
}
|
||||
|
||||
@@ -36,3 +36,6 @@ declare_test!{BlockchainTests_Homestead_bcUncleHeaderValiditiy, "BlockchainTests
|
||||
declare_test!{BlockchainTests_Homestead_bcUncleTest, "BlockchainTests/Homestead/bcUncleTest"}
|
||||
declare_test!{BlockchainTests_Homestead_bcValidBlockTest, "BlockchainTests/Homestead/bcValidBlockTest"}
|
||||
declare_test!{BlockchainTests_Homestead_bcWalletTest, "BlockchainTests/Homestead/bcWalletTest"}
|
||||
declare_test!{BlockchainTests_Homestead_bcShanghaiLove, "BlockchainTests/Homestead/bcShanghaiLove"}
|
||||
// declare_test!{BlockchainTests_Homestead_bcSuicideIssue, "BlockchainTests/Homestead/bcSuicideIssue"}
|
||||
declare_test!{BlockchainTests_Homestead_bcExploitTest, "BlockchainTests/Homestead/bcExploitTest"}
|
||||
|
||||
@@ -23,4 +23,6 @@ mod state;
|
||||
mod chain;
|
||||
mod homestead_state;
|
||||
mod homestead_chain;
|
||||
mod eip150_state;
|
||||
mod eip161_state;
|
||||
mod trie;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -33,17 +33,24 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
||||
Some(x) if x < 1_150_000 => &old_schedule,
|
||||
Some(_) => &new_schedule
|
||||
};
|
||||
let allow_network_id_of_one = number.map_or(false, |n| n > 2600000);
|
||||
|
||||
let rlp: Vec<u8> = test.rlp.into();
|
||||
let res = UntrustedRlp::new(&rlp)
|
||||
.as_val()
|
||||
.map_err(From::from)
|
||||
.and_then(|t: SignedTransaction| t.validate(schedule, schedule.have_delegate_call));
|
||||
.and_then(|t: SignedTransaction| t.validate(schedule, schedule.have_delegate_call, allow_network_id_of_one));
|
||||
|
||||
fail_unless(test.transaction.is_none() == res.is_err());
|
||||
if let (Some(tx), Some(sender)) = (test.transaction, test.sender) {
|
||||
let t = res.unwrap();
|
||||
fail_unless(t.sender().unwrap() == sender.into());
|
||||
let is_acceptable_network_id = match t.network_id() {
|
||||
None => true,
|
||||
Some(1) if allow_network_id_of_one => true,
|
||||
_ => false,
|
||||
};
|
||||
fail_unless(is_acceptable_network_id);
|
||||
let data: Vec<u8> = tx.data.into();
|
||||
fail_unless(t.data == data);
|
||||
fail_unless(t.gas_price == tx.gas_price.into());
|
||||
|
||||
@@ -21,7 +21,7 @@ use util::*;
|
||||
use util::using_queue::{UsingQueue, GetAction};
|
||||
use account_provider::AccountProvider;
|
||||
use views::{BlockView, HeaderView};
|
||||
use state::State;
|
||||
use state::{State, CleanupMode};
|
||||
use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockID, CallAnalytics};
|
||||
use executive::contract_address;
|
||||
use block::{ClosedBlock, IsBlock, Block};
|
||||
@@ -278,6 +278,7 @@ impl Miner {
|
||||
trace!(target: "miner", "done recalibration.");
|
||||
}
|
||||
|
||||
let _timer = PerfTimer::new("prepare_block");
|
||||
let (transactions, mut open_block, original_work_hash) = {
|
||||
let transactions = {self.transaction_queue.lock().top_transactions()};
|
||||
let mut sealing_work = self.sealing_work.lock();
|
||||
@@ -421,8 +422,8 @@ impl Miner {
|
||||
let mut queue = self.transaction_queue.lock();
|
||||
queue.set_gas_limit(gas_limit);
|
||||
if let GasLimit::Auto = self.options.tx_queue_gas_limit {
|
||||
// Set total tx queue gas limit to be 2x the block gas limit.
|
||||
queue.set_total_gas_limit(gas_limit << 1);
|
||||
// Set total tx queue gas limit to be 20x the block gas limit.
|
||||
queue.set_total_gas_limit(gas_limit * 20.into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -543,7 +544,7 @@ impl MinerService for Miner {
|
||||
let needed_balance = t.value + t.gas * t.gas_price;
|
||||
if balance < needed_balance {
|
||||
// give the sender a sufficient balance
|
||||
state.add_balance(&sender, &(needed_balance - balance));
|
||||
state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty);
|
||||
}
|
||||
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
|
||||
let mut ret = try!(Executive::new(&mut state, &env_info, &*self.engine, chain.vm_factory()).transact(t, options));
|
||||
@@ -1068,7 +1069,7 @@ mod tests {
|
||||
gas: U256::from(100_000),
|
||||
gas_price: U256::zero(),
|
||||
nonce: U256::zero(),
|
||||
}.sign(keypair.secret())
|
||||
}.sign(keypair.secret(), None)
|
||||
};
|
||||
let best_block = 0;
|
||||
// when
|
||||
@@ -1098,7 +1099,7 @@ mod tests {
|
||||
gas: U256::from(100_000),
|
||||
gas_price: U256::zero(),
|
||||
nonce: U256::zero(),
|
||||
}.sign(keypair.secret())
|
||||
}.sign(keypair.secret(), None)
|
||||
};
|
||||
let best_block = 10;
|
||||
// when
|
||||
@@ -1126,7 +1127,7 @@ mod tests {
|
||||
gas: U256::from(100_000),
|
||||
gas_price: U256::zero(),
|
||||
nonce: U256::zero(),
|
||||
}.sign(keypair.secret())
|
||||
}.sign(keypair.secret(), None)
|
||||
};
|
||||
let best_block = 0;
|
||||
// when
|
||||
|
||||
@@ -21,55 +21,6 @@
|
||||
//! transaction's nonce and next nonce expected from this sender). If nonces are equal transaction's gas price is used
|
||||
//! for comparison (higher gas price = higher priority).
|
||||
//!
|
||||
//! # Usage Example
|
||||
//!
|
||||
//! ```rust
|
||||
//! extern crate ethcore_util as util;
|
||||
//! extern crate ethcore;
|
||||
//! extern crate rustc_serialize;
|
||||
//!
|
||||
//! use util::crypto::KeyPair;
|
||||
//! use util::hash::Address;
|
||||
//! use util::numbers::{Uint, U256};
|
||||
//! use ethcore::miner::{TransactionQueue, AccountDetails, TransactionOrigin};
|
||||
//! use ethcore::transaction::*;
|
||||
//! use rustc_serialize::hex::FromHex;
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let key = KeyPair::create().unwrap();
|
||||
//! let t1 = Transaction { action: Action::Create, value: U256::from(100), data: "3331600055".from_hex().unwrap(),
|
||||
//! gas: U256::from(100_000), gas_price: U256::one(), nonce: U256::from(10) };
|
||||
//! let t2 = Transaction { action: Action::Create, value: U256::from(100), data: "3331600055".from_hex().unwrap(),
|
||||
//! gas: U256::from(100_000), gas_price: U256::one(), nonce: U256::from(11) };
|
||||
//!
|
||||
//! let st1 = t1.sign(&key.secret());
|
||||
//! let st2 = t2.sign(&key.secret());
|
||||
//! let default_nonce = |_a: &Address| AccountDetails {
|
||||
//! nonce: U256::from(10),
|
||||
//! balance: U256::from(1_000_000),
|
||||
//! };
|
||||
//!
|
||||
//! let mut txq = TransactionQueue::default();
|
||||
//! txq.add(st2.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
//! txq.add(st1.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
//!
|
||||
//! // Check status
|
||||
//! assert_eq!(txq.status().pending, 2);
|
||||
//! // Check top transactions
|
||||
//! let top = txq.top_transactions();
|
||||
//! assert_eq!(top.len(), 2);
|
||||
//! assert_eq!(top[0], st1);
|
||||
//! assert_eq!(top[1], st2);
|
||||
//!
|
||||
//! // And when transaction is removed (but nonce haven't changed)
|
||||
//! // it will move subsequent transactions to future
|
||||
//! txq.remove_invalid(&st1.hash(), &default_nonce);
|
||||
//! assert_eq!(txq.status().pending, 0);
|
||||
//! assert_eq!(txq.status().future, 1);
|
||||
//! assert_eq!(txq.top_transactions().len(), 0);
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! # Maintaing valid state
|
||||
//!
|
||||
//! 1. Whenever transaction is imported to queue (to queue) all other transactions from this sender are revalidated in current. It means that they are moved to future and back again (height recalculation & gap filling).
|
||||
@@ -305,14 +256,14 @@ impl TransactionSet {
|
||||
let to_drop : Vec<(Address, U256)> = {
|
||||
self.by_priority
|
||||
.iter()
|
||||
.skip_while(|order| {
|
||||
.filter(|order| {
|
||||
count = count + 1;
|
||||
let r = gas.overflowing_add(order.gas);
|
||||
if r.1 { return false }
|
||||
gas = r.0;
|
||||
// Own and retracted transactions are allowed to go above the gas limit, bot not above the count limit.
|
||||
(gas <= self.gas_limit || order.origin == TransactionOrigin::Local || order.origin == TransactionOrigin::RetractedBlock) &&
|
||||
count <= self.limit
|
||||
// Own and retracted transactions are allowed to go above all limits.
|
||||
order.origin != TransactionOrigin::Local && order.origin != TransactionOrigin::RetractedBlock &&
|
||||
(gas > self.gas_limit || count > self.limit)
|
||||
})
|
||||
.map(|order| by_hash.get(&order.hash)
|
||||
.expect("All transactions in `self.by_priority` and `self.by_address` are kept in sync with `by_hash`."))
|
||||
@@ -324,6 +275,7 @@ impl TransactionSet {
|
||||
.fold(HashMap::new(), |mut removed, (sender, nonce)| {
|
||||
let order = self.drop(&sender, &nonce)
|
||||
.expect("Transaction has just been found in `by_priority`; so it is in `by_address` also.");
|
||||
trace!(target: "txqueue", "Dropped out of limit transaction: {:?}", order.hash);
|
||||
|
||||
by_hash.remove(&order.hash)
|
||||
.expect("Hash found in `by_priorty` matches the one dropped; so it is included in `by_hash`");
|
||||
@@ -647,6 +599,8 @@ impl TransactionQueue {
|
||||
let nonce = transaction.nonce();
|
||||
let current_nonce = fetch_account(&sender).nonce;
|
||||
|
||||
trace!(target: "txqueue", "Removing invalid transaction: {:?}", transaction.hash());
|
||||
|
||||
// Remove from future
|
||||
let order = self.future.drop(&sender, &nonce);
|
||||
if order.is_some() {
|
||||
@@ -920,12 +874,14 @@ impl TransactionQueue {
|
||||
let old_fee = old.gas_price;
|
||||
let new_fee = order.gas_price;
|
||||
if old_fee.cmp(&new_fee) == Ordering::Greater {
|
||||
trace!(target: "txqueue", "Didn't insert transaction because gas price was too low: {:?} ({:?} stays in the queue)", order.hash, old.hash);
|
||||
// Put back old transaction since it has greater priority (higher gas_price)
|
||||
set.insert(address, nonce, old);
|
||||
// and remove new one
|
||||
by_hash.remove(&order.hash).expect("The hash has been just inserted and no other line is altering `by_hash`.");
|
||||
false
|
||||
} else {
|
||||
trace!(target: "txqueue", "Replaced transaction: {:?} with transaction with higher gas price: {:?}", old.hash, order.hash);
|
||||
// Make sure we remove old transaction entirely
|
||||
by_hash.remove(&old.hash).expect("The hash is coming from `future` so it has to be in `by_hash`.");
|
||||
true
|
||||
@@ -1001,7 +957,7 @@ mod test {
|
||||
|
||||
fn new_tx_with_gas(gas: U256, gas_price: U256) -> SignedTransaction {
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
new_unsigned_tx_with_gas(default_nonce_val(), gas, gas_price).sign(keypair.secret())
|
||||
new_unsigned_tx_with_gas(default_nonce_val(), gas, gas_price).sign(keypair.secret(), None)
|
||||
}
|
||||
|
||||
fn new_tx() -> SignedTransaction {
|
||||
@@ -1024,7 +980,7 @@ mod test {
|
||||
let mut tx2 = new_unsigned_tx(nonce);
|
||||
tx2.gas_price = 2.into();
|
||||
|
||||
(tx.sign(secret), tx2.sign(secret))
|
||||
(tx.sign(secret, None), tx2.sign(secret, None))
|
||||
}
|
||||
|
||||
fn new_txs(second_nonce: U256) -> (SignedTransaction, SignedTransaction) {
|
||||
@@ -1039,8 +995,7 @@ mod test {
|
||||
tx.gas_price = tx.gas_price + gas_price;
|
||||
let mut tx2 = new_unsigned_tx(nonce + 1.into());
|
||||
tx2.gas_price = tx2.gas_price + gas_price;
|
||||
|
||||
(tx.sign(secret), tx2.sign(secret))
|
||||
(tx.sign(secret, None), tx2.sign(secret, None))
|
||||
}
|
||||
|
||||
fn new_txs_with_gas_price_diff(second_nonce: U256, gas_price: U256) -> (SignedTransaction, SignedTransaction) {
|
||||
@@ -1051,7 +1006,7 @@ mod test {
|
||||
let mut tx2 = new_unsigned_tx(nonce + second_nonce);
|
||||
tx2.gas_price = tx2.gas_price + gas_price;
|
||||
|
||||
(tx.sign(secret), tx2.sign(secret))
|
||||
(tx.sign(secret, None), tx2.sign(secret, None))
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1613,9 +1568,9 @@ mod test {
|
||||
let mut txq = TransactionQueue::default();
|
||||
let kp = KeyPair::create().unwrap();
|
||||
let secret = kp.secret();
|
||||
let tx = new_unsigned_tx(U256::from(123)).sign(secret);
|
||||
let tx1 = new_unsigned_tx(U256::from(124)).sign(secret);
|
||||
let tx2 = new_unsigned_tx(U256::from(125)).sign(secret);
|
||||
let tx = new_unsigned_tx(U256::from(123)).sign(secret, None);
|
||||
let tx1 = new_unsigned_tx(U256::from(124)).sign(secret, None);
|
||||
let tx2 = new_unsigned_tx(U256::from(125)).sign(secret, None);
|
||||
|
||||
txq.add(tx, &default_nonce, TransactionOrigin::External).unwrap();
|
||||
assert_eq!(txq.status().pending, 1);
|
||||
@@ -1773,8 +1728,12 @@ mod test {
|
||||
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 100, default_gas_val() * U256::from(2), !U256::zero());
|
||||
let (tx1, tx2) = new_txs_with_gas_price_diff(U256::from(1), U256::from(1));
|
||||
let (tx3, tx4) = new_txs_with_gas_price_diff(U256::from(1), U256::from(2));
|
||||
let (tx5, tx6) = new_txs_with_gas_price_diff(U256::from(1), U256::from(2));
|
||||
txq.add(tx1.clone(), &default_nonce, TransactionOrigin::Local).unwrap();
|
||||
txq.add(tx2.clone(), &default_nonce, TransactionOrigin::Local).unwrap();
|
||||
txq.add(tx5.clone(), &default_nonce, TransactionOrigin::External).unwrap();
|
||||
// Not accepted because of limit
|
||||
txq.add(tx6.clone(), &default_nonce, TransactionOrigin::External).unwrap_err();
|
||||
txq.add(tx3.clone(), &default_nonce, TransactionOrigin::Local).unwrap();
|
||||
txq.add(tx4.clone(), &default_nonce, TransactionOrigin::Local).unwrap();
|
||||
assert_eq!(txq.status().pending, 4);
|
||||
@@ -1867,11 +1826,11 @@ mod test {
|
||||
// given
|
||||
let mut txq = TransactionQueue::default();
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
let tx = new_unsigned_tx(U256::from(123)).sign(keypair.secret());
|
||||
let tx = new_unsigned_tx(U256::from(123)).sign(keypair.secret(), None);
|
||||
let tx2 = {
|
||||
let mut tx2 = (*tx).clone();
|
||||
tx2.gas_price = U256::from(200);
|
||||
tx2.sign(keypair.secret())
|
||||
tx2.sign(keypair.secret(), None)
|
||||
};
|
||||
|
||||
// when
|
||||
@@ -1890,16 +1849,16 @@ mod test {
|
||||
// given
|
||||
let mut txq = TransactionQueue::default();
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
let tx0 = new_unsigned_tx(U256::from(123)).sign(keypair.secret());
|
||||
let tx0 = new_unsigned_tx(U256::from(123)).sign(keypair.secret(), None);
|
||||
let tx1 = {
|
||||
let mut tx1 = (*tx0).clone();
|
||||
tx1.nonce = U256::from(124);
|
||||
tx1.sign(keypair.secret())
|
||||
tx1.sign(keypair.secret(), None)
|
||||
};
|
||||
let tx2 = {
|
||||
let mut tx2 = (*tx1).clone();
|
||||
tx2.gas_price = U256::from(200);
|
||||
tx2.sign(keypair.secret())
|
||||
tx2.sign(keypair.secret(), None)
|
||||
};
|
||||
|
||||
// when
|
||||
@@ -2052,7 +2011,7 @@ mod test {
|
||||
let tx3 = new_unsigned_tx(nonce + 2.into());
|
||||
|
||||
|
||||
(tx.sign(secret), tx2.sign(secret), tx2_2.sign(secret), tx3.sign(secret))
|
||||
(tx.sign(secret, None), tx2.sign(secret, None), tx2_2.sign(secret, None), tx3.sign(secret, None))
|
||||
};
|
||||
let sender = tx1.sender().unwrap();
|
||||
txq.add(tx1, &default_nonce, TransactionOrigin::Local).unwrap();
|
||||
|
||||
@@ -27,7 +27,7 @@ use ids::BlockID;
|
||||
use views::BlockView;
|
||||
use super::state_db::StateDB;
|
||||
|
||||
use util::{Bytes, Hashable, HashDB, snappy, TrieDB, TrieDBMut, TrieMut, BytesConvertable};
|
||||
use util::{Bytes, Hashable, HashDB, snappy, TrieDB, TrieDBMut, TrieMut, BytesConvertable, U256, Uint};
|
||||
use util::Mutex;
|
||||
use util::hash::{FixedHash, H256};
|
||||
use util::journaldb::{self, Algorithm, JournalDB};
|
||||
@@ -39,6 +39,8 @@ use self::account::Account;
|
||||
use self::block::AbridgedBlock;
|
||||
use self::io::SnapshotWriter;
|
||||
|
||||
use super::account::Account as StateAccount;
|
||||
|
||||
use crossbeam::{scope, ScopedJoinHandle};
|
||||
use rand::{Rng, OsRng};
|
||||
|
||||
@@ -417,6 +419,7 @@ impl StateRebuilder {
|
||||
/// Feed an uncompressed state chunk into the rebuilder.
|
||||
pub fn feed(&mut self, chunk: &[u8]) -> Result<(), ::error::Error> {
|
||||
let rlp = UntrustedRlp::new(chunk);
|
||||
let empty_rlp = StateAccount::new_basic(U256::zero(), U256::zero()).rlp();
|
||||
let account_fat_rlps: Vec<_> = rlp.iter().map(|r| r.as_raw()).collect();
|
||||
let mut pairs = Vec::with_capacity(rlp.item_count());
|
||||
let backing = self.db.backing().clone();
|
||||
@@ -464,7 +467,9 @@ impl StateRebuilder {
|
||||
};
|
||||
|
||||
for (hash, thin_rlp) in pairs {
|
||||
bloom.set(hash.as_slice());
|
||||
if &thin_rlp[..] != &empty_rlp[..] {
|
||||
bloom.set(hash.as_slice());
|
||||
}
|
||||
try!(account_trie.insert(&hash, &thin_rlp));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,9 @@ pub struct CommonParams {
|
||||
/// Maximum size of extra data.
|
||||
pub maximum_extra_data_size: usize,
|
||||
/// Network id.
|
||||
pub network_id: U256,
|
||||
pub network_id: usize,
|
||||
/// Main subprotocol name.
|
||||
pub subprotocol_name: String,
|
||||
/// Minimum gas limit.
|
||||
pub min_gas_limit: U256,
|
||||
/// Fork block to check.
|
||||
@@ -50,6 +52,7 @@ impl From<ethjson::spec::Params> for CommonParams {
|
||||
account_start_nonce: p.account_start_nonce.into(),
|
||||
maximum_extra_data_size: p.maximum_extra_data_size.into(),
|
||||
network_id: p.network_id.into(),
|
||||
subprotocol_name: p.subprotocol_name.unwrap_or_else(|| "eth".to_owned()),
|
||||
min_gas_limit: p.min_gas_limit.into(),
|
||||
fork_block: if let (Some(n), Some(h)) = (p.fork_block, p.fork_hash) { Some((n.into(), h.into())) } else { None },
|
||||
}
|
||||
@@ -155,7 +158,7 @@ impl Spec {
|
||||
pub fn nodes(&self) -> &[String] { &self.nodes }
|
||||
|
||||
/// Get the configured Network ID.
|
||||
pub fn network_id(&self) -> U256 { self.params.network_id }
|
||||
pub fn network_id(&self) -> usize { self.params.network_id }
|
||||
|
||||
/// Get the configured network fork block.
|
||||
pub fn fork_block(&self) -> Option<(BlockNumber, H256)> { self.params.fork_block }
|
||||
@@ -240,7 +243,7 @@ impl Spec {
|
||||
}
|
||||
}
|
||||
for (address, account) in self.genesis_state.get().iter() {
|
||||
db.note_account_bloom(address);
|
||||
db.note_non_null_account(address);
|
||||
account.insert_additional(&mut AccountDBMut::new(db.as_hashdb_mut(), address));
|
||||
}
|
||||
assert!(db.as_hashdb().contains(&self.state_root()));
|
||||
|
||||
@@ -191,6 +191,13 @@ enum RequireCache {
|
||||
Code,
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum CleanupMode<'a> {
|
||||
ForceCreate,
|
||||
NoEmpty,
|
||||
KillEmpty(&'a mut HashSet<Address>),
|
||||
}
|
||||
|
||||
const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \
|
||||
Therefore creating a SecTrieDB with this state's root will not fail.";
|
||||
|
||||
@@ -234,15 +241,15 @@ impl State {
|
||||
|
||||
/// Create a recoverable snaphot of this state.
|
||||
pub fn snapshot(&mut self) {
|
||||
self.snapshots.borrow_mut().push(HashMap::new());
|
||||
self.snapshots.get_mut().push(HashMap::new());
|
||||
}
|
||||
|
||||
/// Merge last snapshot with previous.
|
||||
pub fn discard_snapshot(&mut self) {
|
||||
// merge with previous snapshot
|
||||
let last = self.snapshots.borrow_mut().pop();
|
||||
let last = self.snapshots.get_mut().pop();
|
||||
if let Some(mut snapshot) = last {
|
||||
if let Some(ref mut prev) = self.snapshots.borrow_mut().last_mut() {
|
||||
if let Some(ref mut prev) = self.snapshots.get_mut().last_mut() {
|
||||
if prev.is_empty() {
|
||||
**prev = snapshot;
|
||||
} else {
|
||||
@@ -256,11 +263,11 @@ impl State {
|
||||
|
||||
/// Revert to the last snapshot and discard it.
|
||||
pub fn revert_to_snapshot(&mut self) {
|
||||
if let Some(mut snapshot) = self.snapshots.borrow_mut().pop() {
|
||||
if let Some(mut snapshot) = self.snapshots.get_mut().pop() {
|
||||
for (k, v) in snapshot.drain() {
|
||||
match v {
|
||||
Some(v) => {
|
||||
match self.cache.borrow_mut().entry(k) {
|
||||
match self.cache.get_mut().entry(k) {
|
||||
Entry::Occupied(mut e) => {
|
||||
// Merge snapshotted changes back into the main account
|
||||
// storage preserving the cache.
|
||||
@@ -272,7 +279,7 @@ impl State {
|
||||
}
|
||||
},
|
||||
None => {
|
||||
match self.cache.borrow_mut().entry(k) {
|
||||
match self.cache.get_mut().entry(k) {
|
||||
Entry::Occupied(e) => {
|
||||
if e.get().is_dirty() {
|
||||
e.remove();
|
||||
@@ -324,8 +331,8 @@ impl State {
|
||||
|
||||
/// Create a new contract at address `contract`. If there is already an account at the address
|
||||
/// it will have its code reset, ready for `init_code()`.
|
||||
pub fn new_contract(&mut self, contract: &Address, balance: U256) {
|
||||
self.insert_cache(contract, AccountEntry::new_dirty(Some(Account::new_contract(balance, self.account_start_nonce))));
|
||||
pub fn new_contract(&mut self, contract: &Address, balance: U256, nonce_offset: U256) {
|
||||
self.insert_cache(contract, AccountEntry::new_dirty(Some(Account::new_contract(balance, self.account_start_nonce + nonce_offset))));
|
||||
}
|
||||
|
||||
/// Remove an existing account.
|
||||
@@ -335,18 +342,25 @@ impl State {
|
||||
|
||||
/// Determine whether an account exists.
|
||||
pub fn exists(&self, a: &Address) -> bool {
|
||||
self.ensure_cached(a, RequireCache::None, |a| a.is_some())
|
||||
// Bloom filter does not contain empty accounts, so it is important here to
|
||||
// check if account exists in the database directly before EIP-161 is in effect.
|
||||
self.ensure_cached(a, RequireCache::None, false, |a| a.is_some())
|
||||
}
|
||||
|
||||
/// Determine whether an account exists and if not empty.
|
||||
pub fn exists_and_not_null(&self, a: &Address) -> bool {
|
||||
self.ensure_cached(a, RequireCache::None, false, |a| a.map_or(false, |a| !a.is_null()))
|
||||
}
|
||||
|
||||
/// Get the balance of account `a`.
|
||||
pub fn balance(&self, a: &Address) -> U256 {
|
||||
self.ensure_cached(a, RequireCache::None,
|
||||
self.ensure_cached(a, RequireCache::None, true,
|
||||
|a| a.as_ref().map_or(U256::zero(), |account| *account.balance()))
|
||||
}
|
||||
|
||||
/// Get the nonce of account `a`.
|
||||
pub fn nonce(&self, a: &Address) -> U256 {
|
||||
self.ensure_cached(a, RequireCache::None,
|
||||
self.ensure_cached(a, RequireCache::None, true,
|
||||
|a| a.as_ref().map_or(self.account_start_nonce, |account| *account.nonce()))
|
||||
}
|
||||
|
||||
@@ -389,8 +403,10 @@ impl State {
|
||||
}
|
||||
}
|
||||
}
|
||||
// account is not found in the global cache, get from the DB and insert into local
|
||||
if !self.db.check_account_bloom(address) { return H256::zero() }
|
||||
|
||||
// check bloom before any requests to trie
|
||||
if !self.db.check_non_null_bloom(address) { return H256::zero() }
|
||||
|
||||
let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
|
||||
let maybe_acc = match db.get(address) {
|
||||
Ok(acc) => acc.map(Account::from_rlp),
|
||||
@@ -403,26 +419,34 @@ impl State {
|
||||
|
||||
/// Get accounts' code.
|
||||
pub fn code(&self, a: &Address) -> Option<Arc<Bytes>> {
|
||||
self.ensure_cached(a, RequireCache::Code,
|
||||
self.ensure_cached(a, RequireCache::Code, true,
|
||||
|a| a.as_ref().map_or(None, |a| a.code().clone()))
|
||||
}
|
||||
|
||||
pub fn code_hash(&self, a: &Address) -> H256 {
|
||||
self.ensure_cached(a, RequireCache::None,
|
||||
self.ensure_cached(a, RequireCache::None, true,
|
||||
|a| a.as_ref().map_or(SHA3_EMPTY, |a| a.code_hash()))
|
||||
}
|
||||
|
||||
/// Get accounts' code size.
|
||||
pub fn code_size(&self, a: &Address) -> Option<u64> {
|
||||
self.ensure_cached(a, RequireCache::CodeSize,
|
||||
self.ensure_cached(a, RequireCache::CodeSize, true,
|
||||
|a| a.as_ref().and_then(|a| a.code_size()))
|
||||
}
|
||||
|
||||
/// Add `incr` to the balance of account `a`.
|
||||
pub fn add_balance(&mut self, a: &Address, incr: &U256) {
|
||||
pub fn add_balance(&mut self, a: &Address, incr: &U256, cleanup_mode: CleanupMode) {
|
||||
trace!(target: "state", "add_balance({}, {}): {}", a, incr, self.balance(a));
|
||||
if !incr.is_zero() || !self.exists(a) {
|
||||
let is_value_transfer = !incr.is_zero();
|
||||
if is_value_transfer || (cleanup_mode == CleanupMode::ForceCreate && !self.exists(a)) {
|
||||
self.require(a, false).add_balance(incr);
|
||||
} else {
|
||||
match cleanup_mode {
|
||||
CleanupMode::KillEmpty(set) => if !is_value_transfer && self.exists(a) && !self.exists_and_not_null(a) {
|
||||
set.insert(a.clone());
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -435,9 +459,9 @@ impl State {
|
||||
}
|
||||
|
||||
/// Subtracts `by` from the balance of `from` and adds it to that of `to`.
|
||||
pub fn transfer_balance(&mut self, from: &Address, to: &Address, by: &U256) {
|
||||
pub fn transfer_balance(&mut self, from: &Address, to: &Address, by: &U256, cleanup_mode: CleanupMode) {
|
||||
self.sub_balance(from, by);
|
||||
self.add_balance(to, by);
|
||||
self.add_balance(to, by, cleanup_mode);
|
||||
}
|
||||
|
||||
/// Increment the nonce of account `a` by 1.
|
||||
@@ -490,14 +514,16 @@ impl State {
|
||||
) -> Result<(), Error> {
|
||||
// first, commit the sub trees.
|
||||
for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) {
|
||||
match a.account {
|
||||
Some(ref mut account) => {
|
||||
db.note_account_bloom(&address);
|
||||
let mut account_db = AccountDBMut::from_hash(db.as_hashdb_mut(), account.address_hash(address));
|
||||
if let Some(ref mut account) = a.account {
|
||||
let addr_hash = account.address_hash(address);
|
||||
{
|
||||
let mut account_db = AccountDBMut::from_hash(db.as_hashdb_mut(), addr_hash);
|
||||
account.commit_storage(trie_factory, &mut account_db);
|
||||
account.commit_code(&mut account_db);
|
||||
}
|
||||
_ => {}
|
||||
if !account.is_empty() {
|
||||
db.note_non_null_account(address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -545,7 +571,6 @@ impl State {
|
||||
pub fn populate_from(&mut self, accounts: PodState) {
|
||||
assert!(self.snapshots.borrow().is_empty());
|
||||
for (add, acc) in accounts.drain().into_iter() {
|
||||
self.db.note_account_bloom(&add);
|
||||
self.cache.borrow_mut().insert(add, AccountEntry::new_dirty(Some(Account::from_pod(acc))));
|
||||
}
|
||||
}
|
||||
@@ -564,14 +589,14 @@ impl State {
|
||||
}
|
||||
|
||||
fn query_pod(&mut self, query: &PodState) {
|
||||
for (ref address, ref pod_account) in query.get() {
|
||||
self.ensure_cached(address, RequireCache::Code, |a| {
|
||||
if a.is_some() {
|
||||
for key in pod_account.storage.keys() {
|
||||
self.storage_at(address, key);
|
||||
}
|
||||
}
|
||||
});
|
||||
for (address, pod_account) in query.get().into_iter()
|
||||
.filter(|&(ref a, _)| self.ensure_cached(a, RequireCache::Code, true, |a| a.is_some()))
|
||||
{
|
||||
// needs to be split into two parts for the refcell code here
|
||||
// to work.
|
||||
for key in pod_account.storage.keys() {
|
||||
self.storage_at(address, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -601,7 +626,7 @@ impl State {
|
||||
/// Check caches for required data
|
||||
/// First searches for account in the local, then the shared cache.
|
||||
/// Populates local cache if nothing found.
|
||||
fn ensure_cached<F, U>(&self, a: &Address, require: RequireCache, f: F) -> U
|
||||
fn ensure_cached<F, U>(&self, a: &Address, require: RequireCache, check_bloom: bool, f: F) -> U
|
||||
where F: Fn(Option<&Account>) -> U {
|
||||
// check local cache first
|
||||
if let Some(ref mut maybe_acc) = self.cache.borrow_mut().get_mut(a) {
|
||||
@@ -621,8 +646,10 @@ impl State {
|
||||
match result {
|
||||
Some(r) => r,
|
||||
None => {
|
||||
// first check bloom if it is not in database for sure
|
||||
if check_bloom && !self.db.check_non_null_bloom(a) { return f(None); }
|
||||
|
||||
// not found in the global cache, get from the DB and insert into local
|
||||
if !self.db.check_account_bloom(a) { return f(None); }
|
||||
let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
|
||||
let mut maybe_acc = match db.get(a) {
|
||||
Ok(acc) => acc.map(Account::from_rlp),
|
||||
@@ -653,14 +680,13 @@ impl State {
|
||||
match self.db.get_cached_account(a) {
|
||||
Some(acc) => self.insert_cache(a, AccountEntry::new_clean_cached(acc)),
|
||||
None => {
|
||||
let maybe_acc = if self.db.check_account_bloom(a) {
|
||||
let maybe_acc = if self.db.check_non_null_bloom(a) {
|
||||
let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
|
||||
let maybe_acc = match db.get(a) {
|
||||
Ok(Some(acc)) => AccountEntry::new_clean(Some(Account::from_rlp(acc))),
|
||||
match db.get(a) {
|
||||
Ok(Some(acc)) => AccountEntry::new_clean(Some(Account::from_rlp(&acc))),
|
||||
Ok(None) => AccountEntry::new_clean(None),
|
||||
Err(e) => panic!("Potential DB corruption encountered: {}", e),
|
||||
};
|
||||
maybe_acc
|
||||
}
|
||||
}
|
||||
else {
|
||||
AccountEntry::new_clean(None)
|
||||
@@ -758,9 +784,9 @@ fn should_apply_create_transaction() {
|
||||
action: Action::Create,
|
||||
value: 100.into(),
|
||||
data: FromHex::from_hex("601080600c6000396000f3006000355415600957005b60203560003555").unwrap(),
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
@@ -819,9 +845,9 @@ fn should_trace_failed_create_transaction() {
|
||||
action: Action::Create,
|
||||
value: 100.into(),
|
||||
data: FromHex::from_hex("5b600056").unwrap(),
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
@@ -857,10 +883,10 @@ fn should_trace_call_transaction() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("6000").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
@@ -901,9 +927,9 @@ fn should_trace_basic_call_transaction() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
@@ -944,7 +970,7 @@ fn should_trace_call_transaction_to_builtin() {
|
||||
action: Action::Call(0x1.into()),
|
||||
value: 0.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap();
|
||||
@@ -987,7 +1013,7 @@ fn should_not_trace_subcall_transaction_to_builtin() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 0.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060006001610be0f1").unwrap());
|
||||
let vm_factory = Default::default();
|
||||
@@ -1031,7 +1057,7 @@ fn should_not_trace_callcode() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 0.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b611000f2").unwrap());
|
||||
state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap());
|
||||
@@ -1094,7 +1120,7 @@ fn should_not_trace_delegatecall() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 0.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("6000600060006000600b618000f4").unwrap());
|
||||
state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap());
|
||||
@@ -1154,10 +1180,10 @@ fn should_trace_failed_call_transaction() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("5b600056").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
@@ -1195,11 +1221,11 @@ fn should_trace_call_with_subcall_transaction() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
|
||||
state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
|
||||
@@ -1256,10 +1282,10 @@ fn should_trace_call_with_basic_subcall_transaction() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006045600b6000f1").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
@@ -1312,10 +1338,10 @@ fn should_not_trace_call_with_invalid_basic_subcall_transaction() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()); // not enough funds.
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
@@ -1356,11 +1382,11 @@ fn should_trace_failed_subcall_transaction() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],//600480600b6000396000f35b600056
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
|
||||
state.init_code(&0xb.into(), FromHex::from_hex("5b600056").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
@@ -1413,12 +1439,12 @@ fn should_trace_call_with_subcall_with_subcall_transaction() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
|
||||
state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap());
|
||||
state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
@@ -1489,12 +1515,12 @@ fn should_trace_failed_subcall_with_subcall_transaction() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],//600480600b6000396000f35b600056
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
|
||||
state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap());
|
||||
state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
|
||||
@@ -1563,11 +1589,11 @@ fn should_trace_suicide() {
|
||||
action: Action::Call(0xa.into()),
|
||||
value: 100.into(),
|
||||
data: vec![],
|
||||
}.sign(&"".sha3());
|
||||
}.sign(&"".sha3(), None);
|
||||
|
||||
state.init_code(&0xa.into(), FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap());
|
||||
state.add_balance(&0xa.into(), &50.into());
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &100.into());
|
||||
state.add_balance(&0xa.into(), &50.into(), CleanupMode::NoEmpty);
|
||||
state.add_balance(t.sender().as_ref().unwrap(), &100.into(), CleanupMode::NoEmpty);
|
||||
let vm_factory = Default::default();
|
||||
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
|
||||
let expected_trace = vec![FlatTrace {
|
||||
@@ -1639,7 +1665,7 @@ fn get_from_database() {
|
||||
let (root, db) = {
|
||||
let mut state = get_temp_state_in(temp.as_path());
|
||||
state.inc_nonce(&a);
|
||||
state.add_balance(&a, &U256::from(69u64));
|
||||
state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty);
|
||||
state.commit().unwrap();
|
||||
assert_eq!(state.balance(&a), U256::from(69u64));
|
||||
state.drop()
|
||||
@@ -1656,14 +1682,49 @@ fn remove() {
|
||||
let mut state_result = get_temp_state();
|
||||
let mut state = state_result.reference_mut();
|
||||
assert_eq!(state.exists(&a), false);
|
||||
assert_eq!(state.exists_and_not_null(&a), false);
|
||||
state.inc_nonce(&a);
|
||||
assert_eq!(state.exists(&a), true);
|
||||
assert_eq!(state.exists_and_not_null(&a), true);
|
||||
assert_eq!(state.nonce(&a), U256::from(1u64));
|
||||
state.kill_account(&a);
|
||||
assert_eq!(state.exists(&a), false);
|
||||
assert_eq!(state.exists_and_not_null(&a), false);
|
||||
assert_eq!(state.nonce(&a), U256::from(0u64));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_account_is_not_created() {
|
||||
let a = Address::zero();
|
||||
let path = RandomTempPath::new();
|
||||
let db = get_temp_state_db_in(path.as_path());
|
||||
let (root, db) = {
|
||||
let mut state = State::new(db, U256::from(0), Default::default());
|
||||
state.add_balance(&a, &U256::default(), CleanupMode::NoEmpty); // create an empty account
|
||||
state.commit().unwrap();
|
||||
state.drop()
|
||||
};
|
||||
let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap();
|
||||
assert!(!state.exists(&a));
|
||||
assert!(!state.exists_and_not_null(&a));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_account_exists_when_creation_forced() {
|
||||
let a = Address::zero();
|
||||
let path = RandomTempPath::new();
|
||||
let db = get_temp_state_db_in(path.as_path());
|
||||
let (root, db) = {
|
||||
let mut state = State::new(db, U256::from(0), Default::default());
|
||||
state.add_balance(&a, &U256::default(), CleanupMode::ForceCreate); // create an empty account
|
||||
state.commit().unwrap();
|
||||
state.drop()
|
||||
};
|
||||
let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap();
|
||||
assert!(state.exists(&a));
|
||||
assert!(!state.exists_and_not_null(&a));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_from_database() {
|
||||
let a = Address::zero();
|
||||
@@ -1699,7 +1760,7 @@ fn alter_balance() {
|
||||
let mut state = state_result.reference_mut();
|
||||
let a = Address::zero();
|
||||
let b = address_from_u64(1u64);
|
||||
state.add_balance(&a, &U256::from(69u64));
|
||||
state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty);
|
||||
assert_eq!(state.balance(&a), U256::from(69u64));
|
||||
state.commit().unwrap();
|
||||
assert_eq!(state.balance(&a), U256::from(69u64));
|
||||
@@ -1707,7 +1768,7 @@ fn alter_balance() {
|
||||
assert_eq!(state.balance(&a), U256::from(27u64));
|
||||
state.commit().unwrap();
|
||||
assert_eq!(state.balance(&a), U256::from(27u64));
|
||||
state.transfer_balance(&a, &b, &U256::from(18u64));
|
||||
state.transfer_balance(&a, &b, &U256::from(18u64), CleanupMode::NoEmpty);
|
||||
assert_eq!(state.balance(&a), U256::from(9u64));
|
||||
assert_eq!(state.balance(&b), U256::from(18u64));
|
||||
state.commit().unwrap();
|
||||
@@ -1760,12 +1821,12 @@ fn snapshot_basic() {
|
||||
let mut state = state_result.reference_mut();
|
||||
let a = Address::zero();
|
||||
state.snapshot();
|
||||
state.add_balance(&a, &U256::from(69u64));
|
||||
state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty);
|
||||
assert_eq!(state.balance(&a), U256::from(69u64));
|
||||
state.discard_snapshot();
|
||||
assert_eq!(state.balance(&a), U256::from(69u64));
|
||||
state.snapshot();
|
||||
state.add_balance(&a, &U256::from(1u64));
|
||||
state.add_balance(&a, &U256::from(1u64), CleanupMode::NoEmpty);
|
||||
assert_eq!(state.balance(&a), U256::from(70u64));
|
||||
state.revert_to_snapshot();
|
||||
assert_eq!(state.balance(&a), U256::from(69u64));
|
||||
@@ -1778,7 +1839,7 @@ fn snapshot_nested() {
|
||||
let a = Address::zero();
|
||||
state.snapshot();
|
||||
state.snapshot();
|
||||
state.add_balance(&a, &U256::from(69u64));
|
||||
state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty);
|
||||
assert_eq!(state.balance(&a), U256::from(69u64));
|
||||
state.discard_snapshot();
|
||||
assert_eq!(state.balance(&a), U256::from(69u64));
|
||||
@@ -1794,4 +1855,20 @@ fn create_empty() {
|
||||
assert_eq!(state.root().hex(), "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_panic_on_state_diff_with_storage() {
|
||||
let state = get_temp_state();
|
||||
let mut state = state.reference().clone();
|
||||
|
||||
let a: Address = 0xa.into();
|
||||
state.init_code(&a, b"abcdefg".to_vec());
|
||||
state.add_balance(&a, &256.into(), CleanupMode::NoEmpty);
|
||||
state.set_storage(&a, 0xb.into(), 0xc.into());
|
||||
|
||||
let mut new_state = state.clone();
|
||||
new_state.set_storage(&a, 0xb.into(), 0xd.into());
|
||||
|
||||
new_state.diff_from(state);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ use client::DB_COL_ACCOUNT_BLOOM;
|
||||
use byteorder::{LittleEndian, ByteOrder};
|
||||
|
||||
const STATE_CACHE_ITEMS: usize = 256000;
|
||||
const STATE_CACHE_BLOCKS: usize = 8;
|
||||
const STATE_CACHE_BLOCKS: usize = 12;
|
||||
|
||||
/// Shared canonical state cache.
|
||||
struct AccountCache {
|
||||
@@ -137,13 +137,13 @@ impl StateDB {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_account_bloom(&self, address: &Address) -> bool {
|
||||
pub fn check_non_null_bloom(&self, address: &Address) -> bool {
|
||||
trace!(target: "account_bloom", "Check account bloom: {:?}", address);
|
||||
let bloom = self.account_bloom.lock();
|
||||
bloom.check(address.sha3().as_slice())
|
||||
}
|
||||
|
||||
pub fn note_account_bloom(&self, address: &Address) {
|
||||
pub fn note_non_null_account(&self, address: &Address) {
|
||||
trace!(target: "account_bloom", "Note account bloom: {:?}", address);
|
||||
let mut bloom = self.account_bloom.lock();
|
||||
bloom.set(address.sha3().as_slice());
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
use std::collections::HashSet;
|
||||
use util::{Address, U256};
|
||||
use log_entry::LogEntry;
|
||||
use evm::Schedule;
|
||||
use state::CleanupMode;
|
||||
|
||||
/// State changes which should be applied in finalize,
|
||||
/// after transaction is fully executed.
|
||||
@@ -26,6 +28,9 @@ pub struct Substate {
|
||||
/// Any accounts that have suicided.
|
||||
pub suicides: HashSet<Address>,
|
||||
|
||||
/// Any accounts that are tagged for garbage collection.
|
||||
pub garbage: HashSet<Address>,
|
||||
|
||||
/// Any logs.
|
||||
pub logs: Vec<LogEntry>,
|
||||
|
||||
@@ -45,10 +50,20 @@ impl Substate {
|
||||
/// Merge secondary substate `s` into self, accruing each element correspondingly.
|
||||
pub fn accrue(&mut self, s: Substate) {
|
||||
self.suicides.extend(s.suicides.into_iter());
|
||||
self.garbage.extend(s.garbage.into_iter());
|
||||
self.logs.extend(s.logs.into_iter());
|
||||
self.sstore_clears_count = self.sstore_clears_count + s.sstore_clears_count;
|
||||
self.contracts_created.extend(s.contracts_created.into_iter());
|
||||
}
|
||||
|
||||
/// Get the cleanup mode object from this.
|
||||
pub fn to_cleanup_mode(&mut self, schedule: &Schedule) -> CleanupMode {
|
||||
match (schedule.no_empty, schedule.kill_empty) {
|
||||
(false, _) => CleanupMode::ForceCreate,
|
||||
(true, false) => CleanupMode::NoEmpty,
|
||||
(true, true) => CleanupMode::KillEmpty(&mut self.garbage),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
use io::IoChannel;
|
||||
use client::{BlockChainClient, MiningBlockChainClient, Client, ClientConfig, BlockID};
|
||||
use state::CleanupMode;
|
||||
use ethereum;
|
||||
use block::IsBlock;
|
||||
use tests::helpers::*;
|
||||
@@ -152,7 +153,7 @@ fn can_handle_long_fork() {
|
||||
push_blocks_to_client(client, 49, 1201, 800);
|
||||
push_blocks_to_client(client, 53, 1201, 600);
|
||||
|
||||
for _ in 0..40 {
|
||||
for _ in 0..400 {
|
||||
client.import_verified_blocks();
|
||||
}
|
||||
assert_eq!(2000, client.chain_info().best_block_number);
|
||||
@@ -180,7 +181,7 @@ fn change_history_size() {
|
||||
let client = Client::new(ClientConfig::default(), &test_spec, dir.as_path(), Arc::new(Miner::with_spec(&test_spec)), IoChannel::disconnected()).unwrap();
|
||||
for _ in 0..20 {
|
||||
let mut b = client.prepare_open_block(Address::default(), (3141562.into(), 31415620.into()), vec![]);
|
||||
b.block_mut().fields_mut().state.add_balance(&address, &5.into());
|
||||
b.block_mut().fields_mut().state.add_balance(&address, &5.into(), CleanupMode::NoEmpty);
|
||||
b.block_mut().fields_mut().state.commit().unwrap();
|
||||
let b = b.close_and_lock().seal(&*test_spec.engine, vec![]).unwrap();
|
||||
client.import_sealed_block(b).unwrap(); // account change is in the journal overlay
|
||||
|
||||
@@ -32,19 +32,21 @@ use miner::Miner;
|
||||
pub enum ChainEra {
|
||||
Frontier,
|
||||
Homestead,
|
||||
DaoHardfork,
|
||||
Eip150,
|
||||
Eip161,
|
||||
TransitionTest,
|
||||
}
|
||||
|
||||
pub struct TestEngine {
|
||||
engine: Arc<Engine>,
|
||||
max_depth: usize
|
||||
max_depth: usize,
|
||||
}
|
||||
|
||||
impl TestEngine {
|
||||
pub fn new(max_depth: usize) -> TestEngine {
|
||||
TestEngine {
|
||||
engine: ethereum::new_frontier_test().engine,
|
||||
max_depth: max_depth
|
||||
max_depth: max_depth,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -180,7 +182,7 @@ pub fn generate_dummy_client_with_spec_and_data<F>(get_test_spec: F, block_numbe
|
||||
action: Action::Create,
|
||||
data: vec![],
|
||||
value: U256::zero(),
|
||||
}.sign(kp.secret()), None).unwrap();
|
||||
}.sign(kp.secret(), None), None).unwrap();
|
||||
n += 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,4 +16,6 @@
|
||||
|
||||
pub mod helpers;
|
||||
mod client;
|
||||
|
||||
#[cfg(feature="ipc")]
|
||||
mod rpc;
|
||||
|
||||
@@ -20,7 +20,7 @@ use util::numbers::*;
|
||||
use std::ops::Deref;
|
||||
use util::rlp::*;
|
||||
use util::sha3::*;
|
||||
use util::{UtilError, CryptoError, Bytes, Signature, Secret, ec};
|
||||
use util::{UtilError, CryptoError, Bytes, Signature, Secret, Public, ec};
|
||||
use std::cell::*;
|
||||
use error::*;
|
||||
use evm::Schedule;
|
||||
@@ -75,8 +75,8 @@ pub struct Transaction {
|
||||
|
||||
impl Transaction {
|
||||
/// Append object with a without signature into RLP stream
|
||||
pub fn rlp_append_unsigned_transaction(&self, s: &mut RlpStream) {
|
||||
s.begin_list(6);
|
||||
pub fn rlp_append_unsigned_transaction(&self, s: &mut RlpStream, network_id: Option<u8>) {
|
||||
s.begin_list(if let None = network_id { 6 } else { 9 });
|
||||
s.append(&self.nonce);
|
||||
s.append(&self.gas_price);
|
||||
s.append(&self.gas);
|
||||
@@ -86,6 +86,11 @@ impl Transaction {
|
||||
};
|
||||
s.append(&self.value);
|
||||
s.append(&self.data);
|
||||
if let Some(n) = network_id {
|
||||
s.append(&n);
|
||||
s.append(&0u8);
|
||||
s.append(&0u8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +107,7 @@ impl From<ethjson::state::Transaction> for SignedTransaction {
|
||||
},
|
||||
value: t.value.into(),
|
||||
data: t.data.into(),
|
||||
}.sign(&t.secret.into())
|
||||
}.sign(&t.secret.into(), None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,26 +137,27 @@ impl From<ethjson::transaction::Transaction> for SignedTransaction {
|
||||
|
||||
impl Transaction {
|
||||
/// The message hash of the transaction.
|
||||
pub fn hash(&self) -> H256 {
|
||||
pub fn hash(&self, network_id: Option<u8>) -> H256 {
|
||||
let mut stream = RlpStream::new();
|
||||
self.rlp_append_unsigned_transaction(&mut stream);
|
||||
self.rlp_append_unsigned_transaction(&mut stream, network_id);
|
||||
stream.out().sha3()
|
||||
}
|
||||
|
||||
/// Signs the transaction as coming from `sender`.
|
||||
pub fn sign(self, secret: &Secret) -> SignedTransaction {
|
||||
let sig = ec::sign(secret, &self.hash()).unwrap();
|
||||
self.with_signature(sig)
|
||||
pub fn sign(self, secret: &Secret, network_id: Option<u8>) -> SignedTransaction {
|
||||
let sig = ec::sign(secret, &self.hash(network_id))
|
||||
.expect("data is valid and context has signing capabilities; qed");
|
||||
self.with_signature(sig, network_id)
|
||||
}
|
||||
|
||||
/// Signs the transaction with signature.
|
||||
pub fn with_signature(self, sig: H520) -> SignedTransaction {
|
||||
pub fn with_signature(self, sig: Signature, network_id: Option<u8>) -> SignedTransaction {
|
||||
let (r, s, v) = sig.to_rsv();
|
||||
SignedTransaction {
|
||||
unsigned: self,
|
||||
r: r,
|
||||
s: s,
|
||||
v: v + 27,
|
||||
v: v + if let Some(n) = network_id { 35 + n * 2 } else { 27 },
|
||||
hash: Cell::new(None),
|
||||
sender: Cell::new(None),
|
||||
}
|
||||
@@ -201,7 +207,8 @@ impl Transaction {
|
||||
pub struct SignedTransaction {
|
||||
/// Plain Transaction.
|
||||
unsigned: Transaction,
|
||||
/// The V field of the signature, either 27 or 28; helps describe the point on the curve.
|
||||
/// The V field of the signature; the LS bit described which half of the curve our point falls
|
||||
/// in. The MS bits describe which network this transaction is for. If 27/28, its for all networks.
|
||||
v: u8,
|
||||
/// The R field of the signature; helps describe the point on the curve.
|
||||
r: U256,
|
||||
@@ -257,7 +264,7 @@ impl Encodable for SignedTransaction {
|
||||
|
||||
impl SignedTransaction {
|
||||
/// Append object with a signature into RLP stream
|
||||
pub fn rlp_append_sealed_transaction(&self, s: &mut RlpStream) {
|
||||
fn rlp_append_sealed_transaction(&self, s: &mut RlpStream) {
|
||||
s.begin_list(9);
|
||||
s.append(&self.nonce);
|
||||
s.append(&self.gas_price);
|
||||
@@ -286,8 +293,16 @@ impl SignedTransaction {
|
||||
}
|
||||
}
|
||||
|
||||
/// 0 is `v` is 27, 1 if 28, and 4 otherwise.
|
||||
pub fn standard_v(&self) -> u8 { match self.v { 27 => 0, 28 => 1, _ => 4 } }
|
||||
/// 0 if `v` would have been 27 under "Electrum" notation, 1 if 28 or 4 if invalid.
|
||||
pub fn standard_v(&self) -> u8 { match self.v { v if v == 27 || v == 28 || v > 36 => (v - 1) % 2, _ => 4 } }
|
||||
|
||||
/// The network ID, or `None` if this is a global transaction.
|
||||
pub fn network_id(&self) -> Option<u8> {
|
||||
match self.v {
|
||||
v if v > 36 => Some((v - 35) / 2),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a signature object from the sig.
|
||||
pub fn signature(&self) -> Signature { Signature::from_rsv(&From::from(&self.r), &From::from(&self.s), self.standard_v()) }
|
||||
@@ -303,28 +318,38 @@ impl SignedTransaction {
|
||||
|
||||
/// Returns transaction sender.
|
||||
pub fn sender(&self) -> Result<Address, Error> {
|
||||
let sender = self.sender.get();
|
||||
match sender {
|
||||
Some(s) => Ok(s),
|
||||
None => {
|
||||
let s = Address::from(try!(ec::recover(&self.signature(), &self.unsigned.hash())).sha3());
|
||||
self.sender.set(Some(s));
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
let sender = self.sender.get();
|
||||
match sender {
|
||||
Some(s) => Ok(s),
|
||||
None => {
|
||||
let s = Address::from(try!(self.public_key()).sha3());
|
||||
self.sender.set(Some(s));
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the public key of the sender.
|
||||
pub fn public_key(&self) -> Result<Public, Error> {
|
||||
Ok(try!(ec::recover(&self.signature(), &self.unsigned.hash(self.network_id()))))
|
||||
}
|
||||
|
||||
/// Do basic validation, checking for valid signature and minimum gas,
|
||||
// TODO: consider use in block validation.
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "json-tests")]
|
||||
pub fn validate(self, schedule: &Schedule, require_low: bool) -> Result<SignedTransaction, Error> {
|
||||
pub fn validate(self, schedule: &Schedule, require_low: bool, allow_network_id_of_one: bool) -> Result<SignedTransaction, Error> {
|
||||
if require_low && !ec::is_low_s(&self.s) {
|
||||
return Err(Error::Util(UtilError::Crypto(CryptoError::InvalidSignature)));
|
||||
}
|
||||
match self.network_id() {
|
||||
None => {},
|
||||
Some(1) if allow_network_id_of_one => {},
|
||||
_ => return Err(TransactionError::InvalidNetworkId.into()),
|
||||
}
|
||||
try!(self.sender());
|
||||
if self.gas < U256::from(self.gas_required(&schedule)) {
|
||||
Err(From::from(TransactionError::InvalidGasLimit(::util::OutOfBounds{min: Some(U256::from(self.gas_required(&schedule))), max: None, found: self.gas})))
|
||||
Err(TransactionError::InvalidGasLimit(::util::OutOfBounds{min: Some(U256::from(self.gas_required(&schedule))), max: None, found: self.gas}).into())
|
||||
} else {
|
||||
Ok(self)
|
||||
}
|
||||
@@ -364,6 +389,7 @@ fn sender_test() {
|
||||
} else { panic!(); }
|
||||
assert_eq!(t.value, U256::from(0x0au64));
|
||||
assert_eq!(t.sender().unwrap(), address_from_hex("0f65fe9276bc9a24ae7083ae28e2660ef72df99e"));
|
||||
assert_eq!(t.network_id(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -376,8 +402,9 @@ fn signing() {
|
||||
gas: U256::from(50_000),
|
||||
value: U256::from(1),
|
||||
data: b"Hello!".to_vec()
|
||||
}.sign(&key.secret());
|
||||
}.sign(&key.secret(), None);
|
||||
assert_eq!(Address::from(key.public().sha3()), t.sender().unwrap());
|
||||
assert_eq!(t.network_id(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -391,7 +418,47 @@ fn fake_signing() {
|
||||
data: b"Hello!".to_vec()
|
||||
}.fake_sign(Address::from(0x69));
|
||||
assert_eq!(Address::from(0x69), t.sender().unwrap());
|
||||
assert_eq!(t.network_id(), None);
|
||||
|
||||
let t = t.clone();
|
||||
assert_eq!(Address::from(0x69), t.sender().unwrap());
|
||||
assert_eq!(t.network_id(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_recover_from_network_specific_signing() {
|
||||
let key = ::util::crypto::KeyPair::create().unwrap();
|
||||
let t = Transaction {
|
||||
action: Action::Create,
|
||||
nonce: U256::from(42),
|
||||
gas_price: U256::from(3000),
|
||||
gas: U256::from(50_000),
|
||||
value: U256::from(1),
|
||||
data: b"Hello!".to_vec()
|
||||
}.sign(&key.secret(), Some(69));
|
||||
assert_eq!(Address::from(key.public().sha3()), t.sender().unwrap());
|
||||
assert_eq!(t.network_id(), Some(69));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_agree_with_vitalik() {
|
||||
use rustc_serialize::hex::FromHex;
|
||||
|
||||
let test_vector = |tx_data: &str, address: &'static str| {
|
||||
let signed: SignedTransaction = decode(&FromHex::from_hex(tx_data).unwrap());
|
||||
signed.check_low_s().unwrap();
|
||||
assert_eq!(signed.sender().unwrap(), address.into());
|
||||
flushln!("networkid: {:?}", signed.network_id());
|
||||
};
|
||||
|
||||
test_vector("f864808504a817c800825208943535353535353535353535353535353535353535808025a0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116da0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d", "0xf0f6f18bca1b28cd68e4357452947e021241e9ce")
|
||||
test_vector("f864018504a817c80182a410943535353535353535353535353535353535353535018025a0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bcaa0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6", "0x23ef145a395ea3fa3deb533b8a9e1b4c6c25d112")
|
||||
test_vector("f864028504a817c80282f618943535353535353535353535353535353535353535088025a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5", "0x2e485e0c23b4c3c542628a5f672eeab0ad4888be")
|
||||
test_vector("f865038504a817c803830148209435353535353535353535353535353535353535351b8025a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4e0a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4de", "0x82a88539669a3fd524d669e858935de5e5410cf0")
|
||||
test_vector("f865048504a817c80483019a28943535353535353535353535353535353535353535408025a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c063a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c060", "0xf9358f2538fd5ccfeb848b64a96b743fcc930554")
|
||||
test_vector("f865058504a817c8058301ec309435353535353535353535353535353535353535357d8025a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1", "0xa8f7aba377317440bc5b26198a363ad22af1f3a4")
|
||||
test_vector("f866068504a817c80683023e3894353535353535353535353535353535353535353581d88025a06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2fa06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2d", "0xf1f571dc362a0e5b2696b8e775f8491d3e50de35")
|
||||
test_vector("f867078504a817c807830290409435353535353535353535353535353535353535358201578025a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021", "0xd37922162ab7cea97c97a87551ed02c9a38b7332")
|
||||
test_vector("f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10", "0x9bddad43f934d313c2b79ca28a432dd2b7281029")
|
||||
test_vector("f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb", "0x3c24d7329e92f84f08556ceb6df1cdb0104ca49f")
|
||||
}
|
||||
|
||||
@@ -360,7 +360,7 @@ mod tests {
|
||||
gas: U256::from(30_000),
|
||||
gas_price: U256::from(40_000),
|
||||
nonce: U256::one()
|
||||
}.sign(keypair.secret());
|
||||
}.sign(keypair.secret(), None);
|
||||
|
||||
let tr2 = Transaction {
|
||||
action: Action::Create,
|
||||
@@ -369,7 +369,7 @@ mod tests {
|
||||
gas: U256::from(30_000),
|
||||
gas_price: U256::from(40_000),
|
||||
nonce: U256::from(2)
|
||||
}.sign(keypair.secret());
|
||||
}.sign(keypair.secret(), None);
|
||||
|
||||
let good_transactions = [ tr1.clone(), tr2.clone() ];
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use keccak::Keccak256;
|
||||
use super::{KeyPair, Error, Generator, Secret};
|
||||
use super::{KeyPair, Error, Generator};
|
||||
|
||||
/// Simple brainwallet.
|
||||
pub struct Brain(String);
|
||||
@@ -38,9 +38,9 @@ impl Generator for Brain {
|
||||
match i > 16384 {
|
||||
false => i += 1,
|
||||
true => {
|
||||
let result = KeyPair::from_secret(Secret::from(secret.clone()));
|
||||
if result.is_ok() {
|
||||
return result
|
||||
let result = KeyPair::from_secret(secret.clone().into());
|
||||
if result.as_ref().ok().map_or(false, |r| r.address()[0] == 0) {
|
||||
return result;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -162,7 +162,8 @@ mod test {
|
||||
#[test]
|
||||
fn should_create_new_account() {
|
||||
// given
|
||||
let dir = env::temp_dir();
|
||||
let mut dir = env::temp_dir();
|
||||
dir.push("ethstore_should_create_new_account");
|
||||
let keypair = Random.generate().unwrap();
|
||||
let password = "hello world";
|
||||
let directory = DiskDirectory::create(dir.clone()).unwrap();
|
||||
|
||||
@@ -48,6 +48,14 @@ impl Ext for FakeExt {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn exists_and_not_null(&self, address: &Address) -> bool {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn origin_balance(&self) -> U256 {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn balance(&self, _address: &Address) -> U256 {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ include!(concat!(env!("OUT_DIR"), "/lib.rs"));
|
||||
include!("lib.rs.in");
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
pub fn register(reg: &mut syntex::Registry) {
|
||||
pub fn register_cleaner(reg: &mut syntex::Registry) {
|
||||
use syntax::{ast, fold};
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
@@ -59,6 +59,7 @@ pub fn register(reg: &mut syntex::Registry) {
|
||||
fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
|
||||
match attr.node.value.node {
|
||||
ast::MetaItemKind::List(ref n, _) if n == &"ipc" => { return None; }
|
||||
ast::MetaItemKind::Word(ref n) if n == &"ipc" => { return None; }
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -73,19 +74,24 @@ pub fn register(reg: &mut syntex::Registry) {
|
||||
fold::Folder::fold_crate(&mut StripAttributeFolder, krate)
|
||||
}
|
||||
|
||||
reg.add_post_expansion_pass(strip_attributes);
|
||||
}
|
||||
|
||||
#[cfg(feature = "with-syntex")]
|
||||
pub fn register(reg: &mut syntex::Registry) {
|
||||
reg.add_attr("feature(custom_derive)");
|
||||
reg.add_attr("feature(custom_attribute)");
|
||||
|
||||
reg.add_decorator("derive_Ipc", codegen::expand_ipc_implementation);
|
||||
reg.add_decorator("ipc", codegen::expand_ipc_implementation);
|
||||
reg.add_decorator("derive_Binary", serialization::expand_serialization_implementation);
|
||||
|
||||
reg.add_post_expansion_pass(strip_attributes);
|
||||
register_cleaner(reg);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "with-syntex"))]
|
||||
pub fn register(reg: &mut rustc_plugin::Registry) {
|
||||
reg.register_syntax_extension(
|
||||
syntax::parse::token::intern("derive_Ipc"),
|
||||
syntax::parse::token::intern("ipc"),
|
||||
syntax::ext::base::MultiDecorator(
|
||||
Box::new(codegen::expand_ipc_implementation)));
|
||||
reg.register_syntax_extension(
|
||||
@@ -133,6 +139,30 @@ pub fn derive_ipc(src_path: &str) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn cleanup_ipc(src_path: &str) -> Result<(), Error> {
|
||||
use std::env;
|
||||
use std::path::{Path, PathBuf};
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
let file_name = try!(PathBuf::from(src_path).file_name().ok_or(Error::InvalidFileName).map(|val| val.to_str().unwrap().to_owned()));
|
||||
let mut registry = syntex::Registry::new();
|
||||
register_cleaner(&mut registry);
|
||||
if let Err(_) = registry.expand("", &Path::new(src_path), &Path::new(&out_dir).join(&file_name))
|
||||
{
|
||||
// will be reported by compiler
|
||||
return Err(Error::ExpandFailure)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn derive_ipc_cond(src_path: &str, cond: bool) -> Result<(), Error> {
|
||||
if cond {
|
||||
derive_ipc(src_path)
|
||||
}
|
||||
else {
|
||||
cleanup_ipc(src_path)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn derive_binary(src_path: &str) -> Result<(), Error> {
|
||||
use std::env;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
@@ -34,7 +34,7 @@ pub struct HypervisorService {
|
||||
check_list: RwLock<HashMap<IpcModuleId, bool>>,
|
||||
}
|
||||
|
||||
#[derive(Ipc)]
|
||||
#[ipc]
|
||||
impl HypervisorService {
|
||||
fn module_ready(&self, module_id: u64) -> bool {
|
||||
let mut check_list = self.check_list.write().unwrap();
|
||||
|
||||
@@ -36,7 +36,7 @@ impl IpcConfig for DBWriter {}
|
||||
#[derive(Binary)]
|
||||
pub enum DBError { Write, Read }
|
||||
|
||||
#[derive(Ipc)]
|
||||
#[ipc]
|
||||
impl<L: Sized> DBWriter for DB<L> {
|
||||
fn write(&self, data: Vec<u8>) -> Result<(), DBError> {
|
||||
let mut writes = self.writes.write().unwrap();
|
||||
@@ -51,7 +51,7 @@ impl<L: Sized> DBWriter for DB<L> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Ipc)]
|
||||
#[ipc]
|
||||
trait DBNotify {
|
||||
fn notify(&self, a: u64, b: u64) -> bool;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ pub struct CustomData {
|
||||
pub b: u64,
|
||||
}
|
||||
|
||||
#[derive(Ipc)]
|
||||
#[ipc]
|
||||
impl Service {
|
||||
fn commit(&self, f: u32) -> u32 {
|
||||
let mut lock = self.commits.write().unwrap();
|
||||
|
||||
@@ -21,7 +21,7 @@ use std::collections::VecDeque;
|
||||
|
||||
pub struct BadlyNamedService;
|
||||
|
||||
#[derive(Ipc)]
|
||||
//#[derive(Ipc)]
|
||||
#[ipc(client_ident="PrettyNamedClient")]
|
||||
impl BadlyNamedService {
|
||||
fn is_zero(&self, x: u64) -> bool {
|
||||
|
||||
@@ -63,7 +63,7 @@ mod tests {
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit" : "0x",
|
||||
"homesteadTransition" : "0x",
|
||||
"daoHardforkTransition": "0xffffffffffffffff",
|
||||
"daoHardforkBeneficiary": "0x0000000000000000000000000000000000000000",
|
||||
"daoHardforkAccounts": []
|
||||
|
||||
@@ -40,8 +40,8 @@ pub struct EthashParams {
|
||||
/// See main EthashParams docs.
|
||||
pub registrar: Option<Address>,
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="frontierCompatibilityModeLimit")]
|
||||
pub frontier_compatibility_mode_limit: Option<Uint>,
|
||||
#[serde(rename="homesteadTransition")]
|
||||
pub homestead_transition: Option<Uint>,
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="daoHardforkTransition")]
|
||||
pub dao_hardfork_transition: Option<Uint>,
|
||||
@@ -51,6 +51,33 @@ pub struct EthashParams {
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="daoHardforkAccounts")]
|
||||
pub dao_hardfork_accounts: Option<Vec<Address>>,
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="difficultyHardforkTransition")]
|
||||
pub difficulty_hardfork_transition: Option<Uint>,
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="difficultyHardforkBoundDivisor")]
|
||||
pub difficulty_hardfork_bound_divisor: Option<Uint>,
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="bombDefuseTransition")]
|
||||
pub bomb_defuse_transition: Option<Uint>,
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="eip150Transition")]
|
||||
pub eip150_transition: Option<Uint>,
|
||||
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="eip155Transition")]
|
||||
pub eip155_transition: Option<Uint>,
|
||||
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="eip160Transition")]
|
||||
pub eip160_transition: Option<Uint>,
|
||||
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="eip161abcTransition")]
|
||||
pub eip161abc_transition: Option<Uint>,
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="eip161dTransition")]
|
||||
pub eip161d_transition: Option<Uint>,
|
||||
}
|
||||
|
||||
/// Ethash engine deserialization.
|
||||
@@ -75,7 +102,7 @@ mod tests {
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar": "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0x42",
|
||||
"homesteadTransition": "0x42",
|
||||
"daoHardforkTransition": "0x08",
|
||||
"daoHardforkBeneficiary": "0xabcabcabcabcabcabcabcabcabcabcabcabcabca",
|
||||
"daoHardforkAccounts": [
|
||||
@@ -99,7 +126,15 @@ mod tests {
|
||||
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
||||
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
||||
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
||||
]
|
||||
],
|
||||
"difficultyHardforkTransition": "0x59d9",
|
||||
"difficultyHardforkBoundDivisor": "0x0200",
|
||||
"bombDefuseTransition": "0x42",
|
||||
"eip150Transition": "0x42",
|
||||
"eip155Transition": "0x42",
|
||||
"eip160Transition": "0x42",
|
||||
"eip161abcTransition": "0x42",
|
||||
"eip161dTransition": "0x42"
|
||||
}
|
||||
}"#;
|
||||
|
||||
|
||||
@@ -28,12 +28,17 @@ pub struct Params {
|
||||
/// Maximum size of extra data.
|
||||
#[serde(rename="maximumExtraDataSize")]
|
||||
pub maximum_extra_data_size: Uint,
|
||||
/// Network id.
|
||||
#[serde(rename="networkID")]
|
||||
pub network_id: Uint,
|
||||
/// Minimum gas limit.
|
||||
#[serde(rename="minGasLimit")]
|
||||
pub min_gas_limit: Uint,
|
||||
|
||||
/// Network id.
|
||||
#[serde(rename="networkID")]
|
||||
pub network_id: Uint,
|
||||
/// Name of the main ("eth") subprotocol.
|
||||
#[serde(rename="subprotocolName")]
|
||||
pub subprotocol_name: Option<String>,
|
||||
|
||||
/// Option fork block number to check.
|
||||
#[serde(rename="forkBlock")]
|
||||
pub fork_block: Option<Uint>,
|
||||
@@ -50,7 +55,7 @@ mod tests {
|
||||
#[test]
|
||||
fn params_deserialization() {
|
||||
let s = r#"{
|
||||
"frontierCompatibilityModeLimit": "0x118c30",
|
||||
"homesteadTransition": "0x118c30",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"networkID" : "0x1",
|
||||
"minGasLimit": "0x1388",
|
||||
|
||||
@@ -66,7 +66,7 @@ mod tests {
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit" : "0x",
|
||||
"homesteadTransition" : "0x",
|
||||
"daoHardforkTransition": "0xffffffffffffffff",
|
||||
"daoHardforkBeneficiary": "0x0000000000000000000000000000000000000000",
|
||||
"daoHardforkAccounts": []
|
||||
@@ -75,7 +75,7 @@ mod tests {
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x0100000",
|
||||
"frontierCompatibilityModeLimit": "0x789b0",
|
||||
"homesteadTransition": "0x789b0",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x2",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
!define DESCRIPTION "Fast, light, robust Ethereum implementation"
|
||||
!define VERSIONMAJOR 1
|
||||
!define VERSIONMINOR 3
|
||||
!define VERSIONBUILD 7
|
||||
!define VERSIONBUILD 11
|
||||
|
||||
!addplugindir .\
|
||||
|
||||
|
||||
@@ -126,7 +126,6 @@ API and Console Options:
|
||||
--ipc-apis APIS Specify custom API set available via JSON-RPC over
|
||||
IPC [default: web3,eth,net,ethcore,personal,traces,rpc].
|
||||
|
||||
--no-dapps Disable the Dapps server (e.g. status page).
|
||||
--dapps-port PORT Specify the port portion of the Dapps server
|
||||
[default: 8080].
|
||||
--dapps-interface IP Specify the hostname portion of the Dapps
|
||||
@@ -202,7 +201,7 @@ Sealing/Mining Options:
|
||||
and gas limit ratio [default: gas_factor].
|
||||
--tx-queue-gas LIMIT Maximum amount of total gas for external transactions in
|
||||
the queue. LIMIT can be either an amount of gas or
|
||||
'auto' or 'off'. 'auto' sets the limit to be 2x
|
||||
'auto' or 'off'. 'auto' sets the limit to be 20x
|
||||
the current block gas limit. [default: auto].
|
||||
--remove-solved Move solved blocks from the work package queue
|
||||
instead of cloning them. This gives a slightly
|
||||
@@ -289,6 +288,7 @@ Legacy Options:
|
||||
--etherbase ADDRESS Equivalent to --author ADDRESS.
|
||||
--extradata STRING Equivalent to --extra-data STRING.
|
||||
--cache MB Equivalent to --cache-size MB.
|
||||
--no-dapps Disable the Dapps server (e.g. status page).
|
||||
|
||||
Miscellaneous Options:
|
||||
-l --logging LOGGING Specify the logging level. Must conform to the same
|
||||
@@ -329,7 +329,7 @@ pub struct Args {
|
||||
pub flag_keys_iterations: u32,
|
||||
pub flag_no_import_keys: bool,
|
||||
pub flag_bootnodes: Option<String>,
|
||||
pub flag_network_id: Option<String>,
|
||||
pub flag_network_id: Option<usize>,
|
||||
pub flag_pruning: String,
|
||||
pub flag_pruning_history: u64,
|
||||
pub flag_tracing: String,
|
||||
@@ -415,7 +415,7 @@ pub struct Args {
|
||||
pub flag_rpccorsdomain: Option<String>,
|
||||
pub flag_rpcapi: Option<String>,
|
||||
pub flag_testnet: bool,
|
||||
pub flag_networkid: Option<String>,
|
||||
pub flag_networkid: Option<usize>,
|
||||
pub flag_ipcdisable: bool,
|
||||
pub flag_ipc_off: bool,
|
||||
pub flag_jsonrpc_off: bool,
|
||||
|
||||
@@ -81,7 +81,7 @@ impl Configuration {
|
||||
let http_conf = try!(self.http_config());
|
||||
let ipc_conf = try!(self.ipc_config());
|
||||
let net_conf = try!(self.net_config());
|
||||
let network_id = try!(self.network_id());
|
||||
let network_id = self.network_id();
|
||||
let cache_config = self.cache_config();
|
||||
let spec = try!(self.chain().parse());
|
||||
let tracing = try!(self.args.flag_tracing.parse());
|
||||
@@ -465,12 +465,8 @@ impl Configuration {
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn network_id(&self) -> Result<Option<U256>, String> {
|
||||
let net_id = self.args.flag_network_id.as_ref().or(self.args.flag_networkid.as_ref());
|
||||
match net_id {
|
||||
Some(id) => Ok(Some(try!(to_u256(id)))),
|
||||
None => Ok(None),
|
||||
}
|
||||
fn network_id(&self) -> Option<usize> {
|
||||
self.args.flag_network_id.or(self.args.flag_networkid)
|
||||
}
|
||||
|
||||
fn rpc_apis(&self) -> String {
|
||||
@@ -552,10 +548,10 @@ impl Configuration {
|
||||
let dapps_path = replace_home(&self.args.flag_dapps_path);
|
||||
let signer_path = replace_home(&self.args.flag_signer_path);
|
||||
|
||||
if self.args.flag_geth {
|
||||
let geth_path = path::ethereum::default();
|
||||
::std::fs::create_dir_all(geth_path.as_path()).unwrap_or_else(
|
||||
|e| warn!("Failed to create '{}' for geth mode: {}", &geth_path.to_str().unwrap(), e));
|
||||
if self.args.flag_geth && !cfg!(windows) {
|
||||
let geth_root = if self.args.flag_testnet { path::ethereum::test() } else { path::ethereum::default() };
|
||||
::std::fs::create_dir_all(geth_root.as_path()).unwrap_or_else(
|
||||
|e| warn!("Failed to create '{}' for geth mode: {}", &geth_root.to_str().unwrap(), e));
|
||||
}
|
||||
|
||||
Directories {
|
||||
|
||||
@@ -81,7 +81,6 @@ mod account;
|
||||
mod blockchain;
|
||||
mod presale;
|
||||
mod run;
|
||||
mod sync;
|
||||
mod snapshot;
|
||||
|
||||
use std::{process, env};
|
||||
@@ -119,11 +118,6 @@ fn start() -> Result<String, String> {
|
||||
fn main() {
|
||||
// Always print backtrace on panic.
|
||||
::std::env::set_var("RUST_BACKTRACE", "1");
|
||||
// just redirect to the sync::main()
|
||||
if std::env::args().nth(1).map_or(false, |arg| arg == "sync") {
|
||||
sync::main();
|
||||
return;
|
||||
}
|
||||
|
||||
match start() {
|
||||
Ok(result) => {
|
||||
|
||||
@@ -43,13 +43,15 @@ pub enum Error {
|
||||
/// Returned when current version cannot be read or guessed.
|
||||
UnknownDatabaseVersion,
|
||||
/// Migration does not support existing pruning algorithm.
|
||||
UnsuportedPruningMethod,
|
||||
UnsupportedPruningMethod,
|
||||
/// Existing DB is newer than the known one.
|
||||
FutureDBVersion,
|
||||
/// Migration is not possible.
|
||||
MigrationImpossible,
|
||||
/// Migration unexpectadly failed.
|
||||
MigrationFailed,
|
||||
/// Internal migration error.
|
||||
Internal(MigrationError),
|
||||
/// Migration was completed succesfully,
|
||||
/// but there was a problem with io.
|
||||
Io(IoError),
|
||||
@@ -59,10 +61,11 @@ impl Display for Error {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
|
||||
let out = match *self {
|
||||
Error::UnknownDatabaseVersion => "Current database version cannot be read".into(),
|
||||
Error::UnsuportedPruningMethod => "Unsupported pruning method for database migration. Delete DB and resync.".into(),
|
||||
Error::UnsupportedPruningMethod => "Unsupported pruning method for database migration. Delete DB and resync.".into(),
|
||||
Error::FutureDBVersion => "Database was created with newer client version. Upgrade your client or delete DB and resync.".into(),
|
||||
Error::MigrationImpossible => format!("Database migration to version {} is not possible.", CURRENT_VERSION),
|
||||
Error::MigrationFailed => "Database migration unexpectedly failed".into(),
|
||||
Error::Internal(ref err) => format!("{}", err),
|
||||
Error::Io(ref err) => format!("Unexpected io error on DB migration: {}.", err),
|
||||
};
|
||||
|
||||
@@ -80,7 +83,7 @@ impl From<MigrationError> for Error {
|
||||
fn from(err: MigrationError) -> Self {
|
||||
match err {
|
||||
MigrationError::Io(e) => Error::Io(e),
|
||||
_ => Error::MigrationFailed,
|
||||
_ => Error::Internal(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -245,9 +248,11 @@ pub fn migrate(path: &Path, pruning: Algorithm, compaction_profile: CompactionPr
|
||||
// Perform pre-consolidation migrations
|
||||
if version < CONSOLIDATION_VERSION && exists(&legacy::blocks_database_path(path)) {
|
||||
println!("Migrating database from version {} to {}", version, CONSOLIDATION_VERSION);
|
||||
try!(migrate_database(version, legacy::blocks_database_path(path), try!(legacy::blocks_database_migrations(&compaction_profile))));
|
||||
|
||||
try!(migrate_database(version, legacy::extras_database_path(path), try!(legacy::extras_database_migrations(&compaction_profile))));
|
||||
try!(migrate_database(version, legacy::state_database_path(path), try!(legacy::state_database_migrations(pruning, &compaction_profile))));
|
||||
try!(migrate_database(version, legacy::blocks_database_path(path), try!(legacy::blocks_database_migrations(&compaction_profile))));
|
||||
|
||||
let db_path = consolidated_database_path(path);
|
||||
// Remove the database dir (it shouldn't exist anyway, but it might when migration was interrupted)
|
||||
let _ = fs::remove_dir_all(db_path.clone());
|
||||
@@ -263,6 +268,9 @@ pub fn migrate(path: &Path, pruning: Algorithm, compaction_profile: CompactionPr
|
||||
println!("Migration finished");
|
||||
}
|
||||
|
||||
// update version so we can apply post-consolidation migrations.
|
||||
let version = ::std::cmp::max(CONSOLIDATION_VERSION, version);
|
||||
|
||||
// Further migrations
|
||||
if version >= CONSOLIDATION_VERSION && version < CURRENT_VERSION && exists(&consolidated_database_path(path)) {
|
||||
println!("Migrating database from version {} to {}", ::std::cmp::max(CONSOLIDATION_VERSION, version), CURRENT_VERSION);
|
||||
@@ -336,7 +344,7 @@ mod legacy {
|
||||
let res = match pruning {
|
||||
Algorithm::Archive => manager.add_migration(migrations::state::ArchiveV7::default()),
|
||||
Algorithm::OverlayRecent => manager.add_migration(migrations::state::OverlayRecentV7::default()),
|
||||
_ => return Err(Error::UnsuportedPruningMethod),
|
||||
_ => return Err(Error::UnsupportedPruningMethod),
|
||||
};
|
||||
|
||||
try!(res.map_err(|_| Error::MigrationImpossible));
|
||||
|
||||
@@ -25,10 +25,6 @@ use self::ipc_deps::*;
|
||||
use ethcore_logger::Config as LogConfig;
|
||||
|
||||
pub mod service_urls {
|
||||
pub const CLIENT: &'static str = "ipc:///tmp/parity-chain.ipc";
|
||||
pub const SYNC: &'static str = "ipc:///tmp/parity-sync.ipc";
|
||||
pub const SYNC_NOTIFY: &'static str = "ipc:///tmp/parity-sync-notify.ipc";
|
||||
pub const NETWORK_MANAGER: &'static str = "ipc:///tmp/parity-manage-net.ipc";
|
||||
}
|
||||
|
||||
#[cfg(not(feature="ipc"))]
|
||||
|
||||
@@ -22,7 +22,7 @@ use fdlimit::raise_fd_limit;
|
||||
use ethcore_logger::{Config as LogConfig, setup_log};
|
||||
use ethcore_rpc::NetworkSettings;
|
||||
use ethsync::NetworkConfiguration;
|
||||
use util::{Colour, version, U256};
|
||||
use util::{Colour, version};
|
||||
use io::{MayPanic, ForwardPanic, PanicHandler};
|
||||
use ethcore::client::{Mode, Switch, DatabaseCompactionProfile, VMType, ChainNotify};
|
||||
use ethcore::service::ClientService;
|
||||
@@ -60,7 +60,7 @@ pub struct RunCmd {
|
||||
pub http_conf: HttpConfiguration,
|
||||
pub ipc_conf: IpcConfiguration,
|
||||
pub net_conf: NetworkConfiguration,
|
||||
pub network_id: Option<U256>,
|
||||
pub network_id: Option<usize>,
|
||||
pub acc_conf: AccountsConfig,
|
||||
pub gas_pricer: GasPricerConfig,
|
||||
pub miner_extras: MinerExtras,
|
||||
@@ -80,13 +80,17 @@ pub struct RunCmd {
|
||||
pub custom_bootnodes: bool,
|
||||
}
|
||||
|
||||
pub fn execute(cmd: RunCmd) -> Result<(), String> {
|
||||
pub fn execute(mut cmd: RunCmd) -> Result<(), String> {
|
||||
// increase max number of open files
|
||||
raise_fd_limit();
|
||||
|
||||
// set up logger
|
||||
let logger = try!(setup_log(&cmd.logger_config));
|
||||
|
||||
if cmd.dapps_conf.enabled {
|
||||
warn!("Warning: Parity UI is disabled in this release. Please upgrade to version 1.4.2 or higher.");
|
||||
cmd.dapps_conf.enabled = false;
|
||||
}
|
||||
// set up panic handler
|
||||
let panic_handler = PanicHandler::new_in_arc();
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ log = "0.3"
|
||||
serde = "0.7.0"
|
||||
serde_json = "0.7.0"
|
||||
jsonrpc-core = "2.1"
|
||||
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git" }
|
||||
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git", branch = "beta" }
|
||||
ethcore-io = { path = "../util/io" }
|
||||
ethcore-util = { path = "../util" }
|
||||
ethcore = { path = "../ethcore" }
|
||||
|
||||
@@ -62,15 +62,16 @@ pub fn signature_with_password(accounts: &AccountProvider, address: Address, has
|
||||
pub fn unlock_sign_and_dispatch<C, M>(client: &C, miner: &M, request: TransactionRequest, account_provider: &AccountProvider, password: String) -> Result<Value, Error>
|
||||
where C: MiningBlockChainClient, M: MinerService {
|
||||
|
||||
let network_id = client.signing_network_id();
|
||||
let address = request.from;
|
||||
let signed_transaction = {
|
||||
let t = prepare_transaction(client, miner, request);
|
||||
let hash = t.hash();
|
||||
let hash = t.hash(network_id);
|
||||
let signature = try!(account_provider.sign_with_password(address, password, hash).map_err(errors::from_password_error));
|
||||
t.with_signature(signature)
|
||||
t.with_signature(signature, network_id)
|
||||
};
|
||||
|
||||
trace!(target: "miner", "send_transaction: dispatching tx: {}", encode(&signed_transaction).to_vec().pretty());
|
||||
trace!(target: "miner", "send_transaction: dispatching tx: {} for network ID {:?}", encode(&signed_transaction).to_vec().pretty(), network_id);
|
||||
dispatch_transaction(&*client, &*miner, signed_transaction)
|
||||
}
|
||||
|
||||
@@ -79,9 +80,9 @@ pub fn sign_and_dispatch<C, M>(client: &C, miner: &M, request: TransactionReques
|
||||
|
||||
let signed_transaction = {
|
||||
let t = prepare_transaction(client, miner, request);
|
||||
let hash = t.hash();
|
||||
let hash = t.hash(None);
|
||||
let signature = try!(account_provider.sign(address, hash).map_err(errors::from_signing_error));
|
||||
t.with_signature(signature)
|
||||
t.with_signature(signature, None)
|
||||
};
|
||||
|
||||
trace!(target: "miner", "send_transaction: dispatching tx: {}", encode(&signed_transaction).to_vec().pretty());
|
||||
|
||||
@@ -170,6 +170,7 @@ pub fn from_transaction_error(error: EthcoreError) -> Error {
|
||||
format!("Transaction cost exceeds current gas limit. Limit: {}, got: {}. Try decreasing supplied gas.", limit, got)
|
||||
},
|
||||
InvalidGasLimit(_) => "Supplied gas is beyond limit.".into(),
|
||||
e => format!("{}", e).into(),
|
||||
};
|
||||
Error {
|
||||
code: ErrorCode::ServerError(codes::TRANSACTION_ERROR),
|
||||
|
||||
@@ -45,7 +45,7 @@ fn account_provider() -> Arc<AccountProvider> {
|
||||
|
||||
fn sync_provider() -> Arc<TestSyncProvider> {
|
||||
Arc::new(TestSyncProvider::new(Config {
|
||||
network_id: U256::from(3),
|
||||
network_id: 3,
|
||||
num_peers: 120,
|
||||
}))
|
||||
}
|
||||
@@ -201,7 +201,7 @@ const TRANSACTION_COUNT_SPEC: &'static [u8] = br#"{
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0xffffffffffffffff",
|
||||
"homesteadTransition": "0xffffffffffffffff",
|
||||
"daoHardforkTransition": "0xffffffffffffffff",
|
||||
"daoHardforkBeneficiary": "0x0000000000000000000000000000000000000000",
|
||||
"daoHardforkAccounts": []
|
||||
@@ -249,7 +249,7 @@ const POSITIVE_NONCE_SPEC: &'static [u8] = br#"{
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0xffffffffffffffff",
|
||||
"homesteadTransition": "0xffffffffffffffff",
|
||||
"daoHardforkTransition": "0xffffffffffffffff",
|
||||
"daoHardforkBeneficiary": "0x0000000000000000000000000000000000000000",
|
||||
"daoHardforkAccounts": []
|
||||
|
||||
@@ -16,13 +16,13 @@
|
||||
|
||||
//! Test implementation of SyncProvider.
|
||||
|
||||
use util::{RwLock, U256};
|
||||
use util::{RwLock};
|
||||
use ethsync::{SyncProvider, SyncStatus, SyncState};
|
||||
|
||||
/// TestSyncProvider config.
|
||||
pub struct Config {
|
||||
/// Protocol version.
|
||||
pub network_id: U256,
|
||||
pub network_id: usize,
|
||||
/// Number of peers.
|
||||
pub num_peers: usize,
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ fn accounts_provider() -> Arc<AccountProvider> {
|
||||
|
||||
fn sync_provider() -> Arc<TestSyncProvider> {
|
||||
Arc::new(TestSyncProvider::new(Config {
|
||||
network_id: U256::from(3),
|
||||
network_id: 3,
|
||||
num_peers: 120,
|
||||
}))
|
||||
}
|
||||
@@ -621,8 +621,8 @@ fn rpc_eth_send_transaction() {
|
||||
value: U256::from(0x9184e72au64),
|
||||
data: vec![]
|
||||
};
|
||||
let signature = tester.accounts_provider.sign(address, t.hash()).unwrap();
|
||||
let t = t.with_signature(signature);
|
||||
let signature = tester.accounts_provider.sign(address, t.hash(None)).unwrap();
|
||||
let t = t.with_signature(signature, None);
|
||||
|
||||
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#;
|
||||
|
||||
@@ -638,8 +638,8 @@ fn rpc_eth_send_transaction() {
|
||||
value: U256::from(0x9184e72au64),
|
||||
data: vec![]
|
||||
};
|
||||
let signature = tester.accounts_provider.sign(address, t.hash()).unwrap();
|
||||
let t = t.with_signature(signature);
|
||||
let signature = tester.accounts_provider.sign(address, t.hash(None)).unwrap();
|
||||
let t = t.with_signature(signature, None);
|
||||
|
||||
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#;
|
||||
|
||||
@@ -703,8 +703,8 @@ fn rpc_eth_send_raw_transaction() {
|
||||
value: U256::from(0x9184e72au64),
|
||||
data: vec![]
|
||||
};
|
||||
let signature = tester.accounts_provider.sign(address, t.hash()).unwrap();
|
||||
let t = t.with_signature(signature);
|
||||
let signature = tester.accounts_provider.sign(address, t.hash(None)).unwrap();
|
||||
let t = t.with_signature(signature, None);
|
||||
|
||||
let rlp = ::util::rlp::encode(&t).to_vec().to_hex();
|
||||
|
||||
|
||||
@@ -233,8 +233,8 @@ fn should_dispatch_transaction_if_account_is_unlock() {
|
||||
value: U256::from(0x9184e72au64),
|
||||
data: vec![]
|
||||
};
|
||||
let signature = tester.accounts.sign(acc, t.hash()).unwrap();
|
||||
let t = t.with_signature(signature);
|
||||
let signature = tester.accounts.sign(acc, t.hash(None)).unwrap();
|
||||
let t = t.with_signature(signature, None);
|
||||
|
||||
// when
|
||||
let request = r#"{
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
use util::log::RotatingLogger;
|
||||
use util::U256;
|
||||
use ethsync::ManageNetwork;
|
||||
use ethcore::client::{TestBlockChainClient};
|
||||
|
||||
@@ -36,7 +35,7 @@ fn client_service() -> Arc<TestBlockChainClient> {
|
||||
|
||||
fn sync_provider() -> Arc<TestSyncProvider> {
|
||||
Arc::new(TestSyncProvider::new(Config {
|
||||
network_id: U256::from(3),
|
||||
network_id: 3,
|
||||
num_peers: 120,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -18,11 +18,10 @@ use std::sync::Arc;
|
||||
use jsonrpc_core::IoHandler;
|
||||
use v1::{Net, NetClient};
|
||||
use v1::tests::helpers::{Config, TestSyncProvider};
|
||||
use util::numbers::*;
|
||||
|
||||
fn sync_provider() -> Arc<TestSyncProvider> {
|
||||
Arc::new(TestSyncProvider::new(Config {
|
||||
network_id: U256::from(3),
|
||||
network_id: 3,
|
||||
num_peers: 120,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -227,8 +227,8 @@ fn sign_and_send_transaction() {
|
||||
data: vec![]
|
||||
};
|
||||
tester.accounts.unlock_account_temporarily(address, "password123".into()).unwrap();
|
||||
let signature = tester.accounts.sign(address, t.hash()).unwrap();
|
||||
let t = t.with_signature(signature);
|
||||
let signature = tester.accounts.sign(address, t.hash(None)).unwrap();
|
||||
let t = t.with_signature(signature, None);
|
||||
|
||||
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#;
|
||||
|
||||
@@ -245,8 +245,8 @@ fn sign_and_send_transaction() {
|
||||
data: vec![]
|
||||
};
|
||||
tester.accounts.unlock_account_temporarily(address, "password123".into()).unwrap();
|
||||
let signature = tester.accounts.sign(address, t.hash()).unwrap();
|
||||
let t = t.with_signature(signature);
|
||||
let signature = tester.accounts.sign(address, t.hash(None)).unwrap();
|
||||
let t = t.with_signature(signature, None);
|
||||
|
||||
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#;
|
||||
|
||||
|
||||
@@ -186,8 +186,8 @@ fn should_confirm_transaction_and_dispatch() {
|
||||
data: vec![]
|
||||
};
|
||||
tester.accounts.unlock_account_temporarily(address, "test".into()).unwrap();
|
||||
let signature = tester.accounts.sign(address, t.hash()).unwrap();
|
||||
let t = t.with_signature(signature);
|
||||
let signature = tester.accounts.sign(address, t.hash(None)).unwrap();
|
||||
let t = t.with_signature(signature, None);
|
||||
|
||||
assert_eq!(tester.queue.requests().len(), 1);
|
||||
|
||||
|
||||
31
scripts/deb-build.sh
Normal file
31
scripts/deb-build.sh
Normal file
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e # fail on any error
|
||||
set -u # treat unset variables as error
|
||||
rm -rf deb
|
||||
#create DEBIAN files
|
||||
mkdir -p deb/usr/bin/
|
||||
mkdir -p deb/DEBIAN
|
||||
#create copyright, docs, compat
|
||||
cp LICENSE deb/DEBIAN/copyright
|
||||
echo "https://github.com/ethcore/parity/wiki" >> deb/DEBIAN/docs
|
||||
echo "8" >> deb/DEBIAN/compat
|
||||
#create control file
|
||||
control=deb/DEBIAN/control
|
||||
echo "Package: parity" >> $control
|
||||
version=`grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n"`
|
||||
echo "Version: $version" >> $control
|
||||
echo "Source: parity" >> $control
|
||||
echo "Section: science" >> $control
|
||||
echo "Priority: extra" >> $control
|
||||
echo "Maintainer: Ethcore <devops@ethcore.io>" >> $control
|
||||
echo "Build-Depends: debhelper (>=9)" >> $control
|
||||
echo "Standards-Version: 3.9.5" >> $control
|
||||
echo "Homepage: https://ethcore.io" >> $control
|
||||
echo "Vcs-Git: git://github.com/ethcore/parity.git" >> $control
|
||||
echo "Vcs-Browser: https://github.com/ethcore/parity" >> $control
|
||||
echo "Architecture: $1" >> $control
|
||||
echo "Description: Ethereum network client by Ethcore" >> $control
|
||||
#build .deb package
|
||||
|
||||
exit
|
||||
@@ -18,7 +18,7 @@ use std::sync::Arc;
|
||||
use std::collections::HashMap;
|
||||
use network::{NetworkProtocolHandler, NetworkService, NetworkContext, PeerId,
|
||||
NetworkConfiguration as BasicNetworkConfiguration, NonReservedPeerMode, NetworkError};
|
||||
use util::{U256, H256, Secret, Populatable, Bytes};
|
||||
use util::{H256, Secret, Populatable, Bytes};
|
||||
use io::{TimerToken};
|
||||
use ethcore::client::{BlockChainClient, ChainNotify};
|
||||
use ethcore::header::BlockNumber;
|
||||
@@ -40,7 +40,9 @@ pub struct SyncConfig {
|
||||
/// Max blocks to download ahead
|
||||
pub max_download_ahead_blocks: usize,
|
||||
/// Network ID
|
||||
pub network_id: U256,
|
||||
pub network_id: usize,
|
||||
/// Main "eth" subprotocol name.
|
||||
pub subprotocol_name: [u8; 3],
|
||||
/// Fork block to check
|
||||
pub fork_block: Option<(BlockNumber, H256)>,
|
||||
}
|
||||
@@ -49,7 +51,8 @@ impl Default for SyncConfig {
|
||||
fn default() -> SyncConfig {
|
||||
SyncConfig {
|
||||
max_download_ahead_blocks: 20000,
|
||||
network_id: U256::from(1),
|
||||
network_id: 1,
|
||||
subprotocol_name: *b"eth",
|
||||
fork_block: None,
|
||||
}
|
||||
}
|
||||
@@ -90,8 +93,6 @@ impl EthSync {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Ipc)]
|
||||
#[ipc(client_ident="SyncClient")]
|
||||
impl SyncProvider for EthSync {
|
||||
/// Get sync status
|
||||
fn status(&self) -> SyncStatus {
|
||||
@@ -185,8 +186,6 @@ pub trait ManageNetwork : Send + Sync {
|
||||
}
|
||||
|
||||
|
||||
#[derive(Ipc)]
|
||||
#[ipc(client_ident="NetworkManagerClient")]
|
||||
impl ManageNetwork for EthSync {
|
||||
fn accept_unreserved_peers(&self) {
|
||||
self.network.set_non_reserved_mode(NonReservedPeerMode::Accept);
|
||||
|
||||
@@ -163,7 +163,7 @@ pub struct SyncStatus {
|
||||
/// Syncing protocol version. That's the maximum protocol version we connect to.
|
||||
pub protocol_version: u8,
|
||||
/// The underlying p2p network version.
|
||||
pub network_id: U256,
|
||||
pub network_id: usize,
|
||||
/// `BlockChain` height for the moment the sync started.
|
||||
pub start_block_number: BlockNumber,
|
||||
/// Last fully downloaded and imported block number (if any).
|
||||
@@ -226,7 +226,7 @@ struct PeerInfo {
|
||||
/// Peer chain genesis hash
|
||||
genesis: H256,
|
||||
/// Peer network id
|
||||
network_id: U256,
|
||||
network_id: usize,
|
||||
/// Peer best block hash
|
||||
latest_hash: H256,
|
||||
/// Peer total difficulty if known
|
||||
@@ -285,7 +285,7 @@ pub struct ChainSync {
|
||||
/// Block parents imported this round (hash, parent)
|
||||
round_parents: VecDeque<(H256, H256)>,
|
||||
/// Network ID
|
||||
network_id: U256,
|
||||
network_id: usize,
|
||||
/// Optional fork block to check
|
||||
fork_block: Option<(BlockNumber, H256)>,
|
||||
}
|
||||
@@ -562,12 +562,7 @@ impl ChainSync {
|
||||
}
|
||||
match self.state {
|
||||
SyncState::ChainHead => {
|
||||
if headers.is_empty() {
|
||||
// peer is not on our chain
|
||||
// track back and try again
|
||||
self.imported_this_round = Some(0);
|
||||
self.start_sync_round(io);
|
||||
} else {
|
||||
if !headers.is_empty() {
|
||||
// TODO: validate heads better. E.g. check that there is enough distance between blocks.
|
||||
trace!(target: "sync", "Received {} subchain heads, proceeding to download", headers.len());
|
||||
self.blocks.reset_to(hashes);
|
||||
@@ -1742,7 +1737,7 @@ mod tests {
|
||||
PeerInfo {
|
||||
protocol_version: 0,
|
||||
genesis: H256::zero(),
|
||||
network_id: U256::zero(),
|
||||
network_id: 0,
|
||||
latest_hash: peer_latest_hash,
|
||||
difficulty: None,
|
||||
asking: PeerAsking::Nothing,
|
||||
|
||||
@@ -91,7 +91,7 @@ mod api {
|
||||
include!(concat!(env!("OUT_DIR"), "/api.rs"));
|
||||
}
|
||||
|
||||
pub use api::{EthSync, SyncProvider, SyncClient, NetworkManagerClient, ManageNetwork, SyncConfig,
|
||||
pub use api::{EthSync, SyncProvider, ManageNetwork, SyncConfig,
|
||||
ServiceConfiguration, NetworkConfiguration};
|
||||
pub use chain::{SyncStatus, SyncState};
|
||||
pub use network::{is_valid_node_url, NonReservedPeerMode, NetworkError};
|
||||
|
||||
4
test.sh
4
test.sh
@@ -1,11 +1,11 @@
|
||||
#!/bin/sh
|
||||
# Running Parity Full Test Sute
|
||||
|
||||
FEATURES="json-tests ipc"
|
||||
FEATURES="json-tests"
|
||||
|
||||
case $1 in
|
||||
--no-json)
|
||||
FEATURES="ipc"
|
||||
FEATURES=""
|
||||
shift # past argument=value
|
||||
;;
|
||||
*)
|
||||
|
||||
@@ -3,7 +3,7 @@ description = "Ethcore utility library"
|
||||
homepage = "http://ethcore.io"
|
||||
license = "GPL-3.0"
|
||||
name = "ethcore-util"
|
||||
version = "1.3.7"
|
||||
version = "1.3.11"
|
||||
authors = ["Ethcore <admin@ethcore.io>"]
|
||||
build = "build.rs"
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// 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 std::sync::{Arc, Weak};
|
||||
use std::thread::{self, JoinHandle};
|
||||
use std::collections::HashMap;
|
||||
use mio::*;
|
||||
@@ -75,12 +75,12 @@ pub enum IoMessage<Message> where Message: Send + Clone + Sized {
|
||||
}
|
||||
|
||||
/// IO access point. This is passed to all IO handlers and provides an interface to the IO subsystem.
|
||||
pub struct IoContext<Message> where Message: Send + Clone + 'static {
|
||||
pub struct IoContext<Message> where Message: Send + Clone + Sync + 'static {
|
||||
channel: IoChannel<Message>,
|
||||
handler: HandlerId,
|
||||
}
|
||||
|
||||
impl<Message> IoContext<Message> where Message: Send + Clone + 'static {
|
||||
impl<Message> IoContext<Message> where Message: Send + Clone + Sync + 'static {
|
||||
/// Create a new IO access point. Takes references to all the data that can be updated within the IO handler.
|
||||
pub fn new(channel: IoChannel<Message>, handler: HandlerId) -> IoContext<Message> {
|
||||
IoContext {
|
||||
@@ -165,7 +165,7 @@ struct UserTimer {
|
||||
/// Root IO handler. Manages user handlers, messages and IO timers.
|
||||
pub struct IoManager<Message> where Message: Send + Sync {
|
||||
timers: Arc<RwLock<HashMap<HandlerId, UserTimer>>>,
|
||||
handlers: Slab<Arc<IoHandler<Message>>, HandlerId>,
|
||||
handlers: Arc<RwLock<Slab<Arc<IoHandler<Message>>, HandlerId>>>,
|
||||
workers: Vec<Worker>,
|
||||
worker_channel: chase_lev::Worker<Work<Message>>,
|
||||
work_ready: Arc<SCondvar>,
|
||||
@@ -173,7 +173,11 @@ pub struct IoManager<Message> where Message: Send + Sync {
|
||||
|
||||
impl<Message> IoManager<Message> where Message: Send + Sync + Clone + 'static {
|
||||
/// Creates a new instance and registers it with the event loop.
|
||||
pub fn start(panic_handler: Arc<PanicHandler>, event_loop: &mut EventLoop<IoManager<Message>>) -> Result<(), IoError> {
|
||||
pub fn start(
|
||||
panic_handler: Arc<PanicHandler>,
|
||||
event_loop: &mut EventLoop<IoManager<Message>>,
|
||||
handlers: Arc<RwLock<Slab<Arc<IoHandler<Message>>, HandlerId>>>
|
||||
) -> Result<(), IoError> {
|
||||
let (worker, stealer) = chase_lev::deque();
|
||||
let num_workers = 4;
|
||||
let work_ready_mutex = Arc::new(SMutex::new(()));
|
||||
@@ -182,7 +186,7 @@ impl<Message> IoManager<Message> where Message: Send + Sync + Clone + 'static {
|
||||
Worker::new(
|
||||
i,
|
||||
stealer.clone(),
|
||||
IoChannel::new(event_loop.channel()),
|
||||
IoChannel::new(event_loop.channel(), Arc::downgrade(&handlers)),
|
||||
work_ready.clone(),
|
||||
work_ready_mutex.clone(),
|
||||
panic_handler.clone(),
|
||||
@@ -191,7 +195,7 @@ impl<Message> IoManager<Message> where Message: Send + Sync + Clone + 'static {
|
||||
|
||||
let mut io = IoManager {
|
||||
timers: Arc::new(RwLock::new(HashMap::new())),
|
||||
handlers: Slab::new(MAX_HANDLERS),
|
||||
handlers: handlers,
|
||||
worker_channel: worker,
|
||||
workers: workers,
|
||||
work_ready: work_ready,
|
||||
@@ -208,7 +212,7 @@ impl<Message> Handler for IoManager<Message> where Message: Send + Clone + Sync
|
||||
fn ready(&mut self, _event_loop: &mut EventLoop<Self>, token: Token, events: EventSet) {
|
||||
let handler_index = token.as_usize() / TOKENS_PER_HANDLER;
|
||||
let token_id = token.as_usize() % TOKENS_PER_HANDLER;
|
||||
if let Some(handler) = self.handlers.get(handler_index) {
|
||||
if let Some(handler) = self.handlers.read().get(handler_index) {
|
||||
if events.is_hup() {
|
||||
self.worker_channel.push(Work { work_type: WorkType::Hup, token: token_id, handler: handler.clone(), handler_id: handler_index });
|
||||
}
|
||||
@@ -227,7 +231,7 @@ impl<Message> Handler for IoManager<Message> where Message: Send + Clone + Sync
|
||||
fn timeout(&mut self, event_loop: &mut EventLoop<Self>, token: Token) {
|
||||
let handler_index = token.as_usize() / TOKENS_PER_HANDLER;
|
||||
let token_id = token.as_usize() % TOKENS_PER_HANDLER;
|
||||
if let Some(handler) = self.handlers.get(handler_index) {
|
||||
if let Some(handler) = self.handlers.read().get(handler_index) {
|
||||
if let Some(timer) = self.timers.read().get(&token.as_usize()) {
|
||||
event_loop.timeout_ms(token, timer.delay).expect("Error re-registering user timer");
|
||||
self.worker_channel.push(Work { work_type: WorkType::Timeout, token: token_id, handler: handler.clone(), handler_id: handler_index });
|
||||
@@ -243,12 +247,12 @@ impl<Message> Handler for IoManager<Message> where Message: Send + Clone + Sync
|
||||
event_loop.shutdown();
|
||||
},
|
||||
IoMessage::AddHandler { handler } => {
|
||||
let handler_id = self.handlers.insert(handler.clone()).unwrap_or_else(|_| panic!("Too many handlers registered"));
|
||||
handler.initialize(&IoContext::new(IoChannel::new(event_loop.channel()), handler_id));
|
||||
let handler_id = self.handlers.write().insert(handler.clone()).unwrap_or_else(|_| panic!("Too many handlers registered"));
|
||||
handler.initialize(&IoContext::new(IoChannel::new(event_loop.channel(), Arc::downgrade(&self.handlers)), handler_id));
|
||||
},
|
||||
IoMessage::RemoveHandler { handler_id } => {
|
||||
// TODO: flush event loop
|
||||
self.handlers.remove(handler_id);
|
||||
self.handlers.write().remove(handler_id);
|
||||
// unregister timers
|
||||
let mut timers = self.timers.write();
|
||||
let to_remove: Vec<_> = timers.keys().cloned().filter(|timer_id| timer_id / TOKENS_PER_HANDLER == handler_id).collect();
|
||||
@@ -269,12 +273,12 @@ impl<Message> Handler for IoManager<Message> where Message: Send + Clone + Sync
|
||||
}
|
||||
},
|
||||
IoMessage::RegisterStream { handler_id, token } => {
|
||||
if let Some(handler) = self.handlers.get(handler_id) {
|
||||
if let Some(handler) = self.handlers.read().get(handler_id) {
|
||||
handler.register_stream(token, Token(token + handler_id * TOKENS_PER_HANDLER), event_loop);
|
||||
}
|
||||
},
|
||||
IoMessage::DeregisterStream { handler_id, token } => {
|
||||
if let Some(handler) = self.handlers.get(handler_id) {
|
||||
if let Some(handler) = self.handlers.read().get(handler_id) {
|
||||
handler.deregister_stream(token, event_loop);
|
||||
// unregister a timer associated with the token (if any)
|
||||
let timer_id = token + handler_id * TOKENS_PER_HANDLER;
|
||||
@@ -284,14 +288,14 @@ impl<Message> Handler for IoManager<Message> where Message: Send + Clone + Sync
|
||||
}
|
||||
},
|
||||
IoMessage::UpdateStreamRegistration { handler_id, token } => {
|
||||
if let Some(handler) = self.handlers.get(handler_id) {
|
||||
if let Some(handler) = self.handlers.read().get(handler_id) {
|
||||
handler.update_stream(token, Token(token + handler_id * TOKENS_PER_HANDLER), event_loop);
|
||||
}
|
||||
},
|
||||
IoMessage::UserMessage(data) => {
|
||||
//TODO: better way to iterate the slab
|
||||
for id in 0 .. MAX_HANDLERS {
|
||||
if let Some(h) = self.handlers.get(id) {
|
||||
if let Some(h) = self.handlers.read().get(id) {
|
||||
let handler = h.clone();
|
||||
self.worker_channel.push(Work { work_type: WorkType::Message(data.clone()), token: 0, handler: handler, handler_id: id });
|
||||
}
|
||||
@@ -305,19 +309,21 @@ impl<Message> Handler for IoManager<Message> where Message: Send + Clone + Sync
|
||||
/// Allows sending messages into the event loop. All the IO handlers will get the message
|
||||
/// in the `message` callback.
|
||||
pub struct IoChannel<Message> where Message: Send + Clone{
|
||||
channel: Option<Sender<IoMessage<Message>>>
|
||||
channel: Option<Sender<IoMessage<Message>>>,
|
||||
handlers: Weak<RwLock<Slab<Arc<IoHandler<Message>>, HandlerId>>>,
|
||||
}
|
||||
|
||||
impl<Message> Clone for IoChannel<Message> where Message: Send + Clone {
|
||||
impl<Message> Clone for IoChannel<Message> where Message: Send + Clone + Sync + 'static {
|
||||
fn clone(&self) -> IoChannel<Message> {
|
||||
IoChannel {
|
||||
channel: self.channel.clone()
|
||||
channel: self.channel.clone(),
|
||||
handlers: self.handlers.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Message> IoChannel<Message> where Message: Send + Clone {
|
||||
/// Send a msessage through the channel
|
||||
impl<Message> IoChannel<Message> where Message: Send + Clone + Sync + 'static {
|
||||
/// Send a message through the channel
|
||||
pub fn send(&self, message: Message) -> Result<(), IoError> {
|
||||
if let Some(ref channel) = self.channel {
|
||||
try!(channel.send(IoMessage::UserMessage(message)));
|
||||
@@ -325,6 +331,19 @@ impl<Message> IoChannel<Message> where Message: Send + Clone {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send a message through the channel and handle it synchronously
|
||||
pub fn send_sync(&self, message: Message) -> Result<(), IoError> {
|
||||
if let Some(handlers) = self.handlers.upgrade() {
|
||||
for id in 0 .. MAX_HANDLERS {
|
||||
if let Some(h) = handlers.read().get(id) {
|
||||
let handler = h.clone();
|
||||
handler.message(&IoContext::new(self.clone(), id), &message);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send low level io message
|
||||
pub fn send_io(&self, message: IoMessage<Message>) -> Result<(), IoError> {
|
||||
if let Some(ref channel) = self.channel {
|
||||
@@ -334,11 +353,17 @@ impl<Message> IoChannel<Message> where Message: Send + Clone {
|
||||
}
|
||||
/// Create a new channel to connected to event loop.
|
||||
pub fn disconnected() -> IoChannel<Message> {
|
||||
IoChannel { channel: None }
|
||||
IoChannel {
|
||||
channel: None,
|
||||
handlers: Weak::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn new(channel: Sender<IoMessage<Message>>) -> IoChannel<Message> {
|
||||
IoChannel { channel: Some(channel) }
|
||||
fn new(channel: Sender<IoMessage<Message>>, handlers: Weak<RwLock<Slab<Arc<IoHandler<Message>>, HandlerId>>>) -> IoChannel<Message> {
|
||||
IoChannel {
|
||||
channel: Some(channel),
|
||||
handlers: handlers,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,6 +373,7 @@ pub struct IoService<Message> where Message: Send + Sync + Clone + 'static {
|
||||
panic_handler: Arc<PanicHandler>,
|
||||
thread: Option<JoinHandle<()>>,
|
||||
host_channel: Sender<IoMessage<Message>>,
|
||||
handlers: Arc<RwLock<Slab<Arc<IoHandler<Message>>, HandlerId>>>,
|
||||
}
|
||||
|
||||
impl<Message> MayPanic for IoService<Message> where Message: Send + Sync + Clone + 'static {
|
||||
@@ -365,16 +391,19 @@ impl<Message> IoService<Message> where Message: Send + Sync + Clone + 'static {
|
||||
let mut event_loop = EventLoop::configured(config).expect("Error creating event loop");
|
||||
let channel = event_loop.channel();
|
||||
let panic = panic_handler.clone();
|
||||
let handlers = Arc::new(RwLock::new(Slab::new(MAX_HANDLERS)));
|
||||
let h = handlers.clone();
|
||||
let thread = thread::spawn(move || {
|
||||
let p = panic.clone();
|
||||
panic.catch_panic(move || {
|
||||
IoManager::<Message>::start(p, &mut event_loop).unwrap();
|
||||
IoManager::<Message>::start(p, &mut event_loop, h).unwrap();
|
||||
}).unwrap()
|
||||
});
|
||||
Ok(IoService {
|
||||
panic_handler: panic_handler,
|
||||
thread: Some(thread),
|
||||
host_channel: channel
|
||||
host_channel: channel,
|
||||
handlers: handlers,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -394,7 +423,7 @@ impl<Message> IoService<Message> where Message: Send + Sync + Clone + 'static {
|
||||
|
||||
/// Create a new message channel
|
||||
pub fn channel(&self) -> IoChannel<Message> {
|
||||
IoChannel { channel: Some(self.host_channel.clone()) }
|
||||
IoChannel::new(self.host_channel.clone(), Arc::downgrade(&self.handlers))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ impl<Socket: GenericSocket> GenericConnection<Socket> {
|
||||
}
|
||||
|
||||
/// Add a packet to send queue.
|
||||
pub fn send<Message>(&mut self, io: &IoContext<Message>, data: Bytes) where Message: Send + Clone {
|
||||
pub fn send<Message>(&mut self, io: &IoContext<Message>, data: Bytes) where Message: Send + Clone + Sync + 'static {
|
||||
if !data.is_empty() {
|
||||
self.send_queue.push_back(Cursor::new(data));
|
||||
}
|
||||
@@ -120,7 +120,7 @@ impl<Socket: GenericSocket> GenericConnection<Socket> {
|
||||
}
|
||||
|
||||
/// Writable IO handler. Called when the socket is ready to send.
|
||||
pub fn writable<Message>(&mut self, io: &IoContext<Message>) -> Result<WriteStatus, NetworkError> where Message: Send + Clone {
|
||||
pub fn writable<Message>(&mut self, io: &IoContext<Message>) -> Result<WriteStatus, NetworkError> where Message: Send + Clone + Sync + 'static {
|
||||
if self.send_queue.is_empty() {
|
||||
return Ok(WriteStatus::Complete)
|
||||
}
|
||||
@@ -340,7 +340,7 @@ impl EncryptedConnection {
|
||||
}
|
||||
|
||||
/// Send a packet
|
||||
pub fn send_packet<Message>(&mut self, io: &IoContext<Message>, payload: &[u8]) -> Result<(), NetworkError> where Message: Send + Clone {
|
||||
pub fn send_packet<Message>(&mut self, io: &IoContext<Message>, payload: &[u8]) -> Result<(), NetworkError> where Message: Send + Clone + Sync + 'static {
|
||||
let mut header = RlpStream::new();
|
||||
let len = payload.len() as usize;
|
||||
header.append_raw(&[(len >> 16) as u8, (len >> 8) as u8, len as u8], 1);
|
||||
@@ -435,7 +435,7 @@ impl EncryptedConnection {
|
||||
}
|
||||
|
||||
/// Readable IO handler. Tracker receive status and returns decoded packet if avaialable.
|
||||
pub fn readable<Message>(&mut self, io: &IoContext<Message>) -> Result<Option<Packet>, NetworkError> where Message: Send + Clone{
|
||||
pub fn readable<Message>(&mut self, io: &IoContext<Message>) -> Result<Option<Packet>, NetworkError> where Message: Send + Clone + Sync + 'static {
|
||||
try!(io.clear_timer(self.connection.token));
|
||||
if let EncryptedConnectionState::Header = self.read_state {
|
||||
if let Some(data) = try!(self.connection.readable()) {
|
||||
@@ -458,7 +458,7 @@ impl EncryptedConnection {
|
||||
}
|
||||
|
||||
/// Writable IO handler. Processes send queeue.
|
||||
pub fn writable<Message>(&mut self, io: &IoContext<Message>) -> Result<(), NetworkError> where Message: Send + Clone {
|
||||
pub fn writable<Message>(&mut self, io: &IoContext<Message>) -> Result<(), NetworkError> where Message: Send + Clone + Sync + 'static {
|
||||
try!(self.connection.writable(io));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ impl Handshake {
|
||||
}
|
||||
|
||||
/// Start a handhsake
|
||||
pub fn start<Message>(&mut self, io: &IoContext<Message>, host: &HostInfo, originated: bool) -> Result<(), NetworkError> where Message: Send + Clone{
|
||||
pub fn start<Message>(&mut self, io: &IoContext<Message>, host: &HostInfo, originated: bool) -> Result<(), NetworkError> where Message: Send + Clone+ Sync + 'static {
|
||||
self.originated = originated;
|
||||
io.register_timer(self.connection.token, HANDSHAKE_TIMEOUT).ok();
|
||||
if originated {
|
||||
@@ -125,7 +125,7 @@ impl Handshake {
|
||||
}
|
||||
|
||||
/// Readable IO handler. Drives the state change.
|
||||
pub fn readable<Message>(&mut self, io: &IoContext<Message>, host: &HostInfo) -> Result<(), NetworkError> where Message: Send + Clone {
|
||||
pub fn readable<Message>(&mut self, io: &IoContext<Message>, host: &HostInfo) -> Result<(), NetworkError> where Message: Send + Clone + Sync + 'static {
|
||||
if !self.expired() {
|
||||
while let Some(data) = try!(self.connection.readable()) {
|
||||
match self.state {
|
||||
@@ -154,7 +154,7 @@ impl Handshake {
|
||||
}
|
||||
|
||||
/// Writabe IO handler.
|
||||
pub fn writable<Message>(&mut self, io: &IoContext<Message>) -> Result<(), NetworkError> where Message: Send + Clone {
|
||||
pub fn writable<Message>(&mut self, io: &IoContext<Message>) -> Result<(), NetworkError> where Message: Send + Clone + Sync + 'static {
|
||||
if !self.expired() {
|
||||
try!(self.connection.writable(io));
|
||||
}
|
||||
@@ -172,7 +172,7 @@ impl Handshake {
|
||||
}
|
||||
|
||||
/// Parse, validate and confirm auth message
|
||||
fn read_auth<Message>(&mut self, io: &IoContext<Message>, secret: &Secret, data: &[u8]) -> Result<(), NetworkError> where Message: Send + Clone {
|
||||
fn read_auth<Message>(&mut self, io: &IoContext<Message>, secret: &Secret, data: &[u8]) -> Result<(), NetworkError> where Message: Send + Clone + Sync + 'static {
|
||||
trace!(target: "network", "Received handshake auth from {:?}", self.connection.remote_addr_str());
|
||||
if data.len() != V4_AUTH_PACKET_SIZE {
|
||||
debug!(target: "network", "Wrong auth packet size");
|
||||
@@ -203,7 +203,7 @@ impl Handshake {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_auth_eip8<Message>(&mut self, io: &IoContext<Message>, secret: &Secret, data: &[u8]) -> Result<(), NetworkError> where Message: Send + Clone {
|
||||
fn read_auth_eip8<Message>(&mut self, io: &IoContext<Message>, secret: &Secret, data: &[u8]) -> Result<(), NetworkError> where Message: Send + Clone + Sync + 'static {
|
||||
trace!(target: "network", "Received EIP8 handshake auth from {:?}", self.connection.remote_addr_str());
|
||||
self.auth_cipher.extend_from_slice(data);
|
||||
let auth = try!(ecies::decrypt(secret, &self.auth_cipher[0..2], &self.auth_cipher[2..]));
|
||||
@@ -259,7 +259,7 @@ impl Handshake {
|
||||
}
|
||||
|
||||
/// Sends auth message
|
||||
fn write_auth<Message>(&mut self, io: &IoContext<Message>, secret: &Secret, public: &Public) -> Result<(), NetworkError> where Message: Send + Clone {
|
||||
fn write_auth<Message>(&mut self, io: &IoContext<Message>, secret: &Secret, public: &Public) -> Result<(), NetworkError> where Message: Send + Clone + Sync + 'static {
|
||||
trace!(target: "network", "Sending handshake auth to {:?}", self.connection.remote_addr_str());
|
||||
let mut data = [0u8; /*Signature::SIZE*/ 65 + /*H256::SIZE*/ 32 + /*Public::SIZE*/ 64 + /*H256::SIZE*/ 32 + 1]; //TODO: use associated constants
|
||||
let len = data.len();
|
||||
@@ -286,7 +286,7 @@ impl Handshake {
|
||||
}
|
||||
|
||||
/// Sends ack message
|
||||
fn write_ack<Message>(&mut self, io: &IoContext<Message>) -> Result<(), NetworkError> where Message: Send + Clone {
|
||||
fn write_ack<Message>(&mut self, io: &IoContext<Message>) -> Result<(), NetworkError> where Message: Send + Clone + Sync + 'static {
|
||||
trace!(target: "network", "Sending handshake ack to {:?}", self.connection.remote_addr_str());
|
||||
let mut data = [0u8; 1 + /*Public::SIZE*/ 64 + /*H256::SIZE*/ 32]; //TODO: use associated constants
|
||||
let len = data.len();
|
||||
@@ -305,7 +305,7 @@ impl Handshake {
|
||||
}
|
||||
|
||||
/// Sends EIP8 ack message
|
||||
fn write_ack_eip8<Message>(&mut self, io: &IoContext<Message>) -> Result<(), NetworkError> where Message: Send + Clone {
|
||||
fn write_ack_eip8<Message>(&mut self, io: &IoContext<Message>) -> Result<(), NetworkError> where Message: Send + Clone + Sync + 'static {
|
||||
trace!(target: "network", "Sending EIP8 handshake ack to {:?}", self.connection.remote_addr_str());
|
||||
let mut rlp = RlpStream::new_list(3);
|
||||
rlp.append(self.ecdhe.public());
|
||||
|
||||
@@ -125,7 +125,7 @@ impl Session {
|
||||
/// and leaves the handhsake in limbo to be deregistered from the event loop.
|
||||
pub fn new<Message>(io: &IoContext<Message>, socket: TcpStream, token: StreamToken, id: Option<&NodeId>,
|
||||
nonce: &H256, stats: Arc<NetworkStats>, host: &HostInfo) -> Result<Session, NetworkError>
|
||||
where Message: Send + Clone {
|
||||
where Message: Send + Clone + Sync + 'static {
|
||||
let originated = id.is_some();
|
||||
let mut handshake = Handshake::new(token, id, socket, nonce, stats).expect("Can't create handshake");
|
||||
try!(handshake.start(io, host, originated));
|
||||
|
||||
@@ -21,7 +21,7 @@ use elastic_array::*;
|
||||
use std::default::Default;
|
||||
use rlp::{UntrustedRlp, RlpType, View, Compressible};
|
||||
use rocksdb::{DB, Writable, WriteBatch, WriteOptions, IteratorMode, DBIterator,
|
||||
Options, DBCompactionStyle, BlockBasedOptions, Direction, Cache, Column};
|
||||
Options, DBCompactionStyle, BlockBasedOptions, Direction, Cache, Column, ReadOptions};
|
||||
|
||||
const DB_BACKGROUND_FLUSHES: i32 = 2;
|
||||
const DB_BACKGROUND_COMPACTIONS: i32 = 2;
|
||||
@@ -198,7 +198,14 @@ pub struct Database {
|
||||
db: DB,
|
||||
write_opts: WriteOptions,
|
||||
cfs: Vec<Column>,
|
||||
read_opts: ReadOptions,
|
||||
// Dirty values added with `write_buffered`. Cleaned on `flush`.
|
||||
overlay: RwLock<Vec<HashMap<ElasticArray32<u8>, KeyState>>>,
|
||||
// Values currently being flushed. Cleared when `flush` completes.
|
||||
flushing: RwLock<Vec<HashMap<ElasticArray32<u8>, KeyState>>>,
|
||||
// Prevents concurrent flushes.
|
||||
// Value indicates if a flush is in progress.
|
||||
flushing_lock: Mutex<bool>,
|
||||
}
|
||||
|
||||
impl Database {
|
||||
@@ -213,7 +220,8 @@ impl Database {
|
||||
if let Some(rate_limit) = config.compaction.write_rate_limit {
|
||||
try!(opts.set_parsed_options(&format!("rate_limiter_bytes_per_sec={}", rate_limit)));
|
||||
}
|
||||
try!(opts.set_parsed_options(&format!("max_total_wal_size={}", 256 * 1024 * 1024)));
|
||||
try!(opts.set_parsed_options(&format!("max_total_wal_size={}", 64 * 1024 * 1024)));
|
||||
try!(opts.set_parsed_options("verify_checksums_in_compaction=0"));
|
||||
opts.set_max_open_files(config.max_open_files);
|
||||
opts.create_if_missing(true);
|
||||
opts.set_use_fsync(false);
|
||||
@@ -246,6 +254,8 @@ impl Database {
|
||||
if !config.wal {
|
||||
write_opts.disable_wal(true);
|
||||
}
|
||||
let mut read_opts = ReadOptions::new();
|
||||
read_opts.set_verify_checksums(false);
|
||||
|
||||
let mut cfs: Vec<Column> = Vec::new();
|
||||
let db = match config.columns {
|
||||
@@ -286,7 +296,10 @@ impl Database {
|
||||
db: db,
|
||||
write_opts: write_opts,
|
||||
overlay: RwLock::new((0..(cfs.len() + 1)).map(|_| HashMap::new()).collect()),
|
||||
flushing: RwLock::new((0..(cfs.len() + 1)).map(|_| HashMap::new()).collect()),
|
||||
cfs: cfs,
|
||||
flushing_lock: Mutex::new(false),
|
||||
read_opts: read_opts,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -323,15 +336,12 @@ impl Database {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Commit buffered changes to database.
|
||||
pub fn flush(&self) -> Result<(), String> {
|
||||
fn write_flushing_with_lock(&self, _lock: &mut MutexGuard<bool>) -> Result<(), String> {
|
||||
mem::swap(&mut *self.overlay.write(), &mut *self.flushing.write());
|
||||
let batch = WriteBatch::new();
|
||||
let mut overlay = self.overlay.write();
|
||||
|
||||
for (c, column) in overlay.iter_mut().enumerate() {
|
||||
let column_data = mem::replace(column, HashMap::new());
|
||||
for (key, state) in column_data.into_iter() {
|
||||
match state {
|
||||
for (c, column) in self.flushing.read().iter().enumerate() {
|
||||
for (key, state) in column.iter() {
|
||||
match *state {
|
||||
KeyState::Delete => {
|
||||
if c > 0 {
|
||||
try!(batch.delete_cf(self.cfs[c - 1], &key));
|
||||
@@ -339,14 +349,14 @@ impl Database {
|
||||
try!(batch.delete(&key));
|
||||
}
|
||||
},
|
||||
KeyState::Insert(value) => {
|
||||
KeyState::Insert(ref value) => {
|
||||
if c > 0 {
|
||||
try!(batch.put_cf(self.cfs[c - 1], &key, &value));
|
||||
} else {
|
||||
try!(batch.put(&key, &value));
|
||||
}
|
||||
},
|
||||
KeyState::InsertCompressed(value) => {
|
||||
KeyState::InsertCompressed(ref value) => {
|
||||
let compressed = UntrustedRlp::new(&value).compress(RlpType::Blocks);
|
||||
if c > 0 {
|
||||
try!(batch.put_cf(self.cfs[c - 1], &key, &compressed));
|
||||
@@ -357,9 +367,27 @@ impl Database {
|
||||
}
|
||||
}
|
||||
}
|
||||
self.db.write_opt(batch, &self.write_opts)
|
||||
try!(self.db.write_opt(batch, &self.write_opts));
|
||||
for column in self.flushing.write().iter_mut() {
|
||||
column.clear();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Commit buffered changes to database.
|
||||
pub fn flush(&self) -> Result<(), String> {
|
||||
let mut lock = self.flushing_lock.lock();
|
||||
// If RocksDB batch allocation fails the thread gets terminated and the lock is released.
|
||||
// The value inside the lock is used to detect that.
|
||||
if *lock {
|
||||
// This can only happen if another flushing thread is terminated unexpectedly.
|
||||
return Err("Database write failure. Running low on memory perhaps?".to_owned());
|
||||
}
|
||||
*lock = true;
|
||||
let result = self.write_flushing_with_lock(&mut lock);
|
||||
*lock = false;
|
||||
result
|
||||
}
|
||||
|
||||
/// Commit transaction to database.
|
||||
pub fn write(&self, tr: DBTransaction) -> Result<(), String> {
|
||||
@@ -389,9 +417,16 @@ impl Database {
|
||||
Some(&KeyState::Insert(ref value)) | Some(&KeyState::InsertCompressed(ref value)) => Ok(Some(value.clone())),
|
||||
Some(&KeyState::Delete) => Ok(None),
|
||||
None => {
|
||||
col.map_or_else(
|
||||
|| self.db.get(key).map(|r| r.map(|v| v.to_vec())),
|
||||
|c| self.db.get_cf(self.cfs[c as usize], key).map(|r| r.map(|v| v.to_vec())))
|
||||
let flushing = &self.flushing.read()[Self::to_overlay_column(col)];
|
||||
match flushing.get(key) {
|
||||
Some(&KeyState::Insert(ref value)) | Some(&KeyState::InsertCompressed(ref value)) => Ok(Some(value.clone())),
|
||||
Some(&KeyState::Delete) => Ok(None),
|
||||
None => {
|
||||
col.map_or_else(
|
||||
|| self.db.get_opt(key, &self.read_opts).map(|r| r.map(|v| v.to_vec())),
|
||||
|c| self.db.get_cf_opt(self.cfs[c as usize], key, &self.read_opts).map(|r| r.map(|v| v.to_vec())))
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -399,8 +434,8 @@ impl Database {
|
||||
/// Get value by partial key. Prefix size should match configured prefix size. Only searches flushed values.
|
||||
// TODO: support prefix seek for unflushed ata
|
||||
pub fn get_by_prefix(&self, col: Option<u32>, prefix: &[u8]) -> Option<Box<[u8]>> {
|
||||
let mut iter = col.map_or_else(|| self.db.iterator(IteratorMode::From(prefix, Direction::Forward)),
|
||||
|c| self.db.iterator_cf(self.cfs[c as usize], IteratorMode::From(prefix, Direction::Forward)).unwrap());
|
||||
let mut iter = col.map_or_else(|| self.db.iterator_opt(IteratorMode::From(prefix, Direction::Forward), &self.read_opts),
|
||||
|c| self.db.iterator_cf_opt(self.cfs[c as usize], IteratorMode::From(prefix, Direction::Forward), &self.read_opts).unwrap());
|
||||
match iter.next() {
|
||||
// TODO: use prefix_same_as_start read option (not availabele in C API currently)
|
||||
Some((k, v)) => if k[0 .. prefix.len()] == prefix[..] { Some(v) } else { None },
|
||||
@@ -411,8 +446,8 @@ impl Database {
|
||||
/// Get database iterator for flushed data.
|
||||
pub fn iter(&self, col: Option<u32>) -> DatabaseIterator {
|
||||
//TODO: iterate over overlay
|
||||
col.map_or_else(|| DatabaseIterator { iter: self.db.iterator(IteratorMode::Start) },
|
||||
|c| DatabaseIterator { iter: self.db.iterator_cf(self.cfs[c as usize], IteratorMode::Start).unwrap() })
|
||||
col.map_or_else(|| DatabaseIterator { iter: self.db.iterator_opt(IteratorMode::Start, &self.read_opts) },
|
||||
|c| DatabaseIterator { iter: self.db.iterator_cf_opt(self.cfs[c as usize], IteratorMode::Start, &self.read_opts).unwrap() })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ mod tests;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use ::kvdb::{CompactionProfile, Database, DatabaseConfig, DBTransaction};
|
||||
@@ -96,6 +97,17 @@ pub enum Error {
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
Error::CannotAddMigration => write!(f, "Cannot add migration"),
|
||||
Error::MigrationImpossible => write!(f, "Migration impossible"),
|
||||
Error::Io(ref err) => write!(f, "{}", err),
|
||||
Error::Custom(ref err) => write!(f, "{}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<::std::io::Error> for Error {
|
||||
fn from(e: ::std::io::Error) -> Self {
|
||||
Error::Io(e)
|
||||
@@ -110,6 +122,8 @@ impl From<String> for Error {
|
||||
|
||||
/// A generalized migration from the given db to a destination db.
|
||||
pub trait Migration: 'static {
|
||||
/// Number of columns in the database before the migration.
|
||||
fn pre_columns(&self) -> Option<u32> { self.columns() }
|
||||
/// Number of columns in database after the migration.
|
||||
fn columns(&self) -> Option<u32>;
|
||||
/// Version of the database after the migration.
|
||||
@@ -201,6 +215,7 @@ impl Manager {
|
||||
Some(last) => migration.version() > last.version(),
|
||||
None => true,
|
||||
};
|
||||
|
||||
match is_new {
|
||||
true => Ok(self.migrations.push(Box::new(migration))),
|
||||
false => Err(Error::CannotAddMigration),
|
||||
@@ -211,9 +226,11 @@ impl Manager {
|
||||
/// and producing a path where the final migration lives.
|
||||
pub fn execute(&mut self, old_path: &Path, version: u32) -> Result<PathBuf, Error> {
|
||||
let config = self.config.clone();
|
||||
let columns = self.no_of_columns_at(version);
|
||||
let migrations = self.migrations_from(version);
|
||||
if migrations.is_empty() { return Err(Error::MigrationImpossible) };
|
||||
|
||||
let columns = migrations.iter().find(|m| m.version() == version).and_then(|m| m.pre_columns());
|
||||
|
||||
let mut db_config = DatabaseConfig {
|
||||
max_open_files: 64,
|
||||
cache_size: None,
|
||||
@@ -231,6 +248,7 @@ impl Manager {
|
||||
let mut cur_db = try!(Database::open(&db_config, old_path_str).map_err(Error::Custom));
|
||||
|
||||
for migration in migrations {
|
||||
trace!(target: "migration", "starting migration to version {}", migration.version());
|
||||
// Change number of columns in new db
|
||||
let current_columns = db_config.columns;
|
||||
db_config.columns = migration.columns();
|
||||
@@ -273,14 +291,6 @@ impl Manager {
|
||||
fn migrations_from(&mut self, version: u32) -> Vec<&mut Box<Migration>> {
|
||||
self.migrations.iter_mut().filter(|m| m.version() > version).collect()
|
||||
}
|
||||
|
||||
fn no_of_columns_at(&self, version: u32) -> Option<u32> {
|
||||
let migration = self.migrations.iter().find(|m| m.version() == version);
|
||||
match migration {
|
||||
Some(m) => m.columns(),
|
||||
None => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Prints a dot every `max` ticks
|
||||
|
||||
@@ -49,7 +49,7 @@ pub fn version() -> String {
|
||||
let date_dash = if commit_date.is_empty() { "" } else { "-" };
|
||||
let env = Target::env();
|
||||
let env_dash = if env.is_empty() { "" } else { "-" };
|
||||
format!("Parity/v{}-beta{}{}{}{}/{}-{}{}{}/rustc{}", env!("CARGO_PKG_VERSION"), sha3_dash, sha3, date_dash, commit_date, Target::arch(), Target::os(), env_dash, env, rustc_version())
|
||||
format!("Parity/v{}-stable{}{}{}{}/{}-{}{}{}/rustc{}", env!("CARGO_PKG_VERSION"), sha3_dash, sha3, date_dash, commit_date, Target::arch(), Target::os(), env_dash, env, rustc_version())
|
||||
}
|
||||
|
||||
/// Get the standard version data for this software.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user