Merge remote-tracking branch 'parity/master' into auth-round-no-mocknet
This commit is contained in:
commit
aa05823afe
@ -121,7 +121,6 @@ linux-armv7:
|
|||||||
stage: build
|
stage: build
|
||||||
image: ethcore/rust-armv7:latest
|
image: ethcore/rust-armv7:latest
|
||||||
only:
|
only:
|
||||||
- master
|
|
||||||
- beta
|
- beta
|
||||||
- tags
|
- tags
|
||||||
- stable
|
- stable
|
||||||
@ -150,7 +149,6 @@ linux-arm:
|
|||||||
stage: build
|
stage: build
|
||||||
image: ethcore/rust-arm:latest
|
image: ethcore/rust-arm:latest
|
||||||
only:
|
only:
|
||||||
- master
|
|
||||||
- beta
|
- beta
|
||||||
- tags
|
- tags
|
||||||
- stable
|
- stable
|
||||||
@ -179,7 +177,6 @@ linux-armv6:
|
|||||||
stage: build
|
stage: build
|
||||||
image: ethcore/rust-armv6:latest
|
image: ethcore/rust-armv6:latest
|
||||||
only:
|
only:
|
||||||
- master
|
|
||||||
- beta
|
- beta
|
||||||
- tags
|
- tags
|
||||||
- stable
|
- stable
|
||||||
@ -208,7 +205,6 @@ linux-aarch64:
|
|||||||
stage: build
|
stage: build
|
||||||
image: ethcore/rust-aarch64:latest
|
image: ethcore/rust-aarch64:latest
|
||||||
only:
|
only:
|
||||||
- master
|
|
||||||
- beta
|
- beta
|
||||||
- tags
|
- tags
|
||||||
- stable
|
- stable
|
||||||
@ -266,13 +262,14 @@ windows:
|
|||||||
- set RUST_BACKTRACE=1
|
- set RUST_BACKTRACE=1
|
||||||
- set RUSTFLAGS=%RUSTFLAGS% -Zorbit=off -D warnings
|
- set RUSTFLAGS=%RUSTFLAGS% -Zorbit=off -D warnings
|
||||||
- rustup default stable-x86_64-pc-windows-msvc
|
- rustup default stable-x86_64-pc-windows-msvc
|
||||||
|
- git submodule update --init
|
||||||
- cargo build --release --verbose
|
- cargo build --release --verbose
|
||||||
- curl -sL --url "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -o nsis\SimpleFC.dll
|
- curl -sL --url "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -o nsis\SimpleFC.dll
|
||||||
- curl -sL --url "https://github.com/ethcore/win-build/raw/master/vc_redist.x64.exe" -o nsis\vc_redist.x64.exe
|
- curl -sL --url "https://github.com/ethcore/win-build/raw/master/vc_redist.x64.exe" -o nsis\vc_redist.x64.exe
|
||||||
- signtool sign /f %keyfile% /p %certpass% target\release\parity.exe
|
- signtool sign /f %keyfile% /p %certpass% target\release\parity.exe
|
||||||
- cd nsis
|
- cd nsis
|
||||||
- makensis.exe installer.nsi
|
- makensis.exe installer.nsi
|
||||||
- cp installer.exe InstallParity.exe
|
- copy installer.exe InstallParity.exe
|
||||||
- signtool sign /f %keyfile% /p %certpass% InstallParity.exe
|
- signtool sign /f %keyfile% /p %certpass% InstallParity.exe
|
||||||
- md5sums InstallParity.exe > InstallParity.exe.md5
|
- md5sums InstallParity.exe > InstallParity.exe.md5
|
||||||
- zip win-installer.zip InstallParity.exe InstallParity.exe.md5
|
- zip win-installer.zip InstallParity.exe InstallParity.exe.md5
|
||||||
@ -299,8 +296,9 @@ windows:
|
|||||||
paths:
|
paths:
|
||||||
- target/release/parity.exe
|
- target/release/parity.exe
|
||||||
- target/release/parity.pdb
|
- target/release/parity.pdb
|
||||||
- nsis/installer.exe
|
- nsis/InstallParity.exe
|
||||||
name: "x86_64-pc-windows-msvc_parity"
|
name: "x86_64-pc-windows-msvc_parity"
|
||||||
|
allow_failure: true
|
||||||
test-linux:
|
test-linux:
|
||||||
stage: test
|
stage: test
|
||||||
before_script:
|
before_script:
|
||||||
@ -312,3 +310,46 @@ test-linux:
|
|||||||
- rust-test
|
- rust-test
|
||||||
dependencies:
|
dependencies:
|
||||||
- linux-stable
|
- linux-stable
|
||||||
|
js-release:
|
||||||
|
stage: build
|
||||||
|
image: ethcore/javascript:latest
|
||||||
|
only:
|
||||||
|
- js
|
||||||
|
- master
|
||||||
|
- beta
|
||||||
|
- tags
|
||||||
|
- stable
|
||||||
|
before_script:
|
||||||
|
- ./js/scripts/install-deps.sh
|
||||||
|
script:
|
||||||
|
- ./js/scripts/build.sh
|
||||||
|
- ./js/scripts/release.sh
|
||||||
|
tags:
|
||||||
|
- javascript
|
||||||
|
js-lint:
|
||||||
|
stage: test
|
||||||
|
image: ethcore/javascript:latest
|
||||||
|
before_script:
|
||||||
|
- ./js/scripts/install-deps.sh
|
||||||
|
script:
|
||||||
|
- ./js/scripts/lint.sh
|
||||||
|
tags:
|
||||||
|
- javascript-test
|
||||||
|
js-test:
|
||||||
|
stage: test
|
||||||
|
image: ethcore/javascript:latest
|
||||||
|
before_script:
|
||||||
|
- ./js/scripts/install-deps.sh
|
||||||
|
script:
|
||||||
|
- ./js/scripts/test.sh
|
||||||
|
tags:
|
||||||
|
- javascript-test
|
||||||
|
js-pack:
|
||||||
|
stage: test
|
||||||
|
image: ethcore/javascript:latest
|
||||||
|
before_script:
|
||||||
|
- ./js/scripts/install-deps.sh
|
||||||
|
script:
|
||||||
|
- ./js/scripts/build.sh
|
||||||
|
tags:
|
||||||
|
- javascript-test
|
||||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -2,3 +2,6 @@
|
|||||||
path = ethcore/res/ethereum/tests
|
path = ethcore/res/ethereum/tests
|
||||||
url = https://github.com/ethereum/tests.git
|
url = https://github.com/ethereum/tests.git
|
||||||
branch = develop
|
branch = develop
|
||||||
|
[submodule "js/build"]
|
||||||
|
path = js/build
|
||||||
|
url = https://github.com/ethcore/js-precompiled
|
||||||
|
132
Cargo.lock
generated
132
Cargo.lock
generated
@ -264,7 +264,7 @@ name = "ethash"
|
|||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log 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)",
|
"parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"primal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"primal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sha3 0.1.0",
|
"sha3 0.1.0",
|
||||||
]
|
]
|
||||||
@ -295,7 +295,7 @@ dependencies = [
|
|||||||
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
||||||
"lazy_static 0.2.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)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lru-cache 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lru-cache 0.0.7 (git+https://github.com/contain-rs/lru-cache)",
|
||||||
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rayon 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rayon 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -319,6 +319,9 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ethcore-bloom-journal"
|
name = "ethcore-bloom-journal"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"siphasher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethcore-dapps"
|
name = "ethcore-dapps"
|
||||||
@ -332,14 +335,15 @@ dependencies = [
|
|||||||
"fetch 0.1.0",
|
"fetch 0.1.0",
|
||||||
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
||||||
"jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-core 3.0.2 (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.1 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
|
||||||
"linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 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)",
|
||||||
|
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"mime_guess 1.6.1 (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)",
|
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||||
|
"parity-dapps-glue 1.4.0",
|
||||||
"parity-dapps-home 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps-home 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||||
"parity-dapps-status 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-ui 1.4.0",
|
||||||
"parity-dapps-wallet 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
|
||||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-serialize 0.3.19 (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.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -364,7 +368,7 @@ dependencies = [
|
|||||||
"crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 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)",
|
||||||
"mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)",
|
"mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)",
|
||||||
"parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -453,7 +457,7 @@ dependencies = [
|
|||||||
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 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)",
|
||||||
"mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)",
|
"mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)",
|
||||||
"parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rlp 0.1.0",
|
"rlp 0.1.0",
|
||||||
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -482,7 +486,7 @@ dependencies = [
|
|||||||
"fetch 0.1.0",
|
"fetch 0.1.0",
|
||||||
"json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)",
|
"json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)",
|
||||||
"jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-core 3.0.2 (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.1 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
|
||||||
"log 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)",
|
||||||
"rlp 0.1.0",
|
"rlp 0.1.0",
|
||||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -505,7 +509,8 @@ dependencies = [
|
|||||||
"ethcore-util 1.4.0",
|
"ethcore-util 1.4.0",
|
||||||
"jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 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)",
|
||||||
"parity-dapps-signer 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps-glue 1.4.0",
|
||||||
|
"parity-ui 1.4.0",
|
||||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ws 0.5.2 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)",
|
"ws 0.5.2 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)",
|
||||||
@ -547,8 +552,9 @@ dependencies = [
|
|||||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 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)",
|
"parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rlp 0.1.0",
|
"rlp 0.1.0",
|
||||||
"rocksdb 0.4.5 (git+https://github.com/ethcore/rust-rocksdb)",
|
"rocksdb 0.4.5 (git+https://github.com/ethcore/rust-rocksdb)",
|
||||||
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -633,7 +639,7 @@ dependencies = [
|
|||||||
"ethcore-util 1.4.0",
|
"ethcore-util 1.4.0",
|
||||||
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rlp 0.1.0",
|
"rlp 0.1.0",
|
||||||
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -839,7 +845,7 @@ version = "3.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log 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)",
|
"parking_lot 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_codegen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_codegen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -847,8 +853,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonrpc-http-server"
|
name = "jsonrpc-http-server"
|
||||||
version = "6.1.0"
|
version = "6.1.1"
|
||||||
source = "git+https://github.com/ethcore/jsonrpc-http-server.git#2766c6708f66f6f4e667211461d220b49c0d9fdf"
|
source = "git+https://github.com/ethcore/jsonrpc-http-server.git#ee72e4778583daf901b5692468fc622f46abecb6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
||||||
"jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -882,7 +888,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linked-hash-map"
|
name = "linked-hash-map"
|
||||||
version = "0.0.9"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -898,9 +904,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "lru-cache"
|
name = "lru-cache"
|
||||||
version = "0.0.7"
|
version = "0.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/contain-rs/lru-cache#13255e33c45ceb69a4b143f235a4322df5fb580e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"linked-hash-map 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"linked-hash-map 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1164,10 +1170,28 @@ name = "odds"
|
|||||||
version = "0.2.12"
|
version = "0.2.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "owning_ref"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-dapps"
|
name = "parity-dapps"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
source = "git+https://github.com/ethcore/parity-ui.git#926b09b66c4940b09dc82c52adb4afd9e31155bc"
|
source = "git+https://github.com/ethcore/parity-ui.git#8b1c31319228ad4cf9bd4ae740a0b933aa9e19c7"
|
||||||
|
dependencies = [
|
||||||
|
"aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"quasi 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"quasi_codegen 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parity-dapps-glue"
|
||||||
|
version = "1.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -1181,38 +1205,21 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-dapps-home"
|
name = "parity-dapps-home"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
source = "git+https://github.com/ethcore/parity-ui.git#926b09b66c4940b09dc82c52adb4afd9e31155bc"
|
source = "git+https://github.com/ethcore/parity-ui.git#8b1c31319228ad4cf9bd4ae740a0b933aa9e19c7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-dapps-signer"
|
name = "parity-ui"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
source = "git+https://github.com/ethcore/parity-ui.git#926b09b66c4940b09dc82c52adb4afd9e31155bc"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps-glue 1.4.0",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "parity-dapps-status"
|
|
||||||
version = "1.4.0"
|
|
||||||
source = "git+https://github.com/ethcore/parity-ui.git#926b09b66c4940b09dc82c52adb4afd9e31155bc"
|
|
||||||
dependencies = [
|
|
||||||
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "parity-dapps-wallet"
|
|
||||||
version = "1.4.0"
|
|
||||||
source = "git+https://github.com/ethcore/parity-ui.git#926b09b66c4940b09dc82c52adb4afd9e31155bc"
|
|
||||||
dependencies = [
|
|
||||||
"parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.2.6"
|
version = "0.2.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -1221,6 +1228,27 @@ dependencies = [
|
|||||||
"winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"owning_ref 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phf"
|
name = "phf"
|
||||||
version = "0.7.14"
|
version = "0.7.14"
|
||||||
@ -1406,7 +1434,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "rocksdb"
|
name = "rocksdb"
|
||||||
version = "0.4.5"
|
version = "0.4.5"
|
||||||
source = "git+https://github.com/ethcore/rust-rocksdb#ffc7c82380fe8569f85ae6743f7f620af2d4a679"
|
source = "git+https://github.com/ethcore/rust-rocksdb#64c63ccbe1f62c2e2b39262486f9ba813793af58"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)",
|
"rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)",
|
||||||
@ -1415,7 +1443,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "rocksdb-sys"
|
name = "rocksdb-sys"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "git+https://github.com/ethcore/rust-rocksdb#ffc7c82380fe8569f85ae6743f7f620af2d4a679"
|
source = "git+https://github.com/ethcore/rust-rocksdb#64c63ccbe1f62c2e2b39262486f9ba813793af58"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -1544,6 +1572,11 @@ dependencies = [
|
|||||||
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "siphasher"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
@ -1851,7 +1884,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ws"
|
name = "ws"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
source = "git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable#afbff59776ce16ccec5ee9e218b8891830ee6fdf"
|
source = "git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable#609b21fdab96c8fffedec8699755ce3bea9454cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 0.4.0-dev (git+https://github.com/carllerche/bytes)",
|
"bytes 0.4.0-dev (git+https://github.com/carllerche/bytes)",
|
||||||
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -1946,15 +1979,15 @@ dependencies = [
|
|||||||
"checksum json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)" = "<none>"
|
"checksum json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)" = "<none>"
|
||||||
"checksum json-tcp-server 0.1.0 (git+https://github.com/ethcore/json-tcp-server)" = "<none>"
|
"checksum json-tcp-server 0.1.0 (git+https://github.com/ethcore/json-tcp-server)" = "<none>"
|
||||||
"checksum jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3c5094610b07f28f3edaf3947b732dadb31dbba4941d4d0c1c7a8350208f4414"
|
"checksum jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3c5094610b07f28f3edaf3947b732dadb31dbba4941d4d0c1c7a8350208f4414"
|
||||||
"checksum jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)" = "<none>"
|
"checksum jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc-http-server.git)" = "<none>"
|
||||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
"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 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"
|
"checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f"
|
||||||
"checksum libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "23e3757828fa702a20072c37ff47938e9dd331b92fac6e223d26d4b7a55f7ee2"
|
"checksum libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "23e3757828fa702a20072c37ff47938e9dd331b92fac6e223d26d4b7a55f7ee2"
|
||||||
"checksum linked-hash-map 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "83f7ff3baae999fdf921cccf54b61842bb3b26868d50d02dff48052ebec8dd79"
|
"checksum linked-hash-map 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bda158e0dabeb97ee8a401f4d17e479d6b891a14de0bba79d5cc2d4d325b5e48"
|
||||||
"checksum linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd"
|
"checksum linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd"
|
||||||
"checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054"
|
"checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054"
|
||||||
"checksum lru-cache 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "42d50dcb5d9f145df83b1043207e1ac0c37c9c779c4e128ca4655abc3f3cbf8c"
|
"checksum lru-cache 0.0.7 (git+https://github.com/contain-rs/lru-cache)" = "<none>"
|
||||||
"checksum matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "15305656809ce5a4805b1ff2946892810992197ce1270ff79baded852187942e"
|
"checksum matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "15305656809ce5a4805b1ff2946892810992197ce1270ff79baded852187942e"
|
||||||
"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
|
"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
|
||||||
"checksum mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a74cc2587bf97c49f3f5bab62860d6abf3902ca73b66b51d9b049fbdcd727bd2"
|
"checksum mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a74cc2587bf97c49f3f5bab62860d6abf3902ca73b66b51d9b049fbdcd727bd2"
|
||||||
@ -1982,12 +2015,12 @@ dependencies = [
|
|||||||
"checksum num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "51fedae97a05f7353612fe017ab705a37e6db8f4d67c5c6fe739a9e70d6eed09"
|
"checksum num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "51fedae97a05f7353612fe017ab705a37e6db8f4d67c5c6fe739a9e70d6eed09"
|
||||||
"checksum number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "084d05f4bf60621a9ac9bde941a410df548f4de9545f06e5ee9d3aef4b97cd77"
|
"checksum number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "084d05f4bf60621a9ac9bde941a410df548f4de9545f06e5ee9d3aef4b97cd77"
|
||||||
"checksum odds 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "b28c06e81b0f789122d415d6394b5fe849bde8067469f4c2980d3cdc10c78ec1"
|
"checksum odds 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "b28c06e81b0f789122d415d6394b5fe849bde8067469f4c2980d3cdc10c78ec1"
|
||||||
|
"checksum owning_ref 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8d91377085359426407a287ab16884a0111ba473aa6844ff01d4ec20ce3d75e7"
|
||||||
"checksum parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
"checksum parity-dapps 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
||||||
"checksum parity-dapps-home 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
"checksum parity-dapps-home 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
||||||
"checksum parity-dapps-signer 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
"checksum parking_lot 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "968f685642555d2f7e202c48b8b11de80569e9bfea817f7f12d7c61aac62d4e6"
|
||||||
"checksum parity-dapps-status 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
"checksum parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "dbc5847584161f273e69edc63c1a86254a22f570a0b5dd87aa6f9773f6f7d125"
|
||||||
"checksum parity-dapps-wallet 1.4.0 (git+https://github.com/ethcore/parity-ui.git)" = "<none>"
|
"checksum parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb1b97670a2ffadce7c397fb80a3d687c4f3060140b885621ef1653d0e5d5068"
|
||||||
"checksum parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e0fd1be2c3cf5fef20a6d18fec252c4f3c87c14fc3039002eb7d4ed91e436826"
|
|
||||||
"checksum phf 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "447d9d45f2e0b4a9b532e808365abf18fc211be6ca217202fcd45236ef12f026"
|
"checksum phf 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "447d9d45f2e0b4a9b532e808365abf18fc211be6ca217202fcd45236ef12f026"
|
||||||
"checksum phf_codegen 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "8af7ae7c3f75a502292b491e5cc0a1f69e3407744abe6e57e2a3b712bb82f01d"
|
"checksum phf_codegen 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "8af7ae7c3f75a502292b491e5cc0a1f69e3407744abe6e57e2a3b712bb82f01d"
|
||||||
"checksum phf_generator 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "db005608fd99800c8c74106a7c894cf582055b689aa14a79462cefdcb7dc1cc3"
|
"checksum phf_generator 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "db005608fd99800c8c74106a7c894cf582055b689aa14a79462cefdcb7dc1cc3"
|
||||||
@ -2024,6 +2057,7 @@ dependencies = [
|
|||||||
"checksum serde_codegen_internals 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f877e2781ed0a323295d1c9f0e26556117b5a11489fc47b1848dfb98b3173d21"
|
"checksum serde_codegen_internals 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f877e2781ed0a323295d1c9f0e26556117b5a11489fc47b1848dfb98b3173d21"
|
||||||
"checksum serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e10f8a9d94b06cf5d3bef66475f04c8ff90950f1be7004c357ff9472ccbaebc"
|
"checksum serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e10f8a9d94b06cf5d3bef66475f04c8ff90950f1be7004c357ff9472ccbaebc"
|
||||||
"checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c"
|
"checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c"
|
||||||
|
"checksum siphasher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c44e42fa187b5a8782489cf7740cc27c3125806be2bf33563cf5e02e9533fcd"
|
||||||
"checksum slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d807fd58c4181bbabed77cb3b891ba9748241a552bcc5be698faaebefc54f46e"
|
"checksum slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d807fd58c4181bbabed77cb3b891ba9748241a552bcc5be698faaebefc54f46e"
|
||||||
"checksum slab 0.2.0 (git+https://github.com/carllerche/slab?rev=5476efcafb)" = "<none>"
|
"checksum slab 0.2.0 (git+https://github.com/carllerche/slab?rev=5476efcafb)" = "<none>"
|
||||||
"checksum slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6dbdd334bd28d328dad1c41b0ea662517883d8880d8533895ef96c8003dec9c4"
|
"checksum slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6dbdd334bd28d328dad1c41b0ea662517883d8880d8533895ef96c8003dec9c4"
|
||||||
|
@ -58,7 +58,7 @@ version = "0.9"
|
|||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["ui", "use-precompiled-js", "ipc"]
|
default = ["ui", "use-precompiled-js"]
|
||||||
ui = ["dapps", "ethcore-signer/ui"]
|
ui = ["dapps", "ethcore-signer/ui"]
|
||||||
use-precompiled-js = ["ethcore-dapps/use-precompiled-js", "ethcore-signer/use-precompiled-js"]
|
use-precompiled-js = ["ethcore-dapps/use-precompiled-js", "ethcore-signer/use-precompiled-js"]
|
||||||
dapps = ["ethcore-dapps"]
|
dapps = ["ethcore-dapps"]
|
||||||
|
@ -27,11 +27,14 @@ ethcore-devtools = { path = "../devtools" }
|
|||||||
ethcore-rpc = { path = "../rpc" }
|
ethcore-rpc = { path = "../rpc" }
|
||||||
ethcore-util = { path = "../util" }
|
ethcore-util = { path = "../util" }
|
||||||
fetch = { path = "../util/fetch" }
|
fetch = { path = "../util/fetch" }
|
||||||
|
parity-ui = { path = "./ui" }
|
||||||
|
parity-dapps-glue = { path = "./js-glue" }
|
||||||
|
mime = "0.2"
|
||||||
|
### DEPRECATED
|
||||||
parity-dapps = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
|
parity-dapps = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
|
||||||
# List of apps
|
|
||||||
parity-dapps-status = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
|
|
||||||
parity-dapps-home = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
|
parity-dapps-home = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
|
||||||
parity-dapps-wallet = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4", optional = true }
|
### /DEPRECATED
|
||||||
|
|
||||||
mime_guess = { version = "1.6.1" }
|
mime_guess = { version = "1.6.1" }
|
||||||
clippy = { version = "0.0.90", optional = true}
|
clippy = { version = "0.0.90", optional = true}
|
||||||
|
|
||||||
@ -39,13 +42,11 @@ clippy = { version = "0.0.90", optional = true}
|
|||||||
serde_codegen = { version = "0.8", optional = true }
|
serde_codegen = { version = "0.8", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["serde_codegen", "extra-dapps"]
|
default = ["serde_codegen"]
|
||||||
extra-dapps = ["parity-dapps-wallet"]
|
|
||||||
nightly = ["serde_macros"]
|
nightly = ["serde_macros"]
|
||||||
dev = ["clippy", "ethcore-rpc/dev", "ethcore-util/dev"]
|
dev = ["clippy", "ethcore-rpc/dev", "ethcore-util/dev"]
|
||||||
|
|
||||||
use-precompiled-js = [
|
use-precompiled-js = [
|
||||||
"parity-dapps-status/use-precompiled-js",
|
"parity-ui/use-precompiled-js",
|
||||||
"parity-dapps-home/use-precompiled-js",
|
"parity-dapps-home/use-precompiled-js",
|
||||||
"parity-dapps-wallet/use-precompiled-js"
|
|
||||||
]
|
]
|
||||||
|
31
dapps/js-glue/Cargo.toml
Normal file
31
dapps/js-glue/Cargo.toml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
[package]
|
||||||
|
description = "Base Package for all Parity built-in dapps"
|
||||||
|
name = "parity-dapps-glue"
|
||||||
|
version = "1.4.0"
|
||||||
|
license = "GPL-3.0"
|
||||||
|
authors = ["Ethcore <admin@ethcore.io"]
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
quasi_codegen = { version = "0.11", optional = true }
|
||||||
|
syntex = { version = "0.33", optional = true }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
glob = { version = "0.2.11" }
|
||||||
|
mime_guess = { version = "1.6.1" }
|
||||||
|
aster = { version = "0.17", default-features = false }
|
||||||
|
quasi = { version = "0.11", default-features = false }
|
||||||
|
quasi_macros = { version = "0.11", optional = true }
|
||||||
|
syntex = { version = "0.33", optional = true }
|
||||||
|
syntex_syntax = { version = "0.33", optional = true }
|
||||||
|
clippy = { version = "0.0.90", optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
dev = ["clippy"]
|
||||||
|
default = ["with-syntex"]
|
||||||
|
nightly = ["quasi_macros"]
|
||||||
|
nightly-testing = ["clippy"]
|
||||||
|
with-syntex = ["quasi/with-syntex", "quasi_codegen", "quasi_codegen/with-syntex", "syntex", "syntex_syntax"]
|
||||||
|
use-precompiled-js = []
|
||||||
|
|
||||||
|
|
65
dapps/js-glue/README.md
Normal file
65
dapps/js-glue/README.md
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# Parity Dapps (JS-glue)
|
||||||
|
|
||||||
|
Code generator to simplify creating a built-in Parity Dapp
|
||||||
|
|
||||||
|
# How to create new builtin Dapp.
|
||||||
|
1. Clone this repository.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git clone https://github.com/ethcore/parity.git
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Create a new directory for your Dapp. (`./myapp`)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ mkdir -p ./parity/dapps/myapp/src/web
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Copy your frontend files to `./dapps/myapp/src/web` (bundled ones)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ cp -r ./myapp-src/* ./parity/dapps/myapp/src/web
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Instead of creating `web3` in your app. Load (as the first script tag in `head`):
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script src="/parity-utils/inject.js"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
The `inject.js` script will create global `web3` instance with proper provider that should be used by your dapp.
|
||||||
|
|
||||||
|
1. Create `./parity/dapps/myapp/Cargo.toml` with you apps details. See example here: [parity-status Cargo.toml](https://github.com/ethcore/parity-ui/blob/master/status/Cargo.toml).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git clone https://github.com/ethcore/parity-ui.git
|
||||||
|
$ cd ./parity-ui/
|
||||||
|
$ cp ./home/Cargo.toml ../parity/dapps/myapp/Cargo.toml
|
||||||
|
$ cp ./home/build.rs ../parity/dapps/myapp/build.rs
|
||||||
|
$ cp ./home/src/lib.rs ../parity/dapps/myapp/src/lib.rs
|
||||||
|
$ cp ./home/src/lib.rs.in ../parity/dapps/myapp/src/lib.rs.in
|
||||||
|
# And edit the details of your app
|
||||||
|
$ vim ../parity/dapps/myapp/Cargo.toml # Edit the details
|
||||||
|
$ vim ./parity/dapps/myapp/src/lib.rs.in # Edit the details
|
||||||
|
```
|
||||||
|
# How to include your Dapp into `Parity`?
|
||||||
|
1. Edit `dapps/Cargo.toml` and add dependency to your application (it can be optional)
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Use git repo and version
|
||||||
|
parity-dapps-myapp = { path="./myapp" }
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Edit `dapps/src/apps.rs` and add your application to `all_pages` (if it's optional you need to specify two functions - see `parity-dapps-wallet` example)
|
||||||
|
|
||||||
|
1. Compile parity.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ cargo build --release # While inside `parity`
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Commit the results.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git add myapp && git commit -am "My first Parity Dapp".
|
||||||
|
```
|
45
dapps/js-glue/build.rs
Normal file
45
dapps/js-glue/build.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(feature = "with-syntex")]
|
||||||
|
mod inner {
|
||||||
|
extern crate syntex;
|
||||||
|
extern crate quasi_codegen;
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
|
let mut registry = syntex::Registry::new();
|
||||||
|
quasi_codegen::register(&mut registry);
|
||||||
|
|
||||||
|
let src = Path::new("src/lib.rs.in");
|
||||||
|
let dst = Path::new(&out_dir).join("lib.rs");
|
||||||
|
|
||||||
|
registry.expand("", &src, &dst).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "with-syntex"))]
|
||||||
|
mod inner {
|
||||||
|
pub fn main() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
inner::main();
|
||||||
|
}
|
66
dapps/js-glue/src/build.rs
Normal file
66
dapps/js-glue/src/build.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
|
||||||
|
#[cfg(feature = "with-syntex")]
|
||||||
|
pub mod inner {
|
||||||
|
use syntex;
|
||||||
|
use codegen;
|
||||||
|
use syntax::{ast, fold};
|
||||||
|
use std::env;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
fn strip_attributes(krate: ast::Crate) -> ast::Crate {
|
||||||
|
/// Helper folder that strips the serde attributes after the extensions have been expanded.
|
||||||
|
struct StripAttributeFolder;
|
||||||
|
|
||||||
|
impl fold::Folder for StripAttributeFolder {
|
||||||
|
fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
|
||||||
|
match attr.node.value.node {
|
||||||
|
ast::MetaItemKind::List(ref n, _) if n == &"webapp" => { return None; }
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
|
||||||
|
fold::noop_fold_mac(mac, self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fold::Folder::fold_crate(&mut StripAttributeFolder, krate)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register(reg: &mut syntex::Registry) {
|
||||||
|
reg.add_attr("feature(custom_derive)");
|
||||||
|
reg.add_attr("feature(custom_attribute)");
|
||||||
|
|
||||||
|
reg.add_decorator("derive_WebAppFiles", codegen::expand_webapp_implementation);
|
||||||
|
reg.add_post_expansion_pass(strip_attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate() {
|
||||||
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
|
let mut registry = syntex::Registry::new();
|
||||||
|
register(&mut registry);
|
||||||
|
|
||||||
|
let src = Path::new("src/lib.rs.in");
|
||||||
|
let dst = Path::new(&out_dir).join("lib.rs");
|
||||||
|
|
||||||
|
registry.expand("", &src, &dst).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "with-syntex"))]
|
||||||
|
pub mod inner {
|
||||||
|
use codegen;
|
||||||
|
|
||||||
|
pub fn register(reg: &mut rustc_plugin::Registry) {
|
||||||
|
reg.register_syntax_extension(
|
||||||
|
syntax::parse::token::intern("derive_WebAppFiles"),
|
||||||
|
syntax::ext::base::MultiDecorator(
|
||||||
|
Box::new(codegen::expand_webapp_implementation)));
|
||||||
|
|
||||||
|
reg.register_attribute("webapp".to_owned(), AttributeType::Normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate() {}
|
||||||
|
}
|
189
dapps/js-glue/src/codegen.rs
Normal file
189
dapps/js-glue/src/codegen.rs
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
extern crate aster;
|
||||||
|
extern crate glob;
|
||||||
|
extern crate mime_guess;
|
||||||
|
|
||||||
|
use self::mime_guess::guess_mime_type;
|
||||||
|
use std::path::{self, Path, PathBuf};
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
use syntax::ast::{MetaItem, Item};
|
||||||
|
|
||||||
|
use syntax::ast;
|
||||||
|
use syntax::attr;
|
||||||
|
use syntax::codemap::Span;
|
||||||
|
use syntax::ext::base::{Annotatable, ExtCtxt};
|
||||||
|
use syntax::ptr::P;
|
||||||
|
use syntax::print::pprust::{lit_to_string};
|
||||||
|
use syntax::parse::token::{InternedString};
|
||||||
|
|
||||||
|
|
||||||
|
pub fn expand_webapp_implementation(
|
||||||
|
cx: &mut ExtCtxt,
|
||||||
|
span: Span,
|
||||||
|
meta_item: &MetaItem,
|
||||||
|
annotatable: &Annotatable,
|
||||||
|
push: &mut FnMut(Annotatable)
|
||||||
|
) {
|
||||||
|
let item = match *annotatable {
|
||||||
|
Annotatable::Item(ref item) => item,
|
||||||
|
_ => {
|
||||||
|
cx.span_err(meta_item.span, "`#[derive(WebAppFiles)]` may only be applied to struct implementations");
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let builder = aster::AstBuilder::new().span(span);
|
||||||
|
implement_webapp(cx, &builder, &item, push);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn implement_webapp(cx: &ExtCtxt, builder: &aster::AstBuilder, item: &Item, push: &mut FnMut(Annotatable)) {
|
||||||
|
let static_files_dir = extract_path(cx, item);
|
||||||
|
|
||||||
|
let src = Path::new("src");
|
||||||
|
let static_files = {
|
||||||
|
let mut buf = src.to_path_buf();
|
||||||
|
buf.push(static_files_dir.deref());
|
||||||
|
buf
|
||||||
|
};
|
||||||
|
|
||||||
|
let search_location = {
|
||||||
|
let mut buf = static_files.to_path_buf();
|
||||||
|
buf.push("**");
|
||||||
|
buf.push("*");
|
||||||
|
buf
|
||||||
|
};
|
||||||
|
|
||||||
|
let files = glob::glob(search_location.to_str().expect("Valid UTF8 path"))
|
||||||
|
.expect("The sources directory is missing.")
|
||||||
|
.collect::<Result<Vec<PathBuf>, glob::GlobError>>()
|
||||||
|
.expect("There should be no error when reading a list of files.");
|
||||||
|
|
||||||
|
let statements = files
|
||||||
|
.iter()
|
||||||
|
.filter(|path_buf| path_buf.is_file())
|
||||||
|
.map(|path_buf| {
|
||||||
|
let path = path_buf.as_path();
|
||||||
|
let filename = path.file_name().and_then(|s| s.to_str()).expect("Only UTF8 paths.");
|
||||||
|
let mime_type = guess_mime_type(filename).to_string();
|
||||||
|
let file_path = as_uri(path.strip_prefix(&static_files).ok().expect("Prefix is always there, cause it's absolute path;qed"));
|
||||||
|
let file_path_in_source = path.to_str().expect("Only UTF8 paths.");
|
||||||
|
|
||||||
|
let path_lit = builder.expr().str(file_path.as_str());
|
||||||
|
let mime_lit = builder.expr().str(mime_type.as_str());
|
||||||
|
let web_path_lit = builder.expr().str(file_path_in_source);
|
||||||
|
let separator_lit = builder.expr().str(path::MAIN_SEPARATOR.to_string().as_str());
|
||||||
|
let concat_id = builder.id("concat!");
|
||||||
|
let env_id = builder.id("env!");
|
||||||
|
let macro_id = builder.id("include_bytes!");
|
||||||
|
|
||||||
|
let content = quote_expr!(
|
||||||
|
cx,
|
||||||
|
$macro_id($concat_id($env_id("CARGO_MANIFEST_DIR"), $separator_lit, $web_path_lit))
|
||||||
|
);
|
||||||
|
quote_stmt!(
|
||||||
|
cx,
|
||||||
|
files.insert($path_lit, File { path: $path_lit, content_type: $mime_lit, content: $content });
|
||||||
|
).expect("The statement is always ok, because it just uses literals.")
|
||||||
|
}).collect::<Vec<ast::Stmt>>();
|
||||||
|
|
||||||
|
let type_name = item.ident;
|
||||||
|
|
||||||
|
let files_impl = quote_item!(cx,
|
||||||
|
impl $type_name {
|
||||||
|
fn files() -> ::std::collections::HashMap<&'static str, File> {
|
||||||
|
let mut files = ::std::collections::HashMap::new();
|
||||||
|
$statements
|
||||||
|
files
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
push(Annotatable::Item(files_impl));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_path(cx: &ExtCtxt, item: &Item) -> String {
|
||||||
|
for meta_items in item.attrs().iter().filter_map(webapp_meta_items) {
|
||||||
|
for meta_item in meta_items {
|
||||||
|
match meta_item.node {
|
||||||
|
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"path" => {
|
||||||
|
if let Some(s) = get_str_from_lit(cx, name, lit) {
|
||||||
|
return s.deref().to_owned();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// default
|
||||||
|
"web".to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_str_from_lit(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Option<InternedString> {
|
||||||
|
match lit.node {
|
||||||
|
ast::LitKind::Str(ref s, _) => Some(s.clone()),
|
||||||
|
_ => {
|
||||||
|
cx.span_err(
|
||||||
|
lit.span,
|
||||||
|
&format!("webapp annotation `{}` must be a string, not `{}`",
|
||||||
|
name,
|
||||||
|
lit_to_string(lit)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn webapp_meta_items(attr: &ast::Attribute) -> Option<&[P<ast::MetaItem>]> {
|
||||||
|
match attr.node.value.node {
|
||||||
|
ast::MetaItemKind::List(ref name, ref items) if name == &"webapp" => {
|
||||||
|
attr::mark_used(&attr);
|
||||||
|
Some(items)
|
||||||
|
}
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_uri(path: &Path) -> String {
|
||||||
|
let mut s = String::new();
|
||||||
|
for component in path.iter() {
|
||||||
|
s.push_str(component.to_str().expect("Only UTF-8 filenames are supported."));
|
||||||
|
s.push('/');
|
||||||
|
}
|
||||||
|
s[0..s.len()-1].into()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_convert_path_separators_on_all_platforms() {
|
||||||
|
// given
|
||||||
|
let p = {
|
||||||
|
let mut p = PathBuf::new();
|
||||||
|
p.push("web");
|
||||||
|
p.push("src");
|
||||||
|
p.push("index.html");
|
||||||
|
p
|
||||||
|
};
|
||||||
|
|
||||||
|
// when
|
||||||
|
let path = as_uri(&p);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(path, "web/src/index.html".to_owned());
|
||||||
|
}
|
89
dapps/js-glue/src/js.rs
Normal file
89
dapps/js-glue/src/js.rs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
#![cfg_attr(feature="use-precompiled-js", allow(dead_code))]
|
||||||
|
#![cfg_attr(feature="use-precompiled-js", allow(unused_imports))]
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
mod platform {
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
pub static NPM_CMD: &'static str = "npm";
|
||||||
|
pub fn handle_fd(cmd: &mut Command) -> &mut Command {
|
||||||
|
cmd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
mod platform {
|
||||||
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
|
pub static NPM_CMD: &'static str = "npm.cmd";
|
||||||
|
// NOTE [ToDr] For some reason on windows
|
||||||
|
// We cannot have any file descriptors open when running a child process
|
||||||
|
// during build phase.
|
||||||
|
pub fn handle_fd(cmd: &mut Command) -> &mut Command {
|
||||||
|
cmd.stdin(Stdio::null())
|
||||||
|
.stdout(Stdio::null())
|
||||||
|
.stderr(Stdio::null())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn die<T : fmt::Debug>(s: &'static str, e: T) -> ! {
|
||||||
|
panic!("Error: {}: {:?}", s, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "use-precompiled-js")]
|
||||||
|
pub fn test(_path: &str) {
|
||||||
|
}
|
||||||
|
#[cfg(feature = "use-precompiled-js")]
|
||||||
|
pub fn build(_path: &str) {
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "use-precompiled-js"))]
|
||||||
|
pub fn build(path: &str) {
|
||||||
|
let child = platform::handle_fd(&mut Command::new(platform::NPM_CMD))
|
||||||
|
.arg("install")
|
||||||
|
.arg("--no-progress")
|
||||||
|
.current_dir(path)
|
||||||
|
.status()
|
||||||
|
.unwrap_or_else(|e| die("Installing node.js dependencies with npm", e));
|
||||||
|
assert!(child.success(), "There was an error installing dependencies.");
|
||||||
|
|
||||||
|
let child = platform::handle_fd(&mut Command::new(platform::NPM_CMD))
|
||||||
|
.arg("run")
|
||||||
|
.arg("build")
|
||||||
|
.env("NODE_ENV", "production")
|
||||||
|
.env("BUILD_DEST", "build")
|
||||||
|
.current_dir(path)
|
||||||
|
.status()
|
||||||
|
.unwrap_or_else(|e| die("Building JS code", e));
|
||||||
|
assert!(child.success(), "There was an error build JS code.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "use-precompiled-js"))]
|
||||||
|
pub fn test(path: &str) {
|
||||||
|
let child = Command::new(platform::NPM_CMD)
|
||||||
|
.arg("run")
|
||||||
|
.arg("test")
|
||||||
|
.current_dir(path)
|
||||||
|
.status()
|
||||||
|
.unwrap_or_else(|e| die("Running test command", e));
|
||||||
|
assert!(child.success(), "There was an error while running JS tests.");
|
||||||
|
}
|
39
dapps/js-glue/src/lib.rs
Normal file
39
dapps/js-glue/src/lib.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
|
||||||
|
#![cfg_attr(not(feature = "with-syntex"), feature(rustc_private, plugin))]
|
||||||
|
#![cfg_attr(not(feature = "with-syntex"), plugin(quasi_macros))]
|
||||||
|
|
||||||
|
#[cfg(feature = "with-syntex")]
|
||||||
|
extern crate syntex;
|
||||||
|
|
||||||
|
#[cfg(feature = "with-syntex")]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate syntex_syntax as syntax;
|
||||||
|
|
||||||
|
#[cfg(feature = "with-syntex")]
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/lib.rs"));
|
||||||
|
|
||||||
|
#[cfg(not(feature = "with-syntex"))]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate syntax;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "with-syntex"))]
|
||||||
|
extern crate rustc_plugin;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "with-syntex"))]
|
||||||
|
include!("lib.rs.in");
|
46
dapps/js-glue/src/lib.rs.in
Normal file
46
dapps/js-glue/src/lib.rs.in
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
extern crate quasi;
|
||||||
|
|
||||||
|
mod codegen;
|
||||||
|
mod build;
|
||||||
|
pub mod js;
|
||||||
|
pub use build::inner::generate;
|
||||||
|
|
||||||
|
use std::default::Default;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct File {
|
||||||
|
pub path: &'static str,
|
||||||
|
pub content: &'static [u8],
|
||||||
|
// TODO: use strongly-typed MIME.
|
||||||
|
pub content_type: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Info {
|
||||||
|
pub name: &'static str,
|
||||||
|
pub version: &'static str,
|
||||||
|
pub author: &'static str,
|
||||||
|
pub description: &'static str,
|
||||||
|
pub icon_url: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait WebApp : Default + Send + Sync {
|
||||||
|
fn file(&self, path: &str) -> Option<&File>;
|
||||||
|
fn info(&self) -> Info;
|
||||||
|
}
|
@ -19,12 +19,16 @@ use serde_json;
|
|||||||
use endpoint::Handler;
|
use endpoint::Handler;
|
||||||
use handlers::{ContentHandler, EchoHandler};
|
use handlers::{ContentHandler, EchoHandler};
|
||||||
|
|
||||||
pub fn as_json<T : Serialize>(val: &T) -> Box<Handler> {
|
pub fn as_json<T: Serialize>(val: &T) -> Box<Handler> {
|
||||||
Box::new(ContentHandler::ok(serde_json::to_string(val).unwrap(), "application/json".to_owned()))
|
let json = serde_json::to_string(val)
|
||||||
|
.expect("serialization to string is infallible; qed");
|
||||||
|
Box::new(ContentHandler::ok(json, mime!(Application/Json)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_json_error<T : Serialize>(val: &T) -> Box<Handler> {
|
pub fn as_json_error<T: Serialize>(val: &T) -> Box<Handler> {
|
||||||
Box::new(ContentHandler::not_found(serde_json::to_string(val).unwrap(), "application/json".to_owned()))
|
let json = serde_json::to_string(val)
|
||||||
|
.expect("serialization to string is infallible; qed");
|
||||||
|
Box::new(ContentHandler::not_found(json, mime!(Application/Json)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ping_response(local_domain: &str) -> Box<Handler> {
|
pub fn ping_response(local_domain: &str) -> Box<Handler> {
|
||||||
|
@ -47,15 +47,17 @@ impl ContentCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_garbage(&mut self, expected_size: usize) -> Vec<(String, ContentStatus)> {
|
pub fn clear_garbage(&mut self, expected_size: usize) -> Vec<(String, ContentStatus)> {
|
||||||
let mut len = self.cache.len();
|
let len = self.cache.len();
|
||||||
|
|
||||||
if len <= expected_size {
|
if len <= expected_size {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut removed = Vec::with_capacity(len - expected_size);
|
let mut removed = Vec::with_capacity(len - expected_size);
|
||||||
while len > expected_size {
|
|
||||||
let entry = self.cache.pop_front().unwrap();
|
while self.cache.len() > expected_size {
|
||||||
|
let entry = self.cache.pop_front().expect("expected_size bounded at 0, len is greater; qed");
|
||||||
|
|
||||||
match entry.1 {
|
match entry.1 {
|
||||||
ContentStatus::Fetching(ref fetch) => {
|
ContentStatus::Fetching(ref fetch) => {
|
||||||
trace!(target: "dapps", "Aborting {} because of limit.", entry.0);
|
trace!(target: "dapps", "Aborting {} because of limit.", entry.0);
|
||||||
@ -73,7 +75,6 @@ impl ContentCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
removed.push(entry);
|
removed.push(entry);
|
||||||
len -= 1;
|
|
||||||
}
|
}
|
||||||
removed
|
removed
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ impl<R: URLHint> ContentFetcher<R> {
|
|||||||
// fallback to resolver
|
// fallback to resolver
|
||||||
if let Ok(content_id) = content_id.from_hex() {
|
if let Ok(content_id) = content_id.from_hex() {
|
||||||
// if app_id is valid, but we are syncing always return true.
|
// if app_id is valid, but we are syncing always return true.
|
||||||
if self.sync.is_major_syncing() {
|
if self.sync.is_major_importing() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// else try to resolve the app_id
|
// else try to resolve the app_id
|
||||||
@ -99,7 +99,7 @@ impl<R: URLHint> ContentFetcher<R> {
|
|||||||
let mut cache = self.cache.lock();
|
let mut cache = self.cache.lock();
|
||||||
let content_id = path.app_id.clone();
|
let content_id = path.app_id.clone();
|
||||||
|
|
||||||
if self.sync.is_major_syncing() {
|
if self.sync.is_major_importing() {
|
||||||
return Box::new(ContentHandler::error(
|
return Box::new(ContentHandler::error(
|
||||||
StatusCode::ServiceUnavailable,
|
StatusCode::ServiceUnavailable,
|
||||||
"Sync In Progress",
|
"Sync In Progress",
|
||||||
|
@ -17,7 +17,8 @@
|
|||||||
use endpoint::{Endpoints, Endpoint};
|
use endpoint::{Endpoints, Endpoint};
|
||||||
use page::PageEndpoint;
|
use page::PageEndpoint;
|
||||||
use proxypac::ProxyPac;
|
use proxypac::ProxyPac;
|
||||||
use parity_dapps::WebApp;
|
use parity_dapps::{self, WebApp};
|
||||||
|
use parity_dapps_glue::WebApp as NewWebApp;
|
||||||
|
|
||||||
mod cache;
|
mod cache;
|
||||||
mod fs;
|
mod fs;
|
||||||
@ -25,8 +26,8 @@ pub mod urlhint;
|
|||||||
pub mod fetcher;
|
pub mod fetcher;
|
||||||
pub mod manifest;
|
pub mod manifest;
|
||||||
|
|
||||||
extern crate parity_dapps_status;
|
|
||||||
extern crate parity_dapps_home;
|
extern crate parity_dapps_home;
|
||||||
|
extern crate parity_ui;
|
||||||
|
|
||||||
pub const DAPPS_DOMAIN : &'static str = ".parity";
|
pub const DAPPS_DOMAIN : &'static str = ".parity";
|
||||||
pub const RPC_PATH : &'static str = "rpc";
|
pub const RPC_PATH : &'static str = "rpc";
|
||||||
@ -48,33 +49,69 @@ pub fn utils() -> Box<Endpoint> {
|
|||||||
Box::new(PageEndpoint::with_prefix(parity_dapps_home::App::default(), UTILS_PATH.to_owned()))
|
Box::new(PageEndpoint::with_prefix(parity_dapps_home::App::default(), UTILS_PATH.to_owned()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn all_endpoints(dapps_path: String) -> Endpoints {
|
pub fn all_endpoints(dapps_path: String, signer_port: Option<u16>) -> Endpoints {
|
||||||
// fetch fs dapps at first to avoid overwriting builtins
|
// fetch fs dapps at first to avoid overwriting builtins
|
||||||
let mut pages = fs::local_endpoints(dapps_path);
|
let mut pages = fs::local_endpoints(dapps_path);
|
||||||
// Home page needs to be safe embed
|
|
||||||
// because we use Cross-Origin LocalStorage.
|
|
||||||
// TODO [ToDr] Account naming should be moved to parity.
|
|
||||||
pages.insert("home".into(), Box::new(
|
|
||||||
PageEndpoint::new_safe_to_embed(parity_dapps_home::App::default())
|
|
||||||
));
|
|
||||||
pages.insert("proxy".into(), ProxyPac::boxed());
|
|
||||||
insert::<parity_dapps_status::App>(&mut pages, "parity");
|
|
||||||
insert::<parity_dapps_status::App>(&mut pages, "status");
|
|
||||||
|
|
||||||
// Optional dapps
|
// NOTE [ToDr] Dapps will be currently embeded on 8180
|
||||||
wallet_page(&mut pages);
|
pages.insert("ui".into(), Box::new(
|
||||||
|
PageEndpoint::new_safe_to_embed(NewUi::default(), signer_port)
|
||||||
|
));
|
||||||
|
|
||||||
|
pages.insert("proxy".into(), ProxyPac::boxed());
|
||||||
|
insert::<parity_dapps_home::App>(&mut pages, "home");
|
||||||
|
|
||||||
|
|
||||||
pages
|
pages
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "parity-dapps-wallet")]
|
|
||||||
fn wallet_page(pages: &mut Endpoints) {
|
|
||||||
extern crate parity_dapps_wallet;
|
|
||||||
insert::<parity_dapps_wallet::App>(pages, "wallet");
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "parity-dapps-wallet"))]
|
|
||||||
fn wallet_page(_pages: &mut Endpoints) {}
|
|
||||||
|
|
||||||
fn insert<T : WebApp + Default + 'static>(pages: &mut Endpoints, id: &str) {
|
fn insert<T : WebApp + Default + 'static>(pages: &mut Endpoints, id: &str) {
|
||||||
pages.insert(id.to_owned(), Box::new(PageEndpoint::new(T::default())));
|
pages.insert(id.to_owned(), Box::new(PageEndpoint::new(T::default())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO [ToDr] Temporary wrapper until we get rid of old built-ins.
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
struct NewUi {
|
||||||
|
app: parity_ui::App,
|
||||||
|
files: HashMap<&'static str, parity_dapps::File>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for NewUi {
|
||||||
|
fn default() -> Self {
|
||||||
|
let app = parity_ui::App::default();
|
||||||
|
let files = {
|
||||||
|
let mut files = HashMap::new();
|
||||||
|
for (k, v) in &app.files {
|
||||||
|
files.insert(*k, parity_dapps::File {
|
||||||
|
path: v.path,
|
||||||
|
content: v.content,
|
||||||
|
content_type: v.content_type,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
files
|
||||||
|
};
|
||||||
|
|
||||||
|
NewUi {
|
||||||
|
app: app,
|
||||||
|
files: files,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WebApp for NewUi {
|
||||||
|
fn file(&self, path: &str) -> Option<&parity_dapps::File> {
|
||||||
|
self.files.get(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn info(&self) -> parity_dapps::Info {
|
||||||
|
let info = self.app.info();
|
||||||
|
parity_dapps::Info {
|
||||||
|
name: info.name,
|
||||||
|
version: info.version,
|
||||||
|
author: info.author,
|
||||||
|
description: info.description,
|
||||||
|
icon_url: info.icon_url,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -156,9 +156,9 @@ impl URLHintContract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut it = vec.into_iter();
|
let mut it = vec.into_iter();
|
||||||
let account_slash_repo = it.next().unwrap();
|
let account_slash_repo = it.next().expect("element 0 of 3-len vector known to exist; qed");
|
||||||
let commit = it.next().unwrap();
|
let commit = it.next().expect("element 1 of 3-len vector known to exist; qed");
|
||||||
let owner = it.next().unwrap();
|
let owner = it.next().expect("element 2 of 3-len vector known to exist qed");
|
||||||
|
|
||||||
match (account_slash_repo, commit, owner) {
|
match (account_slash_repo, commit, owner) {
|
||||||
(Token::String(account_slash_repo), Token::FixedBytes(commit), Token::Address(owner)) => {
|
(Token::String(account_slash_repo), Token::FixedBytes(commit), Token::Address(owner)) => {
|
||||||
|
@ -19,57 +19,60 @@
|
|||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use hyper::{header, server, Decoder, Encoder, Next};
|
use hyper::{header, server, Decoder, Encoder, Next};
|
||||||
use hyper::net::HttpStream;
|
use hyper::net::HttpStream;
|
||||||
|
use hyper::mime::Mime;
|
||||||
use hyper::status::StatusCode;
|
use hyper::status::StatusCode;
|
||||||
|
|
||||||
use util::version;
|
use util::version;
|
||||||
|
|
||||||
|
use handlers::add_security_headers;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ContentHandler {
|
pub struct ContentHandler {
|
||||||
code: StatusCode,
|
code: StatusCode,
|
||||||
content: String,
|
content: String,
|
||||||
mimetype: String,
|
mimetype: Mime,
|
||||||
write_pos: usize,
|
write_pos: usize,
|
||||||
|
safe_to_embed_at_port: Option<u16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContentHandler {
|
impl ContentHandler {
|
||||||
pub fn ok(content: String, mimetype: String) -> Self {
|
pub fn ok(content: String, mimetype: Mime) -> Self {
|
||||||
ContentHandler {
|
Self::new(StatusCode::Ok, content, mimetype)
|
||||||
code: StatusCode::Ok,
|
|
||||||
content: content,
|
|
||||||
mimetype: mimetype,
|
|
||||||
write_pos: 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn not_found(content: String, mimetype: String) -> Self {
|
pub fn not_found(content: String, mimetype: Mime) -> Self {
|
||||||
ContentHandler {
|
Self::new(StatusCode::NotFound, content, mimetype)
|
||||||
code: StatusCode::NotFound,
|
|
||||||
content: content,
|
|
||||||
mimetype: mimetype,
|
|
||||||
write_pos: 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn html(code: StatusCode, content: String) -> Self {
|
pub fn html(code: StatusCode, content: String, embeddable_at: Option<u16>) -> Self {
|
||||||
Self::new(code, content, "text/html".into())
|
Self::new_embeddable(code, content, mime!(Text/Html), embeddable_at)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn error(code: StatusCode, title: &str, message: &str, details: Option<&str>) -> Self {
|
pub fn error(code: StatusCode, title: &str, message: &str, details: Option<&str>) -> Self {
|
||||||
|
Self::error_embeddable(code, title, message, details, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn error_embeddable(code: StatusCode, title: &str, message: &str, details: Option<&str>, embeddable_at: Option<u16>) -> Self {
|
||||||
Self::html(code, format!(
|
Self::html(code, format!(
|
||||||
include_str!("../error_tpl.html"),
|
include_str!("../error_tpl.html"),
|
||||||
title=title,
|
title=title,
|
||||||
message=message,
|
message=message,
|
||||||
details=details.unwrap_or_else(|| ""),
|
details=details.unwrap_or_else(|| ""),
|
||||||
version=version(),
|
version=version(),
|
||||||
))
|
), embeddable_at)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(code: StatusCode, content: String, mimetype: String) -> Self {
|
pub fn new(code: StatusCode, content: String, mimetype: Mime) -> Self {
|
||||||
|
Self::new_embeddable(code, content, mimetype, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_embeddable(code: StatusCode, content: String, mimetype: Mime, embeddable_at: Option<u16>) -> Self {
|
||||||
ContentHandler {
|
ContentHandler {
|
||||||
code: code,
|
code: code,
|
||||||
content: content,
|
content: content,
|
||||||
mimetype: mimetype,
|
mimetype: mimetype,
|
||||||
write_pos: 0,
|
write_pos: 0,
|
||||||
|
safe_to_embed_at_port: embeddable_at,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,7 +88,8 @@ impl server::Handler<HttpStream> for ContentHandler {
|
|||||||
|
|
||||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||||
res.set_status(self.code);
|
res.set_status(self.code);
|
||||||
res.headers_mut().set(header::ContentType(self.mimetype.parse().unwrap()));
|
res.headers_mut().set(header::ContentType(self.mimetype.clone()));
|
||||||
|
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_at_port.clone());
|
||||||
Next::write()
|
Next::write()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ impl server::Handler<HttpStream> for EchoHandler {
|
|||||||
|
|
||||||
// Don't even read the payload if origin is forbidden!
|
// Don't even read the payload if origin is forbidden!
|
||||||
if let Cors::Forbidden = self.cors {
|
if let Cors::Forbidden = self.cors {
|
||||||
self.handler = Some(ContentHandler::ok(String::new(), "text/plain".into()));
|
self.handler = Some(ContentHandler::ok(String::new(), mime!(Text/Plain)));
|
||||||
Next::write()
|
Next::write()
|
||||||
} else {
|
} else {
|
||||||
Next::read()
|
Next::read()
|
||||||
@ -92,7 +92,7 @@ impl server::Handler<HttpStream> for EchoHandler {
|
|||||||
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
|
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
|
||||||
match decoder.read_to_string(&mut self.content) {
|
match decoder.read_to_string(&mut self.content) {
|
||||||
Ok(0) => {
|
Ok(0) => {
|
||||||
self.handler = Some(ContentHandler::ok(self.content.clone(), "application/json".into()));
|
self.handler = Some(ContentHandler::ok(self.content.clone(), mime!(Application/Json)));
|
||||||
Next::write()
|
Next::write()
|
||||||
},
|
},
|
||||||
Ok(_) => Next::read(),
|
Ok(_) => Next::read(),
|
||||||
@ -114,11 +114,15 @@ impl server::Handler<HttpStream> for EchoHandler {
|
|||||||
]));
|
]));
|
||||||
headers.set(header::AccessControlAllowOrigin::Value(domain.clone()));
|
headers.set(header::AccessControlAllowOrigin::Value(domain.clone()));
|
||||||
}
|
}
|
||||||
self.handler.as_mut().unwrap().on_response(res)
|
self.handler.as_mut()
|
||||||
|
.expect("handler always set in on_request, which is before now; qed")
|
||||||
|
.on_response(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||||
self.handler.as_mut().unwrap().on_response_writable(encoder)
|
self.handler.as_mut()
|
||||||
|
.expect("handler always set in on_request, which is before now; qed")
|
||||||
|
.on_response_writable(encoder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,24 @@ pub use self::fetch::{ContentFetcherHandler, ContentValidator, FetchControl};
|
|||||||
use url::Url;
|
use url::Url;
|
||||||
use hyper::{server, header, net, uri};
|
use hyper::{server, header, net, uri};
|
||||||
|
|
||||||
|
/// Adds security-related headers to the Response.
|
||||||
|
pub fn add_security_headers(headers: &mut header::Headers, embeddable_at: Option<u16>) {
|
||||||
|
headers.set_raw("X-XSS-Protection", vec![b"1; mode=block".to_vec()]);
|
||||||
|
headers.set_raw("X-Content-Type-Options", vec![b"nosniff".to_vec()]);
|
||||||
|
|
||||||
|
// Embedding header:
|
||||||
|
if let Some(port) = embeddable_at {
|
||||||
|
headers.set_raw(
|
||||||
|
"X-Frame-Options",
|
||||||
|
vec![format!("ALLOW-FROM http://127.0.0.1:{}", port).into_bytes()]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// TODO [ToDr] Should we be more strict here (DENY?)?
|
||||||
|
headers.set_raw("X-Frame-Options", vec![b"SAMEORIGIN".to_vec()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extracts URL part from the Request.
|
||||||
pub fn extract_url(req: &server::Request<net::HttpStream>) -> Option<Url> {
|
pub fn extract_url(req: &server::Request<net::HttpStream>) -> Option<Url> {
|
||||||
match *req.uri() {
|
match *req.uri() {
|
||||||
uri::RequestUri::AbsoluteUri(ref url) => {
|
uri::RequestUri::AbsoluteUri(ref url) => {
|
||||||
|
@ -43,10 +43,8 @@
|
|||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![cfg_attr(feature="nightly", plugin(clippy))]
|
#![cfg_attr(feature="nightly", plugin(clippy))]
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate log;
|
|
||||||
extern crate url as url_lib;
|
|
||||||
extern crate hyper;
|
extern crate hyper;
|
||||||
|
extern crate url as url_lib;
|
||||||
extern crate unicase;
|
extern crate unicase;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
@ -57,14 +55,24 @@ extern crate jsonrpc_core;
|
|||||||
extern crate jsonrpc_http_server;
|
extern crate jsonrpc_http_server;
|
||||||
extern crate mime_guess;
|
extern crate mime_guess;
|
||||||
extern crate rustc_serialize;
|
extern crate rustc_serialize;
|
||||||
extern crate parity_dapps;
|
|
||||||
extern crate ethcore_rpc;
|
extern crate ethcore_rpc;
|
||||||
extern crate ethcore_util as util;
|
extern crate ethcore_util as util;
|
||||||
extern crate linked_hash_map;
|
extern crate linked_hash_map;
|
||||||
extern crate fetch;
|
extern crate fetch;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate mime;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
extern crate ethcore_devtools as devtools;
|
extern crate ethcore_devtools as devtools;
|
||||||
|
|
||||||
|
extern crate parity_dapps_glue;
|
||||||
|
// TODO [ToDr] - Deprecate when we get rid of old dapps.
|
||||||
|
extern crate parity_dapps;
|
||||||
|
|
||||||
mod endpoint;
|
mod endpoint;
|
||||||
mod apps;
|
mod apps;
|
||||||
mod page;
|
mod page;
|
||||||
@ -92,11 +100,11 @@ static DAPPS_DOMAIN : &'static str = ".parity";
|
|||||||
/// Indicates sync status
|
/// Indicates sync status
|
||||||
pub trait SyncStatus: Send + Sync {
|
pub trait SyncStatus: Send + Sync {
|
||||||
/// Returns true if there is a major sync happening.
|
/// Returns true if there is a major sync happening.
|
||||||
fn is_major_syncing(&self) -> bool;
|
fn is_major_importing(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F> SyncStatus for F where F: Fn() -> bool + Send + Sync {
|
impl<F> SyncStatus for F where F: Fn() -> bool + Send + Sync {
|
||||||
fn is_major_syncing(&self) -> bool { self() }
|
fn is_major_importing(&self) -> bool { self() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Webapps HTTP+RPC server build.
|
/// Webapps HTTP+RPC server build.
|
||||||
@ -105,6 +113,7 @@ pub struct ServerBuilder {
|
|||||||
handler: Arc<IoHandler>,
|
handler: Arc<IoHandler>,
|
||||||
registrar: Arc<ContractClient>,
|
registrar: Arc<ContractClient>,
|
||||||
sync_status: Arc<SyncStatus>,
|
sync_status: Arc<SyncStatus>,
|
||||||
|
signer_port: Option<u16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Extendable for ServerBuilder {
|
impl Extendable for ServerBuilder {
|
||||||
@ -121,6 +130,7 @@ impl ServerBuilder {
|
|||||||
handler: Arc::new(IoHandler::new()),
|
handler: Arc::new(IoHandler::new()),
|
||||||
registrar: registrar,
|
registrar: registrar,
|
||||||
sync_status: Arc::new(|| false),
|
sync_status: Arc::new(|| false),
|
||||||
|
signer_port: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,6 +139,11 @@ impl ServerBuilder {
|
|||||||
self.sync_status = status;
|
self.sync_status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Change default signer port.
|
||||||
|
pub fn with_signer_port(&mut self, signer_port: Option<u16>) {
|
||||||
|
self.signer_port = signer_port;
|
||||||
|
}
|
||||||
|
|
||||||
/// Asynchronously start server with no authentication,
|
/// Asynchronously start server with no authentication,
|
||||||
/// returns result with `Server` handle on success or an error.
|
/// returns result with `Server` handle on success or an error.
|
||||||
pub fn start_unsecured_http(&self, addr: &SocketAddr, hosts: Option<Vec<String>>) -> Result<Server, ServerError> {
|
pub fn start_unsecured_http(&self, addr: &SocketAddr, hosts: Option<Vec<String>>) -> Result<Server, ServerError> {
|
||||||
@ -138,6 +153,7 @@ impl ServerBuilder {
|
|||||||
NoAuth,
|
NoAuth,
|
||||||
self.handler.clone(),
|
self.handler.clone(),
|
||||||
self.dapps_path.clone(),
|
self.dapps_path.clone(),
|
||||||
|
self.signer_port.clone(),
|
||||||
self.registrar.clone(),
|
self.registrar.clone(),
|
||||||
self.sync_status.clone(),
|
self.sync_status.clone(),
|
||||||
)
|
)
|
||||||
@ -152,6 +168,7 @@ impl ServerBuilder {
|
|||||||
HttpBasicAuth::single_user(username, password),
|
HttpBasicAuth::single_user(username, password),
|
||||||
self.handler.clone(),
|
self.handler.clone(),
|
||||||
self.dapps_path.clone(),
|
self.dapps_path.clone(),
|
||||||
|
self.signer_port.clone(),
|
||||||
self.registrar.clone(),
|
self.registrar.clone(),
|
||||||
self.sync_status.clone(),
|
self.sync_status.clone(),
|
||||||
)
|
)
|
||||||
@ -186,13 +203,14 @@ impl Server {
|
|||||||
authorization: A,
|
authorization: A,
|
||||||
handler: Arc<IoHandler>,
|
handler: Arc<IoHandler>,
|
||||||
dapps_path: String,
|
dapps_path: String,
|
||||||
|
signer_port: Option<u16>,
|
||||||
registrar: Arc<ContractClient>,
|
registrar: Arc<ContractClient>,
|
||||||
sync_status: Arc<SyncStatus>,
|
sync_status: Arc<SyncStatus>,
|
||||||
) -> Result<Server, ServerError> {
|
) -> Result<Server, ServerError> {
|
||||||
let panic_handler = Arc::new(Mutex::new(None));
|
let panic_handler = Arc::new(Mutex::new(None));
|
||||||
let authorization = Arc::new(authorization);
|
let authorization = Arc::new(authorization);
|
||||||
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(apps::urlhint::URLHintContract::new(registrar), sync_status));
|
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(apps::urlhint::URLHintContract::new(registrar), sync_status));
|
||||||
let endpoints = Arc::new(apps::all_endpoints(dapps_path));
|
let endpoints = Arc::new(apps::all_endpoints(dapps_path, signer_port));
|
||||||
let special = Arc::new({
|
let special = Arc::new({
|
||||||
let mut special = HashMap::new();
|
let mut special = HashMap::new();
|
||||||
special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, panic_handler.clone()));
|
special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, panic_handler.clone()));
|
||||||
|
@ -25,7 +25,7 @@ pub struct PageEndpoint<T : WebApp + 'static> {
|
|||||||
/// Prefix to strip from the path (when `None` deducted from `app_id`)
|
/// Prefix to strip from the path (when `None` deducted from `app_id`)
|
||||||
pub prefix: Option<String>,
|
pub prefix: Option<String>,
|
||||||
/// Safe to be loaded in frame by other origin. (use wisely!)
|
/// Safe to be loaded in frame by other origin. (use wisely!)
|
||||||
safe_to_embed: bool,
|
safe_to_embed_at_port: Option<u16>,
|
||||||
info: EndpointInfo,
|
info: EndpointInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ impl<T: WebApp + 'static> PageEndpoint<T> {
|
|||||||
PageEndpoint {
|
PageEndpoint {
|
||||||
app: Arc::new(app),
|
app: Arc::new(app),
|
||||||
prefix: None,
|
prefix: None,
|
||||||
safe_to_embed: false,
|
safe_to_embed_at_port: None,
|
||||||
info: EndpointInfo::from(info),
|
info: EndpointInfo::from(info),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -49,7 +49,7 @@ impl<T: WebApp + 'static> PageEndpoint<T> {
|
|||||||
PageEndpoint {
|
PageEndpoint {
|
||||||
app: Arc::new(app),
|
app: Arc::new(app),
|
||||||
prefix: Some(prefix),
|
prefix: Some(prefix),
|
||||||
safe_to_embed: false,
|
safe_to_embed_at_port: None,
|
||||||
info: EndpointInfo::from(info),
|
info: EndpointInfo::from(info),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -57,12 +57,12 @@ impl<T: WebApp + 'static> PageEndpoint<T> {
|
|||||||
/// Creates new `PageEndpoint` which can be safely used in iframe
|
/// Creates new `PageEndpoint` which can be safely used in iframe
|
||||||
/// even from different origin. It might be dangerous (clickjacking).
|
/// even from different origin. It might be dangerous (clickjacking).
|
||||||
/// Use wisely!
|
/// Use wisely!
|
||||||
pub fn new_safe_to_embed(app: T) -> Self {
|
pub fn new_safe_to_embed(app: T, port: Option<u16>) -> Self {
|
||||||
let info = app.info();
|
let info = app.info();
|
||||||
PageEndpoint {
|
PageEndpoint {
|
||||||
app: Arc::new(app),
|
app: Arc::new(app),
|
||||||
prefix: None,
|
prefix: None,
|
||||||
safe_to_embed: true,
|
safe_to_embed_at_port: port,
|
||||||
info: EndpointInfo::from(info),
|
info: EndpointInfo::from(info),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,8 +79,8 @@ impl<T: WebApp> Endpoint for PageEndpoint<T> {
|
|||||||
app: BuiltinDapp::new(self.app.clone()),
|
app: BuiltinDapp::new(self.app.clone()),
|
||||||
prefix: self.prefix.clone(),
|
prefix: self.prefix.clone(),
|
||||||
path: path,
|
path: path,
|
||||||
file: Default::default(),
|
file: handler::ServedFile::new(self.safe_to_embed_at_port.clone()),
|
||||||
safe_to_embed: self.safe_to_embed,
|
safe_to_embed_at_port: self.safe_to_embed_at_port.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ use hyper::net::HttpStream;
|
|||||||
use hyper::status::StatusCode;
|
use hyper::status::StatusCode;
|
||||||
use hyper::{Decoder, Encoder, Next};
|
use hyper::{Decoder, Encoder, Next};
|
||||||
use endpoint::EndpointPath;
|
use endpoint::EndpointPath;
|
||||||
use handlers::ContentHandler;
|
use handlers::{ContentHandler, add_security_headers};
|
||||||
|
|
||||||
/// Represents a file that can be sent to client.
|
/// Represents a file that can be sent to client.
|
||||||
/// Implementation should keep track of bytes already sent internally.
|
/// Implementation should keep track of bytes already sent internally.
|
||||||
@ -57,13 +57,14 @@ pub enum ServedFile<T: Dapp> {
|
|||||||
Error(ContentHandler),
|
Error(ContentHandler),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Dapp> Default for ServedFile<T> {
|
impl<T: Dapp> ServedFile<T> {
|
||||||
fn default() -> Self {
|
pub fn new(embeddable_at: Option<u16>) -> Self {
|
||||||
ServedFile::Error(ContentHandler::error(
|
ServedFile::Error(ContentHandler::error_embeddable(
|
||||||
StatusCode::NotFound,
|
StatusCode::NotFound,
|
||||||
"404 Not Found",
|
"404 Not Found",
|
||||||
"Requested dapp resource was not found.",
|
"Requested dapp resource was not found.",
|
||||||
None
|
None,
|
||||||
|
embeddable_at,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,7 +82,7 @@ pub struct PageHandler<T: Dapp> {
|
|||||||
/// Requested path.
|
/// Requested path.
|
||||||
pub path: EndpointPath,
|
pub path: EndpointPath,
|
||||||
/// Flag indicating if the file can be safely embeded (put in iframe).
|
/// Flag indicating if the file can be safely embeded (put in iframe).
|
||||||
pub safe_to_embed: bool,
|
pub safe_to_embed_at_port: Option<u16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Dapp> PageHandler<T> {
|
impl<T: Dapp> PageHandler<T> {
|
||||||
@ -115,7 +116,7 @@ impl<T: Dapp> server::Handler<HttpStream> for PageHandler<T> {
|
|||||||
self.app.file(&self.extract_path(url.path()))
|
self.app.file(&self.extract_path(url.path()))
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
}.map_or_else(|| ServedFile::default(), |f| ServedFile::File(f));
|
}.map_or_else(|| ServedFile::new(self.safe_to_embed_at_port.clone()), |f| ServedFile::File(f));
|
||||||
Next::write()
|
Next::write()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,10 +128,14 @@ impl<T: Dapp> server::Handler<HttpStream> for PageHandler<T> {
|
|||||||
match self.file {
|
match self.file {
|
||||||
ServedFile::File(ref f) => {
|
ServedFile::File(ref f) => {
|
||||||
res.set_status(StatusCode::Ok);
|
res.set_status(StatusCode::Ok);
|
||||||
res.headers_mut().set(header::ContentType(f.content_type().parse().unwrap()));
|
|
||||||
if !self.safe_to_embed {
|
match f.content_type().parse() {
|
||||||
res.headers_mut().set_raw("X-Frame-Options", vec![b"SAMEORIGIN".to_vec()]);
|
Ok(mime) => res.headers_mut().set(header::ContentType(mime)),
|
||||||
|
Err(()) => debug!(target: "page_handler", "invalid MIME type: {}", f.content_type()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Security headers:
|
||||||
|
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_at_port);
|
||||||
Next::write()
|
Next::write()
|
||||||
},
|
},
|
||||||
ServedFile::Error(ref mut handler) => {
|
ServedFile::Error(ref mut handler) => {
|
||||||
@ -212,8 +217,8 @@ fn should_extract_path_with_appid() {
|
|||||||
port: 8080,
|
port: 8080,
|
||||||
using_dapps_domains: true,
|
using_dapps_domains: true,
|
||||||
},
|
},
|
||||||
file: Default::default(),
|
file: ServedFile::new(None),
|
||||||
safe_to_embed: true,
|
safe_to_embed_at_port: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// when
|
// when
|
||||||
|
@ -61,16 +61,16 @@ impl Endpoint for LocalPageEndpoint {
|
|||||||
app: LocalSingleFile { path: self.path.clone(), mime: mime.clone() },
|
app: LocalSingleFile { path: self.path.clone(), mime: mime.clone() },
|
||||||
prefix: None,
|
prefix: None,
|
||||||
path: path,
|
path: path,
|
||||||
file: Default::default(),
|
file: handler::ServedFile::new(None),
|
||||||
safe_to_embed: false,
|
safe_to_embed_at_port: None,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Box::new(handler::PageHandler {
|
Box::new(handler::PageHandler {
|
||||||
app: LocalDapp { path: self.path.clone() },
|
app: LocalDapp { path: self.path.clone() },
|
||||||
prefix: None,
|
prefix: None,
|
||||||
path: path,
|
path: path,
|
||||||
file: Default::default(),
|
file: handler::ServedFile::new(None),
|
||||||
safe_to_embed: false,
|
safe_to_embed_at_port: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ function FindProxyForURL(url, host) {{
|
|||||||
}}
|
}}
|
||||||
"#,
|
"#,
|
||||||
DAPPS_DOMAIN, path.host, path.port);
|
DAPPS_DOMAIN, path.host, path.port);
|
||||||
Box::new(ContentHandler::ok(content, "application/javascript".to_owned()))
|
Box::new(ContentHandler::ok(content, mime!(Application/Javascript)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,11 +81,15 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
|
|||||||
self.handler = match endpoint {
|
self.handler = match endpoint {
|
||||||
// First check special endpoints
|
// First check special endpoints
|
||||||
(ref path, ref endpoint) if self.special.contains_key(endpoint) => {
|
(ref path, ref endpoint) if self.special.contains_key(endpoint) => {
|
||||||
self.special.get(endpoint).unwrap().to_async_handler(path.clone().unwrap_or_default(), control)
|
self.special.get(endpoint)
|
||||||
|
.expect("special known to contain key; qed")
|
||||||
|
.to_async_handler(path.clone().unwrap_or_default(), control)
|
||||||
},
|
},
|
||||||
// Then delegate to dapp
|
// Then delegate to dapp
|
||||||
(Some(ref path), _) if self.endpoints.contains_key(&path.app_id) => {
|
(Some(ref path), _) if self.endpoints.contains_key(&path.app_id) => {
|
||||||
self.endpoints.get(&path.app_id).unwrap().to_async_handler(path.clone(), control)
|
self.endpoints.get(&path.app_id)
|
||||||
|
.expect("special known to contain key; qed")
|
||||||
|
.to_async_handler(path.clone(), control)
|
||||||
},
|
},
|
||||||
// Try to resolve and fetch the dapp
|
// Try to resolve and fetch the dapp
|
||||||
(Some(ref path), _) if self.fetch.contains(&path.app_id) => {
|
(Some(ref path), _) if self.fetch.contains(&path.app_id) => {
|
||||||
@ -108,7 +112,9 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
|
|||||||
},
|
},
|
||||||
// RPC by default
|
// RPC by default
|
||||||
_ => {
|
_ => {
|
||||||
self.special.get(&SpecialEndpoint::Rpc).unwrap().to_async_handler(EndpointPath::default(), control)
|
self.special.get(&SpecialEndpoint::Rpc)
|
||||||
|
.expect("RPC endpoint always stored; qed")
|
||||||
|
.to_async_handler(EndpointPath::default(), control)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -143,7 +149,9 @@ impl<A: Authorization> Router<A> {
|
|||||||
allowed_hosts: Option<Vec<String>>,
|
allowed_hosts: Option<Vec<String>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
|
||||||
let handler = special.get(&SpecialEndpoint::Utils).unwrap().to_handler(EndpointPath::default());
|
let handler = special.get(&SpecialEndpoint::Utils)
|
||||||
|
.expect("Utils endpoint always stored; qed")
|
||||||
|
.to_handler(EndpointPath::default());
|
||||||
Router {
|
Router {
|
||||||
control: Some(control),
|
control: Some(control),
|
||||||
main_page: main_page,
|
main_page: main_page,
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use tests::helpers::{serve, serve_with_registrar, request};
|
use tests::helpers::{serve, serve_with_registrar, request, assert_security_headers};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_return_error() {
|
fn should_return_error() {
|
||||||
@ -36,6 +36,7 @@ fn should_return_error() {
|
|||||||
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
|
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
|
||||||
assert_eq!(response.headers.get(0).unwrap(), "Content-Type: application/json");
|
assert_eq!(response.headers.get(0).unwrap(), "Content-Type: application/json");
|
||||||
assert_eq!(response.body, format!("58\n{}\n0\n\n", r#"{"code":"404","title":"Not Found","detail":"Resource you requested has not been found."}"#));
|
assert_eq!(response.body, format!("58\n{}\n0\n\n", r#"{"code":"404","title":"Not Found","detail":"Resource you requested has not been found."}"#));
|
||||||
|
assert_security_headers(&response.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -58,6 +59,7 @@ fn should_serve_apps() {
|
|||||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||||
assert_eq!(response.headers.get(0).unwrap(), "Content-Type: application/json");
|
assert_eq!(response.headers.get(0).unwrap(), "Content-Type: application/json");
|
||||||
assert!(response.body.contains("Parity Home Screen"), response.body);
|
assert!(response.body.contains("Parity Home Screen"), response.body);
|
||||||
|
assert_security_headers(&response.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -80,6 +82,7 @@ fn should_handle_ping() {
|
|||||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||||
assert_eq!(response.headers.get(0).unwrap(), "Content-Type: application/json");
|
assert_eq!(response.headers.get(0).unwrap(), "Content-Type: application/json");
|
||||||
assert_eq!(response.body, "0\n\n".to_owned());
|
assert_eq!(response.body, "0\n\n".to_owned());
|
||||||
|
assert_security_headers(&response.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -101,5 +104,6 @@ fn should_try_to_resolve_dapp() {
|
|||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
|
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
|
||||||
assert_eq!(registrar.calls.lock().len(), 2);
|
assert_eq!(registrar.calls.lock().len(), 2);
|
||||||
|
assert_security_headers(&response.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use tests::helpers::{serve_with_auth, request};
|
use tests::helpers::{serve_with_auth, request, assert_security_headers};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_require_authorization() {
|
fn should_require_authorization() {
|
||||||
@ -76,4 +76,5 @@ fn should_allow_on_valid_auth() {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||||
|
assert_security_headers(&response.headers);
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use tests::helpers::{serve_with_registrar, request};
|
use tests::helpers::{serve_with_registrar, request, assert_security_headers};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_resolve_dapp() {
|
fn should_resolve_dapp() {
|
||||||
@ -34,5 +34,6 @@ fn should_resolve_dapp() {
|
|||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
|
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
|
||||||
assert_eq!(registrar.calls.lock().len(), 2);
|
assert_eq!(registrar.calls.lock().len(), 2);
|
||||||
|
assert_security_headers(&response.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,3 +92,7 @@ pub fn serve() -> Server {
|
|||||||
pub fn request(server: Server, request: &str) -> http_client::Response {
|
pub fn request(server: Server, request: &str) -> http_client::Response {
|
||||||
http_client::request(server.addr(), request)
|
http_client::request(server.addr(), request)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn assert_security_headers(headers: &[String]) {
|
||||||
|
http_client::assert_security_headers_present(headers)
|
||||||
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use tests::helpers::{serve, request};
|
use tests::helpers::{serve, request, assert_security_headers};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_redirect_to_home() {
|
fn should_redirect_to_home() {
|
||||||
@ -74,6 +74,7 @@ fn should_display_404_on_invalid_dapp() {
|
|||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
|
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
|
||||||
assert!(response.body.contains("href=\"/home/"));
|
assert!(response.body.contains("href=\"/home/"));
|
||||||
|
assert_security_headers(&response.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -94,6 +95,7 @@ fn should_display_404_on_invalid_dapp_with_domain() {
|
|||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
|
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
|
||||||
assert!(response.body.contains("href=\"http://home.parity/"));
|
assert!(response.body.contains("href=\"http://home.parity/"));
|
||||||
|
assert_security_headers(&response.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -160,6 +162,7 @@ fn should_serve_proxy_pac() {
|
|||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||||
assert_eq!(response.body, "86\n\nfunction FindProxyForURL(url, host) {\n\tif (shExpMatch(host, \"*.parity\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:8080\";\n\t}\n\n\treturn \"DIRECT\";\n}\n\n0\n\n".to_owned());
|
assert_eq!(response.body, "86\n\nfunction FindProxyForURL(url, host) {\n\tif (shExpMatch(host, \"*.parity\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:8080\";\n\t}\n\n\treturn \"DIRECT\";\n}\n\n0\n\n".to_owned());
|
||||||
|
assert_security_headers(&response.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -181,5 +184,6 @@ fn should_serve_utils() {
|
|||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||||
assert_eq!(response.body.contains("function(){"), true);
|
assert_eq!(response.body.contains("function(){"), true);
|
||||||
|
assert_security_headers(&response.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
19
dapps/ui/Cargo.toml
Normal file
19
dapps/ui/Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
[package]
|
||||||
|
description = "Parity built-in dapps."
|
||||||
|
name = "parity-ui"
|
||||||
|
version = "1.4.0"
|
||||||
|
license = "GPL-3.0"
|
||||||
|
authors = ["Ethcore <admin@ethcore.io>"]
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["with-syntex"]
|
||||||
|
use-precompiled-js = ["parity-dapps-glue/use-precompiled-js"]
|
||||||
|
with-syntex = ["parity-dapps-glue/with-syntex"]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
parity-dapps-glue = { path = "../js-glue" }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
parity-dapps-glue = { path = "../js-glue" }
|
||||||
|
|
22
dapps/ui/build.rs
Normal file
22
dapps/ui/build.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
extern crate parity_dapps_glue;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
parity_dapps_glue::js::build(concat!(env!("CARGO_MANIFEST_DIR"), "/../../js"));
|
||||||
|
parity_dapps_glue::generate();
|
||||||
|
}
|
22
dapps/ui/src/lib.rs
Normal file
22
dapps/ui/src/lib.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
#[cfg(feature = "with-syntex")]
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/lib.rs"));
|
||||||
|
|
||||||
|
#[cfg(not(feature = "with-syntex"))]
|
||||||
|
include!("lib.rs.in");
|
||||||
|
|
55
dapps/ui/src/lib.rs.in
Normal file
55
dapps/ui/src/lib.rs.in
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
extern crate parity_dapps_glue;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use parity_dapps_glue::{WebApp, File, Info};
|
||||||
|
|
||||||
|
#[derive(WebAppFiles)]
|
||||||
|
#[webapp(path = "../../../js/build")]
|
||||||
|
pub struct App {
|
||||||
|
pub files: HashMap<&'static str, File>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for App {
|
||||||
|
fn default() -> App {
|
||||||
|
App {
|
||||||
|
files: Self::files(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WebApp for App {
|
||||||
|
fn file(&self, path: &str) -> Option<&File> {
|
||||||
|
self.files.get(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn info(&self) -> Info {
|
||||||
|
Info {
|
||||||
|
name: "Parity UI",
|
||||||
|
version: env!("CARGO_PKG_VERSION"),
|
||||||
|
author: "Ethcore <admin@ethcore.io>",
|
||||||
|
description: "New UI for Parity. (Experimental)",
|
||||||
|
icon_url: "icon.png",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_js() {
|
||||||
|
parity_dapps_glue::js::build(concat!(env!("CARGO_MANIFEST_DIR"), "/../../js"));
|
||||||
|
}
|
@ -64,3 +64,18 @@ pub fn request(address: &SocketAddr, request: &str) -> Response {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if all required security headers are present
|
||||||
|
pub fn assert_security_headers_present(headers: &[String]) {
|
||||||
|
assert!(
|
||||||
|
headers.iter().find(|header| header.as_str() == "X-Frame-Options: SAMEORIGIN").is_some(),
|
||||||
|
"X-Frame-Options missing: {:?}", headers
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
headers.iter().find(|header| header.as_str() == "X-XSS-Protection: 1; mode=block").is_some(),
|
||||||
|
"X-XSS-Protection missing: {:?}", headers
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
headers.iter().find(|header| header.as_str() == "X-Content-Type-Options: nosniff").is_some(),
|
||||||
|
"X-Content-Type-Options missing: {:?}", headers
|
||||||
|
);
|
||||||
|
}
|
||||||
|
39
docker/ubuntu-stable/Dockerfile
Normal file
39
docker/ubuntu-stable/Dockerfile
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
FROM ubuntu:14.04
|
||||||
|
WORKDIR /build
|
||||||
|
# install tools and dependencies
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y \
|
||||||
|
g++ \
|
||||||
|
curl \
|
||||||
|
git \
|
||||||
|
file \
|
||||||
|
binutils
|
||||||
|
|
||||||
|
# install rustup
|
||||||
|
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||||
|
|
||||||
|
# rustup directory
|
||||||
|
ENV PATH /root/.cargo/bin:$PATH
|
||||||
|
|
||||||
|
# show backtraces
|
||||||
|
ENV RUST_BACKTRACE 1
|
||||||
|
|
||||||
|
# show tools
|
||||||
|
RUN rustc -vV && \
|
||||||
|
cargo -V && \
|
||||||
|
gcc -v &&\
|
||||||
|
g++ -v
|
||||||
|
|
||||||
|
# build parity
|
||||||
|
RUN git clone https://github.com/ethcore/parity && \
|
||||||
|
cd parity && \
|
||||||
|
git checkout stable && \
|
||||||
|
git pull && \
|
||||||
|
cargo build --release --verbose && \
|
||||||
|
ls /build/parity/target/release/parity && \
|
||||||
|
strip /build/parity/target/release/parity
|
||||||
|
|
||||||
|
RUN file /build/parity/target/release/parity
|
||||||
|
|
||||||
|
EXPOSE 8080 8545 8180
|
||||||
|
ENTRYPOINT ["/build/parity/target/release/parity"]
|
@ -9,4 +9,4 @@ authors = ["arkpar <arkadiy@ethcore.io"]
|
|||||||
log = "0.3"
|
log = "0.3"
|
||||||
sha3 = { path = "../util/sha3" }
|
sha3 = { path = "../util/sha3" }
|
||||||
primal = "0.2.3"
|
primal = "0.2.3"
|
||||||
parking_lot = "0.2.6"
|
parking_lot = "0.3"
|
||||||
|
@ -37,7 +37,7 @@ ethkey = { path = "../ethkey" }
|
|||||||
ethcore-ipc-nano = { path = "../ipc/nano" }
|
ethcore-ipc-nano = { path = "../ipc/nano" }
|
||||||
rlp = { path = "../util/rlp" }
|
rlp = { path = "../util/rlp" }
|
||||||
rand = "0.3"
|
rand = "0.3"
|
||||||
lru-cache = "0.0.7"
|
lru-cache = { git = "https://github.com/contain-rs/lru-cache" }
|
||||||
ethcore-bloom-journal = { path = "../util/bloom" }
|
ethcore-bloom-journal = { path = "../util/bloom" }
|
||||||
byteorder = "0.5"
|
byteorder = "0.5"
|
||||||
|
|
||||||
|
@ -10,7 +10,8 @@
|
|||||||
"durationLimit": "0x0d",
|
"durationLimit": "0x0d",
|
||||||
"blockReward": "0x4563918244F40000",
|
"blockReward": "0x4563918244F40000",
|
||||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||||
"frontierCompatibilityModeLimit": "0x118c30"
|
"homesteadTransition": "0x118c30",
|
||||||
|
"eip150Transition": "0x2625a0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
43
ethcore/res/ethereum/eip150_test.json
Normal file
43
ethcore/res/ethereum/eip150_test.json
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"name": "Homestead (Test)",
|
||||||
|
"engine": {
|
||||||
|
"Ethash": {
|
||||||
|
"params": {
|
||||||
|
"gasLimitBoundDivisor": "0x0400",
|
||||||
|
"minimumDifficulty": "0x020000",
|
||||||
|
"difficultyBoundDivisor": "0x0800",
|
||||||
|
"durationLimit": "0x0d",
|
||||||
|
"blockReward": "0x4563918244F40000",
|
||||||
|
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||||
|
"homesteadTransition": "0x0",
|
||||||
|
"eip150Transition": "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 } } } }
|
||||||
|
}
|
||||||
|
}
|
@ -11,10 +11,11 @@
|
|||||||
"durationLimit": "0x3C",
|
"durationLimit": "0x3C",
|
||||||
"blockReward": "0x6f05b59d3b200000",
|
"blockReward": "0x6f05b59d3b200000",
|
||||||
"registrar" : "0x6c221ca53705f3497ec90ca7b84c59ae7382fc21",
|
"registrar" : "0x6c221ca53705f3497ec90ca7b84c59ae7382fc21",
|
||||||
"frontierCompatibilityModeLimit": "0x30d40",
|
"homesteadTransition": "0x30d40",
|
||||||
"difficultyHardforkTransition": "0x59d9",
|
"difficultyHardforkTransition": "0x59d9",
|
||||||
"difficultyHardforkBoundDivisor": "0x0200",
|
"difficultyHardforkBoundDivisor": "0x0200",
|
||||||
"bombDefuseTransition": "0x30d40"
|
"bombDefuseTransition": "0x30d40",
|
||||||
|
"eip150Transition": "0x7fffffffffffffff"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -8,8 +8,8 @@
|
|||||||
"difficultyBoundDivisor": "0x0800",
|
"difficultyBoundDivisor": "0x0800",
|
||||||
"durationLimit": "0x0d",
|
"durationLimit": "0x0d",
|
||||||
"blockReward": "0x4563918244F40000",
|
"blockReward": "0x4563918244F40000",
|
||||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
"registrar" : "0x3bb2bb5c6c9c9b7f4ef430b47dc7e026310042ea",
|
||||||
"frontierCompatibilityModeLimit": "0x118c30",
|
"homesteadTransition": "0x118c30",
|
||||||
"daoHardforkTransition": "0x1d4c00",
|
"daoHardforkTransition": "0x1d4c00",
|
||||||
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
||||||
"daoHardforkAccounts": [
|
"daoHardforkAccounts": [
|
||||||
@ -129,7 +129,8 @@
|
|||||||
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
||||||
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
||||||
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
||||||
]
|
],
|
||||||
|
"eip150Transition": "0x259518"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -163,7 +164,8 @@
|
|||||||
"enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303",
|
"enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303",
|
||||||
"enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303",
|
"enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303",
|
||||||
"enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303",
|
"enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303",
|
||||||
"enode://248f12bc8b18d5289358085520ac78cd8076485211e6d96ab0bc93d6cd25442db0ce3a937dc404f64f207b0b9aed50e25e98ce32af5ac7cb321ff285b97de485@zero.parity.io:30303"
|
"enode://4cd540b2c3292e17cff39922e864094bf8b0741fcc8c5dcea14957e389d7944c70278d872902e3d0345927f621547efa659013c400865485ab4bfa0c6596936f@zero.parity.io:30303",
|
||||||
|
"enode://cc92c4c40d612a10c877ca023ef0496c843fbc92b6c6c0d55ce0b863d51d821c4bd70daebb54324a6086374e6dc05708fed39862b275f169cb678e655da9d07d@136.243.154.246:30303"
|
||||||
],
|
],
|
||||||
"accounts": {
|
"accounts": {
|
||||||
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
"durationLimit": "0x0d",
|
"durationLimit": "0x0d",
|
||||||
"blockReward": "0x4563918244F40000",
|
"blockReward": "0x4563918244F40000",
|
||||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||||
"frontierCompatibilityModeLimit": "0x118c30",
|
"homesteadTransition": "0x118c30",
|
||||||
"daoHardforkTransition": "0x1d4c00",
|
"daoHardforkTransition": "0x1d4c00",
|
||||||
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
||||||
"daoHardforkAccounts": [
|
"daoHardforkAccounts": [
|
||||||
@ -129,7 +129,8 @@
|
|||||||
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
||||||
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
||||||
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
||||||
]
|
],
|
||||||
|
"eip150Transition": "0x7fffffffffffffff"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
"durationLimit": "0x0d",
|
"durationLimit": "0x0d",
|
||||||
"blockReward": "0x4563918244F40000",
|
"blockReward": "0x4563918244F40000",
|
||||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||||
"frontierCompatibilityModeLimit": "0xffffffffffffffff"
|
"homesteadTransition": "0x7fffffffffffffff",
|
||||||
|
"eip150Transition": "0x7fffffffffffffff"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
"durationLimit": "0x0d",
|
"durationLimit": "0x0d",
|
||||||
"blockReward": "0x4563918244F40000",
|
"blockReward": "0x4563918244F40000",
|
||||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||||
"frontierCompatibilityModeLimit": "0x0"
|
"homesteadTransition": "0x0",
|
||||||
|
"eip150Transition": "0x7fffffffffffffff"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
"durationLimit": "0x0d",
|
"durationLimit": "0x0d",
|
||||||
"blockReward": "0x4563918244F40000",
|
"blockReward": "0x4563918244F40000",
|
||||||
"registrar": "0x52dff57a8a1532e6afb3dc07e2af58bb9eb05b3d",
|
"registrar": "0x52dff57a8a1532e6afb3dc07e2af58bb9eb05b3d",
|
||||||
"frontierCompatibilityModeLimit": "0x789b0"
|
"homesteadTransition": "0x789b0",
|
||||||
|
"eip150Transition": "0x1b34d8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -17,7 +18,9 @@
|
|||||||
"accountStartNonce": "0x0100000",
|
"accountStartNonce": "0x0100000",
|
||||||
"maximumExtraDataSize": "0x20",
|
"maximumExtraDataSize": "0x20",
|
||||||
"minGasLimit": "0x1388",
|
"minGasLimit": "0x1388",
|
||||||
"networkID" : "0x2"
|
"networkID" : "0x2",
|
||||||
|
"forkBlock": "0x1b34d8",
|
||||||
|
"forkCanonHash": "0xf376243aeff1f256d970714c3de9fd78fa4e63cf63e32a51fe1169e375d98145"
|
||||||
},
|
},
|
||||||
"genesis": {
|
"genesis": {
|
||||||
"seal": {
|
"seal": {
|
||||||
|
@ -8,7 +8,9 @@
|
|||||||
"difficultyBoundDivisor": "0x0800",
|
"difficultyBoundDivisor": "0x0800",
|
||||||
"durationLimit": "0x08",
|
"durationLimit": "0x08",
|
||||||
"blockReward": "0x14D1120D7B160000",
|
"blockReward": "0x14D1120D7B160000",
|
||||||
"registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050"
|
"registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050",
|
||||||
|
"homesteadTransition": "0x7fffffffffffffff",
|
||||||
|
"eip150Transition": "0x7fffffffffffffff"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 8f07dbc8294a32db5ebe8098925fcefc2eab3e71
|
Subproject commit 97066e40ccd061f727deb5cd860e4d9135aa2551
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "DAO hard-fork consensus test",
|
"name": "EIP150.1b hard-fork consensus test",
|
||||||
"engine": {
|
"engine": {
|
||||||
"Ethash": {
|
"Ethash": {
|
||||||
"params": {
|
"params": {
|
||||||
@ -9,7 +9,7 @@
|
|||||||
"durationLimit": "0x0d",
|
"durationLimit": "0x0d",
|
||||||
"blockReward": "0x4563918244F40000",
|
"blockReward": "0x4563918244F40000",
|
||||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||||
"frontierCompatibilityModeLimit": "0x5",
|
"homesteadTransition": "0x5",
|
||||||
"daoHardforkTransition": "0x8",
|
"daoHardforkTransition": "0x8",
|
||||||
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
||||||
"daoHardforkAccounts": [
|
"daoHardforkAccounts": [
|
||||||
@ -129,7 +129,8 @@
|
|||||||
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
||||||
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
||||||
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
||||||
]
|
],
|
||||||
|
"eip150Transition": "0xa"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
@ -23,7 +23,7 @@ use std::time::{Instant, Duration};
|
|||||||
use util::{Mutex, RwLock};
|
use util::{Mutex, RwLock};
|
||||||
use ethstore::{SecretStore, Error as SSError, SafeAccount, EthStore};
|
use ethstore::{SecretStore, Error as SSError, SafeAccount, EthStore};
|
||||||
use ethstore::dir::{KeyDirectory};
|
use ethstore::dir::{KeyDirectory};
|
||||||
use ethstore::ethkey::{Address, Message, Secret, Random, Generator};
|
use ethstore::ethkey::{Address, Message, Public, Secret, Random, Generator};
|
||||||
use ethjson::misc::AccountMeta;
|
use ethjson::misc::AccountMeta;
|
||||||
pub use ethstore::ethkey::Signature;
|
pub use ethstore::ethkey::Signature;
|
||||||
|
|
||||||
@ -176,15 +176,23 @@ impl AccountProvider {
|
|||||||
AccountProvider {
|
AccountProvider {
|
||||||
unlocked: Mutex::new(HashMap::new()),
|
unlocked: Mutex::new(HashMap::new()),
|
||||||
address_book: Mutex::new(AddressBook::new(Default::default())),
|
address_book: Mutex::new(AddressBook::new(Default::default())),
|
||||||
sstore: Box::new(EthStore::open(Box::new(NullDir::default())).unwrap())
|
sstore: Box::new(EthStore::open(Box::new(NullDir::default()))
|
||||||
|
.expect("NullDir load always succeeds; qed"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates new random account.
|
/// Creates new random account.
|
||||||
pub fn new_account(&self, password: &str) -> Result<Address, Error> {
|
pub fn new_account(&self, password: &str) -> Result<Address, Error> {
|
||||||
let secret = Random.generate().unwrap().secret().clone();
|
self.new_account_and_public(password).map(|d| d.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates new random account and returns address and public key
|
||||||
|
pub fn new_account_and_public(&self, password: &str) -> Result<(Address, Public), Error> {
|
||||||
|
let acc = Random.generate().expect("secp context has generation capabilities; qed");
|
||||||
|
let public = acc.public().clone();
|
||||||
|
let secret = acc.secret().clone();
|
||||||
let address = try!(self.sstore.insert_account(secret, password));
|
let address = try!(self.sstore.insert_account(secret, password));
|
||||||
Ok(address)
|
Ok((address, public))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts new account into underlying store.
|
/// Inserts new account into underlying store.
|
||||||
@ -280,6 +288,21 @@ impl AccountProvider {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn password(&self, account: &Address) -> Result<String, Error> {
|
||||||
|
let mut unlocked = self.unlocked.lock();
|
||||||
|
let data = try!(unlocked.get(account).ok_or(Error::NotUnlocked)).clone();
|
||||||
|
if let Unlock::Temp = data.unlock {
|
||||||
|
unlocked.remove(account).expect("data exists: so key must exist: qed");
|
||||||
|
}
|
||||||
|
if let Unlock::Timed((ref start, ref duration)) = data.unlock {
|
||||||
|
if start.elapsed() > Duration::from_millis(*duration as u64) {
|
||||||
|
unlocked.remove(account).expect("data exists: so key must exist: qed");
|
||||||
|
return Err(Error::NotUnlocked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(data.password.clone())
|
||||||
|
}
|
||||||
|
|
||||||
/// Unlocks account permanently.
|
/// Unlocks account permanently.
|
||||||
pub fn unlock_account_permanently(&self, account: Address, password: String) -> Result<(), Error> {
|
pub fn unlock_account_permanently(&self, account: Address, password: String) -> Result<(), Error> {
|
||||||
self.unlock_account(account, password, Unlock::Perm)
|
self.unlock_account(account, password, Unlock::Perm)
|
||||||
@ -301,51 +324,16 @@ impl AccountProvider {
|
|||||||
unlocked.get(&account).is_some()
|
unlocked.get(&account).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Signs the message. Account must be unlocked.
|
/// Signs the message. If password is not provided the account must be unlocked.
|
||||||
pub fn sign(&self, account: Address, message: Message) -> Result<Signature, Error> {
|
pub fn sign(&self, account: Address, password: Option<String>, message: Message) -> Result<Signature, Error> {
|
||||||
let data = {
|
let password = try!(password.map(Ok).unwrap_or_else(|| self.password(&account)));
|
||||||
let mut unlocked = self.unlocked.lock();
|
Ok(try!(self.sstore.sign(&account, &password, &message)))
|
||||||
let data = try!(unlocked.get(&account).ok_or(Error::NotUnlocked)).clone();
|
|
||||||
if let Unlock::Temp = data.unlock {
|
|
||||||
unlocked.remove(&account).expect("data exists: so key must exist: qed");
|
|
||||||
}
|
|
||||||
if let Unlock::Timed((ref start, ref duration)) = data.unlock {
|
|
||||||
if start.elapsed() > Duration::from_millis(*duration as u64) {
|
|
||||||
unlocked.remove(&account).expect("data exists: so key must exist: qed");
|
|
||||||
return Err(Error::NotUnlocked);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data
|
|
||||||
};
|
|
||||||
|
|
||||||
let signature = try!(self.sstore.sign(&account, &data.password, &message));
|
|
||||||
Ok(signature)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decrypts a message. Account must be unlocked.
|
/// Decrypts a message. If password is not provided the account must be unlocked.
|
||||||
pub fn decrypt(&self, account: Address, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
pub fn decrypt(&self, account: Address, password: Option<String>, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
||||||
let data = {
|
let password = try!(password.map(Ok).unwrap_or_else(|| self.password(&account)));
|
||||||
let mut unlocked = self.unlocked.lock();
|
Ok(try!(self.sstore.decrypt(&account, &password, shared_mac, message)))
|
||||||
let data = try!(unlocked.get(&account).ok_or(Error::NotUnlocked)).clone();
|
|
||||||
if let Unlock::Temp = data.unlock {
|
|
||||||
unlocked.remove(&account).expect("data exists: so key must exist: qed");
|
|
||||||
}
|
|
||||||
if let Unlock::Timed((ref start, ref duration)) = data.unlock {
|
|
||||||
if start.elapsed() > Duration::from_millis(*duration as u64) {
|
|
||||||
unlocked.remove(&account).expect("data exists: so key must exist: qed");
|
|
||||||
return Err(Error::NotUnlocked);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(try!(self.sstore.decrypt(&account, &data.password, shared_mac, message)))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unlocks an account, signs the message, and locks it again.
|
|
||||||
pub fn sign_with_password(&self, account: Address, password: String, message: Message) -> Result<Signature, Error> {
|
|
||||||
let signature = try!(self.sstore.sign(&account, &password, &message));
|
|
||||||
Ok(signature)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the underlying `SecretStore` reference if one exists.
|
/// Returns the underlying `SecretStore` reference if one exists.
|
||||||
@ -386,8 +374,8 @@ mod tests {
|
|||||||
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
||||||
assert!(ap.unlock_account_temporarily(kp.address(), "test1".into()).is_err());
|
assert!(ap.unlock_account_temporarily(kp.address(), "test1".into()).is_err());
|
||||||
assert!(ap.unlock_account_temporarily(kp.address(), "test".into()).is_ok());
|
assert!(ap.unlock_account_temporarily(kp.address(), "test".into()).is_ok());
|
||||||
assert!(ap.sign(kp.address(), Default::default()).is_ok());
|
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||||
assert!(ap.sign(kp.address(), Default::default()).is_err());
|
assert!(ap.sign(kp.address(), None, Default::default()).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -397,11 +385,11 @@ mod tests {
|
|||||||
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
||||||
assert!(ap.unlock_account_permanently(kp.address(), "test1".into()).is_err());
|
assert!(ap.unlock_account_permanently(kp.address(), "test1".into()).is_err());
|
||||||
assert!(ap.unlock_account_permanently(kp.address(), "test".into()).is_ok());
|
assert!(ap.unlock_account_permanently(kp.address(), "test".into()).is_ok());
|
||||||
assert!(ap.sign(kp.address(), Default::default()).is_ok());
|
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||||
assert!(ap.sign(kp.address(), Default::default()).is_ok());
|
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||||
assert!(ap.unlock_account_temporarily(kp.address(), "test".into()).is_ok());
|
assert!(ap.unlock_account_temporarily(kp.address(), "test".into()).is_ok());
|
||||||
assert!(ap.sign(kp.address(), Default::default()).is_ok());
|
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||||
assert!(ap.sign(kp.address(), Default::default()).is_ok());
|
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -411,8 +399,8 @@ mod tests {
|
|||||||
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
||||||
assert!(ap.unlock_account_timed(kp.address(), "test1".into(), 2000).is_err());
|
assert!(ap.unlock_account_timed(kp.address(), "test1".into(), 2000).is_err());
|
||||||
assert!(ap.unlock_account_timed(kp.address(), "test".into(), 2000).is_ok());
|
assert!(ap.unlock_account_timed(kp.address(), "test".into(), 2000).is_ok());
|
||||||
assert!(ap.sign(kp.address(), Default::default()).is_ok());
|
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||||
::std::thread::sleep(Duration::from_millis(2000));
|
::std::thread::sleep(Duration::from_millis(2000));
|
||||||
assert!(ap.sign(kp.address(), Default::default()).is_err());
|
assert!(ap.sign(kp.address(), None, Default::default()).is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -350,7 +350,7 @@ impl<'x> OpenBlock<'x> {
|
|||||||
let t = outcome.trace;
|
let t = outcome.trace;
|
||||||
self.block.traces.as_mut().map(|traces| traces.push(t));
|
self.block.traces.as_mut().map(|traces| traces.push(t));
|
||||||
self.block.receipts.push(outcome.receipt);
|
self.block.receipts.push(outcome.receipt);
|
||||||
Ok(self.block.receipts.last().unwrap())
|
Ok(self.block.receipts.last().expect("receipt just pushed; qed"))
|
||||||
}
|
}
|
||||||
Err(x) => Err(From::from(x))
|
Err(x) => Err(From::from(x))
|
||||||
}
|
}
|
||||||
@ -404,6 +404,10 @@ impl<'x> OpenBlock<'x> {
|
|||||||
uncle_bytes: uncle_bytes,
|
uncle_bytes: uncle_bytes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
/// Return mutable block reference. To be used in tests only.
|
||||||
|
pub fn block_mut (&mut self) -> &mut ExecutedBlock { &mut self.block }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'x> IsBlock for OpenBlock<'x> {
|
impl<'x> IsBlock for OpenBlock<'x> {
|
||||||
|
@ -29,3 +29,12 @@ pub struct BestBlock {
|
|||||||
/// Best block uncompressed bytes
|
/// Best block uncompressed bytes
|
||||||
pub block: Bytes,
|
pub block: Bytes,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Best ancient block info. If the blockchain has a gap this keeps track of where it starts.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct BestAncientBlock {
|
||||||
|
/// Best block hash.
|
||||||
|
pub hash: H256,
|
||||||
|
/// Best block number.
|
||||||
|
pub number: BlockNumber,
|
||||||
|
}
|
||||||
|
@ -27,7 +27,8 @@ use log_entry::{LogEntry, LocalizedLogEntry};
|
|||||||
use receipt::Receipt;
|
use receipt::Receipt;
|
||||||
use blooms::{Bloom, BloomGroup};
|
use blooms::{Bloom, BloomGroup};
|
||||||
use blockchain::block_info::{BlockInfo, BlockLocation, BranchBecomingCanonChainData};
|
use blockchain::block_info::{BlockInfo, BlockLocation, BranchBecomingCanonChainData};
|
||||||
use blockchain::best_block::BestBlock;
|
use blockchain::best_block::{BestBlock, BestAncientBlock};
|
||||||
|
use types::blockchain_info::BlockChainInfo;
|
||||||
use types::tree_route::TreeRoute;
|
use types::tree_route::TreeRoute;
|
||||||
use blockchain::update::ExtrasUpdate;
|
use blockchain::update::ExtrasUpdate;
|
||||||
use blockchain::{CacheSize, ImportRoute, Config};
|
use blockchain::{CacheSize, ImportRoute, Config};
|
||||||
@ -43,16 +44,24 @@ pub trait BlockProvider {
|
|||||||
/// (though not necessarily a part of the canon chain).
|
/// (though not necessarily a part of the canon chain).
|
||||||
fn is_known(&self, hash: &H256) -> bool;
|
fn is_known(&self, hash: &H256) -> bool;
|
||||||
|
|
||||||
/// Get the first block which this chain holds.
|
/// Get the first block of the best part of the chain.
|
||||||
|
/// Return `None` if there is no gap and the first block is the genesis.
|
||||||
/// Any queries of blocks which precede this one are not guaranteed to
|
/// Any queries of blocks which precede this one are not guaranteed to
|
||||||
/// succeed.
|
/// succeed.
|
||||||
fn first_block(&self) -> H256;
|
fn first_block(&self) -> Option<H256>;
|
||||||
|
|
||||||
/// Get the number of the first block.
|
/// Get the number of the first block.
|
||||||
fn first_block_number(&self) -> BlockNumber {
|
fn first_block_number(&self) -> Option<BlockNumber> {
|
||||||
self.block_number(&self.first_block()).expect("First block always stored; qed")
|
self.first_block().map(|b| self.block_number(&b).expect("First block is always set to an existing block or `None`. Existing block always has a number; qed"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the best block of an first block sequence if there is a gap.
|
||||||
|
fn best_ancient_block(&self) -> Option<H256>;
|
||||||
|
|
||||||
|
/// Get the number of the first block.
|
||||||
|
fn best_ancient_number(&self) -> Option<BlockNumber> {
|
||||||
|
self.best_ancient_block().map(|h| self.block_number(&h).expect("Ancient block is always set to an existing block or `None`. Existing block always has a number; qed"))
|
||||||
|
}
|
||||||
/// Get raw block data
|
/// Get raw block data
|
||||||
fn block(&self, hash: &H256) -> Option<Bytes>;
|
fn block(&self, hash: &H256) -> Option<Bytes>;
|
||||||
|
|
||||||
@ -123,7 +132,8 @@ pub trait BlockProvider {
|
|||||||
|
|
||||||
/// Returns the header of the genesis block.
|
/// Returns the header of the genesis block.
|
||||||
fn genesis_header(&self) -> Header {
|
fn genesis_header(&self) -> Header {
|
||||||
self.block_header(&self.genesis_hash()).unwrap()
|
self.block_header(&self.genesis_hash())
|
||||||
|
.expect("Genesis header always stored; qed")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns numbers of blocks containing given bloom.
|
/// Returns numbers of blocks containing given bloom.
|
||||||
@ -160,9 +170,14 @@ impl bc::group::BloomGroupDatabase for BlockChain {
|
|||||||
pub struct BlockChain {
|
pub struct BlockChain {
|
||||||
// All locks must be captured in the order declared here.
|
// All locks must be captured in the order declared here.
|
||||||
blooms_config: bc::Config,
|
blooms_config: bc::Config,
|
||||||
first_block: H256,
|
|
||||||
|
|
||||||
best_block: RwLock<BestBlock>,
|
best_block: RwLock<BestBlock>,
|
||||||
|
// Stores best block of the first uninterrupted sequence of blocks. `None` if there are no gaps.
|
||||||
|
// Only updated with `insert_unordered_block`.
|
||||||
|
best_ancient_block: RwLock<Option<BestAncientBlock>>,
|
||||||
|
// Stores the last block of the last sequence of blocks. `None` if there are no gaps.
|
||||||
|
// This is calculated on start and does not get updated.
|
||||||
|
first_block: Option<H256>,
|
||||||
|
|
||||||
// block cache
|
// block cache
|
||||||
block_headers: RwLock<HashMap<H256, Bytes>>,
|
block_headers: RwLock<HashMap<H256, Bytes>>,
|
||||||
@ -191,8 +206,16 @@ impl BlockProvider for BlockChain {
|
|||||||
self.db.exists_with_cache(db::COL_EXTRA, &self.block_details, hash)
|
self.db.exists_with_cache(db::COL_EXTRA, &self.block_details, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn first_block(&self) -> H256 {
|
fn first_block(&self) -> Option<H256> {
|
||||||
self.first_block
|
self.first_block.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn best_ancient_block(&self) -> Option<H256> {
|
||||||
|
self.best_ancient_block.read().as_ref().map(|b| b.hash.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn best_ancient_number(&self) -> Option<BlockNumber> {
|
||||||
|
self.best_ancient_block.read().as_ref().map(|b| b.number)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get raw block data
|
/// Get raw block data
|
||||||
@ -332,7 +355,10 @@ impl BlockProvider for BlockChain {
|
|||||||
.filter_map(|(number, hash)| self.block_receipts(&hash).map(|r| (number, hash, r.receipts)))
|
.filter_map(|(number, hash)| self.block_receipts(&hash).map(|r| (number, hash, r.receipts)))
|
||||||
.filter_map(|(number, hash, receipts)| self.block_body(&hash).map(|ref b| (number, hash, receipts, BodyView::new(b).transaction_hashes())))
|
.filter_map(|(number, hash, receipts)| self.block_body(&hash).map(|ref b| (number, hash, receipts, BodyView::new(b).transaction_hashes())))
|
||||||
.flat_map(|(number, hash, mut receipts, mut hashes)| {
|
.flat_map(|(number, hash, mut receipts, mut hashes)| {
|
||||||
assert_eq!(receipts.len(), hashes.len());
|
if receipts.len() != hashes.len() {
|
||||||
|
warn!("Block {} ({}) has different number of receipts ({}) to transactions ({}). Database corrupt?", number, hash, receipts.len(), hashes.len());
|
||||||
|
assert!(false);
|
||||||
|
}
|
||||||
log_index = receipts.iter().fold(0, |sum, receipt| sum + receipt.logs.len());
|
log_index = receipts.iter().fold(0, |sum, receipt| sum + receipt.logs.len());
|
||||||
|
|
||||||
let receipts_len = receipts.len();
|
let receipts_len = receipts.len();
|
||||||
@ -397,8 +423,9 @@ impl BlockChain {
|
|||||||
levels: LOG_BLOOMS_LEVELS,
|
levels: LOG_BLOOMS_LEVELS,
|
||||||
elements_per_index: LOG_BLOOMS_ELEMENTS_PER_INDEX,
|
elements_per_index: LOG_BLOOMS_ELEMENTS_PER_INDEX,
|
||||||
},
|
},
|
||||||
first_block: H256::zero(),
|
first_block: None,
|
||||||
best_block: RwLock::new(BestBlock::default()),
|
best_block: RwLock::new(BestBlock::default()),
|
||||||
|
best_ancient_block: RwLock::new(None),
|
||||||
block_headers: RwLock::new(HashMap::new()),
|
block_headers: RwLock::new(HashMap::new()),
|
||||||
block_bodies: RwLock::new(HashMap::new()),
|
block_bodies: RwLock::new(HashMap::new()),
|
||||||
block_details: RwLock::new(HashMap::new()),
|
block_details: RwLock::new(HashMap::new()),
|
||||||
@ -440,7 +467,6 @@ impl BlockChain {
|
|||||||
batch.write(db::COL_EXTRA, &header.number(), &hash);
|
batch.write(db::COL_EXTRA, &header.number(), &hash);
|
||||||
|
|
||||||
batch.put(db::COL_EXTRA, b"best", &hash);
|
batch.put(db::COL_EXTRA, b"best", &hash);
|
||||||
batch.put(db::COL_EXTRA, b"first", &hash);
|
|
||||||
bc.db.write(batch).expect("Low level database error. Some issue with disk?");
|
bc.db.write(batch).expect("Low level database error. Some issue with disk?");
|
||||||
hash
|
hash
|
||||||
}
|
}
|
||||||
@ -452,32 +478,45 @@ impl BlockChain {
|
|||||||
let best_block_total_difficulty = bc.block_details(&best_block_hash).unwrap().total_difficulty;
|
let best_block_total_difficulty = bc.block_details(&best_block_hash).unwrap().total_difficulty;
|
||||||
let best_block_rlp = bc.block(&best_block_hash).unwrap();
|
let best_block_rlp = bc.block(&best_block_hash).unwrap();
|
||||||
|
|
||||||
let raw_first = bc.db.get(db::COL_EXTRA, b"first").unwrap().map_or(Vec::new(), |v| v.to_vec());
|
let raw_first = bc.db.get(db::COL_EXTRA, b"first").unwrap().map(|v| v.to_vec());
|
||||||
|
let mut best_ancient = bc.db.get(db::COL_EXTRA, b"ancient").unwrap().map(|h| H256::from_slice(&h));
|
||||||
|
let best_ancient_number;
|
||||||
|
if best_ancient.is_none() && best_block_number > 1 && bc.block_hash(1).is_none() {
|
||||||
|
best_ancient = Some(bc.genesis_hash());
|
||||||
|
best_ancient_number = Some(0);
|
||||||
|
} else {
|
||||||
|
best_ancient_number = best_ancient.as_ref().and_then(|h| bc.block_number(h));
|
||||||
|
}
|
||||||
|
|
||||||
// binary search for the first block.
|
// binary search for the first block.
|
||||||
if raw_first.is_empty() {
|
match raw_first {
|
||||||
let (mut f, mut hash) = (best_block_number, best_block_hash);
|
None => {
|
||||||
let mut l = 0;
|
let (mut f, mut hash) = (best_block_number, best_block_hash);
|
||||||
|
let mut l = best_ancient_number.unwrap_or(0);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if l >= f { break; }
|
if l >= f { break; }
|
||||||
|
|
||||||
let step = (f - l) >> 1;
|
let step = (f - l) >> 1;
|
||||||
let m = l + step;
|
let m = l + step;
|
||||||
|
|
||||||
match bc.block_hash(m) {
|
match bc.block_hash(m) {
|
||||||
Some(h) => { f = m; hash = h },
|
Some(h) => { f = m; hash = h },
|
||||||
None => { l = m + 1 },
|
None => { l = m + 1 },
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let mut batch = db.transaction();
|
if hash != bc.genesis_hash() {
|
||||||
batch.put(db::COL_EXTRA, b"first", &hash);
|
trace!("First block calculated: {:?}", hash);
|
||||||
db.write(batch).expect("Low level database error.");
|
let mut batch = db.transaction();
|
||||||
|
batch.put(db::COL_EXTRA, b"first", &hash);
|
||||||
bc.first_block = hash;
|
db.write(batch).expect("Low level database error.");
|
||||||
} else {
|
bc.first_block = Some(hash);
|
||||||
bc.first_block = H256::from_slice(&raw_first);
|
}
|
||||||
|
},
|
||||||
|
Some(raw_first) => {
|
||||||
|
bc.first_block = Some(H256::from_slice(&raw_first));
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// and write them
|
// and write them
|
||||||
@ -488,6 +527,14 @@ impl BlockChain {
|
|||||||
hash: best_block_hash,
|
hash: best_block_hash,
|
||||||
block: best_block_rlp,
|
block: best_block_rlp,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let (Some(hash), Some(number)) = (best_ancient, best_ancient_number) {
|
||||||
|
let mut best_ancient_block = bc.best_ancient_block.write();
|
||||||
|
*best_ancient_block = Some(BestAncientBlock {
|
||||||
|
hash: hash,
|
||||||
|
number: number,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bc
|
bc
|
||||||
@ -641,11 +688,12 @@ impl BlockChain {
|
|||||||
/// Inserts a verified, known block from the canonical chain.
|
/// Inserts a verified, known block from the canonical chain.
|
||||||
///
|
///
|
||||||
/// Can be performed out-of-order, but care must be taken that the final chain is in a correct state.
|
/// Can be performed out-of-order, but care must be taken that the final chain is in a correct state.
|
||||||
/// This is used by snapshot restoration.
|
/// This is used by snapshot restoration and when downloading missing blocks for the chain gap.
|
||||||
///
|
/// `is_best` forces the best block to be updated to this block.
|
||||||
|
/// `is_ancient` forces the best block of the first block sequence to be updated to this block.
|
||||||
/// Supply a dummy parent total difficulty when the parent block may not be in the chain.
|
/// Supply a dummy parent total difficulty when the parent block may not be in the chain.
|
||||||
/// Returns true if the block is disconnected.
|
/// Returns true if the block is disconnected.
|
||||||
pub fn insert_snapshot_block(&self, bytes: &[u8], receipts: Vec<Receipt>, parent_td: Option<U256>, is_best: bool) -> bool {
|
pub fn insert_unordered_block(&self, batch: &mut DBTransaction, bytes: &[u8], receipts: Vec<Receipt>, parent_td: Option<U256>, is_best: bool, is_ancient: bool) -> bool {
|
||||||
let block = BlockView::new(bytes);
|
let block = BlockView::new(bytes);
|
||||||
let header = block.header_view();
|
let header = block.header_view();
|
||||||
let hash = header.sha3();
|
let hash = header.sha3();
|
||||||
@ -656,8 +704,6 @@ impl BlockChain {
|
|||||||
|
|
||||||
assert!(self.pending_best_block.read().is_none());
|
assert!(self.pending_best_block.read().is_none());
|
||||||
|
|
||||||
let mut batch = self.db.transaction();
|
|
||||||
|
|
||||||
let block_rlp = UntrustedRlp::new(bytes);
|
let block_rlp = UntrustedRlp::new(bytes);
|
||||||
let compressed_header = block_rlp.at(0).unwrap().compress(RlpType::Blocks);
|
let compressed_header = block_rlp.at(0).unwrap().compress(RlpType::Blocks);
|
||||||
let compressed_body = UntrustedRlp::new(&Self::block_to_body(bytes)).compress(RlpType::Blocks);
|
let compressed_body = UntrustedRlp::new(&Self::block_to_body(bytes)).compress(RlpType::Blocks);
|
||||||
@ -671,13 +717,13 @@ impl BlockChain {
|
|||||||
if let Some(parent_details) = maybe_parent {
|
if let Some(parent_details) = maybe_parent {
|
||||||
// parent known to be in chain.
|
// parent known to be in chain.
|
||||||
let info = BlockInfo {
|
let info = BlockInfo {
|
||||||
hash: hash,
|
hash: hash.clone(),
|
||||||
number: header.number(),
|
number: header.number(),
|
||||||
total_difficulty: parent_details.total_difficulty + header.difficulty(),
|
total_difficulty: parent_details.total_difficulty + header.difficulty(),
|
||||||
location: BlockLocation::CanonChain,
|
location: BlockLocation::CanonChain,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.prepare_update(&mut batch, ExtrasUpdate {
|
self.prepare_update(batch, ExtrasUpdate {
|
||||||
block_hashes: self.prepare_block_hashes_update(bytes, &info),
|
block_hashes: self.prepare_block_hashes_update(bytes, &info),
|
||||||
block_details: self.prepare_block_details_update(bytes, &info),
|
block_details: self.prepare_block_details_update(bytes, &info),
|
||||||
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
||||||
@ -686,7 +732,21 @@ impl BlockChain {
|
|||||||
info: info,
|
info: info,
|
||||||
block: bytes
|
block: bytes
|
||||||
}, is_best);
|
}, is_best);
|
||||||
self.db.write(batch).unwrap();
|
|
||||||
|
if is_ancient {
|
||||||
|
let mut best_ancient_block = self.best_ancient_block.write();
|
||||||
|
let ancient_number = best_ancient_block.as_ref().map_or(0, |b| b.number);
|
||||||
|
if self.block_hash(header.number() + 1).is_some() {
|
||||||
|
batch.delete(db::COL_EXTRA, b"ancient");
|
||||||
|
*best_ancient_block = None;
|
||||||
|
} else if header.number() > ancient_number {
|
||||||
|
batch.put(db::COL_EXTRA, b"ancient", &hash);
|
||||||
|
*best_ancient_block = Some(BestAncientBlock {
|
||||||
|
hash: hash,
|
||||||
|
number: header.number(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
@ -711,7 +771,7 @@ impl BlockChain {
|
|||||||
let mut update = HashMap::new();
|
let mut update = HashMap::new();
|
||||||
update.insert(hash, block_details);
|
update.insert(hash, block_details);
|
||||||
|
|
||||||
self.prepare_update(&mut batch, ExtrasUpdate {
|
self.prepare_update(batch, ExtrasUpdate {
|
||||||
block_hashes: self.prepare_block_hashes_update(bytes, &info),
|
block_hashes: self.prepare_block_hashes_update(bytes, &info),
|
||||||
block_details: update,
|
block_details: update,
|
||||||
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
||||||
@ -720,8 +780,6 @@ impl BlockChain {
|
|||||||
info: info,
|
info: info,
|
||||||
block: bytes,
|
block: bytes,
|
||||||
}, is_best);
|
}, is_best);
|
||||||
self.db.write(batch).unwrap();
|
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1207,6 +1265,24 @@ impl BlockChain {
|
|||||||
body.append_raw(block_rlp.at(2).as_raw(), 1);
|
body.append_raw(block_rlp.at(2).as_raw(), 1);
|
||||||
body.out()
|
body.out()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns general blockchain information
|
||||||
|
pub fn chain_info(&self) -> BlockChainInfo {
|
||||||
|
// ensure data consistencly by locking everything first
|
||||||
|
let best_block = self.best_block.read();
|
||||||
|
let best_ancient_block = self.best_ancient_block.read();
|
||||||
|
BlockChainInfo {
|
||||||
|
total_difficulty: best_block.total_difficulty.clone(),
|
||||||
|
pending_total_difficulty: best_block.total_difficulty.clone(),
|
||||||
|
genesis_hash: self.genesis_hash(),
|
||||||
|
best_block_hash: best_block.hash.clone(),
|
||||||
|
best_block_number: best_block.number,
|
||||||
|
first_block_hash: self.first_block(),
|
||||||
|
first_block_number: From::from(self.first_block_number()),
|
||||||
|
ancient_block_hash: best_ancient_block.as_ref().map(|b| b.hash.clone()),
|
||||||
|
ancient_block_number: best_ancient_block.as_ref().map(|b| b.number),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -55,18 +55,23 @@ impl<T> CacheManager<T> where T: Eq + Hash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _ in 0..COLLECTION_QUEUE_SIZE {
|
for _ in 0..COLLECTION_QUEUE_SIZE {
|
||||||
let current_size = notify_unused(self.cache_usage.pop_back().unwrap());
|
if let Some(back) = self.cache_usage.pop_back() {
|
||||||
self.cache_usage.push_front(Default::default());
|
let current_size = notify_unused(back);
|
||||||
if current_size < self.max_cache_size {
|
self.cache_usage.push_front(Default::default());
|
||||||
break;
|
if current_size < self.max_cache_size {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rotate_cache_if_needed(&mut self) {
|
fn rotate_cache_if_needed(&mut self) {
|
||||||
|
if self.cache_usage.len() == 0 { return }
|
||||||
|
|
||||||
if self.cache_usage[0].len() * self.bytes_per_cache_entry > self.pref_cache_size / COLLECTION_QUEUE_SIZE {
|
if self.cache_usage[0].len() * self.bytes_per_cache_entry > self.pref_cache_size / COLLECTION_QUEUE_SIZE {
|
||||||
let cache = self.cache_usage.pop_back().unwrap();
|
if let Some(cache) = self.cache_usage.pop_back() {
|
||||||
self.cache_usage.push_front(cache);
|
self.cache_usage.push_front(cache);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ use util::kvdb::*;
|
|||||||
|
|
||||||
// other
|
// other
|
||||||
use io::*;
|
use io::*;
|
||||||
use views::{HeaderView, BodyView};
|
use views::{HeaderView, BodyView, BlockView};
|
||||||
use error::{ImportError, ExecutionError, CallError, BlockError, ImportResult, Error as EthcoreError};
|
use error::{ImportError, ExecutionError, CallError, BlockError, ImportResult, Error as EthcoreError};
|
||||||
use header::BlockNumber;
|
use header::BlockNumber;
|
||||||
use state::State;
|
use state::State;
|
||||||
@ -46,7 +46,7 @@ use transaction::{LocalizedTransaction, SignedTransaction, Action};
|
|||||||
use blockchain::extras::TransactionAddress;
|
use blockchain::extras::TransactionAddress;
|
||||||
use types::filter::Filter;
|
use types::filter::Filter;
|
||||||
use log_entry::LocalizedLogEntry;
|
use log_entry::LocalizedLogEntry;
|
||||||
use verification::queue::{BlockQueue, QueueInfo as BlockQueueInfo};
|
use verification::queue::BlockQueue;
|
||||||
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
|
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
|
||||||
use client::{
|
use client::{
|
||||||
BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient,
|
BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient,
|
||||||
@ -71,9 +71,11 @@ use state_db::StateDB;
|
|||||||
pub use types::blockchain_info::BlockChainInfo;
|
pub use types::blockchain_info::BlockChainInfo;
|
||||||
pub use types::block_status::BlockStatus;
|
pub use types::block_status::BlockStatus;
|
||||||
pub use blockchain::CacheSize as BlockChainCacheSize;
|
pub use blockchain::CacheSize as BlockChainCacheSize;
|
||||||
|
pub use verification::queue::QueueInfo as BlockQueueInfo;
|
||||||
|
|
||||||
const MAX_TX_QUEUE_SIZE: usize = 4096;
|
const MAX_TX_QUEUE_SIZE: usize = 4096;
|
||||||
const MAX_QUEUE_SIZE_TO_SLEEP_ON: usize = 2;
|
const MAX_QUEUE_SIZE_TO_SLEEP_ON: usize = 2;
|
||||||
|
const MIN_HISTORY_SIZE: u64 = 8;
|
||||||
|
|
||||||
impl fmt::Display for BlockChainInfo {
|
impl fmt::Display for BlockChainInfo {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
@ -141,12 +143,9 @@ pub struct Client {
|
|||||||
queue_transactions: AtomicUsize,
|
queue_transactions: AtomicUsize,
|
||||||
last_hashes: RwLock<VecDeque<H256>>,
|
last_hashes: RwLock<VecDeque<H256>>,
|
||||||
factories: Factories,
|
factories: Factories,
|
||||||
|
history: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The pruning constant -- how old blocks must be before we
|
|
||||||
/// assume finality of a given candidate.
|
|
||||||
pub const HISTORY: u64 = 1200;
|
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
/// Create a new client with given spec and DB path and custom verifier.
|
/// Create a new client with given spec and DB path and custom verifier.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
@ -170,13 +169,35 @@ impl Client {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let journal_db = journaldb::new(db.clone(), config.pruning, ::db::COL_STATE);
|
let journal_db = journaldb::new(db.clone(), config.pruning, ::db::COL_STATE);
|
||||||
let mut state_db = StateDB::new(journal_db);
|
let mut state_db = StateDB::new(journal_db, config.state_cache_size);
|
||||||
if state_db.journal_db().is_empty() && try!(spec.ensure_db_good(&mut state_db)) {
|
if state_db.journal_db().is_empty() && try!(spec.ensure_db_good(&mut state_db)) {
|
||||||
let mut batch = DBTransaction::new(&db);
|
let mut batch = DBTransaction::new(&db);
|
||||||
try!(state_db.commit(&mut batch, 0, &spec.genesis_header().hash(), None));
|
try!(state_db.journal_under(&mut batch, 0, &spec.genesis_header().hash()));
|
||||||
try!(db.write(batch).map_err(ClientError::Database));
|
try!(db.write(batch).map_err(ClientError::Database));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trace!("Cleanup journal: DB Earliest = {:?}, Latest = {:?}", state_db.journal_db().earliest_era(), state_db.journal_db().latest_era());
|
||||||
|
|
||||||
|
let history = if config.history < MIN_HISTORY_SIZE {
|
||||||
|
info!(target: "client", "Ignoring pruning history parameter of {}\
|
||||||
|
, falling back to minimum of {}",
|
||||||
|
config.history, MIN_HISTORY_SIZE);
|
||||||
|
MIN_HISTORY_SIZE
|
||||||
|
} else {
|
||||||
|
config.history
|
||||||
|
};
|
||||||
|
|
||||||
|
if let (Some(earliest), Some(latest)) = (state_db.journal_db().earliest_era(), state_db.journal_db().latest_era()) {
|
||||||
|
if latest > earliest && latest - earliest > history {
|
||||||
|
for era in earliest..(latest - history + 1) {
|
||||||
|
trace!("Removing era {}", era);
|
||||||
|
let mut batch = DBTransaction::new(&db);
|
||||||
|
try!(state_db.mark_canonical(&mut batch, era, &chain.block_hash(era).expect("Old block not found in the database")));
|
||||||
|
try!(db.write(batch).map_err(ClientError::Database));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !chain.block_header(&chain.best_block_hash()).map_or(true, |h| state_db.journal_db().contains(h.state_root())) {
|
if !chain.block_header(&chain.best_block_hash()).map_or(true, |h| state_db.journal_db().contains(h.state_root())) {
|
||||||
warn!("State root not found for block #{} ({})", chain.best_block_number(), chain.best_block_hash().hex());
|
warn!("State root not found for block #{} ({})", chain.best_block_number(), chain.best_block_hash().hex());
|
||||||
}
|
}
|
||||||
@ -190,7 +211,7 @@ impl Client {
|
|||||||
let awake = match config.mode { Mode::Dark(..) => false, _ => true };
|
let awake = match config.mode { Mode::Dark(..) => false, _ => true };
|
||||||
|
|
||||||
let factories = Factories {
|
let factories = Factories {
|
||||||
vm: EvmFactory::new(config.vm_type.clone()),
|
vm: EvmFactory::new(config.vm_type.clone(), config.jump_table_size),
|
||||||
trie: TrieFactory::new(trie_spec),
|
trie: TrieFactory::new(trie_spec),
|
||||||
accountdb: Default::default(),
|
accountdb: Default::default(),
|
||||||
};
|
};
|
||||||
@ -217,6 +238,7 @@ impl Client {
|
|||||||
queue_transactions: AtomicUsize::new(0),
|
queue_transactions: AtomicUsize::new(0),
|
||||||
last_hashes: RwLock::new(VecDeque::new()),
|
last_hashes: RwLock::new(VecDeque::new()),
|
||||||
factories: factories,
|
factories: factories,
|
||||||
|
history: history,
|
||||||
};
|
};
|
||||||
Ok(Arc::new(client))
|
Ok(Arc::new(client))
|
||||||
}
|
}
|
||||||
@ -275,7 +297,7 @@ impl Client {
|
|||||||
let chain = self.chain.read();
|
let chain = self.chain.read();
|
||||||
// Check the block isn't so old we won't be able to enact it.
|
// Check the block isn't so old we won't be able to enact it.
|
||||||
let best_block_number = chain.best_block_number();
|
let best_block_number = chain.best_block_number();
|
||||||
if best_block_number >= HISTORY && header.number() <= best_block_number - HISTORY {
|
if best_block_number >= self.history && header.number() <= best_block_number - self.history {
|
||||||
warn!(target: "client", "Block import failed for #{} ({})\nBlock is ancient (current best block: #{}).", header.number(), header.hash(), best_block_number);
|
warn!(target: "client", "Block import failed for #{} ({})\nBlock is ancient (current best block: #{}).", header.number(), header.hash(), best_block_number);
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
@ -340,16 +362,19 @@ impl Client {
|
|||||||
|
|
||||||
/// This is triggered by a message coming from a block queue when the block is ready for insertion
|
/// This is triggered by a message coming from a block queue when the block is ready for insertion
|
||||||
pub fn import_verified_blocks(&self) -> usize {
|
pub fn import_verified_blocks(&self) -> usize {
|
||||||
let max_blocks_to_import = 64;
|
let max_blocks_to_import = 4;
|
||||||
let (imported_blocks, import_results, invalid_blocks, imported, duration) = {
|
let (imported_blocks, import_results, invalid_blocks, imported, duration, is_empty) = {
|
||||||
let mut imported_blocks = Vec::with_capacity(max_blocks_to_import);
|
let mut imported_blocks = Vec::with_capacity(max_blocks_to_import);
|
||||||
let mut invalid_blocks = HashSet::new();
|
let mut invalid_blocks = HashSet::new();
|
||||||
let mut import_results = Vec::with_capacity(max_blocks_to_import);
|
let mut import_results = Vec::with_capacity(max_blocks_to_import);
|
||||||
|
|
||||||
let _import_lock = self.import_lock.lock();
|
let _import_lock = self.import_lock.lock();
|
||||||
|
let blocks = self.block_queue.drain(max_blocks_to_import);
|
||||||
|
if blocks.is_empty() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
let _timer = PerfTimer::new("import_verified_blocks");
|
let _timer = PerfTimer::new("import_verified_blocks");
|
||||||
let start = precise_time_ns();
|
let start = precise_time_ns();
|
||||||
let blocks = self.block_queue.drain(max_blocks_to_import);
|
|
||||||
|
|
||||||
for block in blocks {
|
for block in blocks {
|
||||||
let header = &block.header;
|
let header = &block.header;
|
||||||
@ -373,23 +398,19 @@ impl Client {
|
|||||||
let imported = imported_blocks.len();
|
let imported = imported_blocks.len();
|
||||||
let invalid_blocks = invalid_blocks.into_iter().collect::<Vec<H256>>();
|
let invalid_blocks = invalid_blocks.into_iter().collect::<Vec<H256>>();
|
||||||
|
|
||||||
{
|
if !invalid_blocks.is_empty() {
|
||||||
if !invalid_blocks.is_empty() {
|
self.block_queue.mark_as_bad(&invalid_blocks);
|
||||||
self.block_queue.mark_as_bad(&invalid_blocks);
|
|
||||||
}
|
|
||||||
if !imported_blocks.is_empty() {
|
|
||||||
self.block_queue.mark_as_good(&imported_blocks);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
let is_empty = self.block_queue.mark_as_good(&imported_blocks);
|
||||||
let duration_ns = precise_time_ns() - start;
|
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);
|
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);
|
self.miner.chain_new_blocks(self, &imported_blocks, &invalid_blocks, &enacted, &retracted);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,17 +431,33 @@ impl Client {
|
|||||||
imported
|
imported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Import a block with transaction receipts.
|
||||||
|
/// The block is guaranteed to be the next best blocks in the first block sequence.
|
||||||
|
/// Does no sealing or transaction validation.
|
||||||
|
fn import_old_block(&self, block_bytes: Bytes, receipts_bytes: Bytes) -> H256 {
|
||||||
|
let block = BlockView::new(&block_bytes);
|
||||||
|
let hash = block.header().hash();
|
||||||
|
let _import_lock = self.import_lock.lock();
|
||||||
|
{
|
||||||
|
let _timer = PerfTimer::new("import_old_block");
|
||||||
|
let chain = self.chain.read();
|
||||||
|
|
||||||
|
// Commit results
|
||||||
|
let receipts = ::rlp::decode(&receipts_bytes);
|
||||||
|
let mut batch = DBTransaction::new(&self.db.read());
|
||||||
|
chain.insert_unordered_block(&mut batch, &block_bytes, receipts, None, false, true);
|
||||||
|
// Final commit to the DB
|
||||||
|
self.db.read().write_buffered(batch);
|
||||||
|
chain.commit();
|
||||||
|
}
|
||||||
|
self.db.read().flush().expect("DB flush failed.");
|
||||||
|
hash
|
||||||
|
}
|
||||||
|
|
||||||
fn commit_block<B>(&self, block: B, hash: &H256, block_data: &[u8]) -> ImportRoute where B: IsBlock + Drain {
|
fn commit_block<B>(&self, block: B, hash: &H256, block_data: &[u8]) -> ImportRoute where B: IsBlock + Drain {
|
||||||
let number = block.header().number();
|
let number = block.header().number();
|
||||||
let parent = block.header().parent_hash().clone();
|
let parent = block.header().parent_hash().clone();
|
||||||
let chain = self.chain.read();
|
let chain = self.chain.read();
|
||||||
// Are we committing an era?
|
|
||||||
let ancient = if number >= HISTORY {
|
|
||||||
let n = number - HISTORY;
|
|
||||||
Some((n, chain.block_hash(n).expect("only verified blocks can be commited; verified block has hash; qed")))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
// Commit results
|
// Commit results
|
||||||
let receipts = block.receipts().to_owned();
|
let receipts = block.receipts().to_owned();
|
||||||
@ -436,7 +473,17 @@ impl Client {
|
|||||||
// already-imported block of the same number.
|
// already-imported block of the same number.
|
||||||
// TODO: Prove it with a test.
|
// TODO: Prove it with a test.
|
||||||
let mut state = block.drain();
|
let mut state = block.drain();
|
||||||
state.commit(&mut batch, number, hash, ancient).expect("DB commit failed.");
|
|
||||||
|
state.journal_under(&mut batch, number, hash).expect("DB commit failed");
|
||||||
|
|
||||||
|
if number >= self.history {
|
||||||
|
let n = number - self.history;
|
||||||
|
if let Some(ancient_hash) = chain.block_hash(n) {
|
||||||
|
state.mark_canonical(&mut batch, n, &ancient_hash).expect("DB commit failed");
|
||||||
|
} else {
|
||||||
|
debug!(target: "client", "Missing expected hash for block {}", n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let route = chain.insert_block(&mut batch, block_data, receipts);
|
let route = chain.insert_block(&mut batch, block_data, receipts);
|
||||||
self.tracedb.read().import(&mut batch, TraceImportRequest {
|
self.tracedb.read().import(&mut batch, TraceImportRequest {
|
||||||
@ -446,6 +493,7 @@ impl Client {
|
|||||||
enacted: route.enacted.clone(),
|
enacted: route.enacted.clone(),
|
||||||
retracted: route.retracted.len()
|
retracted: route.retracted.len()
|
||||||
});
|
});
|
||||||
|
|
||||||
let is_canon = route.enacted.last().map_or(false, |h| h == hash);
|
let is_canon = route.enacted.last().map_or(false, |h| h == hash);
|
||||||
state.sync_cache(&route.enacted, &route.retracted, is_canon);
|
state.sync_cache(&route.enacted, &route.retracted, is_canon);
|
||||||
// Final commit to the DB
|
// Final commit to the DB
|
||||||
@ -501,7 +549,7 @@ impl Client {
|
|||||||
let db = self.state_db.lock().boxed_clone();
|
let db = self.state_db.lock().boxed_clone();
|
||||||
|
|
||||||
// early exit for pruned blocks
|
// early exit for pruned blocks
|
||||||
if db.is_pruned() && self.chain.read().best_block_number() >= block_number + HISTORY {
|
if db.is_pruned() && self.chain.read().best_block_number() >= block_number + self.history {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -606,20 +654,23 @@ impl Client {
|
|||||||
let best_block_number = self.chain_info().best_block_number;
|
let best_block_number = self.chain_info().best_block_number;
|
||||||
let block_number = try!(self.block_number(at).ok_or(snapshot::Error::InvalidStartingBlock(at)));
|
let block_number = try!(self.block_number(at).ok_or(snapshot::Error::InvalidStartingBlock(at)));
|
||||||
|
|
||||||
if best_block_number > HISTORY + block_number && db.is_pruned() {
|
if best_block_number > self.history + block_number && db.is_pruned() {
|
||||||
return Err(snapshot::Error::OldBlockPrunedDB.into());
|
return Err(snapshot::Error::OldBlockPrunedDB.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let history = ::std::cmp::min(self.history, 1000);
|
||||||
|
|
||||||
let start_hash = match at {
|
let start_hash = match at {
|
||||||
BlockID::Latest => {
|
BlockID::Latest => {
|
||||||
let start_num = if best_block_number > 1000 {
|
let start_num = match db.earliest_era() {
|
||||||
best_block_number - 1000
|
Some(era) => ::std::cmp::max(era, best_block_number - history),
|
||||||
} else {
|
None => best_block_number - history,
|
||||||
0
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.block_hash(BlockID::Number(start_num))
|
match self.block_hash(BlockID::Number(start_num)) {
|
||||||
.expect("blocks within HISTORY are always stored.")
|
Some(h) => h,
|
||||||
|
None => return Err(snapshot::Error::InvalidStartingBlock(at).into()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => match self.block_hash(at) {
|
_ => match self.block_hash(at) {
|
||||||
Some(hash) => hash,
|
Some(hash) => hash,
|
||||||
@ -632,6 +683,11 @@ impl Client {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ask the client what the history parameter is.
|
||||||
|
pub fn pruning_history(&self) -> u64 {
|
||||||
|
self.history
|
||||||
|
}
|
||||||
|
|
||||||
fn block_hash(chain: &BlockChain, id: BlockID) -> Option<H256> {
|
fn block_hash(chain: &BlockChain, id: BlockID) -> Option<H256> {
|
||||||
match id {
|
match id {
|
||||||
BlockID::Hash(hash) => Some(hash),
|
BlockID::Hash(hash) => Some(hash),
|
||||||
@ -688,7 +744,8 @@ impl snapshot::DatabaseRestore for Client {
|
|||||||
let db = self.db.write();
|
let db = self.db.write();
|
||||||
try!(db.restore(new_db));
|
try!(db.restore(new_db));
|
||||||
|
|
||||||
*state_db = StateDB::new(journaldb::new(db.clone(), self.pruning, ::db::COL_STATE));
|
let cache_size = state_db.cache_size();
|
||||||
|
*state_db = StateDB::new(journaldb::new(db.clone(), self.pruning, ::db::COL_STATE), cache_size);
|
||||||
*chain = Arc::new(BlockChain::new(self.config.blockchain.clone(), &[], db.clone()));
|
*chain = Arc::new(BlockChain::new(self.config.blockchain.clone(), &[], db.clone()));
|
||||||
*tracedb = TraceDB::new(self.config.tracing.clone(), db.clone(), chain.clone());
|
*tracedb = TraceDB::new(self.config.tracing.clone(), db.clone(), chain.clone());
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -974,6 +1031,20 @@ impl BlockChainClient for Client {
|
|||||||
Ok(try!(self.block_queue.import(unverified)))
|
Ok(try!(self.block_queue.import(unverified)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn import_block_with_receipts(&self, block_bytes: Bytes, receipts_bytes: Bytes) -> Result<H256, BlockImportError> {
|
||||||
|
{
|
||||||
|
// check block order
|
||||||
|
let header = BlockView::new(&block_bytes).header_view();
|
||||||
|
if self.chain.read().is_known(&header.hash()) {
|
||||||
|
return Err(BlockImportError::Import(ImportError::AlreadyInChain));
|
||||||
|
}
|
||||||
|
if self.block_status(BlockID::Hash(header.parent_hash())) == BlockStatus::Unknown {
|
||||||
|
return Err(BlockImportError::Block(BlockError::UnknownParent(header.parent_hash())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(self.import_old_block(block_bytes, receipts_bytes))
|
||||||
|
}
|
||||||
|
|
||||||
fn queue_info(&self) -> BlockQueueInfo {
|
fn queue_info(&self) -> BlockQueueInfo {
|
||||||
self.block_queue.queue_info()
|
self.block_queue.queue_info()
|
||||||
}
|
}
|
||||||
@ -983,14 +1054,7 @@ impl BlockChainClient for Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn chain_info(&self) -> BlockChainInfo {
|
fn chain_info(&self) -> BlockChainInfo {
|
||||||
let chain = self.chain.read();
|
self.chain.read().chain_info()
|
||||||
BlockChainInfo {
|
|
||||||
total_difficulty: chain.best_block_total_difficulty(),
|
|
||||||
pending_total_difficulty: chain.best_block_total_difficulty(),
|
|
||||||
genesis_hash: chain.genesis_hash(),
|
|
||||||
best_block_hash: chain.best_block_hash(),
|
|
||||||
best_block_number: From::from(chain.best_block_number())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn additional_params(&self) -> BTreeMap<String, String> {
|
fn additional_params(&self) -> BTreeMap<String, String> {
|
||||||
@ -1120,21 +1184,22 @@ impl MiningBlockChainClient for Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn import_sealed_block(&self, block: SealedBlock) -> ImportResult {
|
fn import_sealed_block(&self, block: SealedBlock) -> ImportResult {
|
||||||
let _import_lock = self.import_lock.lock();
|
|
||||||
let _timer = PerfTimer::new("import_sealed_block");
|
|
||||||
let start = precise_time_ns();
|
|
||||||
|
|
||||||
let h = block.header().hash();
|
let h = block.header().hash();
|
||||||
let number = block.header().number();
|
let start = precise_time_ns();
|
||||||
|
let route = {
|
||||||
let block_data = block.rlp_bytes();
|
// scope for self.import_lock
|
||||||
let route = self.commit_block(block, &h, &block_data);
|
let _import_lock = self.import_lock.lock();
|
||||||
trace!(target: "client", "Imported sealed block #{} ({})", number, h);
|
let _timer = PerfTimer::new("import_sealed_block");
|
||||||
self.state_db.lock().sync_cache(&route.enacted, &route.retracted, false);
|
|
||||||
|
|
||||||
|
let number = block.header().number();
|
||||||
|
let block_data = block.rlp_bytes();
|
||||||
|
let route = self.commit_block(block, &h, &block_data);
|
||||||
|
trace!(target: "client", "Imported sealed block #{} ({})", number, h);
|
||||||
|
self.state_db.lock().sync_cache(&route.enacted, &route.retracted, false);
|
||||||
|
route
|
||||||
|
};
|
||||||
let (enacted, retracted) = self.calculate_enacted_retracted(&[route]);
|
let (enacted, retracted) = self.calculate_enacted_retracted(&[route]);
|
||||||
self.miner.chain_new_blocks(self, &[h.clone()], &[], &enacted, &retracted);
|
self.miner.chain_new_blocks(self, &[h.clone()], &[], &enacted, &retracted);
|
||||||
|
|
||||||
self.notify(|notify| {
|
self.notify(|notify| {
|
||||||
notify.new_blocks(
|
notify.new_blocks(
|
||||||
vec![h.clone()],
|
vec![h.clone()],
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use std::path::Path;
|
||||||
pub use std::time::Duration;
|
pub use std::time::Duration;
|
||||||
pub use blockchain::Config as BlockChainConfig;
|
pub use blockchain::Config as BlockChainConfig;
|
||||||
pub use trace::Config as TraceConfig;
|
pub use trace::Config as TraceConfig;
|
||||||
@ -26,23 +27,26 @@ use util::{journaldb, CompactionProfile};
|
|||||||
/// Client state db compaction profile
|
/// Client state db compaction profile
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum DatabaseCompactionProfile {
|
pub enum DatabaseCompactionProfile {
|
||||||
/// Default compaction profile
|
/// Try to determine compaction profile automatically
|
||||||
Default,
|
Auto,
|
||||||
|
/// SSD compaction profile
|
||||||
|
SSD,
|
||||||
/// HDD or other slow storage io compaction profile
|
/// HDD or other slow storage io compaction profile
|
||||||
HDD,
|
HDD,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for DatabaseCompactionProfile {
|
impl Default for DatabaseCompactionProfile {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
DatabaseCompactionProfile::Default
|
DatabaseCompactionProfile::Auto
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DatabaseCompactionProfile {
|
impl DatabaseCompactionProfile {
|
||||||
/// Returns corresponding compaction profile.
|
/// Returns corresponding compaction profile.
|
||||||
pub fn compaction_profile(&self) -> CompactionProfile {
|
pub fn compaction_profile(&self, db_path: &Path) -> CompactionProfile {
|
||||||
match *self {
|
match *self {
|
||||||
DatabaseCompactionProfile::Default => Default::default(),
|
DatabaseCompactionProfile::Auto => CompactionProfile::auto(db_path),
|
||||||
|
DatabaseCompactionProfile::SSD => CompactionProfile::ssd(),
|
||||||
DatabaseCompactionProfile::HDD => CompactionProfile::hdd(),
|
DatabaseCompactionProfile::HDD => CompactionProfile::hdd(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,9 +57,10 @@ impl FromStr for DatabaseCompactionProfile {
|
|||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
match s {
|
match s {
|
||||||
"ssd" | "default" => Ok(DatabaseCompactionProfile::Default),
|
"auto" => Ok(DatabaseCompactionProfile::Auto),
|
||||||
|
"ssd" => Ok(DatabaseCompactionProfile::SSD),
|
||||||
"hdd" => Ok(DatabaseCompactionProfile::HDD),
|
"hdd" => Ok(DatabaseCompactionProfile::HDD),
|
||||||
_ => Err("Invalid compaction profile given. Expected hdd/ssd (default).".into()),
|
_ => Err("Invalid compaction profile given. Expected default/hdd/ssd.".into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,7 +101,7 @@ pub struct ClientConfig {
|
|||||||
pub pruning: journaldb::Algorithm,
|
pub pruning: journaldb::Algorithm,
|
||||||
/// The name of the client instance.
|
/// The name of the client instance.
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// State db cache-size if not default
|
/// RocksDB state column cache-size if not default
|
||||||
pub db_cache_size: Option<usize>,
|
pub db_cache_size: Option<usize>,
|
||||||
/// State db compaction profile
|
/// State db compaction profile
|
||||||
pub db_compaction: DatabaseCompactionProfile,
|
pub db_compaction: DatabaseCompactionProfile,
|
||||||
@ -106,6 +111,12 @@ pub struct ClientConfig {
|
|||||||
pub mode: Mode,
|
pub mode: Mode,
|
||||||
/// Type of block verifier used by client.
|
/// Type of block verifier used by client.
|
||||||
pub verifier_type: VerifierType,
|
pub verifier_type: VerifierType,
|
||||||
|
/// State db cache-size.
|
||||||
|
pub state_cache_size: usize,
|
||||||
|
/// EVM jump-tables cache size.
|
||||||
|
pub jump_table_size: usize,
|
||||||
|
/// State pruning history size.
|
||||||
|
pub history: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -114,13 +125,13 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_default_compaction_profile() {
|
fn test_default_compaction_profile() {
|
||||||
assert_eq!(DatabaseCompactionProfile::default(), DatabaseCompactionProfile::Default);
|
assert_eq!(DatabaseCompactionProfile::default(), DatabaseCompactionProfile::Auto);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parsing_compaction_profile() {
|
fn test_parsing_compaction_profile() {
|
||||||
assert_eq!(DatabaseCompactionProfile::Default, "ssd".parse().unwrap());
|
assert_eq!(DatabaseCompactionProfile::Auto, "auto".parse().unwrap());
|
||||||
assert_eq!(DatabaseCompactionProfile::Default, "default".parse().unwrap());
|
assert_eq!(DatabaseCompactionProfile::SSD, "ssd".parse().unwrap());
|
||||||
assert_eq!(DatabaseCompactionProfile::HDD, "hdd".parse().unwrap());
|
assert_eq!(DatabaseCompactionProfile::HDD, "hdd".parse().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ impl TestBlockChainClient {
|
|||||||
queue_size: AtomicUsize::new(0),
|
queue_size: AtomicUsize::new(0),
|
||||||
miner: Arc::new(Miner::with_spec(&spec)),
|
miner: Arc::new(Miner::with_spec(&spec)),
|
||||||
spec: spec,
|
spec: spec,
|
||||||
vm_factory: EvmFactory::new(VMType::Interpreter),
|
vm_factory: EvmFactory::new(VMType::Interpreter, 1024 * 1024),
|
||||||
latest_block_timestamp: RwLock::new(10_000_000),
|
latest_block_timestamp: RwLock::new(10_000_000),
|
||||||
};
|
};
|
||||||
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
|
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
|
||||||
@ -308,7 +308,7 @@ pub fn get_temp_state_db() -> GuardedTempResult<StateDB> {
|
|||||||
let temp = RandomTempPath::new();
|
let temp = RandomTempPath::new();
|
||||||
let db = Database::open(&DatabaseConfig::with_columns(NUM_COLUMNS), temp.as_str()).unwrap();
|
let db = Database::open(&DatabaseConfig::with_columns(NUM_COLUMNS), temp.as_str()).unwrap();
|
||||||
let journal_db = journaldb::new(Arc::new(db), journaldb::Algorithm::EarlyMerge, COL_STATE);
|
let journal_db = journaldb::new(Arc::new(db), journaldb::Algorithm::EarlyMerge, COL_STATE);
|
||||||
let state_db = StateDB::new(journal_db);
|
let state_db = StateDB::new(journal_db, 1024 * 1024);
|
||||||
GuardedTempResult {
|
GuardedTempResult {
|
||||||
_temp: temp,
|
_temp: temp,
|
||||||
result: Some(state_db)
|
result: Some(state_db)
|
||||||
@ -570,6 +570,10 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
Ok(h)
|
Ok(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn import_block_with_receipts(&self, b: Bytes, _r: Bytes) -> Result<H256, BlockImportError> {
|
||||||
|
self.import_block(b)
|
||||||
|
}
|
||||||
|
|
||||||
fn queue_info(&self) -> QueueInfo {
|
fn queue_info(&self) -> QueueInfo {
|
||||||
QueueInfo {
|
QueueInfo {
|
||||||
verified_queue_size: self.queue_size.load(AtomicOrder::Relaxed),
|
verified_queue_size: self.queue_size.load(AtomicOrder::Relaxed),
|
||||||
@ -595,6 +599,10 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
genesis_hash: self.genesis_hash.clone(),
|
genesis_hash: self.genesis_hash.clone(),
|
||||||
best_block_hash: self.last_hash.read().clone(),
|
best_block_hash: self.last_hash.read().clone(),
|
||||||
best_block_number: self.blocks.read().len() as BlockNumber - 1,
|
best_block_number: self.blocks.read().len() as BlockNumber - 1,
|
||||||
|
first_block_hash: None,
|
||||||
|
first_block_number: None,
|
||||||
|
ancient_block_hash: None,
|
||||||
|
ancient_block_number: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,6 +139,9 @@ pub trait BlockChainClient : Sync + Send {
|
|||||||
/// Import a block into the blockchain.
|
/// Import a block into the blockchain.
|
||||||
fn import_block(&self, bytes: Bytes) -> Result<H256, BlockImportError>;
|
fn import_block(&self, bytes: Bytes) -> Result<H256, BlockImportError>;
|
||||||
|
|
||||||
|
/// Import a block with transaction receipts. Does no sealing and transaction validation.
|
||||||
|
fn import_block_with_receipts(&self, block_bytes: Bytes, receipts_bytes: Bytes) -> Result<H256, BlockImportError>;
|
||||||
|
|
||||||
/// Get block queue information.
|
/// Get block queue information.
|
||||||
fn queue_info(&self) -> BlockQueueInfo;
|
fn queue_info(&self) -> BlockQueueInfo;
|
||||||
|
|
||||||
|
@ -197,7 +197,7 @@ impl Engine for AuthorityRound {
|
|||||||
if self.is_step_proposer(step, header.author()) {
|
if self.is_step_proposer(step, header.author()) {
|
||||||
if let Some(ap) = accounts {
|
if let Some(ap) = accounts {
|
||||||
// Account should be permanently unlocked, otherwise sealing will fail.
|
// Account should be permanently unlocked, otherwise sealing will fail.
|
||||||
if let Ok(signature) = ap.sign(*header.author(), header.bare_hash()) {
|
if let Ok(signature) = ap.sign(*header.author(), None, header.bare_hash()) {
|
||||||
return Some(vec![encode(&step).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
return Some(vec![encode(&step).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
||||||
} else {
|
} else {
|
||||||
trace!(target: "authorityround", "generate_seal: FAIL: accounts secret key unavailable");
|
trace!(target: "authorityround", "generate_seal: FAIL: accounts secret key unavailable");
|
||||||
|
@ -112,7 +112,7 @@ impl Engine for BasicAuthority {
|
|||||||
let header = block.header();
|
let header = block.header();
|
||||||
let message = header.bare_hash();
|
let message = header.bare_hash();
|
||||||
// account should be pernamently unlocked, otherwise sealing will fail
|
// account should be pernamently unlocked, otherwise sealing will fail
|
||||||
if let Ok(signature) = ap.sign(*block.header().author(), message) {
|
if let Ok(signature) = ap.sign(*block.header().author(), None, message) {
|
||||||
return Some(vec![::rlp::encode(&(&*signature as &[u8])).to_vec()]);
|
return Some(vec![::rlp::encode(&(&*signature as &[u8])).to_vec()]);
|
||||||
} else {
|
} else {
|
||||||
trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable");
|
trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable");
|
||||||
|
@ -127,10 +127,14 @@ pub trait Engine : Sync + Send {
|
|||||||
fn is_builtin(&self, a: &Address) -> bool { self.builtins().contains_key(a) }
|
fn is_builtin(&self, a: &Address) -> bool { self.builtins().contains_key(a) }
|
||||||
/// Determine the code execution cost of the builtin contract with address `a`.
|
/// Determine the code execution cost of the builtin contract with address `a`.
|
||||||
/// Panics if `is_builtin(a)` is not true.
|
/// Panics if `is_builtin(a)` is not true.
|
||||||
fn cost_of_builtin(&self, a: &Address, input: &[u8]) -> U256 { self.builtins().get(a).unwrap().cost(input.len()) }
|
fn cost_of_builtin(&self, a: &Address, input: &[u8]) -> U256 {
|
||||||
|
self.builtins().get(a).expect("queried cost of nonexistent builtin").cost(input.len())
|
||||||
|
}
|
||||||
/// Execution the builtin contract `a` on `input` and return `output`.
|
/// Execution the builtin contract `a` on `input` and return `output`.
|
||||||
/// Panics if `is_builtin(a)` is not true.
|
/// Panics if `is_builtin(a)` is not true.
|
||||||
fn execute_builtin(&self, a: &Address, input: &[u8], output: &mut BytesRef) { self.builtins().get(a).unwrap().execute(input, output); }
|
fn execute_builtin(&self, a: &Address, input: &[u8], output: &mut BytesRef) {
|
||||||
|
self.builtins().get(a).expect("attempted to execute nonexistent builtin").execute(input, output);
|
||||||
|
}
|
||||||
|
|
||||||
/// Add a channel for communication with Client which can be used for sealing.
|
/// Add a channel for communication with Client which can be used for sealing.
|
||||||
fn register_message_channel(&self, _message_channel: IoChannel<ClientIoMessage>) {}
|
fn register_message_channel(&self, _message_channel: IoChannel<ClientIoMessage>) {}
|
||||||
|
@ -41,7 +41,7 @@ pub struct EthashParams {
|
|||||||
/// Namereg contract address.
|
/// Namereg contract address.
|
||||||
pub registrar: Address,
|
pub registrar: Address,
|
||||||
/// Homestead transition block number.
|
/// Homestead transition block number.
|
||||||
pub frontier_compatibility_mode_limit: u64,
|
pub homestead_transition: u64,
|
||||||
/// DAO hard-fork transition block (X).
|
/// DAO hard-fork transition block (X).
|
||||||
pub dao_hardfork_transition: u64,
|
pub dao_hardfork_transition: u64,
|
||||||
/// DAO hard-fork refund contract address (C).
|
/// DAO hard-fork refund contract address (C).
|
||||||
@ -54,6 +54,8 @@ pub struct EthashParams {
|
|||||||
pub difficulty_hardfork_bound_divisor: U256,
|
pub difficulty_hardfork_bound_divisor: U256,
|
||||||
/// Block on which there is no additional difficulty from the exponential bomb.
|
/// Block on which there is no additional difficulty from the exponential bomb.
|
||||||
pub bomb_defuse_transition: u64,
|
pub bomb_defuse_transition: u64,
|
||||||
|
/// Bad gas transition block number.
|
||||||
|
pub eip150_transition: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ethjson::spec::EthashParams> for EthashParams {
|
impl From<ethjson::spec::EthashParams> for EthashParams {
|
||||||
@ -66,13 +68,14 @@ impl From<ethjson::spec::EthashParams> for EthashParams {
|
|||||||
duration_limit: p.duration_limit.into(),
|
duration_limit: p.duration_limit.into(),
|
||||||
block_reward: p.block_reward.into(),
|
block_reward: p.block_reward.into(),
|
||||||
registrar: p.registrar.map_or_else(Address::new, Into::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_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_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(),
|
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_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),
|
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),
|
bomb_defuse_transition: p.bomb_defuse_transition.map_or(0x7fffffffffffffff, Into::into),
|
||||||
|
eip150_transition: p.eip150_transition.map_or(0, Into::into),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,12 +120,14 @@ impl Engine for Ethash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn schedule(&self, env_info: &EnvInfo) -> Schedule {
|
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()
|
Schedule::new_frontier()
|
||||||
} else {
|
} else if env_info.number < self.ethash_params.eip150_transition {
|
||||||
Schedule::new_homestead()
|
Schedule::new_homestead()
|
||||||
|
} else {
|
||||||
|
Schedule::new_homestead_gas_fix()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,7 +269,7 @@ impl Engine for Ethash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn verify_transaction_basic(&self, t: &SignedTransaction, header: &Header) -> result::Result<(), Error> {
|
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());
|
try!(t.check_low_s());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -290,7 +295,7 @@ impl Ethash {
|
|||||||
false => self.ethash_params.difficulty_bound_divisor,
|
false => self.ethash_params.difficulty_bound_divisor,
|
||||||
};
|
};
|
||||||
let duration_limit = self.ethash_params.duration_limit;
|
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 {
|
let mut target = if header.number() < frontier_limit {
|
||||||
if header.timestamp() >= parent.timestamp() + duration_limit {
|
if header.timestamp() >= parent.timestamp() + duration_limit {
|
||||||
|
@ -51,8 +51,11 @@ pub fn new_frontier_test() -> Spec { load(include_bytes!("../../res/ethereum/fro
|
|||||||
/// Create a new Homestead chain spec as though it never changed from Frontier.
|
/// Create a new Homestead chain spec as though it never changed from Frontier.
|
||||||
pub fn new_homestead_test() -> Spec { load(include_bytes!("../../res/ethereum/homestead_test.json")) }
|
pub fn new_homestead_test() -> 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 { load(include_bytes!("../../res/ethereum/eip150_test.json")) }
|
||||||
|
|
||||||
/// Create a new Frontier/Homestead/DAO chain spec with transition points at #5 and #8.
|
/// Create a new Frontier/Homestead/DAO chain spec with transition points at #5 and #8.
|
||||||
pub fn new_daohardfork_test() -> Spec { load(include_bytes!("../../res/ethereum/daohardfork_test.json")) }
|
pub fn new_transition_test() -> Spec { load(include_bytes!("../../res/ethereum/transition_test.json")) }
|
||||||
|
|
||||||
/// Create a new Frontier main net chain spec without genesis accounts.
|
/// Create a new Frontier main net chain spec without genesis accounts.
|
||||||
pub fn new_mainnet_like() -> Spec { load(include_bytes!("../../res/ethereum/frontier_like_test.json")) }
|
pub fn new_mainnet_like() -> Spec { load(include_bytes!("../../res/ethereum/frontier_like_test.json")) }
|
||||||
|
@ -118,11 +118,12 @@ impl Factory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create new instance of specific `VMType` factory
|
/// Create new instance of specific `VMType` factory, with a size in bytes
|
||||||
pub fn new(evm: VMType) -> Self {
|
/// for caching jump destinations.
|
||||||
|
pub fn new(evm: VMType, cache_size: usize) -> Self {
|
||||||
Factory {
|
Factory {
|
||||||
evm: evm,
|
evm: evm,
|
||||||
evm_cache: Arc::new(SharedCache::default()),
|
evm_cache: Arc::new(SharedCache::new(cache_size)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,22 +165,22 @@ macro_rules! evm_test(
|
|||||||
#[ignore]
|
#[ignore]
|
||||||
#[cfg(feature = "jit")]
|
#[cfg(feature = "jit")]
|
||||||
fn $name_jit() {
|
fn $name_jit() {
|
||||||
$name_test(Factory::new(VMType::Jit));
|
$name_test(Factory::new(VMType::Jit, 1024 * 32));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn $name_int() {
|
fn $name_int() {
|
||||||
$name_test(Factory::new(VMType::Interpreter));
|
$name_test(Factory::new(VMType::Interpreter, 1024 * 32));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($name_test: ident: $name_jit: ident, $name_int: ident) => {
|
($name_test: ident: $name_jit: ident, $name_int: ident) => {
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature = "jit")]
|
#[cfg(feature = "jit")]
|
||||||
fn $name_jit() {
|
fn $name_jit() {
|
||||||
$name_test(Factory::new(VMType::Jit));
|
$name_test(Factory::new(VMType::Jit, 1024 * 32));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn $name_int() {
|
fn $name_int() {
|
||||||
$name_test(Factory::new(VMType::Interpreter));
|
$name_test(Factory::new(VMType::Interpreter, 1024 * 32));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -193,13 +194,13 @@ macro_rules! evm_test_ignore(
|
|||||||
#[cfg(feature = "jit")]
|
#[cfg(feature = "jit")]
|
||||||
#[cfg(feature = "ignored-tests")]
|
#[cfg(feature = "ignored-tests")]
|
||||||
fn $name_jit() {
|
fn $name_jit() {
|
||||||
$name_test(Factory::new(VMType::Jit));
|
$name_test(Factory::new(VMType::Jit, 1024 * 32));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
#[cfg(feature = "ignored-tests")]
|
#[cfg(feature = "ignored-tests")]
|
||||||
fn $name_int() {
|
fn $name_int() {
|
||||||
$name_test(Factory::new(VMType::Interpreter));
|
$name_test(Factory::new(VMType::Interpreter, 1024 * 32));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -19,6 +19,7 @@ use super::u256_to_address;
|
|||||||
use evm::{self, CostType};
|
use evm::{self, CostType};
|
||||||
use evm::instructions::{self, Instruction, InstructionInfo};
|
use evm::instructions::{self, Instruction, InstructionInfo};
|
||||||
use evm::interpreter::stack::Stack;
|
use evm::interpreter::stack::Stack;
|
||||||
|
use evm::schedule::Schedule;
|
||||||
|
|
||||||
macro_rules! overflowing {
|
macro_rules! overflowing {
|
||||||
($x: expr) => {{
|
($x: expr) => {{
|
||||||
@ -31,7 +32,7 @@ macro_rules! overflowing {
|
|||||||
#[cfg_attr(feature="dev", allow(enum_variant_names))]
|
#[cfg_attr(feature="dev", allow(enum_variant_names))]
|
||||||
enum InstructionCost<Cost: CostType> {
|
enum InstructionCost<Cost: CostType> {
|
||||||
Gas(Cost),
|
Gas(Cost),
|
||||||
GasMem(Cost, Cost),
|
GasMem(Cost, Cost, Option<Cost>),
|
||||||
GasMemCopy(Cost, Cost, 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))]
|
#[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(
|
pub fn get_gas_cost_mem(
|
||||||
&mut self,
|
&mut self,
|
||||||
ext: &evm::Ext,
|
ext: &evm::Ext,
|
||||||
@ -64,7 +95,7 @@ impl<Gas: CostType> Gasometer<Gas> {
|
|||||||
info: &InstructionInfo,
|
info: &InstructionInfo,
|
||||||
stack: &Stack<U256>,
|
stack: &Stack<U256>,
|
||||||
current_mem_size: usize,
|
current_mem_size: usize,
|
||||||
) -> evm::Result<(Gas, Gas, usize)> {
|
) -> evm::Result<(Gas, Gas, usize, Option<Gas>)> {
|
||||||
let schedule = ext.schedule();
|
let schedule = ext.schedule();
|
||||||
let tier = instructions::get_tier_idx(info.tier);
|
let tier = instructions::get_tier_idx(info.tier);
|
||||||
let default_gas = Gas::from(schedule.tier_step_gas[tier]);
|
let default_gas = Gas::from(schedule.tier_step_gas[tier]);
|
||||||
@ -90,26 +121,42 @@ impl<Gas: CostType> Gasometer<Gas> {
|
|||||||
instructions::SLOAD => {
|
instructions::SLOAD => {
|
||||||
InstructionCost::Gas(Gas::from(schedule.sload_gas))
|
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 address = u256_to_address(stack.peek(0));
|
||||||
|
if !ext.exists(&address) {
|
||||||
|
gas = overflowing!(gas.overflow_add(schedule.suicide_to_new_account_cost.into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
InstructionCost::Gas(gas)
|
||||||
|
},
|
||||||
instructions::MSTORE | instructions::MLOAD => {
|
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 => {
|
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 => {
|
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 => {
|
instructions::SHA3 => {
|
||||||
let w = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(1))), 31));
|
let w = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(1))), 31));
|
||||||
let words = w >> 5;
|
let words = w >> 5;
|
||||||
let gas = Gas::from(schedule.sha3_gas) + (Gas::from(schedule.sha3_word_gas) * words);
|
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 => {
|
instructions::CALLDATACOPY | instructions::CODECOPY => {
|
||||||
InstructionCost::GasMemCopy(default_gas, try!(mem_needed(stack.peek(0), stack.peek(2))), try!(Gas::from_u256(*stack.peek(2))))
|
InstructionCost::GasMemCopy(default_gas, try!(mem_needed(stack.peek(0), stack.peek(2))), try!(Gas::from_u256(*stack.peek(2))))
|
||||||
},
|
},
|
||||||
instructions::EXTCODECOPY => {
|
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 => {
|
instructions::LOG0...instructions::LOG4 => {
|
||||||
let no_of_topics = instructions::get_log_topics(instruction);
|
let no_of_topics = instructions::get_log_topics(instruction);
|
||||||
@ -117,10 +164,10 @@ 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 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)));
|
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 => {
|
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(
|
let mem = cmp::max(
|
||||||
try!(mem_needed(stack.peek(5), stack.peek(6))),
|
try!(mem_needed(stack.peek(5), stack.peek(6))),
|
||||||
try!(mem_needed(stack.peek(3), stack.peek(4)))
|
try!(mem_needed(stack.peek(3), stack.peek(4)))
|
||||||
@ -129,27 +176,49 @@ impl<Gas: CostType> Gasometer<Gas> {
|
|||||||
let address = u256_to_address(stack.peek(1));
|
let address = u256_to_address(stack.peek(1));
|
||||||
|
|
||||||
if instruction == instructions::CALL && !ext.exists(&address) {
|
if instruction == instructions::CALL && !ext.exists(&address) {
|
||||||
gas = overflowing!(gas.overflow_add(Gas::from(schedule.call_new_account_gas)));
|
gas = overflowing!(gas.overflow_add(schedule.call_new_account_gas.into()));
|
||||||
};
|
};
|
||||||
|
|
||||||
if !stack.peek(2).is_zero() {
|
if !stack.peek(2).is_zero() {
|
||||||
gas = overflowing!(gas.overflow_add(Gas::from(schedule.call_value_transfer_gas)));
|
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 => {
|
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(
|
let mem = cmp::max(
|
||||||
try!(mem_needed(stack.peek(4), stack.peek(5))),
|
try!(mem_needed(stack.peek(4), stack.peek(5))),
|
||||||
try!(mem_needed(stack.peek(2), stack.peek(3)))
|
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 => {
|
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)));
|
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 => {
|
instructions::EXP => {
|
||||||
let expon = stack.peek(1);
|
let expon = stack.peek(1);
|
||||||
@ -157,17 +226,17 @@ impl<Gas: CostType> Gasometer<Gas> {
|
|||||||
let gas = Gas::from(schedule.exp_gas + schedule.exp_byte_gas * bytes);
|
let gas = Gas::from(schedule.exp_gas + schedule.exp_byte_gas * bytes);
|
||||||
InstructionCost::Gas(gas)
|
InstructionCost::Gas(gas)
|
||||||
},
|
},
|
||||||
_ => InstructionCost::Gas(default_gas)
|
_ => InstructionCost::Gas(default_gas),
|
||||||
};
|
};
|
||||||
|
|
||||||
match cost {
|
match cost {
|
||||||
InstructionCost::Gas(gas) => {
|
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 (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));
|
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) => {
|
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));
|
let (mem_gas_cost, new_mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem_size));
|
||||||
@ -175,7 +244,7 @@ impl<Gas: CostType> Gasometer<Gas> {
|
|||||||
let copy_gas = Gas::from(schedule.copy_gas) * copy;
|
let copy_gas = Gas::from(schedule.copy_gas) * copy;
|
||||||
let gas = overflowing!(gas.overflow_add(copy_gas));
|
let gas = overflowing!(gas.overflow_add(copy_gas));
|
||||||
let gas = overflowing!(gas.overflow_add(mem_gas_cost));
|
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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,8 +81,6 @@ impl<'a> CodeReader<'a> {
|
|||||||
|
|
||||||
enum InstructionResult<Gas> {
|
enum InstructionResult<Gas> {
|
||||||
Ok,
|
Ok,
|
||||||
UseAllGas,
|
|
||||||
GasLeft(Gas),
|
|
||||||
UnusedGas(Gas),
|
UnusedGas(Gas),
|
||||||
JumpToPosition(U256),
|
JumpToPosition(U256),
|
||||||
// gas left, init_orf, init_size
|
// gas left, init_orf, init_size
|
||||||
@ -104,7 +102,7 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
|||||||
|
|
||||||
let mut informant = informant::EvmInformant::new(ext.depth());
|
let mut informant = informant::EvmInformant::new(ext.depth());
|
||||||
|
|
||||||
let code = ¶ms.code.as_ref().unwrap();
|
let code = ¶ms.code.as_ref().expect("exec always called with code; qed");
|
||||||
let valid_jump_destinations = self.cache.jump_destinations(¶ms.code_hash, code);
|
let valid_jump_destinations = self.cache.jump_destinations(¶ms.code_hash, code);
|
||||||
|
|
||||||
let mut gasometer = Gasometer::<Cost>::new(try!(Cost::from_u256(params.gas)));
|
let mut gasometer = Gasometer::<Cost>::new(try!(Cost::from_u256(params.gas)));
|
||||||
@ -120,7 +118,7 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
|||||||
try!(self.verify_instruction(ext, instruction, info, &stack));
|
try!(self.verify_instruction(ext, instruction, info, &stack));
|
||||||
|
|
||||||
// Calculate gas cost
|
// 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.
|
// 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());
|
let trace_executed = ext.trace_prepare_execute(reader.position - 1, instruction, &gas_cost.as_u256());
|
||||||
|
|
||||||
@ -138,27 +136,21 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
|||||||
|
|
||||||
// Execute instruction
|
// Execute instruction
|
||||||
let result = try!(self.exec_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
|
||||||
));
|
));
|
||||||
|
|
||||||
evm_debug!({ informant.after_instruction(instruction) });
|
evm_debug!({ informant.after_instruction(instruction) });
|
||||||
|
|
||||||
|
if let InstructionResult::UnusedGas(ref gas) = result {
|
||||||
|
gasometer.current_gas = gasometer.current_gas + *gas;
|
||||||
|
}
|
||||||
|
|
||||||
if trace_executed {
|
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);
|
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
|
// Advance
|
||||||
match result {
|
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) => {
|
InstructionResult::JumpToPosition(position) => {
|
||||||
let pos = try!(self.verify_jump(position, &valid_jump_destinations));
|
let pos = try!(self.verify_jump(position, &valid_jump_destinations));
|
||||||
reader.position = pos;
|
reader.position = pos;
|
||||||
@ -168,6 +160,7 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
|||||||
return Ok(GasLeft::NeedsReturn(gas.as_u256(), self.mem.read_slice(off, size)));
|
return Ok(GasLeft::NeedsReturn(gas.as_u256(), self.mem.read_slice(off, size)));
|
||||||
},
|
},
|
||||||
InstructionResult::StopExecution => break,
|
InstructionResult::StopExecution => break,
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
informant.done();
|
informant.done();
|
||||||
@ -250,7 +243,8 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
ext: &mut evm::Ext,
|
ext: &mut evm::Ext,
|
||||||
instruction: Instruction,
|
instruction: Instruction,
|
||||||
code: &mut CodeReader,
|
code: &mut CodeReader,
|
||||||
stack: &mut Stack<U256>
|
stack: &mut Stack<U256>,
|
||||||
|
provided: Option<Cost>
|
||||||
) -> evm::Result<InstructionResult<Cost>> {
|
) -> evm::Result<InstructionResult<Cost>> {
|
||||||
match instruction {
|
match instruction {
|
||||||
instructions::JUMP => {
|
instructions::JUMP => {
|
||||||
@ -275,31 +269,32 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
let endowment = stack.pop_back();
|
let endowment = stack.pop_back();
|
||||||
let init_off = stack.pop_back();
|
let init_off = stack.pop_back();
|
||||||
let init_size = 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 contract_code = self.mem.read_slice(init_off, init_size);
|
||||||
let can_create = ext.balance(¶ms.address) >= endowment && ext.depth() < ext.schedule().max_depth;
|
let can_create = ext.balance(¶ms.address) >= endowment && ext.depth() < ext.schedule().max_depth;
|
||||||
|
|
||||||
if !can_create {
|
if !can_create {
|
||||||
stack.push(U256::zero());
|
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 {
|
return match create_result {
|
||||||
ContractCreateResult::Created(address, gas_left) => {
|
ContractCreateResult::Created(address, gas_left) => {
|
||||||
stack.push(address_to_u256(address));
|
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 => {
|
ContractCreateResult::Failed => {
|
||||||
stack.push(U256::zero());
|
stack.push(U256::zero());
|
||||||
// TODO [todr] Should we just StopExecution here?
|
Ok(InstructionResult::Ok)
|
||||||
Ok(InstructionResult::UseAllGas)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL => {
|
instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL => {
|
||||||
assert!(ext.schedule().call_value_transfer_gas > ext.schedule().call_stipend, "overflow possible");
|
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 = stack.pop_back();
|
||||||
let code_address = u256_to_address(&code_address);
|
let code_address = u256_to_address(&code_address);
|
||||||
|
|
||||||
@ -317,17 +312,17 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
// Add stipend (only CALL|CALLCODE when value > 0)
|
// 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() {
|
let call_gas = call_gas + value.map_or_else(|| Cost::from(0), |val| match val.is_zero() {
|
||||||
false => Cost::from(ext.schedule().call_stipend),
|
false => Cost::from(ext.schedule().call_stipend),
|
||||||
true => Cost::from(0)
|
true => Cost::from(0),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get sender & receive addresses, check if we have balance
|
// Get sender & receive addresses, check if we have balance
|
||||||
let (sender_address, receive_address, has_balance, call_type) = match instruction {
|
let (sender_address, receive_address, has_balance, call_type) = match instruction {
|
||||||
instructions::CALL => {
|
instructions::CALL => {
|
||||||
let has_balance = ext.balance(¶ms.address) >= value.unwrap();
|
let has_balance = ext.balance(¶ms.address) >= value.expect("value set for all but delegate call; qed");
|
||||||
(¶ms.address, &code_address, has_balance, CallType::Call)
|
(¶ms.address, &code_address, has_balance, CallType::Call)
|
||||||
},
|
},
|
||||||
instructions::CALLCODE => {
|
instructions::CALLCODE => {
|
||||||
let has_balance = ext.balance(¶ms.address) >= value.unwrap();
|
let has_balance = ext.balance(¶ms.address) >= value.expect("value set for all but delegate call; qed");
|
||||||
(¶ms.address, ¶ms.address, has_balance, CallType::CallCode)
|
(¶ms.address, ¶ms.address, has_balance, CallType::CallCode)
|
||||||
},
|
},
|
||||||
instructions::DELEGATECALL => (¶ms.sender, ¶ms.address, true, CallType::DelegateCall),
|
instructions::DELEGATECALL => (¶ms.sender, ¶ms.address, true, CallType::DelegateCall),
|
||||||
|
@ -21,25 +21,66 @@ use util::sha3::*;
|
|||||||
use bit_set::BitSet;
|
use bit_set::BitSet;
|
||||||
use super::super::instructions;
|
use super::super::instructions;
|
||||||
|
|
||||||
const CACHE_CODE_ITEMS: usize = 65536;
|
const INITIAL_CAPACITY: usize = 32;
|
||||||
|
const DEFAULT_CACHE_SIZE: usize = 4 * 1024 * 1024;
|
||||||
|
|
||||||
/// GLobal cache for EVM interpreter
|
/// Global cache for EVM interpreter
|
||||||
pub struct SharedCache {
|
pub struct SharedCache {
|
||||||
jump_destinations: Mutex<LruCache<H256, Arc<BitSet>>>
|
jump_destinations: Mutex<LruCache<H256, Arc<BitSet>>>,
|
||||||
|
max_size: usize,
|
||||||
|
cur_size: Mutex<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SharedCache {
|
impl SharedCache {
|
||||||
/// Get jump destincations bitmap for a contract.
|
/// Create a jump destinations cache with a maximum size in bytes
|
||||||
|
/// to cache.
|
||||||
|
pub fn new(max_size: usize) -> Self {
|
||||||
|
SharedCache {
|
||||||
|
jump_destinations: Mutex::new(LruCache::new(INITIAL_CAPACITY)),
|
||||||
|
max_size: max_size * 8, // dealing with bits here.
|
||||||
|
cur_size: Mutex::new(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get jump destinations bitmap for a contract.
|
||||||
pub fn jump_destinations(&self, code_hash: &H256, code: &[u8]) -> Arc<BitSet> {
|
pub fn jump_destinations(&self, code_hash: &H256, code: &[u8]) -> Arc<BitSet> {
|
||||||
if code_hash == &SHA3_EMPTY {
|
if code_hash == &SHA3_EMPTY {
|
||||||
return Self::find_jump_destinations(code);
|
return Self::find_jump_destinations(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(d) = self.jump_destinations.lock().get_mut(code_hash) {
|
if let Some(d) = self.jump_destinations.lock().get_mut(code_hash) {
|
||||||
return d.clone();
|
return d.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
let d = Self::find_jump_destinations(code);
|
let d = Self::find_jump_destinations(code);
|
||||||
self.jump_destinations.lock().insert(code_hash.clone(), d.clone());
|
|
||||||
|
{
|
||||||
|
let mut cur_size = self.cur_size.lock();
|
||||||
|
*cur_size += d.capacity();
|
||||||
|
|
||||||
|
let mut jump_dests = self.jump_destinations.lock();
|
||||||
|
let cap = jump_dests.capacity();
|
||||||
|
|
||||||
|
// grow the cache as necessary; it operates on amount of items
|
||||||
|
// but we're working based on memory usage.
|
||||||
|
if jump_dests.len() == cap && *cur_size < self.max_size {
|
||||||
|
jump_dests.set_capacity(cap * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// account for any element displaced from the cache.
|
||||||
|
if let Some(lru) = jump_dests.insert(code_hash.clone(), d.clone()) {
|
||||||
|
*cur_size -= lru.capacity();
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove elements until we are below the memory target.
|
||||||
|
while *cur_size > self.max_size {
|
||||||
|
match jump_dests.remove_lru() {
|
||||||
|
Some((_, v)) => *cur_size -= v.capacity(),
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
d
|
d
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,15 +98,15 @@ impl SharedCache {
|
|||||||
}
|
}
|
||||||
position += 1;
|
position += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jump_dests.shrink_to_fit();
|
||||||
Arc::new(jump_dests)
|
Arc::new(jump_dests)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SharedCache {
|
impl Default for SharedCache {
|
||||||
fn default() -> SharedCache {
|
fn default() -> Self {
|
||||||
SharedCache {
|
SharedCache::new(DEFAULT_CACHE_SIZE)
|
||||||
jump_destinations: Mutex::new(LruCache::new(CACHE_CODE_ITEMS)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,7 +359,7 @@ impl evm::Evm for JitEvm {
|
|||||||
data.timestamp = ext.env_info().timestamp as i64;
|
data.timestamp = ext.env_info().timestamp as i64;
|
||||||
|
|
||||||
self.context = Some(unsafe { evmjit::ContextHandle::new(data, schedule, &mut ext_handle) });
|
self.context = Some(unsafe { evmjit::ContextHandle::new(data, schedule, &mut ext_handle) });
|
||||||
let mut context = self.context.as_mut().unwrap();
|
let mut context = self.context.as_mut().expect("context handle set on the prior line; qed");
|
||||||
let res = context.exec();
|
let res = context.exec();
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
|
@ -80,6 +80,19 @@ pub struct Schedule {
|
|||||||
pub tx_data_non_zero_gas: usize,
|
pub tx_data_non_zero_gas: usize,
|
||||||
/// Gas price for copying memory
|
/// Gas price for copying memory
|
||||||
pub copy_gas: usize,
|
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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Schedule {
|
impl Schedule {
|
||||||
@ -93,6 +106,49 @@ impl Schedule {
|
|||||||
Self::new(true, true, 53000)
|
Self::new(true, true, 53000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Schedule for the Homestead-era of the Ethereum main net.
|
||||||
|
pub fn new_homestead_gas_fix() -> 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: 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn new(efcd: bool, hdc: bool, tcg: usize) -> Schedule {
|
fn new(efcd: bool, hdc: bool, tcg: usize) -> Schedule {
|
||||||
Schedule{
|
Schedule{
|
||||||
exceptional_failed_code_deposit: efcd,
|
exceptional_failed_code_deposit: efcd,
|
||||||
@ -126,6 +182,12 @@ impl Schedule {
|
|||||||
tx_data_zero_gas: 4,
|
tx_data_zero_gas: 4,
|
||||||
tx_data_non_zero_gas: 68,
|
tx_data_non_zero_gas: 68,
|
||||||
copy_gas: 3,
|
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -817,7 +817,7 @@ fn test_signextend(factory: super::Factory) {
|
|||||||
|
|
||||||
#[test] // JIT just returns out of gas
|
#[test] // JIT just returns out of gas
|
||||||
fn test_badinstruction_int() {
|
fn test_badinstruction_int() {
|
||||||
let factory = super::Factory::new(VMType::Interpreter);
|
let factory = super::Factory::new(VMType::Interpreter, 1024 * 32);
|
||||||
let code = "af".from_hex().unwrap();
|
let code = "af".from_hex().unwrap();
|
||||||
|
|
||||||
let mut params = ActionParams::default();
|
let mut params = ActionParams::default();
|
||||||
|
@ -427,8 +427,16 @@ 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",
|
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);
|
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());
|
let sender = match t.sender() {
|
||||||
self.state.add_balance(&t.sender().unwrap(), &refund_value);
|
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);
|
||||||
|
self.state.add_balance(&sender, &refund_value);
|
||||||
trace!("exec::finalize: Compensating author: fees_value={}, author={}\n", fees_value, &self.info.author);
|
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);
|
||||||
|
|
||||||
@ -598,7 +606,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
// Tracing is not suported in JIT
|
// Tracing is not suported in JIT
|
||||||
fn test_call_to_create() {
|
fn test_call_to_create() {
|
||||||
let factory = Factory::new(VMType::Interpreter);
|
let factory = Factory::new(VMType::Interpreter, 1024 * 32);
|
||||||
|
|
||||||
// code:
|
// code:
|
||||||
//
|
//
|
||||||
@ -697,7 +705,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: 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: 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: 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: 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() }) }) }
|
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() }) }) }
|
||||||
],
|
],
|
||||||
@ -724,7 +732,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_create_contract() {
|
fn test_create_contract() {
|
||||||
// Tracing is not supported in JIT
|
// Tracing is not supported in JIT
|
||||||
let factory = Factory::new(VMType::Interpreter);
|
let factory = Factory::new(VMType::Interpreter, 1024 * 32);
|
||||||
// code:
|
// code:
|
||||||
//
|
//
|
||||||
// 60 10 - push 16
|
// 60 10 - push 16
|
||||||
|
@ -199,8 +199,9 @@ impl Header {
|
|||||||
match &mut *hash {
|
match &mut *hash {
|
||||||
&mut Some(ref h) => h.clone(),
|
&mut Some(ref h) => h.clone(),
|
||||||
hash @ &mut None => {
|
hash @ &mut None => {
|
||||||
*hash = Some(self.rlp_sha3(Seal::With));
|
let h = self.rlp_sha3(Seal::With);
|
||||||
hash.as_ref().unwrap().clone()
|
*hash = Some(h.clone());
|
||||||
|
h
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -211,8 +212,9 @@ impl Header {
|
|||||||
match &mut *hash {
|
match &mut *hash {
|
||||||
&mut Some(ref h) => h.clone(),
|
&mut Some(ref h) => h.clone(),
|
||||||
hash @ &mut None => {
|
hash @ &mut None => {
|
||||||
*hash = Some(self.rlp_sha3(Seal::Without));
|
let h = self.rlp_sha3(Seal::Without);
|
||||||
hash.as_ref().unwrap().clone()
|
*hash = Some(h.clone());
|
||||||
|
h
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,8 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
|
|||||||
let mut spec = match era {
|
let mut spec = match era {
|
||||||
ChainEra::Frontier => ethereum::new_frontier_test(),
|
ChainEra::Frontier => ethereum::new_frontier_test(),
|
||||||
ChainEra::Homestead => ethereum::new_homestead_test(),
|
ChainEra::Homestead => ethereum::new_homestead_test(),
|
||||||
ChainEra::DaoHardfork => ethereum::new_daohardfork_test(),
|
ChainEra::Eip150 => ethereum::new_eip150_test(),
|
||||||
|
ChainEra::TransitionTest => ethereum::new_transition_test(),
|
||||||
};
|
};
|
||||||
spec.set_genesis_state(state);
|
spec.set_genesis_state(state);
|
||||||
spec.overwrite_genesis_params(genesis);
|
spec.overwrite_genesis_params(genesis);
|
||||||
@ -116,14 +117,38 @@ mod frontier_era_tests {
|
|||||||
declare_test!{BlockchainTests_RandomTests_bl201507071825GO, "BlockchainTests/RandomTests/bl201507071825GO"}
|
declare_test!{BlockchainTests_RandomTests_bl201507071825GO, "BlockchainTests/RandomTests/bl201507071825GO"}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod daohardfork_tests {
|
mod transition_tests {
|
||||||
use tests::helpers::*;
|
use tests::helpers::*;
|
||||||
use super::json_chain_test;
|
use super::json_chain_test;
|
||||||
|
|
||||||
fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
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_bcSimpleTransitionTest, "BlockchainTests/TestNetwork/bcSimpleTransitionTest"}
|
||||||
declare_test!{BlockchainTests_TestNetwork_bcTheDaoTest, "BlockchainTests/TestNetwork/bcTheDaoTest"}
|
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"}
|
@ -191,7 +191,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec<String> {
|
|||||||
state.populate_from(From::from(vm.pre_state.clone()));
|
state.populate_from(From::from(vm.pre_state.clone()));
|
||||||
let info = From::from(vm.env);
|
let info = From::from(vm.env);
|
||||||
let engine = TestEngine::new(1);
|
let engine = TestEngine::new(1);
|
||||||
let vm_factory = Factory::new(vm_type.clone());
|
let vm_factory = Factory::new(vm_type.clone(), 1024 * 32);
|
||||||
let params = ActionParams::from(vm.transaction);
|
let params = ActionParams::from(vm.transaction);
|
||||||
|
|
||||||
let mut substate = Substate::new();
|
let mut substate = Substate::new();
|
||||||
|
@ -37,6 +37,6 @@ declare_test!{BlockchainTests_Homestead_bcUncleTest, "BlockchainTests/Homestead/
|
|||||||
declare_test!{BlockchainTests_Homestead_bcValidBlockTest, "BlockchainTests/Homestead/bcValidBlockTest"}
|
declare_test!{BlockchainTests_Homestead_bcValidBlockTest, "BlockchainTests/Homestead/bcValidBlockTest"}
|
||||||
declare_test!{BlockchainTests_Homestead_bcWalletTest, "BlockchainTests/Homestead/bcWalletTest"}
|
declare_test!{BlockchainTests_Homestead_bcWalletTest, "BlockchainTests/Homestead/bcWalletTest"}
|
||||||
declare_test!{BlockchainTests_Homestead_bcShanghaiLove, "BlockchainTests/Homestead/bcShanghaiLove"}
|
declare_test!{BlockchainTests_Homestead_bcShanghaiLove, "BlockchainTests/Homestead/bcShanghaiLove"}
|
||||||
// Uncomment once the test is correct.
|
// TODO [ToDr] uncomment as soon as eip150 tests are merged to develop branch of ethereum/tests
|
||||||
// declare_test!{BlockchainTests_Homestead_bcSuicideIssue, "BlockchainTests/Homestead/bcSuicideIssue"}
|
// declare_test!{BlockchainTests_Homestead_bcSuicideIssue, "BlockchainTests/Homestead/bcSuicideIssue"}
|
||||||
declare_test!{BlockchainTests_Homestead_bcExploitTest, "BlockchainTests/Homestead/bcExploitTest"}
|
declare_test!{BlockchainTests_Homestead_bcExploitTest, "BlockchainTests/Homestead/bcExploitTest"}
|
||||||
|
@ -23,4 +23,5 @@ mod state;
|
|||||||
mod chain;
|
mod chain;
|
||||||
mod homestead_state;
|
mod homestead_state;
|
||||||
mod homestead_chain;
|
mod homestead_chain;
|
||||||
|
mod eip150_state;
|
||||||
mod trie;
|
mod trie;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -30,7 +30,7 @@ use transaction::{Action, SignedTransaction};
|
|||||||
use receipt::{Receipt, RichReceipt};
|
use receipt::{Receipt, RichReceipt};
|
||||||
use spec::Spec;
|
use spec::Spec;
|
||||||
use engines::Engine;
|
use engines::Engine;
|
||||||
use miner::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionOrigin};
|
use miner::{MinerService, MinerStatus, TransactionQueue, PrioritizationStrategy, AccountDetails, TransactionOrigin};
|
||||||
use miner::work_notify::WorkPoster;
|
use miner::work_notify::WorkPoster;
|
||||||
use client::TransactionImportResult;
|
use client::TransactionImportResult;
|
||||||
use miner::price_info::PriceInfo;
|
use miner::price_info::PriceInfo;
|
||||||
@ -76,6 +76,8 @@ pub struct MinerOptions {
|
|||||||
pub tx_gas_limit: U256,
|
pub tx_gas_limit: U256,
|
||||||
/// Maximum size of the transaction queue.
|
/// Maximum size of the transaction queue.
|
||||||
pub tx_queue_size: usize,
|
pub tx_queue_size: usize,
|
||||||
|
/// Strategy to use for prioritizing transactions in the queue.
|
||||||
|
pub tx_queue_strategy: PrioritizationStrategy,
|
||||||
/// Whether we should fallback to providing all the queue's transactions or just pending.
|
/// Whether we should fallback to providing all the queue's transactions or just pending.
|
||||||
pub pending_set: PendingSet,
|
pub pending_set: PendingSet,
|
||||||
/// How many historical work packages can we store before running out?
|
/// How many historical work packages can we store before running out?
|
||||||
@ -94,12 +96,13 @@ impl Default for MinerOptions {
|
|||||||
reseal_on_external_tx: false,
|
reseal_on_external_tx: false,
|
||||||
reseal_on_own_tx: true,
|
reseal_on_own_tx: true,
|
||||||
tx_gas_limit: !U256::zero(),
|
tx_gas_limit: !U256::zero(),
|
||||||
tx_queue_size: 2048,
|
tx_queue_size: 1024,
|
||||||
|
tx_queue_gas_limit: GasLimit::Auto,
|
||||||
|
tx_queue_strategy: PrioritizationStrategy::GasFactorAndGasPrice,
|
||||||
pending_set: PendingSet::AlwaysQueue,
|
pending_set: PendingSet::AlwaysQueue,
|
||||||
reseal_min_period: Duration::from_secs(2),
|
reseal_min_period: Duration::from_secs(2),
|
||||||
work_queue_size: 20,
|
work_queue_size: 20,
|
||||||
enable_resubmission: true,
|
enable_resubmission: true,
|
||||||
tx_queue_gas_limit: GasLimit::Auto,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -133,7 +136,7 @@ impl GasPriceCalibrator {
|
|||||||
let gas_per_tx: f32 = 21000.0;
|
let gas_per_tx: f32 = 21000.0;
|
||||||
let wei_per_gas: f32 = wei_per_usd * usd_per_tx / gas_per_tx;
|
let wei_per_gas: f32 = wei_per_usd * usd_per_tx / gas_per_tx;
|
||||||
info!(target: "miner", "Updated conversion rate to Ξ1 = {} ({} wei/gas)", Colour::White.bold().paint(format!("US${}", usd_per_eth)), Colour::Yellow.bold().paint(format!("{}", wei_per_gas)));
|
info!(target: "miner", "Updated conversion rate to Ξ1 = {} ({} wei/gas)", Colour::White.bold().paint(format!("US${}", usd_per_eth)), Colour::Yellow.bold().paint(format!("{}", wei_per_gas)));
|
||||||
set_price(U256::from_dec_str(&format!("{:.0}", wei_per_gas)).unwrap());
|
set_price(U256::from(wei_per_gas as u64));
|
||||||
}) {
|
}) {
|
||||||
self.next_calibration = Instant::now() + self.options.recalibration_period;
|
self.next_calibration = Instant::now() + self.options.recalibration_period;
|
||||||
} else {
|
} else {
|
||||||
@ -212,7 +215,9 @@ impl Miner {
|
|||||||
GasLimit::Fixed(ref limit) => *limit,
|
GasLimit::Fixed(ref limit) => *limit,
|
||||||
_ => !U256::zero(),
|
_ => !U256::zero(),
|
||||||
};
|
};
|
||||||
let txq = Arc::new(Mutex::new(TransactionQueue::with_limits(options.tx_queue_size, gas_limit, options.tx_gas_limit)));
|
let txq = Arc::new(Mutex::new(TransactionQueue::with_limits(
|
||||||
|
options.tx_queue_strategy, options.tx_queue_size, gas_limit, options.tx_gas_limit
|
||||||
|
)));
|
||||||
Miner {
|
Miner {
|
||||||
transaction_queue: txq,
|
transaction_queue: txq,
|
||||||
next_allowed_reseal: Mutex::new(Instant::now()),
|
next_allowed_reseal: Mutex::new(Instant::now()),
|
||||||
@ -282,6 +287,7 @@ impl Miner {
|
|||||||
trace!(target: "miner", "prepare_block: done recalibration.");
|
trace!(target: "miner", "prepare_block: done recalibration.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _timer = PerfTimer::new("prepare_block");
|
||||||
let (transactions, mut open_block, original_work_hash) = {
|
let (transactions, mut open_block, original_work_hash) = {
|
||||||
let transactions = {self.transaction_queue.lock().top_transactions()};
|
let transactions = {self.transaction_queue.lock().top_transactions()};
|
||||||
let mut sealing_work = self.sealing_work.lock();
|
let mut sealing_work = self.sealing_work.lock();
|
||||||
@ -468,8 +474,8 @@ impl Miner {
|
|||||||
let mut queue = self.transaction_queue.lock();
|
let mut queue = self.transaction_queue.lock();
|
||||||
queue.set_gas_limit(gas_limit);
|
queue.set_gas_limit(gas_limit);
|
||||||
if let GasLimit::Auto = self.options.tx_queue_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.
|
// Set total tx queue gas limit to be 20x the block gas limit.
|
||||||
queue.set_total_gas_limit(gas_limit << 1);
|
queue.set_total_gas_limit(gas_limit * 20.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -747,7 +753,7 @@ impl MinerService for Miner {
|
|||||||
let mut transaction_queue = self.transaction_queue.lock();
|
let mut transaction_queue = self.transaction_queue.lock();
|
||||||
let import = self.add_transactions_to_queue(
|
let import = self.add_transactions_to_queue(
|
||||||
chain, vec![transaction], TransactionOrigin::Local, &mut transaction_queue
|
chain, vec![transaction], TransactionOrigin::Local, &mut transaction_queue
|
||||||
).pop().unwrap();
|
).pop().expect("one result returned per added transaction; one added => one result; qed");
|
||||||
|
|
||||||
match import {
|
match import {
|
||||||
Ok(ref res) => {
|
Ok(ref res) => {
|
||||||
@ -869,7 +875,11 @@ impl MinerService for Miner {
|
|||||||
gas_used: receipt.gas_used - prev_gas,
|
gas_used: receipt.gas_used - prev_gas,
|
||||||
contract_address: match tx.action {
|
contract_address: match tx.action {
|
||||||
Action::Call(_) => None,
|
Action::Call(_) => None,
|
||||||
Action::Create => Some(contract_address(&tx.sender().unwrap(), &tx.nonce)),
|
Action::Create => {
|
||||||
|
let sender = tx.sender()
|
||||||
|
.expect("transactions in pending block have already been checked for valid sender; qed");
|
||||||
|
Some(contract_address(&sender, &tx.nonce))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
logs: receipt.logs.clone(),
|
logs: receipt.logs.clone(),
|
||||||
}
|
}
|
||||||
@ -1035,7 +1045,7 @@ impl MinerService for Miner {
|
|||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use super::super::MinerService;
|
use super::super::{MinerService, PrioritizationStrategy};
|
||||||
use super::*;
|
use super::*;
|
||||||
use util::*;
|
use util::*;
|
||||||
use ethkey::{Generator, Random};
|
use ethkey::{Generator, Random};
|
||||||
@ -1089,6 +1099,7 @@ mod tests {
|
|||||||
tx_gas_limit: !U256::zero(),
|
tx_gas_limit: !U256::zero(),
|
||||||
tx_queue_size: 1024,
|
tx_queue_size: 1024,
|
||||||
tx_queue_gas_limit: GasLimit::None,
|
tx_queue_gas_limit: GasLimit::None,
|
||||||
|
tx_queue_strategy: PrioritizationStrategy::GasFactorAndGasPrice,
|
||||||
pending_set: PendingSet::AlwaysSealing,
|
pending_set: PendingSet::AlwaysSealing,
|
||||||
work_queue_size: 5,
|
work_queue_size: 5,
|
||||||
enable_resubmission: true,
|
enable_resubmission: true,
|
||||||
|
@ -47,7 +47,7 @@ mod transaction_queue;
|
|||||||
mod work_notify;
|
mod work_notify;
|
||||||
mod price_info;
|
mod price_info;
|
||||||
|
|
||||||
pub use self::transaction_queue::{TransactionQueue, AccountDetails, TransactionOrigin};
|
pub use self::transaction_queue::{TransactionQueue, PrioritizationStrategy, AccountDetails, TransactionOrigin};
|
||||||
pub use self::miner::{Miner, MinerOptions, PendingSet, GasPricer, GasPriceCalibratorOptions, GasLimit};
|
pub use self::miner::{Miner, MinerOptions, PendingSet, GasPricer, GasPriceCalibratorOptions, GasLimit};
|
||||||
pub use self::external::{ExternalMiner, ExternalMinerService};
|
pub use self::external::{ExternalMiner, ExternalMinerService};
|
||||||
pub use client::TransactionImportResult;
|
pub use client::TransactionImportResult;
|
||||||
|
@ -52,7 +52,8 @@ impl<F: Fn(PriceInfo) + Sync + Send + 'static> Handler<HttpStream> for SetPriceH
|
|||||||
.and_then(|json| json.find_path(&["result", "ethusd"])
|
.and_then(|json| json.find_path(&["result", "ethusd"])
|
||||||
.and_then(|obj| match *obj {
|
.and_then(|obj| match *obj {
|
||||||
Json::String(ref s) => Some((self.set_price)(PriceInfo {
|
Json::String(ref s) => Some((self.set_price)(PriceInfo {
|
||||||
ethusd: FromStr::from_str(s).unwrap()
|
ethusd: FromStr::from_str(s)
|
||||||
|
.expect("Etherscan API will always return properly formatted price; qed")
|
||||||
})),
|
})),
|
||||||
_ => None,
|
_ => None,
|
||||||
}));
|
}));
|
||||||
@ -67,10 +68,14 @@ impl PriceInfo {
|
|||||||
let client = try!(Client::new().map_err(|_| ()));
|
let client = try!(Client::new().map_err(|_| ()));
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
let _ = client.request(FromStr::from_str("http://api.etherscan.io/api?module=stats&action=ethprice").unwrap(), SetPriceHandler {
|
let url = FromStr::from_str("http://api.etherscan.io/api?module=stats&action=ethprice")
|
||||||
set_price: set_price,
|
.expect("string known to be a valid URL; qed");
|
||||||
channel: tx,
|
let _ = client.request(
|
||||||
}).ok().and_then(|_| rx.recv().ok());
|
url,
|
||||||
|
SetPriceHandler {
|
||||||
|
set_price: set_price,
|
||||||
|
channel: tx,
|
||||||
|
}).ok().and_then(|_| rx.recv().ok());
|
||||||
client.close();
|
client.close();
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -49,7 +49,7 @@
|
|||||||
//! balance: U256::from(1_000_000),
|
//! balance: U256::from(1_000_000),
|
||||||
//! };
|
//! };
|
||||||
//!
|
//!
|
||||||
//! let mut txq = TransactionQueue::new();
|
//! let mut txq = TransactionQueue::default();
|
||||||
//! txq.add(st2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
//! txq.add(st2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||||
//! txq.add(st1.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
//! txq.add(st1.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||||
//!
|
//!
|
||||||
@ -130,11 +130,20 @@ struct TransactionOrder {
|
|||||||
/// (e.g. Tx(nonce:5), State(nonce:0) -> height: 5)
|
/// (e.g. Tx(nonce:5), State(nonce:0) -> height: 5)
|
||||||
/// High nonce_height = Low priority (processed later)
|
/// High nonce_height = Low priority (processed later)
|
||||||
nonce_height: U256,
|
nonce_height: U256,
|
||||||
/// Gas specified in the transaction.
|
|
||||||
gas: U256,
|
|
||||||
/// Gas Price of the transaction.
|
/// Gas Price of the transaction.
|
||||||
/// Low gas price = Low priority (processed later)
|
/// Low gas price = Low priority (processed later)
|
||||||
gas_price: U256,
|
gas_price: U256,
|
||||||
|
/// Gas usage priority factor. Usage depends on strategy.
|
||||||
|
/// Represents the linear increment in required gas price for heavy transactions.
|
||||||
|
///
|
||||||
|
/// High gas limit + Low gas price = Low priority
|
||||||
|
/// High gas limit + High gas price = High priority
|
||||||
|
gas_factor: U256,
|
||||||
|
/// Gas (limit) of the transaction. Usage depends on strategy.
|
||||||
|
/// Low gas limit = High priority (processed earlier)
|
||||||
|
gas: U256,
|
||||||
|
/// Transaction ordering strategy
|
||||||
|
strategy: PrioritizationStrategy,
|
||||||
/// Hash to identify associated transaction
|
/// Hash to identify associated transaction
|
||||||
hash: H256,
|
hash: H256,
|
||||||
/// Origin of the transaction
|
/// Origin of the transaction
|
||||||
@ -145,11 +154,15 @@ struct TransactionOrder {
|
|||||||
|
|
||||||
|
|
||||||
impl TransactionOrder {
|
impl TransactionOrder {
|
||||||
fn for_transaction(tx: &VerifiedTransaction, base_nonce: U256) -> Self {
|
|
||||||
|
fn for_transaction(tx: &VerifiedTransaction, base_nonce: U256, min_gas_price: U256, strategy: PrioritizationStrategy) -> Self {
|
||||||
|
let factor = (tx.transaction.gas >> 15) * min_gas_price;
|
||||||
TransactionOrder {
|
TransactionOrder {
|
||||||
nonce_height: tx.nonce() - base_nonce,
|
nonce_height: tx.nonce() - base_nonce,
|
||||||
gas: tx.transaction.gas.clone(),
|
|
||||||
gas_price: tx.transaction.gas_price,
|
gas_price: tx.transaction.gas_price,
|
||||||
|
gas: tx.transaction.gas,
|
||||||
|
gas_factor: factor,
|
||||||
|
strategy: strategy,
|
||||||
hash: tx.hash(),
|
hash: tx.hash(),
|
||||||
origin: tx.origin,
|
origin: tx.origin,
|
||||||
penalties: 0,
|
penalties: 0,
|
||||||
@ -197,11 +210,28 @@ impl Ord for TransactionOrder {
|
|||||||
return self.origin.cmp(&b.origin);
|
return self.origin.cmp(&b.origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match self.strategy {
|
||||||
|
PrioritizationStrategy::GasAndGasPrice => {
|
||||||
|
if self.gas != b.gas {
|
||||||
|
return self.gas.cmp(&b.gas);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PrioritizationStrategy::GasFactorAndGasPrice => {
|
||||||
|
// avoiding overflows
|
||||||
|
// (gp1 - g1) > (gp2 - g2) <=>
|
||||||
|
// (gp1 + g2) > (gp2 + g1)
|
||||||
|
let f_a = self.gas_price + b.gas_factor;
|
||||||
|
let f_b = b.gas_price + self.gas_factor;
|
||||||
|
if f_a != f_b {
|
||||||
|
return f_b.cmp(&f_a);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PrioritizationStrategy::GasPriceOnly => {},
|
||||||
|
}
|
||||||
|
|
||||||
// Then compare gas_prices
|
// Then compare gas_prices
|
||||||
let a_gas = self.gas_price;
|
if self.gas_price != b.gas_price {
|
||||||
let b_gas = b.gas_price;
|
return b.gas_price.cmp(&self.gas_price);
|
||||||
if a_gas != b_gas {
|
|
||||||
return b_gas.cmp(&a_gas);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare hashes
|
// Compare hashes
|
||||||
@ -326,14 +356,14 @@ impl TransactionSet {
|
|||||||
let to_drop : Vec<(Address, U256)> = {
|
let to_drop : Vec<(Address, U256)> = {
|
||||||
self.by_priority
|
self.by_priority
|
||||||
.iter()
|
.iter()
|
||||||
.skip_while(|order| {
|
.filter(|order| {
|
||||||
count = count + 1;
|
count = count + 1;
|
||||||
let r = gas.overflowing_add(order.gas);
|
let r = gas.overflowing_add(order.gas);
|
||||||
if r.1 { return false }
|
if r.1 { return false }
|
||||||
gas = r.0;
|
gas = r.0;
|
||||||
// Own and retracted transactions are allowed to go above the gas limit, bot not above the count limit.
|
// Own and retracted transactions are allowed to go above all limits.
|
||||||
(gas <= self.gas_limit || order.origin == TransactionOrigin::Local || order.origin == TransactionOrigin::RetractedBlock) &&
|
order.origin != TransactionOrigin::Local && order.origin != TransactionOrigin::RetractedBlock &&
|
||||||
count <= self.limit
|
(gas > self.gas_limit || count > self.limit)
|
||||||
})
|
})
|
||||||
.map(|order| by_hash.get(&order.hash)
|
.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`."))
|
.expect("All transactions in `self.by_priority` and `self.by_address` are kept in sync with `by_hash`."))
|
||||||
@ -345,6 +375,7 @@ impl TransactionSet {
|
|||||||
.fold(HashMap::new(), |mut removed, (sender, nonce)| {
|
.fold(HashMap::new(), |mut removed, (sender, nonce)| {
|
||||||
let order = self.drop(&sender, &nonce)
|
let order = self.drop(&sender, &nonce)
|
||||||
.expect("Transaction has just been found in `by_priority`; so it is in `by_address` also.");
|
.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)
|
by_hash.remove(&order.hash)
|
||||||
.expect("hash is in `by_priorty`; all hashes in `by_priority` must be in `by_hash`; qed");
|
.expect("hash is in `by_priorty`; all hashes in `by_priority` must be in `by_hash`; qed");
|
||||||
@ -414,8 +445,32 @@ pub struct AccountDetails {
|
|||||||
/// Transactions with `gas > (gas_limit + gas_limit * Factor(in percents))` are not imported to the queue.
|
/// Transactions with `gas > (gas_limit + gas_limit * Factor(in percents))` are not imported to the queue.
|
||||||
const GAS_LIMIT_HYSTERESIS: usize = 10; // (100/GAS_LIMIT_HYSTERESIS) %
|
const GAS_LIMIT_HYSTERESIS: usize = 10; // (100/GAS_LIMIT_HYSTERESIS) %
|
||||||
|
|
||||||
|
/// Describes the strategy used to prioritize transactions in the queue.
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum PrioritizationStrategy {
|
||||||
|
/// Use only gas price. Disregards the actual computation cost of the transaction.
|
||||||
|
/// i.e. Higher gas price = Higher priority
|
||||||
|
GasPriceOnly,
|
||||||
|
/// Use gas limit and then gas price.
|
||||||
|
/// i.e. Higher gas limit = Lower priority
|
||||||
|
GasAndGasPrice,
|
||||||
|
/// Calculate and use priority based on gas and gas price.
|
||||||
|
/// PRIORITY = GAS_PRICE - GAS/2^15 * MIN_GAS_PRICE
|
||||||
|
///
|
||||||
|
/// Rationale:
|
||||||
|
/// Heavy transactions are paying linear cost (GAS * GAS_PRICE)
|
||||||
|
/// while the computation might be more expensive.
|
||||||
|
///
|
||||||
|
/// i.e.
|
||||||
|
/// 1M gas tx with `gas_price=30*min` has the same priority
|
||||||
|
/// as 32k gas tx with `gas_price=min`
|
||||||
|
GasFactorAndGasPrice,
|
||||||
|
}
|
||||||
|
|
||||||
/// `TransactionQueue` implementation
|
/// `TransactionQueue` implementation
|
||||||
pub struct TransactionQueue {
|
pub struct TransactionQueue {
|
||||||
|
/// Prioritization strategy for this queue
|
||||||
|
strategy: PrioritizationStrategy,
|
||||||
/// Gas Price threshold for transactions that can be imported to this queue (defaults to 0)
|
/// Gas Price threshold for transactions that can be imported to this queue (defaults to 0)
|
||||||
minimal_gas_price: U256,
|
minimal_gas_price: U256,
|
||||||
/// The maximum amount of gas any individual transaction may use.
|
/// The maximum amount of gas any individual transaction may use.
|
||||||
@ -434,18 +489,18 @@ pub struct TransactionQueue {
|
|||||||
|
|
||||||
impl Default for TransactionQueue {
|
impl Default for TransactionQueue {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
TransactionQueue::new()
|
TransactionQueue::new(PrioritizationStrategy::GasPriceOnly)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransactionQueue {
|
impl TransactionQueue {
|
||||||
/// Creates new instance of this Queue
|
/// Creates new instance of this Queue
|
||||||
pub fn new() -> Self {
|
pub fn new(strategy: PrioritizationStrategy) -> Self {
|
||||||
Self::with_limits(1024, !U256::zero(), !U256::zero())
|
Self::with_limits(strategy, 1024, !U256::zero(), !U256::zero())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create new instance of this Queue with specified limits
|
/// Create new instance of this Queue with specified limits
|
||||||
pub fn with_limits(limit: usize, gas_limit: U256, tx_gas_limit: U256) -> Self {
|
pub fn with_limits(strategy: PrioritizationStrategy, limit: usize, gas_limit: U256, tx_gas_limit: U256) -> Self {
|
||||||
let current = TransactionSet {
|
let current = TransactionSet {
|
||||||
by_priority: BTreeSet::new(),
|
by_priority: BTreeSet::new(),
|
||||||
by_address: Table::new(),
|
by_address: Table::new(),
|
||||||
@ -463,6 +518,7 @@ impl TransactionQueue {
|
|||||||
};
|
};
|
||||||
|
|
||||||
TransactionQueue {
|
TransactionQueue {
|
||||||
|
strategy: strategy,
|
||||||
minimal_gas_price: U256::zero(),
|
minimal_gas_price: U256::zero(),
|
||||||
tx_gas_limit: tx_gas_limit,
|
tx_gas_limit: tx_gas_limit,
|
||||||
gas_limit: !U256::zero(),
|
gas_limit: !U256::zero(),
|
||||||
@ -644,7 +700,7 @@ impl TransactionQueue {
|
|||||||
None => vec![],
|
None => vec![],
|
||||||
};
|
};
|
||||||
for k in nonces_from_sender {
|
for k in nonces_from_sender {
|
||||||
let order = self.current.drop(&sender, &k).unwrap();
|
let order = self.current.drop(&sender, &k).expect("transaction known to be in self.current; qed");
|
||||||
self.current.insert(sender, k, order.penalize());
|
self.current.insert(sender, k, order.penalize());
|
||||||
}
|
}
|
||||||
// Same thing for future
|
// Same thing for future
|
||||||
@ -653,7 +709,7 @@ impl TransactionQueue {
|
|||||||
None => vec![],
|
None => vec![],
|
||||||
};
|
};
|
||||||
for k in nonces_from_sender {
|
for k in nonces_from_sender {
|
||||||
let order = self.future.drop(&sender, &k).unwrap();
|
let order = self.future.drop(&sender, &k).expect("transaction known to be in self.future; qed");
|
||||||
self.future.insert(sender, k, order.penalize());
|
self.future.insert(sender, k, order.penalize());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -678,6 +734,8 @@ impl TransactionQueue {
|
|||||||
let nonce = transaction.nonce();
|
let nonce = transaction.nonce();
|
||||||
let current_nonce = fetch_account(&sender).nonce;
|
let current_nonce = fetch_account(&sender).nonce;
|
||||||
|
|
||||||
|
trace!(target: "txqueue", "Removing invalid transaction: {:?}", transaction.hash());
|
||||||
|
|
||||||
// Remove from future
|
// Remove from future
|
||||||
let order = self.future.drop(&sender, &nonce);
|
let order = self.future.drop(&sender, &nonce);
|
||||||
if order.is_some() {
|
if order.is_some() {
|
||||||
@ -841,6 +899,7 @@ impl TransactionQueue {
|
|||||||
return Err(TransactionError::AlreadyImported);
|
return Err(TransactionError::AlreadyImported);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let min_gas_price = (self.minimal_gas_price, self.strategy);
|
||||||
let address = tx.sender();
|
let address = tx.sender();
|
||||||
let nonce = tx.nonce();
|
let nonce = tx.nonce();
|
||||||
let hash = tx.hash();
|
let hash = tx.hash();
|
||||||
@ -878,7 +937,7 @@ impl TransactionQueue {
|
|||||||
if nonce > next_nonce {
|
if nonce > next_nonce {
|
||||||
// We have a gap - put to future.
|
// We have a gap - put to future.
|
||||||
// Insert transaction (or replace old one with lower gas price)
|
// Insert transaction (or replace old one with lower gas price)
|
||||||
try!(check_too_cheap(Self::replace_transaction(tx, state_nonce, &mut self.future, &mut self.by_hash)));
|
try!(check_too_cheap(Self::replace_transaction(tx, state_nonce, min_gas_price, &mut self.future, &mut self.by_hash)));
|
||||||
// Enforce limit in Future
|
// Enforce limit in Future
|
||||||
let removed = self.future.enforce_limit(&mut self.by_hash);
|
let removed = self.future.enforce_limit(&mut self.by_hash);
|
||||||
// Return an error if this transaction was not imported because of limit.
|
// Return an error if this transaction was not imported because of limit.
|
||||||
@ -894,7 +953,7 @@ impl TransactionQueue {
|
|||||||
self.move_matching_future_to_current(address, nonce + U256::one(), state_nonce);
|
self.move_matching_future_to_current(address, nonce + U256::one(), state_nonce);
|
||||||
|
|
||||||
// Replace transaction if any
|
// Replace transaction if any
|
||||||
try!(check_too_cheap(Self::replace_transaction(tx, state_nonce, &mut self.current, &mut self.by_hash)));
|
try!(check_too_cheap(Self::replace_transaction(tx, state_nonce, min_gas_price, &mut self.current, &mut self.by_hash)));
|
||||||
// Keep track of highest nonce stored in current
|
// Keep track of highest nonce stored in current
|
||||||
let new_max = self.last_nonces.get(&address).map_or(nonce, |n| cmp::max(nonce, *n));
|
let new_max = self.last_nonces.get(&address).map_or(nonce, |n| cmp::max(nonce, *n));
|
||||||
self.last_nonces.insert(address, new_max);
|
self.last_nonces.insert(address, new_max);
|
||||||
@ -931,8 +990,8 @@ impl TransactionQueue {
|
|||||||
///
|
///
|
||||||
/// Returns `true` if transaction actually got to the queue (`false` if there was already a transaction with higher
|
/// Returns `true` if transaction actually got to the queue (`false` if there was already a transaction with higher
|
||||||
/// gas_price)
|
/// gas_price)
|
||||||
fn replace_transaction(tx: VerifiedTransaction, base_nonce: U256, set: &mut TransactionSet, by_hash: &mut HashMap<H256, VerifiedTransaction>) -> bool {
|
fn replace_transaction(tx: VerifiedTransaction, base_nonce: U256, min_gas_price: (U256, PrioritizationStrategy), set: &mut TransactionSet, by_hash: &mut HashMap<H256, VerifiedTransaction>) -> bool {
|
||||||
let order = TransactionOrder::for_transaction(&tx, base_nonce);
|
let order = TransactionOrder::for_transaction(&tx, base_nonce, min_gas_price.0, min_gas_price.1);
|
||||||
let hash = tx.hash();
|
let hash = tx.hash();
|
||||||
let address = tx.sender();
|
let address = tx.sender();
|
||||||
let nonce = tx.nonce();
|
let nonce = tx.nonce();
|
||||||
@ -953,12 +1012,14 @@ impl TransactionQueue {
|
|||||||
let old_fee = old.gas_price;
|
let old_fee = old.gas_price;
|
||||||
let new_fee = order.gas_price;
|
let new_fee = order.gas_price;
|
||||||
if old_fee.cmp(&new_fee) == Ordering::Greater {
|
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)
|
// Put back old transaction since it has greater priority (higher gas_price)
|
||||||
set.insert(address, nonce, old);
|
set.insert(address, nonce, old);
|
||||||
// and remove new one
|
// and remove new one
|
||||||
by_hash.remove(&order.hash).expect("The hash has been just inserted and no other line is altering `by_hash`.");
|
by_hash.remove(&order.hash).expect("The hash has been just inserted and no other line is altering `by_hash`.");
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
|
trace!(target: "txqueue", "Replaced transaction: {:?} with transaction with higher gas price: {:?}", old.hash, order.hash);
|
||||||
// Make sure we remove old transaction entirely
|
// 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`.");
|
by_hash.remove(&old.hash).expect("The hash is coming from `future` so it has to be in `by_hash`.");
|
||||||
true
|
true
|
||||||
@ -1010,12 +1071,12 @@ mod test {
|
|||||||
fn default_gas_val() -> U256 { 100_000.into() }
|
fn default_gas_val() -> U256 { 100_000.into() }
|
||||||
fn default_gas_price() -> U256 { 1.into() }
|
fn default_gas_price() -> U256 { 1.into() }
|
||||||
|
|
||||||
fn new_unsigned_tx(nonce: U256, gas_price: U256) -> Transaction {
|
fn new_unsigned_tx(nonce: U256, gas: U256, gas_price: U256) -> Transaction {
|
||||||
Transaction {
|
Transaction {
|
||||||
action: Action::Create,
|
action: Action::Create,
|
||||||
value: U256::from(100),
|
value: U256::from(100),
|
||||||
data: "3331600055".from_hex().unwrap(),
|
data: "3331600055".from_hex().unwrap(),
|
||||||
gas: default_gas_val(),
|
gas: gas,
|
||||||
gas_price: gas_price,
|
gas_price: gas_price,
|
||||||
nonce: nonce
|
nonce: nonce
|
||||||
}
|
}
|
||||||
@ -1023,7 +1084,12 @@ mod test {
|
|||||||
|
|
||||||
fn new_tx(nonce: U256, gas_price: U256) -> SignedTransaction {
|
fn new_tx(nonce: U256, gas_price: U256) -> SignedTransaction {
|
||||||
let keypair = Random.generate().unwrap();
|
let keypair = Random.generate().unwrap();
|
||||||
new_unsigned_tx(nonce, gas_price).sign(keypair.secret())
|
new_unsigned_tx(nonce, default_gas_val(), gas_price).sign(keypair.secret())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_tx_with_gas(gas: U256, gas_price: U256) -> SignedTransaction {
|
||||||
|
let keypair = Random.generate().unwrap();
|
||||||
|
new_unsigned_tx(default_nonce(), gas, gas_price).sign(keypair.secret())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_tx_default() -> SignedTransaction {
|
fn new_tx_default() -> SignedTransaction {
|
||||||
@ -1038,8 +1104,8 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn new_tx_pair(nonce: U256, gas_price: U256, nonce_increment: U256, gas_price_increment: U256) -> (SignedTransaction, SignedTransaction) {
|
fn new_tx_pair(nonce: U256, gas_price: U256, nonce_increment: U256, gas_price_increment: U256) -> (SignedTransaction, SignedTransaction) {
|
||||||
let tx1 = new_unsigned_tx(nonce, gas_price);
|
let tx1 = new_unsigned_tx(nonce, default_gas_val(), gas_price);
|
||||||
let tx2 = new_unsigned_tx(nonce + nonce_increment, gas_price + gas_price_increment);
|
let tx2 = new_unsigned_tx(nonce + nonce_increment, default_gas_val(), gas_price + gas_price_increment);
|
||||||
|
|
||||||
let keypair = Random.generate().unwrap();
|
let keypair = Random.generate().unwrap();
|
||||||
let secret = &keypair.secret();
|
let secret = &keypair.secret();
|
||||||
@ -1049,8 +1115,8 @@ mod test {
|
|||||||
/// Returns two consecutive transactions, both with increased gas price
|
/// Returns two consecutive transactions, both with increased gas price
|
||||||
fn new_tx_pair_with_gas_price_increment(gas_price_increment: U256) -> (SignedTransaction, SignedTransaction) {
|
fn new_tx_pair_with_gas_price_increment(gas_price_increment: U256) -> (SignedTransaction, SignedTransaction) {
|
||||||
let gas = default_gas_price() + gas_price_increment;
|
let gas = default_gas_price() + gas_price_increment;
|
||||||
let tx1 = new_unsigned_tx(default_nonce(), gas);
|
let tx1 = new_unsigned_tx(default_nonce(), default_gas_val(), gas);
|
||||||
let tx2 = new_unsigned_tx(default_nonce() + 1.into(), gas);
|
let tx2 = new_unsigned_tx(default_nonce() + 1.into(), default_gas_val(), gas);
|
||||||
|
|
||||||
let keypair = Random.generate().unwrap();
|
let keypair = Random.generate().unwrap();
|
||||||
let secret = &keypair.secret();
|
let secret = &keypair.secret();
|
||||||
@ -1077,17 +1143,21 @@ mod test {
|
|||||||
assert_eq!(TransactionOrigin::External.cmp(&TransactionOrigin::RetractedBlock), Ordering::Greater);
|
assert_eq!(TransactionOrigin::External.cmp(&TransactionOrigin::RetractedBlock), Ordering::Greater);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transaction_order(tx: &VerifiedTransaction, nonce: U256) -> TransactionOrder {
|
||||||
|
TransactionOrder::for_transaction(tx, nonce, 0.into(), PrioritizationStrategy::GasPriceOnly)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_return_correct_nonces_when_dropped_because_of_limit() {
|
fn should_return_correct_nonces_when_dropped_because_of_limit() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::with_limits(2, !U256::zero(), !U256::zero());
|
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 2, !U256::zero(), !U256::zero());
|
||||||
let (tx1, tx2) = new_tx_pair(123.into(), 1.into(), 1.into(), 0.into());
|
let (tx1, tx2) = new_tx_pair(123.into(), 1.into(), 1.into(), 0.into());
|
||||||
let sender = tx1.sender().unwrap();
|
let sender = tx1.sender().unwrap();
|
||||||
let nonce = tx1.nonce;
|
let nonce = tx1.nonce;
|
||||||
txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||||
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||||
assert_eq!(txq.status().pending, 2);
|
assert_eq!(txq.status().pending, 2);
|
||||||
assert_eq!(txq.last_nonce(&sender), Some(nonce + U256::one()));
|
assert_eq!(txq.last_nonce(&sender), Some(nonce + 1.into()));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let tx = new_tx(123.into(), 1.into());
|
let tx = new_tx(123.into(), 1.into());
|
||||||
@ -1133,9 +1203,9 @@ mod test {
|
|||||||
x
|
x
|
||||||
};
|
};
|
||||||
// Insert both transactions
|
// Insert both transactions
|
||||||
let order1 = TransactionOrder::for_transaction(&tx1, U256::zero());
|
let order1 = transaction_order(&tx1, U256::zero());
|
||||||
set.insert(tx1.sender(), tx1.nonce(), order1.clone());
|
set.insert(tx1.sender(), tx1.nonce(), order1.clone());
|
||||||
let order2 = TransactionOrder::for_transaction(&tx2, U256::zero());
|
let order2 = transaction_order(&tx2, U256::zero());
|
||||||
set.insert(tx2.sender(), tx2.nonce(), order2.clone());
|
set.insert(tx2.sender(), tx2.nonce(), order2.clone());
|
||||||
assert_eq!(set.by_priority.len(), 2);
|
assert_eq!(set.by_priority.len(), 2);
|
||||||
assert_eq!(set.by_address.len(), 2);
|
assert_eq!(set.by_address.len(), 2);
|
||||||
@ -1176,7 +1246,7 @@ mod test {
|
|||||||
x
|
x
|
||||||
};
|
};
|
||||||
// Insert both transactions
|
// Insert both transactions
|
||||||
let order1 = TransactionOrder::for_transaction(&tx1, U256::zero());
|
let order1 = transaction_order(&tx1, U256::zero());
|
||||||
set.insert(tx1.sender(), tx1.nonce(), order1.clone());
|
set.insert(tx1.sender(), tx1.nonce(), order1.clone());
|
||||||
assert_eq!(set.by_priority.len(), 1);
|
assert_eq!(set.by_priority.len(), 1);
|
||||||
assert_eq!(set.by_address.len(), 1);
|
assert_eq!(set.by_address.len(), 1);
|
||||||
@ -1184,7 +1254,7 @@ mod test {
|
|||||||
assert_eq!(*set.by_gas_price.iter().next().unwrap().0, 1.into());
|
assert_eq!(*set.by_gas_price.iter().next().unwrap().0, 1.into());
|
||||||
assert_eq!(set.by_gas_price.iter().next().unwrap().1.len(), 1);
|
assert_eq!(set.by_gas_price.iter().next().unwrap().1.len(), 1);
|
||||||
// Two different orders (imagine nonce changed in the meantime)
|
// Two different orders (imagine nonce changed in the meantime)
|
||||||
let order2 = TransactionOrder::for_transaction(&tx2, U256::one());
|
let order2 = transaction_order(&tx2, U256::one());
|
||||||
set.insert(tx2.sender(), tx2.nonce(), order2.clone());
|
set.insert(tx2.sender(), tx2.nonce(), order2.clone());
|
||||||
assert_eq!(set.by_priority.len(), 1);
|
assert_eq!(set.by_priority.len(), 1);
|
||||||
assert_eq!(set.by_address.len(), 1);
|
assert_eq!(set.by_address.len(), 1);
|
||||||
@ -1213,10 +1283,10 @@ mod test {
|
|||||||
};
|
};
|
||||||
let tx = new_tx_default();
|
let tx = new_tx_default();
|
||||||
let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External).unwrap();
|
let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External).unwrap();
|
||||||
let order1 = TransactionOrder::for_transaction(&tx1, U256::zero());
|
let order1 = TransactionOrder::for_transaction(&tx1, 0.into(), 1.into(), PrioritizationStrategy::GasPriceOnly);
|
||||||
assert!(set.insert(tx1.sender(), tx1.nonce(), order1).is_none());
|
assert!(set.insert(tx1.sender(), tx1.nonce(), order1).is_none());
|
||||||
let tx2 = VerifiedTransaction::new(tx, TransactionOrigin::External).unwrap();
|
let tx2 = VerifiedTransaction::new(tx, TransactionOrigin::External).unwrap();
|
||||||
let order2 = TransactionOrder::for_transaction(&tx2, U256::zero());
|
let order2 = TransactionOrder::for_transaction(&tx2, 0.into(), 1.into(), PrioritizationStrategy::GasPriceOnly);
|
||||||
assert!(set.insert(tx2.sender(), tx2.nonce(), order2).is_some());
|
assert!(set.insert(tx2.sender(), tx2.nonce(), order2).is_some());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1233,7 +1303,7 @@ mod test {
|
|||||||
assert_eq!(set.gas_price_entry_limit(), 0.into());
|
assert_eq!(set.gas_price_entry_limit(), 0.into());
|
||||||
let tx = new_tx_default();
|
let tx = new_tx_default();
|
||||||
let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External).unwrap();
|
let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External).unwrap();
|
||||||
let order1 = TransactionOrder::for_transaction(&tx1, U256::zero());
|
let order1 = TransactionOrder::for_transaction(&tx1, 0.into(), 1.into(), PrioritizationStrategy::GasPriceOnly);
|
||||||
assert!(set.insert(tx1.sender(), tx1.nonce(), order1.clone()).is_none());
|
assert!(set.insert(tx1.sender(), tx1.nonce(), order1.clone()).is_none());
|
||||||
assert_eq!(set.gas_price_entry_limit(), 2.into());
|
assert_eq!(set.gas_price_entry_limit(), 2.into());
|
||||||
}
|
}
|
||||||
@ -1241,7 +1311,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_handle_same_transaction_imported_twice_with_different_state_nonces() {
|
fn should_handle_same_transaction_imported_twice_with_different_state_nonces() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let (tx, tx2) = new_similar_tx_pair();
|
let (tx, tx2) = new_similar_tx_pair();
|
||||||
let prev_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance:
|
let prev_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance:
|
||||||
!U256::zero() };
|
!U256::zero() };
|
||||||
@ -1266,7 +1336,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_move_all_transactions_from_future() {
|
fn should_move_all_transactions_from_future() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let (tx, tx2) = new_tx_pair_default(1.into(), 1.into());
|
let (tx, tx2) = new_tx_pair_default(1.into(), 1.into());
|
||||||
let prev_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance:
|
let prev_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance:
|
||||||
!U256::zero() };
|
!U256::zero() };
|
||||||
@ -1292,7 +1362,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_import_tx() {
|
fn should_import_tx() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let tx = new_tx_default();
|
let tx = new_tx_default();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -1304,10 +1374,77 @@ mod test {
|
|||||||
assert_eq!(stats.pending, 1);
|
assert_eq!(stats.pending, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_order_by_gas() {
|
||||||
|
// given
|
||||||
|
let mut txq = TransactionQueue::new(PrioritizationStrategy::GasAndGasPrice);
|
||||||
|
let tx1 = new_tx_with_gas(50000.into(), 40.into());
|
||||||
|
let tx2 = new_tx_with_gas(40000.into(), 30.into());
|
||||||
|
let tx3 = new_tx_with_gas(30000.into(), 10.into());
|
||||||
|
let tx4 = new_tx_with_gas(50000.into(), 20.into());
|
||||||
|
txq.set_minimal_gas_price(15.into());
|
||||||
|
|
||||||
|
// when
|
||||||
|
let res1 = txq.add(tx1, &default_account_details, TransactionOrigin::External);
|
||||||
|
let res2 = txq.add(tx2, &default_account_details, TransactionOrigin::External);
|
||||||
|
let res3 = txq.add(tx3, &default_account_details, TransactionOrigin::External);
|
||||||
|
let res4 = txq.add(tx4, &default_account_details, TransactionOrigin::External);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(res1.unwrap(), TransactionImportResult::Current);
|
||||||
|
assert_eq!(res2.unwrap(), TransactionImportResult::Current);
|
||||||
|
assert_eq!(unwrap_tx_err(res3), TransactionError::InsufficientGasPrice {
|
||||||
|
minimal: U256::from(15),
|
||||||
|
got: U256::from(10),
|
||||||
|
});
|
||||||
|
assert_eq!(res4.unwrap(), TransactionImportResult::Current);
|
||||||
|
let stats = txq.status();
|
||||||
|
assert_eq!(stats.pending, 3);
|
||||||
|
assert_eq!(txq.top_transactions()[0].gas, 40000.into());
|
||||||
|
assert_eq!(txq.top_transactions()[1].gas, 50000.into());
|
||||||
|
assert_eq!(txq.top_transactions()[2].gas, 50000.into());
|
||||||
|
assert_eq!(txq.top_transactions()[1].gas_price, 40.into());
|
||||||
|
assert_eq!(txq.top_transactions()[2].gas_price, 20.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_order_by_gas_factor() {
|
||||||
|
// given
|
||||||
|
let mut txq = TransactionQueue::new(PrioritizationStrategy::GasFactorAndGasPrice);
|
||||||
|
|
||||||
|
let tx1 = new_tx_with_gas(150_000.into(), 40.into());
|
||||||
|
let tx2 = new_tx_with_gas(40_000.into(), 16.into());
|
||||||
|
let tx3 = new_tx_with_gas(30_000.into(), 15.into());
|
||||||
|
let tx4 = new_tx_with_gas(150_000.into(), 62.into());
|
||||||
|
txq.set_minimal_gas_price(15.into());
|
||||||
|
|
||||||
|
// when
|
||||||
|
let res1 = txq.add(tx1, &default_account_details, TransactionOrigin::External);
|
||||||
|
let res2 = txq.add(tx2, &default_account_details, TransactionOrigin::External);
|
||||||
|
let res3 = txq.add(tx3, &default_account_details, TransactionOrigin::External);
|
||||||
|
let res4 = txq.add(tx4, &default_account_details, TransactionOrigin::External);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(res1.unwrap(), TransactionImportResult::Current);
|
||||||
|
assert_eq!(res2.unwrap(), TransactionImportResult::Current);
|
||||||
|
assert_eq!(res3.unwrap(), TransactionImportResult::Current);
|
||||||
|
assert_eq!(res4.unwrap(), TransactionImportResult::Current);
|
||||||
|
let stats = txq.status();
|
||||||
|
assert_eq!(stats.pending, 4);
|
||||||
|
assert_eq!(txq.top_transactions()[0].gas, 30_000.into());
|
||||||
|
assert_eq!(txq.top_transactions()[1].gas, 150_000.into());
|
||||||
|
assert_eq!(txq.top_transactions()[2].gas, 40_000.into());
|
||||||
|
assert_eq!(txq.top_transactions()[3].gas, 150_000.into());
|
||||||
|
assert_eq!(txq.top_transactions()[0].gas_price, 15.into());
|
||||||
|
assert_eq!(txq.top_transactions()[1].gas_price, 62.into());
|
||||||
|
assert_eq!(txq.top_transactions()[2].gas_price, 16.into());
|
||||||
|
assert_eq!(txq.top_transactions()[3].gas_price, 40.into());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gas_limit_should_never_overflow() {
|
fn gas_limit_should_never_overflow() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
txq.set_gas_limit(U256::zero());
|
txq.set_gas_limit(U256::zero());
|
||||||
assert_eq!(txq.gas_limit, U256::zero());
|
assert_eq!(txq.gas_limit, U256::zero());
|
||||||
|
|
||||||
@ -1321,7 +1458,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_not_import_transaction_above_gas_limit() {
|
fn should_not_import_transaction_above_gas_limit() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let tx = new_tx_default();
|
let tx = new_tx_default();
|
||||||
let gas = tx.gas;
|
let gas = tx.gas;
|
||||||
let limit = gas / U256::from(2);
|
let limit = gas / U256::from(2);
|
||||||
@ -1344,7 +1481,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_drop_transactions_from_senders_without_balance() {
|
fn should_drop_transactions_from_senders_without_balance() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let tx = new_tx_default();
|
let tx = new_tx_default();
|
||||||
let account = |a: &Address| AccountDetails {
|
let account = |a: &Address| AccountDetails {
|
||||||
nonce: default_account_details(a).nonce,
|
nonce: default_account_details(a).nonce,
|
||||||
@ -1367,7 +1504,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_not_import_transaction_below_min_gas_price_threshold_if_external() {
|
fn should_not_import_transaction_below_min_gas_price_threshold_if_external() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let tx = new_tx_default();
|
let tx = new_tx_default();
|
||||||
txq.set_minimal_gas_price(tx.gas_price + U256::one());
|
txq.set_minimal_gas_price(tx.gas_price + U256::one());
|
||||||
|
|
||||||
@ -1387,7 +1524,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_import_transaction_below_min_gas_price_threshold_if_local() {
|
fn should_import_transaction_below_min_gas_price_threshold_if_local() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let tx = new_tx_default();
|
let tx = new_tx_default();
|
||||||
txq.set_minimal_gas_price(tx.gas_price + U256::one());
|
txq.set_minimal_gas_price(tx.gas_price + U256::one());
|
||||||
|
|
||||||
@ -1406,8 +1543,8 @@ mod test {
|
|||||||
use rlp::{self, RlpStream, Stream};
|
use rlp::{self, RlpStream, Stream};
|
||||||
|
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let tx = new_unsigned_tx(123.into(), 1.into());
|
let tx = new_unsigned_tx(123.into(), 100.into(), 1.into());
|
||||||
let stx = {
|
let stx = {
|
||||||
let mut s = RlpStream::new_list(9);
|
let mut s = RlpStream::new_list(9);
|
||||||
s.append(&tx.nonce);
|
s.append(&tx.nonce);
|
||||||
@ -1431,7 +1568,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_import_txs_from_same_sender() {
|
fn should_import_txs_from_same_sender() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
|
|
||||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
|
|
||||||
@ -1449,7 +1586,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_prioritize_local_transactions_within_same_nonce_height() {
|
fn should_prioritize_local_transactions_within_same_nonce_height() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let tx = new_tx_default();
|
let tx = new_tx_default();
|
||||||
// the second one has same nonce but higher `gas_price`
|
// the second one has same nonce but higher `gas_price`
|
||||||
let (_, tx2) = new_similar_tx_pair();
|
let (_, tx2) = new_similar_tx_pair();
|
||||||
@ -1470,7 +1607,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_prioritize_reimported_transactions_within_same_nonce_height() {
|
fn should_prioritize_reimported_transactions_within_same_nonce_height() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let tx = new_tx_default();
|
let tx = new_tx_default();
|
||||||
// the second one has same nonce but higher `gas_price`
|
// the second one has same nonce but higher `gas_price`
|
||||||
let (_, tx2) = new_similar_tx_pair();
|
let (_, tx2) = new_similar_tx_pair();
|
||||||
@ -1491,7 +1628,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_not_prioritize_local_transactions_with_different_nonce_height() {
|
fn should_not_prioritize_local_transactions_with_different_nonce_height() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -1509,7 +1646,7 @@ mod test {
|
|||||||
fn should_penalize_transactions_from_sender_in_future() {
|
fn should_penalize_transactions_from_sender_in_future() {
|
||||||
// given
|
// given
|
||||||
let prev_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance: !U256::zero() };
|
let prev_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance: !U256::zero() };
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
// txa, txb - slightly bigger gas price to have consistent ordering
|
// txa, txb - slightly bigger gas price to have consistent ordering
|
||||||
let (txa, txb) = new_tx_pair_default(1.into(), 0.into());
|
let (txa, txb) = new_tx_pair_default(1.into(), 0.into());
|
||||||
let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into());
|
let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into());
|
||||||
@ -1538,7 +1675,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_penalize_transactions_from_sender() {
|
fn should_penalize_transactions_from_sender() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
// txa, txb - slightly bigger gas price to have consistent ordering
|
// txa, txb - slightly bigger gas price to have consistent ordering
|
||||||
let (txa, txb) = new_tx_pair_default(1.into(), 0.into());
|
let (txa, txb) = new_tx_pair_default(1.into(), 0.into());
|
||||||
let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into());
|
let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into());
|
||||||
@ -1571,7 +1708,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_return_pending_hashes() {
|
fn should_return_pending_hashes() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
|
|
||||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
|
|
||||||
@ -1589,7 +1726,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_put_transaction_to_futures_if_gap_detected() {
|
fn should_put_transaction_to_futures_if_gap_detected() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
|
|
||||||
let (tx, tx2) = new_tx_pair_default(2.into(), 0.into());
|
let (tx, tx2) = new_tx_pair_default(2.into(), 0.into());
|
||||||
|
|
||||||
@ -1615,7 +1752,7 @@ mod test {
|
|||||||
!U256::zero() };
|
!U256::zero() };
|
||||||
let next2_nonce = default_nonce() + U256::from(3);
|
let next2_nonce = default_nonce() + U256::from(3);
|
||||||
|
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
|
|
||||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
txq.add(tx.clone(), &prev_nonce, TransactionOrigin::External).unwrap();
|
txq.add(tx.clone(), &prev_nonce, TransactionOrigin::External).unwrap();
|
||||||
@ -1634,12 +1771,12 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_move_transactions_if_gap_filled() {
|
fn should_move_transactions_if_gap_filled() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let kp = Random.generate().unwrap();
|
let kp = Random.generate().unwrap();
|
||||||
let secret = kp.secret();
|
let secret = kp.secret();
|
||||||
let tx = new_unsigned_tx(123.into(), 1.into()).sign(secret);
|
let tx = new_unsigned_tx(123.into(), default_gas_val(), 1.into()).sign(secret);
|
||||||
let tx1 = new_unsigned_tx(124.into(), 1.into()).sign(secret);
|
let tx1 = new_unsigned_tx(124.into(), default_gas_val(), 1.into()).sign(secret);
|
||||||
let tx2 = new_unsigned_tx(125.into(), 1.into()).sign(secret);
|
let tx2 = new_unsigned_tx(125.into(), default_gas_val(), 1.into()).sign(secret);
|
||||||
|
|
||||||
txq.add(tx, &default_account_details, TransactionOrigin::External).unwrap();
|
txq.add(tx, &default_account_details, TransactionOrigin::External).unwrap();
|
||||||
assert_eq!(txq.status().pending, 1);
|
assert_eq!(txq.status().pending, 1);
|
||||||
@ -1661,7 +1798,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_remove_transaction() {
|
fn should_remove_transaction() {
|
||||||
// given
|
// given
|
||||||
let mut txq2 = TransactionQueue::new();
|
let mut txq2 = TransactionQueue::default();
|
||||||
let (tx, tx2) = new_tx_pair_default(3.into(), 0.into());
|
let (tx, tx2) = new_tx_pair_default(3.into(), 0.into());
|
||||||
txq2.add(tx.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
txq2.add(tx.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||||
txq2.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
txq2.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||||
@ -1682,7 +1819,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_move_transactions_to_future_if_gap_introduced() {
|
fn should_move_transactions_to_future_if_gap_introduced() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
let tx3 = new_tx_default();
|
let tx3 = new_tx_default();
|
||||||
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||||
@ -1703,7 +1840,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_clear_queue() {
|
fn should_clear_queue() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
|
|
||||||
// add
|
// add
|
||||||
@ -1723,7 +1860,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_drop_old_transactions_when_hitting_the_limit() {
|
fn should_drop_old_transactions_when_hitting_the_limit() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::with_limits(1, !U256::zero(), !U256::zero());
|
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 1, !U256::zero(), !U256::zero());
|
||||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
let sender = tx.sender().unwrap();
|
let sender = tx.sender().unwrap();
|
||||||
let nonce = tx.nonce;
|
let nonce = tx.nonce;
|
||||||
@ -1744,7 +1881,7 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_limit_future_transactions() {
|
fn should_limit_future_transactions() {
|
||||||
let mut txq = TransactionQueue::with_limits(1, !U256::zero(), !U256::zero());
|
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 1, !U256::zero(), !U256::zero());
|
||||||
txq.current.set_limit(10);
|
txq.current.set_limit(10);
|
||||||
let (tx1, tx2) = new_tx_pair_default(4.into(), 1.into());
|
let (tx1, tx2) = new_tx_pair_default(4.into(), 1.into());
|
||||||
let (tx3, tx4) = new_tx_pair_default(4.into(), 2.into());
|
let (tx3, tx4) = new_tx_pair_default(4.into(), 2.into());
|
||||||
@ -1763,23 +1900,28 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_limit_by_gas() {
|
fn should_limit_by_gas() {
|
||||||
let mut txq = TransactionQueue::with_limits(100, default_gas_val() * U256::from(2), !U256::zero());
|
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 100, default_gas_val() * U256::from(2), !U256::zero());
|
||||||
let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1));
|
let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1));
|
||||||
let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2));
|
let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2));
|
||||||
txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).ok();
|
txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||||
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).ok();
|
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||||
txq.add(tx3.clone(), &default_account_details, TransactionOrigin::External).ok();
|
txq.add(tx3.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||||
txq.add(tx4.clone(), &default_account_details, TransactionOrigin::External).ok();
|
// limited by gas
|
||||||
|
txq.add(tx4.clone(), &default_account_details, TransactionOrigin::External).unwrap_err();
|
||||||
assert_eq!(txq.status().pending, 2);
|
assert_eq!(txq.status().pending, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_keep_own_transactions_above_gas_limit() {
|
fn should_keep_own_transactions_above_gas_limit() {
|
||||||
let mut txq = TransactionQueue::with_limits(100, default_gas_val() * U256::from(2), !U256::zero());
|
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 100, default_gas_val() * U256::from(2), !U256::zero());
|
||||||
let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1));
|
let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1));
|
||||||
let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2));
|
let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2));
|
||||||
|
let (tx5, tx6) = new_tx_pair_default(U256::from(1), U256::from(2));
|
||||||
txq.add(tx1.clone(), &default_account_details, TransactionOrigin::Local).unwrap();
|
txq.add(tx1.clone(), &default_account_details, TransactionOrigin::Local).unwrap();
|
||||||
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::Local).unwrap();
|
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::Local).unwrap();
|
||||||
|
txq.add(tx5.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||||
|
// Not accepted because of limit
|
||||||
|
txq.add(tx6.clone(), &default_account_details, TransactionOrigin::External).unwrap_err();
|
||||||
txq.add(tx3.clone(), &default_account_details, TransactionOrigin::Local).unwrap();
|
txq.add(tx3.clone(), &default_account_details, TransactionOrigin::Local).unwrap();
|
||||||
txq.add(tx4.clone(), &default_account_details, TransactionOrigin::Local).unwrap();
|
txq.add(tx4.clone(), &default_account_details, TransactionOrigin::Local).unwrap();
|
||||||
assert_eq!(txq.status().pending, 4);
|
assert_eq!(txq.status().pending, 4);
|
||||||
@ -1787,10 +1929,10 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_drop_transactions_with_old_nonces() {
|
fn should_drop_transactions_with_old_nonces() {
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let tx = new_tx_default();
|
let tx = new_tx_default();
|
||||||
let last_nonce = tx.nonce + U256::one();
|
let last_nonce = tx.nonce + U256::one();
|
||||||
let fetch_last_nonce = |_a: &Address| AccountDetails{ nonce: last_nonce, balance: !U256::zero() };
|
let fetch_last_nonce = |_a: &Address| AccountDetails { nonce: last_nonce, balance: !U256::zero() };
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let res = txq.add(tx, &fetch_last_nonce, TransactionOrigin::External);
|
let res = txq.add(tx, &fetch_last_nonce, TransactionOrigin::External);
|
||||||
@ -1807,7 +1949,7 @@ mod test {
|
|||||||
// given
|
// given
|
||||||
let nonce = |a: &Address| AccountDetails { nonce: default_account_details(a).nonce + U256::one(),
|
let nonce = |a: &Address| AccountDetails { nonce: default_account_details(a).nonce + U256::one(),
|
||||||
balance: !U256::zero() };
|
balance: !U256::zero() };
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let (_tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (_tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||||
assert_eq!(txq.status().future, 1);
|
assert_eq!(txq.status().future, 1);
|
||||||
@ -1826,7 +1968,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_accept_same_transaction_twice_if_removed() {
|
fn should_accept_same_transaction_twice_if_removed() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||||
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||||
@ -1847,7 +1989,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_not_move_to_future_if_state_nonce_is_higher() {
|
fn should_not_move_to_future_if_state_nonce_is_higher() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
let tx3 = new_tx_default();
|
let tx3 = new_tx_default();
|
||||||
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
||||||
@ -1870,9 +2012,9 @@ mod test {
|
|||||||
fn should_replace_same_transaction_when_has_higher_fee() {
|
fn should_replace_same_transaction_when_has_higher_fee() {
|
||||||
init_log();
|
init_log();
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let keypair = Random.generate().unwrap();
|
let keypair = Random.generate().unwrap();
|
||||||
let tx = new_unsigned_tx(123.into(), 1.into()).sign(keypair.secret());
|
let tx = new_unsigned_tx(123.into(), default_gas_val(), 1.into()).sign(keypair.secret());
|
||||||
let tx2 = {
|
let tx2 = {
|
||||||
let mut tx2 = (*tx).clone();
|
let mut tx2 = (*tx).clone();
|
||||||
tx2.gas_price = U256::from(200);
|
tx2.gas_price = U256::from(200);
|
||||||
@ -1893,9 +2035,9 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_replace_same_transaction_when_importing_to_futures() {
|
fn should_replace_same_transaction_when_importing_to_futures() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let keypair = Random.generate().unwrap();
|
let keypair = Random.generate().unwrap();
|
||||||
let tx0 = new_unsigned_tx(123.into(), 1.into()).sign(keypair.secret());
|
let tx0 = new_unsigned_tx(123.into(), default_gas_val(), 1.into()).sign(keypair.secret());
|
||||||
let tx1 = {
|
let tx1 = {
|
||||||
let mut tx1 = (*tx0).clone();
|
let mut tx1 = (*tx0).clone();
|
||||||
tx1.nonce = U256::from(124);
|
tx1.nonce = U256::from(124);
|
||||||
@ -1927,7 +2069,7 @@ mod test {
|
|||||||
!U256::zero() };
|
!U256::zero() };
|
||||||
let next_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce + U256::one(), balance:
|
let next_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce + U256::one(), balance:
|
||||||
!U256::zero() };
|
!U256::zero() };
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
txq.add(tx1.clone(), &previous_nonce, TransactionOrigin::External).unwrap();
|
txq.add(tx1.clone(), &previous_nonce, TransactionOrigin::External).unwrap();
|
||||||
txq.add(tx2, &previous_nonce, TransactionOrigin::External).unwrap();
|
txq.add(tx2, &previous_nonce, TransactionOrigin::External).unwrap();
|
||||||
@ -1945,7 +2087,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_return_none_when_transaction_from_given_address_does_not_exist() {
|
fn should_return_none_when_transaction_from_given_address_does_not_exist() {
|
||||||
// given
|
// given
|
||||||
let txq = TransactionQueue::new();
|
let txq = TransactionQueue::default();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(txq.last_nonce(&Address::default()), None);
|
assert_eq!(txq.last_nonce(&Address::default()), None);
|
||||||
@ -1954,7 +2096,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_return_correct_nonce_when_transactions_from_given_address_exist() {
|
fn should_return_correct_nonce_when_transactions_from_given_address_exist() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let tx = new_tx_default();
|
let tx = new_tx_default();
|
||||||
let from = tx.sender().unwrap();
|
let from = tx.sender().unwrap();
|
||||||
let nonce = tx.nonce;
|
let nonce = tx.nonce;
|
||||||
@ -1970,7 +2112,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_remove_old_transaction_even_if_newer_transaction_was_not_known() {
|
fn should_remove_old_transaction_even_if_newer_transaction_was_not_known() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
let (nonce1, nonce2) = (tx1.nonce, tx2.nonce);
|
let (nonce1, nonce2) = (tx1.nonce, tx2.nonce);
|
||||||
let details1 = |_a: &Address| AccountDetails { nonce: nonce1, balance: !U256::zero() };
|
let details1 = |_a: &Address| AccountDetails { nonce: nonce1, balance: !U256::zero() };
|
||||||
@ -1988,7 +2130,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_return_valid_last_nonce_after_remove_all() {
|
fn should_return_valid_last_nonce_after_remove_all() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let (tx1, tx2) = new_tx_pair_default(4.into(), 0.into());
|
let (tx1, tx2) = new_tx_pair_default(4.into(), 0.into());
|
||||||
let sender = tx1.sender().unwrap();
|
let sender = tx1.sender().unwrap();
|
||||||
let (nonce1, nonce2) = (tx1.nonce, tx2.nonce);
|
let (nonce1, nonce2) = (tx1.nonce, tx2.nonce);
|
||||||
@ -2012,7 +2154,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_return_true_if_there_is_local_transaction_pending() {
|
fn should_return_true_if_there_is_local_transaction_pending() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
assert_eq!(txq.has_local_pending_transactions(), false);
|
assert_eq!(txq.has_local_pending_transactions(), false);
|
||||||
|
|
||||||
@ -2028,7 +2170,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_keep_right_order_in_future() {
|
fn should_keep_right_order_in_future() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::with_limits(1, !U256::zero(), !U256::zero());
|
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 1, !U256::zero(), !U256::zero());
|
||||||
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
let prev_nonce = |a: &Address| AccountDetails { nonce: default_account_details(a).nonce - U256::one(), balance:
|
let prev_nonce = |a: &Address| AccountDetails { nonce: default_account_details(a).nonce - U256::one(), balance:
|
||||||
default_account_details(a).balance };
|
default_account_details(a).balance };
|
||||||
@ -2045,15 +2187,16 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_return_correct_last_nonce() {
|
fn should_return_correct_last_nonce() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::default();
|
||||||
let (tx1, tx2, tx2_2, tx3) = {
|
let (tx1, tx2, tx2_2, tx3) = {
|
||||||
let keypair = Random.generate().unwrap();
|
let keypair = Random.generate().unwrap();
|
||||||
let secret = &keypair.secret();
|
let secret = &keypair.secret();
|
||||||
let nonce = 123.into();
|
let nonce = 123.into();
|
||||||
let tx = new_unsigned_tx(nonce, 1.into());
|
let gas = default_gas_val();
|
||||||
let tx2 = new_unsigned_tx(nonce + 1.into(), 1.into());
|
let tx = new_unsigned_tx(nonce, gas, 1.into());
|
||||||
let tx2_2 = new_unsigned_tx(nonce + 1.into(), 5.into());
|
let tx2 = new_unsigned_tx(nonce + 1.into(), gas, 1.into());
|
||||||
let tx3 = new_unsigned_tx(nonce + 2.into(), 1.into());
|
let tx2_2 = new_unsigned_tx(nonce + 1.into(), gas, 5.into());
|
||||||
|
let tx3 = new_unsigned_tx(nonce + 2.into(), gas, 1.into());
|
||||||
|
|
||||||
|
|
||||||
(tx.sign(secret), tx2.sign(secret), tx2_2.sign(secret), tx3.sign(secret))
|
(tx.sign(secret), tx2.sign(secret), tx2_2.sign(secret), tx3.sign(secret))
|
||||||
|
@ -89,7 +89,7 @@ impl ClientService {
|
|||||||
db_config.set_cache(::db::COL_STATE, size);
|
db_config.set_cache(::db::COL_STATE, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
db_config.compaction = config.db_compaction.compaction_profile();
|
db_config.compaction = config.db_compaction.compaction_profile(&client_path);
|
||||||
db_config.wal = config.db_wal;
|
db_config.wal = config.db_wal;
|
||||||
|
|
||||||
let pruning = config.pruning;
|
let pruning = config.pruning;
|
||||||
|
@ -19,12 +19,20 @@
|
|||||||
use account_db::{AccountDB, AccountDBMut};
|
use account_db::{AccountDB, AccountDBMut};
|
||||||
use snapshot::Error;
|
use snapshot::Error;
|
||||||
|
|
||||||
use util::{U256, FixedHash, H256, Bytes, HashDB, SHA3_EMPTY};
|
use util::{U256, FixedHash, H256, Bytes, HashDB, SHA3_EMPTY, SHA3_NULL_RLP};
|
||||||
use util::trie::{TrieDB, Trie};
|
use util::trie::{TrieDB, Trie};
|
||||||
use rlp::{Rlp, RlpStream, Stream, UntrustedRlp, View};
|
use rlp::{Rlp, RlpStream, Stream, UntrustedRlp, View};
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
|
// An empty account -- these are replaced with RLP null data for a space optimization.
|
||||||
|
const ACC_EMPTY: Account = Account {
|
||||||
|
nonce: U256([0, 0, 0, 0]),
|
||||||
|
balance: U256([0, 0, 0, 0]),
|
||||||
|
storage_root: SHA3_NULL_RLP,
|
||||||
|
code_hash: SHA3_EMPTY,
|
||||||
|
};
|
||||||
|
|
||||||
// whether an encoded account has code and how it is referred to.
|
// whether an encoded account has code and how it is referred to.
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
enum CodeState {
|
enum CodeState {
|
||||||
@ -88,6 +96,10 @@ impl Account {
|
|||||||
// walk the account's storage trie, returning an RLP item containing the
|
// walk the account's storage trie, returning an RLP item containing the
|
||||||
// account properties and the storage.
|
// account properties and the storage.
|
||||||
pub fn to_fat_rlp(&self, acct_db: &AccountDB, used_code: &mut HashSet<H256>) -> Result<Bytes, Error> {
|
pub fn to_fat_rlp(&self, acct_db: &AccountDB, used_code: &mut HashSet<H256>) -> Result<Bytes, Error> {
|
||||||
|
if self == &ACC_EMPTY {
|
||||||
|
return Ok(::rlp::NULL_RLP.to_vec());
|
||||||
|
}
|
||||||
|
|
||||||
let db = try!(TrieDB::new(acct_db, &self.storage_root));
|
let db = try!(TrieDB::new(acct_db, &self.storage_root));
|
||||||
|
|
||||||
let mut pairs = Vec::new();
|
let mut pairs = Vec::new();
|
||||||
@ -142,6 +154,11 @@ impl Account {
|
|||||||
) -> Result<(Self, Option<Bytes>), Error> {
|
) -> Result<(Self, Option<Bytes>), Error> {
|
||||||
use util::{TrieDBMut, TrieMut};
|
use util::{TrieDBMut, TrieMut};
|
||||||
|
|
||||||
|
// check for special case of empty account.
|
||||||
|
if rlp.is_empty() {
|
||||||
|
return Ok((ACC_EMPTY, None));
|
||||||
|
}
|
||||||
|
|
||||||
let nonce = try!(rlp.val_at(0));
|
let nonce = try!(rlp.val_at(0));
|
||||||
let balance = try!(rlp.val_at(1));
|
let balance = try!(rlp.val_at(1));
|
||||||
let code_state: CodeState = {
|
let code_state: CodeState = {
|
||||||
@ -214,7 +231,7 @@ mod tests {
|
|||||||
|
|
||||||
use std::collections::{HashSet, HashMap};
|
use std::collections::{HashSet, HashMap};
|
||||||
|
|
||||||
use super::Account;
|
use super::{ACC_EMPTY, Account};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn encoding_basic() {
|
fn encoding_basic() {
|
||||||
@ -310,4 +327,14 @@ mod tests {
|
|||||||
assert_eq!(maybe_code, Some(b"this is definitely code".to_vec()));
|
assert_eq!(maybe_code, Some(b"this is definitely code".to_vec()));
|
||||||
assert_eq!(acc, account1);
|
assert_eq!(acc, account1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn encoding_empty_acc() {
|
||||||
|
let mut db = get_temp_state_db();
|
||||||
|
let mut used_code = HashSet::new();
|
||||||
|
let code_map = HashMap::new();
|
||||||
|
|
||||||
|
assert_eq!(ACC_EMPTY.to_fat_rlp(&AccountDB::new(db.as_hashdb(), &Address::default()), &mut used_code).unwrap(), ::rlp::NULL_RLP.to_vec());
|
||||||
|
assert_eq!(Account::from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &Address::default()), UntrustedRlp::new(&::rlp::NULL_RLP), &code_map).unwrap(), (ACC_EMPTY, None));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ use engines::Engine;
|
|||||||
use ids::BlockID;
|
use ids::BlockID;
|
||||||
use views::BlockView;
|
use views::BlockView;
|
||||||
|
|
||||||
use util::{Bytes, Hashable, HashDB, snappy};
|
use util::{Bytes, Hashable, HashDB, snappy, U256, Uint};
|
||||||
use util::memorydb::MemoryDB;
|
use util::memorydb::MemoryDB;
|
||||||
use util::Mutex;
|
use util::Mutex;
|
||||||
use util::hash::{FixedHash, H256};
|
use util::hash::{FixedHash, H256};
|
||||||
@ -44,6 +44,7 @@ use self::block::AbridgedBlock;
|
|||||||
use self::io::SnapshotWriter;
|
use self::io::SnapshotWriter;
|
||||||
|
|
||||||
use super::state_db::StateDB;
|
use super::state_db::StateDB;
|
||||||
|
use super::state::Account as StateAccount;
|
||||||
|
|
||||||
use crossbeam::{scope, ScopedJoinHandle};
|
use crossbeam::{scope, ScopedJoinHandle};
|
||||||
use rand::{Rng, OsRng};
|
use rand::{Rng, OsRng};
|
||||||
@ -409,6 +410,7 @@ impl StateRebuilder {
|
|||||||
/// Feed an uncompressed state chunk into the rebuilder.
|
/// Feed an uncompressed state chunk into the rebuilder.
|
||||||
pub fn feed(&mut self, chunk: &[u8]) -> Result<(), ::error::Error> {
|
pub fn feed(&mut self, chunk: &[u8]) -> Result<(), ::error::Error> {
|
||||||
let rlp = UntrustedRlp::new(chunk);
|
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 account_fat_rlps: Vec<_> = rlp.iter().map(|r| r.as_raw()).collect();
|
||||||
let mut pairs = Vec::with_capacity(rlp.item_count());
|
let mut pairs = Vec::with_capacity(rlp.item_count());
|
||||||
|
|
||||||
@ -476,7 +478,9 @@ impl StateRebuilder {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (hash, thin_rlp) in pairs {
|
for (hash, thin_rlp) in pairs {
|
||||||
bloom.set(&*hash);
|
if &thin_rlp[..] != &empty_rlp[..] {
|
||||||
|
bloom.set(&*hash);
|
||||||
|
}
|
||||||
try!(account_trie.insert(&hash, &thin_rlp));
|
try!(account_trie.insert(&hash, &thin_rlp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -564,6 +568,7 @@ const POW_VERIFY_RATE: f32 = 0.02;
|
|||||||
/// After all chunks have been submitted, we "glue" the chunks together.
|
/// After all chunks have been submitted, we "glue" the chunks together.
|
||||||
pub struct BlockRebuilder {
|
pub struct BlockRebuilder {
|
||||||
chain: BlockChain,
|
chain: BlockChain,
|
||||||
|
db: Arc<Database>,
|
||||||
rng: OsRng,
|
rng: OsRng,
|
||||||
disconnected: Vec<(u64, H256)>,
|
disconnected: Vec<(u64, H256)>,
|
||||||
best_number: u64,
|
best_number: u64,
|
||||||
@ -571,9 +576,10 @@ pub struct BlockRebuilder {
|
|||||||
|
|
||||||
impl BlockRebuilder {
|
impl BlockRebuilder {
|
||||||
/// Create a new BlockRebuilder.
|
/// Create a new BlockRebuilder.
|
||||||
pub fn new(chain: BlockChain, best_number: u64) -> Result<Self, ::error::Error> {
|
pub fn new(chain: BlockChain, db: Arc<Database>, best_number: u64) -> Result<Self, ::error::Error> {
|
||||||
Ok(BlockRebuilder {
|
Ok(BlockRebuilder {
|
||||||
chain: chain,
|
chain: chain,
|
||||||
|
db: db,
|
||||||
rng: try!(OsRng::new()),
|
rng: try!(OsRng::new()),
|
||||||
disconnected: Vec::new(),
|
disconnected: Vec::new(),
|
||||||
best_number: best_number,
|
best_number: best_number,
|
||||||
@ -616,15 +622,17 @@ impl BlockRebuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let is_best = cur_number == self.best_number;
|
let is_best = cur_number == self.best_number;
|
||||||
|
let mut batch = self.db.transaction();
|
||||||
|
|
||||||
// special-case the first block in each chunk.
|
// special-case the first block in each chunk.
|
||||||
if idx == 3 {
|
if idx == 3 {
|
||||||
if self.chain.insert_snapshot_block(&block_bytes, receipts, Some(parent_total_difficulty), is_best) {
|
if self.chain.insert_unordered_block(&mut batch, &block_bytes, receipts, Some(parent_total_difficulty), is_best, false) {
|
||||||
self.disconnected.push((cur_number, block.header.hash()));
|
self.disconnected.push((cur_number, block.header.hash()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.chain.insert_snapshot_block(&block_bytes, receipts, None, is_best);
|
self.chain.insert_unordered_block(&mut batch, &block_bytes, receipts, None, is_best, false);
|
||||||
}
|
}
|
||||||
|
self.db.write(batch).expect("Error writing to the DB");
|
||||||
self.chain.commit();
|
self.chain.commit();
|
||||||
|
|
||||||
parent_hash = BlockView::new(&block_bytes).hash();
|
parent_hash = BlockView::new(&block_bytes).hash();
|
||||||
|
@ -98,7 +98,7 @@ impl Restoration {
|
|||||||
.map_err(UtilError::SimpleString)));
|
.map_err(UtilError::SimpleString)));
|
||||||
|
|
||||||
let chain = BlockChain::new(Default::default(), params.genesis, raw_db.clone());
|
let chain = BlockChain::new(Default::default(), params.genesis, raw_db.clone());
|
||||||
let blocks = try!(BlockRebuilder::new(chain, manifest.block_number));
|
let blocks = try!(BlockRebuilder::new(chain, raw_db.clone(), manifest.block_number));
|
||||||
|
|
||||||
let root = manifest.state_root.clone();
|
let root = manifest.state_root.clone();
|
||||||
Ok(Restoration {
|
Ok(Restoration {
|
||||||
@ -346,7 +346,7 @@ impl Service {
|
|||||||
|
|
||||||
self.taking_snapshot.store(false, Ordering::SeqCst);
|
self.taking_snapshot.store(false, Ordering::SeqCst);
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
if client.chain_info().best_block_number >= num + ::client::HISTORY {
|
if client.chain_info().best_block_number >= num + client.pruning_history() {
|
||||||
// "Cancelled" is mincing words a bit -- what really happened
|
// "Cancelled" is mincing words a bit -- what really happened
|
||||||
// is that the state we were snapshotting got pruned out
|
// is that the state we were snapshotting got pruned out
|
||||||
// before we could finish.
|
// before we could finish.
|
||||||
@ -415,9 +415,14 @@ impl Service {
|
|||||||
guard: Guard::new(rest_dir),
|
guard: Guard::new(rest_dir),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let state_chunks = params.manifest.state_hashes.len();
|
||||||
|
let block_chunks = params.manifest.block_hashes.len();
|
||||||
|
|
||||||
*res = Some(try!(Restoration::new(params)));
|
*res = Some(try!(Restoration::new(params)));
|
||||||
|
|
||||||
*self.status.lock() = RestorationStatus::Ongoing {
|
*self.status.lock() = RestorationStatus::Ongoing {
|
||||||
|
state_chunks: state_chunks as u32,
|
||||||
|
block_chunks: block_chunks as u32,
|
||||||
state_chunks_done: self.state_chunks.load(Ordering::SeqCst) as u32,
|
state_chunks_done: self.state_chunks.load(Ordering::SeqCst) as u32,
|
||||||
block_chunks_done: self.block_chunks.load(Ordering::SeqCst) as u32,
|
block_chunks_done: self.block_chunks.load(Ordering::SeqCst) as u32,
|
||||||
};
|
};
|
||||||
@ -535,7 +540,7 @@ impl SnapshotService for Service {
|
|||||||
|
|
||||||
fn status(&self) -> RestorationStatus {
|
fn status(&self) -> RestorationStatus {
|
||||||
let mut cur_status = self.status.lock();
|
let mut cur_status = self.status.lock();
|
||||||
if let RestorationStatus::Ongoing { ref mut state_chunks_done, ref mut block_chunks_done } = *cur_status {
|
if let RestorationStatus::Ongoing { ref mut state_chunks_done, ref mut block_chunks_done, .. } = *cur_status {
|
||||||
*state_chunks_done = self.state_chunks.load(Ordering::SeqCst) as u32;
|
*state_chunks_done = self.state_chunks.load(Ordering::SeqCst) as u32;
|
||||||
*block_chunks_done = self.block_chunks.load(Ordering::SeqCst) as u32;
|
*block_chunks_done = self.block_chunks.load(Ordering::SeqCst) as u32;
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ fn chunk_and_restore(amount: u64) {
|
|||||||
// restore it.
|
// restore it.
|
||||||
let new_db = Arc::new(Database::open(&db_cfg, new_path.as_str()).unwrap());
|
let new_db = Arc::new(Database::open(&db_cfg, new_path.as_str()).unwrap());
|
||||||
let new_chain = BlockChain::new(Default::default(), &genesis, new_db.clone());
|
let new_chain = BlockChain::new(Default::default(), &genesis, new_db.clone());
|
||||||
let mut rebuilder = BlockRebuilder::new(new_chain, amount).unwrap();
|
let mut rebuilder = BlockRebuilder::new(new_chain, new_db.clone(), amount).unwrap();
|
||||||
let reader = PackedReader::new(&snapshot_path).unwrap().unwrap();
|
let reader = PackedReader::new(&snapshot_path).unwrap().unwrap();
|
||||||
let engine = ::engines::NullEngine::new(Default::default(), Default::default());
|
let engine = ::engines::NullEngine::new(Default::default(), Default::default());
|
||||||
for chunk_hash in &reader.manifest().block_hashes {
|
for chunk_hash in &reader.manifest().block_hashes {
|
||||||
|
@ -30,7 +30,7 @@ use std::sync::Arc;
|
|||||||
trait Oracle: Send + Sync {
|
trait Oracle: Send + Sync {
|
||||||
fn to_number(&self, hash: H256) -> Option<u64>;
|
fn to_number(&self, hash: H256) -> Option<u64>;
|
||||||
|
|
||||||
fn is_major_syncing(&self) -> bool;
|
fn is_major_importing(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct StandardOracle<F> where F: 'static + Send + Sync + Fn() -> bool {
|
struct StandardOracle<F> where F: 'static + Send + Sync + Fn() -> bool {
|
||||||
@ -45,10 +45,8 @@ impl<F> Oracle for StandardOracle<F>
|
|||||||
self.client.block_header(BlockID::Hash(hash)).map(|h| HeaderView::new(&h).number())
|
self.client.block_header(BlockID::Hash(hash)).map(|h| HeaderView::new(&h).number())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_major_syncing(&self) -> bool {
|
fn is_major_importing(&self) -> bool {
|
||||||
let queue_info = self.client.queue_info();
|
(self.sync_status)()
|
||||||
|
|
||||||
(self.sync_status)() || queue_info.unverified_queue_size + queue_info.verified_queue_size > 3
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +108,7 @@ impl ChainNotify for Watcher {
|
|||||||
_: Vec<H256>,
|
_: Vec<H256>,
|
||||||
_duration: u64)
|
_duration: u64)
|
||||||
{
|
{
|
||||||
if self.oracle.is_major_syncing() { return }
|
if self.oracle.is_major_importing() { return }
|
||||||
|
|
||||||
trace!(target: "snapshot_watcher", "{} imported", imported.len());
|
trace!(target: "snapshot_watcher", "{} imported", imported.len());
|
||||||
|
|
||||||
@ -145,7 +143,7 @@ mod tests {
|
|||||||
self.0.get(&hash).cloned()
|
self.0.get(&hash).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_major_syncing(&self) -> bool { false }
|
fn is_major_importing(&self) -> bool { false }
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TestBroadcast(Option<u64>);
|
struct TestBroadcast(Option<u64>);
|
||||||
|
@ -151,7 +151,8 @@ impl Spec {
|
|||||||
if self.state_root_memo.read().is_none() {
|
if self.state_root_memo.read().is_none() {
|
||||||
*self.state_root_memo.write() = Some(self.genesis_state.root());
|
*self.state_root_memo.write() = Some(self.genesis_state.root());
|
||||||
}
|
}
|
||||||
self.state_root_memo.read().as_ref().unwrap().clone()
|
self.state_root_memo.read().as_ref().cloned()
|
||||||
|
.expect("state root memo ensured to be set at this point; qed")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the known knodes of the network in enode format.
|
/// Get the known knodes of the network in enode format.
|
||||||
|
@ -288,6 +288,15 @@ impl Account {
|
|||||||
/// Determine whether there are any un-`commit()`-ed storage-setting operations.
|
/// Determine whether there are any un-`commit()`-ed storage-setting operations.
|
||||||
pub fn storage_is_clean(&self) -> bool { self.storage_changes.is_empty() }
|
pub fn storage_is_clean(&self) -> bool { self.storage_changes.is_empty() }
|
||||||
|
|
||||||
|
/// Check if account has zero nonce, balance, no code and no storage.
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.storage_changes.is_empty() &&
|
||||||
|
self.balance.is_zero() &&
|
||||||
|
self.nonce.is_zero() &&
|
||||||
|
self.storage_root == SHA3_NULL_RLP &&
|
||||||
|
self.code_hash == SHA3_EMPTY
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
/// return the storage root associated with this account or None if it has been altered via the overlay.
|
/// 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} }
|
pub fn storage_root(&self) -> Option<&H256> { if self.storage_is_clean() {Some(&self.storage_root)} else {None} }
|
||||||
|
@ -239,15 +239,15 @@ impl State {
|
|||||||
|
|
||||||
/// Create a recoverable snaphot of this state.
|
/// Create a recoverable snaphot of this state.
|
||||||
pub fn snapshot(&mut self) {
|
pub fn snapshot(&mut self) {
|
||||||
self.snapshots.borrow_mut().push(HashMap::new());
|
self.snapshots.get_mut().push(HashMap::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Merge last snapshot with previous.
|
/// Merge last snapshot with previous.
|
||||||
pub fn discard_snapshot(&mut self) {
|
pub fn discard_snapshot(&mut self) {
|
||||||
// merge with previous snapshot
|
// 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(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() {
|
if prev.is_empty() {
|
||||||
**prev = snapshot;
|
**prev = snapshot;
|
||||||
} else {
|
} else {
|
||||||
@ -261,11 +261,11 @@ impl State {
|
|||||||
|
|
||||||
/// Revert to the last snapshot and discard it.
|
/// Revert to the last snapshot and discard it.
|
||||||
pub fn revert_to_snapshot(&mut self) {
|
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() {
|
for (k, v) in snapshot.drain() {
|
||||||
match v {
|
match v {
|
||||||
Some(v) => {
|
Some(v) => {
|
||||||
match self.cache.borrow_mut().entry(k) {
|
match self.cache.get_mut().entry(k) {
|
||||||
Entry::Occupied(mut e) => {
|
Entry::Occupied(mut e) => {
|
||||||
// Merge snapshotted changes back into the main account
|
// Merge snapshotted changes back into the main account
|
||||||
// storage preserving the cache.
|
// storage preserving the cache.
|
||||||
@ -277,7 +277,7 @@ impl State {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
match self.cache.borrow_mut().entry(k) {
|
match self.cache.get_mut().entry(k) {
|
||||||
Entry::Occupied(e) => {
|
Entry::Occupied(e) => {
|
||||||
if e.get().is_dirty() {
|
if e.get().is_dirty() {
|
||||||
e.remove();
|
e.remove();
|
||||||
@ -340,18 +340,20 @@ impl State {
|
|||||||
|
|
||||||
/// Determine whether an account exists.
|
/// Determine whether an account exists.
|
||||||
pub fn exists(&self, a: &Address) -> bool {
|
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-158 is in effect.
|
||||||
|
self.ensure_cached(a, RequireCache::None, false, |a| a.is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the balance of account `a`.
|
/// Get the balance of account `a`.
|
||||||
pub fn balance(&self, a: &Address) -> U256 {
|
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()))
|
|a| a.as_ref().map_or(U256::zero(), |account| *account.balance()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the nonce of account `a`.
|
/// Get the nonce of account `a`.
|
||||||
pub fn nonce(&self, a: &Address) -> U256 {
|
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()))
|
|a| a.as_ref().map_or(self.account_start_nonce, |account| *account.nonce()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -415,18 +417,18 @@ impl State {
|
|||||||
|
|
||||||
/// Get accounts' code.
|
/// Get accounts' code.
|
||||||
pub fn code(&self, a: &Address) -> Option<Arc<Bytes>> {
|
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()))
|
|a| a.as_ref().map_or(None, |a| a.code().clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn code_hash(&self, a: &Address) -> H256 {
|
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()))
|
|a| a.as_ref().map_or(SHA3_EMPTY, |a| a.code_hash()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get accounts' code size.
|
/// Get accounts' code size.
|
||||||
pub fn code_size(&self, a: &Address) -> Option<usize> {
|
pub fn code_size(&self, a: &Address) -> Option<usize> {
|
||||||
self.ensure_cached(a, RequireCache::CodeSize,
|
self.ensure_cached(a, RequireCache::CodeSize, true,
|
||||||
|a| a.as_ref().and_then(|a| a.code_size()))
|
|a| a.as_ref().and_then(|a| a.code_size()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -505,7 +507,9 @@ impl State {
|
|||||||
for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) {
|
for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) {
|
||||||
match a.account {
|
match a.account {
|
||||||
Some(ref mut account) => {
|
Some(ref mut account) => {
|
||||||
db.note_account_bloom(&address);
|
if !account.is_empty() {
|
||||||
|
db.note_account_bloom(&address);
|
||||||
|
}
|
||||||
let addr_hash = account.address_hash(address);
|
let addr_hash = account.address_hash(address);
|
||||||
let mut account_db = factories.accountdb.create(db.as_hashdb_mut(), addr_hash);
|
let mut account_db = factories.accountdb.create(db.as_hashdb_mut(), addr_hash);
|
||||||
account.commit_storage(&factories.trie, account_db.as_hashdb_mut());
|
account.commit_storage(&factories.trie, account_db.as_hashdb_mut());
|
||||||
@ -516,7 +520,7 @@ impl State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut trie = factories.trie.from_existing(db.as_hashdb_mut(), root).unwrap();
|
let mut trie = try!(factories.trie.from_existing(db.as_hashdb_mut(), root));
|
||||||
for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) {
|
for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) {
|
||||||
a.state = AccountState::Committed;
|
a.state = AccountState::Committed;
|
||||||
match a.account {
|
match a.account {
|
||||||
@ -559,7 +563,6 @@ impl State {
|
|||||||
pub fn populate_from(&mut self, accounts: PodState) {
|
pub fn populate_from(&mut self, accounts: PodState) {
|
||||||
assert!(self.snapshots.borrow().is_empty());
|
assert!(self.snapshots.borrow().is_empty());
|
||||||
for (add, acc) in accounts.drain().into_iter() {
|
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))));
|
self.cache.borrow_mut().insert(add, AccountEntry::new_dirty(Some(Account::from_pod(acc))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -578,14 +581,14 @@ impl State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn query_pod(&mut self, query: &PodState) {
|
fn query_pod(&mut self, query: &PodState) {
|
||||||
for (address, pod_account) in query.get() {
|
for (address, pod_account) in query.get().into_iter()
|
||||||
self.ensure_cached(address, RequireCache::Code, |a| {
|
.filter(|&(ref a, _)| self.ensure_cached(a, RequireCache::Code, true, |a| a.is_some()))
|
||||||
if a.is_some() {
|
{
|
||||||
for key in pod_account.storage.keys() {
|
// needs to be split into two parts for the refcell code here
|
||||||
self.storage_at(address, key);
|
// to work.
|
||||||
}
|
for key in pod_account.storage.keys() {
|
||||||
}
|
self.storage_at(address, key);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -613,7 +616,7 @@ impl State {
|
|||||||
/// Check caches for required data
|
/// Check caches for required data
|
||||||
/// First searches for account in the local, then the shared cache.
|
/// First searches for account in the local, then the shared cache.
|
||||||
/// Populates local cache if nothing found.
|
/// 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 {
|
where F: Fn(Option<&Account>) -> U {
|
||||||
// check local cache first
|
// check local cache first
|
||||||
if let Some(ref mut maybe_acc) = self.cache.borrow_mut().get_mut(a) {
|
if let Some(ref mut maybe_acc) = self.cache.borrow_mut().get_mut(a) {
|
||||||
@ -636,7 +639,7 @@ impl State {
|
|||||||
Some(r) => r,
|
Some(r) => r,
|
||||||
None => {
|
None => {
|
||||||
// first check bloom if it is not in database for sure
|
// first check bloom if it is not in database for sure
|
||||||
if !self.db.check_account_bloom(a) { return f(None); }
|
if check_bloom && !self.db.check_account_bloom(a) { return f(None); }
|
||||||
|
|
||||||
// not found in the global cache, get from the DB and insert into local
|
// not found in the global cache, get from the DB and insert into local
|
||||||
let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
|
let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
|
||||||
@ -688,14 +691,15 @@ impl State {
|
|||||||
}
|
}
|
||||||
self.note_cache(a);
|
self.note_cache(a);
|
||||||
|
|
||||||
match &mut self.cache.borrow_mut().get_mut(a).unwrap().account {
|
// at this point the entry is guaranteed to be in the cache.
|
||||||
&mut Some(ref mut acc) => not_default(acc),
|
|
||||||
slot => *slot = Some(default()),
|
|
||||||
}
|
|
||||||
|
|
||||||
// at this point the account is guaranteed to be in the cache.
|
|
||||||
RefMut::map(self.cache.borrow_mut(), |c| {
|
RefMut::map(self.cache.borrow_mut(), |c| {
|
||||||
let mut entry = c.get_mut(a).unwrap();
|
let mut entry = c.get_mut(a).expect("entry known to exist in the cache; qed");
|
||||||
|
|
||||||
|
match &mut entry.account {
|
||||||
|
&mut Some(ref mut acc) => not_default(acc),
|
||||||
|
slot => *slot = Some(default()),
|
||||||
|
}
|
||||||
|
|
||||||
// set the dirty flag after changing account data.
|
// set the dirty flag after changing account data.
|
||||||
entry.state = AccountState::Dirty;
|
entry.state = AccountState::Dirty;
|
||||||
match entry.account {
|
match entry.account {
|
||||||
@ -1667,6 +1671,21 @@ fn remove() {
|
|||||||
assert_eq!(state.nonce(&a), U256::from(0u64));
|
assert_eq!(state.nonce(&a), U256::from(0u64));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_account_exists() {
|
||||||
|
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()); // 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));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_from_database() {
|
fn remove_from_database() {
|
||||||
let a = Address::zero();
|
let a = Address::zero();
|
||||||
@ -1797,4 +1816,20 @@ fn create_empty() {
|
|||||||
assert_eq!(state.root().hex(), "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421");
|
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());
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -26,17 +26,18 @@ use bloom_journal::{Bloom, BloomJournal};
|
|||||||
use db::COL_ACCOUNT_BLOOM;
|
use db::COL_ACCOUNT_BLOOM;
|
||||||
use byteorder::{LittleEndian, ByteOrder};
|
use byteorder::{LittleEndian, ByteOrder};
|
||||||
|
|
||||||
const STATE_CACHE_ITEMS: usize = 256000;
|
|
||||||
const STATE_CACHE_BLOCKS: usize = 8;
|
|
||||||
|
|
||||||
pub const ACCOUNT_BLOOM_SPACE: usize = 1048576;
|
pub const ACCOUNT_BLOOM_SPACE: usize = 1048576;
|
||||||
pub const DEFAULT_ACCOUNT_PRESET: usize = 1000000;
|
pub const DEFAULT_ACCOUNT_PRESET: usize = 1000000;
|
||||||
|
|
||||||
pub const ACCOUNT_BLOOM_HASHCOUNT_KEY: &'static [u8] = b"account_hash_count";
|
pub const ACCOUNT_BLOOM_HASHCOUNT_KEY: &'static [u8] = b"account_hash_count";
|
||||||
|
|
||||||
|
const STATE_CACHE_BLOCKS: usize = 12;
|
||||||
|
|
||||||
/// Shared canonical state cache.
|
/// Shared canonical state cache.
|
||||||
struct AccountCache {
|
struct AccountCache {
|
||||||
/// DB Account cache. `None` indicates that account is known to be missing.
|
/// DB Account cache. `None` indicates that account is known to be missing.
|
||||||
|
// When changing the type of the values here, be sure to update `mem_used` and
|
||||||
|
// `new`.
|
||||||
accounts: LruCache<Address, Option<Account>>,
|
accounts: LruCache<Address, Option<Account>>,
|
||||||
/// Information on the modifications in recently committed blocks; specifically which addresses
|
/// Information on the modifications in recently committed blocks; specifically which addresses
|
||||||
/// changed in which block. Ordered by block number.
|
/// changed in which block. Ordered by block number.
|
||||||
@ -92,6 +93,7 @@ pub struct StateDB {
|
|||||||
local_cache: Vec<CacheQueueItem>,
|
local_cache: Vec<CacheQueueItem>,
|
||||||
/// Shared account bloom. Does not handle chain reorganizations.
|
/// Shared account bloom. Does not handle chain reorganizations.
|
||||||
account_bloom: Arc<Mutex<Bloom>>,
|
account_bloom: Arc<Mutex<Bloom>>,
|
||||||
|
cache_size: usize,
|
||||||
/// Hash of the block on top of which this instance was created or
|
/// Hash of the block on top of which this instance was created or
|
||||||
/// `None` if cache is disabled
|
/// `None` if cache is disabled
|
||||||
parent_hash: Option<H256>,
|
parent_hash: Option<H256>,
|
||||||
@ -102,16 +104,41 @@ pub struct StateDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl StateDB {
|
impl StateDB {
|
||||||
|
|
||||||
|
/// Create a new instance wrapping `JournalDB` and the maximum allowed size
|
||||||
|
/// of the LRU cache in bytes. Actual used memory may (read: will) be higher due to bookkeeping.
|
||||||
|
// TODO: make the cache size actually accurate by moving the account storage cache
|
||||||
|
// into the `AccountCache` structure as its own `LruCache<(Address, H256), H256>`.
|
||||||
|
pub fn new(db: Box<JournalDB>, cache_size: usize) -> StateDB {
|
||||||
|
let bloom = Self::load_bloom(db.backing());
|
||||||
|
let cache_items = cache_size / ::std::mem::size_of::<Option<Account>>();
|
||||||
|
|
||||||
|
StateDB {
|
||||||
|
db: db,
|
||||||
|
account_cache: Arc::new(Mutex::new(AccountCache {
|
||||||
|
accounts: LruCache::new(cache_items),
|
||||||
|
modifications: VecDeque::new(),
|
||||||
|
})),
|
||||||
|
local_cache: Vec::new(),
|
||||||
|
account_bloom: Arc::new(Mutex::new(bloom)),
|
||||||
|
cache_size: cache_size,
|
||||||
|
parent_hash: None,
|
||||||
|
commit_hash: None,
|
||||||
|
commit_number: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Loads accounts bloom from the database
|
/// Loads accounts bloom from the database
|
||||||
/// This bloom is used to handle request for the non-existant account fast
|
/// This bloom is used to handle request for the non-existant account fast
|
||||||
pub fn load_bloom(db: &Database) -> Bloom {
|
pub fn load_bloom(db: &Database) -> Bloom {
|
||||||
let hash_count_entry = db.get(COL_ACCOUNT_BLOOM, ACCOUNT_BLOOM_HASHCOUNT_KEY)
|
let hash_count_entry = db.get(COL_ACCOUNT_BLOOM, ACCOUNT_BLOOM_HASHCOUNT_KEY)
|
||||||
.expect("Low-level database error");
|
.expect("Low-level database error");
|
||||||
|
|
||||||
if hash_count_entry.is_none() {
|
let hash_count_bytes = match hash_count_entry {
|
||||||
return Bloom::new(ACCOUNT_BLOOM_SPACE, DEFAULT_ACCOUNT_PRESET);
|
Some(bytes) => bytes,
|
||||||
}
|
None => return Bloom::new(ACCOUNT_BLOOM_SPACE, DEFAULT_ACCOUNT_PRESET),
|
||||||
let hash_count_bytes = hash_count_entry.unwrap();
|
};
|
||||||
|
|
||||||
assert_eq!(hash_count_bytes.len(), 1);
|
assert_eq!(hash_count_bytes.len(), 1);
|
||||||
let hash_count = hash_count_bytes[0];
|
let hash_count = hash_count_bytes[0];
|
||||||
|
|
||||||
@ -129,23 +156,6 @@ impl StateDB {
|
|||||||
bloom
|
bloom
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new instance wrapping `JournalDB`
|
|
||||||
pub fn new(db: Box<JournalDB>) -> StateDB {
|
|
||||||
let bloom = Self::load_bloom(db.backing());
|
|
||||||
StateDB {
|
|
||||||
db: db,
|
|
||||||
account_cache: Arc::new(Mutex::new(AccountCache {
|
|
||||||
accounts: LruCache::new(STATE_CACHE_ITEMS),
|
|
||||||
modifications: VecDeque::new(),
|
|
||||||
})),
|
|
||||||
local_cache: Vec::new(),
|
|
||||||
account_bloom: Arc::new(Mutex::new(bloom)),
|
|
||||||
parent_hash: None,
|
|
||||||
commit_hash: None,
|
|
||||||
commit_number: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_account_bloom(&self, address: &Address) -> bool {
|
pub fn check_account_bloom(&self, address: &Address) -> bool {
|
||||||
trace!(target: "account_bloom", "Check account bloom: {:?}", address);
|
trace!(target: "account_bloom", "Check account bloom: {:?}", address);
|
||||||
let bloom = self.account_bloom.lock();
|
let bloom = self.account_bloom.lock();
|
||||||
@ -172,19 +182,24 @@ impl StateDB {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Commit all recent insert operations and canonical historical commits' removals from the
|
/// Journal all recent operations under the given era and ID.
|
||||||
/// old era to the backing database, reverting any non-canonical historical commit's inserts.
|
pub fn journal_under(&mut self, batch: &mut DBTransaction, now: u64, id: &H256) -> Result<u32, UtilError> {
|
||||||
pub fn commit(&mut self, batch: &mut DBTransaction, now: u64, id: &H256, end: Option<(u64, H256)>) -> Result<u32, UtilError> {
|
|
||||||
{
|
{
|
||||||
let mut bloom_lock = self.account_bloom.lock();
|
let mut bloom_lock = self.account_bloom.lock();
|
||||||
try!(Self::commit_bloom(batch, bloom_lock.drain_journal()));
|
try!(Self::commit_bloom(batch, bloom_lock.drain_journal()));
|
||||||
}
|
}
|
||||||
let records = try!(self.db.commit(batch, now, id, end));
|
let records = try!(self.db.journal_under(batch, now, id));
|
||||||
self.commit_hash = Some(id.clone());
|
self.commit_hash = Some(id.clone());
|
||||||
self.commit_number = Some(now);
|
self.commit_number = Some(now);
|
||||||
Ok(records)
|
Ok(records)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mark a given candidate from an ancient era as canonical, enacting its removals from the
|
||||||
|
/// backing database and reverting any non-canonical historical commit's insertions.
|
||||||
|
pub fn mark_canonical(&mut self, batch: &mut DBTransaction, end_era: u64, canon_id: &H256) -> Result<u32, UtilError> {
|
||||||
|
self.db.mark_canonical(batch, end_era, canon_id)
|
||||||
|
}
|
||||||
|
|
||||||
/// Propagate local cache into the global cache and synchonize
|
/// Propagate local cache into the global cache and synchonize
|
||||||
/// the global cache with the best block state.
|
/// the global cache with the best block state.
|
||||||
/// This function updates the global cache by removing entries
|
/// This function updates the global cache by removing entries
|
||||||
@ -298,6 +313,7 @@ impl StateDB {
|
|||||||
account_cache: self.account_cache.clone(),
|
account_cache: self.account_cache.clone(),
|
||||||
local_cache: Vec::new(),
|
local_cache: Vec::new(),
|
||||||
account_bloom: self.account_bloom.clone(),
|
account_bloom: self.account_bloom.clone(),
|
||||||
|
cache_size: self.cache_size,
|
||||||
parent_hash: None,
|
parent_hash: None,
|
||||||
commit_hash: None,
|
commit_hash: None,
|
||||||
commit_number: None,
|
commit_number: None,
|
||||||
@ -311,6 +327,7 @@ impl StateDB {
|
|||||||
account_cache: self.account_cache.clone(),
|
account_cache: self.account_cache.clone(),
|
||||||
local_cache: Vec::new(),
|
local_cache: Vec::new(),
|
||||||
account_bloom: self.account_bloom.clone(),
|
account_bloom: self.account_bloom.clone(),
|
||||||
|
cache_size: self.cache_size,
|
||||||
parent_hash: Some(parent.clone()),
|
parent_hash: Some(parent.clone()),
|
||||||
commit_hash: None,
|
commit_hash: None,
|
||||||
commit_number: None,
|
commit_number: None,
|
||||||
@ -324,7 +341,8 @@ impl StateDB {
|
|||||||
|
|
||||||
/// Heap size used.
|
/// Heap size used.
|
||||||
pub fn mem_used(&self) -> usize {
|
pub fn mem_used(&self) -> usize {
|
||||||
self.db.mem_used() //TODO: + self.account_cache.lock().heap_size_of_children()
|
// TODO: account for LRU-cache overhead; this is a close approximation.
|
||||||
|
self.db.mem_used() + self.account_cache.lock().accounts.len() * ::std::mem::size_of::<Option<Account>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns underlying `JournalDB`.
|
/// Returns underlying `JournalDB`.
|
||||||
@ -365,6 +383,11 @@ impl StateDB {
|
|||||||
cache.accounts.get_mut(a).map(|c| f(c.as_mut()))
|
cache.accounts.get_mut(a).map(|c| f(c.as_mut()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Query how much memory is set aside for the accounts cache (in bytes).
|
||||||
|
pub fn cache_size(&self) -> usize {
|
||||||
|
self.cache_size
|
||||||
|
}
|
||||||
|
|
||||||
/// Check if the account can be returned from cache by matching current block parent hash against canonical
|
/// Check if the account can be returned from cache by matching current block parent hash against canonical
|
||||||
/// state and filtering out account modified in later blocks.
|
/// state and filtering out account modified in later blocks.
|
||||||
fn is_allowed(addr: &Address, parent_hash: &Option<H256>, modifications: &VecDeque<BlockChanges>) -> bool {
|
fn is_allowed(addr: &Address, parent_hash: &Option<H256>, modifications: &VecDeque<BlockChanges>) -> bool {
|
||||||
@ -404,77 +427,77 @@ impl StateDB {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use util::{U256, H256, FixedHash, Address, DBTransaction};
|
use util::{U256, H256, FixedHash, Address, DBTransaction};
|
||||||
use tests::helpers::*;
|
use tests::helpers::*;
|
||||||
use state::Account;
|
use state::Account;
|
||||||
use util::log::init_log;
|
use util::log::init_log;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn state_db_smoke() {
|
fn state_db_smoke() {
|
||||||
init_log();
|
init_log();
|
||||||
|
|
||||||
let mut state_db_result = get_temp_state_db();
|
let mut state_db_result = get_temp_state_db();
|
||||||
let state_db = state_db_result.take();
|
let state_db = state_db_result.take();
|
||||||
let root_parent = H256::random();
|
let root_parent = H256::random();
|
||||||
let address = Address::random();
|
let address = Address::random();
|
||||||
let h0 = H256::random();
|
let h0 = H256::random();
|
||||||
let h1a = H256::random();
|
let h1a = H256::random();
|
||||||
let h1b = H256::random();
|
let h1b = H256::random();
|
||||||
let h2a = H256::random();
|
let h2a = H256::random();
|
||||||
let h2b = H256::random();
|
let h2b = H256::random();
|
||||||
let h3a = H256::random();
|
let h3a = H256::random();
|
||||||
let h3b = H256::random();
|
let h3b = H256::random();
|
||||||
let mut batch = DBTransaction::new(state_db.journal_db().backing());
|
let mut batch = DBTransaction::new(state_db.journal_db().backing());
|
||||||
|
|
||||||
// blocks [ 3a(c) 2a(c) 2b 1b 1a(c) 0 ]
|
// blocks [ 3a(c) 2a(c) 2b 1b 1a(c) 0 ]
|
||||||
// balance [ 5 5 4 3 2 2 ]
|
// balance [ 5 5 4 3 2 2 ]
|
||||||
let mut s = state_db.boxed_clone_canon(&root_parent);
|
let mut s = state_db.boxed_clone_canon(&root_parent);
|
||||||
s.add_to_account_cache(address, Some(Account::new_basic(2.into(), 0.into())), false);
|
s.add_to_account_cache(address, Some(Account::new_basic(2.into(), 0.into())), false);
|
||||||
s.commit(&mut batch, 0, &h0, None).unwrap();
|
s.journal_under(&mut batch, 0, &h0).unwrap();
|
||||||
s.sync_cache(&[], &[], true);
|
s.sync_cache(&[], &[], true);
|
||||||
|
|
||||||
let mut s = state_db.boxed_clone_canon(&h0);
|
let mut s = state_db.boxed_clone_canon(&h0);
|
||||||
s.commit(&mut batch, 1, &h1a, None).unwrap();
|
s.journal_under(&mut batch, 1, &h1a).unwrap();
|
||||||
s.sync_cache(&[], &[], true);
|
s.sync_cache(&[], &[], true);
|
||||||
|
|
||||||
let mut s = state_db.boxed_clone_canon(&h0);
|
let mut s = state_db.boxed_clone_canon(&h0);
|
||||||
s.add_to_account_cache(address, Some(Account::new_basic(3.into(), 0.into())), true);
|
s.add_to_account_cache(address, Some(Account::new_basic(3.into(), 0.into())), true);
|
||||||
s.commit(&mut batch, 1, &h1b, None).unwrap();
|
s.journal_under(&mut batch, 1, &h1b).unwrap();
|
||||||
s.sync_cache(&[], &[], false);
|
s.sync_cache(&[], &[], false);
|
||||||
|
|
||||||
let mut s = state_db.boxed_clone_canon(&h1b);
|
let mut s = state_db.boxed_clone_canon(&h1b);
|
||||||
s.add_to_account_cache(address, Some(Account::new_basic(4.into(), 0.into())), true);
|
s.add_to_account_cache(address, Some(Account::new_basic(4.into(), 0.into())), true);
|
||||||
s.commit(&mut batch, 2, &h2b, None).unwrap();
|
s.journal_under(&mut batch, 2, &h2b).unwrap();
|
||||||
s.sync_cache(&[], &[], false);
|
s.sync_cache(&[], &[], false);
|
||||||
|
|
||||||
let mut s = state_db.boxed_clone_canon(&h1a);
|
let mut s = state_db.boxed_clone_canon(&h1a);
|
||||||
s.add_to_account_cache(address, Some(Account::new_basic(5.into(), 0.into())), true);
|
s.add_to_account_cache(address, Some(Account::new_basic(5.into(), 0.into())), true);
|
||||||
s.commit(&mut batch, 2, &h2a, None).unwrap();
|
s.journal_under(&mut batch, 2, &h2a).unwrap();
|
||||||
s.sync_cache(&[], &[], true);
|
s.sync_cache(&[], &[], true);
|
||||||
|
|
||||||
let mut s = state_db.boxed_clone_canon(&h2a);
|
let mut s = state_db.boxed_clone_canon(&h2a);
|
||||||
s.commit(&mut batch, 3, &h3a, None).unwrap();
|
s.journal_under(&mut batch, 3, &h3a).unwrap();
|
||||||
s.sync_cache(&[], &[], true);
|
s.sync_cache(&[], &[], true);
|
||||||
|
|
||||||
let s = state_db.boxed_clone_canon(&h3a);
|
let s = state_db.boxed_clone_canon(&h3a);
|
||||||
assert_eq!(s.get_cached_account(&address).unwrap().unwrap().balance(), &U256::from(5));
|
assert_eq!(s.get_cached_account(&address).unwrap().unwrap().balance(), &U256::from(5));
|
||||||
|
|
||||||
let s = state_db.boxed_clone_canon(&h1a);
|
let s = state_db.boxed_clone_canon(&h1a);
|
||||||
assert!(s.get_cached_account(&address).is_none());
|
assert!(s.get_cached_account(&address).is_none());
|
||||||
|
|
||||||
let s = state_db.boxed_clone_canon(&h2b);
|
let s = state_db.boxed_clone_canon(&h2b);
|
||||||
assert!(s.get_cached_account(&address).is_none());
|
assert!(s.get_cached_account(&address).is_none());
|
||||||
|
|
||||||
let s = state_db.boxed_clone_canon(&h1b);
|
let s = state_db.boxed_clone_canon(&h1b);
|
||||||
assert!(s.get_cached_account(&address).is_none());
|
assert!(s.get_cached_account(&address).is_none());
|
||||||
|
|
||||||
// reorg to 3b
|
// reorg to 3b
|
||||||
// blocks [ 3b(c) 3a 2a 2b(c) 1b 1a 0 ]
|
// blocks [ 3b(c) 3a 2a 2b(c) 1b 1a 0 ]
|
||||||
let mut s = state_db.boxed_clone_canon(&h2b);
|
let mut s = state_db.boxed_clone_canon(&h2b);
|
||||||
s.commit(&mut batch, 3, &h3b, None).unwrap();
|
s.journal_under(&mut batch, 3, &h3b).unwrap();
|
||||||
s.sync_cache(&[h1b.clone(), h2b.clone(), h3b.clone()], &[h1a.clone(), h2a.clone(), h3a.clone()], true);
|
s.sync_cache(&[h1b.clone(), h2b.clone(), h3b.clone()], &[h1a.clone(), h2a.clone(), h3a.clone()], true);
|
||||||
let s = state_db.boxed_clone_canon(&h3a);
|
let s = state_db.boxed_clone_canon(&h3a);
|
||||||
assert!(s.get_cached_account(&address).is_none());
|
assert!(s.get_cached_account(&address).is_none());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ use common::*;
|
|||||||
use devtools::*;
|
use devtools::*;
|
||||||
use miner::Miner;
|
use miner::Miner;
|
||||||
use rlp::{Rlp, View};
|
use rlp::{Rlp, View};
|
||||||
|
use spec::Spec;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn imports_from_empty() {
|
fn imports_from_empty() {
|
||||||
@ -222,7 +223,7 @@ fn can_handle_long_fork() {
|
|||||||
push_blocks_to_client(client, 49, 1201, 800);
|
push_blocks_to_client(client, 49, 1201, 800);
|
||||||
push_blocks_to_client(client, 53, 1201, 600);
|
push_blocks_to_client(client, 53, 1201, 600);
|
||||||
|
|
||||||
for _ in 0..40 {
|
for _ in 0..400 {
|
||||||
client.import_verified_blocks();
|
client.import_verified_blocks();
|
||||||
}
|
}
|
||||||
assert_eq!(2000, client.chain_info().best_block_number);
|
assert_eq!(2000, client.chain_info().best_block_number);
|
||||||
@ -238,3 +239,27 @@ fn can_mine() {
|
|||||||
|
|
||||||
assert_eq!(*b.block().header().parent_hash(), BlockView::new(&dummy_blocks[0]).header_view().sha3());
|
assert_eq!(*b.block().header().parent_hash(), BlockView::new(&dummy_blocks[0]).header_view().sha3());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn change_history_size() {
|
||||||
|
let dir = RandomTempPath::new();
|
||||||
|
let test_spec = Spec::new_null();
|
||||||
|
let mut config = ClientConfig::default();
|
||||||
|
let db_config = DatabaseConfig::with_columns(::db::NUM_COLUMNS);
|
||||||
|
config.history = 2;
|
||||||
|
let address = Address::random();
|
||||||
|
{
|
||||||
|
let client = Client::new(ClientConfig::default(), &test_spec, dir.as_path(), Arc::new(Miner::with_spec(&test_spec)), IoChannel::disconnected(), &db_config).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.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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut config = ClientConfig::default();
|
||||||
|
config.history = 10;
|
||||||
|
let client = Client::new(config, &test_spec, dir.as_path(), Arc::new(Miner::with_spec(&test_spec)), IoChannel::disconnected(), &db_config).unwrap();
|
||||||
|
assert_eq!(client.state().balance(&address), 100.into());
|
||||||
|
}
|
||||||
|
@ -22,7 +22,6 @@ use spec::*;
|
|||||||
use state_db::StateDB;
|
use state_db::StateDB;
|
||||||
use block::{OpenBlock, Drain};
|
use block::{OpenBlock, Drain};
|
||||||
use blockchain::{BlockChain, Config as BlockChainConfig};
|
use blockchain::{BlockChain, Config as BlockChainConfig};
|
||||||
use state::State;
|
|
||||||
use evm::Schedule;
|
use evm::Schedule;
|
||||||
use engines::Engine;
|
use engines::Engine;
|
||||||
use ethereum;
|
use ethereum;
|
||||||
@ -35,19 +34,20 @@ use db::COL_STATE;
|
|||||||
pub enum ChainEra {
|
pub enum ChainEra {
|
||||||
Frontier,
|
Frontier,
|
||||||
Homestead,
|
Homestead,
|
||||||
DaoHardfork,
|
Eip150,
|
||||||
|
TransitionTest,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TestEngine {
|
pub struct TestEngine {
|
||||||
engine: Arc<Engine>,
|
engine: Arc<Engine>,
|
||||||
max_depth: usize
|
max_depth: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestEngine {
|
impl TestEngine {
|
||||||
pub fn new(max_depth: usize) -> TestEngine {
|
pub fn new(max_depth: usize) -> TestEngine {
|
||||||
TestEngine {
|
TestEngine {
|
||||||
engine: ethereum::new_frontier_test().engine,
|
engine: ethereum::new_frontier_test().engine,
|
||||||
max_depth: max_depth
|
max_depth: max_depth,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -347,7 +347,7 @@ pub fn get_temp_state() -> GuardedTempResult<State> {
|
|||||||
pub fn get_temp_state_db_in(path: &Path) -> StateDB {
|
pub fn get_temp_state_db_in(path: &Path) -> StateDB {
|
||||||
let db = new_db(path.to_str().expect("Only valid utf8 paths for tests."));
|
let db = new_db(path.to_str().expect("Only valid utf8 paths for tests."));
|
||||||
let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, COL_STATE);
|
let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, COL_STATE);
|
||||||
StateDB::new(journal_db)
|
StateDB::new(journal_db, 5 * 1024 * 1024)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -129,7 +129,7 @@ impl<T> TraceDB<T> where T: DatabaseExtras {
|
|||||||
pub fn new(config: Config, tracesdb: Arc<Database>, extras: Arc<T>) -> Self {
|
pub fn new(config: Config, tracesdb: Arc<Database>, extras: Arc<T>) -> Self {
|
||||||
let mut batch = DBTransaction::new(&tracesdb);
|
let mut batch = DBTransaction::new(&tracesdb);
|
||||||
batch.put(db::COL_TRACE, b"version", TRACE_DB_VER);
|
batch.put(db::COL_TRACE, b"version", TRACE_DB_VER);
|
||||||
tracesdb.write(batch).unwrap();
|
tracesdb.write(batch).expect("failed to update version");
|
||||||
|
|
||||||
TraceDB {
|
TraceDB {
|
||||||
traces: RwLock::new(HashMap::new()),
|
traces: RwLock::new(HashMap::new()),
|
||||||
@ -275,16 +275,6 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
|
|||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// insert new block traces into the cache and the database
|
|
||||||
{
|
|
||||||
let mut traces = self.traces.write();
|
|
||||||
// it's important to use overwrite here,
|
|
||||||
// cause this value might be queried by hash later
|
|
||||||
batch.write_with_cache(db::COL_TRACE, &mut *traces, request.block_hash, request.traces, CacheUpdatePolicy::Overwrite);
|
|
||||||
// note_used must be called after locking traces to avoid cache/traces deadlock on garbage collection
|
|
||||||
self.note_used(CacheID::Trace(request.block_hash.clone()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let chain = BloomGroupChain::new(self.bloom_config, self);
|
let chain = BloomGroupChain::new(self.bloom_config, self);
|
||||||
let trace_blooms = chain.replace(&replaced_range, enacted_blooms);
|
let trace_blooms = chain.replace(&replaced_range, enacted_blooms);
|
||||||
let blooms_to_insert = trace_blooms.into_iter()
|
let blooms_to_insert = trace_blooms.into_iter()
|
||||||
@ -299,6 +289,16 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
|
|||||||
self.note_used(CacheID::Bloom(key));
|
self.note_used(CacheID::Bloom(key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// insert new block traces into the cache and the database
|
||||||
|
{
|
||||||
|
let mut traces = self.traces.write();
|
||||||
|
// it's important to use overwrite here,
|
||||||
|
// cause this value might be queried by hash later
|
||||||
|
batch.write_with_cache(db::COL_TRACE, &mut *traces, request.block_hash, request.traces, CacheUpdatePolicy::Overwrite);
|
||||||
|
// note_used must be called after locking traces to avoid cache/traces deadlock on garbage collection
|
||||||
|
self.note_used(CacheID::Trace(request.block_hash.clone()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: Vec<usize>) -> Option<LocalizedTrace> {
|
fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: Vec<usize>) -> Option<LocalizedTrace> {
|
||||||
@ -504,6 +504,28 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_noncanon_import_request(block_number: BlockNumber, block_hash: H256) -> ImportRequest {
|
||||||
|
ImportRequest {
|
||||||
|
traces: FlatBlockTraces::from(vec![FlatTransactionTraces::from(vec![FlatTrace {
|
||||||
|
trace_address: Default::default(),
|
||||||
|
subtraces: 0,
|
||||||
|
action: Action::Call(Call {
|
||||||
|
from: 1.into(),
|
||||||
|
to: 2.into(),
|
||||||
|
value: 3.into(),
|
||||||
|
gas: 4.into(),
|
||||||
|
input: vec![],
|
||||||
|
call_type: CallType::Call,
|
||||||
|
}),
|
||||||
|
result: Res::FailedCall(TraceError::OutOfGas),
|
||||||
|
}])]),
|
||||||
|
block_hash: block_hash.clone(),
|
||||||
|
block_number: block_number,
|
||||||
|
enacted: vec![],
|
||||||
|
retracted: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn create_simple_localized_trace(block_number: BlockNumber, block_hash: H256, tx_hash: H256) -> LocalizedTrace {
|
fn create_simple_localized_trace(block_number: BlockNumber, block_hash: H256, tx_hash: H256) -> LocalizedTrace {
|
||||||
LocalizedTrace {
|
LocalizedTrace {
|
||||||
action: Action::Call(Call {
|
action: Action::Call(Call {
|
||||||
@ -524,6 +546,34 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_import_non_canon_traces() {
|
||||||
|
let temp = RandomTempPath::new();
|
||||||
|
let db = Arc::new(Database::open(&DatabaseConfig::with_columns(::db::NUM_COLUMNS), temp.as_str()).unwrap());
|
||||||
|
let mut config = Config::default();
|
||||||
|
config.enabled = true;
|
||||||
|
let block_0 = H256::from(0xa1);
|
||||||
|
let block_1 = H256::from(0xa2);
|
||||||
|
let tx_0 = H256::from(0xff);
|
||||||
|
let tx_1 = H256::from(0xaf);
|
||||||
|
|
||||||
|
let mut extras = Extras::default();
|
||||||
|
extras.block_hashes.insert(0, block_0.clone());
|
||||||
|
extras.block_hashes.insert(1, block_1.clone());
|
||||||
|
extras.transaction_hashes.insert(0, vec![tx_0.clone()]);
|
||||||
|
extras.transaction_hashes.insert(1, vec![tx_1.clone()]);
|
||||||
|
|
||||||
|
let tracedb = TraceDB::new(config, db.clone(), Arc::new(extras));
|
||||||
|
|
||||||
|
// import block 0
|
||||||
|
let request = create_noncanon_import_request(0, block_0.clone());
|
||||||
|
let mut batch = DBTransaction::new(&db);
|
||||||
|
tracedb.import(&mut batch, request);
|
||||||
|
db.write(batch).unwrap();
|
||||||
|
|
||||||
|
assert!(tracedb.traces(&block_0).is_some(), "Traces should be available even if block is non-canon.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_import() {
|
fn test_import() {
|
||||||
|
@ -31,5 +31,13 @@ pub struct BlockChainInfo {
|
|||||||
/// Best blockchain block hash.
|
/// Best blockchain block hash.
|
||||||
pub best_block_hash: H256,
|
pub best_block_hash: H256,
|
||||||
/// Best blockchain block number.
|
/// Best blockchain block number.
|
||||||
pub best_block_number: BlockNumber
|
pub best_block_number: BlockNumber,
|
||||||
|
/// Best ancient block hash.
|
||||||
|
pub ancient_block_hash: Option<H256>,
|
||||||
|
/// Best ancient block number.
|
||||||
|
pub ancient_block_number: Option<BlockNumber>,
|
||||||
|
/// First block on the best sequence.
|
||||||
|
pub first_block_hash: Option<H256>,
|
||||||
|
/// Number of the first block on the best sequence.
|
||||||
|
pub first_block_number: Option<BlockNumber>,
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,10 @@ pub enum RestorationStatus {
|
|||||||
Inactive,
|
Inactive,
|
||||||
/// Ongoing restoration.
|
/// Ongoing restoration.
|
||||||
Ongoing {
|
Ongoing {
|
||||||
|
/// Total number of state chunks.
|
||||||
|
state_chunks: u32,
|
||||||
|
/// Total number of block chunks.
|
||||||
|
block_chunks: u32,
|
||||||
/// Number of state chunks completed.
|
/// Number of state chunks completed.
|
||||||
state_chunks_done: u32,
|
state_chunks_done: u32,
|
||||||
/// Number of block chunks completed.
|
/// Number of block chunks completed.
|
||||||
|
@ -108,7 +108,7 @@ impl Filter {
|
|||||||
|
|
||||||
/// Returns true if given trace matches the filter.
|
/// Returns true if given trace matches the filter.
|
||||||
pub fn matches(&self, trace: &FlatTrace) -> bool {
|
pub fn matches(&self, trace: &FlatTrace) -> bool {
|
||||||
let action = match trace.action {
|
match trace.action {
|
||||||
Action::Call(ref call) => {
|
Action::Call(ref call) => {
|
||||||
let from_matches = self.from_address.matches(&call.from);
|
let from_matches = self.from_address.matches(&call.from);
|
||||||
let to_matches = self.to_address.matches(&call.to);
|
let to_matches = self.to_address.matches(&call.to);
|
||||||
@ -116,7 +116,12 @@ impl Filter {
|
|||||||
}
|
}
|
||||||
Action::Create(ref create) => {
|
Action::Create(ref create) => {
|
||||||
let from_matches = self.from_address.matches(&create.from);
|
let from_matches = self.from_address.matches(&create.from);
|
||||||
let to_matches = self.to_address.matches_all();
|
|
||||||
|
let to_matches = match trace.result {
|
||||||
|
Res::Create(ref create_result) => self.to_address.matches(&create_result.address),
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
|
|
||||||
from_matches && to_matches
|
from_matches && to_matches
|
||||||
},
|
},
|
||||||
Action::Suicide(ref suicide) => {
|
Action::Suicide(ref suicide) => {
|
||||||
@ -124,11 +129,6 @@ impl Filter {
|
|||||||
let to_matches = self.to_address.matches(&suicide.refund_address);
|
let to_matches = self.to_address.matches(&suicide.refund_address);
|
||||||
from_matches && to_matches
|
from_matches && to_matches
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
action || match trace.result {
|
|
||||||
Res::Create(ref create) => self.to_address.matches(&create.address),
|
|
||||||
_ => false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user