Merge branch 'master' into better-timeouts
This commit is contained in:
commit
7b3c648d3e
@ -38,7 +38,8 @@ linux-stable:
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/parity.md5 --body parity.md5
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/"parity_"$VER"_amd64.deb" --body "parity_"$VER"_amd64.deb"
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/"parity_"$VER"_amd64.deb.md5" --body "parity_"$VER"_amd64.deb.md5"
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://icarus.parity.io:1337/push-build/$CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu
|
||||
# - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu
|
||||
tags:
|
||||
- rust
|
||||
- rust-stable
|
||||
@ -106,7 +107,8 @@ linux-centos:
|
||||
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity --body target/release/parity
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity.md5 --body parity.md5
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://icarus.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
# - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
tags:
|
||||
- rust
|
||||
- rust-centos
|
||||
@ -144,7 +146,8 @@ linux-i686:
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_i386.deb" --body "parity_"$VER"_i386.deb"
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_i386.deb.md5" --body "parity_"$VER"_i386.deb.md5"
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://icarus.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
# - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
tags:
|
||||
- rust
|
||||
- rust-i686
|
||||
@ -189,7 +192,8 @@ linux-armv7:
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_armhf.deb" --body "parity_"$VER"_armhf.deb"
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_armhf.deb.md5" --body "parity_"$VER"_armhf.deb.md5"
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://icarus.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
# - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
tags:
|
||||
- rust
|
||||
- rust-arm
|
||||
@ -234,7 +238,8 @@ linux-arm:
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_armhf.deb" --body "parity_"$VER"_armhf.deb"
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_armhf.deb.md5" --body "parity_"$VER"_armhf.deb.md5"
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://icarus.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
# - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://icarus.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
tags:
|
||||
- rust
|
||||
- rust-arm
|
||||
@ -272,7 +277,8 @@ linux-armv6:
|
||||
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity --body target/$PLATFORM/release/parity
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://icarus.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
# - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
tags:
|
||||
- rust
|
||||
- rust-arm
|
||||
@ -316,7 +322,8 @@ linux-aarch64:
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_arm64.deb" --body "parity_"$VER"_arm64.deb"
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_arm64.deb.md5" --body "parity_"$VER"_arm64.deb.md5"
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://icarus.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
# - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
tags:
|
||||
- rust
|
||||
- rust-arm
|
||||
@ -352,7 +359,8 @@ darwin:
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity-"$VER"-osx-installer-EXPERIMENTAL.pkg" --body "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg"
|
||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity-"$VER"-osx-installer-EXPERIMENTAL.pkg.md5" --body "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg.md5"
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://icarus.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
# - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||
tags:
|
||||
- osx
|
||||
artifacts:
|
||||
@ -413,7 +421,8 @@ windows:
|
||||
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/InstallParity.exe.md5 --body nsis\InstallParity.exe.md5
|
||||
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/win-installer.zip --body nsis\win-installer.zip
|
||||
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/win-installer.zip.md5 --body nsis\win-installer.zip.md5
|
||||
- curl --data "commit=%CI_BUILD_REF%&sha3=%SHA3%&filename=parity.exe&secret=%RELEASES_SECRET%" http://icarus.parity.io:1337/push-build/%CI_BUILD_REF_NAME%/%PLATFORM%
|
||||
# - curl --data "commit=%CI_BUILD_REF%&sha3=%SHA3%&filename=parity.exe&secret=%RELEASES_SECRET%" http://update.parity.io:1337/push-build/%CI_BUILD_REF_NAME%/%PLATFORM%
|
||||
- curl --data "commit=%CI_BUILD_REF%&sha3=%SHA3%&filename=parity.exe&secret=%RELEASES_SECRET%" http://update.parity.io:1338/push-build/%CI_BUILD_REF_NAME%/%PLATFORM%
|
||||
tags:
|
||||
- rust-windows
|
||||
artifacts:
|
||||
@ -526,6 +535,7 @@ push-release:
|
||||
- triggers
|
||||
image: ethcore/rust:stable
|
||||
script:
|
||||
- curl --data "secret=$RELEASES_SECRET" http://icarus.parity.io:1337/push-release/$CI_BUILD_REF_NAME/$CI_BUILD_REF
|
||||
# - curl --data "secret=$RELEASES_SECRET" http://update.parity.io:1337/push-release/$CI_BUILD_REF_NAME/$CI_BUILD_REF
|
||||
- curl --data "secret=$RELEASES_SECRET" http://update.parity.io:1338/push-release/$CI_BUILD_REF_NAME/$CI_BUILD_REF
|
||||
tags:
|
||||
- curl
|
||||
|
236
Cargo.lock
generated
236
Cargo.lock
generated
@ -1,6 +1,6 @@
|
||||
[root]
|
||||
name = "parity"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -9,22 +9,22 @@ dependencies = [
|
||||
"daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore 1.5.0",
|
||||
"ethcore-dapps 1.5.0",
|
||||
"ethcore-devtools 1.5.0",
|
||||
"ethcore-io 1.5.0",
|
||||
"ethcore-ipc 1.5.0",
|
||||
"ethcore-ipc-codegen 1.5.0",
|
||||
"ethcore 1.6.0",
|
||||
"ethcore-dapps 1.6.0",
|
||||
"ethcore-devtools 1.6.0",
|
||||
"ethcore-io 1.6.0",
|
||||
"ethcore-ipc 1.6.0",
|
||||
"ethcore-ipc-codegen 1.6.0",
|
||||
"ethcore-ipc-hypervisor 1.2.0",
|
||||
"ethcore-ipc-nano 1.5.0",
|
||||
"ethcore-ipc-nano 1.6.0",
|
||||
"ethcore-ipc-tests 0.1.0",
|
||||
"ethcore-light 1.5.0",
|
||||
"ethcore-logger 1.5.0",
|
||||
"ethcore-rpc 1.5.0",
|
||||
"ethcore-signer 1.5.0",
|
||||
"ethcore-stratum 1.5.0",
|
||||
"ethcore-util 1.5.0",
|
||||
"ethsync 1.5.0",
|
||||
"ethcore-light 1.6.0",
|
||||
"ethcore-logger 1.6.0",
|
||||
"ethcore-rpc 1.6.0",
|
||||
"ethcore-signer 1.6.0",
|
||||
"ethcore-stratum 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethsync 1.6.0",
|
||||
"fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.9.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -32,10 +32,10 @@ dependencies = [
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-hash-fetch 1.5.0",
|
||||
"parity-hash-fetch 1.6.0",
|
||||
"parity-reactor 0.1.0",
|
||||
"parity-rpc-client 1.4.0",
|
||||
"parity-updater 1.5.0",
|
||||
"parity-updater 1.6.0",
|
||||
"regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rlp 0.1.0",
|
||||
"rpassword 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -336,7 +336,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ethash"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -346,7 +346,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ethcore"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -355,18 +355,18 @@ dependencies = [
|
||||
"crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethash 1.5.0",
|
||||
"ethash 1.6.0",
|
||||
"ethcore-bloom-journal 0.1.0",
|
||||
"ethcore-devtools 1.5.0",
|
||||
"ethcore-io 1.5.0",
|
||||
"ethcore-ipc 1.5.0",
|
||||
"ethcore-ipc-codegen 1.5.0",
|
||||
"ethcore-ipc-nano 1.5.0",
|
||||
"ethcore-util 1.5.0",
|
||||
"ethcore-devtools 1.6.0",
|
||||
"ethcore-io 1.6.0",
|
||||
"ethcore-ipc 1.6.0",
|
||||
"ethcore-ipc-codegen 1.6.0",
|
||||
"ethcore-ipc-nano 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethjson 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
"ethstore 0.1.0",
|
||||
"evmjit 1.5.0",
|
||||
"evmjit 1.6.0",
|
||||
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -403,13 +403,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ethcore-dapps"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-devtools 1.5.0",
|
||||
"ethcore-rpc 1.5.0",
|
||||
"ethcore-util 1.5.0",
|
||||
"ethcore-devtools 1.6.0",
|
||||
"ethcore-rpc 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"fetch 0.1.0",
|
||||
"futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
|
||||
@ -420,9 +420,9 @@ dependencies = [
|
||||
"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)",
|
||||
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-hash-fetch 1.5.0",
|
||||
"parity-hash-fetch 1.6.0",
|
||||
"parity-reactor 0.1.0",
|
||||
"parity-ui 1.5.0",
|
||||
"parity-ui 1.6.0",
|
||||
"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)",
|
||||
"serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -436,14 +436,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ethcore-devtools"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ethcore-io"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -454,17 +454,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ethcore-ipc"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"ethcore-devtools 1.5.0",
|
||||
"ethcore-util 1.5.0",
|
||||
"ethcore-devtools 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||
"semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ethcore-ipc-codegen"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quasi 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -477,9 +477,9 @@ dependencies = [
|
||||
name = "ethcore-ipc-hypervisor"
|
||||
version = "1.2.0"
|
||||
dependencies = [
|
||||
"ethcore-ipc 1.5.0",
|
||||
"ethcore-ipc-codegen 1.5.0",
|
||||
"ethcore-ipc-nano 1.5.0",
|
||||
"ethcore-ipc 1.6.0",
|
||||
"ethcore-ipc-codegen 1.6.0",
|
||||
"ethcore-ipc-nano 1.6.0",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||
"semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -488,9 +488,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ethcore-ipc-nano"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"ethcore-ipc 1.5.0",
|
||||
"ethcore-ipc 1.6.0",
|
||||
"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)",
|
||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||
@ -500,11 +500,11 @@ dependencies = [
|
||||
name = "ethcore-ipc-tests"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ethcore-devtools 1.5.0",
|
||||
"ethcore-ipc 1.5.0",
|
||||
"ethcore-ipc-codegen 1.5.0",
|
||||
"ethcore-ipc-nano 1.5.0",
|
||||
"ethcore-util 1.5.0",
|
||||
"ethcore-devtools 1.6.0",
|
||||
"ethcore-ipc 1.6.0",
|
||||
"ethcore-ipc-codegen 1.6.0",
|
||||
"ethcore-ipc-nano 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
|
||||
"semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -512,14 +512,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ethcore-light"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"ethcore 1.5.0",
|
||||
"ethcore-io 1.5.0",
|
||||
"ethcore-ipc 1.5.0",
|
||||
"ethcore-ipc-codegen 1.5.0",
|
||||
"ethcore-network 1.5.0",
|
||||
"ethcore-util 1.5.0",
|
||||
"ethcore 1.6.0",
|
||||
"ethcore-io 1.6.0",
|
||||
"ethcore-ipc 1.6.0",
|
||||
"ethcore-ipc-codegen 1.6.0",
|
||||
"ethcore-network 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rlp 0.1.0",
|
||||
"smallvec 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -528,10 +528,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ethcore-logger"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-util 1.5.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -541,13 +541,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ethcore-network"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-devtools 1.5.0",
|
||||
"ethcore-io 1.5.0",
|
||||
"ethcore-util 1.5.0",
|
||||
"ethcore-devtools 1.6.0",
|
||||
"ethcore-io 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethcrypto 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
"igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -566,20 +566,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ethcore-rpc"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethash 1.5.0",
|
||||
"ethcore 1.5.0",
|
||||
"ethcore-devtools 1.5.0",
|
||||
"ethcore-io 1.5.0",
|
||||
"ethcore-ipc 1.5.0",
|
||||
"ethcore-util 1.5.0",
|
||||
"ethash 1.6.0",
|
||||
"ethcore 1.6.0",
|
||||
"ethcore-devtools 1.6.0",
|
||||
"ethcore-io 1.6.0",
|
||||
"ethcore-ipc 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethcrypto 0.1.0",
|
||||
"ethjson 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
"ethstore 0.1.0",
|
||||
"ethsync 1.5.0",
|
||||
"ethsync 1.6.0",
|
||||
"fetch 0.1.0",
|
||||
"futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||
@ -588,7 +588,7 @@ dependencies = [
|
||||
"jsonrpc-macros 0.1.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-reactor 0.1.0",
|
||||
"parity-updater 1.5.0",
|
||||
"parity-updater 1.6.0",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rlp 0.1.0",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -602,18 +602,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ethcore-signer"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-devtools 1.5.0",
|
||||
"ethcore-io 1.5.0",
|
||||
"ethcore-rpc 1.5.0",
|
||||
"ethcore-util 1.5.0",
|
||||
"ethcore-devtools 1.6.0",
|
||||
"ethcore-io 1.6.0",
|
||||
"ethcore-rpc 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-ui 1.5.0",
|
||||
"parity-ui 1.6.0",
|
||||
"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)",
|
||||
"ws 0.5.3 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)",
|
||||
@ -621,14 +621,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ethcore-stratum"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-devtools 1.5.0",
|
||||
"ethcore-ipc 1.5.0",
|
||||
"ethcore-ipc-codegen 1.5.0",
|
||||
"ethcore-ipc-nano 1.5.0",
|
||||
"ethcore-util 1.5.0",
|
||||
"ethcore-devtools 1.6.0",
|
||||
"ethcore-ipc 1.6.0",
|
||||
"ethcore-ipc-codegen 1.6.0",
|
||||
"ethcore-ipc-nano 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||
"jsonrpc-tcp-server 0.1.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -639,7 +639,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ethcore-util"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -649,7 +649,7 @@ dependencies = [
|
||||
"eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)",
|
||||
"ethcore-bigint 0.1.2",
|
||||
"ethcore-bloom-journal 0.1.0",
|
||||
"ethcore-devtools 1.5.0",
|
||||
"ethcore-devtools 1.6.0",
|
||||
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -688,7 +688,7 @@ dependencies = [
|
||||
name = "ethjson"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ethcore-util 1.5.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_codegen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -732,19 +732,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ethsync"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore 1.5.0",
|
||||
"ethcore-devtools 1.5.0",
|
||||
"ethcore-io 1.5.0",
|
||||
"ethcore-ipc 1.5.0",
|
||||
"ethcore-ipc-codegen 1.5.0",
|
||||
"ethcore-ipc-nano 1.5.0",
|
||||
"ethcore-light 1.5.0",
|
||||
"ethcore-network 1.5.0",
|
||||
"ethcore-util 1.5.0",
|
||||
"ethcore 1.6.0",
|
||||
"ethcore-devtools 1.6.0",
|
||||
"ethcore-io 1.6.0",
|
||||
"ethcore-ipc 1.6.0",
|
||||
"ethcore-ipc-codegen 1.6.0",
|
||||
"ethcore-ipc-nano 1.6.0",
|
||||
"ethcore-light 1.6.0",
|
||||
"ethcore-network 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethkey 0.2.0",
|
||||
"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)",
|
||||
@ -757,7 +757,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "evmjit"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@ -919,11 +919,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ipc-common-types"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"ethcore-ipc 1.5.0",
|
||||
"ethcore-ipc-codegen 1.5.0",
|
||||
"ethcore-util 1.5.0",
|
||||
"ethcore-ipc 1.6.0",
|
||||
"ethcore-ipc-codegen 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@ -1440,10 +1440,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "parity-hash-fetch"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-util 1.5.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"fetch 0.1.0",
|
||||
"futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1466,9 +1466,9 @@ dependencies = [
|
||||
name = "parity-rpc-client"
|
||||
version = "1.4.0"
|
||||
dependencies = [
|
||||
"ethcore-rpc 1.5.0",
|
||||
"ethcore-signer 1.5.0",
|
||||
"ethcore-util 1.5.0",
|
||||
"ethcore-rpc 1.6.0",
|
||||
"ethcore-signer 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1484,7 +1484,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "parity-ui"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"parity-ui-dev 1.4.0",
|
||||
"parity-ui-precompiled 1.4.0 (git+https://github.com/ethcore/js-precompiled.git)",
|
||||
@ -1501,24 +1501,24 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "parity-ui-precompiled"
|
||||
version = "1.4.0"
|
||||
source = "git+https://github.com/ethcore/js-precompiled.git#d95a7dd2cc7469dc58af77743ec3ebc65e51cf36"
|
||||
source = "git+https://github.com/ethcore/js-precompiled.git#e21ae69190fa390f5550e5cf17f5ea362ba4db41"
|
||||
dependencies = [
|
||||
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parity-updater"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore 1.5.0",
|
||||
"ethcore-ipc 1.5.0",
|
||||
"ethcore-ipc-codegen 1.5.0",
|
||||
"ethcore-util 1.5.0",
|
||||
"ethsync 1.5.0",
|
||||
"ipc-common-types 1.5.0",
|
||||
"ethcore 1.6.0",
|
||||
"ethcore-ipc 1.6.0",
|
||||
"ethcore-ipc-codegen 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethsync 1.6.0",
|
||||
"ipc-common-types 1.6.0",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-hash-fetch 1.5.0",
|
||||
"parity-hash-fetch 1.6.0",
|
||||
"parity-reactor 0.1.0",
|
||||
"target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@ -1793,8 +1793,8 @@ name = "rpc-cli"
|
||||
version = "1.4.0"
|
||||
dependencies = [
|
||||
"ethcore-bigint 0.1.2",
|
||||
"ethcore-rpc 1.5.0",
|
||||
"ethcore-util 1.5.0",
|
||||
"ethcore-rpc 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-rpc-client 1.4.0",
|
||||
"rpassword 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
description = "Parity Ethereum client"
|
||||
name = "parity"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
build = "build.rs"
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
description = "Parity Dapps crate"
|
||||
name = "ethcore-dapps"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
build = "build.rs"
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
description = "Base Package for all Parity built-in dapps"
|
||||
name = "parity-dapps-glue"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
build = "build.rs"
|
||||
|
@ -17,7 +17,7 @@
|
||||
use std::io;
|
||||
use std::io::Read;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use page::{LocalPageEndpoint, PageCache};
|
||||
use endpoint::{Endpoints, EndpointInfo};
|
||||
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest};
|
||||
@ -28,10 +28,79 @@ struct LocalDapp {
|
||||
info: EndpointInfo,
|
||||
}
|
||||
|
||||
fn local_dapps(dapps_path: String) -> Vec<LocalDapp> {
|
||||
let files = fs::read_dir(dapps_path.as_str());
|
||||
/// Tries to find and read manifest file in given `path` to extract `EndpointInfo`
|
||||
/// If manifest is not found sensible default `EndpointInfo` is returned based on given `name`.
|
||||
fn read_manifest(name: &str, mut path: PathBuf) -> EndpointInfo {
|
||||
path.push(MANIFEST_FILENAME);
|
||||
|
||||
fs::File::open(path.clone())
|
||||
.map_err(|e| format!("{:?}", e))
|
||||
.and_then(|mut f| {
|
||||
// Reat file
|
||||
let mut s = String::new();
|
||||
f.read_to_string(&mut s).map_err(|e| format!("{:?}", e))?;
|
||||
// Try to deserialize manifest
|
||||
deserialize_manifest(s)
|
||||
})
|
||||
.map(Into::into)
|
||||
.unwrap_or_else(|e| {
|
||||
warn!(target: "dapps", "Cannot read manifest file at: {:?}. Error: {:?}", path, e);
|
||||
|
||||
EndpointInfo {
|
||||
name: name.into(),
|
||||
description: name.into(),
|
||||
version: "0.0.0".into(),
|
||||
author: "?".into(),
|
||||
icon_url: "icon.png".into(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns Dapp Id and Local Dapp Endpoint for given filesystem path.
|
||||
/// Parses the path to extract last component (for name).
|
||||
/// `None` is returned when path is invalid or non-existent.
|
||||
pub fn local_endpoint<P: AsRef<Path>>(path: P, signer_address: Option<(String, u16)>) -> Option<(String, Box<LocalPageEndpoint>)> {
|
||||
let path = path.as_ref().to_owned();
|
||||
path.canonicalize().ok().and_then(|path| {
|
||||
let name = path.file_name().and_then(|name| name.to_str());
|
||||
name.map(|name| {
|
||||
let dapp = local_dapp(name.into(), path.clone());
|
||||
(dapp.id, Box::new(LocalPageEndpoint::new(
|
||||
dapp.path, dapp.info, PageCache::Disabled, signer_address.clone())
|
||||
))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
fn local_dapp(name: String, path: PathBuf) -> LocalDapp {
|
||||
// try to get manifest file
|
||||
let info = read_manifest(&name, path.clone());
|
||||
LocalDapp {
|
||||
id: name,
|
||||
path: path,
|
||||
info: info,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns endpoints for Local Dapps found for given filesystem path.
|
||||
/// Scans the directory and collects `LocalPageEndpoints`.
|
||||
pub fn local_endpoints<P: AsRef<Path>>(dapps_path: P, signer_address: Option<(String, u16)>) -> Endpoints {
|
||||
let mut pages = Endpoints::new();
|
||||
for dapp in local_dapps(dapps_path.as_ref()) {
|
||||
pages.insert(
|
||||
dapp.id,
|
||||
Box::new(LocalPageEndpoint::new(dapp.path, dapp.info, PageCache::Disabled, signer_address.clone()))
|
||||
);
|
||||
}
|
||||
pages
|
||||
}
|
||||
|
||||
|
||||
fn local_dapps(dapps_path: &Path) -> Vec<LocalDapp> {
|
||||
let files = fs::read_dir(dapps_path);
|
||||
if let Err(e) = files {
|
||||
warn!(target: "dapps", "Unable to load local dapps from: {}. Reason: {:?}", dapps_path, e);
|
||||
warn!(target: "dapps", "Unable to load local dapps from: {}. Reason: {:?}", dapps_path.display(), e);
|
||||
return vec![];
|
||||
}
|
||||
|
||||
@ -59,51 +128,6 @@ fn local_dapps(dapps_path: String) -> Vec<LocalDapp> {
|
||||
}
|
||||
m.ok()
|
||||
})
|
||||
.map(|(name, path)| {
|
||||
// try to get manifest file
|
||||
let info = read_manifest(&name, path.clone());
|
||||
LocalDapp {
|
||||
id: name,
|
||||
path: path,
|
||||
info: info,
|
||||
}
|
||||
})
|
||||
.map(|(name, path)| local_dapp(name, path))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn read_manifest(name: &str, mut path: PathBuf) -> EndpointInfo {
|
||||
path.push(MANIFEST_FILENAME);
|
||||
|
||||
fs::File::open(path.clone())
|
||||
.map_err(|e| format!("{:?}", e))
|
||||
.and_then(|mut f| {
|
||||
// Reat file
|
||||
let mut s = String::new();
|
||||
f.read_to_string(&mut s).map_err(|e| format!("{:?}", e))?;
|
||||
// Try to deserialize manifest
|
||||
deserialize_manifest(s)
|
||||
})
|
||||
.map(Into::into)
|
||||
.unwrap_or_else(|e| {
|
||||
warn!(target: "dapps", "Cannot read manifest file at: {:?}. Error: {:?}", path, e);
|
||||
|
||||
EndpointInfo {
|
||||
name: name.into(),
|
||||
description: name.into(),
|
||||
version: "0.0.0".into(),
|
||||
author: "?".into(),
|
||||
icon_url: "icon.png".into(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn local_endpoints(dapps_path: String, signer_address: Option<(String, u16)>) -> Endpoints {
|
||||
let mut pages = Endpoints::new();
|
||||
for dapp in local_dapps(dapps_path) {
|
||||
pages.insert(
|
||||
dapp.id,
|
||||
Box::new(LocalPageEndpoint::new(dapp.path, dapp.info, PageCache::Disabled, signer_address.clone()))
|
||||
);
|
||||
}
|
||||
pages
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use endpoint::{Endpoints, Endpoint};
|
||||
use page::PageEndpoint;
|
||||
@ -43,7 +44,8 @@ pub fn utils() -> Box<Endpoint> {
|
||||
}
|
||||
|
||||
pub fn all_endpoints<F: Fetch>(
|
||||
dapps_path: String,
|
||||
dapps_path: PathBuf,
|
||||
extra_dapps: Vec<PathBuf>,
|
||||
signer_address: Option<(String, u16)>,
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
remote: Remote,
|
||||
@ -51,6 +53,13 @@ pub fn all_endpoints<F: Fetch>(
|
||||
) -> Endpoints {
|
||||
// fetch fs dapps at first to avoid overwriting builtins
|
||||
let mut pages = fs::local_endpoints(dapps_path, signer_address.clone());
|
||||
for path in extra_dapps {
|
||||
if let Some((id, endpoint)) = fs::local_endpoint(path.clone(), signer_address.clone()) {
|
||||
pages.insert(id, endpoint);
|
||||
} else {
|
||||
warn!(target: "dapps", "Ignoring invalid dapp at {}", path.display());
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE [ToDr] Dapps will be currently embeded on 8180
|
||||
insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(signer_address.clone()));
|
||||
|
@ -88,6 +88,7 @@ mod web;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::net::SocketAddr;
|
||||
use std::collections::HashMap;
|
||||
@ -123,7 +124,8 @@ impl<F> WebProxyTokens for F where F: Fn(String) -> bool + Send + Sync {
|
||||
|
||||
/// Webapps HTTP+RPC server build.
|
||||
pub struct ServerBuilder<T: Fetch = FetchClient> {
|
||||
dapps_path: String,
|
||||
dapps_path: PathBuf,
|
||||
extra_dapps: Vec<PathBuf>,
|
||||
handler: Arc<IoHandler>,
|
||||
registrar: Arc<ContractClient>,
|
||||
sync_status: Arc<SyncStatus>,
|
||||
@ -141,9 +143,10 @@ impl<T: Fetch> Extendable for ServerBuilder<T> {
|
||||
|
||||
impl ServerBuilder {
|
||||
/// Construct new dapps server
|
||||
pub fn new(dapps_path: String, registrar: Arc<ContractClient>, remote: Remote) -> Self {
|
||||
pub fn new<P: AsRef<Path>>(dapps_path: P, registrar: Arc<ContractClient>, remote: Remote) -> Self {
|
||||
ServerBuilder {
|
||||
dapps_path: dapps_path,
|
||||
dapps_path: dapps_path.as_ref().to_owned(),
|
||||
extra_dapps: vec![],
|
||||
handler: Arc::new(IoHandler::new()),
|
||||
registrar: registrar,
|
||||
sync_status: Arc::new(|| false),
|
||||
@ -160,6 +163,7 @@ impl<T: Fetch> ServerBuilder<T> {
|
||||
pub fn fetch<X: Fetch>(self, fetch: X) -> ServerBuilder<X> {
|
||||
ServerBuilder {
|
||||
dapps_path: self.dapps_path,
|
||||
extra_dapps: vec![],
|
||||
handler: self.handler,
|
||||
registrar: self.registrar,
|
||||
sync_status: self.sync_status,
|
||||
@ -188,6 +192,12 @@ impl<T: Fetch> ServerBuilder<T> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Change extra dapps paths (apart from `dapps_path`)
|
||||
pub fn extra_dapps<P: AsRef<Path>>(mut self, extra_dapps: &[P]) -> Self {
|
||||
self.extra_dapps = extra_dapps.iter().map(|p| p.as_ref().to_owned()).collect();
|
||||
self
|
||||
}
|
||||
|
||||
/// Asynchronously start server with no authentication,
|
||||
/// 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> {
|
||||
@ -197,6 +207,7 @@ impl<T: Fetch> ServerBuilder<T> {
|
||||
NoAuth,
|
||||
self.handler.clone(),
|
||||
self.dapps_path.clone(),
|
||||
self.extra_dapps.clone(),
|
||||
self.signer_address.clone(),
|
||||
self.registrar.clone(),
|
||||
self.sync_status.clone(),
|
||||
@ -215,6 +226,7 @@ impl<T: Fetch> ServerBuilder<T> {
|
||||
HttpBasicAuth::single_user(username, password),
|
||||
self.handler.clone(),
|
||||
self.dapps_path.clone(),
|
||||
self.extra_dapps.clone(),
|
||||
self.signer_address.clone(),
|
||||
self.registrar.clone(),
|
||||
self.sync_status.clone(),
|
||||
@ -270,7 +282,8 @@ impl Server {
|
||||
hosts: Option<Vec<String>>,
|
||||
authorization: A,
|
||||
handler: Arc<IoHandler>,
|
||||
dapps_path: String,
|
||||
dapps_path: PathBuf,
|
||||
extra_dapps: Vec<PathBuf>,
|
||||
signer_address: Option<(String, u16)>,
|
||||
registrar: Arc<ContractClient>,
|
||||
sync_status: Arc<SyncStatus>,
|
||||
@ -287,7 +300,14 @@ impl Server {
|
||||
remote.clone(),
|
||||
fetch.clone(),
|
||||
));
|
||||
let endpoints = Arc::new(apps::all_endpoints(dapps_path, signer_address.clone(), web_proxy_tokens, remote.clone(), fetch.clone()));
|
||||
let endpoints = Arc::new(apps::all_endpoints(
|
||||
dapps_path,
|
||||
extra_dapps,
|
||||
signer_address.clone(),
|
||||
web_proxy_tokens,
|
||||
remote.clone(),
|
||||
fetch.clone(),
|
||||
));
|
||||
let cors_domains = Self::cors_domains(signer_address.clone());
|
||||
|
||||
let special = Arc::new({
|
||||
|
@ -51,7 +51,7 @@ pub fn init_server<F, B>(hosts: Option<Vec<String>>, process: F, remote: Remote)
|
||||
let mut dapps_path = env::temp_dir();
|
||||
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
|
||||
let server = process(ServerBuilder::new(
|
||||
dapps_path.to_str().unwrap().into(), registrar.clone(), remote,
|
||||
&dapps_path, registrar.clone(), remote,
|
||||
))
|
||||
.signer_address(Some(("127.0.0.1".into(), SIGNER_PORT)))
|
||||
.start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), hosts).unwrap();
|
||||
@ -66,7 +66,7 @@ pub fn serve_with_auth(user: &str, pass: &str) -> Server {
|
||||
let registrar = Arc::new(FakeRegistrar::new());
|
||||
let mut dapps_path = env::temp_dir();
|
||||
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
|
||||
ServerBuilder::new(dapps_path.to_str().unwrap().into(), registrar.clone(), Remote::new_sync())
|
||||
ServerBuilder::new(&dapps_path, registrar.clone(), Remote::new_sync())
|
||||
.signer_address(Some(("127.0.0.1".into(), SIGNER_PORT)))
|
||||
.start_basic_auth_http(&"127.0.0.1:0".parse().unwrap(), None, user, pass).unwrap()
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ description = "Ethcore Parity UI"
|
||||
homepage = "http://parity.io"
|
||||
license = "GPL-3.0"
|
||||
name = "parity-ui"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[build-dependencies]
|
||||
|
@ -3,7 +3,7 @@ description = "Ethcore Database"
|
||||
homepage = "http://parity.io"
|
||||
license = "GPL-3.0"
|
||||
name = "ethcore-db"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
build = "build.rs"
|
||||
|
||||
|
@ -3,7 +3,7 @@ description = "Ethcore development/test/build tools"
|
||||
homepage = "http://parity.io"
|
||||
license = "GPL-3.0"
|
||||
name = "ethcore-devtools"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ethash"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[lib]
|
||||
|
@ -3,7 +3,7 @@ description = "Ethcore library"
|
||||
homepage = "http://parity.io"
|
||||
license = "GPL-3.0"
|
||||
name = "ethcore"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
build = "build.rs"
|
||||
|
||||
|
@ -3,7 +3,7 @@ description = "Parity LES primitives"
|
||||
homepage = "http://parity.io"
|
||||
license = "GPL-3.0"
|
||||
name = "ethcore-light"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
build = "build.rs"
|
||||
|
||||
|
@ -6,10 +6,12 @@
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"stepDuration": 1,
|
||||
"startStep": 2,
|
||||
"authorities" : [
|
||||
"0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e",
|
||||
"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1"
|
||||
]
|
||||
"validators": {
|
||||
"list": [
|
||||
"0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e",
|
||||
"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -5,7 +5,9 @@
|
||||
"params": {
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"durationLimit": "0x0d",
|
||||
"authorities" : ["0x9cce34f7ab185c7aba1b7c8140d620b4bda941d6"]
|
||||
"validators": {
|
||||
"list": ["0x9cce34f7ab185c7aba1b7c8140d620b4bda941d6"]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -17,7 +19,7 @@
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"generic": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"
|
||||
"generic": "0xc180"
|
||||
},
|
||||
"difficulty": "0x20000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
|
@ -4,10 +4,12 @@
|
||||
"tendermint": {
|
||||
"params": {
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"authorities" : [
|
||||
"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1",
|
||||
"0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e"
|
||||
]
|
||||
"validators" : {
|
||||
"list": [
|
||||
"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1",
|
||||
"0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
42
ethcore/res/validator_contract.json
Normal file
42
ethcore/res/validator_contract.json
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "TestValidatorContract",
|
||||
"engine": {
|
||||
"basicAuthority": {
|
||||
"params": {
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"durationLimit": "0x0d",
|
||||
"validators": {
|
||||
"contract": "0x0000000000000000000000000000000000000005"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x0",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x69"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"generic": "0xc180"
|
||||
},
|
||||
"difficulty": "0x20000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x2fefd8"
|
||||
},
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
"0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
|
||||
"0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
|
||||
"0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
|
||||
"0000000000000000000000000000000000000005": {
|
||||
"balance": "1",
|
||||
"constructor": "0x60a06040819052737d577a597b2742b498cb5cf0c26cdcd726d39e6e60609081527382a978b3f5962a5b0957d9ee9eef472ee55b42f1608052600080546002825581805290927f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5639182019291905b828111156100a25782518254600160a060020a031916600160a060020a0390911617825560209092019160019091019061006d565b5b506100cd9291505b808211156100c9578054600160a060020a03191681556001016100ab565b5090565b505034610000575b610332806100e46000396000f300606060405263ffffffff60e060020a60003504166335aa2e4481146100455780634d238c8e14610071578063b7ab4db51461008c578063f94e1867146100f4575b610000565b3461000057610055600435610106565b60408051600160a060020a039092168252519081900360200190f35b346100005761008a600160a060020a0360043516610136565b005b34610000576100996101ad565b60408051602080825283518183015283519192839290830191858101910280838382156100e1575b8051825260208311156100e157601f1990920191602091820191016100c1565b5050509050019250505060405180910390f35b346100005761008a600435610217565b005b600081815481101561000057906000526020600020900160005b915054906101000a9004600160a060020a031681565b60008054806001018281815481835581811511610178576000838152602090206101789181019083015b808211156101745760008155600101610160565b5090565b5b505050916000526020600020900160005b8154600160a060020a038086166101009390930a92830292021916179055505b50565b604080516020818101835260008083528054845181840281018401909552808552929392909183018282801561020c57602002820191906000526020600020905b8154600160a060020a031681526001909101906020018083116101ee575b505050505090505b90565b6000805460001981019081101561000057906000526020600020900160005b9054906101000a9004600160a060020a0316600082815481101561000057906000526020600020900160005b6101000a815481600160a060020a030219169083600160a060020a031602179055506000600160008054905003815481101561000057906000526020600020900160005b6101000a815490600160a060020a03021916905560008054809190600190038154818355818115116102fd576000838152602090206102fd9181019083015b808211156101745760008155600101610160565b5090565b5b505050505b505600a165627a7a72305820d742dd391941c1c255f0e1187ffa5b1e783219264fb10196018aefa379f5638b0029"
|
||||
},
|
||||
"9cce34f7ab185c7aba1b7c8140d620b4bda941d6": { "balance": "1606938044258990275541962092341162602522202993782792835301376" }
|
||||
}
|
||||
}
|
@ -150,6 +150,11 @@ impl AccountProvider {
|
||||
Ok(Address::from(address).into())
|
||||
}
|
||||
|
||||
/// Checks whether an account with a given address is present.
|
||||
pub fn has_account(&self, address: Address) -> Result<bool, Error> {
|
||||
Ok(self.accounts()?.iter().any(|&a| a == address))
|
||||
}
|
||||
|
||||
/// Returns addresses of all accounts.
|
||||
pub fn accounts(&self) -> Result<Vec<Address>, Error> {
|
||||
let accounts = self.sstore.accounts()?;
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
//! Blockchain block.
|
||||
|
||||
use std::cmp;
|
||||
use std::sync::Arc;
|
||||
use std::collections::HashSet;
|
||||
|
||||
@ -266,8 +267,9 @@ impl<'x> OpenBlock<'x> {
|
||||
r.block.base.header.set_extra_data(extra_data);
|
||||
r.block.base.header.note_dirty();
|
||||
|
||||
let gas_floor_target = ::std::cmp::max(gas_range_target.0, engine.params().min_gas_limit);
|
||||
engine.populate_from_parent(&mut r.block.base.header, parent, gas_floor_target, gas_range_target.1);
|
||||
let gas_floor_target = cmp::max(gas_range_target.0, engine.params().min_gas_limit);
|
||||
let gas_ceil_target = cmp::max(gas_range_target.1, gas_floor_target);
|
||||
engine.populate_from_parent(&mut r.block.base.header, parent, gas_floor_target, gas_ceil_target);
|
||||
engine.on_new_block(&mut r.block);
|
||||
Ok(r)
|
||||
}
|
||||
|
@ -378,7 +378,8 @@ impl BlockProvider for BlockChain {
|
||||
.enumerate()
|
||||
.flat_map(move |(index, (mut logs, tx_hash))| {
|
||||
let current_log_index = log_index;
|
||||
log_index -= logs.len();
|
||||
let no_of_logs = logs.len();
|
||||
log_index -= no_of_logs;
|
||||
|
||||
logs.reverse();
|
||||
logs.into_iter()
|
||||
@ -390,6 +391,7 @@ impl BlockProvider for BlockChain {
|
||||
transaction_hash: tx_hash,
|
||||
// iterating in reverse order
|
||||
transaction_index: receipts_len - index - 1,
|
||||
transaction_log_index: no_of_logs - i - 1,
|
||||
log_index: current_log_index - i - 1,
|
||||
})
|
||||
})
|
||||
@ -1936,6 +1938,7 @@ mod tests {
|
||||
block_number: block1.header().number(),
|
||||
transaction_hash: tx_hash1.clone(),
|
||||
transaction_index: 0,
|
||||
transaction_log_index: 0,
|
||||
log_index: 0,
|
||||
},
|
||||
LocalizedLogEntry {
|
||||
@ -1944,6 +1947,7 @@ mod tests {
|
||||
block_number: block1.header().number(),
|
||||
transaction_hash: tx_hash1.clone(),
|
||||
transaction_index: 0,
|
||||
transaction_log_index: 1,
|
||||
log_index: 1,
|
||||
},
|
||||
LocalizedLogEntry {
|
||||
@ -1952,6 +1956,7 @@ mod tests {
|
||||
block_number: block1.header().number(),
|
||||
transaction_hash: tx_hash2.clone(),
|
||||
transaction_index: 1,
|
||||
transaction_log_index: 0,
|
||||
log_index: 2,
|
||||
},
|
||||
LocalizedLogEntry {
|
||||
@ -1960,6 +1965,7 @@ mod tests {
|
||||
block_number: block2.header().number(),
|
||||
transaction_hash: tx_hash3.clone(),
|
||||
transaction_index: 0,
|
||||
transaction_log_index: 0,
|
||||
log_index: 0,
|
||||
}
|
||||
]);
|
||||
@ -1970,6 +1976,7 @@ mod tests {
|
||||
block_number: block2.header().number(),
|
||||
transaction_hash: tx_hash3.clone(),
|
||||
transaction_index: 0,
|
||||
transaction_log_index: 0,
|
||||
log_index: 0,
|
||||
}
|
||||
]);
|
||||
|
@ -53,13 +53,13 @@ use verification::queue::BlockQueue;
|
||||
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
|
||||
use client::{
|
||||
BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient,
|
||||
MiningBlockChainClient, TraceFilter, CallAnalytics, BlockImportError, Mode,
|
||||
MiningBlockChainClient, EngineClient, TraceFilter, CallAnalytics, BlockImportError, Mode,
|
||||
ChainNotify, PruningInfo,
|
||||
};
|
||||
use client::Error as ClientError;
|
||||
use env_info::EnvInfo;
|
||||
use executive::{Executive, Executed, TransactOptions, contract_address};
|
||||
use receipt::LocalizedReceipt;
|
||||
use receipt::{Receipt, LocalizedReceipt};
|
||||
use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase};
|
||||
use trace;
|
||||
use trace::FlatTransactionTraces;
|
||||
@ -837,7 +837,6 @@ impl snapshot::DatabaseRestore for Client {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl BlockChainClient for Client {
|
||||
fn call(&self, t: &SignedTransaction, block: BlockId, analytics: CallAnalytics) -> Result<Executed, CallError> {
|
||||
let header = self.block_header(block).ok_or(CallError::StatePruned)?;
|
||||
@ -849,7 +848,7 @@ impl BlockChainClient for Client {
|
||||
difficulty: header.difficulty(),
|
||||
last_hashes: last_hashes,
|
||||
gas_used: U256::zero(),
|
||||
gas_limit: U256::max_value(),
|
||||
gas_limit: header.gas_limit(),
|
||||
};
|
||||
// that's just a copy of the state.
|
||||
let mut state = self.state_at(block).ok_or(CallError::StatePruned)?;
|
||||
@ -874,6 +873,81 @@ impl BlockChainClient for Client {
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn estimate_gas(&self, t: &SignedTransaction, block: BlockId) -> Result<U256, CallError> {
|
||||
let header = self.block_header(block).ok_or(CallError::StatePruned)?;
|
||||
let last_hashes = self.build_last_hashes(header.parent_hash());
|
||||
let env_info = EnvInfo {
|
||||
number: header.number(),
|
||||
author: header.author(),
|
||||
timestamp: header.timestamp(),
|
||||
difficulty: header.difficulty(),
|
||||
last_hashes: last_hashes,
|
||||
gas_used: U256::zero(),
|
||||
gas_limit: header.gas_limit(),
|
||||
};
|
||||
// that's just a copy of the state.
|
||||
let mut original_state = self.state_at(block).ok_or(CallError::StatePruned)?;
|
||||
let sender = t.sender().map_err(|e| {
|
||||
let message = format!("Transaction malformed: {:?}", e);
|
||||
ExecutionError::TransactionMalformed(message)
|
||||
})?;
|
||||
let balance = original_state.balance(&sender);
|
||||
let needed_balance = t.value + t.gas * t.gas_price;
|
||||
if balance < needed_balance {
|
||||
// give the sender a sufficient balance
|
||||
original_state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty);
|
||||
}
|
||||
let options = TransactOptions { tracing: true, vm_tracing: false, check_nonce: false };
|
||||
let mut tx = t.clone();
|
||||
|
||||
let mut cond = |gas| {
|
||||
let mut state = original_state.clone();
|
||||
tx.gas = gas;
|
||||
Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm)
|
||||
.transact(&tx, options.clone())
|
||||
.map(|r| r.trace[0].result.succeeded())
|
||||
.unwrap_or(false)
|
||||
};
|
||||
|
||||
let mut upper = env_info.gas_limit;
|
||||
if !cond(upper) {
|
||||
// impossible at block gas limit - try `UPPER_CEILING` instead.
|
||||
// TODO: consider raising limit by powers of two.
|
||||
const UPPER_CEILING: u64 = 1_000_000_000_000u64;
|
||||
upper = UPPER_CEILING.into();
|
||||
if !cond(upper) {
|
||||
trace!(target: "estimate_gas", "estimate_gas failed with {}", upper);
|
||||
return Err(CallError::Execution(ExecutionError::Internal))
|
||||
}
|
||||
}
|
||||
let lower = t.gas_required(&self.engine.schedule(&env_info)).into();
|
||||
if cond(lower) {
|
||||
trace!(target: "estimate_gas", "estimate_gas succeeded with {}", lower);
|
||||
return Ok(lower)
|
||||
}
|
||||
|
||||
/// Find transition point between `lower` and `upper` where `cond` changes from `false` to `true`.
|
||||
/// Returns the lowest value between `lower` and `upper` for which `cond` returns true.
|
||||
/// We assert: `cond(lower) = false`, `cond(upper) = true`
|
||||
fn binary_chop<F>(mut lower: U256, mut upper: U256, mut cond: F) -> U256 where F: FnMut(U256) -> bool {
|
||||
while upper - lower > 1.into() {
|
||||
let mid = (lower + upper) / 2.into();
|
||||
trace!(target: "estimate_gas", "{} .. {} .. {}", lower, mid, upper);
|
||||
let c = cond(mid);
|
||||
match c {
|
||||
true => upper = mid,
|
||||
false => lower = mid,
|
||||
};
|
||||
trace!(target: "estimate_gas", "{} => {} .. {}", c, lower, upper);
|
||||
}
|
||||
upper
|
||||
}
|
||||
|
||||
// binary chop to non-excepting call with gas somewhere between 21000 and block gas limit
|
||||
trace!(target: "estimate_gas", "estimate_gas chopping {} .. {}", lower, upper);
|
||||
Ok(binary_chop(lower, upper, cond))
|
||||
}
|
||||
|
||||
fn replay(&self, id: TransactionId, analytics: CallAnalytics) -> Result<Executed, CallError> {
|
||||
let address = self.transaction_address(id).ok_or(CallError::TransactionNotFound)?;
|
||||
let header = self.block_header(BlockId::Hash(address.block_hash)).ok_or(CallError::StatePruned)?;
|
||||
@ -1134,53 +1208,23 @@ impl BlockChainClient for Client {
|
||||
let chain = self.chain.read();
|
||||
self.transaction_address(id)
|
||||
.and_then(|address| chain.block_number(&address.block_hash).and_then(|block_number| {
|
||||
let t = chain.block_body(&address.block_hash)
|
||||
.and_then(|body| {
|
||||
body.view().localized_transaction_at(&address.block_hash, block_number, address.index)
|
||||
});
|
||||
let transaction = chain.block_body(&address.block_hash)
|
||||
.and_then(|body| body.view().localized_transaction_at(&address.block_hash, block_number, address.index));
|
||||
|
||||
let tx_and_sender = t.and_then(|tx| tx.sender().ok().map(|sender| (tx, sender)));
|
||||
|
||||
match (tx_and_sender, chain.transaction_receipt(&address)) {
|
||||
(Some((tx, sender)), Some(receipt)) => {
|
||||
let block_hash = tx.block_hash.clone();
|
||||
let block_number = tx.block_number.clone();
|
||||
let transaction_hash = tx.hash();
|
||||
let transaction_index = tx.transaction_index;
|
||||
let prior_gas_used = match tx.transaction_index {
|
||||
0 => U256::zero(),
|
||||
i => {
|
||||
let prior_address = TransactionAddress { block_hash: address.block_hash, index: i - 1 };
|
||||
let prior_receipt = chain.transaction_receipt(&prior_address).expect("Transaction receipt at `address` exists; `prior_address` has lower index in same block; qed");
|
||||
prior_receipt.gas_used
|
||||
}
|
||||
};
|
||||
Some(LocalizedReceipt {
|
||||
transaction_hash: tx.hash(),
|
||||
transaction_index: tx.transaction_index,
|
||||
block_hash: tx.block_hash,
|
||||
block_number: tx.block_number,
|
||||
cumulative_gas_used: receipt.gas_used,
|
||||
gas_used: receipt.gas_used - prior_gas_used,
|
||||
contract_address: match tx.action {
|
||||
Action::Call(_) => None,
|
||||
Action::Create => Some(contract_address(&sender, &tx.nonce))
|
||||
},
|
||||
logs: receipt.logs.into_iter().enumerate().map(|(i, log)| LocalizedLogEntry {
|
||||
entry: log,
|
||||
block_hash: block_hash.clone(),
|
||||
block_number: block_number,
|
||||
transaction_hash: transaction_hash.clone(),
|
||||
transaction_index: transaction_index,
|
||||
log_index: i
|
||||
}).collect(),
|
||||
log_bloom: receipt.log_bloom,
|
||||
state_root: receipt.state_root,
|
||||
let previous_receipts = (0..address.index + 1)
|
||||
.map(|index| {
|
||||
let mut address = address.clone();
|
||||
address.index = index;
|
||||
chain.transaction_receipt(&address)
|
||||
})
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
}))
|
||||
.collect();
|
||||
match (transaction, previous_receipts) {
|
||||
(Some(transaction), Some(previous_receipts)) => {
|
||||
Some(transaction_receipt(transaction, previous_receipts))
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute> {
|
||||
@ -1346,11 +1390,6 @@ impl BlockChainClient for Client {
|
||||
}
|
||||
}
|
||||
|
||||
fn broadcast_consensus_message(&self, message: Bytes) {
|
||||
self.notify(|notify| notify.broadcast(message.clone()));
|
||||
}
|
||||
|
||||
|
||||
fn signing_network_id(&self) -> Option<u64> {
|
||||
self.engine.signing_network_id(&self.latest_env_info())
|
||||
}
|
||||
@ -1445,16 +1484,6 @@ impl MiningBlockChainClient for Client {
|
||||
&self.factories.vm
|
||||
}
|
||||
|
||||
fn update_sealing(&self) {
|
||||
self.miner.update_sealing(self)
|
||||
}
|
||||
|
||||
fn submit_seal(&self, block_hash: H256, seal: Vec<Bytes>) {
|
||||
if self.miner.submit_seal(self, block_hash, seal).is_err() {
|
||||
warn!(target: "poa", "Wrong internal seal submission!")
|
||||
}
|
||||
}
|
||||
|
||||
fn broadcast_proposal_block(&self, block: SealedBlock) {
|
||||
self.notify(|notify| {
|
||||
notify.new_blocks(
|
||||
@ -1502,6 +1531,22 @@ impl MiningBlockChainClient for Client {
|
||||
}
|
||||
}
|
||||
|
||||
impl EngineClient for Client {
|
||||
fn update_sealing(&self) {
|
||||
self.miner.update_sealing(self)
|
||||
}
|
||||
|
||||
fn submit_seal(&self, block_hash: H256, seal: Vec<Bytes>) {
|
||||
if self.miner.submit_seal(self, block_hash, seal).is_err() {
|
||||
warn!(target: "poa", "Wrong internal seal submission!")
|
||||
}
|
||||
}
|
||||
|
||||
fn broadcast_consensus_message(&self, message: Bytes) {
|
||||
self.notify(|notify| notify.broadcast(message.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
impl MayPanic for Client {
|
||||
fn on_panic<F>(&self, closure: F) where F: OnPanicListener {
|
||||
self.panic_handler.on_panic(closure);
|
||||
@ -1535,6 +1580,49 @@ impl Drop for Client {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `LocalizedReceipt` given `LocalizedTransaction`
|
||||
/// and a vector of receipts from given block up to transaction index.
|
||||
fn transaction_receipt(tx: LocalizedTransaction, mut receipts: Vec<Receipt>) -> LocalizedReceipt {
|
||||
assert_eq!(receipts.len(), tx.transaction_index + 1, "All previous receipts are provided.");
|
||||
|
||||
let sender = tx.sender()
|
||||
.expect("LocalizedTransaction is part of the blockchain; We have only valid transactions in chain; qed");
|
||||
let receipt = receipts.pop().expect("Current receipt is provided; qed");
|
||||
let prior_gas_used = match tx.transaction_index {
|
||||
0 => 0.into(),
|
||||
i => receipts.get(i - 1).expect("All previous receipts are provided; qed").gas_used,
|
||||
};
|
||||
let no_of_logs = receipts.into_iter().map(|receipt| receipt.logs.len()).sum::<usize>();
|
||||
let transaction_hash = tx.hash();
|
||||
let block_hash = tx.block_hash;
|
||||
let block_number = tx.block_number;
|
||||
let transaction_index = tx.transaction_index;
|
||||
|
||||
LocalizedReceipt {
|
||||
transaction_hash: transaction_hash,
|
||||
transaction_index: transaction_index,
|
||||
block_hash: block_hash,
|
||||
block_number:block_number,
|
||||
cumulative_gas_used: receipt.gas_used,
|
||||
gas_used: receipt.gas_used - prior_gas_used,
|
||||
contract_address: match tx.action {
|
||||
Action::Call(_) => None,
|
||||
Action::Create => Some(contract_address(&sender, &tx.nonce))
|
||||
},
|
||||
logs: receipt.logs.into_iter().enumerate().map(|(i, log)| LocalizedLogEntry {
|
||||
entry: log,
|
||||
block_hash: block_hash,
|
||||
block_number: block_number,
|
||||
transaction_hash: transaction_hash,
|
||||
transaction_index: transaction_index,
|
||||
transaction_log_index: i,
|
||||
log_index: no_of_logs + i,
|
||||
}).collect(),
|
||||
log_bloom: receipt.log_bloom,
|
||||
state_root: receipt.state_root,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
@ -1570,4 +1658,91 @@ mod tests {
|
||||
|
||||
assert!(client.tree_route(&genesis, &new_hash).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_correct_log_index() {
|
||||
use super::transaction_receipt;
|
||||
use ethkey::KeyPair;
|
||||
use log_entry::{LogEntry, LocalizedLogEntry};
|
||||
use receipt::{Receipt, LocalizedReceipt};
|
||||
use transaction::{Transaction, LocalizedTransaction, Action};
|
||||
use util::Hashable;
|
||||
|
||||
// given
|
||||
let key = KeyPair::from_secret("test".sha3()).unwrap();
|
||||
let secret = key.secret();
|
||||
|
||||
let block_number = 1;
|
||||
let block_hash = 5.into();
|
||||
let state_root = 99.into();
|
||||
let gas_used = 10.into();
|
||||
let raw_tx = Transaction {
|
||||
nonce: 0.into(),
|
||||
gas_price: 0.into(),
|
||||
gas: 21000.into(),
|
||||
action: Action::Call(10.into()),
|
||||
value: 0.into(),
|
||||
data: vec![],
|
||||
};
|
||||
let tx1 = raw_tx.clone().sign(secret, None);
|
||||
let transaction = LocalizedTransaction {
|
||||
signed: tx1.clone(),
|
||||
block_number: block_number,
|
||||
block_hash: block_hash,
|
||||
transaction_index: 1,
|
||||
};
|
||||
let logs = vec![LogEntry {
|
||||
address: 5.into(),
|
||||
topics: vec![],
|
||||
data: vec![],
|
||||
}, LogEntry {
|
||||
address: 15.into(),
|
||||
topics: vec![],
|
||||
data: vec![],
|
||||
}];
|
||||
let receipts = vec![Receipt {
|
||||
state_root: state_root,
|
||||
gas_used: 5.into(),
|
||||
log_bloom: Default::default(),
|
||||
logs: vec![logs[0].clone()],
|
||||
}, Receipt {
|
||||
state_root: state_root,
|
||||
gas_used: gas_used,
|
||||
log_bloom: Default::default(),
|
||||
logs: logs.clone(),
|
||||
}];
|
||||
|
||||
// when
|
||||
let receipt = transaction_receipt(transaction, receipts);
|
||||
|
||||
// then
|
||||
assert_eq!(receipt, LocalizedReceipt {
|
||||
transaction_hash: tx1.hash(),
|
||||
transaction_index: 1,
|
||||
block_hash: block_hash,
|
||||
block_number: block_number,
|
||||
cumulative_gas_used: gas_used,
|
||||
gas_used: gas_used - 5.into(),
|
||||
contract_address: None,
|
||||
logs: vec![LocalizedLogEntry {
|
||||
entry: logs[0].clone(),
|
||||
block_hash: block_hash,
|
||||
block_number: block_number,
|
||||
transaction_hash: tx1.hash(),
|
||||
transaction_index: 1,
|
||||
transaction_log_index: 0,
|
||||
log_index: 1,
|
||||
}, LocalizedLogEntry {
|
||||
entry: logs[1].clone(),
|
||||
block_hash: block_hash,
|
||||
block_number: block_number,
|
||||
transaction_hash: tx1.hash(),
|
||||
transaction_index: 1,
|
||||
transaction_log_index: 1,
|
||||
log_index: 2,
|
||||
}],
|
||||
log_bloom: Default::default(),
|
||||
state_root: state_root,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockChain
|
||||
pub use self::error::Error;
|
||||
pub use self::test_client::{TestBlockChainClient, EachBlockWith};
|
||||
pub use self::chain_notify::ChainNotify;
|
||||
pub use self::traits::{BlockChainClient, MiningBlockChainClient};
|
||||
pub use self::traits::{BlockChainClient, MiningBlockChainClient, EngineClient};
|
||||
|
||||
pub use self::traits::ProvingBlockChainClient;
|
||||
|
||||
|
@ -24,7 +24,7 @@ use devtools::*;
|
||||
use transaction::{Transaction, LocalizedTransaction, SignedTransaction, PendingTransaction, Action};
|
||||
use blockchain::TreeRoute;
|
||||
use client::{
|
||||
BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockId,
|
||||
BlockChainClient, MiningBlockChainClient, EngineClient, BlockChainInfo, BlockStatus, BlockId,
|
||||
TransactionId, UncleId, TraceId, TraceFilter, LastHashes, CallAnalytics, BlockImportError,
|
||||
};
|
||||
use db::{NUM_COLUMNS, COL_STATE};
|
||||
@ -372,16 +372,6 @@ impl MiningBlockChainClient for TestBlockChainClient {
|
||||
}
|
||||
|
||||
fn broadcast_proposal_block(&self, _block: SealedBlock) {}
|
||||
|
||||
fn update_sealing(&self) {
|
||||
self.miner.update_sealing(self)
|
||||
}
|
||||
|
||||
fn submit_seal(&self, block_hash: H256, seal: Vec<Bytes>) {
|
||||
if self.miner.submit_seal(self, block_hash, seal).is_err() {
|
||||
warn!(target: "poa", "Wrong internal seal submission!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockChainClient for TestBlockChainClient {
|
||||
@ -389,6 +379,10 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
self.execution_result.read().clone().unwrap()
|
||||
}
|
||||
|
||||
fn estimate_gas(&self, _t: &SignedTransaction, _block: BlockId) -> Result<U256, CallError> {
|
||||
Ok(21000.into())
|
||||
}
|
||||
|
||||
fn replay(&self, _id: TransactionId, _analytics: CallAnalytics) -> Result<Executed, CallError> {
|
||||
self.execution_result.read().clone().unwrap()
|
||||
}
|
||||
@ -699,8 +693,6 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
self.spec.engine.handle_message(&message).unwrap();
|
||||
}
|
||||
|
||||
fn broadcast_consensus_message(&self, _message: Bytes) {}
|
||||
|
||||
fn ready_transactions(&self) -> Vec<PendingTransaction> {
|
||||
self.miner.ready_transactions(self.chain_info().best_block_number)
|
||||
}
|
||||
@ -727,3 +719,17 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
|
||||
fn registry_address(&self, _name: String) -> Option<Address> { None }
|
||||
}
|
||||
|
||||
impl EngineClient for TestBlockChainClient {
|
||||
fn update_sealing(&self) {
|
||||
self.miner.update_sealing(self)
|
||||
}
|
||||
|
||||
fn submit_seal(&self, block_hash: H256, seal: Vec<Bytes>) {
|
||||
if self.miner.submit_seal(self, block_hash, seal).is_err() {
|
||||
warn!(target: "poa", "Wrong internal seal submission!")
|
||||
}
|
||||
}
|
||||
|
||||
fn broadcast_consensus_message(&self, _message: Bytes) {}
|
||||
}
|
||||
|
@ -184,6 +184,9 @@ pub trait BlockChainClient : Sync + Send {
|
||||
/// Makes a non-persistent transaction call.
|
||||
fn call(&self, t: &SignedTransaction, block: BlockId, analytics: CallAnalytics) -> Result<Executed, CallError>;
|
||||
|
||||
/// Estimates how much gas will be necessary for a call.
|
||||
fn estimate_gas(&self, t: &SignedTransaction, block: BlockId) -> Result<U256, CallError>;
|
||||
|
||||
/// Replays a given transaction for inspection.
|
||||
fn replay(&self, t: TransactionId, analytics: CallAnalytics) -> Result<Executed, CallError>;
|
||||
|
||||
@ -208,9 +211,6 @@ pub trait BlockChainClient : Sync + Send {
|
||||
/// Queue conensus engine message.
|
||||
fn queue_consensus_message(&self, message: Bytes);
|
||||
|
||||
/// Used by PoA to communicate with peers.
|
||||
fn broadcast_consensus_message(&self, message: Bytes);
|
||||
|
||||
/// List all transactions that are allowed into the next block.
|
||||
fn ready_transactions(&self) -> Vec<PendingTransaction>;
|
||||
|
||||
@ -294,12 +294,6 @@ pub trait MiningBlockChainClient: BlockChainClient {
|
||||
/// Returns EvmFactory.
|
||||
fn vm_factory(&self) -> &EvmFactory;
|
||||
|
||||
/// Used by PoA to try sealing on period change.
|
||||
fn update_sealing(&self);
|
||||
|
||||
/// Used by PoA to submit gathered signatures.
|
||||
fn submit_seal(&self, block_hash: H256, seal: Vec<Bytes>);
|
||||
|
||||
/// Broadcast a block proposal.
|
||||
fn broadcast_proposal_block(&self, block: SealedBlock);
|
||||
|
||||
@ -310,6 +304,18 @@ pub trait MiningBlockChainClient: BlockChainClient {
|
||||
fn latest_schedule(&self) -> Schedule;
|
||||
}
|
||||
|
||||
/// Client facilities used by internally sealing Engines.
|
||||
pub trait EngineClient: MiningBlockChainClient {
|
||||
/// Make a new block and seal it.
|
||||
fn update_sealing(&self);
|
||||
|
||||
/// Submit a seal for a block in the mining queue.
|
||||
fn submit_seal(&self, block_hash: H256, seal: Vec<Bytes>);
|
||||
|
||||
/// Broadcast a consensus message to the network.
|
||||
fn broadcast_consensus_message(&self, message: Bytes);
|
||||
}
|
||||
|
||||
/// Extended client interface for providing proofs of the state.
|
||||
pub trait ProvingBlockChainClient: BlockChainClient {
|
||||
/// Prove account storage at a specific block id.
|
||||
|
@ -32,11 +32,13 @@ use blockchain::extras::BlockDetails;
|
||||
use views::HeaderView;
|
||||
use evm::Schedule;
|
||||
use ethjson;
|
||||
use io::{IoContext, IoHandler, TimerToken, IoService, IoChannel};
|
||||
use service::ClientIoMessage;
|
||||
use io::{IoContext, IoHandler, TimerToken, IoService};
|
||||
use transaction::SignedTransaction;
|
||||
use env_info::EnvInfo;
|
||||
use builtin::Builtin;
|
||||
use client::{Client, EngineClient};
|
||||
use super::validator_set::{ValidatorSet, new_validator_set};
|
||||
use state::CleanupMode;
|
||||
|
||||
/// `AuthorityRound` params.
|
||||
#[derive(Debug, PartialEq)]
|
||||
@ -45,12 +47,12 @@ pub struct AuthorityRoundParams {
|
||||
pub gas_limit_bound_divisor: U256,
|
||||
/// Time to wait before next block or authority switching.
|
||||
pub step_duration: Duration,
|
||||
/// Valid authorities.
|
||||
pub authorities: Vec<Address>,
|
||||
/// Number of authorities.
|
||||
pub authority_n: usize,
|
||||
/// Block reward.
|
||||
pub block_reward: U256,
|
||||
/// Starting step,
|
||||
pub start_step: Option<u64>,
|
||||
/// Valid validators.
|
||||
pub validators: ethjson::spec::ValidatorSet,
|
||||
}
|
||||
|
||||
impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
|
||||
@ -58,8 +60,8 @@ impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
|
||||
AuthorityRoundParams {
|
||||
gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(),
|
||||
step_duration: Duration::from_secs(p.step_duration.into()),
|
||||
authority_n: p.authorities.len(),
|
||||
authorities: p.authorities.into_iter().map(Into::into).collect::<Vec<_>>(),
|
||||
validators: p.validators,
|
||||
block_reward: p.block_reward.map_or_else(U256::zero, Into::into),
|
||||
start_step: p.start_step.map(Into::into),
|
||||
}
|
||||
}
|
||||
@ -69,14 +71,17 @@ impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
|
||||
/// mainnet chains in the Olympic, Frontier and Homestead eras.
|
||||
pub struct AuthorityRound {
|
||||
params: CommonParams,
|
||||
our_params: AuthorityRoundParams,
|
||||
gas_limit_bound_divisor: U256,
|
||||
block_reward: U256,
|
||||
step_duration: Duration,
|
||||
builtins: BTreeMap<Address, Builtin>,
|
||||
transition_service: IoService<()>,
|
||||
message_channel: Mutex<Option<IoChannel<ClientIoMessage>>>,
|
||||
step: AtomicUsize,
|
||||
proposed: AtomicBool,
|
||||
account_provider: Mutex<Option<Arc<AccountProvider>>>,
|
||||
client: RwLock<Option<Weak<EngineClient>>>,
|
||||
account_provider: Mutex<Arc<AccountProvider>>,
|
||||
password: RwLock<Option<String>>,
|
||||
validators: Box<ValidatorSet + Send + Sync>,
|
||||
}
|
||||
|
||||
fn header_step(header: &Header) -> Result<usize, ::rlp::DecoderError> {
|
||||
@ -105,14 +110,17 @@ impl AuthorityRound {
|
||||
let engine = Arc::new(
|
||||
AuthorityRound {
|
||||
params: params,
|
||||
our_params: our_params,
|
||||
gas_limit_bound_divisor: our_params.gas_limit_bound_divisor,
|
||||
block_reward: our_params.block_reward,
|
||||
step_duration: our_params.step_duration,
|
||||
builtins: builtins,
|
||||
transition_service: IoService::<()>::start()?,
|
||||
message_channel: Mutex::new(None),
|
||||
step: AtomicUsize::new(initial_step),
|
||||
proposed: AtomicBool::new(false),
|
||||
account_provider: Mutex::new(None),
|
||||
client: RwLock::new(None),
|
||||
account_provider: Mutex::new(Arc::new(AccountProvider::transient_provider())),
|
||||
password: RwLock::new(None),
|
||||
validators: new_validator_set(our_params.validators),
|
||||
});
|
||||
// Do not initialize timeouts for tests.
|
||||
if should_timeout {
|
||||
@ -124,7 +132,7 @@ impl AuthorityRound {
|
||||
|
||||
fn remaining_step_duration(&self) -> Duration {
|
||||
let now = unix_now();
|
||||
let step_end = self.our_params.step_duration * (self.step.load(AtomicOrdering::SeqCst) as u32 + 1);
|
||||
let step_end = self.step_duration * (self.step.load(AtomicOrdering::SeqCst) as u32 + 1);
|
||||
if step_end > now {
|
||||
step_end - now
|
||||
} else {
|
||||
@ -132,13 +140,12 @@ impl AuthorityRound {
|
||||
}
|
||||
}
|
||||
|
||||
fn step_proposer(&self, step: usize) -> &Address {
|
||||
let p = &self.our_params;
|
||||
p.authorities.get(step % p.authority_n).expect("There are authority_n authorities; taking number modulo authority_n gives number in authority_n range; qed")
|
||||
fn step_proposer(&self, step: usize) -> Address {
|
||||
self.validators.get(step)
|
||||
}
|
||||
|
||||
fn is_step_proposer(&self, step: usize, address: &Address) -> bool {
|
||||
self.step_proposer(step) == address
|
||||
self.step_proposer(step) == *address
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,10 +190,9 @@ impl Engine for AuthorityRound {
|
||||
fn step(&self) {
|
||||
self.step.fetch_add(1, AtomicOrdering::SeqCst);
|
||||
self.proposed.store(false, AtomicOrdering::SeqCst);
|
||||
if let Some(ref channel) = *self.message_channel.lock() {
|
||||
match channel.send(ClientIoMessage::UpdateSealing) {
|
||||
Ok(_) => trace!(target: "poa", "timeout: UpdateSealing message sent for step {}.", self.step.load(AtomicOrdering::Relaxed)),
|
||||
Err(err) => trace!(target: "poa", "timeout: Could not send a sealing message {} for step {}.", err, self.step.load(AtomicOrdering::Relaxed)),
|
||||
if let Some(ref weak) = *self.client.read() {
|
||||
if let Some(c) = weak.upgrade() {
|
||||
c.update_sealing();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -207,7 +213,7 @@ impl Engine for AuthorityRound {
|
||||
header.set_difficulty(parent.difficulty().clone());
|
||||
header.set_gas_limit({
|
||||
let gas_limit = parent.gas_limit().clone();
|
||||
let bound_divisor = self.our_params.gas_limit_bound_divisor;
|
||||
let bound_divisor = self.gas_limit_bound_divisor;
|
||||
if gas_limit < gas_floor_target {
|
||||
min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into())
|
||||
} else {
|
||||
@ -217,8 +223,7 @@ impl Engine for AuthorityRound {
|
||||
}
|
||||
|
||||
fn is_sealer(&self, author: &Address) -> Option<bool> {
|
||||
let p = &self.our_params;
|
||||
Some(p.authorities.contains(author))
|
||||
Some(self.validators.contains(author))
|
||||
}
|
||||
|
||||
/// Attempt to seal the block internally.
|
||||
@ -230,18 +235,13 @@ impl Engine for AuthorityRound {
|
||||
let header = block.header();
|
||||
let step = self.step.load(AtomicOrdering::SeqCst);
|
||||
if self.is_step_proposer(step, header.author()) {
|
||||
if let Some(ref ap) = *self.account_provider.lock() {
|
||||
// Account should be permanently unlocked, otherwise sealing will fail.
|
||||
if let Ok(signature) = ap.sign(*header.author(), self.password.read().clone(), header.bare_hash()) {
|
||||
trace!(target: "poa", "generate_seal: Issuing a block for step {}.", step);
|
||||
self.proposed.store(true, AtomicOrdering::SeqCst);
|
||||
let rlps = vec![encode(&step).to_vec(), encode(&(&*signature as &[u8])).to_vec()];
|
||||
return Seal::Regular(rlps);
|
||||
} else {
|
||||
warn!(target: "poa", "generate_seal: FAIL: Accounts secret key unavailable.");
|
||||
}
|
||||
let ref ap = *self.account_provider.lock();
|
||||
if let Ok(signature) = ap.sign(*header.author(), self.password.read().clone(), header.bare_hash()) {
|
||||
trace!(target: "poa", "generate_seal: Issuing a block for step {}.", step);
|
||||
self.proposed.store(true, AtomicOrdering::SeqCst);
|
||||
return Seal::Regular(vec![encode(&step).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
||||
} else {
|
||||
warn!(target: "poa", "generate_seal: FAIL: Accounts not provided.");
|
||||
warn!(target: "poa", "generate_seal: FAIL: Accounts secret key unavailable.");
|
||||
}
|
||||
} else {
|
||||
trace!(target: "poa", "generate_seal: Not a proposer for step {}.", step);
|
||||
@ -249,6 +249,17 @@ impl Engine for AuthorityRound {
|
||||
Seal::None
|
||||
}
|
||||
|
||||
/// Apply the block reward on finalisation of the block.
|
||||
fn on_close_block(&self, block: &mut ExecutedBlock) {
|
||||
let fields = block.fields_mut();
|
||||
// Bestow block reward
|
||||
fields.state.add_balance(fields.header.author(), &self.block_reward, CleanupMode::NoEmpty);
|
||||
// Commit state so that we can actually figure out the state root.
|
||||
if let Err(e) = fields.state.commit() {
|
||||
warn!("Encountered error on state commit: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
/// Check the number of seal fields.
|
||||
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
if header.seal().len() != self.seal_fields() {
|
||||
@ -267,7 +278,7 @@ impl Engine for AuthorityRound {
|
||||
// Give one step slack if step is lagging, double vote is still not possible.
|
||||
if header_step <= self.step.load(AtomicOrdering::SeqCst) + 1 {
|
||||
let proposer_signature = header_signature(header)?;
|
||||
let ok_sig = verify_address(self.step_proposer(header_step), &proposer_signature, &header.bare_hash())?;
|
||||
let ok_sig = verify_address(&self.step_proposer(header_step), &proposer_signature, &header.bare_hash())?;
|
||||
if ok_sig {
|
||||
Ok(())
|
||||
} else {
|
||||
@ -292,7 +303,7 @@ impl Engine for AuthorityRound {
|
||||
Err(EngineError::DoubleVote(header.author().clone()))?;
|
||||
}
|
||||
|
||||
let gas_limit_divisor = self.our_params.gas_limit_bound_divisor;
|
||||
let gas_limit_divisor = self.gas_limit_bound_divisor;
|
||||
let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor;
|
||||
let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor;
|
||||
if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
|
||||
@ -323,8 +334,9 @@ impl Engine for AuthorityRound {
|
||||
}
|
||||
}
|
||||
|
||||
fn register_message_channel(&self, message_channel: IoChannel<ClientIoMessage>) {
|
||||
*self.message_channel.lock() = Some(message_channel);
|
||||
fn register_client(&self, client: Weak<Client>) {
|
||||
*self.client.write() = Some(client.clone());
|
||||
self.validators.register_call_contract(client);
|
||||
}
|
||||
|
||||
fn set_signer(&self, _address: Address, password: String) {
|
||||
@ -332,7 +344,7 @@ impl Engine for AuthorityRound {
|
||||
}
|
||||
|
||||
fn register_account_provider(&self, account_provider: Arc<AccountProvider>) {
|
||||
*self.account_provider.lock() = Some(account_provider);
|
||||
*self.account_provider.lock() = account_provider;
|
||||
}
|
||||
}
|
||||
|
||||
@ -440,7 +452,7 @@ mod tests {
|
||||
let engine = Spec::new_test_round().engine;
|
||||
|
||||
let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap();
|
||||
// Two authorities.
|
||||
// Two validators.
|
||||
// Spec starts with step 2.
|
||||
header.set_seal(vec![encode(&2usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
||||
assert!(engine.verify_block_seal(&header).is_err());
|
||||
@ -459,7 +471,7 @@ mod tests {
|
||||
let engine = Spec::new_test_round().engine;
|
||||
|
||||
let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap();
|
||||
// Two authorities.
|
||||
// Two validators.
|
||||
// Spec starts with step 2.
|
||||
header.set_seal(vec![encode(&1usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
||||
assert!(engine.verify_block_seal(&header).is_ok());
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
//! A blockchain engine that supports a basic, non-BFT proof-of-authority.
|
||||
|
||||
use std::sync::Weak;
|
||||
use util::*;
|
||||
use ethkey::{recover, public_to_address};
|
||||
use account_provider::AccountProvider;
|
||||
use block::*;
|
||||
@ -28,26 +30,23 @@ use evm::Schedule;
|
||||
use ethjson;
|
||||
use header::Header;
|
||||
use transaction::SignedTransaction;
|
||||
|
||||
use util::*;
|
||||
use client::Client;
|
||||
use super::validator_set::{ValidatorSet, new_validator_set};
|
||||
|
||||
/// `BasicAuthority` params.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct BasicAuthorityParams {
|
||||
/// Gas limit divisor.
|
||||
pub gas_limit_bound_divisor: U256,
|
||||
/// Block duration.
|
||||
pub duration_limit: u64,
|
||||
/// Valid signatories.
|
||||
pub authorities: HashSet<Address>,
|
||||
pub validators: ethjson::spec::ValidatorSet,
|
||||
}
|
||||
|
||||
impl From<ethjson::spec::BasicAuthorityParams> for BasicAuthorityParams {
|
||||
fn from(p: ethjson::spec::BasicAuthorityParams) -> Self {
|
||||
BasicAuthorityParams {
|
||||
gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(),
|
||||
duration_limit: p.duration_limit.into(),
|
||||
authorities: p.authorities.into_iter().map(Into::into).collect::<HashSet<_>>(),
|
||||
validators: p.validators,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -56,10 +55,11 @@ impl From<ethjson::spec::BasicAuthorityParams> for BasicAuthorityParams {
|
||||
/// mainnet chains in the Olympic, Frontier and Homestead eras.
|
||||
pub struct BasicAuthority {
|
||||
params: CommonParams,
|
||||
our_params: BasicAuthorityParams,
|
||||
gas_limit_bound_divisor: U256,
|
||||
builtins: BTreeMap<Address, Builtin>,
|
||||
account_provider: Mutex<Option<Arc<AccountProvider>>>,
|
||||
password: RwLock<Option<String>>,
|
||||
validators: Box<ValidatorSet + Send + Sync>,
|
||||
}
|
||||
|
||||
impl BasicAuthority {
|
||||
@ -67,8 +67,9 @@ impl BasicAuthority {
|
||||
pub fn new(params: CommonParams, our_params: BasicAuthorityParams, builtins: BTreeMap<Address, Builtin>) -> Self {
|
||||
BasicAuthority {
|
||||
params: params,
|
||||
our_params: our_params,
|
||||
gas_limit_bound_divisor: our_params.gas_limit_bound_divisor,
|
||||
builtins: builtins,
|
||||
validators: new_validator_set(our_params.validators),
|
||||
account_provider: Mutex::new(None),
|
||||
password: RwLock::new(None),
|
||||
}
|
||||
@ -95,7 +96,7 @@ impl Engine for BasicAuthority {
|
||||
header.set_difficulty(parent.difficulty().clone());
|
||||
header.set_gas_limit({
|
||||
let gas_limit = parent.gas_limit().clone();
|
||||
let bound_divisor = self.our_params.gas_limit_bound_divisor;
|
||||
let bound_divisor = self.gas_limit_bound_divisor;
|
||||
if gas_limit < gas_floor_target {
|
||||
min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into())
|
||||
} else {
|
||||
@ -105,22 +106,22 @@ impl Engine for BasicAuthority {
|
||||
}
|
||||
|
||||
fn is_sealer(&self, author: &Address) -> Option<bool> {
|
||||
Some(self.our_params.authorities.contains(author))
|
||||
Some(self.validators.contains(author))
|
||||
}
|
||||
|
||||
/// Attempt to seal the block internally.
|
||||
///
|
||||
/// This operation is synchronous and may (quite reasonably) not be available, in which `false` will
|
||||
/// be returned.
|
||||
fn generate_seal(&self, block: &ExecutedBlock) -> Seal {
|
||||
if let Some(ref ap) = *self.account_provider.lock() {
|
||||
let header = block.header();
|
||||
let message = header.bare_hash();
|
||||
// account should be pernamently unlocked, otherwise sealing will fail
|
||||
if let Ok(signature) = ap.sign(*block.header().author(), self.password.read().clone(), message) {
|
||||
return Seal::Regular(vec![::rlp::encode(&(&*signature as &[u8])).to_vec()]);
|
||||
} else {
|
||||
trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable");
|
||||
let author = header.author();
|
||||
if self.validators.contains(author) {
|
||||
let message = header.bare_hash();
|
||||
// account should be pernamently unlocked, otherwise sealing will fail
|
||||
if let Ok(signature) = ap.sign(*author, self.password.read().clone(), message) {
|
||||
return Seal::Regular(vec![::rlp::encode(&(&*signature as &[u8])).to_vec()]);
|
||||
} else {
|
||||
trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trace!(target: "basicauthority", "generate_seal: FAIL: accounts not provided");
|
||||
@ -145,7 +146,7 @@ impl Engine for BasicAuthority {
|
||||
// check the signature is legit.
|
||||
let sig = UntrustedRlp::new(&header.seal()[0]).as_val::<H520>()?;
|
||||
let signer = public_to_address(&recover(&sig.into(), &header.bare_hash())?);
|
||||
if !self.our_params.authorities.contains(&signer) {
|
||||
if !self.validators.contains(&signer) {
|
||||
return Err(BlockError::InvalidSeal)?;
|
||||
}
|
||||
Ok(())
|
||||
@ -161,7 +162,7 @@ impl Engine for BasicAuthority {
|
||||
if header.difficulty() != parent.difficulty() {
|
||||
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: *parent.difficulty(), found: *header.difficulty() })))
|
||||
}
|
||||
let gas_limit_divisor = self.our_params.gas_limit_bound_divisor;
|
||||
let gas_limit_divisor = self.gas_limit_bound_divisor;
|
||||
let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor;
|
||||
let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor;
|
||||
if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
|
||||
@ -179,6 +180,10 @@ impl Engine for BasicAuthority {
|
||||
t.sender().map(|_|()) // Perform EC recovery and cache sender
|
||||
}
|
||||
|
||||
fn register_client(&self, client: Weak<Client>) {
|
||||
self.validators.register_call_contract(client);
|
||||
}
|
||||
|
||||
fn set_signer(&self, _address: Address, password: String) {
|
||||
*self.password.write() = Some(password);
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ mod instant_seal;
|
||||
mod basic_authority;
|
||||
mod authority_round;
|
||||
mod tendermint;
|
||||
mod validator_set;
|
||||
|
||||
pub use self::null_engine::NullEngine;
|
||||
pub use self::instant_seal::InstantSeal;
|
||||
@ -28,6 +29,7 @@ pub use self::basic_authority::BasicAuthority;
|
||||
pub use self::authority_round::AuthorityRound;
|
||||
pub use self::tendermint::Tendermint;
|
||||
|
||||
use std::sync::Weak;
|
||||
use util::*;
|
||||
use account_provider::AccountProvider;
|
||||
use block::ExecutedBlock;
|
||||
@ -36,13 +38,12 @@ use env_info::EnvInfo;
|
||||
use error::Error;
|
||||
use spec::CommonParams;
|
||||
use evm::Schedule;
|
||||
use io::IoChannel;
|
||||
use service::ClientIoMessage;
|
||||
use header::Header;
|
||||
use transaction::SignedTransaction;
|
||||
use ethereum::ethash;
|
||||
use blockchain::extras::BlockDetails;
|
||||
use views::HeaderView;
|
||||
use client::Client;
|
||||
|
||||
/// Voting errors.
|
||||
#[derive(Debug)]
|
||||
@ -207,14 +208,15 @@ pub trait Engine : Sync + Send {
|
||||
/// Register an account which signs consensus messages.
|
||||
fn set_signer(&self, _address: Address, _password: String) {}
|
||||
|
||||
/// Stops any services that the may hold the Engine and makes it safe to drop.
|
||||
fn stop(&self) {}
|
||||
|
||||
/// Add a channel for communication with Client which can be used for sealing.
|
||||
fn register_message_channel(&self, _message_channel: IoChannel<ClientIoMessage>) {}
|
||||
/// Add Client which can be used for sealing, querying the state and sending messages.
|
||||
fn register_client(&self, _client: Weak<Client>) {}
|
||||
|
||||
/// Add an account provider useful for Engines that sign stuff.
|
||||
fn register_account_provider(&self, _account_provider: Arc<AccountProvider>) {}
|
||||
|
||||
/// Trigger next step of the consensus engine.
|
||||
fn step(&self) {}
|
||||
|
||||
/// Stops any services that the may hold the Engine and makes it safe to drop.
|
||||
fn stop(&self) {}
|
||||
}
|
||||
|
@ -27,8 +27,10 @@ mod transition;
|
||||
mod params;
|
||||
mod vote_collector;
|
||||
|
||||
use std::sync::Weak;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
|
||||
use util::*;
|
||||
use client::{Client, EngineClient};
|
||||
use error::{Error, BlockError};
|
||||
use header::Header;
|
||||
use builtin::Builtin;
|
||||
@ -43,8 +45,9 @@ use engines::{Engine, Seal, EngineError};
|
||||
use blockchain::extras::BlockDetails;
|
||||
use views::HeaderView;
|
||||
use evm::Schedule;
|
||||
use io::{IoService, IoChannel};
|
||||
use service::ClientIoMessage;
|
||||
use state::CleanupMode;
|
||||
use io::IoService;
|
||||
use super::validator_set::{ValidatorSet, new_validator_set};
|
||||
use self::message::*;
|
||||
use self::transition::TransitionHandler;
|
||||
use self::params::TendermintParams;
|
||||
@ -74,9 +77,11 @@ pub type BlockHash = H256;
|
||||
/// Engine using `Tendermint` consensus algorithm, suitable for EVM chain.
|
||||
pub struct Tendermint {
|
||||
params: CommonParams,
|
||||
our_params: TendermintParams,
|
||||
gas_limit_bound_divisor: U256,
|
||||
builtins: BTreeMap<Address, Builtin>,
|
||||
step_service: IoService<Step>,
|
||||
client: RwLock<Option<Weak<EngineClient>>>,
|
||||
block_reward: U256,
|
||||
/// Address to be used as authority.
|
||||
authority: RwLock<Address>,
|
||||
/// Password used for signing messages.
|
||||
@ -89,8 +94,6 @@ pub struct Tendermint {
|
||||
step: RwLock<Step>,
|
||||
/// Vote accumulator.
|
||||
votes: VoteCollector,
|
||||
/// Channel for updating the sealing.
|
||||
message_channel: Mutex<Option<IoChannel<ClientIoMessage>>>,
|
||||
/// Used to sign messages and proposals.
|
||||
account_provider: Mutex<Option<Arc<AccountProvider>>>,
|
||||
/// Message for the last PoLC.
|
||||
@ -99,6 +102,8 @@ pub struct Tendermint {
|
||||
last_lock: AtomicUsize,
|
||||
/// Bare hash of the proposed block, used for seal submission.
|
||||
proposal: RwLock<Option<H256>>,
|
||||
/// Set used to determine the current validators.
|
||||
validators: Box<ValidatorSet + Send + Sync>,
|
||||
}
|
||||
|
||||
impl Tendermint {
|
||||
@ -107,53 +112,49 @@ impl Tendermint {
|
||||
let engine = Arc::new(
|
||||
Tendermint {
|
||||
params: params,
|
||||
our_params: our_params,
|
||||
gas_limit_bound_divisor: our_params.gas_limit_bound_divisor,
|
||||
builtins: builtins,
|
||||
client: RwLock::new(None),
|
||||
step_service: IoService::<Step>::start()?,
|
||||
block_reward: our_params.block_reward,
|
||||
authority: RwLock::new(Address::default()),
|
||||
password: RwLock::new(None),
|
||||
height: AtomicUsize::new(1),
|
||||
round: AtomicUsize::new(0),
|
||||
step: RwLock::new(Step::Propose),
|
||||
votes: VoteCollector::new(),
|
||||
message_channel: Mutex::new(None),
|
||||
account_provider: Mutex::new(None),
|
||||
lock_change: RwLock::new(None),
|
||||
last_lock: AtomicUsize::new(0),
|
||||
proposal: RwLock::new(None),
|
||||
validators: new_validator_set(our_params.validators),
|
||||
});
|
||||
let handler = TransitionHandler { engine: Arc::downgrade(&engine) };
|
||||
let handler = TransitionHandler::new(Arc::downgrade(&engine), our_params.timeouts);
|
||||
engine.step_service.register_handler(Arc::new(handler))?;
|
||||
Ok(engine)
|
||||
}
|
||||
|
||||
fn update_sealing(&self) {
|
||||
if let Some(ref channel) = *self.message_channel.lock() {
|
||||
match channel.send(ClientIoMessage::UpdateSealing) {
|
||||
Ok(_) => trace!(target: "poa", "UpdateSealing message sent."),
|
||||
Err(err) => warn!(target: "poa", "Could not send a sealing message {}.", err),
|
||||
if let Some(ref weak) = *self.client.read() {
|
||||
if let Some(c) = weak.upgrade() {
|
||||
c.update_sealing();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn submit_seal(&self, block_hash: H256, seal: Vec<Bytes>) {
|
||||
if let Some(ref channel) = *self.message_channel.lock() {
|
||||
match channel.send(ClientIoMessage::SubmitSeal(block_hash, seal)) {
|
||||
Ok(_) => trace!(target: "poa", "SubmitSeal message sent."),
|
||||
Err(err) => warn!(target: "poa", "Could not send a sealing message {}.", err),
|
||||
if let Some(ref weak) = *self.client.read() {
|
||||
if let Some(c) = weak.upgrade() {
|
||||
c.submit_seal(block_hash, seal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn broadcast_message(&self, message: Bytes) {
|
||||
let channel = self.message_channel.lock().clone();
|
||||
if let Some(ref channel) = channel {
|
||||
match channel.send(ClientIoMessage::BroadcastMessage(message)) {
|
||||
Ok(_) => trace!(target: "poa", "BroadcastMessage message sent."),
|
||||
Err(err) => warn!(target: "poa", "broadcast_message: Could not send a sealing message {}.", err),
|
||||
if let Some(ref weak) = *self.client.read() {
|
||||
if let Some(c) = weak.upgrade() {
|
||||
c.broadcast_consensus_message(message);
|
||||
}
|
||||
} else {
|
||||
warn!(target: "poa", "broadcast_message: No IoChannel available.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -264,23 +265,22 @@ impl Tendermint {
|
||||
}
|
||||
|
||||
fn is_authority(&self, address: &Address) -> bool {
|
||||
self.our_params.authorities.contains(address)
|
||||
self.validators.contains(address)
|
||||
}
|
||||
|
||||
fn is_above_threshold(&self, n: usize) -> bool {
|
||||
n > self.our_params.authority_n * 2/3
|
||||
n > self.validators.count() * 2/3
|
||||
}
|
||||
|
||||
/// Check if address is a proposer for given round.
|
||||
fn is_round_proposer(&self, height: Height, round: Round, address: &Address) -> Result<(), EngineError> {
|
||||
let ref p = self.our_params;
|
||||
let proposer_nonce = height + round;
|
||||
trace!(target: "poa", "is_proposer: Proposer nonce: {}", proposer_nonce);
|
||||
let proposer = p.authorities.get(proposer_nonce % p.authority_n).expect("There are authority_n authorities; taking number modulo authority_n gives number in authority_n range; qed");
|
||||
if proposer == address {
|
||||
let proposer = self.validators.get(proposer_nonce);
|
||||
if proposer == *address {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(EngineError::NotProposer(Mismatch { expected: proposer.clone(), found: address.clone() }))
|
||||
Err(EngineError::NotProposer(Mismatch { expected: proposer, found: address.clone() }))
|
||||
}
|
||||
}
|
||||
|
||||
@ -404,7 +404,7 @@ impl Engine for Tendermint {
|
||||
header.set_difficulty(parent.difficulty().clone());
|
||||
header.set_gas_limit({
|
||||
let gas_limit = parent.gas_limit().clone();
|
||||
let bound_divisor = self.our_params.gas_limit_bound_divisor;
|
||||
let bound_divisor = self.gas_limit_bound_divisor;
|
||||
if gas_limit < gas_floor_target {
|
||||
min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into())
|
||||
} else {
|
||||
@ -469,6 +469,17 @@ impl Engine for Tendermint {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Apply the block reward on finalisation of the block.
|
||||
fn on_close_block(&self, block: &mut ExecutedBlock) {
|
||||
let fields = block.fields_mut();
|
||||
// Bestow block reward
|
||||
fields.state.add_balance(fields.header.author(), &self.block_reward, CleanupMode::NoEmpty);
|
||||
// Commit state so that we can actually figure out the state root.
|
||||
if let Err(e) = fields.state.commit() {
|
||||
warn!("Encountered error on state commit: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
let seal_length = header.seal().len();
|
||||
if seal_length == self.seal_fields() {
|
||||
@ -507,7 +518,7 @@ impl Engine for Tendermint {
|
||||
Some(a) => a,
|
||||
None => public_to_address(&recover(&precommit.signature.into(), &precommit_hash)?),
|
||||
};
|
||||
if !self.our_params.authorities.contains(&address) {
|
||||
if !self.validators.contains(&address) {
|
||||
Err(EngineError::NotAuthorized(address.to_owned()))?
|
||||
}
|
||||
|
||||
@ -540,7 +551,7 @@ impl Engine for Tendermint {
|
||||
Err(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }))?;
|
||||
}
|
||||
|
||||
let gas_limit_divisor = self.our_params.gas_limit_bound_divisor;
|
||||
let gas_limit_divisor = self.gas_limit_bound_divisor;
|
||||
let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor;
|
||||
let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor;
|
||||
if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
|
||||
@ -643,9 +654,9 @@ impl Engine for Tendermint {
|
||||
self.to_step(next_step);
|
||||
}
|
||||
|
||||
fn register_message_channel(&self, message_channel: IoChannel<ClientIoMessage>) {
|
||||
trace!(target: "poa", "Register the IoChannel.");
|
||||
*self.message_channel.lock() = Some(message_channel);
|
||||
fn register_client(&self, client: Weak<Client>) {
|
||||
*self.client.write() = Some(client.clone());
|
||||
self.validators.register_call_contract(client);
|
||||
}
|
||||
|
||||
fn register_account_provider(&self, account_provider: Arc<AccountProvider>) {
|
||||
@ -656,21 +667,20 @@ impl Engine for Tendermint {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use util::*;
|
||||
use io::{IoContext, IoHandler};
|
||||
use block::*;
|
||||
use error::{Error, BlockError};
|
||||
use header::Header;
|
||||
use io::IoChannel;
|
||||
use env_info::EnvInfo;
|
||||
use client::chain_notify::ChainNotify;
|
||||
use miner::MinerService;
|
||||
use tests::helpers::*;
|
||||
use account_provider::AccountProvider;
|
||||
use service::ClientIoMessage;
|
||||
use spec::Spec;
|
||||
use engines::{Engine, EngineError, Seal};
|
||||
use super::*;
|
||||
use super::message::*;
|
||||
|
||||
/// Accounts inserted with "0" and "1" are authorities. First proposer is "0".
|
||||
/// Accounts inserted with "0" and "1" are validators. First proposer is "0".
|
||||
fn setup() -> (Spec, Arc<AccountProvider>) {
|
||||
let tap = Arc::new(AccountProvider::transient_provider());
|
||||
let spec = Spec::new_test_tendermint();
|
||||
@ -678,13 +688,13 @@ mod tests {
|
||||
(spec, tap)
|
||||
}
|
||||
|
||||
fn propose_default(spec: &Spec, proposer: Address) -> (LockedBlock, Vec<Bytes>) {
|
||||
fn propose_default(spec: &Spec, proposer: Address) -> (ClosedBlock, Vec<Bytes>) {
|
||||
let mut db_result = get_temp_state_db();
|
||||
let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap();
|
||||
let genesis_header = spec.genesis_header();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b = OpenBlock::new(spec.engine.as_ref(), Default::default(), false, db.boxed_clone(), &genesis_header, last_hashes, proposer, (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b = b.close_and_lock();
|
||||
let b = b.close();
|
||||
if let Seal::Proposal(seal) = spec.engine.generate_seal(b.block()) {
|
||||
(b, seal)
|
||||
} else {
|
||||
@ -692,7 +702,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn vote<F>(engine: &Arc<Engine>, signer: F, height: usize, round: usize, step: Step, block_hash: Option<H256>) -> Bytes where F: FnOnce(H256) -> Result<H520, ::account_provider::Error> {
|
||||
fn vote<F>(engine: &Engine, signer: F, height: usize, round: usize, step: Step, block_hash: Option<H256>) -> Bytes where F: FnOnce(H256) -> Result<H520, ::account_provider::Error> {
|
||||
let mi = message_info_rlp(height, round, step, block_hash);
|
||||
let m = message_full_rlp(&signer(mi.sha3()).unwrap().into(), &mi);
|
||||
engine.handle_message(&m).unwrap();
|
||||
@ -710,37 +720,26 @@ mod tests {
|
||||
]
|
||||
}
|
||||
|
||||
fn precommit_signatures(tap: &Arc<AccountProvider>, height: Height, round: Round, bare_hash: Option<H256>, v1: H160, v2: H160) -> Bytes {
|
||||
let vote_info = message_info_rlp(height, round, Step::Precommit, bare_hash);
|
||||
::rlp::encode(&vec![
|
||||
H520::from(tap.sign(v1, None, vote_info.sha3()).unwrap()),
|
||||
H520::from(tap.sign(v2, None, vote_info.sha3()).unwrap())
|
||||
]).to_vec()
|
||||
}
|
||||
|
||||
fn insert_and_unlock(tap: &Arc<AccountProvider>, acc: &str) -> Address {
|
||||
let addr = tap.insert_account(acc.sha3(), acc).unwrap();
|
||||
tap.unlock_account_permanently(addr, acc.into()).unwrap();
|
||||
addr
|
||||
}
|
||||
|
||||
fn insert_and_register(tap: &Arc<AccountProvider>, engine: &Arc<Engine>, acc: &str) -> Address {
|
||||
fn insert_and_register(tap: &Arc<AccountProvider>, engine: &Engine, acc: &str) -> Address {
|
||||
let addr = insert_and_unlock(tap, acc);
|
||||
engine.set_signer(addr.clone(), acc.into());
|
||||
addr
|
||||
}
|
||||
|
||||
struct TestIo {
|
||||
received: RwLock<Vec<ClientIoMessage>>
|
||||
#[derive(Default)]
|
||||
struct TestNotify {
|
||||
messages: RwLock<Vec<Bytes>>,
|
||||
}
|
||||
|
||||
impl TestIo {
|
||||
fn new() -> Arc<Self> { Arc::new(TestIo { received: RwLock::new(Vec::new()) }) }
|
||||
}
|
||||
|
||||
impl IoHandler<ClientIoMessage> for TestIo {
|
||||
fn message(&self, _io: &IoContext<ClientIoMessage>, net_message: &ClientIoMessage) {
|
||||
self.received.write().push(net_message.clone());
|
||||
impl ChainNotify for TestNotify {
|
||||
fn broadcast(&self, data: Vec<u8>) {
|
||||
self.messages.write().push(data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -864,10 +863,10 @@ mod tests {
|
||||
fn can_generate_seal() {
|
||||
let (spec, tap) = setup();
|
||||
|
||||
let proposer = insert_and_register(&tap, &spec.engine, "1");
|
||||
let proposer = insert_and_register(&tap, spec.engine.as_ref(), "1");
|
||||
|
||||
let (b, seal) = propose_default(&spec, proposer);
|
||||
assert!(b.try_seal(spec.engine.as_ref(), seal).is_ok());
|
||||
assert!(b.lock().try_seal(spec.engine.as_ref(), seal).is_ok());
|
||||
spec.engine.stop();
|
||||
}
|
||||
|
||||
@ -875,10 +874,10 @@ mod tests {
|
||||
fn can_recognize_proposal() {
|
||||
let (spec, tap) = setup();
|
||||
|
||||
let proposer = insert_and_register(&tap, &spec.engine, "1");
|
||||
let proposer = insert_and_register(&tap, spec.engine.as_ref(), "1");
|
||||
|
||||
let (b, seal) = propose_default(&spec, proposer);
|
||||
let sealed = b.seal(spec.engine.as_ref(), seal).unwrap();
|
||||
let sealed = b.lock().seal(spec.engine.as_ref(), seal).unwrap();
|
||||
assert!(spec.engine.is_proposal(sealed.header()));
|
||||
spec.engine.stop();
|
||||
}
|
||||
@ -888,8 +887,8 @@ mod tests {
|
||||
let (spec, tap) = setup();
|
||||
let engine = spec.engine.clone();
|
||||
|
||||
let v0 = insert_and_register(&tap, &engine, "0");
|
||||
let v1 = insert_and_register(&tap, &engine, "1");
|
||||
let v0 = insert_and_register(&tap, engine.as_ref(), "0");
|
||||
let v1 = insert_and_register(&tap, engine.as_ref(), "1");
|
||||
|
||||
let h = 0;
|
||||
let r = 0;
|
||||
@ -898,57 +897,74 @@ mod tests {
|
||||
let (b, _) = propose_default(&spec, v1.clone());
|
||||
let proposal = Some(b.header().bare_hash());
|
||||
|
||||
// Register IoHandler remembers messages.
|
||||
let test_io = TestIo::new();
|
||||
let channel = IoChannel::to_handler(Arc::downgrade(&(test_io.clone() as Arc<IoHandler<ClientIoMessage>>)));
|
||||
engine.register_message_channel(channel);
|
||||
let client = generate_dummy_client(0);
|
||||
let notify = Arc::new(TestNotify::default());
|
||||
client.add_notify(notify.clone());
|
||||
engine.register_client(Arc::downgrade(&client));
|
||||
|
||||
let prevote_current = vote(&engine, |mh| tap.sign(v0, None, mh).map(H520::from), h, r, Step::Prevote, proposal);
|
||||
let prevote_current = vote(engine.as_ref(), |mh| tap.sign(v0, None, mh).map(H520::from), h, r, Step::Prevote, proposal);
|
||||
|
||||
let precommit_current = vote(&engine, |mh| tap.sign(v0, None, mh).map(H520::from), h, r, Step::Precommit, proposal);
|
||||
let precommit_current = vote(engine.as_ref(), |mh| tap.sign(v0, None, mh).map(H520::from), h, r, Step::Precommit, proposal);
|
||||
|
||||
let prevote_future = vote(&engine, |mh| tap.sign(v0, None, mh).map(H520::from), h + 1, r, Step::Prevote, proposal);
|
||||
let prevote_future = vote(engine.as_ref(), |mh| tap.sign(v0, None, mh).map(H520::from), h + 1, r, Step::Prevote, proposal);
|
||||
|
||||
// Relays all valid present and future messages.
|
||||
assert!(test_io.received.read().contains(&ClientIoMessage::BroadcastMessage(prevote_current)));
|
||||
assert!(test_io.received.read().contains(&ClientIoMessage::BroadcastMessage(precommit_current)));
|
||||
assert!(test_io.received.read().contains(&ClientIoMessage::BroadcastMessage(prevote_future)));
|
||||
assert!(notify.messages.read().contains(&prevote_current));
|
||||
assert!(notify.messages.read().contains(&precommit_current));
|
||||
assert!(notify.messages.read().contains(&prevote_future));
|
||||
engine.stop();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seal_submission() {
|
||||
let (spec, tap) = setup();
|
||||
let engine = spec.engine.clone();
|
||||
|
||||
let v0 = insert_and_register(&tap, &engine, "0");
|
||||
let v1 = insert_and_register(&tap, &engine, "1");
|
||||
use ethkey::{Generator, Random};
|
||||
use types::transaction::{Transaction, Action};
|
||||
use client::BlockChainClient;
|
||||
|
||||
let client = generate_dummy_client_with_spec_and_data(Spec::new_test_tendermint, 0, 0, &[]);
|
||||
let engine = client.engine();
|
||||
let tap = Arc::new(AccountProvider::transient_provider());
|
||||
|
||||
// Accounts for signing votes.
|
||||
let v0 = insert_and_unlock(&tap, "0");
|
||||
let v1 = insert_and_unlock(&tap, "1");
|
||||
|
||||
let notify = Arc::new(TestNotify::default());
|
||||
engine.register_account_provider(tap.clone());
|
||||
client.add_notify(notify.clone());
|
||||
engine.register_client(Arc::downgrade(&client));
|
||||
|
||||
let keypair = Random.generate().unwrap();
|
||||
let transaction = Transaction {
|
||||
action: Action::Create,
|
||||
value: U256::zero(),
|
||||
data: "3331600055".from_hex().unwrap(),
|
||||
gas: U256::from(100_000),
|
||||
gas_price: U256::zero(),
|
||||
nonce: U256::zero(),
|
||||
}.sign(keypair.secret(), None);
|
||||
client.miner().import_own_transaction(client.as_ref(), transaction.into()).unwrap();
|
||||
|
||||
client.miner().set_engine_signer(v1.clone(), "1".into()).unwrap();
|
||||
|
||||
// Propose
|
||||
let proposal = Some(client.miner().pending_block().unwrap().header.bare_hash());
|
||||
// Propose timeout
|
||||
engine.step();
|
||||
|
||||
let h = 1;
|
||||
let r = 0;
|
||||
|
||||
// Register IoHandler remembers messages.
|
||||
let test_io = TestIo::new();
|
||||
let channel = IoChannel::to_handler(Arc::downgrade(&(test_io.clone() as Arc<IoHandler<ClientIoMessage>>)));
|
||||
engine.register_message_channel(channel);
|
||||
|
||||
// Propose
|
||||
let (b, mut seal) = propose_default(&spec, v1.clone());
|
||||
let proposal = Some(b.header().bare_hash());
|
||||
engine.step();
|
||||
|
||||
// Prevote.
|
||||
vote(&engine, |mh| tap.sign(v1, None, mh).map(H520::from), h, r, Step::Prevote, proposal);
|
||||
vote(&engine, |mh| tap.sign(v0, None, mh).map(H520::from), h, r, Step::Prevote, proposal);
|
||||
vote(&engine, |mh| tap.sign(v1, None, mh).map(H520::from), h, r, Step::Precommit, proposal);
|
||||
vote(&engine, |mh| tap.sign(v0, None, mh).map(H520::from), h, r, Step::Precommit, proposal);
|
||||
vote(engine, |mh| tap.sign(v1, None, mh).map(H520::from), h, r, Step::Prevote, proposal);
|
||||
vote(engine, |mh| tap.sign(v0, None, mh).map(H520::from), h, r, Step::Prevote, proposal);
|
||||
vote(engine, |mh| tap.sign(v1, None, mh).map(H520::from), h, r, Step::Precommit, proposal);
|
||||
|
||||
seal[2] = precommit_signatures(&tap, h, r, Some(b.header().bare_hash()), v1, v0);
|
||||
let first = test_io.received.read().contains(&ClientIoMessage::SubmitSeal(proposal.unwrap(), seal.clone()));
|
||||
seal[2] = precommit_signatures(&tap, h, r, Some(b.header().bare_hash()), v0, v1);
|
||||
let second = test_io.received.read().contains(&ClientIoMessage::SubmitSeal(proposal.unwrap(), seal));
|
||||
assert_eq!(client.chain_info().best_block_number, 0);
|
||||
// Last precommit.
|
||||
vote(engine, |mh| tap.sign(v0, None, mh).map(H520::from), h, r, Step::Precommit, proposal);
|
||||
assert_eq!(client.chain_info().best_block_number, 1);
|
||||
|
||||
assert!(first ^ second);
|
||||
engine.stop();
|
||||
}
|
||||
}
|
||||
|
@ -18,33 +18,20 @@
|
||||
|
||||
use ethjson;
|
||||
use super::transition::TendermintTimeouts;
|
||||
use util::{Address, U256};
|
||||
use util::{U256, Uint};
|
||||
use time::Duration;
|
||||
|
||||
/// `Tendermint` params.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub struct TendermintParams {
|
||||
/// Gas limit divisor.
|
||||
pub gas_limit_bound_divisor: U256,
|
||||
/// List of authorities.
|
||||
pub authorities: Vec<Address>,
|
||||
/// Number of authorities.
|
||||
pub authority_n: usize,
|
||||
/// List of validators.
|
||||
pub validators: ethjson::spec::ValidatorSet,
|
||||
/// Timeout durations for different steps.
|
||||
pub timeouts: TendermintTimeouts,
|
||||
}
|
||||
|
||||
impl Default for TendermintParams {
|
||||
fn default() -> Self {
|
||||
let authorities = vec!["0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e".into(), "0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1".into()];
|
||||
let val_n = authorities.len();
|
||||
TendermintParams {
|
||||
gas_limit_bound_divisor: 0x0400.into(),
|
||||
authorities: authorities,
|
||||
authority_n: val_n,
|
||||
timeouts: TendermintTimeouts::default(),
|
||||
}
|
||||
}
|
||||
/// Block reward.
|
||||
pub block_reward: U256,
|
||||
}
|
||||
|
||||
fn to_duration(ms: ethjson::uint::Uint) -> Duration {
|
||||
@ -54,19 +41,17 @@ fn to_duration(ms: ethjson::uint::Uint) -> Duration {
|
||||
|
||||
impl From<ethjson::spec::TendermintParams> for TendermintParams {
|
||||
fn from(p: ethjson::spec::TendermintParams) -> Self {
|
||||
let val: Vec<_> = p.authorities.into_iter().map(Into::into).collect();
|
||||
let val_n = val.len();
|
||||
let dt = TendermintTimeouts::default();
|
||||
TendermintParams {
|
||||
gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(),
|
||||
authorities: val,
|
||||
authority_n: val_n,
|
||||
validators: p.validators,
|
||||
timeouts: TendermintTimeouts {
|
||||
propose: p.timeout_propose.map_or(dt.propose, to_duration),
|
||||
prevote: p.timeout_prevote.map_or(dt.prevote, to_duration),
|
||||
precommit: p.timeout_precommit.map_or(dt.precommit, to_duration),
|
||||
commit: p.timeout_commit.map_or(dt.commit, to_duration),
|
||||
},
|
||||
block_reward: p.block_reward.map_or_else(U256::zero, Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,17 @@ use super::{Tendermint, Step};
|
||||
use engines::Engine;
|
||||
|
||||
pub struct TransitionHandler {
|
||||
pub engine: Weak<Tendermint>,
|
||||
engine: Weak<Tendermint>,
|
||||
timeouts: TendermintTimeouts,
|
||||
}
|
||||
|
||||
impl TransitionHandler {
|
||||
pub fn new(engine: Weak<Tendermint>, timeouts: TendermintTimeouts) -> Self {
|
||||
TransitionHandler {
|
||||
engine: engine,
|
||||
timeouts: timeouts,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Base timeout of each step in ms.
|
||||
@ -67,9 +77,7 @@ fn set_timeout(io: &IoContext<Step>, timeout: Duration) {
|
||||
|
||||
impl IoHandler<Step> for TransitionHandler {
|
||||
fn initialize(&self, io: &IoContext<Step>) {
|
||||
if let Some(engine) = self.engine.upgrade() {
|
||||
set_timeout(io, engine.our_params.timeouts.propose)
|
||||
}
|
||||
set_timeout(io, self.timeouts.propose)
|
||||
}
|
||||
|
||||
fn timeout(&self, _io: &IoContext<Step>, timer: TimerToken) {
|
||||
@ -81,16 +89,14 @@ impl IoHandler<Step> for TransitionHandler {
|
||||
}
|
||||
|
||||
fn message(&self, io: &IoContext<Step>, next_step: &Step) {
|
||||
if let Some(engine) = self.engine.upgrade() {
|
||||
if let Err(io_err) = io.clear_timer(ENGINE_TIMEOUT_TOKEN) {
|
||||
warn!(target: "poa", "Could not remove consensus timer {}.", io_err)
|
||||
}
|
||||
match *next_step {
|
||||
Step::Propose => set_timeout(io, engine.our_params.timeouts.propose),
|
||||
Step::Prevote => set_timeout(io, engine.our_params.timeouts.prevote),
|
||||
Step::Precommit => set_timeout(io, engine.our_params.timeouts.precommit),
|
||||
Step::Commit => set_timeout(io, engine.our_params.timeouts.commit),
|
||||
};
|
||||
if let Err(io_err) = io.clear_timer(ENGINE_TIMEOUT_TOKEN) {
|
||||
warn!(target: "poa", "Could not remove consensus timer {}.", io_err)
|
||||
}
|
||||
match *next_step {
|
||||
Step::Propose => set_timeout(io, self.timeouts.propose),
|
||||
Step::Prevote => set_timeout(io, self.timeouts.prevote),
|
||||
Step::Precommit => set_timeout(io, self.timeouts.precommit),
|
||||
Step::Commit => set_timeout(io, self.timeouts.commit),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
218
ethcore/src/engines/validator_set/contract.rs
Normal file
218
ethcore/src/engines/validator_set/contract.rs
Normal file
@ -0,0 +1,218 @@
|
||||
// Copyright 2015, 2016 Parity Technologies (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/>.
|
||||
|
||||
/// Validator set maintained in a contract.
|
||||
|
||||
use std::sync::Weak;
|
||||
use util::*;
|
||||
use client::{Client, BlockChainClient};
|
||||
use client::chain_notify::ChainNotify;
|
||||
use super::ValidatorSet;
|
||||
use super::simple_list::SimpleList;
|
||||
|
||||
/// The validator contract should have the following interface:
|
||||
/// [{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}]
|
||||
pub struct ValidatorContract {
|
||||
address: Address,
|
||||
validators: RwLock<SimpleList>,
|
||||
provider: RwLock<Option<provider::Contract>>,
|
||||
}
|
||||
|
||||
impl ValidatorContract {
|
||||
pub fn new(contract_address: Address) -> Self {
|
||||
ValidatorContract {
|
||||
address: contract_address,
|
||||
validators: Default::default(),
|
||||
provider: RwLock::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Queries the state and updates the set of validators.
|
||||
pub fn update(&self) {
|
||||
if let Some(ref provider) = *self.provider.read() {
|
||||
match provider.get_validators() {
|
||||
Ok(new) => {
|
||||
debug!(target: "engine", "Set of validators obtained: {:?}", new);
|
||||
*self.validators.write() = SimpleList::new(new);
|
||||
},
|
||||
Err(s) => warn!(target: "engine", "Set of validators could not be updated: {}", s),
|
||||
}
|
||||
} else {
|
||||
warn!(target: "engine", "Set of validators could not be updated: no provider contract.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks validators on every block.
|
||||
impl ChainNotify for ValidatorContract {
|
||||
fn new_blocks(
|
||||
&self,
|
||||
_imported: Vec<H256>,
|
||||
_: Vec<H256>,
|
||||
_: Vec<H256>,
|
||||
_: Vec<H256>,
|
||||
_: Vec<H256>,
|
||||
_: Vec<Bytes>,
|
||||
_duration: u64) {
|
||||
self.update();
|
||||
}
|
||||
}
|
||||
|
||||
impl ValidatorSet for Arc<ValidatorContract> {
|
||||
fn contains(&self, address: &Address) -> bool {
|
||||
self.validators.read().contains(address)
|
||||
}
|
||||
|
||||
fn get(&self, nonce: usize) -> Address {
|
||||
self.validators.read().get(nonce).clone()
|
||||
}
|
||||
|
||||
fn count(&self) -> usize {
|
||||
self.validators.read().count()
|
||||
}
|
||||
|
||||
fn register_call_contract(&self, client: Weak<Client>) {
|
||||
if let Some(c) = client.upgrade() {
|
||||
c.add_notify(self.clone());
|
||||
}
|
||||
{
|
||||
*self.provider.write() = Some(provider::Contract::new(self.address, move |a, d| client.upgrade().ok_or("No client!".into()).and_then(|c| c.call_contract(a, d))));
|
||||
}
|
||||
self.update();
|
||||
}
|
||||
}
|
||||
|
||||
mod provider {
|
||||
// Autogenerated from JSON contract definition using Rust contract convertor.
|
||||
#![allow(unused_imports)]
|
||||
use std::string::String;
|
||||
use std::result::Result;
|
||||
use std::fmt;
|
||||
use {util, ethabi};
|
||||
use util::{FixedHash, Uint};
|
||||
|
||||
pub struct Contract {
|
||||
contract: ethabi::Contract,
|
||||
address: util::Address,
|
||||
do_call: Box<Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send + Sync + 'static>,
|
||||
}
|
||||
impl Contract {
|
||||
pub fn new<F>(address: util::Address, do_call: F) -> Self where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send + Sync + 'static {
|
||||
Contract {
|
||||
contract: ethabi::Contract::new(ethabi::Interface::load(b"[{\"constant\":true,\"inputs\":[],\"name\":\"getValidators\",\"outputs\":[{\"name\":\"\",\"type\":\"address[]\"}],\"payable\":false,\"type\":\"function\"}]").expect("JSON is autogenerated; qed")),
|
||||
address: address,
|
||||
do_call: Box::new(do_call),
|
||||
}
|
||||
}
|
||||
fn as_string<T: fmt::Debug>(e: T) -> String { format!("{:?}", e) }
|
||||
|
||||
/// Auto-generated from: `{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}`
|
||||
#[allow(dead_code)]
|
||||
pub fn get_validators(&self) -> Result<Vec<util::Address>, String> {
|
||||
let call = self.contract.function("getValidators".into()).map_err(Self::as_string)?;
|
||||
let data = call.encode_call(
|
||||
vec![]
|
||||
).map_err(Self::as_string)?;
|
||||
let output = call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||
let mut result = output.into_iter().rev().collect::<Vec<_>>();
|
||||
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_array().and_then(|v| v.into_iter().map(|a| a.to_address()).collect::<Option<Vec<[u8; 20]>>>()).ok_or("Invalid type returned")?; r.into_iter().map(|a| util::Address::from(a)).collect::<Vec<_>>() }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use util::*;
|
||||
use spec::Spec;
|
||||
use account_provider::AccountProvider;
|
||||
use transaction::{Transaction, Action};
|
||||
use client::{BlockChainClient, EngineClient};
|
||||
use miner::MinerService;
|
||||
use tests::helpers::generate_dummy_client_with_spec_and_data;
|
||||
use super::super::ValidatorSet;
|
||||
use super::ValidatorContract;
|
||||
|
||||
#[test]
|
||||
fn fetches_validators() {
|
||||
let client = generate_dummy_client_with_spec_and_data(Spec::new_validator_contract, 0, 0, &[]);
|
||||
let vc = Arc::new(ValidatorContract::new(Address::from_str("0000000000000000000000000000000000000005").unwrap()));
|
||||
vc.register_call_contract(Arc::downgrade(&client));
|
||||
vc.update();
|
||||
assert!(vc.contains(&Address::from_str("7d577a597b2742b498cb5cf0c26cdcd726d39e6e").unwrap()));
|
||||
assert!(vc.contains(&Address::from_str("82a978b3f5962a5b0957d9ee9eef472ee55b42f1").unwrap()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn changes_validators() {
|
||||
let tap = Arc::new(AccountProvider::transient_provider());
|
||||
let v0 = tap.insert_account("1".sha3(), "").unwrap();
|
||||
let v1 = tap.insert_account("0".sha3(), "").unwrap();
|
||||
let spec_factory = || {
|
||||
let spec = Spec::new_validator_contract();
|
||||
spec.engine.register_account_provider(tap.clone());
|
||||
spec
|
||||
};
|
||||
let client = generate_dummy_client_with_spec_and_data(spec_factory, 0, 0, &[]);
|
||||
client.engine().register_client(Arc::downgrade(&client));
|
||||
let validator_contract = Address::from_str("0000000000000000000000000000000000000005").unwrap();
|
||||
|
||||
client.miner().set_engine_signer(v1, "".into()).unwrap();
|
||||
// Remove "1" validator.
|
||||
let tx = Transaction {
|
||||
nonce: 0.into(),
|
||||
gas_price: 0.into(),
|
||||
gas: 500_000.into(),
|
||||
action: Action::Call(validator_contract),
|
||||
value: 0.into(),
|
||||
data: "f94e18670000000000000000000000000000000000000000000000000000000000000001".from_hex().unwrap(),
|
||||
}.sign(&"1".sha3(), None);
|
||||
client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap();
|
||||
client.update_sealing();
|
||||
assert_eq!(client.chain_info().best_block_number, 1);
|
||||
// Add "1" validator back in.
|
||||
let tx = Transaction {
|
||||
nonce: 1.into(),
|
||||
gas_price: 0.into(),
|
||||
gas: 500_000.into(),
|
||||
action: Action::Call(validator_contract),
|
||||
value: 0.into(),
|
||||
data: "4d238c8e00000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1".from_hex().unwrap(),
|
||||
}.sign(&"1".sha3(), None);
|
||||
client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap();
|
||||
client.update_sealing();
|
||||
// The transaction is not yet included so still unable to seal.
|
||||
assert_eq!(client.chain_info().best_block_number, 1);
|
||||
|
||||
// Switch to the validator that is still there.
|
||||
client.miner().set_engine_signer(v0, "".into()).unwrap();
|
||||
client.update_sealing();
|
||||
assert_eq!(client.chain_info().best_block_number, 2);
|
||||
// Switch back to the added validator, since the state is updated.
|
||||
client.miner().set_engine_signer(v1, "".into()).unwrap();
|
||||
let tx = Transaction {
|
||||
nonce: 2.into(),
|
||||
gas_price: 0.into(),
|
||||
gas: 21000.into(),
|
||||
action: Action::Call(Address::default()),
|
||||
value: 0.into(),
|
||||
data: Vec::new(),
|
||||
}.sign(&"1".sha3(), None);
|
||||
client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap();
|
||||
client.update_sealing();
|
||||
// Able to seal again.
|
||||
assert_eq!(client.chain_info().best_block_number, 3);
|
||||
}
|
||||
}
|
46
ethcore/src/engines/validator_set/mod.rs
Normal file
46
ethcore/src/engines/validator_set/mod.rs
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright 2015, 2016 Parity Technologies (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/>.
|
||||
|
||||
/// Validator lists.
|
||||
|
||||
mod simple_list;
|
||||
mod contract;
|
||||
|
||||
use std::sync::Weak;
|
||||
use util::{Address, Arc};
|
||||
use ethjson::spec::ValidatorSet as ValidatorSpec;
|
||||
use client::Client;
|
||||
use self::simple_list::SimpleList;
|
||||
use self::contract::ValidatorContract;
|
||||
|
||||
/// Creates a validator set from spec.
|
||||
pub fn new_validator_set(spec: ValidatorSpec) -> Box<ValidatorSet + Send + Sync> {
|
||||
match spec {
|
||||
ValidatorSpec::List(list) => Box::new(SimpleList::new(list.into_iter().map(Into::into).collect())),
|
||||
ValidatorSpec::Contract(address) => Box::new(Arc::new(ValidatorContract::new(address.into()))),
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ValidatorSet {
|
||||
/// Checks if a given address is a validator.
|
||||
fn contains(&self, address: &Address) -> bool;
|
||||
/// Draws an validator nonce modulo number of validators.
|
||||
fn get(&self, nonce: usize) -> Address;
|
||||
/// Returns the current number of validators.
|
||||
fn count(&self) -> usize;
|
||||
/// Allows blockchain state access.
|
||||
fn register_call_contract(&self, _client: Weak<Client>) {}
|
||||
}
|
68
ethcore/src/engines/validator_set/simple_list.rs
Normal file
68
ethcore/src/engines/validator_set/simple_list.rs
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright 2015, 2016 Parity Technologies (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/>.
|
||||
|
||||
/// Preconfigured validator list.
|
||||
|
||||
use util::Address;
|
||||
use super::ValidatorSet;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Default)]
|
||||
pub struct SimpleList {
|
||||
validators: Vec<Address>,
|
||||
validator_n: usize,
|
||||
}
|
||||
|
||||
impl SimpleList {
|
||||
pub fn new(validators: Vec<Address>) -> Self {
|
||||
SimpleList {
|
||||
validator_n: validators.len(),
|
||||
validators: validators,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ValidatorSet for SimpleList {
|
||||
fn contains(&self, address: &Address) -> bool {
|
||||
self.validators.contains(address)
|
||||
}
|
||||
|
||||
fn get(&self, nonce: usize) -> Address {
|
||||
self.validators.get(nonce % self.validator_n).expect("There are validator_n authorities; taking number modulo validator_n gives number in validator_n range; qed").clone()
|
||||
}
|
||||
|
||||
fn count(&self) -> usize {
|
||||
self.validator_n
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
use util::Address;
|
||||
use super::super::ValidatorSet;
|
||||
use super::SimpleList;
|
||||
|
||||
#[test]
|
||||
fn simple_list() {
|
||||
let a1 = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
|
||||
let a2 = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||
let list = SimpleList::new(vec![a1.clone(), a2.clone()]);
|
||||
assert!(list.contains(&a1));
|
||||
assert_eq!(list.get(0), a1);
|
||||
assert_eq!(list.get(1), a2);
|
||||
assert_eq!(list.get(2), a1);
|
||||
}
|
||||
}
|
@ -45,7 +45,7 @@ pub fn contract_address(address: &Address, nonce: &U256) -> Address {
|
||||
}
|
||||
|
||||
/// Transaction execution options.
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Copy, Clone, PartialEq)]
|
||||
pub struct TransactOptions {
|
||||
/// Enable call tracing.
|
||||
pub tracing: bool,
|
||||
|
@ -33,7 +33,7 @@ fn test_trie(json: &[u8], trie: TrieSpec) -> Vec<String> {
|
||||
let key: Vec<u8> = key.into();
|
||||
let value: Vec<u8> = value.map_or_else(Vec::new, Into::into);
|
||||
t.insert(&key, &value)
|
||||
.expect(&format!("Trie test '{:?}' failed due to internal error", name))
|
||||
.expect(&format!("Trie test '{:?}' failed due to internal error", name));
|
||||
}
|
||||
|
||||
if *t.root() != test.root.into() {
|
||||
|
@ -23,6 +23,7 @@ use std::cell::Cell;
|
||||
use transaction::{SignedTransaction, Action};
|
||||
use transient_hashmap::TransientHashMap;
|
||||
use miner::{TransactionQueue, TransactionImportResult, TransactionOrigin, AccountDetails};
|
||||
use miner::transaction_queue::QueuingInstant;
|
||||
use error::{Error, TransactionError};
|
||||
use util::{Uint, U256, H256, Address, Hashable};
|
||||
|
||||
@ -78,6 +79,7 @@ impl BanningTransactionQueue {
|
||||
pub fn add_with_banlist<F, G>(
|
||||
&mut self,
|
||||
transaction: SignedTransaction,
|
||||
time: QueuingInstant,
|
||||
account_details: &F,
|
||||
gas_estimator: &G,
|
||||
) -> Result<TransactionImportResult, Error> where
|
||||
@ -115,7 +117,7 @@ impl BanningTransactionQueue {
|
||||
}
|
||||
}
|
||||
}
|
||||
self.queue.add(transaction, TransactionOrigin::External, None, account_details, gas_estimator)
|
||||
self.queue.add(transaction, TransactionOrigin::External, time, None, account_details, gas_estimator)
|
||||
}
|
||||
|
||||
/// Ban transaction with given hash.
|
||||
@ -158,7 +160,7 @@ impl BanningTransactionQueue {
|
||||
Threshold::BanAfter(threshold) if count > threshold => {
|
||||
// Banlist the sender.
|
||||
// Remove all transactions from the queue.
|
||||
self.remove_all(address, !U256::zero());
|
||||
self.cull(address, !U256::zero());
|
||||
true
|
||||
},
|
||||
_ => false
|
||||
@ -263,7 +265,7 @@ mod tests {
|
||||
let mut txq = queue();
|
||||
|
||||
// when
|
||||
txq.queue().add(tx, TransactionOrigin::External, None, &default_account_details, &gas_required).unwrap();
|
||||
txq.queue().add(tx, TransactionOrigin::External, 0, None, &default_account_details, &gas_required).unwrap();
|
||||
|
||||
// then
|
||||
// should also deref to queue
|
||||
@ -279,12 +281,12 @@ mod tests {
|
||||
let banlist1 = txq.ban_sender(tx.sender().unwrap());
|
||||
assert!(!banlist1, "Threshold not reached yet.");
|
||||
// Insert once
|
||||
let import1 = txq.add_with_banlist(tx.clone(), &default_account_details, &gas_required).unwrap();
|
||||
let import1 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required).unwrap();
|
||||
assert_eq!(import1, TransactionImportResult::Current);
|
||||
|
||||
// when
|
||||
let banlist2 = txq.ban_sender(tx.sender().unwrap());
|
||||
let import2 = txq.add_with_banlist(tx.clone(), &default_account_details, &gas_required);
|
||||
let import2 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required);
|
||||
|
||||
// then
|
||||
assert!(banlist2, "Threshold should be reached - banned.");
|
||||
@ -303,12 +305,12 @@ mod tests {
|
||||
let banlist1 = txq.ban_recipient(recipient);
|
||||
assert!(!banlist1, "Threshold not reached yet.");
|
||||
// Insert once
|
||||
let import1 = txq.add_with_banlist(tx.clone(), &default_account_details, &gas_required).unwrap();
|
||||
let import1 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required).unwrap();
|
||||
assert_eq!(import1, TransactionImportResult::Current);
|
||||
|
||||
// when
|
||||
let banlist2 = txq.ban_recipient(recipient);
|
||||
let import2 = txq.add_with_banlist(tx.clone(), &default_account_details, &gas_required);
|
||||
let import2 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required);
|
||||
|
||||
// then
|
||||
assert!(banlist2, "Threshold should be reached - banned.");
|
||||
@ -325,12 +327,12 @@ mod tests {
|
||||
let banlist1 = txq.ban_codehash(codehash);
|
||||
assert!(!banlist1, "Threshold not reached yet.");
|
||||
// Insert once
|
||||
let import1 = txq.add_with_banlist(tx.clone(), &default_account_details, &gas_required).unwrap();
|
||||
let import1 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required).unwrap();
|
||||
assert_eq!(import1, TransactionImportResult::Current);
|
||||
|
||||
// when
|
||||
let banlist2 = txq.ban_codehash(codehash);
|
||||
let import2 = txq.add_with_banlist(tx.clone(), &default_account_details, &gas_required);
|
||||
let import2 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required);
|
||||
|
||||
// then
|
||||
assert!(banlist2, "Threshold should be reached - banned.");
|
||||
|
@ -417,15 +417,12 @@ impl Miner {
|
||||
|
||||
let block = open_block.close();
|
||||
|
||||
let fetch_account = |a: &Address| AccountDetails {
|
||||
nonce: chain.latest_nonce(a),
|
||||
balance: chain.latest_balance(a),
|
||||
};
|
||||
let fetch_nonce = |a: &Address| chain.latest_nonce(a);
|
||||
|
||||
{
|
||||
let mut queue = self.transaction_queue.lock();
|
||||
for hash in invalid_transactions {
|
||||
queue.remove_invalid(&hash, &fetch_account);
|
||||
queue.remove_invalid(&hash, &fetch_nonce);
|
||||
}
|
||||
for hash in transactions_to_penalize {
|
||||
queue.penalize(&hash);
|
||||
@ -597,6 +594,8 @@ impl Miner {
|
||||
let schedule = chain.latest_schedule();
|
||||
let gas_required = |tx: &SignedTransaction| tx.gas_required(&schedule).into();
|
||||
let best_block_header = chain.best_block_header().decode();
|
||||
let insertion_time = chain.chain_info().best_block_number;
|
||||
|
||||
transactions.into_iter()
|
||||
.map(|tx| {
|
||||
if chain.transaction_block(TransactionId::Hash(tx.hash())).is_some() {
|
||||
@ -618,10 +617,10 @@ impl Miner {
|
||||
|
||||
match origin {
|
||||
TransactionOrigin::Local | TransactionOrigin::RetractedBlock => {
|
||||
transaction_queue.add(tx, origin, min_block, &fetch_account, &gas_required)
|
||||
transaction_queue.add(tx, origin, insertion_time, min_block, &fetch_account, &gas_required)
|
||||
},
|
||||
TransactionOrigin::External => {
|
||||
transaction_queue.add_with_banlist(tx, &fetch_account, &gas_required)
|
||||
transaction_queue.add_with_banlist(tx, insertion_time, &fetch_account, &gas_required)
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -674,7 +673,7 @@ impl MinerService for Miner {
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, CallError> {
|
||||
fn call(&self, client: &MiningBlockChainClient, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, CallError> {
|
||||
let sealing_work = self.sealing_work.lock();
|
||||
match sealing_work.queue.peek_last_ref() {
|
||||
Some(work) => {
|
||||
@ -682,7 +681,7 @@ impl MinerService for Miner {
|
||||
|
||||
// TODO: merge this code with client.rs's fn call somwhow.
|
||||
let header = block.header();
|
||||
let last_hashes = Arc::new(chain.last_hashes());
|
||||
let last_hashes = Arc::new(client.last_hashes());
|
||||
let env_info = EnvInfo {
|
||||
number: header.number(),
|
||||
author: *header.author(),
|
||||
@ -707,16 +706,14 @@ impl MinerService for Miner {
|
||||
state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty);
|
||||
}
|
||||
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
|
||||
let mut ret = Executive::new(&mut state, &env_info, &*self.engine, chain.vm_factory()).transact(t, options)?;
|
||||
let mut ret = Executive::new(&mut state, &env_info, &*self.engine, client.vm_factory()).transact(t, options)?;
|
||||
|
||||
// TODO gav move this into Executive.
|
||||
ret.state_diff = original_state.map(|original| state.diff_from(original));
|
||||
|
||||
Ok(ret)
|
||||
},
|
||||
None => {
|
||||
chain.call(t, BlockId::Latest, analytics)
|
||||
}
|
||||
None => client.call(t, BlockId::Latest, analytics)
|
||||
}
|
||||
}
|
||||
|
||||
@ -765,9 +762,17 @@ impl MinerService for Miner {
|
||||
if let Some(ref ap) = self.accounts {
|
||||
ap.sign(address.clone(), Some(password.clone()), Default::default())?;
|
||||
}
|
||||
let mut sealing_work = self.sealing_work.lock();
|
||||
sealing_work.enabled = self.engine.is_sealer(&address).unwrap_or(false);
|
||||
*self.author.write() = address;
|
||||
// Limit the scope of the locks.
|
||||
{
|
||||
let mut sealing_work = self.sealing_work.lock();
|
||||
sealing_work.enabled = self.engine.is_sealer(&address).unwrap_or(false);
|
||||
*self.author.write() = address;
|
||||
}
|
||||
// --------------------------------------------------------------------------
|
||||
// | NOTE Code below may require author and sealing_work locks |
|
||||
// | (some `Engine`s call `EngineClient.update_sealing()`) |.
|
||||
// | Make sure to release the locks before calling that method. |
|
||||
// --------------------------------------------------------------------------
|
||||
self.engine.set_signer(address, password);
|
||||
}
|
||||
Ok(())
|
||||
@ -1141,8 +1146,13 @@ impl MinerService for Miner {
|
||||
|
||||
// ...and at the end remove the old ones
|
||||
{
|
||||
let fetch_account = |a: &Address| AccountDetails {
|
||||
nonce: chain.latest_nonce(a),
|
||||
balance: chain.latest_balance(a),
|
||||
};
|
||||
let time = chain.chain_info().best_block_number;
|
||||
let mut transaction_queue = self.transaction_queue.lock();
|
||||
transaction_queue.remove_old(|sender| chain.latest_nonce(sender));
|
||||
transaction_queue.remove_old(&fetch_account, time);
|
||||
}
|
||||
|
||||
if enacted.len() > 0 {
|
||||
|
@ -82,7 +82,7 @@ impl PriceInfo {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[test] #[ignore]
|
||||
fn should_get_price_info() {
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
@ -51,8 +51,8 @@
|
||||
//! let gas_estimator = |_tx: &SignedTransaction| 2.into();
|
||||
//!
|
||||
//! let mut txq = TransactionQueue::default();
|
||||
//! txq.add(st2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
//! txq.add(st1.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
//! txq.add(st2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
//! txq.add(st1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
//!
|
||||
//! // Check status
|
||||
//! assert_eq!(txq.status().pending, 2);
|
||||
@ -64,7 +64,7 @@
|
||||
//!
|
||||
//! // And when transaction is removed (but nonce haven't changed)
|
||||
//! // it will move subsequent transactions to future
|
||||
//! txq.remove_invalid(&st1.hash(), &default_account_details);
|
||||
//! txq.remove_invalid(&st1.hash(), &|_| 10.into());
|
||||
//! assert_eq!(txq.status().pending, 0);
|
||||
//! assert_eq!(txq.status().future, 1);
|
||||
//! assert_eq!(txq.top_transactions().len(), 0);
|
||||
@ -78,11 +78,11 @@
|
||||
//! - When it's removed from `future` - all `future` transactions heights are recalculated and then
|
||||
//! we check if the transactions should go to `current` (comparing state nonce)
|
||||
//! - When it's removed from `current` - all transactions from this sender (`current` & `future`) are recalculated.
|
||||
//! 3. `remove_all` is used to inform the queue about client (state) nonce changes.
|
||||
//! 3. `cull` is used to inform the queue about client (state) nonce changes.
|
||||
//! - It removes all transactions (either from `current` or `future`) with nonce < client nonce
|
||||
//! - It moves matching `future` transactions to `current`
|
||||
//! 4. `remove_old` is used as convenient method to update the state nonce for all senders in the queue.
|
||||
//! - Invokes `remove_all` with latest state nonce for all senders.
|
||||
//! - Invokes `cull` with latest state nonce for all senders.
|
||||
|
||||
use std::ops::Deref;
|
||||
use std::cmp::Ordering;
|
||||
@ -258,16 +258,19 @@ struct VerifiedTransaction {
|
||||
transaction: SignedTransaction,
|
||||
/// Transaction origin.
|
||||
origin: TransactionOrigin,
|
||||
/// Insertion time
|
||||
insertion_time: QueuingInstant,
|
||||
/// Delay until specifid block.
|
||||
min_block: Option<BlockNumber>,
|
||||
}
|
||||
|
||||
impl VerifiedTransaction {
|
||||
fn new(transaction: SignedTransaction, origin: TransactionOrigin, min_block: Option<BlockNumber>) -> Result<Self, Error> {
|
||||
fn new(transaction: SignedTransaction, origin: TransactionOrigin, time: QueuingInstant, min_block: Option<BlockNumber>) -> Result<Self, Error> {
|
||||
transaction.sender()?;
|
||||
Ok(VerifiedTransaction {
|
||||
transaction: transaction,
|
||||
origin: origin,
|
||||
insertion_time: time,
|
||||
min_block: min_block,
|
||||
})
|
||||
}
|
||||
@ -283,6 +286,10 @@ impl VerifiedTransaction {
|
||||
fn sender(&self) -> Address {
|
||||
self.transaction.sender().expect("Sender is verified in new; qed")
|
||||
}
|
||||
|
||||
fn cost(&self) -> U256 {
|
||||
self.transaction.value + self.transaction.gas_price * self.transaction.gas
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@ -488,6 +495,10 @@ pub enum PrioritizationStrategy {
|
||||
GasFactorAndGasPrice,
|
||||
}
|
||||
|
||||
/// Point in time when transaction was inserted.
|
||||
pub type QueuingInstant = BlockNumber;
|
||||
const DEFAULT_QUEUING_PERIOD: BlockNumber = 128;
|
||||
|
||||
/// `TransactionQueue` implementation
|
||||
pub struct TransactionQueue {
|
||||
/// Prioritization strategy for this queue
|
||||
@ -498,6 +509,10 @@ pub struct TransactionQueue {
|
||||
tx_gas_limit: U256,
|
||||
/// Current gas limit (block gas limit * factor). Transactions above the limit will not be accepted (default to !0)
|
||||
gas_limit: U256,
|
||||
/// Maximal time transaction may occupy the queue.
|
||||
/// When we reach `max_time_in_queue / 2^3` we re-validate
|
||||
/// account balance.
|
||||
max_time_in_queue: QueuingInstant,
|
||||
/// Priority queue for transactions that can go to block
|
||||
current: TransactionSet,
|
||||
/// Priority queue for transactions that has been received but are not yet valid to go to block
|
||||
@ -545,6 +560,7 @@ impl TransactionQueue {
|
||||
minimal_gas_price: U256::zero(),
|
||||
tx_gas_limit: tx_gas_limit,
|
||||
gas_limit: !U256::zero(),
|
||||
max_time_in_queue: DEFAULT_QUEUING_PERIOD,
|
||||
current: current,
|
||||
future: future,
|
||||
by_hash: HashMap::new(),
|
||||
@ -624,6 +640,7 @@ impl TransactionQueue {
|
||||
&mut self,
|
||||
tx: SignedTransaction,
|
||||
origin: TransactionOrigin,
|
||||
time: QueuingInstant,
|
||||
min_block: Option<BlockNumber>,
|
||||
fetch_account: &F,
|
||||
gas_estimator: &G,
|
||||
@ -635,7 +652,7 @@ impl TransactionQueue {
|
||||
let hash = tx.hash();
|
||||
let cloned_tx = tx.clone();
|
||||
|
||||
let result = self.add_internal(tx, origin, min_block, fetch_account, gas_estimator);
|
||||
let result = self.add_internal(tx, origin, time, min_block, fetch_account, gas_estimator);
|
||||
match result {
|
||||
Ok(TransactionImportResult::Current) => {
|
||||
self.local_transactions.mark_pending(hash);
|
||||
@ -656,7 +673,7 @@ impl TransactionQueue {
|
||||
}
|
||||
result
|
||||
} else {
|
||||
self.add_internal(tx, origin, min_block, fetch_account, gas_estimator)
|
||||
self.add_internal(tx, origin, time, min_block, fetch_account, gas_estimator)
|
||||
}
|
||||
}
|
||||
|
||||
@ -665,6 +682,7 @@ impl TransactionQueue {
|
||||
&mut self,
|
||||
tx: SignedTransaction,
|
||||
origin: TransactionOrigin,
|
||||
time: QueuingInstant,
|
||||
min_block: Option<BlockNumber>,
|
||||
fetch_account: &F,
|
||||
gas_estimator: &G,
|
||||
@ -734,10 +752,10 @@ impl TransactionQueue {
|
||||
// Verify signature
|
||||
tx.check_low_s()?;
|
||||
|
||||
let vtx = VerifiedTransaction::new(tx, origin, min_block)?;
|
||||
let vtx = VerifiedTransaction::new(tx, origin, time, min_block)?;
|
||||
let client_account = fetch_account(&vtx.sender());
|
||||
|
||||
let cost = vtx.transaction.value + vtx.transaction.gas_price * vtx.transaction.gas;
|
||||
let cost = vtx.cost();
|
||||
if client_account.balance < cost {
|
||||
trace!(target: "txqueue",
|
||||
"Dropping transaction without sufficient balance: {:?} ({} < {})",
|
||||
@ -759,7 +777,7 @@ impl TransactionQueue {
|
||||
|
||||
/// Removes all transactions from particular sender up to (excluding) given client (state) nonce.
|
||||
/// Client (State) Nonce = next valid nonce for this sender.
|
||||
pub fn remove_all(&mut self, sender: Address, client_nonce: U256) {
|
||||
pub fn cull(&mut self, sender: Address, client_nonce: U256) {
|
||||
// Check if there is anything in current...
|
||||
let should_check_in_current = self.current.by_address.row(&sender)
|
||||
// If nonce == client_nonce nothing is changed
|
||||
@ -775,11 +793,11 @@ impl TransactionQueue {
|
||||
return;
|
||||
}
|
||||
|
||||
self.remove_all_internal(sender, client_nonce);
|
||||
self.cull_internal(sender, client_nonce);
|
||||
}
|
||||
|
||||
/// Always updates future and moves transactions from current to future.
|
||||
fn remove_all_internal(&mut self, sender: Address, client_nonce: U256) {
|
||||
fn cull_internal(&mut self, sender: Address, client_nonce: U256) {
|
||||
// We will either move transaction to future or remove it completely
|
||||
// so there will be no transactions from this sender in current
|
||||
self.last_nonces.remove(&sender);
|
||||
@ -794,16 +812,46 @@ impl TransactionQueue {
|
||||
}
|
||||
|
||||
/// Checks the current nonce for all transactions' senders in the queue and removes the old transactions.
|
||||
pub fn remove_old<F>(&mut self, fetch_nonce: F) where
|
||||
F: Fn(&Address) -> U256,
|
||||
pub fn remove_old<F>(&mut self, fetch_account: &F, current_time: QueuingInstant) where
|
||||
F: Fn(&Address) -> AccountDetails,
|
||||
{
|
||||
let senders = self.current.by_address.keys()
|
||||
.chain(self.future.by_address.keys())
|
||||
.cloned()
|
||||
.collect::<HashSet<_>>();
|
||||
.map(|sender| (*sender, fetch_account(sender)))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
for sender in senders {
|
||||
self.remove_all(sender, fetch_nonce(&sender));
|
||||
for (sender, details) in senders.iter() {
|
||||
self.cull(*sender, details.nonce);
|
||||
}
|
||||
|
||||
let max_time = self.max_time_in_queue;
|
||||
let balance_check = max_time >> 3;
|
||||
// Clear transactions occupying the queue too long
|
||||
let invalid = self.by_hash.iter()
|
||||
.filter(|&(_, ref tx)| !tx.origin.is_local())
|
||||
.map(|(hash, tx)| (hash, tx, current_time.saturating_sub(tx.insertion_time)))
|
||||
.filter_map(|(hash, tx, time_diff)| {
|
||||
if time_diff > max_time {
|
||||
return Some(*hash);
|
||||
}
|
||||
|
||||
if time_diff > balance_check {
|
||||
return match senders.get(&tx.sender()) {
|
||||
Some(details) if tx.cost() > details.balance => {
|
||||
Some(*hash)
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
None
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let fetch_nonce = |a: &Address| senders.get(a)
|
||||
.expect("We fetch details for all senders from both current and future")
|
||||
.nonce;
|
||||
for hash in invalid {
|
||||
self.remove_invalid(&hash, &fetch_nonce);
|
||||
}
|
||||
}
|
||||
|
||||
@ -851,8 +899,8 @@ impl TransactionQueue {
|
||||
/// so transactions left in queue are processed according to client nonce.
|
||||
///
|
||||
/// If gap is introduced marks subsequent transactions as future
|
||||
pub fn remove_invalid<T>(&mut self, transaction_hash: &H256, fetch_account: &T)
|
||||
where T: Fn(&Address) -> AccountDetails {
|
||||
pub fn remove_invalid<T>(&mut self, transaction_hash: &H256, fetch_nonce: &T)
|
||||
where T: Fn(&Address) -> U256 {
|
||||
|
||||
assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len());
|
||||
let transaction = self.by_hash.remove(transaction_hash);
|
||||
@ -864,7 +912,7 @@ impl TransactionQueue {
|
||||
let transaction = transaction.expect("None is tested in early-exit condition above; qed");
|
||||
let sender = transaction.sender();
|
||||
let nonce = transaction.nonce();
|
||||
let current_nonce = fetch_account(&sender).nonce;
|
||||
let current_nonce = fetch_nonce(&sender);
|
||||
|
||||
trace!(target: "txqueue", "Removing invalid transaction: {:?}", transaction.hash());
|
||||
|
||||
@ -889,7 +937,7 @@ impl TransactionQueue {
|
||||
if order.is_some() {
|
||||
// This will keep consistency in queue
|
||||
// Moves all to future and then promotes a batch from current:
|
||||
self.remove_all_internal(sender, current_nonce);
|
||||
self.cull_internal(sender, current_nonce);
|
||||
assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len());
|
||||
return;
|
||||
}
|
||||
@ -1039,7 +1087,7 @@ impl TransactionQueue {
|
||||
|
||||
/// Finds transaction in the queue by hash (if any)
|
||||
pub fn find(&self, hash: &H256) -> Option<SignedTransaction> {
|
||||
match self.by_hash.get(hash) { Some(transaction_ref) => Some(transaction_ref.transaction.clone()), None => None }
|
||||
self.by_hash.get(hash).map(|tx| tx.transaction.clone())
|
||||
}
|
||||
|
||||
/// Removes all elements (in any state) from the queue
|
||||
@ -1388,14 +1436,14 @@ mod test {
|
||||
let (tx1, tx2) = new_tx_pair(123.into(), 1.into(), 1.into(), 0.into());
|
||||
let sender = tx1.sender().unwrap();
|
||||
let nonce = tx1.nonce;
|
||||
txq.add(tx1.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
assert_eq!(txq.status().pending, 2);
|
||||
assert_eq!(txq.last_nonce(&sender), Some(nonce + 1.into()));
|
||||
|
||||
// when
|
||||
let tx = new_tx(123.into(), 1.into());
|
||||
let res = txq.add(tx.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator);
|
||||
let res = txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
||||
|
||||
// then
|
||||
// No longer the case as we don't even consider a transaction that isn't above a full
|
||||
@ -1427,12 +1475,12 @@ mod test {
|
||||
gas_limit: !U256::zero(),
|
||||
};
|
||||
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
let tx1 = VerifiedTransaction::new(tx1, TransactionOrigin::External, None).unwrap();
|
||||
let tx2 = VerifiedTransaction::new(tx2, TransactionOrigin::External, None).unwrap();
|
||||
let tx1 = VerifiedTransaction::new(tx1, TransactionOrigin::External, 0, None).unwrap();
|
||||
let tx2 = VerifiedTransaction::new(tx2, TransactionOrigin::External, 0, None).unwrap();
|
||||
let mut by_hash = {
|
||||
let mut x = HashMap::new();
|
||||
let tx1 = VerifiedTransaction::new(tx1.transaction.clone(), TransactionOrigin::External, None).unwrap();
|
||||
let tx2 = VerifiedTransaction::new(tx2.transaction.clone(), TransactionOrigin::External, None).unwrap();
|
||||
let tx1 = VerifiedTransaction::new(tx1.transaction.clone(), TransactionOrigin::External, 0, None).unwrap();
|
||||
let tx2 = VerifiedTransaction::new(tx2.transaction.clone(), TransactionOrigin::External, 0, None).unwrap();
|
||||
x.insert(tx1.hash(), tx1);
|
||||
x.insert(tx2.hash(), tx2);
|
||||
x
|
||||
@ -1470,12 +1518,12 @@ mod test {
|
||||
// Create two transactions with same nonce
|
||||
// (same hash)
|
||||
let (tx1, tx2) = new_tx_pair_default(0.into(), 0.into());
|
||||
let tx1 = VerifiedTransaction::new(tx1, TransactionOrigin::External, None).unwrap();
|
||||
let tx2 = VerifiedTransaction::new(tx2, TransactionOrigin::External, None).unwrap();
|
||||
let tx1 = VerifiedTransaction::new(tx1, TransactionOrigin::External, 0, None).unwrap();
|
||||
let tx2 = VerifiedTransaction::new(tx2, TransactionOrigin::External, 0, None).unwrap();
|
||||
let by_hash = {
|
||||
let mut x = HashMap::new();
|
||||
let tx1 = VerifiedTransaction::new(tx1.transaction.clone(), TransactionOrigin::External, None).unwrap();
|
||||
let tx2 = VerifiedTransaction::new(tx2.transaction.clone(), TransactionOrigin::External, None).unwrap();
|
||||
let tx1 = VerifiedTransaction::new(tx1.transaction.clone(), TransactionOrigin::External, 0, None).unwrap();
|
||||
let tx2 = VerifiedTransaction::new(tx2.transaction.clone(), TransactionOrigin::External, 0, None).unwrap();
|
||||
x.insert(tx1.hash(), tx1);
|
||||
x.insert(tx2.hash(), tx2);
|
||||
x
|
||||
@ -1517,10 +1565,10 @@ mod test {
|
||||
gas_limit: !U256::zero(),
|
||||
};
|
||||
let tx = new_tx_default();
|
||||
let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External, None).unwrap();
|
||||
let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External, 0, None).unwrap();
|
||||
let order1 = TransactionOrder::for_transaction(&tx1, 0.into(), 1.into(), PrioritizationStrategy::GasPriceOnly);
|
||||
assert!(set.insert(tx1.sender(), tx1.nonce(), order1).is_none());
|
||||
let tx2 = VerifiedTransaction::new(tx, TransactionOrigin::External, None).unwrap();
|
||||
let tx2 = VerifiedTransaction::new(tx, TransactionOrigin::External, 0, None).unwrap();
|
||||
let order2 = TransactionOrder::for_transaction(&tx2, 0.into(), 1.into(), PrioritizationStrategy::GasPriceOnly);
|
||||
assert!(set.insert(tx2.sender(), tx2.nonce(), order2).is_some());
|
||||
}
|
||||
@ -1537,7 +1585,7 @@ mod test {
|
||||
|
||||
assert_eq!(set.gas_price_entry_limit(), 0.into());
|
||||
let tx = new_tx_default();
|
||||
let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External, None).unwrap();
|
||||
let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External, 0, None).unwrap();
|
||||
let order1 = TransactionOrder::for_transaction(&tx1, 0.into(), 1.into(), PrioritizationStrategy::GasPriceOnly);
|
||||
assert!(set.insert(tx1.sender(), tx1.nonce(), order1.clone()).is_none());
|
||||
assert_eq!(set.gas_price_entry_limit(), 2.into());
|
||||
@ -1552,12 +1600,12 @@ mod test {
|
||||
!U256::zero() };
|
||||
|
||||
// First insert one transaction to future
|
||||
let res = txq.add(tx, TransactionOrigin::External, None, &prev_nonce, &gas_estimator);
|
||||
let res = txq.add(tx, TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator);
|
||||
assert_eq!(res.unwrap(), TransactionImportResult::Future);
|
||||
assert_eq!(txq.status().future, 1);
|
||||
|
||||
// now import second transaction to current
|
||||
let res = txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator);
|
||||
let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
||||
|
||||
// and then there should be only one transaction in current (the one with higher gas_price)
|
||||
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
||||
@ -1577,12 +1625,12 @@ mod test {
|
||||
!U256::zero() };
|
||||
|
||||
// First insert one transaction to future
|
||||
let res = txq.add(tx.clone(), TransactionOrigin::External, None, &prev_nonce, &gas_estimator);
|
||||
let res = txq.add(tx.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator);
|
||||
assert_eq!(res.unwrap(), TransactionImportResult::Future);
|
||||
assert_eq!(txq.status().future, 1);
|
||||
|
||||
// now import second transaction to current
|
||||
let res = txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator);
|
||||
let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
||||
|
||||
// then
|
||||
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
||||
@ -1601,7 +1649,7 @@ mod test {
|
||||
let tx = new_tx_default();
|
||||
|
||||
// when
|
||||
let res = txq.add(tx, TransactionOrigin::External, None, &default_account_details, &gas_estimator);
|
||||
let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
||||
|
||||
// then
|
||||
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
||||
@ -1620,10 +1668,10 @@ mod test {
|
||||
txq.set_minimal_gas_price(15.into());
|
||||
|
||||
// when
|
||||
let res1 = txq.add(tx1, TransactionOrigin::External, None, &default_account_details, &gas_estimator);
|
||||
let res2 = txq.add(tx2, TransactionOrigin::External, None, &default_account_details, &gas_estimator);
|
||||
let res3 = txq.add(tx3, TransactionOrigin::External, None, &default_account_details, &gas_estimator);
|
||||
let res4 = txq.add(tx4, TransactionOrigin::External, None, &default_account_details, &gas_estimator);
|
||||
let res1 = txq.add(tx1, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
||||
let res2 = txq.add(tx2, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
||||
let res3 = txq.add(tx3, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
||||
let res4 = txq.add(tx4, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
||||
|
||||
// then
|
||||
assert_eq!(res1.unwrap(), TransactionImportResult::Current);
|
||||
@ -1654,10 +1702,10 @@ mod test {
|
||||
txq.set_minimal_gas_price(15.into());
|
||||
|
||||
// when
|
||||
let res1 = txq.add(tx1, TransactionOrigin::External, None, &default_account_details, &gas_estimator);
|
||||
let res2 = txq.add(tx2, TransactionOrigin::External, None, &default_account_details, &gas_estimator);
|
||||
let res3 = txq.add(tx3, TransactionOrigin::External, None, &default_account_details, &gas_estimator);
|
||||
let res4 = txq.add(tx4, TransactionOrigin::External, None, &default_account_details, &gas_estimator);
|
||||
let res1 = txq.add(tx1, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
||||
let res2 = txq.add(tx2, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
||||
let res3 = txq.add(tx3, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
||||
let res4 = txq.add(tx4, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
||||
|
||||
// then
|
||||
assert_eq!(res1.unwrap(), TransactionImportResult::Current);
|
||||
@ -1700,7 +1748,7 @@ mod test {
|
||||
txq.set_gas_limit(limit);
|
||||
|
||||
// when
|
||||
let res = txq.add(tx, TransactionOrigin::External, None, &default_account_details, &gas_estimator);
|
||||
let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
||||
|
||||
// then
|
||||
assert_eq!(unwrap_tx_err(res), TransactionError::GasLimitExceeded {
|
||||
@ -1724,7 +1772,7 @@ mod test {
|
||||
};
|
||||
|
||||
// when
|
||||
let res = txq.add(tx, TransactionOrigin::External, None, &account, &gas_estimator);
|
||||
let res = txq.add(tx, TransactionOrigin::External, 0, None, &account, &gas_estimator);
|
||||
|
||||
// then
|
||||
assert_eq!(unwrap_tx_err(res), TransactionError::InsufficientBalance {
|
||||
@ -1744,7 +1792,7 @@ mod test {
|
||||
txq.set_minimal_gas_price(tx.gas_price + U256::one());
|
||||
|
||||
// when
|
||||
let res = txq.add(tx, TransactionOrigin::External, None, &default_account_details, &gas_estimator);
|
||||
let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
||||
|
||||
// then
|
||||
assert_eq!(unwrap_tx_err(res), TransactionError::InsufficientGasPrice {
|
||||
@ -1764,7 +1812,7 @@ mod test {
|
||||
txq.set_minimal_gas_price(tx.gas_price + U256::one());
|
||||
|
||||
// when
|
||||
let res = txq.add(tx, TransactionOrigin::Local, None, &default_account_details, &gas_estimator);
|
||||
let res = txq.add(tx, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator);
|
||||
|
||||
// then
|
||||
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
||||
@ -1794,7 +1842,7 @@ mod test {
|
||||
rlp::decode(s.as_raw())
|
||||
};
|
||||
// when
|
||||
let res = txq.add(stx, TransactionOrigin::External, None, &default_account_details, &gas_estimator);
|
||||
let res = txq.add(stx, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
||||
|
||||
// then
|
||||
assert!(res.is_err());
|
||||
@ -1808,8 +1856,8 @@ mod test {
|
||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
|
||||
// when
|
||||
txq.add(tx.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
|
||||
// then
|
||||
let top = txq.top_transactions();
|
||||
@ -1828,9 +1876,9 @@ mod test {
|
||||
|
||||
// when
|
||||
// first insert the one with higher gas price
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
// then the one with lower gas price, but local
|
||||
txq.add(tx.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
|
||||
// then
|
||||
let top = txq.top_transactions();
|
||||
@ -1847,15 +1895,15 @@ mod test {
|
||||
// the second one has same nonce but higher `gas_price`
|
||||
let (_, tx0) = new_similar_tx_pair();
|
||||
|
||||
txq.add(tx0.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx1.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx0.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
// the one with higher gas price is first
|
||||
assert_eq!(txq.top_transactions()[0], tx0);
|
||||
assert_eq!(txq.top_transactions()[1], tx1);
|
||||
|
||||
// when
|
||||
// insert second as local
|
||||
txq.add(tx2.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
|
||||
// then
|
||||
// the order should be updated
|
||||
@ -1874,9 +1922,9 @@ mod test {
|
||||
|
||||
// when
|
||||
// first insert local one with higher gas price
|
||||
txq.add(tx2.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
// then the one with lower gas price, but from retracted block
|
||||
txq.add(tx.clone(), TransactionOrigin::RetractedBlock, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx.clone(), TransactionOrigin::RetractedBlock, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
|
||||
// then
|
||||
let top = txq.top_transactions();
|
||||
@ -1892,8 +1940,8 @@ mod test {
|
||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
|
||||
// when
|
||||
txq.add(tx.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
|
||||
// then
|
||||
let top = txq.top_transactions();
|
||||
@ -1912,10 +1960,10 @@ mod test {
|
||||
let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into());
|
||||
|
||||
// insert everything
|
||||
txq.add(txa.clone(), TransactionOrigin::External, None, &prev_nonce, &gas_estimator).unwrap();
|
||||
txq.add(txb.clone(), TransactionOrigin::External, None, &prev_nonce, &gas_estimator).unwrap();
|
||||
txq.add(tx1.clone(), TransactionOrigin::External, None, &prev_nonce, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, None, &prev_nonce, &gas_estimator).unwrap();
|
||||
txq.add(txa.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap();
|
||||
txq.add(txb.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap();
|
||||
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap();
|
||||
|
||||
assert_eq!(txq.status().future, 4);
|
||||
|
||||
@ -1940,10 +1988,10 @@ mod test {
|
||||
let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into());
|
||||
|
||||
// insert everything
|
||||
txq.add(txa.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(txb.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx1.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(txa.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(txb.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx1.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
|
||||
let top = txq.top_transactions();
|
||||
assert_eq!(top[0], tx1);
|
||||
@ -1973,10 +2021,10 @@ mod test {
|
||||
let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into());
|
||||
|
||||
// insert everything
|
||||
txq.add(txa.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(txb.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx1.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(txa.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(txb.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
|
||||
let top = txq.top_transactions();
|
||||
assert_eq!(top[0], tx1);
|
||||
@ -2005,8 +2053,8 @@ mod test {
|
||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
|
||||
// when
|
||||
txq.add(tx.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
|
||||
// then
|
||||
let top = txq.pending_hashes();
|
||||
@ -2023,8 +2071,8 @@ mod test {
|
||||
let (tx, tx2) = new_tx_pair_default(2.into(), 0.into());
|
||||
|
||||
// when
|
||||
let res1 = txq.add(tx.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
let res2 = txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
let res1 = txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
let res2 = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(res1, TransactionImportResult::Current);
|
||||
@ -2045,8 +2093,8 @@ mod test {
|
||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
|
||||
// when
|
||||
let res1 = txq.add(tx.clone(), TransactionOrigin::External, Some(1), &default_account_details, &gas_estimator).unwrap();
|
||||
let res2 = txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
let res1 = txq.add(tx.clone(), TransactionOrigin::External, 0, Some(1), &default_account_details, &gas_estimator).unwrap();
|
||||
let res2 = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(res1, TransactionImportResult::Current);
|
||||
@ -2067,12 +2115,12 @@ mod test {
|
||||
let mut txq = TransactionQueue::default();
|
||||
|
||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
txq.add(tx.clone(), TransactionOrigin::External, None, &prev_nonce, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, None, &prev_nonce, &gas_estimator).unwrap();
|
||||
txq.add(tx.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap();
|
||||
assert_eq!(txq.status().future, 2);
|
||||
|
||||
// when
|
||||
txq.remove_all(tx.sender().unwrap(), next2_nonce);
|
||||
txq.cull(tx.sender().unwrap(), next2_nonce);
|
||||
// should remove both transactions since they are not valid
|
||||
|
||||
// then
|
||||
@ -2090,13 +2138,13 @@ mod test {
|
||||
let tx1 = new_unsigned_tx(124.into(), default_gas_val(), 1.into()).sign(secret, None);
|
||||
let tx2 = new_unsigned_tx(125.into(), default_gas_val(), 1.into()).sign(secret, None);
|
||||
|
||||
txq.add(tx, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
assert_eq!(txq.status().pending, 1);
|
||||
txq.add(tx2, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
assert_eq!(txq.status().future, 1);
|
||||
|
||||
// when
|
||||
txq.add(tx1, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx1, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
|
||||
// then
|
||||
let stats = txq.status();
|
||||
@ -2112,14 +2160,14 @@ mod test {
|
||||
// given
|
||||
let mut txq2 = TransactionQueue::default();
|
||||
let (tx, tx2) = new_tx_pair_default(3.into(), 0.into());
|
||||
txq2.add(tx.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq2.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq2.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq2.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
assert_eq!(txq2.status().pending, 1);
|
||||
assert_eq!(txq2.status().future, 1);
|
||||
|
||||
// when
|
||||
txq2.remove_all(tx.sender().unwrap(), tx.nonce + U256::one());
|
||||
txq2.remove_all(tx2.sender().unwrap(), tx2.nonce + U256::one());
|
||||
txq2.cull(tx.sender().unwrap(), tx.nonce + U256::one());
|
||||
txq2.cull(tx2.sender().unwrap(), tx2.nonce + U256::one());
|
||||
|
||||
|
||||
// then
|
||||
@ -2134,14 +2182,14 @@ mod test {
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
let tx3 = new_tx_default();
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
assert_eq!(txq.status().future, 1);
|
||||
txq.add(tx3.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
assert_eq!(txq.status().pending, 3);
|
||||
|
||||
// when
|
||||
txq.remove_invalid(&tx.hash(), &default_account_details);
|
||||
txq.remove_invalid(&tx.hash(), &|_| default_nonce());
|
||||
|
||||
// then
|
||||
let stats = txq.status();
|
||||
@ -2156,8 +2204,8 @@ mod test {
|
||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
|
||||
// add
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
let stats = txq.status();
|
||||
assert_eq!(stats.pending, 2);
|
||||
|
||||
@ -2176,11 +2224,11 @@ mod test {
|
||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
let sender = tx.sender().unwrap();
|
||||
let nonce = tx.nonce;
|
||||
txq.add(tx.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
assert_eq!(txq.status().pending, 1);
|
||||
|
||||
// when
|
||||
let res = txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator);
|
||||
let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
||||
|
||||
// then
|
||||
let t = txq.top_transactions();
|
||||
@ -2197,14 +2245,14 @@ mod test {
|
||||
txq.current.set_limit(10);
|
||||
let (tx1, tx2) = new_tx_pair_default(4.into(), 1.into());
|
||||
let (tx3, tx4) = new_tx_pair_default(4.into(), 2.into());
|
||||
txq.add(tx1.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx3.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
assert_eq!(txq.status().pending, 2);
|
||||
|
||||
// when
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
assert_eq!(txq.status().future, 1);
|
||||
txq.add(tx4.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx4.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(txq.status().future, 1);
|
||||
@ -2215,11 +2263,11 @@ mod test {
|
||||
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 (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2));
|
||||
txq.add(tx1.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx3.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
// limited by gas
|
||||
txq.add(tx4.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap_err();
|
||||
txq.add(tx4.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap_err();
|
||||
assert_eq!(txq.status().pending, 2);
|
||||
}
|
||||
|
||||
@ -2229,12 +2277,12 @@ mod test {
|
||||
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 (tx5, _) = new_tx_pair_default(U256::from(1), U256::from(2));
|
||||
txq.add(tx1.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx1.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
// Not accepted because of limit
|
||||
txq.add(tx5.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap_err();
|
||||
txq.add(tx3.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx4.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx5.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap_err();
|
||||
txq.add(tx3.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx4.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
assert_eq!(txq.status().pending, 4);
|
||||
}
|
||||
|
||||
@ -2246,7 +2294,7 @@ mod test {
|
||||
let fetch_last_nonce = |_a: &Address| AccountDetails { nonce: last_nonce, balance: !U256::zero() };
|
||||
|
||||
// when
|
||||
let res = txq.add(tx, TransactionOrigin::External, None, &fetch_last_nonce, &gas_estimator);
|
||||
let res = txq.add(tx, TransactionOrigin::External, 0, None, &fetch_last_nonce, &gas_estimator);
|
||||
|
||||
// then
|
||||
assert_eq!(unwrap_tx_err(res), TransactionError::Old);
|
||||
@ -2262,12 +2310,12 @@ mod test {
|
||||
balance: !U256::zero() };
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (_tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
assert_eq!(txq.status().future, 1);
|
||||
assert_eq!(txq.status().pending, 0);
|
||||
|
||||
// when
|
||||
let res = txq.add(tx2.clone(), TransactionOrigin::External, None, &nonce, &gas_estimator);
|
||||
let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &nonce, &gas_estimator);
|
||||
|
||||
// then
|
||||
assert_eq!(unwrap_tx_err(res), TransactionError::AlreadyImported);
|
||||
@ -2281,15 +2329,15 @@ mod test {
|
||||
// given
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
txq.add(tx1.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
assert_eq!(txq.status().pending, 2);
|
||||
|
||||
// when
|
||||
txq.remove_invalid(&tx1.hash(), &default_account_details);
|
||||
txq.remove_invalid(&tx1.hash(), &|_| default_nonce());
|
||||
assert_eq!(txq.status().pending, 0);
|
||||
assert_eq!(txq.status().future, 1);
|
||||
txq.add(tx1.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
|
||||
// then
|
||||
let stats = txq.status();
|
||||
@ -2303,15 +2351,15 @@ mod test {
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
let tx3 = new_tx_default();
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
assert_eq!(txq.status().future, 1);
|
||||
txq.add(tx3.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
assert_eq!(txq.status().pending, 3);
|
||||
|
||||
// when
|
||||
let sender = tx.sender().unwrap();
|
||||
txq.remove_all(sender, default_nonce() + U256::one());
|
||||
txq.cull(sender, default_nonce() + U256::one());
|
||||
|
||||
// then
|
||||
let stats = txq.status();
|
||||
@ -2333,8 +2381,8 @@ mod test {
|
||||
};
|
||||
|
||||
// when
|
||||
txq.add(tx, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
|
||||
// then
|
||||
let stats = txq.status();
|
||||
@ -2361,10 +2409,10 @@ mod test {
|
||||
};
|
||||
|
||||
// when
|
||||
txq.add(tx1, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx1, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
assert_eq!(txq.status().future, 1);
|
||||
txq.add(tx0, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx0, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
|
||||
// then
|
||||
let stats = txq.status();
|
||||
@ -2376,18 +2424,16 @@ mod test {
|
||||
#[test]
|
||||
fn should_recalculate_height_when_removing_from_future() {
|
||||
// given
|
||||
let previous_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance:
|
||||
!U256::zero() };
|
||||
let next_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce + U256::one(), balance:
|
||||
!U256::zero() };
|
||||
let previous_nonce = |a: &Address|
|
||||
AccountDetails { nonce: default_account_details(a).nonce - U256::one(), balance: !U256::zero() };
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
txq.add(tx1.clone(), TransactionOrigin::External, None, &previous_nonce, &gas_estimator).unwrap();
|
||||
txq.add(tx2, TransactionOrigin::External, None, &previous_nonce, &gas_estimator).unwrap();
|
||||
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &previous_nonce, &gas_estimator).unwrap();
|
||||
txq.add(tx2, TransactionOrigin::External, 0, None, &previous_nonce, &gas_estimator).unwrap();
|
||||
assert_eq!(txq.status().future, 2);
|
||||
|
||||
// when
|
||||
txq.remove_invalid(&tx1.hash(), &next_nonce);
|
||||
txq.remove_invalid(&tx1.hash(), &|_| default_nonce() + 1.into());
|
||||
|
||||
// then
|
||||
let stats = txq.status();
|
||||
@ -2414,7 +2460,7 @@ mod test {
|
||||
let details = |_a: &Address| AccountDetails { nonce: nonce, balance: !U256::zero() };
|
||||
|
||||
// when
|
||||
txq.add(tx, TransactionOrigin::External, None, &details, &gas_estimator).unwrap();
|
||||
txq.add(tx, TransactionOrigin::External, 0, None, &details, &gas_estimator).unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(txq.last_nonce(&from), Some(nonce));
|
||||
@ -2429,17 +2475,17 @@ mod test {
|
||||
let details1 = |_a: &Address| AccountDetails { nonce: nonce1, balance: !U256::zero() };
|
||||
|
||||
// Insert first transaction
|
||||
txq.add(tx1, TransactionOrigin::External, None, &details1, &gas_estimator).unwrap();
|
||||
txq.add(tx1, TransactionOrigin::External, 0, None, &details1, &gas_estimator).unwrap();
|
||||
|
||||
// when
|
||||
txq.remove_all(tx2.sender().unwrap(), nonce2 + U256::one());
|
||||
txq.cull(tx2.sender().unwrap(), nonce2 + U256::one());
|
||||
|
||||
// then
|
||||
assert!(txq.top_transactions().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_valid_last_nonce_after_remove_all() {
|
||||
fn should_return_valid_last_nonce_after_cull() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx1, tx2) = new_tx_pair_default(4.into(), 0.into());
|
||||
@ -2449,11 +2495,11 @@ mod test {
|
||||
|
||||
// when
|
||||
// Insert first transaction
|
||||
assert_eq!(txq.add(tx1, TransactionOrigin::External, None, &details1, &gas_estimator).unwrap(), TransactionImportResult::Current);
|
||||
assert_eq!(txq.add(tx1, TransactionOrigin::External, 0, None, &details1, &gas_estimator).unwrap(), TransactionImportResult::Current);
|
||||
// Second should go to future
|
||||
assert_eq!(txq.add(tx2, TransactionOrigin::External, None, &details1, &gas_estimator).unwrap(), TransactionImportResult::Future);
|
||||
assert_eq!(txq.add(tx2, TransactionOrigin::External, 0, None, &details1, &gas_estimator).unwrap(), TransactionImportResult::Future);
|
||||
// Now block is imported
|
||||
txq.remove_all(sender, nonce2 - U256::from(1));
|
||||
txq.cull(sender, nonce2 - U256::from(1));
|
||||
// tx2 should be not be promoted to current
|
||||
assert_eq!(txq.status().pending, 0);
|
||||
assert_eq!(txq.status().future, 1);
|
||||
@ -2470,9 +2516,9 @@ mod test {
|
||||
assert_eq!(txq.has_local_pending_transactions(), false);
|
||||
|
||||
// when
|
||||
assert_eq!(txq.add(tx1, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap(), TransactionImportResult::Current);
|
||||
assert_eq!(txq.add(tx1, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(), TransactionImportResult::Current);
|
||||
assert_eq!(txq.has_local_pending_transactions(), false);
|
||||
assert_eq!(txq.add(tx2, TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap(), TransactionImportResult::Current);
|
||||
assert_eq!(txq.add(tx2, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(), TransactionImportResult::Current);
|
||||
|
||||
// then
|
||||
assert_eq!(txq.has_local_pending_transactions(), true);
|
||||
@ -2487,8 +2533,8 @@ mod test {
|
||||
default_account_details(a).balance };
|
||||
|
||||
// when
|
||||
assert_eq!(txq.add(tx2, TransactionOrigin::External, None, &prev_nonce, &gas_estimator).unwrap(), TransactionImportResult::Future);
|
||||
assert_eq!(txq.add(tx1.clone(), TransactionOrigin::External, None, &prev_nonce, &gas_estimator).unwrap(), TransactionImportResult::Future);
|
||||
assert_eq!(txq.add(tx2, TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap(), TransactionImportResult::Future);
|
||||
assert_eq!(txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap(), TransactionImportResult::Future);
|
||||
|
||||
// then
|
||||
assert_eq!(txq.future.by_priority.len(), 1);
|
||||
@ -2513,14 +2559,14 @@ mod test {
|
||||
(tx.sign(secret, None), tx2.sign(secret, None), tx2_2.sign(secret, None), tx3.sign(secret, None))
|
||||
};
|
||||
let sender = tx1.sender().unwrap();
|
||||
txq.add(tx1, TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2, TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx3, TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx1, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx3, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
assert_eq!(txq.future.by_priority.len(), 0);
|
||||
assert_eq!(txq.current.by_priority.len(), 3);
|
||||
|
||||
// when
|
||||
let res = txq.add(tx2_2, TransactionOrigin::Local, None, &default_account_details, &gas_estimator);
|
||||
let res = txq.add(tx2_2, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator);
|
||||
|
||||
// then
|
||||
assert_eq!(txq.last_nonce(&sender).unwrap(), 125.into());
|
||||
@ -2536,8 +2582,8 @@ mod test {
|
||||
let high_gas = |_: &SignedTransaction| 100_001.into();
|
||||
|
||||
// when
|
||||
let res1 = txq.add(tx1, TransactionOrigin::Local, None, &default_account_details, &gas_estimator);
|
||||
let res2 = txq.add(tx2, TransactionOrigin::Local, None, &default_account_details, &high_gas);
|
||||
let res1 = txq.add(tx1, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator);
|
||||
let res2 = txq.add(tx2, TransactionOrigin::Local, 0, None, &default_account_details, &high_gas);
|
||||
|
||||
// then
|
||||
assert_eq!(res1.unwrap(), TransactionImportResult::Current);
|
||||
@ -2554,20 +2600,45 @@ mod test {
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
let (tx3, tx4) = new_tx_pair_default(1.into(), 0.into());
|
||||
let nonce1 = tx1.nonce;
|
||||
let next_nonce = |_: &Address|
|
||||
AccountDetails { nonce: default_nonce() + U256::one(), balance: !U256::zero() };
|
||||
|
||||
// Insert all transactions
|
||||
txq.add(tx1, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx3, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx4, TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx1, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx3, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx4, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
assert_eq!(txq.top_transactions().len(), 4);
|
||||
|
||||
// when
|
||||
txq.remove_old(|_| nonce1 + U256::one());
|
||||
txq.remove_old(&next_nonce, 0);
|
||||
|
||||
// then
|
||||
assert_eq!(txq.top_transactions().len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_remove_out_of_date_transactions_occupying_queue() {
|
||||
// given
|
||||
let mut txq = TransactionQueue::default();
|
||||
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||
let (tx3, tx4) = new_tx_pair_default(2.into(), 0.into());
|
||||
|
||||
// Insert all transactions
|
||||
txq.add(tx1.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx2, TransactionOrigin::External, 5, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx3.clone(), TransactionOrigin::External, 10, None, &default_account_details, &gas_estimator).unwrap();
|
||||
txq.add(tx4, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
||||
assert_eq!(txq.top_transactions().len(), 3);
|
||||
assert_eq!(txq.future_transactions().len(), 1);
|
||||
|
||||
// when
|
||||
txq.remove_old(&default_account_details, 9 + super::DEFAULT_QUEUING_PERIOD);
|
||||
|
||||
// then
|
||||
assert_eq!(txq.top_transactions().len(), 2);
|
||||
assert_eq!(txq.future_transactions().len(), 0);
|
||||
assert_eq!(txq.top_transactions(), vec![tx1, tx3]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ use util::*;
|
||||
use io::*;
|
||||
use spec::Spec;
|
||||
use error::*;
|
||||
use client::{Client, BlockChainClient, MiningBlockChainClient, ClientConfig, ChainNotify};
|
||||
use client::{Client, ClientConfig, ChainNotify};
|
||||
use miner::Miner;
|
||||
use snapshot::ManifestData;
|
||||
use snapshot::service::{Service as SnapshotService, ServiceParams as SnapServiceParams};
|
||||
@ -46,12 +46,6 @@ pub enum ClientIoMessage {
|
||||
FeedBlockChunk(H256, Bytes),
|
||||
/// Take a snapshot for the block with given number.
|
||||
TakeSnapshot(u64),
|
||||
/// Trigger sealing update (useful for internal sealing).
|
||||
UpdateSealing,
|
||||
/// Submit seal (useful for internal sealing).
|
||||
SubmitSeal(H256, Vec<Bytes>),
|
||||
/// Broadcast a message to the network.
|
||||
BroadcastMessage(Bytes),
|
||||
/// New consensus message received.
|
||||
NewMessage(Bytes)
|
||||
}
|
||||
@ -114,7 +108,7 @@ impl ClientService {
|
||||
});
|
||||
io_service.register_handler(client_io)?;
|
||||
|
||||
spec.engine.register_message_channel(io_service.channel());
|
||||
spec.engine.register_client(Arc::downgrade(&client));
|
||||
|
||||
let stop_guard = ::devtools::StopGuard::new();
|
||||
run_ipc(ipc_path, client.clone(), snapshot.clone(), stop_guard.share());
|
||||
@ -221,9 +215,6 @@ impl IoHandler<ClientIoMessage> for ClientIoHandler {
|
||||
debug!(target: "snapshot", "Failed to initialize periodic snapshot thread: {:?}", e);
|
||||
}
|
||||
},
|
||||
ClientIoMessage::UpdateSealing => self.client.update_sealing(),
|
||||
ClientIoMessage::SubmitSeal(ref hash, ref seal) => self.client.submit_seal(*hash, seal.clone()),
|
||||
ClientIoMessage::BroadcastMessage(ref message) => self.client.broadcast_consensus_message(message.clone()),
|
||||
ClientIoMessage::NewMessage(ref message) => if let Err(e) = self.client.engine().handle_message(message) {
|
||||
trace!(target: "poa", "Invalid message received: {}", e);
|
||||
},
|
||||
|
@ -17,16 +17,17 @@
|
||||
//! Account state encoding and decoding
|
||||
|
||||
use account_db::{AccountDB, AccountDBMut};
|
||||
use basic_account::BasicAccount;
|
||||
use snapshot::Error;
|
||||
|
||||
use util::{U256, FixedHash, H256, Bytes, HashDB, SHA3_EMPTY, SHA3_NULL_RLP};
|
||||
use util::trie::{TrieDB, Trie};
|
||||
use rlp::{Rlp, RlpStream, Stream, UntrustedRlp, View};
|
||||
use rlp::{RlpStream, Stream, UntrustedRlp, View};
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
// An empty account -- these are replaced with RLP null data for a space optimization.
|
||||
const ACC_EMPTY: Account = Account {
|
||||
const ACC_EMPTY: BasicAccount = BasicAccount {
|
||||
nonce: U256([0, 0, 0, 0]),
|
||||
balance: U256([0, 0, 0, 0]),
|
||||
storage_root: SHA3_NULL_RLP,
|
||||
@ -59,165 +60,121 @@ impl CodeState {
|
||||
}
|
||||
}
|
||||
|
||||
// An alternate account structure from ::account::Account.
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub struct Account {
|
||||
nonce: U256,
|
||||
balance: U256,
|
||||
storage_root: H256,
|
||||
code_hash: H256,
|
||||
// walk the account's storage trie, returning an RLP item containing the
|
||||
// account properties and the storage.
|
||||
pub fn to_fat_rlp(acc: &BasicAccount, acct_db: &AccountDB, used_code: &mut HashSet<H256>) -> Result<Bytes, Error> {
|
||||
if acc == &ACC_EMPTY {
|
||||
return Ok(::rlp::NULL_RLP.to_vec());
|
||||
}
|
||||
|
||||
let db = TrieDB::new(acct_db, &acc.storage_root)?;
|
||||
|
||||
let mut pairs = Vec::new();
|
||||
|
||||
for item in db.iter()? {
|
||||
let (k, v) = item?;
|
||||
pairs.push((k, v));
|
||||
}
|
||||
|
||||
let mut stream = RlpStream::new_list(pairs.len());
|
||||
|
||||
for (k, v) in pairs {
|
||||
stream.begin_list(2).append(&k).append(&&*v);
|
||||
}
|
||||
|
||||
let pairs_rlp = stream.out();
|
||||
|
||||
let mut account_stream = RlpStream::new_list(5);
|
||||
account_stream.append(&acc.nonce)
|
||||
.append(&acc.balance);
|
||||
|
||||
// [has_code, code_hash].
|
||||
if acc.code_hash == SHA3_EMPTY {
|
||||
account_stream.append(&CodeState::Empty.raw()).append_empty_data();
|
||||
} else if used_code.contains(&acc.code_hash) {
|
||||
account_stream.append(&CodeState::Hash.raw()).append(&acc.code_hash);
|
||||
} else {
|
||||
match acct_db.get(&acc.code_hash) {
|
||||
Some(c) => {
|
||||
used_code.insert(acc.code_hash.clone());
|
||||
account_stream.append(&CodeState::Inline.raw()).append(&&*c);
|
||||
}
|
||||
None => {
|
||||
warn!("code lookup failed during snapshot");
|
||||
account_stream.append(&false).append_empty_data();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
account_stream.append_raw(&pairs_rlp, 1);
|
||||
|
||||
Ok(account_stream.out())
|
||||
}
|
||||
|
||||
impl Account {
|
||||
// decode the account from rlp.
|
||||
pub fn from_thin_rlp(rlp: &[u8]) -> Self {
|
||||
let r: Rlp = Rlp::new(rlp);
|
||||
// decode a fat rlp, and rebuild the storage trie as we go.
|
||||
// returns the account structure along with its newly recovered code,
|
||||
// if it exists.
|
||||
pub fn from_fat_rlp(
|
||||
acct_db: &mut AccountDBMut,
|
||||
rlp: UntrustedRlp,
|
||||
) -> Result<(BasicAccount, Option<Bytes>), Error> {
|
||||
use util::{TrieDBMut, TrieMut};
|
||||
|
||||
Account {
|
||||
nonce: r.val_at(0),
|
||||
balance: r.val_at(1),
|
||||
storage_root: r.val_at(2),
|
||||
code_hash: r.val_at(3),
|
||||
// check for special case of empty account.
|
||||
if rlp.is_empty() {
|
||||
return Ok((ACC_EMPTY, None));
|
||||
}
|
||||
|
||||
let nonce = rlp.val_at(0)?;
|
||||
let balance = rlp.val_at(1)?;
|
||||
let code_state: CodeState = {
|
||||
let raw: u8 = rlp.val_at(2)?;
|
||||
CodeState::from(raw)?
|
||||
};
|
||||
|
||||
// load the code if it exists.
|
||||
let (code_hash, new_code) = match code_state {
|
||||
CodeState::Empty => (SHA3_EMPTY, None),
|
||||
CodeState::Inline => {
|
||||
let code: Bytes = rlp.val_at(3)?;
|
||||
let code_hash = acct_db.insert(&code);
|
||||
|
||||
(code_hash, Some(code))
|
||||
}
|
||||
CodeState::Hash => {
|
||||
let code_hash = rlp.val_at(3)?;
|
||||
|
||||
(code_hash, None)
|
||||
}
|
||||
};
|
||||
|
||||
let mut storage_root = H256::zero();
|
||||
|
||||
{
|
||||
let mut storage_trie = TrieDBMut::new(acct_db, &mut storage_root);
|
||||
let pairs = rlp.at(4)?;
|
||||
for pair_rlp in pairs.iter() {
|
||||
let k: Bytes = pair_rlp.val_at(0)?;
|
||||
let v: Bytes = pair_rlp.val_at(1)?;
|
||||
|
||||
storage_trie.insert(&k, &v)?;
|
||||
}
|
||||
}
|
||||
|
||||
// encode the account to a standard rlp.
|
||||
pub fn to_thin_rlp(&self) -> Bytes {
|
||||
let mut stream = RlpStream::new_list(4);
|
||||
stream
|
||||
.append(&self.nonce)
|
||||
.append(&self.balance)
|
||||
.append(&self.storage_root)
|
||||
.append(&self.code_hash);
|
||||
let acc = BasicAccount {
|
||||
nonce: nonce,
|
||||
balance: balance,
|
||||
storage_root: storage_root,
|
||||
code_hash: code_hash,
|
||||
};
|
||||
|
||||
stream.out()
|
||||
}
|
||||
|
||||
// walk the account's storage trie, returning an RLP item containing the
|
||||
// account properties and the storage.
|
||||
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 = TrieDB::new(acct_db, &self.storage_root)?;
|
||||
|
||||
let mut pairs = Vec::new();
|
||||
|
||||
for item in db.iter()? {
|
||||
let (k, v) = item?;
|
||||
pairs.push((k, v));
|
||||
}
|
||||
|
||||
let mut stream = RlpStream::new_list(pairs.len());
|
||||
|
||||
for (k, v) in pairs {
|
||||
stream.begin_list(2).append(&k).append(&&*v);
|
||||
}
|
||||
|
||||
let pairs_rlp = stream.out();
|
||||
|
||||
let mut account_stream = RlpStream::new_list(5);
|
||||
account_stream.append(&self.nonce)
|
||||
.append(&self.balance);
|
||||
|
||||
// [has_code, code_hash].
|
||||
if self.code_hash == SHA3_EMPTY {
|
||||
account_stream.append(&CodeState::Empty.raw()).append_empty_data();
|
||||
} else if used_code.contains(&self.code_hash) {
|
||||
account_stream.append(&CodeState::Hash.raw()).append(&self.code_hash);
|
||||
} else {
|
||||
match acct_db.get(&self.code_hash) {
|
||||
Some(c) => {
|
||||
used_code.insert(self.code_hash.clone());
|
||||
account_stream.append(&CodeState::Inline.raw()).append(&&*c);
|
||||
}
|
||||
None => {
|
||||
warn!("code lookup failed during snapshot");
|
||||
account_stream.append(&false).append_empty_data();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
account_stream.append_raw(&pairs_rlp, 1);
|
||||
|
||||
Ok(account_stream.out())
|
||||
}
|
||||
|
||||
// decode a fat rlp, and rebuild the storage trie as we go.
|
||||
// returns the account structure along with its newly recovered code,
|
||||
// if it exists.
|
||||
pub fn from_fat_rlp(
|
||||
acct_db: &mut AccountDBMut,
|
||||
rlp: UntrustedRlp,
|
||||
) -> Result<(Self, Option<Bytes>), Error> {
|
||||
use util::{TrieDBMut, TrieMut};
|
||||
|
||||
// check for special case of empty account.
|
||||
if rlp.is_empty() {
|
||||
return Ok((ACC_EMPTY, None));
|
||||
}
|
||||
|
||||
let nonce = rlp.val_at(0)?;
|
||||
let balance = rlp.val_at(1)?;
|
||||
let code_state: CodeState = {
|
||||
let raw: u8 = rlp.val_at(2)?;
|
||||
CodeState::from(raw)?
|
||||
};
|
||||
|
||||
// load the code if it exists.
|
||||
let (code_hash, new_code) = match code_state {
|
||||
CodeState::Empty => (SHA3_EMPTY, None),
|
||||
CodeState::Inline => {
|
||||
let code: Bytes = rlp.val_at(3)?;
|
||||
let code_hash = acct_db.insert(&code);
|
||||
|
||||
(code_hash, Some(code))
|
||||
}
|
||||
CodeState::Hash => {
|
||||
let code_hash = rlp.val_at(3)?;
|
||||
|
||||
(code_hash, None)
|
||||
}
|
||||
};
|
||||
|
||||
let mut storage_root = H256::zero();
|
||||
|
||||
{
|
||||
let mut storage_trie = TrieDBMut::new(acct_db, &mut storage_root);
|
||||
let pairs = rlp.at(4)?;
|
||||
for pair_rlp in pairs.iter() {
|
||||
let k: Bytes = pair_rlp.val_at(0)?;
|
||||
let v: Bytes = pair_rlp.val_at(1)?;
|
||||
|
||||
storage_trie.insert(&k, &v)?;
|
||||
}
|
||||
}
|
||||
|
||||
let acc = Account {
|
||||
nonce: nonce,
|
||||
balance: balance,
|
||||
storage_root: storage_root,
|
||||
code_hash: code_hash,
|
||||
};
|
||||
|
||||
Ok((acc, new_code))
|
||||
}
|
||||
|
||||
/// Get the account's code hash.
|
||||
pub fn code_hash(&self) -> &H256 {
|
||||
&self.code_hash
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn storage_root_mut(&mut self) -> &mut H256 {
|
||||
&mut self.storage_root
|
||||
}
|
||||
Ok((acc, new_code))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use account_db::{AccountDB, AccountDBMut};
|
||||
use basic_account::BasicAccount;
|
||||
use tests::helpers::get_temp_state_db;
|
||||
use snapshot::tests::helpers::fill_storage;
|
||||
|
||||
@ -227,26 +184,26 @@ mod tests {
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use super::{ACC_EMPTY, Account};
|
||||
use super::{ACC_EMPTY, to_fat_rlp, from_fat_rlp};
|
||||
|
||||
#[test]
|
||||
fn encoding_basic() {
|
||||
let mut db = get_temp_state_db();
|
||||
let addr = Address::random();
|
||||
|
||||
let account = Account {
|
||||
let account = BasicAccount {
|
||||
nonce: 50.into(),
|
||||
balance: 123456789.into(),
|
||||
storage_root: SHA3_NULL_RLP,
|
||||
code_hash: SHA3_EMPTY,
|
||||
};
|
||||
|
||||
let thin_rlp = account.to_thin_rlp();
|
||||
assert_eq!(Account::from_thin_rlp(&thin_rlp), account);
|
||||
let thin_rlp = ::rlp::encode(&account);
|
||||
assert_eq!(::rlp::decode::<BasicAccount>(&thin_rlp), account);
|
||||
|
||||
let fat_rlp = account.to_fat_rlp(&AccountDB::new(db.as_hashdb(), &addr), &mut Default::default()).unwrap();
|
||||
let fat_rlp = to_fat_rlp(&account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default()).unwrap();
|
||||
let fat_rlp = UntrustedRlp::new(&fat_rlp);
|
||||
assert_eq!(Account::from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp).unwrap().0, account);
|
||||
assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp).unwrap().0, account);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -258,7 +215,7 @@ mod tests {
|
||||
let acct_db = AccountDBMut::new(db.as_hashdb_mut(), &addr);
|
||||
let mut root = SHA3_NULL_RLP;
|
||||
fill_storage(acct_db, &mut root, &mut H256::zero());
|
||||
Account {
|
||||
BasicAccount {
|
||||
nonce: 25.into(),
|
||||
balance: 987654321.into(),
|
||||
storage_root: root,
|
||||
@ -266,12 +223,12 @@ mod tests {
|
||||
}
|
||||
};
|
||||
|
||||
let thin_rlp = account.to_thin_rlp();
|
||||
assert_eq!(Account::from_thin_rlp(&thin_rlp), account);
|
||||
let thin_rlp = ::rlp::encode(&account);
|
||||
assert_eq!(::rlp::decode::<BasicAccount>(&thin_rlp), account);
|
||||
|
||||
let fat_rlp = account.to_fat_rlp(&AccountDB::new(db.as_hashdb(), &addr), &mut Default::default()).unwrap();
|
||||
let fat_rlp = to_fat_rlp(&account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default()).unwrap();
|
||||
let fat_rlp = UntrustedRlp::new(&fat_rlp);
|
||||
assert_eq!(Account::from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp).unwrap().0, account);
|
||||
assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp).unwrap().0, account);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -291,14 +248,14 @@ mod tests {
|
||||
acct_db.emplace(code_hash.clone(), DBValue::from_slice(b"this is definitely code"));
|
||||
}
|
||||
|
||||
let account1 = Account {
|
||||
let account1 = BasicAccount {
|
||||
nonce: 50.into(),
|
||||
balance: 123456789.into(),
|
||||
storage_root: SHA3_NULL_RLP,
|
||||
code_hash: code_hash,
|
||||
};
|
||||
|
||||
let account2 = Account {
|
||||
let account2 = BasicAccount {
|
||||
nonce: 400.into(),
|
||||
balance: 98765432123456789usize.into(),
|
||||
storage_root: SHA3_NULL_RLP,
|
||||
@ -307,18 +264,18 @@ mod tests {
|
||||
|
||||
let mut used_code = HashSet::new();
|
||||
|
||||
let fat_rlp1 = account1.to_fat_rlp(&AccountDB::new(db.as_hashdb(), &addr1), &mut used_code).unwrap();
|
||||
let fat_rlp2 = account2.to_fat_rlp(&AccountDB::new(db.as_hashdb(), &addr2), &mut used_code).unwrap();
|
||||
let fat_rlp1 = to_fat_rlp(&account1, &AccountDB::new(db.as_hashdb(), &addr1), &mut used_code).unwrap();
|
||||
let fat_rlp2 = to_fat_rlp(&account2, &AccountDB::new(db.as_hashdb(), &addr2), &mut used_code).unwrap();
|
||||
assert_eq!(used_code.len(), 1);
|
||||
|
||||
let fat_rlp1 = UntrustedRlp::new(&fat_rlp1);
|
||||
let fat_rlp2 = UntrustedRlp::new(&fat_rlp2);
|
||||
|
||||
let (acc, maybe_code) = Account::from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr2), fat_rlp2).unwrap();
|
||||
let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr2), fat_rlp2).unwrap();
|
||||
assert!(maybe_code.is_none());
|
||||
assert_eq!(acc, account2);
|
||||
|
||||
let (acc, maybe_code) = Account::from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr1), fat_rlp1).unwrap();
|
||||
let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr1), fat_rlp1).unwrap();
|
||||
assert_eq!(maybe_code, Some(b"this is definitely code".to_vec()));
|
||||
assert_eq!(acc, account1);
|
||||
}
|
||||
@ -328,7 +285,7 @@ mod tests {
|
||||
let mut db = get_temp_state_db();
|
||||
let mut used_code = HashSet::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)).unwrap(), (ACC_EMPTY, None));
|
||||
assert_eq!(to_fat_rlp(&ACC_EMPTY, &AccountDB::new(db.as_hashdb(), &Address::default()), &mut used_code).unwrap(), ::rlp::NULL_RLP.to_vec());
|
||||
assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &Address::default()), UntrustedRlp::new(&::rlp::NULL_RLP)).unwrap(), (ACC_EMPTY, None));
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,6 @@ use util::sha3::SHA3_NULL_RLP;
|
||||
use rlp::{RlpStream, Stream, UntrustedRlp, View};
|
||||
use bloom_journal::Bloom;
|
||||
|
||||
use self::account::Account;
|
||||
use self::block::AbridgedBlock;
|
||||
use self::io::SnapshotWriter;
|
||||
|
||||
@ -368,12 +367,12 @@ pub fn chunk_state<'a>(db: &HashDB, root: &H256, writer: &Mutex<SnapshotWriter +
|
||||
// account_key here is the address' hash.
|
||||
for item in account_trie.iter()? {
|
||||
let (account_key, account_data) = item?;
|
||||
let account = Account::from_thin_rlp(&*account_data);
|
||||
let account = ::rlp::decode(&*account_data);
|
||||
let account_key_hash = H256::from_slice(&account_key);
|
||||
|
||||
let account_db = AccountDB::from_hash(db, account_key_hash);
|
||||
|
||||
let fat_rlp = account.to_fat_rlp(&account_db, &mut used_code)?;
|
||||
let fat_rlp = account::to_fat_rlp(&account, &account_db, &mut used_code)?;
|
||||
chunker.push(account_key, fat_rlp)?;
|
||||
}
|
||||
|
||||
@ -507,10 +506,10 @@ fn rebuild_accounts(
|
||||
// fill out the storage trie and code while decoding.
|
||||
let (acc, maybe_code) = {
|
||||
let mut acct_db = AccountDBMut::from_hash(db, hash);
|
||||
Account::from_fat_rlp(&mut acct_db, fat_rlp)?
|
||||
account::from_fat_rlp(&mut acct_db, fat_rlp)?
|
||||
};
|
||||
|
||||
let code_hash = acc.code_hash().clone();
|
||||
let code_hash = acc.code_hash.clone();
|
||||
match maybe_code {
|
||||
// new inline code
|
||||
Some(code) => status.new_code.push((code_hash, code, hash)),
|
||||
@ -534,7 +533,7 @@ fn rebuild_accounts(
|
||||
}
|
||||
}
|
||||
|
||||
acc.to_thin_rlp()
|
||||
::rlp::encode(&acc).to_vec()
|
||||
};
|
||||
|
||||
*out = (hash, thin_rlp);
|
||||
|
@ -17,9 +17,9 @@
|
||||
//! Snapshot test helpers. These are used to build blockchains and state tries
|
||||
//! which can be queried before and after a full snapshot/restore cycle.
|
||||
|
||||
use basic_account::BasicAccount;
|
||||
use account_db::AccountDBMut;
|
||||
use rand::Rng;
|
||||
use snapshot::account::Account;
|
||||
|
||||
use util::DBValue;
|
||||
use util::hash::{FixedHash, H256};
|
||||
@ -64,10 +64,10 @@ impl StateProducer {
|
||||
|
||||
// sweep once to alter storage tries.
|
||||
for &mut (ref mut address_hash, ref mut account_data) in &mut accounts_to_modify {
|
||||
let mut account = Account::from_thin_rlp(&*account_data);
|
||||
let mut account: BasicAccount = ::rlp::decode(&*account_data);
|
||||
let acct_db = AccountDBMut::from_hash(db, *address_hash);
|
||||
fill_storage(acct_db, account.storage_root_mut(), &mut self.storage_seed);
|
||||
*account_data = DBValue::from_vec(account.to_thin_rlp());
|
||||
fill_storage(acct_db, &mut account.storage_root, &mut self.storage_seed);
|
||||
*account_data = DBValue::from_vec(::rlp::encode(&account).to_vec());
|
||||
}
|
||||
|
||||
// sweep again to alter account trie.
|
||||
|
@ -16,8 +16,9 @@
|
||||
|
||||
//! State snapshotting tests.
|
||||
|
||||
use basic_account::BasicAccount;
|
||||
use snapshot::account;
|
||||
use snapshot::{chunk_state, Error as SnapshotError, Progress, StateRebuilder};
|
||||
use snapshot::account::Account;
|
||||
use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter};
|
||||
use super::helpers::{compare_dbs, StateProducer};
|
||||
|
||||
@ -113,22 +114,21 @@ fn get_code_from_prev_chunk() {
|
||||
// first one will have code inlined,
|
||||
// second will just have its hash.
|
||||
let thin_rlp = acc_stream.out();
|
||||
let acc1 = Account::from_thin_rlp(&thin_rlp);
|
||||
let acc2 = Account::from_thin_rlp(&thin_rlp);
|
||||
let acc: BasicAccount = ::rlp::decode(&thin_rlp);
|
||||
|
||||
let mut make_chunk = |acc: Account, hash| {
|
||||
let mut make_chunk = |acc, hash| {
|
||||
let mut db = MemoryDB::new();
|
||||
AccountDBMut::from_hash(&mut db, hash).insert(&code[..]);
|
||||
|
||||
let fat_rlp = acc.to_fat_rlp(&AccountDB::from_hash(&db, hash), &mut used_code).unwrap();
|
||||
let fat_rlp = account::to_fat_rlp(&acc, &AccountDB::from_hash(&db, hash), &mut used_code).unwrap();
|
||||
|
||||
let mut stream = RlpStream::new_list(1);
|
||||
stream.begin_list(2).append(&hash).append_raw(&fat_rlp, 1);
|
||||
stream.out()
|
||||
};
|
||||
|
||||
let chunk1 = make_chunk(acc1, h1);
|
||||
let chunk2 = make_chunk(acc2, h2);
|
||||
let chunk1 = make_chunk(acc.clone(), h1);
|
||||
let chunk2 = make_chunk(acc, h2);
|
||||
|
||||
let db_path = RandomTempPath::create_dir();
|
||||
let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS);
|
||||
@ -190,4 +190,4 @@ fn checks_flag() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use util::{Address, H256, Uint, U256};
|
||||
use util::{Address, H256, Uint, U256, FixedHash};
|
||||
use util::sha3::SHA3_NULL_RLP;
|
||||
use ethjson;
|
||||
use super::seal::Seal;
|
||||
@ -50,9 +50,9 @@ impl From<ethjson::spec::Genesis> for Genesis {
|
||||
Genesis {
|
||||
seal: From::from(g.seal),
|
||||
difficulty: g.difficulty.into(),
|
||||
author: g.author.into(),
|
||||
timestamp: g.timestamp.into(),
|
||||
parent_hash: g.parent_hash.into(),
|
||||
author: g.author.map_or_else(Address::zero, Into::into),
|
||||
timestamp: g.timestamp.map_or(0, Into::into),
|
||||
parent_hash: g.parent_hash.map_or_else(H256::zero, Into::into),
|
||||
gas_limit: g.gas_limit.into(),
|
||||
transactions_root: g.transactions_root.map_or_else(|| SHA3_NULL_RLP.clone(), Into::into),
|
||||
receipts_root: g.receipts_root.map_or_else(|| SHA3_NULL_RLP.clone(), Into::into),
|
||||
|
@ -58,7 +58,7 @@ pub struct CommonParams {
|
||||
impl From<ethjson::spec::Params> for CommonParams {
|
||||
fn from(p: ethjson::spec::Params) -> Self {
|
||||
CommonParams {
|
||||
account_start_nonce: p.account_start_nonce.into(),
|
||||
account_start_nonce: p.account_start_nonce.map_or_else(U256::zero, Into::into),
|
||||
maximum_extra_data_size: p.maximum_extra_data_size.into(),
|
||||
network_id: p.network_id.into(),
|
||||
chain_id: if let Some(n) = p.chain_id { n.into() } else { p.network_id.into() },
|
||||
@ -337,9 +337,14 @@ impl Spec {
|
||||
pub fn new_instant() -> Spec { load_bundled!("instant_seal") }
|
||||
|
||||
/// Create a new Spec with AuthorityRound consensus which does internal sealing (not requiring work).
|
||||
/// Accounts with secrets "0".sha3() and "1".sha3() are the authorities.
|
||||
/// Accounts with secrets "0".sha3() and "1".sha3() are the validators.
|
||||
pub fn new_test_round() -> Self { load_bundled!("authority_round") }
|
||||
|
||||
/// Create a new Spec with BasicAuthority which uses a contract at address 5 to determine the current validators.
|
||||
/// Accounts with secrets "0".sha3() and "1".sha3() are initially the validators.
|
||||
/// Second validator can be removed with "0xf94e18670000000000000000000000000000000000000000000000000000000000000001" and added back in using "0x4d238c8e00000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1".
|
||||
pub fn new_validator_contract() -> Self { load_bundled!("validator_contract") }
|
||||
|
||||
/// Create a new Spec with Tendermint consensus which does internal sealing (not requiring work).
|
||||
/// Account "0".sha3() and "1".sha3() are a authorities.
|
||||
pub fn new_test_tendermint() -> Self { load_bundled!("tendermint") }
|
||||
|
@ -20,6 +20,7 @@ use util::*;
|
||||
use pod_account::*;
|
||||
use rlp::*;
|
||||
use lru_cache::LruCache;
|
||||
use basic_account::BasicAccount;
|
||||
|
||||
use std::cell::{RefCell, Cell};
|
||||
|
||||
@ -53,6 +54,23 @@ pub struct Account {
|
||||
address_hash: Cell<Option<H256>>,
|
||||
}
|
||||
|
||||
impl From<BasicAccount> for Account {
|
||||
fn from(basic: BasicAccount) -> Self {
|
||||
Account {
|
||||
balance: basic.balance,
|
||||
nonce: basic.nonce,
|
||||
storage_root: basic.storage_root,
|
||||
storage_cache: Self::empty_storage_cache(),
|
||||
storage_changes: HashMap::new(),
|
||||
code_hash: basic.code_hash,
|
||||
code_size: None,
|
||||
code_cache: Arc::new(vec![]),
|
||||
code_filth: Filth::Clean,
|
||||
address_hash: Cell::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Account {
|
||||
#[cfg(test)]
|
||||
/// General constructor.
|
||||
@ -109,19 +127,8 @@ impl Account {
|
||||
|
||||
/// Create a new account from RLP.
|
||||
pub fn from_rlp(rlp: &[u8]) -> Account {
|
||||
let r: Rlp = Rlp::new(rlp);
|
||||
Account {
|
||||
nonce: r.val_at(0),
|
||||
balance: r.val_at(1),
|
||||
storage_root: r.val_at(2),
|
||||
storage_cache: Self::empty_storage_cache(),
|
||||
storage_changes: HashMap::new(),
|
||||
code_hash: r.val_at(3),
|
||||
code_cache: Arc::new(vec![]),
|
||||
code_size: None,
|
||||
code_filth: Filth::Clean,
|
||||
address_hash: Cell::new(None),
|
||||
}
|
||||
let basic: BasicAccount = ::rlp::decode(rlp);
|
||||
basic.into()
|
||||
}
|
||||
|
||||
/// Create a new contract account.
|
||||
@ -171,8 +178,8 @@ impl Account {
|
||||
SecTrieDBMut would not set it to an invalid state root. Therefore the root is valid and DB creation \
|
||||
using it will not fail.");
|
||||
|
||||
let item: U256 = match db.get(key){
|
||||
Ok(x) => x.map_or_else(U256::zero, |v| decode(&*v)),
|
||||
let item: U256 = match db.get_with(key, ::rlp::decode) {
|
||||
Ok(x) => x.unwrap_or_else(U256::zero),
|
||||
Err(e) => panic!("Encountered potential DB corruption: {}", e),
|
||||
};
|
||||
let value: H256 = item.into();
|
||||
@ -446,12 +453,12 @@ impl Account {
|
||||
/// omitted.
|
||||
pub fn prove_storage(&self, db: &HashDB, storage_key: H256, from_level: u32) -> Result<Vec<Bytes>, Box<TrieError>> {
|
||||
use util::trie::{Trie, TrieDB};
|
||||
use util::trie::recorder::{Recorder, BasicRecorder as TrieRecorder};
|
||||
use util::trie::recorder::Recorder;
|
||||
|
||||
let mut recorder = TrieRecorder::with_depth(from_level);
|
||||
let mut recorder = Recorder::with_depth(from_level);
|
||||
|
||||
let trie = TrieDB::new(db, &self.storage_root)?;
|
||||
let _ = trie.get_recorded(&storage_key, &mut recorder)?;
|
||||
let _ = trie.get_with(&storage_key, &mut recorder)?;
|
||||
|
||||
Ok(recorder.drain().into_iter().map(|r| r.data).collect())
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ use state_db::StateDB;
|
||||
|
||||
use util::*;
|
||||
|
||||
use util::trie::recorder::{Recorder, BasicRecorder as TrieRecorder};
|
||||
use util::trie::recorder::Recorder;
|
||||
|
||||
mod account;
|
||||
mod substate;
|
||||
@ -425,8 +425,8 @@ impl State {
|
||||
|
||||
// account is 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 maybe_acc = match db.get(address) {
|
||||
Ok(acc) => acc.map(|v| Account::from_rlp(&v)),
|
||||
let maybe_acc = match db.get_with(address, Account::from_rlp) {
|
||||
Ok(acc) => acc,
|
||||
Err(e) => panic!("Potential DB corruption encountered: {}", e),
|
||||
};
|
||||
let r = maybe_acc.as_ref().map_or(H256::new(), |a| {
|
||||
@ -690,8 +690,8 @@ impl State {
|
||||
|
||||
// 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 mut maybe_acc = match db.get(a) {
|
||||
Ok(acc) => acc.map(|v| Account::from_rlp(&v)),
|
||||
let mut maybe_acc = match db.get_with(a, Account::from_rlp) {
|
||||
Ok(acc) => acc,
|
||||
Err(e) => panic!("Potential DB corruption encountered: {}", e),
|
||||
};
|
||||
if let Some(ref mut account) = maybe_acc.as_mut() {
|
||||
@ -722,9 +722,8 @@ impl State {
|
||||
None => {
|
||||
let maybe_acc = if self.db.check_non_null_bloom(a) {
|
||||
let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
|
||||
match db.get(a) {
|
||||
Ok(Some(acc)) => AccountEntry::new_clean(Some(Account::from_rlp(&acc))),
|
||||
Ok(None) => AccountEntry::new_clean(None),
|
||||
match db.get_with(a, Account::from_rlp) {
|
||||
Ok(acc) => AccountEntry::new_clean(acc),
|
||||
Err(e) => panic!("Potential DB corruption encountered: {}", e),
|
||||
}
|
||||
} else {
|
||||
@ -770,9 +769,9 @@ impl State {
|
||||
/// Requires a secure trie to be used for accurate results.
|
||||
/// `account_key` == sha3(address)
|
||||
pub fn prove_account(&self, account_key: H256, from_level: u32) -> Result<Vec<Bytes>, Box<TrieError>> {
|
||||
let mut recorder = TrieRecorder::with_depth(from_level);
|
||||
let mut recorder = Recorder::with_depth(from_level);
|
||||
let trie = TrieDB::new(self.db.as_hashdb(), &self.root)?;
|
||||
let _ = trie.get_recorded(&account_key, &mut recorder)?;
|
||||
trie.get_with(&account_key, &mut recorder)?;
|
||||
|
||||
Ok(recorder.drain().into_iter().map(|r| r.data).collect())
|
||||
}
|
||||
@ -786,8 +785,8 @@ impl State {
|
||||
// TODO: probably could look into cache somehow but it's keyed by
|
||||
// address, not sha3(address).
|
||||
let trie = TrieDB::new(self.db.as_hashdb(), &self.root)?;
|
||||
let acc = match trie.get(&account_key)? {
|
||||
Some(rlp) => Account::from_rlp(&rlp),
|
||||
let acc = match trie.get_with(&account_key, Account::from_rlp)? {
|
||||
Some(acc) => acc,
|
||||
None => return Ok(Vec::new()),
|
||||
};
|
||||
|
||||
@ -799,8 +798,8 @@ impl State {
|
||||
/// Only works when backed by a secure trie.
|
||||
pub fn code_by_address_hash(&self, account_key: H256) -> Result<Option<Bytes>, Box<TrieError>> {
|
||||
let trie = TrieDB::new(self.db.as_hashdb(), &self.root)?;
|
||||
let mut acc = match trie.get(&account_key)? {
|
||||
Some(rlp) => Account::from_rlp(&rlp),
|
||||
let mut acc = match trie.get_with(&account_key, Account::from_rlp)? {
|
||||
Some(acc) => acc,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
|
@ -320,4 +320,3 @@ fn does_not_propagate_delayed_transactions() {
|
||||
assert_eq!(2, client.ready_transactions().len());
|
||||
assert_eq!(2, client.miner().pending_transactions().len());
|
||||
}
|
||||
|
||||
|
55
ethcore/src/types/basic_account.rs
Normal file
55
ethcore/src/types/basic_account.rs
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2015, 2016 Parity Technologies (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/>.
|
||||
|
||||
//! Basic account type -- the decoded RLP from the state trie.
|
||||
|
||||
use rlp::*;
|
||||
use util::{U256, H256};
|
||||
|
||||
/// Basic account type.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct BasicAccount {
|
||||
/// Nonce of the account.
|
||||
pub nonce: U256,
|
||||
/// Balance of the account.
|
||||
pub balance: U256,
|
||||
/// Storage root of the account.
|
||||
pub storage_root: H256,
|
||||
/// Code hash of the account.
|
||||
pub code_hash: H256,
|
||||
}
|
||||
|
||||
impl Encodable for BasicAccount {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
s.begin_list(4)
|
||||
.append(&self.nonce)
|
||||
.append(&self.balance)
|
||||
.append(&self.storage_root)
|
||||
.append(&self.code_hash);
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for BasicAccount {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let rlp = decoder.as_rlp();
|
||||
Ok(BasicAccount {
|
||||
nonce: rlp.val_at(0)?,
|
||||
balance: rlp.val_at(1)?,
|
||||
storage_root: rlp.val_at(2)?,
|
||||
code_hash: rlp.val_at(3)?,
|
||||
})
|
||||
}
|
||||
}
|
@ -97,6 +97,8 @@ pub struct LocalizedLogEntry {
|
||||
pub transaction_index: usize,
|
||||
/// Log position in the block.
|
||||
pub log_index: usize,
|
||||
/// Log position in the transaction.
|
||||
pub transaction_log_index: usize,
|
||||
}
|
||||
|
||||
impl Deref for LocalizedLogEntry {
|
||||
|
@ -37,3 +37,4 @@ pub mod mode;
|
||||
pub mod pruning_info;
|
||||
pub mod security_level;
|
||||
pub mod encoded;
|
||||
pub mod basic_account;
|
||||
|
@ -391,6 +391,14 @@ impl Res {
|
||||
Res::Call(_) | Res::FailedCall(_) | Res::FailedCreate(_) | Res::None => Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Did this call fail?
|
||||
pub fn succeeded(&self) -> bool {
|
||||
match *self {
|
||||
Res::Call(_) | Res::Create(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@ -561,4 +569,3 @@ impl Decodable for VMTrace {
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
//! Transaction data structure.
|
||||
|
||||
use std::ops::Deref;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::cell::*;
|
||||
use rlp::*;
|
||||
use util::sha3::Hashable;
|
||||
@ -239,6 +239,12 @@ impl Deref for SignedTransaction {
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for SignedTransaction {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.unsigned
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for SignedTransaction {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
@ -373,7 +379,7 @@ impl SignedTransaction {
|
||||
}
|
||||
|
||||
/// Signed Transaction that is a part of canon blockchain.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "ipc", binary)]
|
||||
pub struct LocalizedTransaction {
|
||||
/// Signed part.
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "evmjit"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[lib]
|
||||
|
@ -3,7 +3,7 @@ description = "Fetching hash-addressed content."
|
||||
homepage = "http://parity.io"
|
||||
license = "GPL-3.0"
|
||||
name = "parity-hash-fetch"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
description = "Types that implement IPC and are common to multiple modules."
|
||||
name = "ipc-common-types"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
build = "build.rs"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ethcore-ipc-codegen"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
license = "GPL-3.0"
|
||||
description = "Macros to auto-generate implementations for ipc call"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ethcore-ipc-nano"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
license = "GPL-3.0"
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ethcore-ipc"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
license = "GPL-3.0"
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "parity.js",
|
||||
"version": "0.2.152",
|
||||
"version": "0.2.182",
|
||||
"main": "release/index.js",
|
||||
"jsnext:main": "src/index.js",
|
||||
"author": "Parity Team <admin@parity.io>",
|
||||
@ -26,9 +26,9 @@
|
||||
],
|
||||
"scripts": {
|
||||
"build": "npm run build:lib && npm run build:dll && npm run build:app",
|
||||
"build:app": "webpack --config webpack/app --progress",
|
||||
"build:lib": "webpack --config webpack/libraries --progress",
|
||||
"build:dll": "webpack --config webpack/vendor --progress",
|
||||
"build:app": "webpack --config webpack/app",
|
||||
"build:lib": "webpack --config webpack/libraries",
|
||||
"build:dll": "webpack --config webpack/vendor",
|
||||
"ci:build": "npm run ci:build:lib && npm run ci:build:dll && npm run ci:build:app",
|
||||
"ci:build:app": "NODE_ENV=production webpack --config webpack/app",
|
||||
"ci:build:lib": "NODE_ENV=production webpack --config webpack/libraries",
|
||||
@ -43,27 +43,27 @@
|
||||
"lint:css": "stylelint ./src/**/*.css",
|
||||
"lint:js": "eslint --ignore-path .gitignore ./src/",
|
||||
"lint:js:cached": "eslint --cache --ignore-path .gitignore ./src/",
|
||||
"test": "NODE_ENV=test mocha 'src/**/*.spec.js'",
|
||||
"test:coverage": "NODE_ENV=test istanbul cover _mocha -- 'src/**/*.spec.js'",
|
||||
"test": "NODE_ENV=test mocha --compilers ejs:ejsify 'src/**/*.spec.js'",
|
||||
"test:coverage": "NODE_ENV=test istanbul cover _mocha -- --compilers ejs:ejsify 'src/**/*.spec.js'",
|
||||
"test:e2e": "NODE_ENV=test mocha 'src/**/*.e2e.js'",
|
||||
"test:npm": "(cd .npmjs && npm i) && node test/npmParity && (rm -rf .npmjs/node_modules)",
|
||||
"prepush": "npm run lint:cached"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "6.18.0",
|
||||
"babel-core": "6.20.0",
|
||||
"babel-core": "6.21.0",
|
||||
"babel-eslint": "7.1.1",
|
||||
"babel-loader": "6.2.10",
|
||||
"babel-plugin-lodash": "3.2.10",
|
||||
"babel-plugin-lodash": "3.2.11",
|
||||
"babel-plugin-react-intl": "2.2.0",
|
||||
"babel-plugin-transform-class-properties": "6.18.0",
|
||||
"babel-plugin-transform-class-properties": "6.19.0",
|
||||
"babel-plugin-transform-decorators-legacy": "1.3.4",
|
||||
"babel-plugin-transform-object-rest-spread": "6.20.2",
|
||||
"babel-plugin-transform-react-remove-prop-types": "0.2.11",
|
||||
"babel-plugin-transform-runtime": "6.15.0",
|
||||
"babel-plugin-webpack-alias": "2.1.2",
|
||||
"babel-polyfill": "6.20.0",
|
||||
"babel-preset-env": "1.0.2",
|
||||
"babel-preset-env": "1.1.4",
|
||||
"babel-preset-es2015": "6.18.0",
|
||||
"babel-preset-es2016": "6.16.0",
|
||||
"babel-preset-es2017": "6.16.0",
|
||||
@ -80,57 +80,59 @@
|
||||
"coveralls": "2.11.15",
|
||||
"css-loader": "0.26.1",
|
||||
"ejs-loader": "0.3.0",
|
||||
"enzyme": "2.6.0",
|
||||
"ejsify": "1.0.0",
|
||||
"enzyme": "2.7.0",
|
||||
"eslint": "3.11.1",
|
||||
"eslint-config-semistandard": "7.0.0",
|
||||
"eslint-config-standard": "6.2.1",
|
||||
"eslint-config-standard-react": "4.2.0",
|
||||
"eslint-plugin-promise": "3.4.0",
|
||||
"eslint-plugin-react": "6.7.1",
|
||||
"eslint-plugin-react": "6.8.0",
|
||||
"eslint-plugin-standard": "2.0.1",
|
||||
"express": "4.14.0",
|
||||
"extract-loader": "0.1.0",
|
||||
"extract-text-webpack-plugin": "2.0.0-beta.4",
|
||||
"file-loader": "0.9.0",
|
||||
"happypack": "3.0.0",
|
||||
"happypack": "3.0.2",
|
||||
"html-loader": "0.4.4",
|
||||
"html-webpack-plugin": "2.24.1",
|
||||
"http-proxy-middleware": "0.17.2",
|
||||
"http-proxy-middleware": "0.17.3",
|
||||
"husky": "0.11.9",
|
||||
"ignore-styles": "5.0.1",
|
||||
"image-webpack-loader": "3.0.0",
|
||||
"image-webpack-loader": "3.1.0",
|
||||
"istanbul": "1.0.0-alpha.2",
|
||||
"jsdom": "9.8.3",
|
||||
"jsdom": "9.9.1",
|
||||
"json-loader": "0.5.4",
|
||||
"mocha": "3.2.0",
|
||||
"mock-local-storage": "1.0.2",
|
||||
"mock-socket": "6.0.3",
|
||||
"mock-socket": "6.0.4",
|
||||
"nock": "9.0.2",
|
||||
"postcss-import": "9.0.0",
|
||||
"postcss-loader": "1.2.0",
|
||||
"postcss-loader": "1.2.1",
|
||||
"postcss-nested": "1.0.0",
|
||||
"postcss-simple-vars": "3.0.0",
|
||||
"progress": "1.1.8",
|
||||
"progress-bar-webpack-plugin": "1.9.1",
|
||||
"raw-loader": "0.5.1",
|
||||
"react-addons-perf": "15.4.1",
|
||||
"react-addons-test-utils": "15.4.1",
|
||||
"react-hot-loader": "3.0.0-beta.6",
|
||||
"react-intl-aggregate-webpack-plugin": "0.0.1",
|
||||
"rucksack-css": "0.9.1",
|
||||
"script-ext-html-webpack-plugin": "1.3.4",
|
||||
"script-ext-html-webpack-plugin": "1.3.5",
|
||||
"serviceworker-webpack-plugin": "0.1.7",
|
||||
"sinon": "1.17.6",
|
||||
"sinon-as-promised": "4.0.2",
|
||||
"sinon-chai": "2.8.0",
|
||||
"style-loader": "0.13.1",
|
||||
"stylelint": "7.6.0",
|
||||
"stylelint-config-standard": "15.0.0",
|
||||
"stylelint": "7.7.0",
|
||||
"stylelint-config-standard": "15.0.1",
|
||||
"url-loader": "0.5.7",
|
||||
"webpack": "2.2.0-rc.2",
|
||||
"webpack-dev-middleware": "1.8.4",
|
||||
"webpack-dev-middleware": "1.9.0",
|
||||
"webpack-error-notification": "0.1.6",
|
||||
"webpack-hot-middleware": "2.13.2",
|
||||
"websocket": "1.0.23"
|
||||
"webpack-hot-middleware": "2.14.0",
|
||||
"websocket": "1.0.24"
|
||||
},
|
||||
"dependencies": {
|
||||
"bignumber.js": "3.0.1",
|
||||
@ -150,6 +152,7 @@
|
||||
"isomorphic-fetch": "2.2.1",
|
||||
"js-sha3": "0.5.5",
|
||||
"lodash": "4.17.2",
|
||||
"loglevel": "1.4.1",
|
||||
"marked": "0.3.6",
|
||||
"material-ui": "0.16.5",
|
||||
"material-ui-chip-input": "0.11.1",
|
||||
|
6
js/src/3rdparty/etherscan/account.js
vendored
6
js/src/3rdparty/etherscan/account.js
vendored
@ -49,17 +49,17 @@ function transactions (address, page, test = false) {
|
||||
// page offset from 0
|
||||
return _call('txlist', {
|
||||
address: address,
|
||||
page: (page || 0) + 1,
|
||||
offset: PAGE_SIZE,
|
||||
page: (page || 0) + 1,
|
||||
sort: 'desc'
|
||||
}, test).then((transactions) => {
|
||||
return transactions.map((tx) => {
|
||||
return {
|
||||
blockNumber: new BigNumber(tx.blockNumber || 0),
|
||||
from: util.toChecksumAddress(tx.from),
|
||||
to: util.toChecksumAddress(tx.to),
|
||||
hash: tx.hash,
|
||||
blockNumber: new BigNumber(tx.blockNumber),
|
||||
timeStamp: tx.timeStamp,
|
||||
to: util.toChecksumAddress(tx.to),
|
||||
value: tx.value
|
||||
};
|
||||
});
|
||||
|
38
js/src/3rdparty/etherscan/helpers.spec.js
vendored
Normal file
38
js/src/3rdparty/etherscan/helpers.spec.js
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2015, 2016 Parity Technologies (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/>.
|
||||
|
||||
import nock from 'nock';
|
||||
import { stringify } from 'qs';
|
||||
|
||||
import { url } from './links';
|
||||
|
||||
function mockget (requests, test) {
|
||||
let scope = nock(url(test));
|
||||
|
||||
requests.forEach((request) => {
|
||||
scope = scope
|
||||
.get(`/api?${stringify(request.query)}`)
|
||||
.reply(request.code || 200, () => {
|
||||
return { result: request.reply };
|
||||
});
|
||||
});
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
export {
|
||||
mockget
|
||||
};
|
18
js/src/3rdparty/shapeshift/helpers.spec.js
vendored
18
js/src/3rdparty/shapeshift/helpers.spec.js
vendored
@ -16,16 +16,10 @@
|
||||
|
||||
const nock = require('nock');
|
||||
|
||||
const ShapeShift = require('./');
|
||||
const initShapeshift = (ShapeShift.default || ShapeShift);
|
||||
|
||||
const APIKEY = '0x123454321';
|
||||
|
||||
const shapeshift = initShapeshift(APIKEY);
|
||||
const rpc = shapeshift.getRpc();
|
||||
|
||||
function mockget (requests) {
|
||||
let scope = nock(rpc.ENDPOINT);
|
||||
function mockget (shapeshift, requests) {
|
||||
let scope = nock(shapeshift.getRpc().ENDPOINT);
|
||||
|
||||
requests.forEach((request) => {
|
||||
scope = scope
|
||||
@ -38,8 +32,8 @@ function mockget (requests) {
|
||||
return scope;
|
||||
}
|
||||
|
||||
function mockpost (requests) {
|
||||
let scope = nock(rpc.ENDPOINT);
|
||||
function mockpost (shapeshift, requests) {
|
||||
let scope = nock(shapeshift.getRpc().ENDPOINT);
|
||||
|
||||
requests.forEach((request) => {
|
||||
scope = scope
|
||||
@ -58,7 +52,5 @@ function mockpost (requests) {
|
||||
module.exports = {
|
||||
APIKEY,
|
||||
mockget,
|
||||
mockpost,
|
||||
shapeshift,
|
||||
rpc
|
||||
mockpost
|
||||
};
|
||||
|
19
js/src/3rdparty/shapeshift/rpc.spec.js
vendored
19
js/src/3rdparty/shapeshift/rpc.spec.js
vendored
@ -16,12 +16,21 @@
|
||||
|
||||
const helpers = require('./helpers.spec.js');
|
||||
|
||||
const APIKEY = helpers.APIKEY;
|
||||
const ShapeShift = require('./');
|
||||
const initShapeshift = (ShapeShift.default || ShapeShift);
|
||||
|
||||
const mockget = helpers.mockget;
|
||||
const mockpost = helpers.mockpost;
|
||||
const rpc = helpers.rpc;
|
||||
|
||||
describe('shapeshift/rpc', () => {
|
||||
let rpc;
|
||||
let shapeshift;
|
||||
|
||||
beforeEach(() => {
|
||||
shapeshift = initShapeshift(helpers.APIKEY);
|
||||
rpc = shapeshift.getRpc();
|
||||
});
|
||||
|
||||
describe('GET', () => {
|
||||
const REPLY = { test: 'this is some result' };
|
||||
|
||||
@ -29,7 +38,7 @@ describe('shapeshift/rpc', () => {
|
||||
let result;
|
||||
|
||||
beforeEach(() => {
|
||||
scope = mockget([{ path: 'test', reply: REPLY }]);
|
||||
scope = mockget(shapeshift, [{ path: 'test', reply: REPLY }]);
|
||||
|
||||
return rpc
|
||||
.get('test')
|
||||
@ -54,7 +63,7 @@ describe('shapeshift/rpc', () => {
|
||||
let result;
|
||||
|
||||
beforeEach(() => {
|
||||
scope = mockpost([{ path: 'test', reply: REPLY }]);
|
||||
scope = mockpost(shapeshift, [{ path: 'test', reply: REPLY }]);
|
||||
|
||||
return rpc
|
||||
.post('test', { input: 'stuff' })
|
||||
@ -76,7 +85,7 @@ describe('shapeshift/rpc', () => {
|
||||
});
|
||||
|
||||
it('passes the apikey specified', () => {
|
||||
expect(scope.body.test.apiKey).to.equal(APIKEY);
|
||||
expect(scope.body.test.apiKey).to.equal(helpers.APIKEY);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
77
js/src/3rdparty/shapeshift/shapeshift.js
vendored
77
js/src/3rdparty/shapeshift/shapeshift.js
vendored
@ -15,8 +15,9 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default function (rpc) {
|
||||
let subscriptions = [];
|
||||
let pollStatusIntervalId = null;
|
||||
let _subscriptions = [];
|
||||
let _pollStatusIntervalId = null;
|
||||
let _subscriptionPromises = null;
|
||||
|
||||
function getCoins () {
|
||||
return rpc.get('getcoins');
|
||||
@ -36,75 +37,93 @@ export default function (rpc) {
|
||||
|
||||
function shift (toAddress, returnAddress, pair) {
|
||||
return rpc.post('shift', {
|
||||
withdrawal: toAddress,
|
||||
pair: pair,
|
||||
returnAddress: returnAddress
|
||||
pair,
|
||||
returnAddress,
|
||||
withdrawal: toAddress
|
||||
});
|
||||
}
|
||||
|
||||
function subscribe (depositAddress, callback) {
|
||||
const idx = subscriptions.length;
|
||||
if (!depositAddress || !callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
subscriptions.push({
|
||||
depositAddress,
|
||||
const index = _subscriptions.length;
|
||||
|
||||
_subscriptions.push({
|
||||
callback,
|
||||
idx
|
||||
depositAddress,
|
||||
index
|
||||
});
|
||||
|
||||
// Only poll if there are subscriptions...
|
||||
if (!pollStatusIntervalId) {
|
||||
pollStatusIntervalId = setInterval(_pollStatus, 2000);
|
||||
if (_pollStatusIntervalId === null) {
|
||||
_pollStatusIntervalId = setInterval(_pollStatus, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
function unsubscribe (depositAddress) {
|
||||
const newSubscriptions = []
|
||||
.concat(subscriptions)
|
||||
.filter((sub) => sub.depositAddress !== depositAddress);
|
||||
_subscriptions = _subscriptions.filter((sub) => sub.depositAddress !== depositAddress);
|
||||
|
||||
subscriptions = newSubscriptions;
|
||||
|
||||
if (subscriptions.length === 0) {
|
||||
clearInterval(pollStatusIntervalId);
|
||||
pollStatusIntervalId = null;
|
||||
if (_subscriptions.length === 0) {
|
||||
clearInterval(_pollStatusIntervalId);
|
||||
_pollStatusIntervalId = null;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function _getSubscriptionStatus (subscription) {
|
||||
if (!subscription) {
|
||||
return;
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
getStatus(subscription.depositAddress)
|
||||
return getStatus(subscription.depositAddress)
|
||||
.then((result) => {
|
||||
switch (result.status) {
|
||||
case 'no_deposits':
|
||||
case 'received':
|
||||
subscription.callback(null, result);
|
||||
return;
|
||||
return true;
|
||||
|
||||
case 'complete':
|
||||
subscription.callback(null, result);
|
||||
subscriptions[subscription.idx] = null;
|
||||
return;
|
||||
unsubscribe(subscription.depositAddress);
|
||||
return true;
|
||||
|
||||
case 'failed':
|
||||
subscription.callback({
|
||||
message: status.error,
|
||||
fatal: true
|
||||
});
|
||||
subscriptions[subscription.idx] = null;
|
||||
return;
|
||||
unsubscribe(subscription.depositAddress);
|
||||
return true;
|
||||
}
|
||||
})
|
||||
.catch(subscription.callback);
|
||||
.catch(() => {
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function _pollStatus () {
|
||||
subscriptions.forEach(_getSubscriptionStatus);
|
||||
_subscriptionPromises = Promise.all(_subscriptions.map(_getSubscriptionStatus));
|
||||
}
|
||||
|
||||
function _getSubscriptions () {
|
||||
return _subscriptions;
|
||||
}
|
||||
|
||||
function _getSubscriptionPromises () {
|
||||
return _subscriptionPromises;
|
||||
}
|
||||
|
||||
function _isPolling () {
|
||||
return _pollStatusIntervalId !== null;
|
||||
}
|
||||
|
||||
return {
|
||||
_getSubscriptions,
|
||||
_getSubscriptionPromises,
|
||||
_isPolling,
|
||||
getCoins,
|
||||
getMarketInfo,
|
||||
getRpc,
|
||||
|
110
js/src/3rdparty/shapeshift/shapeshift.spec.js
vendored
110
js/src/3rdparty/shapeshift/shapeshift.spec.js
vendored
@ -14,13 +14,29 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
const sinon = require('sinon');
|
||||
|
||||
const ShapeShift = require('./');
|
||||
const initShapeshift = (ShapeShift.default || ShapeShift);
|
||||
|
||||
const helpers = require('./helpers.spec.js');
|
||||
|
||||
const mockget = helpers.mockget;
|
||||
const mockpost = helpers.mockpost;
|
||||
const shapeshift = helpers.shapeshift;
|
||||
|
||||
describe('shapeshift/calls', () => {
|
||||
let clock;
|
||||
let shapeshift;
|
||||
|
||||
beforeEach(() => {
|
||||
clock = sinon.useFakeTimers();
|
||||
shapeshift = initShapeshift(helpers.APIKEY);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
describe('getCoins', () => {
|
||||
const REPLY = {
|
||||
BTC: {
|
||||
@ -39,8 +55,8 @@ describe('shapeshift/calls', () => {
|
||||
|
||||
let scope;
|
||||
|
||||
before(() => {
|
||||
scope = mockget([{ path: 'getcoins', reply: REPLY }]);
|
||||
beforeEach(() => {
|
||||
scope = mockget(shapeshift, [{ path: 'getcoins', reply: REPLY }]);
|
||||
|
||||
return shapeshift.getCoins();
|
||||
});
|
||||
@ -61,8 +77,8 @@ describe('shapeshift/calls', () => {
|
||||
|
||||
let scope;
|
||||
|
||||
before(() => {
|
||||
scope = mockget([{ path: 'marketinfo/btc_ltc', reply: REPLY }]);
|
||||
beforeEach(() => {
|
||||
scope = mockget(shapeshift, [{ path: 'marketinfo/btc_ltc', reply: REPLY }]);
|
||||
|
||||
return shapeshift.getMarketInfo('btc_ltc');
|
||||
});
|
||||
@ -80,8 +96,8 @@ describe('shapeshift/calls', () => {
|
||||
|
||||
let scope;
|
||||
|
||||
before(() => {
|
||||
scope = mockget([{ path: 'txStat/0x123', reply: REPLY }]);
|
||||
beforeEach(() => {
|
||||
scope = mockget(shapeshift, [{ path: 'txStat/0x123', reply: REPLY }]);
|
||||
|
||||
return shapeshift.getStatus('0x123');
|
||||
});
|
||||
@ -101,8 +117,8 @@ describe('shapeshift/calls', () => {
|
||||
|
||||
let scope;
|
||||
|
||||
before(() => {
|
||||
scope = mockpost([{ path: 'shift', reply: REPLY }]);
|
||||
beforeEach(() => {
|
||||
scope = mockpost(shapeshift, [{ path: 'shift', reply: REPLY }]);
|
||||
|
||||
return shapeshift.shift('0x456', '1BTC', 'btc_eth');
|
||||
});
|
||||
@ -125,4 +141,80 @@ describe('shapeshift/calls', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('subscriptions', () => {
|
||||
const ADDRESS = '0123456789abcdef';
|
||||
const REPLY = {
|
||||
status: 'complete',
|
||||
address: ADDRESS
|
||||
};
|
||||
|
||||
let callback;
|
||||
|
||||
beforeEach(() => {
|
||||
mockget(shapeshift, [{ path: `txStat/${ADDRESS}`, reply: REPLY }]);
|
||||
callback = sinon.stub();
|
||||
shapeshift.subscribe(ADDRESS, callback);
|
||||
});
|
||||
|
||||
describe('subscribe', () => {
|
||||
it('adds the depositAddress to the list', () => {
|
||||
const subscriptions = shapeshift._getSubscriptions();
|
||||
|
||||
expect(subscriptions.length).to.equal(1);
|
||||
expect(subscriptions[0].depositAddress).to.equal(ADDRESS);
|
||||
});
|
||||
|
||||
it('starts the polling timer', () => {
|
||||
expect(shapeshift._isPolling()).to.be.true;
|
||||
});
|
||||
|
||||
it('calls the callback once the timer has elapsed', () => {
|
||||
clock.tick(2222);
|
||||
|
||||
return shapeshift._getSubscriptionPromises().then(() => {
|
||||
expect(callback).to.have.been.calledWith(null, REPLY);
|
||||
});
|
||||
});
|
||||
|
||||
it('auto-unsubscribes on completed', () => {
|
||||
clock.tick(2222);
|
||||
|
||||
return shapeshift._getSubscriptionPromises().then(() => {
|
||||
expect(shapeshift._getSubscriptions().length).to.equal(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('unsubscribe', () => {
|
||||
it('unbsubscribes when requested', () => {
|
||||
expect(shapeshift._getSubscriptions().length).to.equal(1);
|
||||
shapeshift.unsubscribe(ADDRESS);
|
||||
expect(shapeshift._getSubscriptions().length).to.equal(0);
|
||||
});
|
||||
|
||||
it('clears the polling on no subscriptions', () => {
|
||||
shapeshift.unsubscribe(ADDRESS);
|
||||
expect(shapeshift._isPolling()).to.be.false;
|
||||
});
|
||||
|
||||
it('handles unsubscribe of auto-unsubscribe', () => {
|
||||
clock.tick(2222);
|
||||
|
||||
return shapeshift._getSubscriptionPromises().then(() => {
|
||||
expect(shapeshift.unsubscribe(ADDRESS)).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
it('handles unsubscribe when multiples listed', () => {
|
||||
const ADDRESS2 = 'abcdef0123456789';
|
||||
|
||||
shapeshift.subscribe(ADDRESS2, sinon.stub());
|
||||
expect(shapeshift._getSubscriptions().length).to.equal(2);
|
||||
expect(shapeshift._getSubscriptions()[0].depositAddress).to.equal(ADDRESS);
|
||||
shapeshift.unsubscribe(ADDRESS);
|
||||
expect(shapeshift._getSubscriptions()[0].depositAddress).to.equal(ADDRESS2);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -25,7 +25,7 @@ export default class Encoder {
|
||||
throw new Error('tokens should be array of Token');
|
||||
}
|
||||
|
||||
const mediates = tokens.map((token) => Encoder.encodeToken(token));
|
||||
const mediates = tokens.map((token, index) => Encoder.encodeToken(token, index));
|
||||
const inits = mediates
|
||||
.map((mediate, idx) => mediate.init(Mediate.offsetFor(mediates, idx)))
|
||||
.join('');
|
||||
@ -36,37 +36,40 @@ export default class Encoder {
|
||||
return `${inits}${closings}`;
|
||||
}
|
||||
|
||||
static encodeToken (token) {
|
||||
static encodeToken (token, index = 0) {
|
||||
if (!isInstanceOf(token, Token)) {
|
||||
throw new Error('token should be instanceof Token');
|
||||
}
|
||||
|
||||
switch (token.type) {
|
||||
case 'address':
|
||||
return new Mediate('raw', padAddress(token.value));
|
||||
try {
|
||||
switch (token.type) {
|
||||
case 'address':
|
||||
return new Mediate('raw', padAddress(token.value));
|
||||
|
||||
case 'int':
|
||||
case 'uint':
|
||||
return new Mediate('raw', padU32(token.value));
|
||||
case 'int':
|
||||
case 'uint':
|
||||
return new Mediate('raw', padU32(token.value));
|
||||
|
||||
case 'bool':
|
||||
return new Mediate('raw', padBool(token.value));
|
||||
case 'bool':
|
||||
return new Mediate('raw', padBool(token.value));
|
||||
|
||||
case 'fixedBytes':
|
||||
return new Mediate('raw', padFixedBytes(token.value));
|
||||
case 'fixedBytes':
|
||||
return new Mediate('raw', padFixedBytes(token.value));
|
||||
|
||||
case 'bytes':
|
||||
return new Mediate('prefixed', padBytes(token.value));
|
||||
case 'bytes':
|
||||
return new Mediate('prefixed', padBytes(token.value));
|
||||
|
||||
case 'string':
|
||||
return new Mediate('prefixed', padString(token.value));
|
||||
case 'string':
|
||||
return new Mediate('prefixed', padString(token.value));
|
||||
|
||||
case 'fixedArray':
|
||||
case 'array':
|
||||
return new Mediate(token.type, token.value.map((token) => Encoder.encodeToken(token)));
|
||||
|
||||
default:
|
||||
throw new Error(`Invalid token type ${token.type} in encodeToken`);
|
||||
case 'fixedArray':
|
||||
case 'array':
|
||||
return new Mediate(token.type, token.value.map((token) => Encoder.encodeToken(token)));
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error(`Cannot encode token #${index} [${token.type}: ${token.value}]. ${e.message}`);
|
||||
}
|
||||
|
||||
throw new Error(`Invalid token type ${token.type} in encodeToken`);
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,10 @@ export default class Interface {
|
||||
}
|
||||
|
||||
encodeTokens (paramTypes, values) {
|
||||
return Interface.encodeTokens(paramTypes, values);
|
||||
}
|
||||
|
||||
static encodeTokens (paramTypes, values) {
|
||||
const createToken = function (paramType, value) {
|
||||
if (paramType.subtype) {
|
||||
return new Token(paramType.type, value.map((entry) => createToken(paramType.subtype, entry)));
|
||||
|
@ -31,6 +31,12 @@ export default class Param {
|
||||
}
|
||||
|
||||
static toParams (params) {
|
||||
return params.map((param) => new Param(param.name, param.type));
|
||||
return params.map((param) => {
|
||||
if (param instanceof Param) {
|
||||
return param;
|
||||
}
|
||||
|
||||
return new Param(param.name, param.type);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -34,5 +34,14 @@ describe('abi/spec/Param', () => {
|
||||
expect(params[0].name).to.equal('foo');
|
||||
expect(params[0].kind.type).to.equal('uint');
|
||||
});
|
||||
|
||||
it('converts only if needed', () => {
|
||||
const _params = Param.toParams([{ name: 'foo', type: 'uint' }]);
|
||||
const params = Param.toParams(_params);
|
||||
|
||||
expect(params.length).to.equal(1);
|
||||
expect(params[0].name).to.equal('foo');
|
||||
expect(params[0].kind.type).to.equal('uint');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -114,7 +114,11 @@ export default class Api {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('pollMethod', error);
|
||||
// Don't print if the request is rejected: that's ok
|
||||
if (error.type !== 'REQUEST_REJECTED') {
|
||||
console.error('pollMethod', error);
|
||||
}
|
||||
|
||||
reject(error);
|
||||
});
|
||||
};
|
||||
|
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import Abi from '../../abi';
|
||||
import Abi from '~/abi';
|
||||
|
||||
let nextSubscriptionId = 0;
|
||||
|
||||
@ -53,6 +53,10 @@ export default class Contract {
|
||||
|
||||
this._subscribedToBlock = false;
|
||||
this._blockSubscriptionId = null;
|
||||
|
||||
if (api && api.patch && api.patch.contract) {
|
||||
api.patch.contract(this);
|
||||
}
|
||||
}
|
||||
|
||||
get address () {
|
||||
@ -71,6 +75,10 @@ export default class Contract {
|
||||
return this._functions;
|
||||
}
|
||||
|
||||
get receipt () {
|
||||
return this._receipt;
|
||||
}
|
||||
|
||||
get instance () {
|
||||
this._instance.address = this._address;
|
||||
return this._instance;
|
||||
@ -90,8 +98,10 @@ export default class Contract {
|
||||
}
|
||||
|
||||
deployEstimateGas (options, values) {
|
||||
const _options = this._encodeOptions(this.constructors[0], options, values);
|
||||
|
||||
return this._api.eth
|
||||
.estimateGas(this._encodeOptions(this.constructors[0], options, values))
|
||||
.estimateGas(_options)
|
||||
.then((gasEst) => {
|
||||
return [gasEst, gasEst.mul(1.2)];
|
||||
});
|
||||
@ -115,8 +125,10 @@ export default class Contract {
|
||||
|
||||
setState({ state: 'postTransaction', gas });
|
||||
|
||||
const _options = this._encodeOptions(this.constructors[0], options, values);
|
||||
|
||||
return this._api.parity
|
||||
.postTransaction(this._encodeOptions(this.constructors[0], options, values))
|
||||
.postTransaction(_options)
|
||||
.then((requestId) => {
|
||||
setState({ state: 'checkRequest', requestId });
|
||||
return this._pollCheckRequest(requestId);
|
||||
@ -131,6 +143,7 @@ export default class Contract {
|
||||
}
|
||||
|
||||
setState({ state: 'hasReceipt', receipt });
|
||||
this._receipt = receipt;
|
||||
this._address = receipt.contractAddress;
|
||||
return this._address;
|
||||
});
|
||||
@ -199,7 +212,7 @@ export default class Contract {
|
||||
getCallData = (func, options, values) => {
|
||||
let data = options.data;
|
||||
|
||||
const tokens = func ? this._abi.encodeTokens(func.inputParamTypes(), values) : null;
|
||||
const tokens = func ? Abi.encodeTokens(func.inputParamTypes(), values) : null;
|
||||
const call = tokens ? func.encodeCall(tokens) : null;
|
||||
|
||||
if (data && data.substr(0, 2) === '0x') {
|
||||
@ -210,17 +223,24 @@ export default class Contract {
|
||||
}
|
||||
|
||||
_encodeOptions (func, options, values) {
|
||||
options.data = this.getCallData(func, options, values);
|
||||
return options;
|
||||
const data = this.getCallData(func, options, values);
|
||||
|
||||
return {
|
||||
...options,
|
||||
data
|
||||
};
|
||||
}
|
||||
|
||||
_addOptionsTo (options = {}) {
|
||||
return Object.assign({
|
||||
to: this._address
|
||||
}, options);
|
||||
return {
|
||||
to: this._address,
|
||||
...options
|
||||
};
|
||||
}
|
||||
|
||||
_bindFunction = (func) => {
|
||||
func.contract = this;
|
||||
|
||||
func.call = (options, values = []) => {
|
||||
const callParams = this._encodeOptions(func, this._addOptionsTo(options), values);
|
||||
|
||||
@ -233,13 +253,13 @@ export default class Contract {
|
||||
|
||||
if (!func.constant) {
|
||||
func.postTransaction = (options, values = []) => {
|
||||
return this._api.parity
|
||||
.postTransaction(this._encodeOptions(func, this._addOptionsTo(options), values));
|
||||
const _options = this._encodeOptions(func, this._addOptionsTo(options), values);
|
||||
return this._api.parity.postTransaction(_options);
|
||||
};
|
||||
|
||||
func.estimateGas = (options, values = []) => {
|
||||
return this._api.eth
|
||||
.estimateGas(this._encodeOptions(func, this._addOptionsTo(options), values));
|
||||
const _options = this._encodeOptions(func, this._addOptionsTo(options), values);
|
||||
return this._api.eth.estimateGas(_options);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@ const eth = new Api(transport);
|
||||
|
||||
describe('api/contract/Contract', () => {
|
||||
const ADDR = '0x0123456789';
|
||||
|
||||
const ABI = [
|
||||
{
|
||||
type: 'function', name: 'test',
|
||||
@ -41,12 +42,42 @@ describe('api/contract/Contract', () => {
|
||||
type: 'function', name: 'test2',
|
||||
outputs: [{ type: 'uint' }, { type: 'uint' }]
|
||||
},
|
||||
{ type: 'constructor' },
|
||||
{
|
||||
type: 'constructor',
|
||||
inputs: [{ name: 'boolin', type: 'bool' }, { name: 'stringin', type: 'string' }]
|
||||
},
|
||||
{ type: 'event', name: 'baz' },
|
||||
{ type: 'event', name: 'foo' }
|
||||
];
|
||||
const VALUES = [true, 'jacogr'];
|
||||
const ENCODED = '0x023562050000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000066a61636f67720000000000000000000000000000000000000000000000000000';
|
||||
|
||||
const ABI_NO_PARAMS = [
|
||||
{
|
||||
type: 'function', name: 'test',
|
||||
inputs: [{ name: 'boolin', type: 'bool' }, { name: 'stringin', type: 'string' }],
|
||||
outputs: [{ type: 'uint' }]
|
||||
},
|
||||
{
|
||||
type: 'function', name: 'test2',
|
||||
outputs: [{ type: 'uint' }, { type: 'uint' }]
|
||||
},
|
||||
{
|
||||
type: 'constructor'
|
||||
},
|
||||
{ type: 'event', name: 'baz' },
|
||||
{ type: 'event', name: 'foo' }
|
||||
];
|
||||
|
||||
const VALUES = [ true, 'jacogr' ];
|
||||
const CALLDATA = `
|
||||
0000000000000000000000000000000000000000000000000000000000000001
|
||||
0000000000000000000000000000000000000000000000000000000000000040
|
||||
0000000000000000000000000000000000000000000000000000000000000006
|
||||
6a61636f67720000000000000000000000000000000000000000000000000000
|
||||
`.replace(/\s/g, '');
|
||||
const SIGNATURE = '02356205';
|
||||
|
||||
const ENCODED = `0x${SIGNATURE}${CALLDATA}`;
|
||||
|
||||
const RETURN1 = '0000000000000000000000000000000000000000000000000000000000123456';
|
||||
const RETURN2 = '0000000000000000000000000000000000000000000000000000000000456789';
|
||||
let scope;
|
||||
@ -230,6 +261,33 @@ describe('api/contract/Contract', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('deploy without parameters', () => {
|
||||
const contract = new Contract(eth, ABI_NO_PARAMS);
|
||||
const CODE = '0x123';
|
||||
const ADDRESS = '0xD337e80eEdBdf86eDBba021797d7e4e00Bb78351';
|
||||
const RECEIPT_DONE = { contractAddress: ADDRESS.toLowerCase(), gasUsed: 50, blockNumber: 2500 };
|
||||
|
||||
let scope;
|
||||
|
||||
describe('success', () => {
|
||||
before(() => {
|
||||
scope = mockHttp([
|
||||
{ method: 'eth_estimateGas', reply: { result: 1000 } },
|
||||
{ method: 'parity_postTransaction', reply: { result: '0x678' } },
|
||||
{ method: 'parity_checkRequest', reply: { result: '0x890' } },
|
||||
{ method: 'eth_getTransactionReceipt', reply: { result: RECEIPT_DONE } },
|
||||
{ method: 'eth_getCode', reply: { result: CODE } }
|
||||
]);
|
||||
|
||||
return contract.deploy({ data: CODE }, []);
|
||||
});
|
||||
|
||||
it('passes the options through to postTransaction (incl. gas calculation)', () => {
|
||||
expect(scope.body.parity_postTransaction.params[0].data).to.equal(CODE);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('deploy', () => {
|
||||
const contract = new Contract(eth, ABI);
|
||||
const ADDRESS = '0xD337e80eEdBdf86eDBba021797d7e4e00Bb78351';
|
||||
@ -252,7 +310,7 @@ describe('api/contract/Contract', () => {
|
||||
{ method: 'eth_getCode', reply: { result: '0x456' } }
|
||||
]);
|
||||
|
||||
return contract.deploy({ data: '0x123' }, []);
|
||||
return contract.deploy({ data: '0x123' }, VALUES);
|
||||
});
|
||||
|
||||
it('calls estimateGas, postTransaction, checkRequest, getTransactionReceipt & getCode in order', () => {
|
||||
@ -261,7 +319,7 @@ describe('api/contract/Contract', () => {
|
||||
|
||||
it('passes the options through to postTransaction (incl. gas calculation)', () => {
|
||||
expect(scope.body.parity_postTransaction.params).to.deep.equal([
|
||||
{ data: '0x123', gas: '0x4b0' }
|
||||
{ data: `0x123${CALLDATA}`, gas: '0x4b0' }
|
||||
]);
|
||||
});
|
||||
|
||||
@ -280,7 +338,7 @@ describe('api/contract/Contract', () => {
|
||||
]);
|
||||
|
||||
return contract
|
||||
.deploy({ data: '0x123' }, [])
|
||||
.deploy({ data: '0x123' }, VALUES)
|
||||
.catch((error) => {
|
||||
expect(error.message).to.match(/not deployed, gasUsed/);
|
||||
});
|
||||
@ -296,7 +354,7 @@ describe('api/contract/Contract', () => {
|
||||
]);
|
||||
|
||||
return contract
|
||||
.deploy({ data: '0x123' }, [])
|
||||
.deploy({ data: '0x123' }, VALUES)
|
||||
.catch((error) => {
|
||||
expect(error.message).to.match(/not deployed, getCode/);
|
||||
});
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
import sinon from 'sinon';
|
||||
import 'sinon-as-promised';
|
||||
|
||||
import Eth from './eth';
|
||||
|
||||
|
@ -15,7 +15,6 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sinon from 'sinon';
|
||||
import 'sinon-as-promised';
|
||||
|
||||
import Personal from './personal';
|
||||
|
||||
|
@ -209,7 +209,10 @@ export default class Ws extends JsonRpcBase {
|
||||
if (result.error) {
|
||||
this.error(event.data);
|
||||
|
||||
console.error(`${method}(${JSON.stringify(params)}): ${result.error.code}: ${result.error.message}`);
|
||||
// Don't print error if request rejected...
|
||||
if (!/rejected/.test(result.error.message)) {
|
||||
console.error(`${method}(${JSON.stringify(params)}): ${result.error.code}: ${result.error.message}`);
|
||||
}
|
||||
|
||||
const error = new TransportError(method, result.error.code, result.error.message);
|
||||
reject(error);
|
||||
|
@ -26,7 +26,9 @@ export function decodeCallData (data) {
|
||||
|
||||
if (data.substr(0, 2) === '0x') {
|
||||
return decodeCallData(data.slice(2));
|
||||
} else if (data.length < 8) {
|
||||
}
|
||||
|
||||
if (data.length < 8) {
|
||||
throw new Error('Input to decodeCallData should be method signature + data');
|
||||
}
|
||||
|
||||
@ -42,13 +44,15 @@ export function decodeCallData (data) {
|
||||
export function decodeMethodInput (methodAbi, paramdata) {
|
||||
if (!methodAbi) {
|
||||
throw new Error('decodeMethodInput should receive valid method-specific ABI');
|
||||
} else if (paramdata && paramdata.length) {
|
||||
}
|
||||
|
||||
if (paramdata && paramdata.length) {
|
||||
if (!isHex(paramdata)) {
|
||||
throw new Error('Input to decodeMethodInput should be a hex value');
|
||||
} else if (paramdata.substr(0, 2) === '0x') {
|
||||
}
|
||||
|
||||
if (paramdata.substr(0, 2) === '0x') {
|
||||
return decodeMethodInput(methodAbi, paramdata.slice(2));
|
||||
} else if (paramdata.length % 64 !== 0) {
|
||||
throw new Error('Parameter length in decodeMethodInput not a multiple of 64 characters');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,10 +48,6 @@ describe('api/util/decode', () => {
|
||||
expect(() => decodeMethodInput({}, 'invalid')).to.throw(/should be a hex value/);
|
||||
});
|
||||
|
||||
it('throws on invalid lengths', () => {
|
||||
expect(() => decodeMethodInput({}, DATA.slice(-32))).to.throw(/not a multiple of/);
|
||||
});
|
||||
|
||||
it('correctly decodes valid inputs', () => {
|
||||
expect(decodeMethodInput({
|
||||
type: 'function',
|
||||
|
@ -20,15 +20,21 @@ export function bytesToHex (bytes) {
|
||||
return '0x' + bytes.map((b) => ('0' + b.toString(16)).slice(-2)).join('');
|
||||
}
|
||||
|
||||
export function hex2Ascii (_hex) {
|
||||
const hex = /^(?:0x)?(.*)$/.exec(_hex.toString())[1];
|
||||
export function hexToBytes (hex) {
|
||||
const raw = toHex(hex).slice(2);
|
||||
const bytes = [];
|
||||
|
||||
let str = '';
|
||||
|
||||
for (let i = 0; i < hex.length; i += 2) {
|
||||
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
|
||||
for (let i = 0; i < raw.length; i += 2) {
|
||||
bytes.push(parseInt(raw.substr(i, 2), 16));
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
export function hexToAscii (hex) {
|
||||
const bytes = hexToBytes(hex);
|
||||
const str = bytes.map((byte) => String.fromCharCode(byte)).join('');
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { bytesToHex } from './format';
|
||||
import { bytesToHex, hexToBytes, hexToAscii, bytesToAscii, asciiToHex } from './format';
|
||||
|
||||
describe('api/util/format', () => {
|
||||
describe('bytesToHex', () => {
|
||||
@ -26,4 +26,46 @@ describe('api/util/format', () => {
|
||||
expect(bytesToHex([0, 15, 16])).to.equal('0x000f10');
|
||||
});
|
||||
});
|
||||
|
||||
describe('hexToBytes', () => {
|
||||
it('correctly converts an empty string', () => {
|
||||
expect(hexToBytes('')).to.deep.equal([]);
|
||||
expect(hexToBytes('0x')).to.deep.equal([]);
|
||||
});
|
||||
|
||||
it('correctly converts a non-empty string', () => {
|
||||
expect(hexToBytes('0x000f10')).to.deep.equal([0, 15, 16]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('asciiToHex', () => {
|
||||
it('correctly converts an empty string', () => {
|
||||
expect(asciiToHex('')).to.equal('0x');
|
||||
});
|
||||
|
||||
it('correctly converts a non-empty string', () => {
|
||||
expect(asciiToHex('abc')).to.equal('0x616263');
|
||||
});
|
||||
});
|
||||
|
||||
describe('hexToAscii', () => {
|
||||
it('correctly converts an empty string', () => {
|
||||
expect(hexToAscii('')).to.equal('');
|
||||
expect(hexToAscii('0x')).to.equal('');
|
||||
});
|
||||
|
||||
it('correctly converts a non-empty string', () => {
|
||||
expect(hexToAscii('0x616263')).to.equal('abc');
|
||||
});
|
||||
});
|
||||
|
||||
describe('bytesToAscii', () => {
|
||||
it('correctly converts an empty string', () => {
|
||||
expect(bytesToAscii([])).to.equal('');
|
||||
});
|
||||
|
||||
it('correctly converts a non-empty string', () => {
|
||||
expect(bytesToAscii([97, 98, 99])).to.equal('abc');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -21,7 +21,7 @@ const TEST_ENV = process.env.NODE_ENV === 'test';
|
||||
|
||||
export function createIdentityImg (address, scale = 8) {
|
||||
return TEST_ENV
|
||||
? ''
|
||||
? 'test-createIdentityImg'
|
||||
: blockies({
|
||||
seed: (address || '').toLowerCase(),
|
||||
size: 8,
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
import { isAddress as isAddressValid, toChecksumAddress } from '../../abi/util/address';
|
||||
import { decodeCallData, decodeMethodInput, methodToAbi } from './decode';
|
||||
import { bytesToHex, hex2Ascii, asciiToHex } from './format';
|
||||
import { bytesToHex, hexToAscii, asciiToHex } from './format';
|
||||
import { fromWei, toWei } from './wei';
|
||||
import { sha3 } from './sha3';
|
||||
import { isArray, isFunction, isHex, isInstanceOf, isString } from './types';
|
||||
@ -30,7 +30,7 @@ export default {
|
||||
isInstanceOf,
|
||||
isString,
|
||||
bytesToHex,
|
||||
hex2Ascii,
|
||||
hexToAscii,
|
||||
asciiToHex,
|
||||
createIdentityImg,
|
||||
decodeCallData,
|
||||
|
@ -14,8 +14,22 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { keccak_256 } from 'js-sha3'; // eslint-disable-line camelcase
|
||||
import { keccak_256 } from 'js-sha3'; // eslint-disable-line
|
||||
|
||||
export function sha3 (value) {
|
||||
return `0x${keccak_256(value)}`;
|
||||
import { hexToBytes } from './format';
|
||||
import { isHex } from './types';
|
||||
|
||||
export function sha3 (value, options) {
|
||||
const forceHex = options && options.encoding === 'hex';
|
||||
|
||||
if (forceHex || (!options && isHex(value))) {
|
||||
const bytes = hexToBytes(value);
|
||||
return sha3(bytes);
|
||||
}
|
||||
|
||||
const hash = keccak_256(value);
|
||||
|
||||
return `0x${hash}`;
|
||||
}
|
||||
|
||||
sha3.text = (val) => sha3(val, { encoding: 'raw' });
|
||||
|
@ -21,5 +21,25 @@ describe('api/util/sha3', () => {
|
||||
it('constructs a correct sha3 value', () => {
|
||||
expect(sha3('jacogr')).to.equal('0x2f4ff4b5a87abbd2edfed699db48a97744e028c7f7ce36444d40d29d792aa4dc');
|
||||
});
|
||||
|
||||
it('constructs a correct sha3 encoded as hex', () => {
|
||||
const key = '000000000000000000000000391694e7e0b0cce554cb130d723a9d27458f9298' + '0000000000000000000000000000000000000000000000000000000000000001';
|
||||
expect(sha3(key, { encoding: 'hex' })).to.equal('0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9');
|
||||
expect(sha3(`0x${key}`, { encoding: 'hex' })).to.equal('0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9');
|
||||
});
|
||||
|
||||
it('constructs a correct sha3 from Uint8Array', () => {
|
||||
expect(sha3('01020304', { encoding: 'hex' })).to.equal('0xa6885b3731702da62e8e4a8f584ac46a7f6822f4e2ba50fba902f67b1588d23b');
|
||||
expect(sha3(Uint8Array.from([1, 2, 3, 4]))).to.equal('0xa6885b3731702da62e8e4a8f584ac46a7f6822f4e2ba50fba902f67b1588d23b');
|
||||
});
|
||||
|
||||
it('should interpret as bytes by default', () => {
|
||||
expect(sha3('0x01020304')).to.equal('0xa6885b3731702da62e8e4a8f584ac46a7f6822f4e2ba50fba902f67b1588d23b');
|
||||
});
|
||||
|
||||
it('should force text if option is passed', () => {
|
||||
expect(sha3('0x01020304', { encoding: 'raw' })).to.equal('0x16bff43de576d28857dcba65a56fc17c5e93c09bd6a709268eff8e62025ae869');
|
||||
expect(sha3.text('0x01020304')).to.equal('0x16bff43de576d28857dcba65a56fc17c5e93c09bd6a709268eff8e62025ae869');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -29,6 +29,10 @@ export function isFunction (test) {
|
||||
}
|
||||
|
||||
export function isHex (_test) {
|
||||
if (!isString(_test)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_test.substr(0, 2) === '0x') {
|
||||
return isHex(_test.slice(2));
|
||||
}
|
||||
|
@ -66,6 +66,12 @@ describe('api/util/types', () => {
|
||||
it('correctly identifies non-hex values', () => {
|
||||
expect(isHex('123j')).to.be.false;
|
||||
});
|
||||
|
||||
it('correctly indentifies non-string values', () => {
|
||||
expect(isHex(false)).to.be.false;
|
||||
expect(isHex()).to.be.false;
|
||||
expect(isHex([1, 2, 3])).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('isInstanceOf', () => {
|
||||
|
28
js/src/config.js
Normal file
28
js/src/config.js
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright 2015, 2016 Parity Technologies (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/>.
|
||||
|
||||
import LogLevel from 'loglevel';
|
||||
|
||||
export const LOG_KEYS = {
|
||||
TransferModalStore: {
|
||||
path: 'modals/Transfer/store',
|
||||
desc: 'Transfer Modal MobX Store'
|
||||
}
|
||||
};
|
||||
|
||||
export const getLogger = (LOG_KEY) => {
|
||||
return LogLevel.getLogger(LOG_KEY.path);
|
||||
};
|
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { bytesToHex, hex2Ascii } from '~/api/util/format';
|
||||
import { bytesToHex, hexToAscii } from '~/api/util/format';
|
||||
|
||||
import ABI from './abi/certifier.json';
|
||||
|
||||
@ -62,7 +62,7 @@ export default class BadgeReg {
|
||||
name = bytesToHex(name);
|
||||
name = name === ZERO32
|
||||
? null
|
||||
: hex2Ascii(name);
|
||||
: hexToAscii(name);
|
||||
|
||||
return this.fetchMeta(id)
|
||||
.then(({ title, icon }) => {
|
||||
@ -84,7 +84,7 @@ export default class BadgeReg {
|
||||
})
|
||||
.then(([ title, icon ]) => {
|
||||
title = bytesToHex(title);
|
||||
title = title === ZERO32 ? null : hex2Ascii(title);
|
||||
title = title === ZERO32 ? null : hexToAscii(title);
|
||||
|
||||
if (bytesToHex(icon) === ZERO32) {
|
||||
icon = null;
|
||||
|
@ -91,7 +91,7 @@ export default class Registry {
|
||||
|
||||
lookupAddress (_name) {
|
||||
const name = _name.toLowerCase();
|
||||
const sha3 = this._api.util.sha3(name);
|
||||
const sha3 = this._api.util.sha3.text(name);
|
||||
|
||||
return this.getInstance().then((instance) => {
|
||||
return instance.getAddress.call({}, [sha3, 'A']);
|
||||
|
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import subscribeToEvent from '../util/subscribe-to-event';
|
||||
import subscribeToEvents from '../util/subscribe-to-events';
|
||||
|
||||
export const checkIfVerified = (contract, account) => {
|
||||
return contract.instance.certified.call({}, [account]);
|
||||
@ -72,7 +72,7 @@ export const awaitPuzzle = (api, contract, account) => {
|
||||
return blockNumber(api)
|
||||
.then((block) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const subscription = subscribeToEvent(contract, 'Puzzled', {
|
||||
const subscription = subscribeToEvents(contract, ['Puzzled'], {
|
||||
from: block.toNumber(),
|
||||
filter: (log) => log.params.who.value === account
|
||||
});
|
||||
|
@ -118,7 +118,7 @@ export function attachInstances () {
|
||||
]);
|
||||
})
|
||||
.then(([managerAddress, registryAddress, tokenregAddress]) => {
|
||||
console.log(`contracts were found at basiccoinmgr=${managerAddress}, basiccoinreg=${registryAddress}, tokenreg=${registryAddress}`);
|
||||
console.log(`contracts were found at basiccoinmgr=${managerAddress}, basiccoinreg=${registryAddress}, tokenreg=${tokenregAddress}`);
|
||||
|
||||
managerInstance = api.newContract(abis.basiccoinmanager, managerAddress).instance;
|
||||
registryInstance = api.newContract(abis.tokenreg, registryAddress).instance;
|
||||
|
@ -34,3 +34,16 @@ ReactDOM.render(
|
||||
</Provider>,
|
||||
document.querySelector('#container')
|
||||
);
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept('./registry/Container', () => {
|
||||
require('./registry/Container');
|
||||
|
||||
ReactDOM.render(
|
||||
<Provider store={ store }>
|
||||
<Container />
|
||||
</Provider>,
|
||||
document.querySelector('#container')
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -44,8 +44,8 @@ export default class Application extends Component {
|
||||
|
||||
static propTypes = {
|
||||
accounts: PropTypes.object.isRequired,
|
||||
contract: nullableProptype(PropTypes.object).isRequired,
|
||||
fee: nullableProptype(PropTypes.object).isRequired
|
||||
contract: nullableProptype(PropTypes.object.isRequired),
|
||||
fee: nullableProptype(PropTypes.object.isRequired)
|
||||
};
|
||||
|
||||
render () {
|
||||
|
@ -15,11 +15,13 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { sha3 } from '../parity.js';
|
||||
import { getOwner } from '../util/registry';
|
||||
|
||||
export const clear = () => ({ type: 'lookup clear' });
|
||||
|
||||
export const lookupStart = (name, key) => ({ type: 'lookup start', name, key });
|
||||
export const reverseLookupStart = (address) => ({ type: 'reverseLookup start', address });
|
||||
export const ownerLookupStart = (name) => ({ type: 'ownerLookup start', name });
|
||||
|
||||
export const success = (action, result) => ({ type: `${action} success`, result: result });
|
||||
|
||||
@ -37,7 +39,7 @@ export const lookup = (name, key) => (dispatch, getState) => {
|
||||
name = name.toLowerCase();
|
||||
dispatch(lookupStart(name, key));
|
||||
|
||||
getAddress.call({}, [ sha3(name), key ])
|
||||
getAddress.call({}, [ sha3.text(name), key ])
|
||||
.then((address) => dispatch(success('lookup', address)))
|
||||
.catch((err) => {
|
||||
console.error(`could not lookup ${key} for ${name}`);
|
||||
@ -48,24 +50,50 @@ export const lookup = (name, key) => (dispatch, getState) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const reverseLookup = (address) => (dispatch, getState) => {
|
||||
export const reverseLookup = (lookupAddress) => (dispatch, getState) => {
|
||||
const { contract } = getState();
|
||||
|
||||
if (!contract) {
|
||||
return;
|
||||
}
|
||||
|
||||
const reverse = contract.functions
|
||||
.find((f) => f.name === 'reverse');
|
||||
dispatch(reverseLookupStart(lookupAddress));
|
||||
|
||||
dispatch(reverseLookupStart(address));
|
||||
|
||||
reverse.call({}, [ address ])
|
||||
.then((address) => dispatch(success('reverseLookup', address)))
|
||||
contract.instance
|
||||
.reverse
|
||||
.call({}, [ lookupAddress ])
|
||||
.then((address) => {
|
||||
dispatch(success('reverseLookup', address));
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(`could not lookup reverse for ${address}`);
|
||||
console.error(`could not lookup reverse for ${lookupAddress}`);
|
||||
if (err) {
|
||||
console.error(err.stack);
|
||||
}
|
||||
dispatch(fail('reverseLookup'));
|
||||
});
|
||||
};
|
||||
|
||||
export const ownerLookup = (name) => (dispatch, getState) => {
|
||||
const { contract } = getState();
|
||||
|
||||
if (!contract) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(ownerLookupStart(name));
|
||||
|
||||
return getOwner(contract, name)
|
||||
.then((owner) => {
|
||||
dispatch(success('ownerLookup', owner));
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(`could not lookup owner for ${name}`);
|
||||
|
||||
if (err) {
|
||||
console.error(err.stack);
|
||||
}
|
||||
|
||||
dispatch(fail('ownerLookup'));
|
||||
});
|
||||
};
|
||||
|
@ -23,13 +23,14 @@ import DropDownMenu from 'material-ui/DropDownMenu';
|
||||
import MenuItem from 'material-ui/MenuItem';
|
||||
import RaisedButton from 'material-ui/RaisedButton';
|
||||
import SearchIcon from 'material-ui/svg-icons/action/search';
|
||||
import keycode from 'keycode';
|
||||
|
||||
import { nullableProptype } from '~/util/proptypes';
|
||||
|
||||
import Address from '../ui/address.js';
|
||||
import renderImage from '../ui/image.js';
|
||||
|
||||
import { clear, lookup, reverseLookup } from './actions';
|
||||
import { clear, lookup, ownerLookup, reverseLookup } from './actions';
|
||||
import styles from './lookup.css';
|
||||
|
||||
class Lookup extends Component {
|
||||
@ -39,6 +40,7 @@ class Lookup extends Component {
|
||||
|
||||
clear: PropTypes.func.isRequired,
|
||||
lookup: PropTypes.func.isRequired,
|
||||
ownerLookup: PropTypes.func.isRequired,
|
||||
reverseLookup: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
@ -50,33 +52,6 @@ class Lookup extends Component {
|
||||
const { input, type } = this.state;
|
||||
const { result } = this.props;
|
||||
|
||||
let output = '';
|
||||
if (result) {
|
||||
if (type === 'A') {
|
||||
output = (
|
||||
<code>
|
||||
<Address
|
||||
address={ result }
|
||||
shortenHash={ false }
|
||||
/>
|
||||
</code>
|
||||
);
|
||||
} else if (type === 'IMG') {
|
||||
output = renderImage(result);
|
||||
} else if (type === 'CONTENT') {
|
||||
output = (
|
||||
<div>
|
||||
<code>{ result }</code>
|
||||
<p>Keep in mind that this is most likely the hash of the content you are looking for.</p>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
output = (
|
||||
<code>{ result }</code>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className={ styles.lookup }>
|
||||
<CardHeader title={ 'Query the Registry' } />
|
||||
@ -85,6 +60,7 @@ class Lookup extends Component {
|
||||
hintText={ type === 'reverse' ? 'address' : 'name' }
|
||||
value={ input }
|
||||
onChange={ this.onInputChange }
|
||||
onKeyDown={ this.onKeyDown }
|
||||
/>
|
||||
<DropDownMenu
|
||||
value={ type }
|
||||
@ -94,6 +70,7 @@ class Lookup extends Component {
|
||||
<MenuItem value='IMG' primaryText='IMG – hash of a picture in the blockchain' />
|
||||
<MenuItem value='CONTENT' primaryText='CONTENT – hash of a data in the blockchain' />
|
||||
<MenuItem value='reverse' primaryText='reverse – find a name for an address' />
|
||||
<MenuItem value='owner' primaryText='owner – find a the owner' />
|
||||
</DropDownMenu>
|
||||
<RaisedButton
|
||||
label='Lookup'
|
||||
@ -102,35 +79,102 @@ class Lookup extends Component {
|
||||
onTouchTap={ this.onLookupClick }
|
||||
/>
|
||||
</div>
|
||||
<CardText>{ output }</CardText>
|
||||
<CardText>
|
||||
{ this.renderOutput(type, result) }
|
||||
</CardText>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
renderOutput (type, result) {
|
||||
if (result === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (type === 'A') {
|
||||
return (
|
||||
<code>
|
||||
<Address
|
||||
address={ result }
|
||||
shortenHash={ false }
|
||||
/>
|
||||
</code>
|
||||
);
|
||||
}
|
||||
|
||||
if (type === 'owner') {
|
||||
if (!result) {
|
||||
return (
|
||||
<code>Not reserved yet</code>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<code>
|
||||
<Address
|
||||
address={ result }
|
||||
shortenHash={ false }
|
||||
/>
|
||||
</code>
|
||||
);
|
||||
}
|
||||
|
||||
if (type === 'IMG') {
|
||||
return renderImage(result);
|
||||
}
|
||||
|
||||
if (type === 'CONTENT') {
|
||||
return (
|
||||
<div>
|
||||
<code>{ result }</code>
|
||||
<p>Keep in mind that this is most likely the hash of the content you are looking for.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<code>{ result || 'No data' }</code>
|
||||
);
|
||||
}
|
||||
|
||||
onInputChange = (e) => {
|
||||
this.setState({ input: e.target.value });
|
||||
};
|
||||
}
|
||||
|
||||
onKeyDown = (event) => {
|
||||
const codeName = keycode(event);
|
||||
|
||||
if (codeName !== 'enter') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.onLookupClick();
|
||||
}
|
||||
|
||||
onTypeChange = (e, i, type) => {
|
||||
this.setState({ type });
|
||||
this.props.clear();
|
||||
};
|
||||
}
|
||||
|
||||
onLookupClick = () => {
|
||||
const { input, type } = this.state;
|
||||
|
||||
if (type === 'reverse') {
|
||||
this.props.reverseLookup(input);
|
||||
} else {
|
||||
this.props.lookup(input, type);
|
||||
return this.props.reverseLookup(input);
|
||||
}
|
||||
};
|
||||
|
||||
if (type === 'owner') {
|
||||
return this.props.ownerLookup(input);
|
||||
}
|
||||
|
||||
return this.props.lookup(input, type);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => state.lookup;
|
||||
const mapDispatchToProps = (dispatch) =>
|
||||
bindActionCreators({
|
||||
clear, lookup, reverseLookup
|
||||
clear, lookup, ownerLookup, reverseLookup
|
||||
}, dispatch);
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Lookup);
|
||||
|
@ -24,7 +24,7 @@ const initialState = {
|
||||
export default (state = initialState, action) => {
|
||||
const { type } = action;
|
||||
|
||||
if (type.slice(0, 7) !== 'lookup ' && type.slice(0, 14) !== 'reverseLookup ') {
|
||||
if (!/^(lookup|reverseLookup|ownerLookup)/.test(type)) {
|
||||
return state;
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,13 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { sha3, api } from '../parity.js';
|
||||
import { getOwner, isOwned } from '../util/registry';
|
||||
import postTx from '../util/post-tx';
|
||||
|
||||
export const clearError = () => ({
|
||||
type: 'clearError'
|
||||
});
|
||||
|
||||
const alreadyQueued = (queue, action, name) =>
|
||||
!!queue.find((entry) => entry.action === action && entry.name === name);
|
||||
|
||||
@ -24,13 +29,14 @@ export const reserveStart = (name) => ({ type: 'names reserve start', name });
|
||||
|
||||
export const reserveSuccess = (name) => ({ type: 'names reserve success', name });
|
||||
|
||||
export const reserveFail = (name) => ({ type: 'names reserve fail', name });
|
||||
export const reserveFail = (name, error) => ({ type: 'names reserve fail', name, error });
|
||||
|
||||
export const reserve = (name) => (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const account = state.accounts.selected;
|
||||
const contract = state.contract;
|
||||
const fee = state.fee;
|
||||
|
||||
if (!contract || !account) {
|
||||
return;
|
||||
}
|
||||
@ -40,27 +46,34 @@ export const reserve = (name) => (dispatch, getState) => {
|
||||
if (alreadyQueued(state.names.queue, 'reserve', name)) {
|
||||
return;
|
||||
}
|
||||
const reserve = contract.functions.find((f) => f.name === 'reserve');
|
||||
|
||||
dispatch(reserveStart(name));
|
||||
|
||||
const options = {
|
||||
from: account.address,
|
||||
value: fee
|
||||
};
|
||||
const values = [
|
||||
sha3(name)
|
||||
];
|
||||
return isOwned(contract, name)
|
||||
.then((owned) => {
|
||||
if (owned) {
|
||||
throw new Error(`"${name}" has already been reserved`);
|
||||
}
|
||||
|
||||
postTx(api, reserve, options, values)
|
||||
const { reserve } = contract.instance;
|
||||
|
||||
const options = {
|
||||
from: account.address,
|
||||
value: fee
|
||||
};
|
||||
const values = [
|
||||
sha3.text(name)
|
||||
];
|
||||
|
||||
return postTx(api, reserve, options, values);
|
||||
})
|
||||
.then((txHash) => {
|
||||
dispatch(reserveSuccess(name));
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(`could not reserve ${name}`);
|
||||
|
||||
if (err) {
|
||||
console.error(err.stack);
|
||||
if (err.type !== 'REQUEST_REJECTED') {
|
||||
console.error(`error rerserving ${name}`, err);
|
||||
return dispatch(reserveFail(name, err));
|
||||
}
|
||||
|
||||
dispatch(reserveFail(name));
|
||||
@ -71,43 +84,52 @@ export const dropStart = (name) => ({ type: 'names drop start', name });
|
||||
|
||||
export const dropSuccess = (name) => ({ type: 'names drop success', name });
|
||||
|
||||
export const dropFail = (name) => ({ type: 'names drop fail', name });
|
||||
export const dropFail = (name, error) => ({ type: 'names drop fail', name, error });
|
||||
|
||||
export const drop = (name) => (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const account = state.accounts.selected;
|
||||
const contract = state.contract;
|
||||
|
||||
if (!contract || !account) {
|
||||
return;
|
||||
}
|
||||
|
||||
name = name.toLowerCase();
|
||||
|
||||
if (alreadyQueued(state.names.queue, 'drop', name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const drop = contract.functions.find((f) => f.name === 'drop');
|
||||
|
||||
dispatch(dropStart(name));
|
||||
|
||||
const options = {
|
||||
from: account.address
|
||||
};
|
||||
const values = [
|
||||
sha3(name)
|
||||
];
|
||||
return getOwner(contract, name)
|
||||
.then((owner) => {
|
||||
if (owner.toLowerCase() !== account.address.toLowerCase()) {
|
||||
throw new Error(`you are not the owner of "${name}"`);
|
||||
}
|
||||
|
||||
postTx(api, drop, options, values)
|
||||
const { drop } = contract.instance;
|
||||
|
||||
const options = {
|
||||
from: account.address
|
||||
};
|
||||
|
||||
const values = [
|
||||
sha3.text(name)
|
||||
];
|
||||
|
||||
return postTx(api, drop, options, values);
|
||||
})
|
||||
.then((txhash) => {
|
||||
dispatch(dropSuccess(name));
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(`could not drop ${name}`);
|
||||
|
||||
if (err) {
|
||||
console.error(err.stack);
|
||||
if (err.type !== 'REQUEST_REJECTED') {
|
||||
console.error(`error dropping ${name}`, err);
|
||||
return dispatch(dropFail(name, err));
|
||||
}
|
||||
|
||||
dispatch(reserveFail(name));
|
||||
dispatch(dropFail(name));
|
||||
});
|
||||
};
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user