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.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" --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" |     - 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: |   tags: | ||||||
|     - rust |     - rust | ||||||
|     - rust-stable |     - 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 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 --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 |     - 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: |   tags: | ||||||
|     - rust |     - rust | ||||||
|     - rust-centos |     - 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.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" --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" |     - 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: |   tags: | ||||||
|     - rust |     - rust | ||||||
|     - rust-i686 |     - 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.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" --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" |     - 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: |   tags: | ||||||
|     - rust |     - rust | ||||||
|     - rust-arm |     - 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.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" --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" |     - 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: |   tags: | ||||||
|     - rust |     - rust | ||||||
|     - rust-arm |     - rust-arm | ||||||
| @ -272,7 +277,8 @@ linux-armv6: | |||||||
|     - aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/$PLATFORM |     - 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 --body target/$PLATFORM/release/parity | ||||||
|     - 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.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: |   tags: | ||||||
|     - rust |     - rust | ||||||
|     - rust-arm |     - 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.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" --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" |     - 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: |   tags: | ||||||
|     - rust |     - rust | ||||||
|     - rust-arm |     - 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.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" --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" |     - 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: |   tags: | ||||||
|     - osx |     - osx | ||||||
|   artifacts: |   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/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 --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 |     - 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: |   tags: | ||||||
|    - rust-windows |    - rust-windows | ||||||
|   artifacts: |   artifacts: | ||||||
| @ -526,6 +535,7 @@ push-release: | |||||||
|     - triggers |     - triggers | ||||||
|   image: ethcore/rust:stable |   image: ethcore/rust:stable | ||||||
|   script: |   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: |   tags: | ||||||
|     - curl |     - curl | ||||||
|  | |||||||
							
								
								
									
										236
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										236
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -1,6 +1,6 @@ | |||||||
| [root] | [root] | ||||||
| name = "parity" | name = "parity" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", |  "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)", |  "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)", |  "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)", |  "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)", |  "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "ethcore 1.5.0", |  "ethcore 1.6.0", | ||||||
|  "ethcore-dapps 1.5.0", |  "ethcore-dapps 1.6.0", | ||||||
|  "ethcore-devtools 1.5.0", |  "ethcore-devtools 1.6.0", | ||||||
|  "ethcore-io 1.5.0", |  "ethcore-io 1.6.0", | ||||||
|  "ethcore-ipc 1.5.0", |  "ethcore-ipc 1.6.0", | ||||||
|  "ethcore-ipc-codegen 1.5.0", |  "ethcore-ipc-codegen 1.6.0", | ||||||
|  "ethcore-ipc-hypervisor 1.2.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-ipc-tests 0.1.0", | ||||||
|  "ethcore-light 1.5.0", |  "ethcore-light 1.6.0", | ||||||
|  "ethcore-logger 1.5.0", |  "ethcore-logger 1.6.0", | ||||||
|  "ethcore-rpc 1.5.0", |  "ethcore-rpc 1.6.0", | ||||||
|  "ethcore-signer 1.5.0", |  "ethcore-signer 1.6.0", | ||||||
|  "ethcore-stratum 1.5.0", |  "ethcore-stratum 1.6.0", | ||||||
|  "ethcore-util 1.5.0", |  "ethcore-util 1.6.0", | ||||||
|  "ethsync 1.5.0", |  "ethsync 1.6.0", | ||||||
|  "fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", |  "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)", |  "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)", |  "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)", |  "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)", |  "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)", |  "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-reactor 0.1.0", | ||||||
|  "parity-rpc-client 1.4.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)", |  "regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "rlp 0.1.0", |  "rlp 0.1.0", | ||||||
|  "rpassword 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", |  "rpassword 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| @ -336,7 +336,7 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ethash" | name = "ethash" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", |  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "parking_lot 0.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]] | [[package]] | ||||||
| name = "ethcore" | name = "ethcore" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", |  "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)", |  "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)", |  "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)", |  "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)", |  "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-bloom-journal 0.1.0", | ||||||
|  "ethcore-devtools 1.5.0", |  "ethcore-devtools 1.6.0", | ||||||
|  "ethcore-io 1.5.0", |  "ethcore-io 1.6.0", | ||||||
|  "ethcore-ipc 1.5.0", |  "ethcore-ipc 1.6.0", | ||||||
|  "ethcore-ipc-codegen 1.5.0", |  "ethcore-ipc-codegen 1.6.0", | ||||||
|  "ethcore-ipc-nano 1.5.0", |  "ethcore-ipc-nano 1.6.0", | ||||||
|  "ethcore-util 1.5.0", |  "ethcore-util 1.6.0", | ||||||
|  "ethjson 0.1.0", |  "ethjson 0.1.0", | ||||||
|  "ethkey 0.2.0", |  "ethkey 0.2.0", | ||||||
|  "ethstore 0.1.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)", |  "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)", |  "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)", |  "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| @ -403,13 +403,13 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ethcore-dapps" | name = "ethcore-dapps" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", |  "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)", |  "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "ethcore-devtools 1.5.0", |  "ethcore-devtools 1.6.0", | ||||||
|  "ethcore-rpc 1.5.0", |  "ethcore-rpc 1.6.0", | ||||||
|  "ethcore-util 1.5.0", |  "ethcore-util 1.6.0", | ||||||
|  "fetch 0.1.0", |  "fetch 0.1.0", | ||||||
|  "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", |  "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)", |  "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 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", |  "mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "parity-dapps-glue 1.4.0 (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-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)", |  "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", |  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "serde 0.8.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]] | [[package]] | ||||||
| name = "ethcore-devtools" | name = "ethcore-devtools" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", |  "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ethcore-io" | name = "ethcore-io" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", |  "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)", |  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| @ -454,17 +454,17 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ethcore-ipc" | name = "ethcore-ipc" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "ethcore-devtools 1.5.0", |  "ethcore-devtools 1.6.0", | ||||||
|  "ethcore-util 1.5.0", |  "ethcore-util 1.6.0", | ||||||
|  "nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)", |  "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)", |  "semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ethcore-ipc-codegen" | name = "ethcore-ipc-codegen" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", |  "aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "quasi 0.11.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" | name = "ethcore-ipc-hypervisor" | ||||||
| version = "1.2.0" | version = "1.2.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "ethcore-ipc 1.5.0", |  "ethcore-ipc 1.6.0", | ||||||
|  "ethcore-ipc-codegen 1.5.0", |  "ethcore-ipc-codegen 1.6.0", | ||||||
|  "ethcore-ipc-nano 1.5.0", |  "ethcore-ipc-nano 1.6.0", | ||||||
|  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", |  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)", |  "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)", |  "semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| @ -488,9 +488,9 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ethcore-ipc-nano" | name = "ethcore-ipc-nano" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| dependencies = [ | 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)", |  "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", |  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)", |  "nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)", | ||||||
| @ -500,11 +500,11 @@ dependencies = [ | |||||||
| name = "ethcore-ipc-tests" | name = "ethcore-ipc-tests" | ||||||
| version = "0.1.0" | version = "0.1.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "ethcore-devtools 1.5.0", |  "ethcore-devtools 1.6.0", | ||||||
|  "ethcore-ipc 1.5.0", |  "ethcore-ipc 1.6.0", | ||||||
|  "ethcore-ipc-codegen 1.5.0", |  "ethcore-ipc-codegen 1.6.0", | ||||||
|  "ethcore-ipc-nano 1.5.0", |  "ethcore-ipc-nano 1.6.0", | ||||||
|  "ethcore-util 1.5.0", |  "ethcore-util 1.6.0", | ||||||
|  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", |  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)", |  "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)", |  "semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| @ -512,14 +512,14 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ethcore-light" | name = "ethcore-light" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "ethcore 1.5.0", |  "ethcore 1.6.0", | ||||||
|  "ethcore-io 1.5.0", |  "ethcore-io 1.6.0", | ||||||
|  "ethcore-ipc 1.5.0", |  "ethcore-ipc 1.6.0", | ||||||
|  "ethcore-ipc-codegen 1.5.0", |  "ethcore-ipc-codegen 1.6.0", | ||||||
|  "ethcore-network 1.5.0", |  "ethcore-network 1.6.0", | ||||||
|  "ethcore-util 1.5.0", |  "ethcore-util 1.6.0", | ||||||
|  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", |  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "rlp 0.1.0", |  "rlp 0.1.0", | ||||||
|  "smallvec 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", |  "smallvec 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| @ -528,10 +528,10 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ethcore-logger" | name = "ethcore-logger" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", |  "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)", |  "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)", |  "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", |  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| @ -541,13 +541,13 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ethcore-network" | name = "ethcore-network" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", |  "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)", |  "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "ethcore-devtools 1.5.0", |  "ethcore-devtools 1.6.0", | ||||||
|  "ethcore-io 1.5.0", |  "ethcore-io 1.6.0", | ||||||
|  "ethcore-util 1.5.0", |  "ethcore-util 1.6.0", | ||||||
|  "ethcrypto 0.1.0", |  "ethcrypto 0.1.0", | ||||||
|  "ethkey 0.2.0", |  "ethkey 0.2.0", | ||||||
|  "igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", |  "igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| @ -566,20 +566,20 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ethcore-rpc" | name = "ethcore-rpc" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", |  "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "ethash 1.5.0", |  "ethash 1.6.0", | ||||||
|  "ethcore 1.5.0", |  "ethcore 1.6.0", | ||||||
|  "ethcore-devtools 1.5.0", |  "ethcore-devtools 1.6.0", | ||||||
|  "ethcore-io 1.5.0", |  "ethcore-io 1.6.0", | ||||||
|  "ethcore-ipc 1.5.0", |  "ethcore-ipc 1.6.0", | ||||||
|  "ethcore-util 1.5.0", |  "ethcore-util 1.6.0", | ||||||
|  "ethcrypto 0.1.0", |  "ethcrypto 0.1.0", | ||||||
|  "ethjson 0.1.0", |  "ethjson 0.1.0", | ||||||
|  "ethkey 0.2.0", |  "ethkey 0.2.0", | ||||||
|  "ethstore 0.1.0", |  "ethstore 0.1.0", | ||||||
|  "ethsync 1.5.0", |  "ethsync 1.6.0", | ||||||
|  "fetch 0.1.0", |  "fetch 0.1.0", | ||||||
|  "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", |  "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)", |  "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)", |  "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)", |  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "parity-reactor 0.1.0", |  "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)", |  "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "rlp 0.1.0", |  "rlp 0.1.0", | ||||||
|  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", |  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| @ -602,18 +602,18 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ethcore-signer" | name = "ethcore-signer" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", |  "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)", |  "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "ethcore-devtools 1.5.0", |  "ethcore-devtools 1.6.0", | ||||||
|  "ethcore-io 1.5.0", |  "ethcore-io 1.6.0", | ||||||
|  "ethcore-rpc 1.5.0", |  "ethcore-rpc 1.6.0", | ||||||
|  "ethcore-util 1.5.0", |  "ethcore-util 1.6.0", | ||||||
|  "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", |  "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)", |  "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-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)", |  "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", |  "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "ws 0.5.3 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)", |  "ws 0.5.3 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)", | ||||||
| @ -621,14 +621,14 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ethcore-stratum" | name = "ethcore-stratum" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "env_logger 0.3.3 (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-devtools 1.6.0", | ||||||
|  "ethcore-ipc 1.5.0", |  "ethcore-ipc 1.6.0", | ||||||
|  "ethcore-ipc-codegen 1.5.0", |  "ethcore-ipc-codegen 1.6.0", | ||||||
|  "ethcore-ipc-nano 1.5.0", |  "ethcore-ipc-nano 1.6.0", | ||||||
|  "ethcore-util 1.5.0", |  "ethcore-util 1.6.0", | ||||||
|  "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", |  "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)", |  "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)", |  "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| @ -639,7 +639,7 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ethcore-util" | name = "ethcore-util" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", |  "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)", |  "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)", |  "eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)", | ||||||
|  "ethcore-bigint 0.1.2", |  "ethcore-bigint 0.1.2", | ||||||
|  "ethcore-bloom-journal 0.1.0", |  "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)", |  "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)", |  "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)", |  "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| @ -688,7 +688,7 @@ dependencies = [ | |||||||
| name = "ethjson" | name = "ethjson" | ||||||
| version = "0.1.0" | version = "0.1.0" | ||||||
| dependencies = [ | 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)", |  "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 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)", |  "serde_codegen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| @ -732,19 +732,19 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ethsync" | name = "ethsync" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", |  "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)", |  "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "ethcore 1.5.0", |  "ethcore 1.6.0", | ||||||
|  "ethcore-devtools 1.5.0", |  "ethcore-devtools 1.6.0", | ||||||
|  "ethcore-io 1.5.0", |  "ethcore-io 1.6.0", | ||||||
|  "ethcore-ipc 1.5.0", |  "ethcore-ipc 1.6.0", | ||||||
|  "ethcore-ipc-codegen 1.5.0", |  "ethcore-ipc-codegen 1.6.0", | ||||||
|  "ethcore-ipc-nano 1.5.0", |  "ethcore-ipc-nano 1.6.0", | ||||||
|  "ethcore-light 1.5.0", |  "ethcore-light 1.6.0", | ||||||
|  "ethcore-network 1.5.0", |  "ethcore-network 1.6.0", | ||||||
|  "ethcore-util 1.5.0", |  "ethcore-util 1.6.0", | ||||||
|  "ethkey 0.2.0", |  "ethkey 0.2.0", | ||||||
|  "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", |  "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", |  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| @ -757,7 +757,7 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "evmjit" | name = "evmjit" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", |  "tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| ] | ] | ||||||
| @ -919,11 +919,11 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ipc-common-types" | name = "ipc-common-types" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "ethcore-ipc 1.5.0", |  "ethcore-ipc 1.6.0", | ||||||
|  "ethcore-ipc-codegen 1.5.0", |  "ethcore-ipc-codegen 1.6.0", | ||||||
|  "ethcore-util 1.5.0", |  "ethcore-util 1.6.0", | ||||||
|  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", |  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "semver 0.5.1 (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]] | [[package]] | ||||||
| name = "parity-hash-fetch" | name = "parity-hash-fetch" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", |  "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", |  "fetch 0.1.0", | ||||||
|  "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", |  "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)", |  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| @ -1466,9 +1466,9 @@ dependencies = [ | |||||||
| name = "parity-rpc-client" | name = "parity-rpc-client" | ||||||
| version = "1.4.0" | version = "1.4.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "ethcore-rpc 1.5.0", |  "ethcore-rpc 1.6.0", | ||||||
|  "ethcore-signer 1.5.0", |  "ethcore-signer 1.6.0", | ||||||
|  "ethcore-util 1.5.0", |  "ethcore-util 1.6.0", | ||||||
|  "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", |  "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)", |  "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)", |  "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| @ -1484,7 +1484,7 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "parity-ui" | name = "parity-ui" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "parity-ui-dev 1.4.0", |  "parity-ui-dev 1.4.0", | ||||||
|  "parity-ui-precompiled 1.4.0 (git+https://github.com/ethcore/js-precompiled.git)", |  "parity-ui-precompiled 1.4.0 (git+https://github.com/ethcore/js-precompiled.git)", | ||||||
| @ -1501,24 +1501,24 @@ dependencies = [ | |||||||
| [[package]] | [[package]] | ||||||
| name = "parity-ui-precompiled" | name = "parity-ui-precompiled" | ||||||
| version = "1.4.0" | 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 = [ | dependencies = [ | ||||||
|  "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", |  "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "parity-updater" | name = "parity-updater" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", |  "ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "ethcore 1.5.0", |  "ethcore 1.6.0", | ||||||
|  "ethcore-ipc 1.5.0", |  "ethcore-ipc 1.6.0", | ||||||
|  "ethcore-ipc-codegen 1.5.0", |  "ethcore-ipc-codegen 1.6.0", | ||||||
|  "ethcore-util 1.5.0", |  "ethcore-util 1.6.0", | ||||||
|  "ethsync 1.5.0", |  "ethsync 1.6.0", | ||||||
|  "ipc-common-types 1.5.0", |  "ipc-common-types 1.6.0", | ||||||
|  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", |  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "parity-hash-fetch 1.5.0", |  "parity-hash-fetch 1.6.0", | ||||||
|  "parity-reactor 0.1.0", |  "parity-reactor 0.1.0", | ||||||
|  "target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", |  "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" | version = "1.4.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "ethcore-bigint 0.1.2", |  "ethcore-bigint 0.1.2", | ||||||
|  "ethcore-rpc 1.5.0", |  "ethcore-rpc 1.6.0", | ||||||
|  "ethcore-util 1.5.0", |  "ethcore-util 1.6.0", | ||||||
|  "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", |  "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "parity-rpc-client 1.4.0", |  "parity-rpc-client 1.4.0", | ||||||
|  "rpassword 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", |  "rpassword 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| [package] | [package] | ||||||
| description = "Parity Ethereum client" | description = "Parity Ethereum client" | ||||||
| name = "parity" | name = "parity" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| license = "GPL-3.0" | license = "GPL-3.0" | ||||||
| authors = ["Parity Technologies <admin@parity.io>"] | authors = ["Parity Technologies <admin@parity.io>"] | ||||||
| build = "build.rs" | build = "build.rs" | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| [package] | [package] | ||||||
| description = "Parity Dapps crate" | description = "Parity Dapps crate" | ||||||
| name = "ethcore-dapps" | name = "ethcore-dapps" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| license = "GPL-3.0" | license = "GPL-3.0" | ||||||
| authors = ["Parity Technologies <admin@parity.io>"] | authors = ["Parity Technologies <admin@parity.io>"] | ||||||
| build = "build.rs" | build = "build.rs" | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| [package] | [package] | ||||||
| description = "Base Package for all Parity built-in dapps" | description = "Base Package for all Parity built-in dapps" | ||||||
| name = "parity-dapps-glue" | name = "parity-dapps-glue" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| license = "GPL-3.0" | license = "GPL-3.0" | ||||||
| authors = ["Parity Technologies <admin@parity.io>"] | authors = ["Parity Technologies <admin@parity.io>"] | ||||||
| build = "build.rs" | build = "build.rs" | ||||||
|  | |||||||
| @ -17,7 +17,7 @@ | |||||||
| use std::io; | use std::io; | ||||||
| use std::io::Read; | use std::io::Read; | ||||||
| use std::fs; | use std::fs; | ||||||
| use std::path::PathBuf; | use std::path::{Path, PathBuf}; | ||||||
| use page::{LocalPageEndpoint, PageCache}; | use page::{LocalPageEndpoint, PageCache}; | ||||||
| use endpoint::{Endpoints, EndpointInfo}; | use endpoint::{Endpoints, EndpointInfo}; | ||||||
| use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest}; | use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest}; | ||||||
| @ -28,10 +28,79 @@ struct LocalDapp { | |||||||
| 	info: EndpointInfo, | 	info: EndpointInfo, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn local_dapps(dapps_path: String) -> Vec<LocalDapp> { | /// Tries to find and read manifest file in given `path` to extract `EndpointInfo`
 | ||||||
| 	let files = fs::read_dir(dapps_path.as_str()); | /// 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 { | 	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![]; | 		return vec![]; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -59,51 +128,6 @@ fn local_dapps(dapps_path: String) -> Vec<LocalDapp> { | |||||||
| 			} | 			} | ||||||
| 			m.ok() | 			m.ok() | ||||||
| 		}) | 		}) | ||||||
| 		.map(|(name, path)| { | 		.map(|(name, path)| local_dapp(name, path)) | ||||||
| 			// try to get manifest file
 |  | ||||||
| 			let info = read_manifest(&name, path.clone()); |  | ||||||
| 			LocalDapp { |  | ||||||
| 				id: name, |  | ||||||
| 				path: path, |  | ||||||
| 				info: info, |  | ||||||
| 			} |  | ||||||
| 		}) |  | ||||||
| 		.collect() | 		.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
 | // You should have received a copy of the GNU General Public License
 | ||||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
| 
 | 
 | ||||||
|  | use std::path::PathBuf; | ||||||
| use std::sync::Arc; | use std::sync::Arc; | ||||||
| use endpoint::{Endpoints, Endpoint}; | use endpoint::{Endpoints, Endpoint}; | ||||||
| use page::PageEndpoint; | use page::PageEndpoint; | ||||||
| @ -43,7 +44,8 @@ pub fn utils() -> Box<Endpoint> { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn all_endpoints<F: Fetch>( | pub fn all_endpoints<F: Fetch>( | ||||||
| 	dapps_path: String, | 	dapps_path: PathBuf, | ||||||
|  | 	extra_dapps: Vec<PathBuf>, | ||||||
| 	signer_address: Option<(String, u16)>, | 	signer_address: Option<(String, u16)>, | ||||||
| 	web_proxy_tokens: Arc<WebProxyTokens>, | 	web_proxy_tokens: Arc<WebProxyTokens>, | ||||||
| 	remote: Remote, | 	remote: Remote, | ||||||
| @ -51,6 +53,13 @@ pub fn all_endpoints<F: Fetch>( | |||||||
| ) -> Endpoints { | ) -> Endpoints { | ||||||
| 	// fetch fs dapps at first to avoid overwriting builtins
 | 	// fetch fs dapps at first to avoid overwriting builtins
 | ||||||
| 	let mut pages = fs::local_endpoints(dapps_path, signer_address.clone()); | 	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
 | 	// NOTE [ToDr] Dapps will be currently embeded on 8180
 | ||||||
| 	insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(signer_address.clone())); | 	insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(signer_address.clone())); | ||||||
|  | |||||||
| @ -88,6 +88,7 @@ mod web; | |||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod tests; | mod tests; | ||||||
| 
 | 
 | ||||||
|  | use std::path::{Path, PathBuf}; | ||||||
| use std::sync::{Arc, Mutex}; | use std::sync::{Arc, Mutex}; | ||||||
| use std::net::SocketAddr; | use std::net::SocketAddr; | ||||||
| use std::collections::HashMap; | 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.
 | /// Webapps HTTP+RPC server build.
 | ||||||
| pub struct ServerBuilder<T: Fetch = FetchClient> { | pub struct ServerBuilder<T: Fetch = FetchClient> { | ||||||
| 	dapps_path: String, | 	dapps_path: PathBuf, | ||||||
|  | 	extra_dapps: Vec<PathBuf>, | ||||||
| 	handler: Arc<IoHandler>, | 	handler: Arc<IoHandler>, | ||||||
| 	registrar: Arc<ContractClient>, | 	registrar: Arc<ContractClient>, | ||||||
| 	sync_status: Arc<SyncStatus>, | 	sync_status: Arc<SyncStatus>, | ||||||
| @ -141,9 +143,10 @@ impl<T: Fetch> Extendable for ServerBuilder<T> { | |||||||
| 
 | 
 | ||||||
| impl ServerBuilder { | impl ServerBuilder { | ||||||
| 	/// Construct new dapps server
 | 	/// 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 { | 		ServerBuilder { | ||||||
| 			dapps_path: dapps_path, | 			dapps_path: dapps_path.as_ref().to_owned(), | ||||||
|  | 			extra_dapps: vec![], | ||||||
| 			handler: Arc::new(IoHandler::new()), | 			handler: Arc::new(IoHandler::new()), | ||||||
| 			registrar: registrar, | 			registrar: registrar, | ||||||
| 			sync_status: Arc::new(|| false), | 			sync_status: Arc::new(|| false), | ||||||
| @ -160,6 +163,7 @@ impl<T: Fetch> ServerBuilder<T> { | |||||||
| 	pub fn fetch<X: Fetch>(self, fetch: X) -> ServerBuilder<X> { | 	pub fn fetch<X: Fetch>(self, fetch: X) -> ServerBuilder<X> { | ||||||
| 		ServerBuilder { | 		ServerBuilder { | ||||||
| 			dapps_path: self.dapps_path, | 			dapps_path: self.dapps_path, | ||||||
|  | 			extra_dapps: vec![], | ||||||
| 			handler: self.handler, | 			handler: self.handler, | ||||||
| 			registrar: self.registrar, | 			registrar: self.registrar, | ||||||
| 			sync_status: self.sync_status, | 			sync_status: self.sync_status, | ||||||
| @ -188,6 +192,12 @@ impl<T: Fetch> ServerBuilder<T> { | |||||||
| 		self | 		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,
 | 	/// Asynchronously start server with no authentication,
 | ||||||
| 	/// returns result with `Server` handle on success or an error.
 | 	/// returns result with `Server` handle on success or an error.
 | ||||||
| 	pub fn start_unsecured_http(self, addr: &SocketAddr, hosts: Option<Vec<String>>) -> Result<Server, ServerError> { | 	pub fn start_unsecured_http(self, addr: &SocketAddr, hosts: Option<Vec<String>>) -> Result<Server, ServerError> { | ||||||
| @ -197,6 +207,7 @@ impl<T: Fetch> ServerBuilder<T> { | |||||||
| 			NoAuth, | 			NoAuth, | ||||||
| 			self.handler.clone(), | 			self.handler.clone(), | ||||||
| 			self.dapps_path.clone(), | 			self.dapps_path.clone(), | ||||||
|  | 			self.extra_dapps.clone(), | ||||||
| 			self.signer_address.clone(), | 			self.signer_address.clone(), | ||||||
| 			self.registrar.clone(), | 			self.registrar.clone(), | ||||||
| 			self.sync_status.clone(), | 			self.sync_status.clone(), | ||||||
| @ -215,6 +226,7 @@ impl<T: Fetch> ServerBuilder<T> { | |||||||
| 			HttpBasicAuth::single_user(username, password), | 			HttpBasicAuth::single_user(username, password), | ||||||
| 			self.handler.clone(), | 			self.handler.clone(), | ||||||
| 			self.dapps_path.clone(), | 			self.dapps_path.clone(), | ||||||
|  | 			self.extra_dapps.clone(), | ||||||
| 			self.signer_address.clone(), | 			self.signer_address.clone(), | ||||||
| 			self.registrar.clone(), | 			self.registrar.clone(), | ||||||
| 			self.sync_status.clone(), | 			self.sync_status.clone(), | ||||||
| @ -270,7 +282,8 @@ impl Server { | |||||||
| 		hosts: Option<Vec<String>>, | 		hosts: Option<Vec<String>>, | ||||||
| 		authorization: A, | 		authorization: A, | ||||||
| 		handler: Arc<IoHandler>, | 		handler: Arc<IoHandler>, | ||||||
| 		dapps_path: String, | 		dapps_path: PathBuf, | ||||||
|  | 		extra_dapps: Vec<PathBuf>, | ||||||
| 		signer_address: Option<(String, u16)>, | 		signer_address: Option<(String, u16)>, | ||||||
| 		registrar: Arc<ContractClient>, | 		registrar: Arc<ContractClient>, | ||||||
| 		sync_status: Arc<SyncStatus>, | 		sync_status: Arc<SyncStatus>, | ||||||
| @ -287,7 +300,14 @@ impl Server { | |||||||
| 			remote.clone(), | 			remote.clone(), | ||||||
| 			fetch.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 cors_domains = Self::cors_domains(signer_address.clone()); | ||||||
| 
 | 
 | ||||||
| 		let special = Arc::new({ | 		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(); | 	let mut dapps_path = env::temp_dir(); | ||||||
| 	dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading"); | 	dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading"); | ||||||
| 	let server = process(ServerBuilder::new( | 	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))) | 		.signer_address(Some(("127.0.0.1".into(), SIGNER_PORT))) | ||||||
| 		.start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), hosts).unwrap(); | 		.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 registrar = Arc::new(FakeRegistrar::new()); | ||||||
| 	let mut dapps_path = env::temp_dir(); | 	let mut dapps_path = env::temp_dir(); | ||||||
| 	dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading"); | 	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))) | 		.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() | 		.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" | homepage = "http://parity.io" | ||||||
| license = "GPL-3.0" | license = "GPL-3.0" | ||||||
| name = "parity-ui" | name = "parity-ui" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| authors = ["Parity Technologies <admin@parity.io>"] | authors = ["Parity Technologies <admin@parity.io>"] | ||||||
| 
 | 
 | ||||||
| [build-dependencies] | [build-dependencies] | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ description = "Ethcore Database" | |||||||
| homepage = "http://parity.io" | homepage = "http://parity.io" | ||||||
| license = "GPL-3.0" | license = "GPL-3.0" | ||||||
| name = "ethcore-db" | name = "ethcore-db" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| authors = ["Parity Technologies <admin@parity.io>"] | authors = ["Parity Technologies <admin@parity.io>"] | ||||||
| build = "build.rs" | build = "build.rs" | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ description = "Ethcore development/test/build tools" | |||||||
| homepage = "http://parity.io" | homepage = "http://parity.io" | ||||||
| license = "GPL-3.0" | license = "GPL-3.0" | ||||||
| name = "ethcore-devtools" | name = "ethcore-devtools" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| authors = ["Parity Technologies <admin@parity.io>"] | authors = ["Parity Technologies <admin@parity.io>"] | ||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| [package] | [package] | ||||||
| name = "ethash" | name = "ethash" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| authors = ["Parity Technologies <admin@parity.io>"] | authors = ["Parity Technologies <admin@parity.io>"] | ||||||
| 
 | 
 | ||||||
| [lib] | [lib] | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ description = "Ethcore library" | |||||||
| homepage = "http://parity.io" | homepage = "http://parity.io" | ||||||
| license = "GPL-3.0" | license = "GPL-3.0" | ||||||
| name = "ethcore" | name = "ethcore" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| authors = ["Parity Technologies <admin@parity.io>"] | authors = ["Parity Technologies <admin@parity.io>"] | ||||||
| build = "build.rs" | build = "build.rs" | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ description = "Parity LES primitives" | |||||||
| homepage = "http://parity.io" | homepage = "http://parity.io" | ||||||
| license = "GPL-3.0" | license = "GPL-3.0" | ||||||
| name = "ethcore-light" | name = "ethcore-light" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| authors = ["Parity Technologies <admin@parity.io>"] | authors = ["Parity Technologies <admin@parity.io>"] | ||||||
| build = "build.rs" | build = "build.rs" | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -6,12 +6,14 @@ | |||||||
| 				"gasLimitBoundDivisor": "0x0400", | 				"gasLimitBoundDivisor": "0x0400", | ||||||
| 				"stepDuration": 1, | 				"stepDuration": 1, | ||||||
| 				"startStep": 2, | 				"startStep": 2, | ||||||
| 				"authorities" : [ | 				"validators": { | ||||||
|  | 					"list": [ | ||||||
| 						"0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e", | 						"0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e", | ||||||
| 						"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1" | 						"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1" | ||||||
| 					] | 					] | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  | 		} | ||||||
| 	}, | 	}, | ||||||
| 	"params": { | 	"params": { | ||||||
| 		"accountStartNonce": "0x0", | 		"accountStartNonce": "0x0", | ||||||
|  | |||||||
| @ -5,7 +5,9 @@ | |||||||
| 			"params": { | 			"params": { | ||||||
| 				"gasLimitBoundDivisor": "0x0400", | 				"gasLimitBoundDivisor": "0x0400", | ||||||
| 				"durationLimit": "0x0d", | 				"durationLimit": "0x0d", | ||||||
| 				"authorities" : ["0x9cce34f7ab185c7aba1b7c8140d620b4bda941d6"] | 				"validators": { | ||||||
|  | 					"list": ["0x9cce34f7ab185c7aba1b7c8140d620b4bda941d6"] | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
| @ -17,7 +19,7 @@ | |||||||
| 	}, | 	}, | ||||||
| 	"genesis": { | 	"genesis": { | ||||||
| 		"seal": { | 		"seal": { | ||||||
| 			"generic": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa" | 			"generic": "0xc180" | ||||||
| 		}, | 		}, | ||||||
| 		"difficulty": "0x20000", | 		"difficulty": "0x20000", | ||||||
| 		"author": "0x0000000000000000000000000000000000000000", | 		"author": "0x0000000000000000000000000000000000000000", | ||||||
|  | |||||||
| @ -4,12 +4,14 @@ | |||||||
| 		"tendermint": { | 		"tendermint": { | ||||||
| 			"params": { | 			"params": { | ||||||
| 				"gasLimitBoundDivisor": "0x0400", | 				"gasLimitBoundDivisor": "0x0400", | ||||||
| 				"authorities" : [ | 				"validators" : { | ||||||
|  | 					"list": [ | ||||||
| 						"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1", | 						"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1", | ||||||
| 						"0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e" | 						"0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e" | ||||||
| 					] | 					] | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  | 		} | ||||||
| 	}, | 	}, | ||||||
| 	"params": { | 	"params": { | ||||||
| 		"accountStartNonce": "0x0", | 		"accountStartNonce": "0x0", | ||||||
|  | |||||||
							
								
								
									
										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()) | 		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.
 | 	/// Returns addresses of all accounts.
 | ||||||
| 	pub fn accounts(&self) -> Result<Vec<Address>, Error> { | 	pub fn accounts(&self) -> Result<Vec<Address>, Error> { | ||||||
| 		let accounts = self.sstore.accounts()?; | 		let accounts = self.sstore.accounts()?; | ||||||
|  | |||||||
| @ -16,6 +16,7 @@ | |||||||
| 
 | 
 | ||||||
| //! Blockchain block.
 | //! Blockchain block.
 | ||||||
| 
 | 
 | ||||||
|  | use std::cmp; | ||||||
| use std::sync::Arc; | use std::sync::Arc; | ||||||
| use std::collections::HashSet; | 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.set_extra_data(extra_data); | ||||||
| 		r.block.base.header.note_dirty(); | 		r.block.base.header.note_dirty(); | ||||||
| 
 | 
 | ||||||
| 		let gas_floor_target = ::std::cmp::max(gas_range_target.0, engine.params().min_gas_limit); | 		let gas_floor_target = 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_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); | 		engine.on_new_block(&mut r.block); | ||||||
| 		Ok(r) | 		Ok(r) | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -378,7 +378,8 @@ impl BlockProvider for BlockChain { | |||||||
| 					.enumerate() | 					.enumerate() | ||||||
| 					.flat_map(move |(index, (mut logs, tx_hash))| { | 					.flat_map(move |(index, (mut logs, tx_hash))| { | ||||||
| 						let current_log_index = log_index; | 						let current_log_index = log_index; | ||||||
| 						log_index -= logs.len(); | 						let no_of_logs = logs.len(); | ||||||
|  | 						log_index -= no_of_logs; | ||||||
| 
 | 
 | ||||||
| 						logs.reverse(); | 						logs.reverse(); | ||||||
| 						logs.into_iter() | 						logs.into_iter() | ||||||
| @ -390,6 +391,7 @@ impl BlockProvider for BlockChain { | |||||||
| 								transaction_hash: tx_hash, | 								transaction_hash: tx_hash, | ||||||
| 								// iterating in reverse order
 | 								// iterating in reverse order
 | ||||||
| 								transaction_index: receipts_len - index - 1, | 								transaction_index: receipts_len - index - 1, | ||||||
|  | 								transaction_log_index: no_of_logs - i - 1, | ||||||
| 								log_index: current_log_index - i - 1, | 								log_index: current_log_index - i - 1, | ||||||
| 							}) | 							}) | ||||||
| 					}) | 					}) | ||||||
| @ -1936,6 +1938,7 @@ mod tests { | |||||||
| 				block_number: block1.header().number(), | 				block_number: block1.header().number(), | ||||||
| 				transaction_hash: tx_hash1.clone(), | 				transaction_hash: tx_hash1.clone(), | ||||||
| 				transaction_index: 0, | 				transaction_index: 0, | ||||||
|  | 				transaction_log_index: 0, | ||||||
| 				log_index: 0, | 				log_index: 0, | ||||||
| 			}, | 			}, | ||||||
| 			LocalizedLogEntry { | 			LocalizedLogEntry { | ||||||
| @ -1944,6 +1947,7 @@ mod tests { | |||||||
| 				block_number: block1.header().number(), | 				block_number: block1.header().number(), | ||||||
| 				transaction_hash: tx_hash1.clone(), | 				transaction_hash: tx_hash1.clone(), | ||||||
| 				transaction_index: 0, | 				transaction_index: 0, | ||||||
|  | 				transaction_log_index: 1, | ||||||
| 				log_index: 1, | 				log_index: 1, | ||||||
| 			}, | 			}, | ||||||
| 			LocalizedLogEntry { | 			LocalizedLogEntry { | ||||||
| @ -1952,6 +1956,7 @@ mod tests { | |||||||
| 				block_number: block1.header().number(), | 				block_number: block1.header().number(), | ||||||
| 				transaction_hash: tx_hash2.clone(), | 				transaction_hash: tx_hash2.clone(), | ||||||
| 				transaction_index: 1, | 				transaction_index: 1, | ||||||
|  | 				transaction_log_index: 0, | ||||||
| 				log_index: 2, | 				log_index: 2, | ||||||
| 			}, | 			}, | ||||||
| 			LocalizedLogEntry { | 			LocalizedLogEntry { | ||||||
| @ -1960,6 +1965,7 @@ mod tests { | |||||||
| 				block_number: block2.header().number(), | 				block_number: block2.header().number(), | ||||||
| 				transaction_hash: tx_hash3.clone(), | 				transaction_hash: tx_hash3.clone(), | ||||||
| 				transaction_index: 0, | 				transaction_index: 0, | ||||||
|  | 				transaction_log_index: 0, | ||||||
| 				log_index: 0, | 				log_index: 0, | ||||||
| 			} | 			} | ||||||
| 		]); | 		]); | ||||||
| @ -1970,6 +1976,7 @@ mod tests { | |||||||
| 				block_number: block2.header().number(), | 				block_number: block2.header().number(), | ||||||
| 				transaction_hash: tx_hash3.clone(), | 				transaction_hash: tx_hash3.clone(), | ||||||
| 				transaction_index: 0, | 				transaction_index: 0, | ||||||
|  | 				transaction_log_index: 0, | ||||||
| 				log_index: 0, | 				log_index: 0, | ||||||
| 			} | 			} | ||||||
| 		]); | 		]); | ||||||
|  | |||||||
| @ -53,13 +53,13 @@ use verification::queue::BlockQueue; | |||||||
| use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute}; | use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute}; | ||||||
| use client::{ | use client::{ | ||||||
| 	BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient, | 	BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient, | ||||||
| 	MiningBlockChainClient, TraceFilter, CallAnalytics, BlockImportError, Mode, | 	MiningBlockChainClient, EngineClient, TraceFilter, CallAnalytics, BlockImportError, Mode, | ||||||
| 	ChainNotify, PruningInfo, | 	ChainNotify, PruningInfo, | ||||||
| }; | }; | ||||||
| use client::Error as ClientError; | use client::Error as ClientError; | ||||||
| use env_info::EnvInfo; | use env_info::EnvInfo; | ||||||
| use executive::{Executive, Executed, TransactOptions, contract_address}; | 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::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase}; | ||||||
| use trace; | use trace; | ||||||
| use trace::FlatTransactionTraces; | use trace::FlatTransactionTraces; | ||||||
| @ -837,7 +837,6 @@ impl snapshot::DatabaseRestore for Client { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| impl BlockChainClient for Client { | impl BlockChainClient for Client { | ||||||
| 	fn call(&self, t: &SignedTransaction, block: BlockId, analytics: CallAnalytics) -> Result<Executed, CallError> { | 	fn call(&self, t: &SignedTransaction, block: BlockId, analytics: CallAnalytics) -> Result<Executed, CallError> { | ||||||
| 		let header = self.block_header(block).ok_or(CallError::StatePruned)?; | 		let header = self.block_header(block).ok_or(CallError::StatePruned)?; | ||||||
| @ -849,7 +848,7 @@ impl BlockChainClient for Client { | |||||||
| 			difficulty: header.difficulty(), | 			difficulty: header.difficulty(), | ||||||
| 			last_hashes: last_hashes, | 			last_hashes: last_hashes, | ||||||
| 			gas_used: U256::zero(), | 			gas_used: U256::zero(), | ||||||
| 			gas_limit: U256::max_value(), | 			gas_limit: header.gas_limit(), | ||||||
| 		}; | 		}; | ||||||
| 		// that's just a copy of the state.
 | 		// that's just a copy of the state.
 | ||||||
| 		let mut state = self.state_at(block).ok_or(CallError::StatePruned)?; | 		let mut state = self.state_at(block).ok_or(CallError::StatePruned)?; | ||||||
| @ -874,6 +873,81 @@ impl BlockChainClient for Client { | |||||||
| 		Ok(ret) | 		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> { | 	fn replay(&self, id: TransactionId, analytics: CallAnalytics) -> Result<Executed, CallError> { | ||||||
| 		let address = self.transaction_address(id).ok_or(CallError::TransactionNotFound)?; | 		let address = self.transaction_address(id).ok_or(CallError::TransactionNotFound)?; | ||||||
| 		let header = self.block_header(BlockId::Hash(address.block_hash)).ok_or(CallError::StatePruned)?; | 		let header = self.block_header(BlockId::Hash(address.block_hash)).ok_or(CallError::StatePruned)?; | ||||||
| @ -1134,51 +1208,21 @@ impl BlockChainClient for Client { | |||||||
| 		let chain = self.chain.read(); | 		let chain = self.chain.read(); | ||||||
| 		self.transaction_address(id) | 		self.transaction_address(id) | ||||||
| 			.and_then(|address| chain.block_number(&address.block_hash).and_then(|block_number| { | 			.and_then(|address| chain.block_number(&address.block_hash).and_then(|block_number| { | ||||||
| 			let t = chain.block_body(&address.block_hash) | 				let transaction = chain.block_body(&address.block_hash) | ||||||
| 				.and_then(|body| { | 					.and_then(|body| body.view().localized_transaction_at(&address.block_hash, block_number, address.index)); | ||||||
| 					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))); | 				let previous_receipts = (0..address.index + 1) | ||||||
| 
 | 					.map(|index| { | ||||||
| 			match (tx_and_sender, chain.transaction_receipt(&address)) { | 						let mut address = address.clone(); | ||||||
| 				(Some((tx, sender)), Some(receipt)) => { | 						address.index = index; | ||||||
| 					let block_hash = tx.block_hash.clone(); | 						chain.transaction_receipt(&address) | ||||||
| 					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, |  | ||||||
| 					}) | 					}) | ||||||
|  | 					.collect(); | ||||||
|  | 				match (transaction, previous_receipts) { | ||||||
|  | 					(Some(transaction), Some(previous_receipts)) => { | ||||||
|  | 						Some(transaction_receipt(transaction, previous_receipts)) | ||||||
| 					}, | 					}, | ||||||
| 				_ => None | 					_ => None, | ||||||
| 				} | 				} | ||||||
| 			})) | 			})) | ||||||
| 	} | 	} | ||||||
| @ -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> { | 	fn signing_network_id(&self) -> Option<u64> { | ||||||
| 		self.engine.signing_network_id(&self.latest_env_info()) | 		self.engine.signing_network_id(&self.latest_env_info()) | ||||||
| 	} | 	} | ||||||
| @ -1445,16 +1484,6 @@ impl MiningBlockChainClient for Client { | |||||||
| 		&self.factories.vm | 		&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) { | 	fn broadcast_proposal_block(&self, block: SealedBlock) { | ||||||
| 		self.notify(|notify| { | 		self.notify(|notify| { | ||||||
| 			notify.new_blocks( | 			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 { | impl MayPanic for Client { | ||||||
| 	fn on_panic<F>(&self, closure: F) where F: OnPanicListener { | 	fn on_panic<F>(&self, closure: F) where F: OnPanicListener { | ||||||
| 		self.panic_handler.on_panic(closure); | 		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)] | #[cfg(test)] | ||||||
| mod tests { | mod tests { | ||||||
| 
 | 
 | ||||||
| @ -1570,4 +1658,91 @@ mod tests { | |||||||
| 
 | 
 | ||||||
| 		assert!(client.tree_route(&genesis, &new_hash).is_none()); | 		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::error::Error; | ||||||
| pub use self::test_client::{TestBlockChainClient, EachBlockWith}; | pub use self::test_client::{TestBlockChainClient, EachBlockWith}; | ||||||
| pub use self::chain_notify::ChainNotify; | pub use self::chain_notify::ChainNotify; | ||||||
| pub use self::traits::{BlockChainClient, MiningBlockChainClient}; | pub use self::traits::{BlockChainClient, MiningBlockChainClient, EngineClient}; | ||||||
| 
 | 
 | ||||||
| pub use self::traits::ProvingBlockChainClient; | pub use self::traits::ProvingBlockChainClient; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -24,7 +24,7 @@ use devtools::*; | |||||||
| use transaction::{Transaction, LocalizedTransaction, SignedTransaction, PendingTransaction, Action}; | use transaction::{Transaction, LocalizedTransaction, SignedTransaction, PendingTransaction, Action}; | ||||||
| use blockchain::TreeRoute; | use blockchain::TreeRoute; | ||||||
| use client::{ | use client::{ | ||||||
| 	BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockId, | 	BlockChainClient, MiningBlockChainClient, EngineClient, BlockChainInfo, BlockStatus, BlockId, | ||||||
| 	TransactionId, UncleId, TraceId, TraceFilter, LastHashes, CallAnalytics, BlockImportError, | 	TransactionId, UncleId, TraceId, TraceFilter, LastHashes, CallAnalytics, BlockImportError, | ||||||
| }; | }; | ||||||
| use db::{NUM_COLUMNS, COL_STATE}; | use db::{NUM_COLUMNS, COL_STATE}; | ||||||
| @ -372,16 +372,6 @@ impl MiningBlockChainClient for TestBlockChainClient { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fn broadcast_proposal_block(&self, _block: SealedBlock) {} | 	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 { | impl BlockChainClient for TestBlockChainClient { | ||||||
| @ -389,6 +379,10 @@ impl BlockChainClient for TestBlockChainClient { | |||||||
| 		self.execution_result.read().clone().unwrap() | 		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> { | 	fn replay(&self, _id: TransactionId, _analytics: CallAnalytics) -> Result<Executed, CallError> { | ||||||
| 		self.execution_result.read().clone().unwrap() | 		self.execution_result.read().clone().unwrap() | ||||||
| 	} | 	} | ||||||
| @ -699,8 +693,6 @@ impl BlockChainClient for TestBlockChainClient { | |||||||
| 		self.spec.engine.handle_message(&message).unwrap(); | 		self.spec.engine.handle_message(&message).unwrap(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fn broadcast_consensus_message(&self, _message: Bytes) {} |  | ||||||
| 
 |  | ||||||
| 	fn ready_transactions(&self) -> Vec<PendingTransaction> { | 	fn ready_transactions(&self) -> Vec<PendingTransaction> { | ||||||
| 		self.miner.ready_transactions(self.chain_info().best_block_number) | 		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 } | 	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.
 | 	/// Makes a non-persistent transaction call.
 | ||||||
| 	fn call(&self, t: &SignedTransaction, block: BlockId, analytics: CallAnalytics) -> Result<Executed, CallError>; | 	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.
 | 	/// Replays a given transaction for inspection.
 | ||||||
| 	fn replay(&self, t: TransactionId, analytics: CallAnalytics) -> Result<Executed, CallError>; | 	fn replay(&self, t: TransactionId, analytics: CallAnalytics) -> Result<Executed, CallError>; | ||||||
| 
 | 
 | ||||||
| @ -208,9 +211,6 @@ pub trait BlockChainClient : Sync + Send { | |||||||
| 	/// Queue conensus engine message.
 | 	/// Queue conensus engine message.
 | ||||||
| 	fn queue_consensus_message(&self, message: Bytes); | 	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.
 | 	/// List all transactions that are allowed into the next block.
 | ||||||
| 	fn ready_transactions(&self) -> Vec<PendingTransaction>; | 	fn ready_transactions(&self) -> Vec<PendingTransaction>; | ||||||
| 
 | 
 | ||||||
| @ -294,12 +294,6 @@ pub trait MiningBlockChainClient: BlockChainClient { | |||||||
| 	/// Returns EvmFactory.
 | 	/// Returns EvmFactory.
 | ||||||
| 	fn vm_factory(&self) -> &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.
 | 	/// Broadcast a block proposal.
 | ||||||
| 	fn broadcast_proposal_block(&self, block: SealedBlock); | 	fn broadcast_proposal_block(&self, block: SealedBlock); | ||||||
| 
 | 
 | ||||||
| @ -310,6 +304,18 @@ pub trait MiningBlockChainClient: BlockChainClient { | |||||||
| 	fn latest_schedule(&self) -> Schedule; | 	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.
 | /// Extended client interface for providing proofs of the state.
 | ||||||
| pub trait ProvingBlockChainClient: BlockChainClient { | pub trait ProvingBlockChainClient: BlockChainClient { | ||||||
| 	/// Prove account storage at a specific block id.
 | 	/// Prove account storage at a specific block id.
 | ||||||
|  | |||||||
| @ -32,11 +32,13 @@ use blockchain::extras::BlockDetails; | |||||||
| use views::HeaderView; | use views::HeaderView; | ||||||
| use evm::Schedule; | use evm::Schedule; | ||||||
| use ethjson; | use ethjson; | ||||||
| use io::{IoContext, IoHandler, TimerToken, IoService, IoChannel}; | use io::{IoContext, IoHandler, TimerToken, IoService}; | ||||||
| use service::ClientIoMessage; |  | ||||||
| use transaction::SignedTransaction; | use transaction::SignedTransaction; | ||||||
| use env_info::EnvInfo; | use env_info::EnvInfo; | ||||||
| use builtin::Builtin; | use builtin::Builtin; | ||||||
|  | use client::{Client, EngineClient}; | ||||||
|  | use super::validator_set::{ValidatorSet, new_validator_set}; | ||||||
|  | use state::CleanupMode; | ||||||
| 
 | 
 | ||||||
| /// `AuthorityRound` params.
 | /// `AuthorityRound` params.
 | ||||||
| #[derive(Debug, PartialEq)] | #[derive(Debug, PartialEq)] | ||||||
| @ -45,12 +47,12 @@ pub struct AuthorityRoundParams { | |||||||
| 	pub gas_limit_bound_divisor: U256, | 	pub gas_limit_bound_divisor: U256, | ||||||
| 	/// Time to wait before next block or authority switching.
 | 	/// Time to wait before next block or authority switching.
 | ||||||
| 	pub step_duration: Duration, | 	pub step_duration: Duration, | ||||||
| 	/// Valid authorities.
 | 	/// Block reward.
 | ||||||
| 	pub authorities: Vec<Address>, | 	pub block_reward: U256, | ||||||
| 	/// Number of authorities.
 |  | ||||||
| 	pub authority_n: usize, |  | ||||||
| 	/// Starting step,
 | 	/// Starting step,
 | ||||||
| 	pub start_step: Option<u64>, | 	pub start_step: Option<u64>, | ||||||
|  | 	/// Valid validators.
 | ||||||
|  | 	pub validators: ethjson::spec::ValidatorSet, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams { | impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams { | ||||||
| @ -58,8 +60,8 @@ impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams { | |||||||
| 		AuthorityRoundParams { | 		AuthorityRoundParams { | ||||||
| 			gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(), | 			gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(), | ||||||
| 			step_duration: Duration::from_secs(p.step_duration.into()), | 			step_duration: Duration::from_secs(p.step_duration.into()), | ||||||
| 			authority_n: p.authorities.len(), | 			validators: p.validators, | ||||||
| 			authorities: p.authorities.into_iter().map(Into::into).collect::<Vec<_>>(), | 			block_reward: p.block_reward.map_or_else(U256::zero, Into::into), | ||||||
| 			start_step: p.start_step.map(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.
 | /// mainnet chains in the Olympic, Frontier and Homestead eras.
 | ||||||
| pub struct AuthorityRound { | pub struct AuthorityRound { | ||||||
| 	params: CommonParams, | 	params: CommonParams, | ||||||
| 	our_params: AuthorityRoundParams, | 	gas_limit_bound_divisor: U256, | ||||||
|  | 	block_reward: U256, | ||||||
|  | 	step_duration: Duration, | ||||||
| 	builtins: BTreeMap<Address, Builtin>, | 	builtins: BTreeMap<Address, Builtin>, | ||||||
| 	transition_service: IoService<()>, | 	transition_service: IoService<()>, | ||||||
| 	message_channel: Mutex<Option<IoChannel<ClientIoMessage>>>, |  | ||||||
| 	step: AtomicUsize, | 	step: AtomicUsize, | ||||||
| 	proposed: AtomicBool, | 	proposed: AtomicBool, | ||||||
| 	account_provider: Mutex<Option<Arc<AccountProvider>>>, | 	client: RwLock<Option<Weak<EngineClient>>>, | ||||||
|  | 	account_provider: Mutex<Arc<AccountProvider>>, | ||||||
| 	password: RwLock<Option<String>>, | 	password: RwLock<Option<String>>, | ||||||
|  | 	validators: Box<ValidatorSet + Send + Sync>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn header_step(header: &Header) -> Result<usize, ::rlp::DecoderError> { | fn header_step(header: &Header) -> Result<usize, ::rlp::DecoderError> { | ||||||
| @ -105,14 +110,17 @@ impl AuthorityRound { | |||||||
| 		let engine = Arc::new( | 		let engine = Arc::new( | ||||||
| 			AuthorityRound { | 			AuthorityRound { | ||||||
| 				params: params, | 				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, | 				builtins: builtins, | ||||||
| 				transition_service: IoService::<()>::start()?, | 				transition_service: IoService::<()>::start()?, | ||||||
| 				message_channel: Mutex::new(None), |  | ||||||
| 				step: AtomicUsize::new(initial_step), | 				step: AtomicUsize::new(initial_step), | ||||||
| 				proposed: AtomicBool::new(false), | 				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), | 				password: RwLock::new(None), | ||||||
|  | 				validators: new_validator_set(our_params.validators), | ||||||
| 			}); | 			}); | ||||||
| 		// Do not initialize timeouts for tests.
 | 		// Do not initialize timeouts for tests.
 | ||||||
| 		if should_timeout { | 		if should_timeout { | ||||||
| @ -124,7 +132,7 @@ impl AuthorityRound { | |||||||
| 
 | 
 | ||||||
| 	fn remaining_step_duration(&self) -> Duration { | 	fn remaining_step_duration(&self) -> Duration { | ||||||
| 		let now = unix_now(); | 		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 { | 		if step_end > now { | ||||||
| 			step_end - now | 			step_end - now | ||||||
| 		} else { | 		} else { | ||||||
| @ -132,13 +140,12 @@ impl AuthorityRound { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fn step_proposer(&self, step: usize) -> &Address { | 	fn step_proposer(&self, step: usize) -> Address { | ||||||
| 		let p = &self.our_params; | 		self.validators.get(step) | ||||||
| 		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 is_step_proposer(&self, step: usize, address: &Address) -> bool { | 	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) { | 	fn step(&self) { | ||||||
| 		self.step.fetch_add(1, AtomicOrdering::SeqCst); | 		self.step.fetch_add(1, AtomicOrdering::SeqCst); | ||||||
| 		self.proposed.store(false, AtomicOrdering::SeqCst); | 		self.proposed.store(false, AtomicOrdering::SeqCst); | ||||||
| 		if let Some(ref channel) = *self.message_channel.lock() { | 		if let Some(ref weak) = *self.client.read() { | ||||||
| 			match channel.send(ClientIoMessage::UpdateSealing) { | 			if let Some(c) = weak.upgrade() { | ||||||
| 				Ok(_) => trace!(target: "poa", "timeout: UpdateSealing message sent for step {}.", self.step.load(AtomicOrdering::Relaxed)), | 				c.update_sealing(); | ||||||
| 				Err(err) => trace!(target: "poa", "timeout: Could not send a sealing message {} for step {}.", err, self.step.load(AtomicOrdering::Relaxed)), |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @ -207,7 +213,7 @@ impl Engine for AuthorityRound { | |||||||
| 		header.set_difficulty(parent.difficulty().clone()); | 		header.set_difficulty(parent.difficulty().clone()); | ||||||
| 		header.set_gas_limit({ | 		header.set_gas_limit({ | ||||||
| 			let gas_limit = parent.gas_limit().clone(); | 			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 { | 			if gas_limit < gas_floor_target { | ||||||
| 				min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into()) | 				min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into()) | ||||||
| 			} else { | 			} else { | ||||||
| @ -217,8 +223,7 @@ impl Engine for AuthorityRound { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fn is_sealer(&self, author: &Address) -> Option<bool> { | 	fn is_sealer(&self, author: &Address) -> Option<bool> { | ||||||
| 		let p = &self.our_params; | 		Some(self.validators.contains(author)) | ||||||
| 		Some(p.authorities.contains(author)) |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/// Attempt to seal the block internally.
 | 	/// Attempt to seal the block internally.
 | ||||||
| @ -230,25 +235,31 @@ impl Engine for AuthorityRound { | |||||||
| 		let header = block.header(); | 		let header = block.header(); | ||||||
| 		let step = self.step.load(AtomicOrdering::SeqCst); | 		let step = self.step.load(AtomicOrdering::SeqCst); | ||||||
| 		if self.is_step_proposer(step, header.author()) { | 		if self.is_step_proposer(step, header.author()) { | ||||||
| 			if let Some(ref ap) = *self.account_provider.lock() { | 			let 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()) { | 			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); | 				trace!(target: "poa", "generate_seal: Issuing a block for step {}.", step); | ||||||
| 				self.proposed.store(true, AtomicOrdering::SeqCst); | 				self.proposed.store(true, AtomicOrdering::SeqCst); | ||||||
| 					let rlps = vec![encode(&step).to_vec(), encode(&(&*signature as &[u8])).to_vec()]; | 				return Seal::Regular(vec![encode(&step).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); | ||||||
| 					return Seal::Regular(rlps); |  | ||||||
| 			} else { | 			} else { | ||||||
| 				warn!(target: "poa", "generate_seal: FAIL: Accounts secret key unavailable."); | 				warn!(target: "poa", "generate_seal: FAIL: Accounts secret key unavailable."); | ||||||
| 			} | 			} | ||||||
| 			} else { |  | ||||||
| 				warn!(target: "poa", "generate_seal: FAIL: Accounts not provided."); |  | ||||||
| 			} |  | ||||||
| 		} else { | 		} else { | ||||||
| 			trace!(target: "poa", "generate_seal: Not a proposer for step {}.", step); | 			trace!(target: "poa", "generate_seal: Not a proposer for step {}.", step); | ||||||
| 		} | 		} | ||||||
| 		Seal::None | 		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.
 | 	/// Check the number of seal fields.
 | ||||||
| 	fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { | 	fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { | ||||||
| 		if header.seal().len() != self.seal_fields() { | 		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.
 | 		// Give one step slack if step is lagging, double vote is still not possible.
 | ||||||
| 		if header_step <= self.step.load(AtomicOrdering::SeqCst) + 1 { | 		if header_step <= self.step.load(AtomicOrdering::SeqCst) + 1 { | ||||||
| 			let proposer_signature = header_signature(header)?; | 			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 { | 			if ok_sig { | ||||||
| 				Ok(()) | 				Ok(()) | ||||||
| 			} else { | 			} else { | ||||||
| @ -292,7 +303,7 @@ impl Engine for AuthorityRound { | |||||||
| 			Err(EngineError::DoubleVote(header.author().clone()))?; | 			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 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; | 		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 { | 		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>) { | 	fn register_client(&self, client: Weak<Client>) { | ||||||
| 		*self.message_channel.lock() = Some(message_channel); | 		*self.client.write() = Some(client.clone()); | ||||||
|  | 		self.validators.register_call_contract(client); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fn set_signer(&self, _address: Address, password: String) { | 	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>) { | 	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 engine = Spec::new_test_round().engine; | ||||||
| 
 | 
 | ||||||
| 		let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap(); | 		let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap(); | ||||||
| 		// Two authorities.
 | 		// Two validators.
 | ||||||
| 		// Spec starts with step 2.
 | 		// Spec starts with step 2.
 | ||||||
| 		header.set_seal(vec![encode(&2usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); | 		header.set_seal(vec![encode(&2usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); | ||||||
| 		assert!(engine.verify_block_seal(&header).is_err()); | 		assert!(engine.verify_block_seal(&header).is_err()); | ||||||
| @ -459,7 +471,7 @@ mod tests { | |||||||
| 		let engine = Spec::new_test_round().engine; | 		let engine = Spec::new_test_round().engine; | ||||||
| 
 | 
 | ||||||
| 		let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap(); | 		let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap(); | ||||||
| 		// Two authorities.
 | 		// Two validators.
 | ||||||
| 		// Spec starts with step 2.
 | 		// Spec starts with step 2.
 | ||||||
| 		header.set_seal(vec![encode(&1usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); | 		header.set_seal(vec![encode(&1usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); | ||||||
| 		assert!(engine.verify_block_seal(&header).is_ok()); | 		assert!(engine.verify_block_seal(&header).is_ok()); | ||||||
|  | |||||||
| @ -16,6 +16,8 @@ | |||||||
| 
 | 
 | ||||||
| //! A blockchain engine that supports a basic, non-BFT proof-of-authority.
 | //! 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 ethkey::{recover, public_to_address}; | ||||||
| use account_provider::AccountProvider; | use account_provider::AccountProvider; | ||||||
| use block::*; | use block::*; | ||||||
| @ -28,26 +30,23 @@ use evm::Schedule; | |||||||
| use ethjson; | use ethjson; | ||||||
| use header::Header; | use header::Header; | ||||||
| use transaction::SignedTransaction; | use transaction::SignedTransaction; | ||||||
| 
 | use client::Client; | ||||||
| use util::*; | use super::validator_set::{ValidatorSet, new_validator_set}; | ||||||
| 
 | 
 | ||||||
| /// `BasicAuthority` params.
 | /// `BasicAuthority` params.
 | ||||||
| #[derive(Debug, PartialEq)] | #[derive(Debug, PartialEq)] | ||||||
| pub struct BasicAuthorityParams { | pub struct BasicAuthorityParams { | ||||||
| 	/// Gas limit divisor.
 | 	/// Gas limit divisor.
 | ||||||
| 	pub gas_limit_bound_divisor: U256, | 	pub gas_limit_bound_divisor: U256, | ||||||
| 	/// Block duration.
 |  | ||||||
| 	pub duration_limit: u64, |  | ||||||
| 	/// Valid signatories.
 | 	/// Valid signatories.
 | ||||||
| 	pub authorities: HashSet<Address>, | 	pub validators: ethjson::spec::ValidatorSet, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl From<ethjson::spec::BasicAuthorityParams> for BasicAuthorityParams { | impl From<ethjson::spec::BasicAuthorityParams> for BasicAuthorityParams { | ||||||
| 	fn from(p: ethjson::spec::BasicAuthorityParams) -> Self { | 	fn from(p: ethjson::spec::BasicAuthorityParams) -> Self { | ||||||
| 		BasicAuthorityParams { | 		BasicAuthorityParams { | ||||||
| 			gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(), | 			gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(), | ||||||
| 			duration_limit: p.duration_limit.into(), | 			validators: p.validators, | ||||||
| 			authorities: p.authorities.into_iter().map(Into::into).collect::<HashSet<_>>(), |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @ -56,10 +55,11 @@ impl From<ethjson::spec::BasicAuthorityParams> for BasicAuthorityParams { | |||||||
| /// mainnet chains in the Olympic, Frontier and Homestead eras.
 | /// mainnet chains in the Olympic, Frontier and Homestead eras.
 | ||||||
| pub struct BasicAuthority { | pub struct BasicAuthority { | ||||||
| 	params: CommonParams, | 	params: CommonParams, | ||||||
| 	our_params: BasicAuthorityParams, | 	gas_limit_bound_divisor: U256, | ||||||
| 	builtins: BTreeMap<Address, Builtin>, | 	builtins: BTreeMap<Address, Builtin>, | ||||||
| 	account_provider: Mutex<Option<Arc<AccountProvider>>>, | 	account_provider: Mutex<Option<Arc<AccountProvider>>>, | ||||||
| 	password: RwLock<Option<String>>, | 	password: RwLock<Option<String>>, | ||||||
|  | 	validators: Box<ValidatorSet + Send + Sync>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl BasicAuthority { | impl BasicAuthority { | ||||||
| @ -67,8 +67,9 @@ impl BasicAuthority { | |||||||
| 	pub fn new(params: CommonParams, our_params: BasicAuthorityParams, builtins: BTreeMap<Address, Builtin>) -> Self { | 	pub fn new(params: CommonParams, our_params: BasicAuthorityParams, builtins: BTreeMap<Address, Builtin>) -> Self { | ||||||
| 		BasicAuthority { | 		BasicAuthority { | ||||||
| 			params: params, | 			params: params, | ||||||
| 			our_params: our_params, | 			gas_limit_bound_divisor: our_params.gas_limit_bound_divisor, | ||||||
| 			builtins: builtins, | 			builtins: builtins, | ||||||
|  | 			validators: new_validator_set(our_params.validators), | ||||||
| 			account_provider: Mutex::new(None), | 			account_provider: Mutex::new(None), | ||||||
| 			password: RwLock::new(None), | 			password: RwLock::new(None), | ||||||
| 		} | 		} | ||||||
| @ -95,7 +96,7 @@ impl Engine for BasicAuthority { | |||||||
| 		header.set_difficulty(parent.difficulty().clone()); | 		header.set_difficulty(parent.difficulty().clone()); | ||||||
| 		header.set_gas_limit({ | 		header.set_gas_limit({ | ||||||
| 			let gas_limit = parent.gas_limit().clone(); | 			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 { | 			if gas_limit < gas_floor_target { | ||||||
| 				min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into()) | 				min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into()) | ||||||
| 			} else { | 			} else { | ||||||
| @ -105,23 +106,23 @@ impl Engine for BasicAuthority { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fn is_sealer(&self, author: &Address) -> Option<bool> { | 	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.
 | 	/// 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 { | 	fn generate_seal(&self, block: &ExecutedBlock) -> Seal { | ||||||
| 		if let Some(ref ap) = *self.account_provider.lock() { | 		if let Some(ref ap) = *self.account_provider.lock() { | ||||||
| 			let header = block.header(); | 			let header = block.header(); | ||||||
|  | 			let author = header.author(); | ||||||
|  | 			if self.validators.contains(author) { | ||||||
| 				let message = header.bare_hash(); | 				let message = header.bare_hash(); | ||||||
| 				// account should be pernamently unlocked, otherwise sealing will fail
 | 				// account should be pernamently unlocked, otherwise sealing will fail
 | ||||||
| 			if let Ok(signature) = ap.sign(*block.header().author(), self.password.read().clone(), message) { | 				if let Ok(signature) = ap.sign(*author, self.password.read().clone(), message) { | ||||||
| 					return Seal::Regular(vec![::rlp::encode(&(&*signature as &[u8])).to_vec()]); | 					return Seal::Regular(vec![::rlp::encode(&(&*signature as &[u8])).to_vec()]); | ||||||
| 				} else { | 				} else { | ||||||
| 					trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable"); | 					trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable"); | ||||||
| 				} | 				} | ||||||
|  | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			trace!(target: "basicauthority", "generate_seal: FAIL: accounts not provided"); | 			trace!(target: "basicauthority", "generate_seal: FAIL: accounts not provided"); | ||||||
| 		} | 		} | ||||||
| @ -145,7 +146,7 @@ impl Engine for BasicAuthority { | |||||||
| 		// check the signature is legit.
 | 		// check the signature is legit.
 | ||||||
| 		let sig = UntrustedRlp::new(&header.seal()[0]).as_val::<H520>()?; | 		let sig = UntrustedRlp::new(&header.seal()[0]).as_val::<H520>()?; | ||||||
| 		let signer = public_to_address(&recover(&sig.into(), &header.bare_hash())?); | 		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)?; | 			return Err(BlockError::InvalidSeal)?; | ||||||
| 		} | 		} | ||||||
| 		Ok(()) | 		Ok(()) | ||||||
| @ -161,7 +162,7 @@ impl Engine for BasicAuthority { | |||||||
| 		if header.difficulty() != parent.difficulty() { | 		if header.difficulty() != parent.difficulty() { | ||||||
| 			return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: *parent.difficulty(), found: *header.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 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; | 		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 { | 		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
 | 		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) { | 	fn set_signer(&self, _address: Address, password: String) { | ||||||
| 		*self.password.write() = Some(password); | 		*self.password.write() = Some(password); | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -21,6 +21,7 @@ mod instant_seal; | |||||||
| mod basic_authority; | mod basic_authority; | ||||||
| mod authority_round; | mod authority_round; | ||||||
| mod tendermint; | mod tendermint; | ||||||
|  | mod validator_set; | ||||||
| 
 | 
 | ||||||
| pub use self::null_engine::NullEngine; | pub use self::null_engine::NullEngine; | ||||||
| pub use self::instant_seal::InstantSeal; | 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::authority_round::AuthorityRound; | ||||||
| pub use self::tendermint::Tendermint; | pub use self::tendermint::Tendermint; | ||||||
| 
 | 
 | ||||||
|  | use std::sync::Weak; | ||||||
| use util::*; | use util::*; | ||||||
| use account_provider::AccountProvider; | use account_provider::AccountProvider; | ||||||
| use block::ExecutedBlock; | use block::ExecutedBlock; | ||||||
| @ -36,13 +38,12 @@ use env_info::EnvInfo; | |||||||
| use error::Error; | use error::Error; | ||||||
| use spec::CommonParams; | use spec::CommonParams; | ||||||
| use evm::Schedule; | use evm::Schedule; | ||||||
| use io::IoChannel; |  | ||||||
| use service::ClientIoMessage; |  | ||||||
| use header::Header; | use header::Header; | ||||||
| use transaction::SignedTransaction; | use transaction::SignedTransaction; | ||||||
| use ethereum::ethash; | use ethereum::ethash; | ||||||
| use blockchain::extras::BlockDetails; | use blockchain::extras::BlockDetails; | ||||||
| use views::HeaderView; | use views::HeaderView; | ||||||
|  | use client::Client; | ||||||
| 
 | 
 | ||||||
| /// Voting errors.
 | /// Voting errors.
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| @ -207,14 +208,15 @@ pub trait Engine : Sync + Send { | |||||||
| 	/// Register an account which signs consensus messages.
 | 	/// Register an account which signs consensus messages.
 | ||||||
| 	fn set_signer(&self, _address: Address, _password: String) {} | 	fn set_signer(&self, _address: Address, _password: String) {} | ||||||
| 
 | 
 | ||||||
| 	/// Stops any services that the may hold the Engine and makes it safe to drop.
 | 	/// Add Client which can be used for sealing, querying the state and sending messages.
 | ||||||
| 	fn stop(&self) {} | 	fn register_client(&self, _client: Weak<Client>) {} | ||||||
| 
 |  | ||||||
| 	/// Add a channel for communication with Client which can be used for sealing.
 |  | ||||||
| 	fn register_message_channel(&self, _message_channel: IoChannel<ClientIoMessage>) {} |  | ||||||
| 
 | 
 | ||||||
| 	/// Add an account provider useful for Engines that sign stuff.
 | 	/// Add an account provider useful for Engines that sign stuff.
 | ||||||
| 	fn register_account_provider(&self, _account_provider: Arc<AccountProvider>) {} | 	fn register_account_provider(&self, _account_provider: Arc<AccountProvider>) {} | ||||||
|  | 
 | ||||||
| 	/// Trigger next step of the consensus engine.
 | 	/// Trigger next step of the consensus engine.
 | ||||||
| 	fn step(&self) {} | 	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 params; | ||||||
| mod vote_collector; | mod vote_collector; | ||||||
| 
 | 
 | ||||||
|  | use std::sync::Weak; | ||||||
| use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; | use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; | ||||||
| use util::*; | use util::*; | ||||||
|  | use client::{Client, EngineClient}; | ||||||
| use error::{Error, BlockError}; | use error::{Error, BlockError}; | ||||||
| use header::Header; | use header::Header; | ||||||
| use builtin::Builtin; | use builtin::Builtin; | ||||||
| @ -43,8 +45,9 @@ use engines::{Engine, Seal, EngineError}; | |||||||
| use blockchain::extras::BlockDetails; | use blockchain::extras::BlockDetails; | ||||||
| use views::HeaderView; | use views::HeaderView; | ||||||
| use evm::Schedule; | use evm::Schedule; | ||||||
| use io::{IoService, IoChannel}; | use state::CleanupMode; | ||||||
| use service::ClientIoMessage; | use io::IoService; | ||||||
|  | use super::validator_set::{ValidatorSet, new_validator_set}; | ||||||
| use self::message::*; | use self::message::*; | ||||||
| use self::transition::TransitionHandler; | use self::transition::TransitionHandler; | ||||||
| use self::params::TendermintParams; | use self::params::TendermintParams; | ||||||
| @ -74,9 +77,11 @@ pub type BlockHash = H256; | |||||||
| /// Engine using `Tendermint` consensus algorithm, suitable for EVM chain.
 | /// Engine using `Tendermint` consensus algorithm, suitable for EVM chain.
 | ||||||
| pub struct Tendermint { | pub struct Tendermint { | ||||||
| 	params: CommonParams, | 	params: CommonParams, | ||||||
| 	our_params: TendermintParams, | 	gas_limit_bound_divisor: U256, | ||||||
| 	builtins: BTreeMap<Address, Builtin>, | 	builtins: BTreeMap<Address, Builtin>, | ||||||
| 	step_service: IoService<Step>, | 	step_service: IoService<Step>, | ||||||
|  | 	client: RwLock<Option<Weak<EngineClient>>>, | ||||||
|  | 	block_reward: U256, | ||||||
| 	/// Address to be used as authority.
 | 	/// Address to be used as authority.
 | ||||||
| 	authority: RwLock<Address>, | 	authority: RwLock<Address>, | ||||||
| 	/// Password used for signing messages.
 | 	/// Password used for signing messages.
 | ||||||
| @ -89,8 +94,6 @@ pub struct Tendermint { | |||||||
| 	step: RwLock<Step>, | 	step: RwLock<Step>, | ||||||
| 	/// Vote accumulator.
 | 	/// Vote accumulator.
 | ||||||
| 	votes: VoteCollector, | 	votes: VoteCollector, | ||||||
| 	/// Channel for updating the sealing.
 |  | ||||||
| 	message_channel: Mutex<Option<IoChannel<ClientIoMessage>>>, |  | ||||||
| 	/// Used to sign messages and proposals.
 | 	/// Used to sign messages and proposals.
 | ||||||
| 	account_provider: Mutex<Option<Arc<AccountProvider>>>, | 	account_provider: Mutex<Option<Arc<AccountProvider>>>, | ||||||
| 	/// Message for the last PoLC.
 | 	/// Message for the last PoLC.
 | ||||||
| @ -99,6 +102,8 @@ pub struct Tendermint { | |||||||
| 	last_lock: AtomicUsize, | 	last_lock: AtomicUsize, | ||||||
| 	/// Bare hash of the proposed block, used for seal submission.
 | 	/// Bare hash of the proposed block, used for seal submission.
 | ||||||
| 	proposal: RwLock<Option<H256>>, | 	proposal: RwLock<Option<H256>>, | ||||||
|  | 	/// Set used to determine the current validators.
 | ||||||
|  | 	validators: Box<ValidatorSet + Send + Sync>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Tendermint { | impl Tendermint { | ||||||
| @ -107,53 +112,49 @@ impl Tendermint { | |||||||
| 		let engine = Arc::new( | 		let engine = Arc::new( | ||||||
| 			Tendermint { | 			Tendermint { | ||||||
| 				params: params, | 				params: params, | ||||||
| 				our_params: our_params, | 				gas_limit_bound_divisor: our_params.gas_limit_bound_divisor, | ||||||
| 				builtins: builtins, | 				builtins: builtins, | ||||||
|  | 				client: RwLock::new(None), | ||||||
| 				step_service: IoService::<Step>::start()?, | 				step_service: IoService::<Step>::start()?, | ||||||
|  | 				block_reward: our_params.block_reward, | ||||||
| 				authority: RwLock::new(Address::default()), | 				authority: RwLock::new(Address::default()), | ||||||
| 				password: RwLock::new(None), | 				password: RwLock::new(None), | ||||||
| 				height: AtomicUsize::new(1), | 				height: AtomicUsize::new(1), | ||||||
| 				round: AtomicUsize::new(0), | 				round: AtomicUsize::new(0), | ||||||
| 				step: RwLock::new(Step::Propose), | 				step: RwLock::new(Step::Propose), | ||||||
| 				votes: VoteCollector::new(), | 				votes: VoteCollector::new(), | ||||||
| 				message_channel: Mutex::new(None), |  | ||||||
| 				account_provider: Mutex::new(None), | 				account_provider: Mutex::new(None), | ||||||
| 				lock_change: RwLock::new(None), | 				lock_change: RwLock::new(None), | ||||||
| 				last_lock: AtomicUsize::new(0), | 				last_lock: AtomicUsize::new(0), | ||||||
| 				proposal: RwLock::new(None), | 				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))?; | 		engine.step_service.register_handler(Arc::new(handler))?; | ||||||
| 		Ok(engine) | 		Ok(engine) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fn update_sealing(&self) { | 	fn update_sealing(&self) { | ||||||
| 		if let Some(ref channel) = *self.message_channel.lock() { | 		if let Some(ref weak) = *self.client.read() { | ||||||
| 			match channel.send(ClientIoMessage::UpdateSealing) { | 			if let Some(c) = weak.upgrade() { | ||||||
| 				Ok(_) => trace!(target: "poa", "UpdateSealing message sent."), | 				c.update_sealing(); | ||||||
| 				Err(err) => warn!(target: "poa", "Could not send a sealing message {}.", err), |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fn submit_seal(&self, block_hash: H256, seal: Vec<Bytes>) { | 	fn submit_seal(&self, block_hash: H256, seal: Vec<Bytes>) { | ||||||
| 		if let Some(ref channel) = *self.message_channel.lock() { | 		if let Some(ref weak) = *self.client.read() { | ||||||
| 			match channel.send(ClientIoMessage::SubmitSeal(block_hash, seal)) { | 			if let Some(c) = weak.upgrade() { | ||||||
| 				Ok(_) => trace!(target: "poa", "SubmitSeal message sent."), | 				c.submit_seal(block_hash, seal); | ||||||
| 				Err(err) => warn!(target: "poa", "Could not send a sealing message {}.", err), |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fn broadcast_message(&self, message: Bytes) { | 	fn broadcast_message(&self, message: Bytes) { | ||||||
| 		let channel = self.message_channel.lock().clone(); | 		if let Some(ref weak) = *self.client.read() { | ||||||
| 		if let Some(ref channel) = channel { | 			if let Some(c) = weak.upgrade() { | ||||||
| 			match channel.send(ClientIoMessage::BroadcastMessage(message)) { | 				c.broadcast_consensus_message(message); | ||||||
| 				Ok(_) => trace!(target: "poa", "BroadcastMessage message sent."), |  | ||||||
| 				Err(err) => warn!(target: "poa", "broadcast_message: Could not send a sealing message {}.", err), |  | ||||||
| 			} | 			} | ||||||
| 		} else { |  | ||||||
| 			warn!(target: "poa", "broadcast_message: No IoChannel available."); |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -264,23 +265,22 @@ impl Tendermint { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fn is_authority(&self, address: &Address) -> bool { | 	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 { | 	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.
 | 	/// Check if address is a proposer for given round.
 | ||||||
| 	fn is_round_proposer(&self, height: Height, round: Round, address: &Address) -> Result<(), EngineError> { | 	fn is_round_proposer(&self, height: Height, round: Round, address: &Address) -> Result<(), EngineError> { | ||||||
| 		let ref p = self.our_params; |  | ||||||
| 		let proposer_nonce = height + round; | 		let proposer_nonce = height + round; | ||||||
| 		trace!(target: "poa", "is_proposer: Proposer nonce: {}", proposer_nonce); | 		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"); | 		let proposer = self.validators.get(proposer_nonce); | ||||||
| 		if proposer == address { | 		if proposer == *address { | ||||||
| 			Ok(()) | 			Ok(()) | ||||||
| 		} else { | 		} 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_difficulty(parent.difficulty().clone()); | ||||||
| 		header.set_gas_limit({ | 		header.set_gas_limit({ | ||||||
| 			let gas_limit = parent.gas_limit().clone(); | 			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 { | 			if gas_limit < gas_floor_target { | ||||||
| 				min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into()) | 				min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into()) | ||||||
| 			} else { | 			} else { | ||||||
| @ -469,6 +469,17 @@ impl Engine for Tendermint { | |||||||
| 		Ok(()) | 		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> { | 	fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { | ||||||
| 		let seal_length = header.seal().len(); | 		let seal_length = header.seal().len(); | ||||||
| 		if seal_length == self.seal_fields() { | 		if seal_length == self.seal_fields() { | ||||||
| @ -507,7 +518,7 @@ impl Engine for Tendermint { | |||||||
| 				Some(a) => a, | 				Some(a) => a, | ||||||
| 				None => public_to_address(&recover(&precommit.signature.into(), &precommit_hash)?), | 				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()))? | 				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() }))?; | 			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 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; | 		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 { | 		if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas { | ||||||
| @ -643,9 +654,9 @@ impl Engine for Tendermint { | |||||||
| 		self.to_step(next_step); | 		self.to_step(next_step); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fn register_message_channel(&self, message_channel: IoChannel<ClientIoMessage>) { | 	fn register_client(&self, client: Weak<Client>) { | ||||||
| 		trace!(target: "poa", "Register the IoChannel."); | 		*self.client.write() = Some(client.clone()); | ||||||
| 		*self.message_channel.lock() = Some(message_channel); | 		self.validators.register_call_contract(client); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fn register_account_provider(&self, account_provider: Arc<AccountProvider>) { | 	fn register_account_provider(&self, account_provider: Arc<AccountProvider>) { | ||||||
| @ -656,21 +667,20 @@ impl Engine for Tendermint { | |||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod tests { | mod tests { | ||||||
| 	use util::*; | 	use util::*; | ||||||
| 	use io::{IoContext, IoHandler}; |  | ||||||
| 	use block::*; | 	use block::*; | ||||||
| 	use error::{Error, BlockError}; | 	use error::{Error, BlockError}; | ||||||
| 	use header::Header; | 	use header::Header; | ||||||
| 	use io::IoChannel; |  | ||||||
| 	use env_info::EnvInfo; | 	use env_info::EnvInfo; | ||||||
|  | 	use client::chain_notify::ChainNotify; | ||||||
|  | 	use miner::MinerService; | ||||||
| 	use tests::helpers::*; | 	use tests::helpers::*; | ||||||
| 	use account_provider::AccountProvider; | 	use account_provider::AccountProvider; | ||||||
| 	use service::ClientIoMessage; |  | ||||||
| 	use spec::Spec; | 	use spec::Spec; | ||||||
| 	use engines::{Engine, EngineError, Seal}; | 	use engines::{Engine, EngineError, Seal}; | ||||||
| 	use super::*; | 	use super::*; | ||||||
| 	use super::message::*; | 	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>) { | 	fn setup() -> (Spec, Arc<AccountProvider>) { | ||||||
| 		let tap = Arc::new(AccountProvider::transient_provider()); | 		let tap = Arc::new(AccountProvider::transient_provider()); | ||||||
| 		let spec = Spec::new_test_tendermint(); | 		let spec = Spec::new_test_tendermint(); | ||||||
| @ -678,13 +688,13 @@ mod tests { | |||||||
| 		(spec, tap) | 		(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 mut db_result = get_temp_state_db(); | ||||||
| 		let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); | 		let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); | ||||||
| 		let genesis_header = spec.genesis_header(); | 		let genesis_header = spec.genesis_header(); | ||||||
| 		let last_hashes = Arc::new(vec![genesis_header.hash()]); | 		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 = 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()) { | 		if let Seal::Proposal(seal) = spec.engine.generate_seal(b.block()) { | ||||||
| 			(b, seal) | 			(b, seal) | ||||||
| 		} else { | 		} 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 mi = message_info_rlp(height, round, step, block_hash); | ||||||
| 		let m = message_full_rlp(&signer(mi.sha3()).unwrap().into(), &mi); | 		let m = message_full_rlp(&signer(mi.sha3()).unwrap().into(), &mi); | ||||||
| 		engine.handle_message(&m).unwrap(); | 		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 { | 	fn insert_and_unlock(tap: &Arc<AccountProvider>, acc: &str) -> Address { | ||||||
| 		let addr = tap.insert_account(acc.sha3(), acc).unwrap(); | 		let addr = tap.insert_account(acc.sha3(), acc).unwrap(); | ||||||
| 		tap.unlock_account_permanently(addr, acc.into()).unwrap(); | 		tap.unlock_account_permanently(addr, acc.into()).unwrap(); | ||||||
| 		addr | 		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); | 		let addr = insert_and_unlock(tap, acc); | ||||||
| 		engine.set_signer(addr.clone(), acc.into()); | 		engine.set_signer(addr.clone(), acc.into()); | ||||||
| 		addr | 		addr | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	struct TestIo { | 	#[derive(Default)] | ||||||
| 		received: RwLock<Vec<ClientIoMessage>> | 	struct TestNotify { | ||||||
|  | 		messages: RwLock<Vec<Bytes>>, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	impl TestIo { | 	impl ChainNotify for TestNotify { | ||||||
| 		fn new() -> Arc<Self> { Arc::new(TestIo { received: RwLock::new(Vec::new()) }) } | 		fn broadcast(&self, data: Vec<u8>) { | ||||||
| 	} | 			self.messages.write().push(data); | ||||||
| 
 |  | ||||||
| 	impl IoHandler<ClientIoMessage> for TestIo { |  | ||||||
| 		fn message(&self, _io: &IoContext<ClientIoMessage>, net_message: &ClientIoMessage) { |  | ||||||
| 			self.received.write().push(net_message.clone()); |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -864,10 +863,10 @@ mod tests { | |||||||
| 	fn can_generate_seal() { | 	fn can_generate_seal() { | ||||||
| 		let (spec, tap) = setup(); | 		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 (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(); | 		spec.engine.stop(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -875,10 +874,10 @@ mod tests { | |||||||
| 	fn can_recognize_proposal() { | 	fn can_recognize_proposal() { | ||||||
| 		let (spec, tap) = setup(); | 		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 (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())); | 		assert!(spec.engine.is_proposal(sealed.header())); | ||||||
| 		spec.engine.stop(); | 		spec.engine.stop(); | ||||||
| 	} | 	} | ||||||
| @ -888,8 +887,8 @@ mod tests { | |||||||
| 		let (spec, tap) = setup(); | 		let (spec, tap) = setup(); | ||||||
| 		let engine = spec.engine.clone(); | 		let engine = spec.engine.clone(); | ||||||
| 		
 | 		
 | ||||||
| 		let v0 = insert_and_register(&tap, &engine, "0"); | 		let v0 = insert_and_register(&tap, engine.as_ref(), "0"); | ||||||
| 		let v1 = insert_and_register(&tap, &engine, "1"); | 		let v1 = insert_and_register(&tap, engine.as_ref(), "1"); | ||||||
| 
 | 
 | ||||||
| 		let h = 0; | 		let h = 0; | ||||||
| 		let r = 0; | 		let r = 0; | ||||||
| @ -898,57 +897,74 @@ mod tests { | |||||||
| 		let (b, _) = propose_default(&spec, v1.clone()); | 		let (b, _) = propose_default(&spec, v1.clone()); | ||||||
| 		let proposal = Some(b.header().bare_hash()); | 		let proposal = Some(b.header().bare_hash()); | ||||||
| 
 | 
 | ||||||
| 		// Register IoHandler remembers messages.
 | 		let client = generate_dummy_client(0); | ||||||
| 		let test_io = TestIo::new(); | 		let notify = Arc::new(TestNotify::default()); | ||||||
| 		let channel = IoChannel::to_handler(Arc::downgrade(&(test_io.clone() as Arc<IoHandler<ClientIoMessage>>))); | 		client.add_notify(notify.clone()); | ||||||
| 		engine.register_message_channel(channel); | 		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.
 | 		// Relays all valid present and future messages.
 | ||||||
| 		assert!(test_io.received.read().contains(&ClientIoMessage::BroadcastMessage(prevote_current))); | 		assert!(notify.messages.read().contains(&prevote_current)); | ||||||
| 		assert!(test_io.received.read().contains(&ClientIoMessage::BroadcastMessage(precommit_current))); | 		assert!(notify.messages.read().contains(&precommit_current)); | ||||||
| 		assert!(test_io.received.read().contains(&ClientIoMessage::BroadcastMessage(prevote_future))); | 		assert!(notify.messages.read().contains(&prevote_future)); | ||||||
| 		engine.stop(); | 		engine.stop(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	#[test] | 	#[test] | ||||||
| 	fn seal_submission() { | 	fn seal_submission() { | ||||||
| 		let (spec, tap) = setup(); | 		use ethkey::{Generator, Random}; | ||||||
| 		let engine = spec.engine.clone(); | 		use types::transaction::{Transaction, Action}; | ||||||
|  | 		use client::BlockChainClient; | ||||||
| 
 | 
 | ||||||
| 		let v0 = insert_and_register(&tap, &engine, "0"); | 		let client = generate_dummy_client_with_spec_and_data(Spec::new_test_tendermint, 0, 0, &[]); | ||||||
| 		let v1 = insert_and_register(&tap, &engine, "1"); | 		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 h = 1; | ||||||
| 		let r = 0; | 		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.
 | 		// Prevote.
 | ||||||
| 		vote(&engine, |mh| tap.sign(v1, None, mh).map(H520::from), h, r, Step::Prevote, 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(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(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); |  | ||||||
| 
 | 
 | ||||||
| 		seal[2] = precommit_signatures(&tap, h, r, Some(b.header().bare_hash()), v1, v0); | 		assert_eq!(client.chain_info().best_block_number, 0); | ||||||
| 		let first = test_io.received.read().contains(&ClientIoMessage::SubmitSeal(proposal.unwrap(), seal.clone())); | 		// Last precommit.
 | ||||||
| 		seal[2] = precommit_signatures(&tap, h, r, Some(b.header().bare_hash()), v0, v1); | 		vote(engine, |mh| tap.sign(v0, None, mh).map(H520::from), h, r, Step::Precommit, proposal); | ||||||
| 		let second = test_io.received.read().contains(&ClientIoMessage::SubmitSeal(proposal.unwrap(), seal)); | 		assert_eq!(client.chain_info().best_block_number, 1); | ||||||
| 
 | 
 | ||||||
| 		assert!(first ^ second); |  | ||||||
| 		engine.stop(); | 		engine.stop(); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -18,33 +18,20 @@ | |||||||
| 
 | 
 | ||||||
| use ethjson; | use ethjson; | ||||||
| use super::transition::TendermintTimeouts; | use super::transition::TendermintTimeouts; | ||||||
| use util::{Address, U256}; | use util::{U256, Uint}; | ||||||
| use time::Duration; | use time::Duration; | ||||||
| 
 | 
 | ||||||
| /// `Tendermint` params.
 | /// `Tendermint` params.
 | ||||||
| #[derive(Debug, Clone)] | #[derive(Debug)] | ||||||
| pub struct TendermintParams { | pub struct TendermintParams { | ||||||
| 	/// Gas limit divisor.
 | 	/// Gas limit divisor.
 | ||||||
| 	pub gas_limit_bound_divisor: U256, | 	pub gas_limit_bound_divisor: U256, | ||||||
| 	/// List of authorities.
 | 	/// List of validators.
 | ||||||
| 	pub authorities: Vec<Address>, | 	pub validators: ethjson::spec::ValidatorSet, | ||||||
| 	/// Number of authorities.
 |  | ||||||
| 	pub authority_n: usize, |  | ||||||
| 	/// Timeout durations for different steps.
 | 	/// Timeout durations for different steps.
 | ||||||
| 	pub timeouts: TendermintTimeouts, | 	pub timeouts: TendermintTimeouts, | ||||||
| } | 	/// Block reward.
 | ||||||
| 
 | 	pub block_reward: U256, | ||||||
| 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(), |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn to_duration(ms: ethjson::uint::Uint) -> Duration { | 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 { | impl From<ethjson::spec::TendermintParams> for TendermintParams { | ||||||
| 	fn from(p: ethjson::spec::TendermintParams) -> Self { | 	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(); | 		let dt = TendermintTimeouts::default(); | ||||||
| 		TendermintParams { | 		TendermintParams { | ||||||
| 			gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(), | 			gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(), | ||||||
| 			authorities: val, | 			validators: p.validators, | ||||||
| 			authority_n: val_n, |  | ||||||
| 			timeouts: TendermintTimeouts { | 			timeouts: TendermintTimeouts { | ||||||
| 				propose: p.timeout_propose.map_or(dt.propose, to_duration), | 				propose: p.timeout_propose.map_or(dt.propose, to_duration), | ||||||
| 				prevote: p.timeout_prevote.map_or(dt.prevote, to_duration), | 				prevote: p.timeout_prevote.map_or(dt.prevote, to_duration), | ||||||
| 				precommit: p.timeout_precommit.map_or(dt.precommit, to_duration), | 				precommit: p.timeout_precommit.map_or(dt.precommit, to_duration), | ||||||
| 				commit: p.timeout_commit.map_or(dt.commit, 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; | use engines::Engine; | ||||||
| 
 | 
 | ||||||
| pub struct TransitionHandler { | 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.
 | /// Base timeout of each step in ms.
 | ||||||
| @ -67,9 +77,7 @@ fn set_timeout(io: &IoContext<Step>, timeout: Duration) { | |||||||
| 
 | 
 | ||||||
| impl IoHandler<Step> for TransitionHandler { | impl IoHandler<Step> for TransitionHandler { | ||||||
| 	fn initialize(&self, io: &IoContext<Step>) { | 	fn initialize(&self, io: &IoContext<Step>) { | ||||||
| 		if let Some(engine) = self.engine.upgrade() { | 		set_timeout(io, self.timeouts.propose) | ||||||
| 			set_timeout(io, engine.our_params.timeouts.propose) |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fn timeout(&self, _io: &IoContext<Step>, timer: TimerToken) { | 	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) { | 	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) { | 		if let Err(io_err) = io.clear_timer(ENGINE_TIMEOUT_TOKEN) { | ||||||
| 			warn!(target: "poa", "Could not remove consensus timer {}.", io_err) | 			warn!(target: "poa", "Could not remove consensus timer {}.", io_err) | ||||||
| 		} | 		} | ||||||
| 		match *next_step { | 		match *next_step { | ||||||
| 				Step::Propose => set_timeout(io, engine.our_params.timeouts.propose), | 			Step::Propose => set_timeout(io, self.timeouts.propose), | ||||||
| 				Step::Prevote => set_timeout(io, engine.our_params.timeouts.prevote), | 			Step::Prevote => set_timeout(io, self.timeouts.prevote), | ||||||
| 				Step::Precommit => set_timeout(io, engine.our_params.timeouts.precommit), | 			Step::Precommit => set_timeout(io, self.timeouts.precommit), | ||||||
| 				Step::Commit => set_timeout(io, engine.our_params.timeouts.commit), | 			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.
 | /// Transaction execution options.
 | ||||||
| #[derive(Default)] | #[derive(Default, Copy, Clone, PartialEq)] | ||||||
| pub struct TransactOptions { | pub struct TransactOptions { | ||||||
| 	/// Enable call tracing.
 | 	/// Enable call tracing.
 | ||||||
| 	pub tracing: bool, | 	pub tracing: bool, | ||||||
|  | |||||||
| @ -33,7 +33,7 @@ fn test_trie(json: &[u8], trie: TrieSpec) -> Vec<String> { | |||||||
| 			let key: Vec<u8> = key.into(); | 			let key: Vec<u8> = key.into(); | ||||||
| 			let value: Vec<u8> = value.map_or_else(Vec::new, Into::into); | 			let value: Vec<u8> = value.map_or_else(Vec::new, Into::into); | ||||||
| 			t.insert(&key, &value) | 			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() { | 		if *t.root() != test.root.into() { | ||||||
|  | |||||||
| @ -23,6 +23,7 @@ use std::cell::Cell; | |||||||
| use transaction::{SignedTransaction, Action}; | use transaction::{SignedTransaction, Action}; | ||||||
| use transient_hashmap::TransientHashMap; | use transient_hashmap::TransientHashMap; | ||||||
| use miner::{TransactionQueue, TransactionImportResult, TransactionOrigin, AccountDetails}; | use miner::{TransactionQueue, TransactionImportResult, TransactionOrigin, AccountDetails}; | ||||||
|  | use miner::transaction_queue::QueuingInstant; | ||||||
| use error::{Error, TransactionError}; | use error::{Error, TransactionError}; | ||||||
| use util::{Uint, U256, H256, Address, Hashable}; | use util::{Uint, U256, H256, Address, Hashable}; | ||||||
| 
 | 
 | ||||||
| @ -78,6 +79,7 @@ impl BanningTransactionQueue { | |||||||
| 	pub fn add_with_banlist<F, G>( | 	pub fn add_with_banlist<F, G>( | ||||||
| 		&mut self, | 		&mut self, | ||||||
| 		transaction: SignedTransaction, | 		transaction: SignedTransaction, | ||||||
|  | 		time: QueuingInstant, | ||||||
| 		account_details: &F, | 		account_details: &F, | ||||||
| 		gas_estimator: &G, | 		gas_estimator: &G, | ||||||
| 	) -> Result<TransactionImportResult, Error> where | 	) -> 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.
 | 	/// Ban transaction with given hash.
 | ||||||
| @ -158,7 +160,7 @@ impl BanningTransactionQueue { | |||||||
| 			Threshold::BanAfter(threshold) if count > threshold => { | 			Threshold::BanAfter(threshold) if count > threshold => { | ||||||
| 				// Banlist the sender.
 | 				// Banlist the sender.
 | ||||||
| 				// Remove all transactions from the queue.
 | 				// Remove all transactions from the queue.
 | ||||||
| 				self.remove_all(address, !U256::zero()); | 				self.cull(address, !U256::zero()); | ||||||
| 				true | 				true | ||||||
| 			}, | 			}, | ||||||
| 			_ => false | 			_ => false | ||||||
| @ -263,7 +265,7 @@ mod tests { | |||||||
| 		let mut txq = queue(); | 		let mut txq = queue(); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// 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
 | 		// then
 | ||||||
| 		// should also deref to queue
 | 		// should also deref to queue
 | ||||||
| @ -279,12 +281,12 @@ mod tests { | |||||||
| 		let banlist1 = txq.ban_sender(tx.sender().unwrap()); | 		let banlist1 = txq.ban_sender(tx.sender().unwrap()); | ||||||
| 		assert!(!banlist1, "Threshold not reached yet."); | 		assert!(!banlist1, "Threshold not reached yet."); | ||||||
| 		// Insert once
 | 		// 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); | 		assert_eq!(import1, TransactionImportResult::Current); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		let banlist2 = txq.ban_sender(tx.sender().unwrap()); | 		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
 | 		// then
 | ||||||
| 		assert!(banlist2, "Threshold should be reached - banned."); | 		assert!(banlist2, "Threshold should be reached - banned."); | ||||||
| @ -303,12 +305,12 @@ mod tests { | |||||||
| 		let banlist1 = txq.ban_recipient(recipient); | 		let banlist1 = txq.ban_recipient(recipient); | ||||||
| 		assert!(!banlist1, "Threshold not reached yet."); | 		assert!(!banlist1, "Threshold not reached yet."); | ||||||
| 		// Insert once
 | 		// 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); | 		assert_eq!(import1, TransactionImportResult::Current); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		let banlist2 = txq.ban_recipient(recipient); | 		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
 | 		// then
 | ||||||
| 		assert!(banlist2, "Threshold should be reached - banned."); | 		assert!(banlist2, "Threshold should be reached - banned."); | ||||||
| @ -325,12 +327,12 @@ mod tests { | |||||||
| 		let banlist1 = txq.ban_codehash(codehash); | 		let banlist1 = txq.ban_codehash(codehash); | ||||||
| 		assert!(!banlist1, "Threshold not reached yet."); | 		assert!(!banlist1, "Threshold not reached yet."); | ||||||
| 		// Insert once
 | 		// 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); | 		assert_eq!(import1, TransactionImportResult::Current); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		let banlist2 = txq.ban_codehash(codehash); | 		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
 | 		// then
 | ||||||
| 		assert!(banlist2, "Threshold should be reached - banned."); | 		assert!(banlist2, "Threshold should be reached - banned."); | ||||||
|  | |||||||
| @ -417,15 +417,12 @@ impl Miner { | |||||||
| 
 | 
 | ||||||
| 		let block = open_block.close(); | 		let block = open_block.close(); | ||||||
| 
 | 
 | ||||||
| 		let fetch_account = |a: &Address| AccountDetails { | 		let fetch_nonce = |a: &Address| chain.latest_nonce(a); | ||||||
| 			nonce: chain.latest_nonce(a), |  | ||||||
| 			balance: chain.latest_balance(a), |  | ||||||
| 		}; |  | ||||||
| 
 | 
 | ||||||
| 		{ | 		{ | ||||||
| 			let mut queue = self.transaction_queue.lock(); | 			let mut queue = self.transaction_queue.lock(); | ||||||
| 			for hash in invalid_transactions { | 			for hash in invalid_transactions { | ||||||
| 				queue.remove_invalid(&hash, &fetch_account); | 				queue.remove_invalid(&hash, &fetch_nonce); | ||||||
| 			} | 			} | ||||||
| 			for hash in transactions_to_penalize { | 			for hash in transactions_to_penalize { | ||||||
| 				queue.penalize(&hash); | 				queue.penalize(&hash); | ||||||
| @ -597,6 +594,8 @@ impl Miner { | |||||||
| 		let schedule = chain.latest_schedule(); | 		let schedule = chain.latest_schedule(); | ||||||
| 		let gas_required = |tx: &SignedTransaction| tx.gas_required(&schedule).into(); | 		let gas_required = |tx: &SignedTransaction| tx.gas_required(&schedule).into(); | ||||||
| 		let best_block_header = chain.best_block_header().decode(); | 		let best_block_header = chain.best_block_header().decode(); | ||||||
|  | 		let insertion_time = chain.chain_info().best_block_number; | ||||||
|  | 
 | ||||||
| 		transactions.into_iter() | 		transactions.into_iter() | ||||||
| 			.map(|tx| { | 			.map(|tx| { | ||||||
| 				if chain.transaction_block(TransactionId::Hash(tx.hash())).is_some() { | 				if chain.transaction_block(TransactionId::Hash(tx.hash())).is_some() { | ||||||
| @ -618,10 +617,10 @@ impl Miner { | |||||||
| 
 | 
 | ||||||
| 						match origin { | 						match origin { | ||||||
| 							TransactionOrigin::Local | TransactionOrigin::RetractedBlock => { | 							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 => { | 							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(); | 		let sealing_work = self.sealing_work.lock(); | ||||||
| 		match sealing_work.queue.peek_last_ref() { | 		match sealing_work.queue.peek_last_ref() { | ||||||
| 			Some(work) => { | 			Some(work) => { | ||||||
| @ -682,7 +681,7 @@ impl MinerService for Miner { | |||||||
| 
 | 
 | ||||||
| 				// TODO: merge this code with client.rs's fn call somwhow.
 | 				// TODO: merge this code with client.rs's fn call somwhow.
 | ||||||
| 				let header = block.header(); | 				let header = block.header(); | ||||||
| 				let last_hashes = Arc::new(chain.last_hashes()); | 				let last_hashes = Arc::new(client.last_hashes()); | ||||||
| 				let env_info = EnvInfo { | 				let env_info = EnvInfo { | ||||||
| 					number: header.number(), | 					number: header.number(), | ||||||
| 					author: *header.author(), | 					author: *header.author(), | ||||||
| @ -707,16 +706,14 @@ impl MinerService for Miner { | |||||||
| 					state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty); | 					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 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.
 | 				// TODO gav move this into Executive.
 | ||||||
| 				ret.state_diff = original_state.map(|original| state.diff_from(original)); | 				ret.state_diff = original_state.map(|original| state.diff_from(original)); | ||||||
| 
 | 
 | ||||||
| 				Ok(ret) | 				Ok(ret) | ||||||
| 			}, | 			}, | ||||||
| 			None => { | 			None => client.call(t, BlockId::Latest, analytics) | ||||||
| 				chain.call(t, BlockId::Latest, analytics) |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -765,9 +762,17 @@ impl MinerService for Miner { | |||||||
| 			if let Some(ref ap) = self.accounts { | 			if let Some(ref ap) = self.accounts { | ||||||
| 				ap.sign(address.clone(), Some(password.clone()), Default::default())?; | 				ap.sign(address.clone(), Some(password.clone()), Default::default())?; | ||||||
| 			} | 			} | ||||||
|  | 			// Limit the scope of the locks.
 | ||||||
|  | 			{ | ||||||
| 				let mut sealing_work = self.sealing_work.lock(); | 				let mut sealing_work = self.sealing_work.lock(); | ||||||
| 				sealing_work.enabled = self.engine.is_sealer(&address).unwrap_or(false); | 				sealing_work.enabled = self.engine.is_sealer(&address).unwrap_or(false); | ||||||
| 				*self.author.write() = address; | 				*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); | 			self.engine.set_signer(address, password); | ||||||
| 		} | 		} | ||||||
| 		Ok(()) | 		Ok(()) | ||||||
| @ -1141,8 +1146,13 @@ impl MinerService for Miner { | |||||||
| 
 | 
 | ||||||
| 		// ...and at the end remove the old ones
 | 		// ...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(); | 			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 { | 		if enacted.len() > 0 { | ||||||
|  | |||||||
| @ -82,7 +82,7 @@ impl PriceInfo { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[test] | #[test] #[ignore] | ||||||
| fn should_get_price_info() { | fn should_get_price_info() { | ||||||
| 	use std::sync::Arc; | 	use std::sync::Arc; | ||||||
| 	use std::time::Duration; | 	use std::time::Duration; | ||||||
|  | |||||||
| @ -51,8 +51,8 @@ | |||||||
| //!		let gas_estimator = |_tx: &SignedTransaction| 2.into();
 | //!		let gas_estimator = |_tx: &SignedTransaction| 2.into();
 | ||||||
| //!
 | //!
 | ||||||
| //!		let mut txq = TransactionQueue::default();
 | //!		let mut txq = TransactionQueue::default();
 | ||||||
| //!		txq.add(st2.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, None, &default_account_details, &gas_estimator).unwrap();
 | //!		txq.add(st1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
 | ||||||
| //!
 | //!
 | ||||||
| //!		// Check status
 | //!		// Check status
 | ||||||
| //!		assert_eq!(txq.status().pending, 2);
 | //!		assert_eq!(txq.status().pending, 2);
 | ||||||
| @ -64,7 +64,7 @@ | |||||||
| //!
 | //!
 | ||||||
| //!		// And when transaction is removed (but nonce haven't changed)
 | //!		// And when transaction is removed (but nonce haven't changed)
 | ||||||
| //!		// it will move subsequent transactions to future
 | //!		// 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().pending, 0);
 | ||||||
| //!		assert_eq!(txq.status().future, 1);
 | //!		assert_eq!(txq.status().future, 1);
 | ||||||
| //!		assert_eq!(txq.top_transactions().len(), 0);
 | //!		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
 | //!		- 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)
 | //!		  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.
 | //!		- 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 removes all transactions (either from `current` or `future`) with nonce < client nonce
 | ||||||
| //!     - It moves matching `future` transactions to `current`
 | //!     - 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.
 | //! 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::ops::Deref; | ||||||
| use std::cmp::Ordering; | use std::cmp::Ordering; | ||||||
| @ -258,16 +258,19 @@ struct VerifiedTransaction { | |||||||
| 	transaction: SignedTransaction, | 	transaction: SignedTransaction, | ||||||
| 	/// Transaction origin.
 | 	/// Transaction origin.
 | ||||||
| 	origin: TransactionOrigin, | 	origin: TransactionOrigin, | ||||||
|  | 	/// Insertion time
 | ||||||
|  | 	insertion_time: QueuingInstant, | ||||||
| 	/// Delay until specifid block.
 | 	/// Delay until specifid block.
 | ||||||
| 	min_block: Option<BlockNumber>, | 	min_block: Option<BlockNumber>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl VerifiedTransaction { | 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()?; | 		transaction.sender()?; | ||||||
| 		Ok(VerifiedTransaction { | 		Ok(VerifiedTransaction { | ||||||
| 			transaction: transaction, | 			transaction: transaction, | ||||||
| 			origin: origin, | 			origin: origin, | ||||||
|  | 			insertion_time: time, | ||||||
| 			min_block: min_block, | 			min_block: min_block, | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| @ -283,6 +286,10 @@ impl VerifiedTransaction { | |||||||
| 	fn sender(&self) -> Address { | 	fn sender(&self) -> Address { | ||||||
| 		self.transaction.sender().expect("Sender is verified in new; qed") | 		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)] | #[derive(Debug, Default)] | ||||||
| @ -488,6 +495,10 @@ pub enum PrioritizationStrategy { | |||||||
| 	GasFactorAndGasPrice, | 	GasFactorAndGasPrice, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /// Point in time when transaction was inserted.
 | ||||||
|  | pub type QueuingInstant = BlockNumber; | ||||||
|  | const DEFAULT_QUEUING_PERIOD: BlockNumber = 128; | ||||||
|  | 
 | ||||||
| /// `TransactionQueue` implementation
 | /// `TransactionQueue` implementation
 | ||||||
| pub struct TransactionQueue { | pub struct TransactionQueue { | ||||||
| 	/// Prioritization strategy for this queue
 | 	/// Prioritization strategy for this queue
 | ||||||
| @ -498,6 +509,10 @@ pub struct TransactionQueue { | |||||||
| 	tx_gas_limit: U256, | 	tx_gas_limit: U256, | ||||||
| 	/// Current gas limit (block gas limit * factor). Transactions above the limit will not be accepted (default to !0)
 | 	/// Current gas limit (block gas limit * factor). Transactions above the limit will not be accepted (default to !0)
 | ||||||
| 	gas_limit: U256, | 	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
 | 	/// Priority queue for transactions that can go to block
 | ||||||
| 	current: TransactionSet, | 	current: TransactionSet, | ||||||
| 	/// Priority queue for transactions that has been received but are not yet valid to go to block
 | 	/// Priority queue for transactions that has been received but are not yet valid to go to block
 | ||||||
| @ -545,6 +560,7 @@ impl TransactionQueue { | |||||||
| 			minimal_gas_price: U256::zero(), | 			minimal_gas_price: U256::zero(), | ||||||
| 			tx_gas_limit: tx_gas_limit, | 			tx_gas_limit: tx_gas_limit, | ||||||
| 			gas_limit: !U256::zero(), | 			gas_limit: !U256::zero(), | ||||||
|  | 			max_time_in_queue: DEFAULT_QUEUING_PERIOD, | ||||||
| 			current: current, | 			current: current, | ||||||
| 			future: future, | 			future: future, | ||||||
| 			by_hash: HashMap::new(), | 			by_hash: HashMap::new(), | ||||||
| @ -624,6 +640,7 @@ impl TransactionQueue { | |||||||
| 		&mut self, | 		&mut self, | ||||||
| 		tx: SignedTransaction, | 		tx: SignedTransaction, | ||||||
| 		origin: TransactionOrigin, | 		origin: TransactionOrigin, | ||||||
|  | 		time: QueuingInstant, | ||||||
| 		min_block: Option<BlockNumber>, | 		min_block: Option<BlockNumber>, | ||||||
| 		fetch_account: &F, | 		fetch_account: &F, | ||||||
| 		gas_estimator: &G, | 		gas_estimator: &G, | ||||||
| @ -635,7 +652,7 @@ impl TransactionQueue { | |||||||
| 			let hash = tx.hash(); | 			let hash = tx.hash(); | ||||||
| 			let cloned_tx = tx.clone(); | 			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 { | 			match result { | ||||||
| 				Ok(TransactionImportResult::Current) => { | 				Ok(TransactionImportResult::Current) => { | ||||||
| 					self.local_transactions.mark_pending(hash); | 					self.local_transactions.mark_pending(hash); | ||||||
| @ -656,7 +673,7 @@ impl TransactionQueue { | |||||||
| 			} | 			} | ||||||
| 			result | 			result | ||||||
| 		} else { | 		} 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, | 		&mut self, | ||||||
| 		tx: SignedTransaction, | 		tx: SignedTransaction, | ||||||
| 		origin: TransactionOrigin, | 		origin: TransactionOrigin, | ||||||
|  | 		time: QueuingInstant, | ||||||
| 		min_block: Option<BlockNumber>, | 		min_block: Option<BlockNumber>, | ||||||
| 		fetch_account: &F, | 		fetch_account: &F, | ||||||
| 		gas_estimator: &G, | 		gas_estimator: &G, | ||||||
| @ -734,10 +752,10 @@ impl TransactionQueue { | |||||||
| 		// Verify signature
 | 		// Verify signature
 | ||||||
| 		tx.check_low_s()?; | 		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 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 { | 		if client_account.balance < cost { | ||||||
| 			trace!(target: "txqueue", | 			trace!(target: "txqueue", | ||||||
| 				"Dropping transaction without sufficient balance: {:?} ({} < {})", | 				"Dropping transaction without sufficient balance: {:?} ({} < {})", | ||||||
| @ -759,7 +777,7 @@ impl TransactionQueue { | |||||||
| 
 | 
 | ||||||
| 	/// Removes all transactions from particular sender up to (excluding) given client (state) nonce.
 | 	/// Removes all transactions from particular sender up to (excluding) given client (state) nonce.
 | ||||||
| 	/// Client (State) Nonce = next valid nonce for this sender.
 | 	/// 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...
 | 		// Check if there is anything in current...
 | ||||||
| 		let should_check_in_current = self.current.by_address.row(&sender) | 		let should_check_in_current = self.current.by_address.row(&sender) | ||||||
| 			// If nonce == client_nonce nothing is changed
 | 			// If nonce == client_nonce nothing is changed
 | ||||||
| @ -775,11 +793,11 @@ impl TransactionQueue { | |||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		self.remove_all_internal(sender, client_nonce); | 		self.cull_internal(sender, client_nonce); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/// Always updates future and moves transactions from current to future.
 | 	/// 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
 | 		// We will either move transaction to future or remove it completely
 | ||||||
| 		// so there will be no transactions from this sender in current
 | 		// so there will be no transactions from this sender in current
 | ||||||
| 		self.last_nonces.remove(&sender); | 		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.
 | 	/// 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 | 	pub fn remove_old<F>(&mut self, fetch_account: &F, current_time: QueuingInstant) where | ||||||
| 		F: Fn(&Address) -> U256, | 		F: Fn(&Address) -> AccountDetails, | ||||||
| 	{ | 	{ | ||||||
| 		let senders = self.current.by_address.keys() | 		let senders = self.current.by_address.keys() | ||||||
| 			.chain(self.future.by_address.keys()) | 			.chain(self.future.by_address.keys()) | ||||||
| 			.cloned() | 			.map(|sender| (*sender, fetch_account(sender))) | ||||||
| 			.collect::<HashSet<_>>(); | 			.collect::<HashMap<_, _>>(); | ||||||
| 
 | 
 | ||||||
| 		for sender in senders { | 		for (sender, details) in senders.iter() { | ||||||
| 			self.remove_all(sender, fetch_nonce(&sender)); | 			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.
 | 	/// so transactions left in queue are processed according to client nonce.
 | ||||||
| 	///
 | 	///
 | ||||||
| 	/// If gap is introduced marks subsequent transactions as future
 | 	/// If gap is introduced marks subsequent transactions as future
 | ||||||
| 	pub fn remove_invalid<T>(&mut self, transaction_hash: &H256, fetch_account: &T) | 	pub fn remove_invalid<T>(&mut self, transaction_hash: &H256, fetch_nonce: &T) | ||||||
| 		where T: Fn(&Address) -> AccountDetails { | 		where T: Fn(&Address) -> U256 { | ||||||
| 
 | 
 | ||||||
| 		assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len()); | 		assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len()); | ||||||
| 		let transaction = self.by_hash.remove(transaction_hash); | 		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 transaction = transaction.expect("None is tested in early-exit condition above; qed"); | ||||||
| 		let sender = transaction.sender(); | 		let sender = transaction.sender(); | ||||||
| 		let nonce = transaction.nonce(); | 		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()); | 		trace!(target: "txqueue", "Removing invalid transaction: {:?}", transaction.hash()); | ||||||
| 
 | 
 | ||||||
| @ -889,7 +937,7 @@ impl TransactionQueue { | |||||||
| 		if order.is_some() { | 		if order.is_some() { | ||||||
| 			// This will keep consistency in queue
 | 			// This will keep consistency in queue
 | ||||||
| 			// Moves all to future and then promotes a batch from current:
 | 			// 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()); | 			assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len()); | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
| @ -1039,7 +1087,7 @@ impl TransactionQueue { | |||||||
| 
 | 
 | ||||||
| 	/// Finds transaction in the queue by hash (if any)
 | 	/// Finds transaction in the queue by hash (if any)
 | ||||||
| 	pub fn find(&self, hash: &H256) -> Option<SignedTransaction> { | 	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
 | 	/// 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 (tx1, tx2) = new_tx_pair(123.into(), 1.into(), 1.into(), 0.into()); | ||||||
| 		let sender = tx1.sender().unwrap(); | 		let sender = tx1.sender().unwrap(); | ||||||
| 		let nonce = tx1.nonce; | 		let nonce = tx1.nonce; | ||||||
| 		txq.add(tx1.clone(), 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, 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.status().pending, 2); | ||||||
| 		assert_eq!(txq.last_nonce(&sender), Some(nonce + 1.into())); | 		assert_eq!(txq.last_nonce(&sender), Some(nonce + 1.into())); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		let tx = new_tx(123.into(), 1.into()); | 		let tx = new_tx(123.into(), 1.into()); | ||||||
| 		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
 | 		// then
 | ||||||
| 		// No longer the case as we don't even consider a transaction that isn't above a full
 | 		// 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(), | 			gas_limit: !U256::zero(), | ||||||
| 		}; | 		}; | ||||||
| 		let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); | 		let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); | ||||||
| 		let tx1 = VerifiedTransaction::new(tx1, TransactionOrigin::External, None).unwrap(); | 		let tx1 = VerifiedTransaction::new(tx1, TransactionOrigin::External, 0, None).unwrap(); | ||||||
| 		let tx2 = VerifiedTransaction::new(tx2, TransactionOrigin::External, None).unwrap(); | 		let tx2 = VerifiedTransaction::new(tx2, TransactionOrigin::External, 0, None).unwrap(); | ||||||
| 		let mut by_hash = { | 		let mut by_hash = { | ||||||
| 			let mut x = HashMap::new(); | 			let mut x = HashMap::new(); | ||||||
| 			let tx1 = VerifiedTransaction::new(tx1.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, None).unwrap(); | 			let tx2 = VerifiedTransaction::new(tx2.transaction.clone(), TransactionOrigin::External, 0, None).unwrap(); | ||||||
| 			x.insert(tx1.hash(), tx1); | 			x.insert(tx1.hash(), tx1); | ||||||
| 			x.insert(tx2.hash(), tx2); | 			x.insert(tx2.hash(), tx2); | ||||||
| 			x | 			x | ||||||
| @ -1470,12 +1518,12 @@ mod test { | |||||||
| 		// Create two transactions with same nonce
 | 		// Create two transactions with same nonce
 | ||||||
| 		// (same hash)
 | 		// (same hash)
 | ||||||
| 		let (tx1, tx2) = new_tx_pair_default(0.into(), 0.into()); | 		let (tx1, tx2) = new_tx_pair_default(0.into(), 0.into()); | ||||||
| 		let tx1 = VerifiedTransaction::new(tx1, TransactionOrigin::External, None).unwrap(); | 		let tx1 = VerifiedTransaction::new(tx1, TransactionOrigin::External, 0, None).unwrap(); | ||||||
| 		let tx2 = VerifiedTransaction::new(tx2, TransactionOrigin::External, None).unwrap(); | 		let tx2 = VerifiedTransaction::new(tx2, TransactionOrigin::External, 0, None).unwrap(); | ||||||
| 		let by_hash = { | 		let by_hash = { | ||||||
| 			let mut x = HashMap::new(); | 			let mut x = HashMap::new(); | ||||||
| 			let tx1 = VerifiedTransaction::new(tx1.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, None).unwrap(); | 			let tx2 = VerifiedTransaction::new(tx2.transaction.clone(), TransactionOrigin::External, 0, None).unwrap(); | ||||||
| 			x.insert(tx1.hash(), tx1); | 			x.insert(tx1.hash(), tx1); | ||||||
| 			x.insert(tx2.hash(), tx2); | 			x.insert(tx2.hash(), tx2); | ||||||
| 			x | 			x | ||||||
| @ -1517,10 +1565,10 @@ mod test { | |||||||
| 			gas_limit: !U256::zero(), | 			gas_limit: !U256::zero(), | ||||||
| 		}; | 		}; | ||||||
| 		let tx = new_tx_default(); | 		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); | 		let order1 = TransactionOrder::for_transaction(&tx1, 0.into(), 1.into(), PrioritizationStrategy::GasPriceOnly); | ||||||
| 		assert!(set.insert(tx1.sender(), tx1.nonce(), order1).is_none()); | 		assert!(set.insert(tx1.sender(), tx1.nonce(), order1).is_none()); | ||||||
| 		let tx2 = VerifiedTransaction::new(tx, TransactionOrigin::External, None).unwrap(); | 		let tx2 = VerifiedTransaction::new(tx, TransactionOrigin::External, 0, None).unwrap(); | ||||||
| 		let order2 = TransactionOrder::for_transaction(&tx2, 0.into(), 1.into(), PrioritizationStrategy::GasPriceOnly); | 		let order2 = TransactionOrder::for_transaction(&tx2, 0.into(), 1.into(), PrioritizationStrategy::GasPriceOnly); | ||||||
| 		assert!(set.insert(tx2.sender(), tx2.nonce(), order2).is_some()); | 		assert!(set.insert(tx2.sender(), tx2.nonce(), order2).is_some()); | ||||||
| 	} | 	} | ||||||
| @ -1537,7 +1585,7 @@ mod test { | |||||||
| 
 | 
 | ||||||
| 		assert_eq!(set.gas_price_entry_limit(), 0.into()); | 		assert_eq!(set.gas_price_entry_limit(), 0.into()); | ||||||
| 		let tx = new_tx_default(); | 		let tx = new_tx_default(); | ||||||
| 		let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External, 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); | 		let order1 = TransactionOrder::for_transaction(&tx1, 0.into(), 1.into(), PrioritizationStrategy::GasPriceOnly); | ||||||
| 		assert!(set.insert(tx1.sender(), tx1.nonce(), order1.clone()).is_none()); | 		assert!(set.insert(tx1.sender(), tx1.nonce(), order1.clone()).is_none()); | ||||||
| 		assert_eq!(set.gas_price_entry_limit(), 2.into()); | 		assert_eq!(set.gas_price_entry_limit(), 2.into()); | ||||||
| @ -1552,12 +1600,12 @@ mod test { | |||||||
| 			!U256::zero() }; | 			!U256::zero() }; | ||||||
| 
 | 
 | ||||||
| 		// First insert one transaction to future
 | 		// 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!(res.unwrap(), TransactionImportResult::Future); | ||||||
| 		assert_eq!(txq.status().future, 1); | 		assert_eq!(txq.status().future, 1); | ||||||
| 
 | 
 | ||||||
| 		// now import second transaction to current
 | 		// 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)
 | 		// and then there should be only one transaction in current (the one with higher gas_price)
 | ||||||
| 		assert_eq!(res.unwrap(), TransactionImportResult::Current); | 		assert_eq!(res.unwrap(), TransactionImportResult::Current); | ||||||
| @ -1577,12 +1625,12 @@ mod test { | |||||||
| 			!U256::zero() }; | 			!U256::zero() }; | ||||||
| 
 | 
 | ||||||
| 		// First insert one transaction to future
 | 		// 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!(res.unwrap(), TransactionImportResult::Future); | ||||||
| 		assert_eq!(txq.status().future, 1); | 		assert_eq!(txq.status().future, 1); | ||||||
| 
 | 
 | ||||||
| 		// now import second transaction to current
 | 		// 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
 | 		// then
 | ||||||
| 		assert_eq!(res.unwrap(), TransactionImportResult::Current); | 		assert_eq!(res.unwrap(), TransactionImportResult::Current); | ||||||
| @ -1601,7 +1649,7 @@ mod test { | |||||||
| 		let tx = new_tx_default(); | 		let tx = new_tx_default(); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// 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
 | 		// then
 | ||||||
| 		assert_eq!(res.unwrap(), TransactionImportResult::Current); | 		assert_eq!(res.unwrap(), TransactionImportResult::Current); | ||||||
| @ -1620,10 +1668,10 @@ mod test { | |||||||
| 		txq.set_minimal_gas_price(15.into()); | 		txq.set_minimal_gas_price(15.into()); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		let res1 = txq.add(tx1, 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, 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, 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, None, &default_account_details, &gas_estimator); | 		let res4 = txq.add(tx4, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); | ||||||
| 
 | 
 | ||||||
| 		// then
 | 		// then
 | ||||||
| 		assert_eq!(res1.unwrap(), TransactionImportResult::Current); | 		assert_eq!(res1.unwrap(), TransactionImportResult::Current); | ||||||
| @ -1654,10 +1702,10 @@ mod test { | |||||||
| 		txq.set_minimal_gas_price(15.into()); | 		txq.set_minimal_gas_price(15.into()); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		let res1 = txq.add(tx1, 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, 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, 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, None, &default_account_details, &gas_estimator); | 		let res4 = txq.add(tx4, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator); | ||||||
| 
 | 
 | ||||||
| 		// then
 | 		// then
 | ||||||
| 		assert_eq!(res1.unwrap(), TransactionImportResult::Current); | 		assert_eq!(res1.unwrap(), TransactionImportResult::Current); | ||||||
| @ -1700,7 +1748,7 @@ mod test { | |||||||
| 		txq.set_gas_limit(limit); | 		txq.set_gas_limit(limit); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// 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
 | 		// then
 | ||||||
| 		assert_eq!(unwrap_tx_err(res), TransactionError::GasLimitExceeded { | 		assert_eq!(unwrap_tx_err(res), TransactionError::GasLimitExceeded { | ||||||
| @ -1724,7 +1772,7 @@ mod test { | |||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// 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
 | 		// then
 | ||||||
| 		assert_eq!(unwrap_tx_err(res), TransactionError::InsufficientBalance { | 		assert_eq!(unwrap_tx_err(res), TransactionError::InsufficientBalance { | ||||||
| @ -1744,7 +1792,7 @@ mod test { | |||||||
| 		txq.set_minimal_gas_price(tx.gas_price + U256::one()); | 		txq.set_minimal_gas_price(tx.gas_price + U256::one()); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// 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
 | 		// then
 | ||||||
| 		assert_eq!(unwrap_tx_err(res), TransactionError::InsufficientGasPrice { | 		assert_eq!(unwrap_tx_err(res), TransactionError::InsufficientGasPrice { | ||||||
| @ -1764,7 +1812,7 @@ mod test { | |||||||
| 		txq.set_minimal_gas_price(tx.gas_price + U256::one()); | 		txq.set_minimal_gas_price(tx.gas_price + U256::one()); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// 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
 | 		// then
 | ||||||
| 		assert_eq!(res.unwrap(), TransactionImportResult::Current); | 		assert_eq!(res.unwrap(), TransactionImportResult::Current); | ||||||
| @ -1794,7 +1842,7 @@ mod test { | |||||||
| 			rlp::decode(s.as_raw()) | 			rlp::decode(s.as_raw()) | ||||||
| 		}; | 		}; | ||||||
| 		// when
 | 		// 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
 | 		// then
 | ||||||
| 		assert!(res.is_err()); | 		assert!(res.is_err()); | ||||||
| @ -1808,8 +1856,8 @@ mod test { | |||||||
| 		let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); | 		let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		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(); | ||||||
| 		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
 | 		// then
 | ||||||
| 		let top = txq.top_transactions(); | 		let top = txq.top_transactions(); | ||||||
| @ -1828,9 +1876,9 @@ mod test { | |||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		// first insert the one with higher gas price
 | 		// 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
 | 		// 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
 | 		// then
 | ||||||
| 		let top = txq.top_transactions(); | 		let top = txq.top_transactions(); | ||||||
| @ -1847,15 +1895,15 @@ mod test { | |||||||
| 		// the second one has same nonce but higher `gas_price`
 | 		// the second one has same nonce but higher `gas_price`
 | ||||||
| 		let (_, tx0) = new_similar_tx_pair(); | 		let (_, tx0) = new_similar_tx_pair(); | ||||||
| 
 | 
 | ||||||
| 		txq.add(tx0.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, 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
 | 		// the one with higher gas price is first
 | ||||||
| 		assert_eq!(txq.top_transactions()[0], tx0); | 		assert_eq!(txq.top_transactions()[0], tx0); | ||||||
| 		assert_eq!(txq.top_transactions()[1], tx1); | 		assert_eq!(txq.top_transactions()[1], tx1); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		// insert second as local
 | 		// 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
 | 		// then
 | ||||||
| 		// the order should be updated
 | 		// the order should be updated
 | ||||||
| @ -1874,9 +1922,9 @@ mod test { | |||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		// first insert local one with higher gas price
 | 		// 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
 | 		// 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
 | 		// then
 | ||||||
| 		let top = txq.top_transactions(); | 		let top = txq.top_transactions(); | ||||||
| @ -1892,8 +1940,8 @@ mod test { | |||||||
| 		let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); | 		let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		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(); | ||||||
| 		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
 | 		// then
 | ||||||
| 		let top = txq.top_transactions(); | 		let top = txq.top_transactions(); | ||||||
| @ -1912,10 +1960,10 @@ mod test { | |||||||
| 		let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into()); | 		let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into()); | ||||||
| 
 | 
 | ||||||
| 		// insert everything
 | 		// insert everything
 | ||||||
| 		txq.add(txa.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, 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, 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, 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); | 		assert_eq!(txq.status().future, 4); | ||||||
| 
 | 
 | ||||||
| @ -1940,10 +1988,10 @@ mod test { | |||||||
| 		let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into()); | 		let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into()); | ||||||
| 
 | 
 | ||||||
| 		// insert everything
 | 		// insert everything
 | ||||||
| 		txq.add(txa.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, 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, 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, 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(); | 		let top = txq.top_transactions(); | ||||||
| 		assert_eq!(top[0], tx1); | 		assert_eq!(top[0], tx1); | ||||||
| @ -1973,10 +2021,10 @@ mod test { | |||||||
| 		let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into()); | 		let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into()); | ||||||
| 
 | 
 | ||||||
| 		// insert everything
 | 		// insert everything
 | ||||||
| 		txq.add(txa.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, 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, 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, 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(); | 		let top = txq.top_transactions(); | ||||||
| 		assert_eq!(top[0], tx1); | 		assert_eq!(top[0], tx1); | ||||||
| @ -2005,8 +2053,8 @@ mod test { | |||||||
| 		let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); | 		let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		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(); | ||||||
| 		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
 | 		// then
 | ||||||
| 		let top = txq.pending_hashes(); | 		let top = txq.pending_hashes(); | ||||||
| @ -2023,8 +2071,8 @@ mod test { | |||||||
| 		let (tx, tx2) = new_tx_pair_default(2.into(), 0.into()); | 		let (tx, tx2) = new_tx_pair_default(2.into(), 0.into()); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		let res1 = txq.add(tx.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, None, &default_account_details, &gas_estimator).unwrap(); | 		let res2 = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); | ||||||
| 
 | 
 | ||||||
| 		// then
 | 		// then
 | ||||||
| 		assert_eq!(res1, TransactionImportResult::Current); | 		assert_eq!(res1, TransactionImportResult::Current); | ||||||
| @ -2045,8 +2093,8 @@ mod test { | |||||||
| 		let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); | 		let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		let res1 = txq.add(tx.clone(), TransactionOrigin::External, Some(1), &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, None, &default_account_details, &gas_estimator).unwrap(); | 		let res2 = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); | ||||||
| 
 | 
 | ||||||
| 		// then
 | 		// then
 | ||||||
| 		assert_eq!(res1, TransactionImportResult::Current); | 		assert_eq!(res1, TransactionImportResult::Current); | ||||||
| @ -2067,12 +2115,12 @@ mod test { | |||||||
| 		let mut txq = TransactionQueue::default(); | 		let mut txq = TransactionQueue::default(); | ||||||
| 
 | 
 | ||||||
| 		let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); | 		let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); | ||||||
| 		txq.add(tx.clone(), 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, 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); | 		assert_eq!(txq.status().future, 2); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// 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
 | 		// should remove both transactions since they are not valid
 | ||||||
| 
 | 
 | ||||||
| 		// then
 | 		// then
 | ||||||
| @ -2090,13 +2138,13 @@ mod test { | |||||||
| 		let tx1 = new_unsigned_tx(124.into(), default_gas_val(), 1.into()).sign(secret, None); | 		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); | 		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); | 		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); | 		assert_eq!(txq.status().future, 1); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// 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
 | 		// then
 | ||||||
| 		let stats = txq.status(); | 		let stats = txq.status(); | ||||||
| @ -2112,14 +2160,14 @@ mod test { | |||||||
| 		// given
 | 		// given
 | ||||||
| 		let mut txq2 = TransactionQueue::default(); | 		let mut txq2 = TransactionQueue::default(); | ||||||
| 		let (tx, tx2) = new_tx_pair_default(3.into(), 0.into()); | 		let (tx, tx2) = new_tx_pair_default(3.into(), 0.into()); | ||||||
| 		txq2.add(tx.clone(), 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, 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().pending, 1); | ||||||
| 		assert_eq!(txq2.status().future, 1); | 		assert_eq!(txq2.status().future, 1); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		txq2.remove_all(tx.sender().unwrap(), tx.nonce + U256::one()); | 		txq2.cull(tx.sender().unwrap(), tx.nonce + U256::one()); | ||||||
| 		txq2.remove_all(tx2.sender().unwrap(), tx2.nonce + U256::one()); | 		txq2.cull(tx2.sender().unwrap(), tx2.nonce + U256::one()); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 		// then
 | 		// then
 | ||||||
| @ -2134,14 +2182,14 @@ mod test { | |||||||
| 		let mut txq = TransactionQueue::default(); | 		let mut txq = TransactionQueue::default(); | ||||||
| 		let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); | 		let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); | ||||||
| 		let tx3 = new_tx_default(); | 		let tx3 = new_tx_default(); | ||||||
| 		txq.add(tx2.clone(), 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().future, 1); | ||||||
| 		txq.add(tx3.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, 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); | 		assert_eq!(txq.status().pending, 3); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		txq.remove_invalid(&tx.hash(), &default_account_details); | 		txq.remove_invalid(&tx.hash(), &|_| default_nonce()); | ||||||
| 
 | 
 | ||||||
| 		// then
 | 		// then
 | ||||||
| 		let stats = txq.status(); | 		let stats = txq.status(); | ||||||
| @ -2156,8 +2204,8 @@ mod test { | |||||||
| 		let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); | 		let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); | ||||||
| 
 | 
 | ||||||
| 		// add
 | 		// add
 | ||||||
| 		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(); | ||||||
| 		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(); | ||||||
| 		let stats = txq.status(); | 		let stats = txq.status(); | ||||||
| 		assert_eq!(stats.pending, 2); | 		assert_eq!(stats.pending, 2); | ||||||
| 
 | 
 | ||||||
| @ -2176,11 +2224,11 @@ mod test { | |||||||
| 		let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); | 		let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); | ||||||
| 		let sender = tx.sender().unwrap(); | 		let sender = tx.sender().unwrap(); | ||||||
| 		let nonce = tx.nonce; | 		let nonce = tx.nonce; | ||||||
| 		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); | 		assert_eq!(txq.status().pending, 1); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// 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
 | 		// then
 | ||||||
| 		let t = txq.top_transactions(); | 		let t = txq.top_transactions(); | ||||||
| @ -2197,14 +2245,14 @@ mod test { | |||||||
| 		txq.current.set_limit(10); | 		txq.current.set_limit(10); | ||||||
| 		let (tx1, tx2) = new_tx_pair_default(4.into(), 1.into()); | 		let (tx1, tx2) = new_tx_pair_default(4.into(), 1.into()); | ||||||
| 		let (tx3, tx4) = new_tx_pair_default(4.into(), 2.into()); | 		let (tx3, tx4) = new_tx_pair_default(4.into(), 2.into()); | ||||||
| 		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(); | ||||||
| 		txq.add(tx3.clone(), TransactionOrigin::External, 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); | 		assert_eq!(txq.status().pending, 2); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// 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); | 		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
 | 		// then
 | ||||||
| 		assert_eq!(txq.status().future, 1); | 		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 mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 100, default_gas_val() * U256::from(2), !U256::zero()); | ||||||
| 		let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1)); | 		let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1)); | ||||||
| 		let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2)); | 		let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2)); | ||||||
| 		txq.add(tx1.clone(), 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, 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, None, &default_account_details, &gas_estimator).unwrap(); | 		txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(); | ||||||
| 		// limited by gas
 | 		// 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); | 		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 (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1)); | ||||||
| 		let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2)); | 		let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2)); | ||||||
| 		let (tx5, _) = 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(tx1.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); | ||||||
| 		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(); | ||||||
| 		// Not accepted because of limit
 | 		// Not accepted because of limit
 | ||||||
| 		txq.add(tx5.clone(), TransactionOrigin::External, None, &default_account_details, &gas_estimator).unwrap_err(); | 		txq.add(tx5.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap_err(); | ||||||
| 		txq.add(tx3.clone(), TransactionOrigin::Local, None, &default_account_details, &gas_estimator).unwrap(); | 		txq.add(tx3.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(); | ||||||
| 		txq.add(tx4.clone(), TransactionOrigin::Local, 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); | 		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() }; | 		let fetch_last_nonce = |_a: &Address| AccountDetails { nonce: last_nonce, balance: !U256::zero() }; | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// 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
 | 		// then
 | ||||||
| 		assert_eq!(unwrap_tx_err(res), TransactionError::Old); | 		assert_eq!(unwrap_tx_err(res), TransactionError::Old); | ||||||
| @ -2262,12 +2310,12 @@ mod test { | |||||||
| 			balance: !U256::zero() }; | 			balance: !U256::zero() }; | ||||||
| 		let mut txq = TransactionQueue::default(); | 		let mut txq = TransactionQueue::default(); | ||||||
| 		let (_tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); | 		let (_tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); | ||||||
| 		txq.add(tx2.clone(), 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().future, 1); | ||||||
| 		assert_eq!(txq.status().pending, 0); | 		assert_eq!(txq.status().pending, 0); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// 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
 | 		// then
 | ||||||
| 		assert_eq!(unwrap_tx_err(res), TransactionError::AlreadyImported); | 		assert_eq!(unwrap_tx_err(res), TransactionError::AlreadyImported); | ||||||
| @ -2281,15 +2329,15 @@ mod test { | |||||||
| 		// given
 | 		// given
 | ||||||
| 		let mut txq = TransactionQueue::default(); | 		let mut txq = TransactionQueue::default(); | ||||||
| 		let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); | 		let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); | ||||||
| 		txq.add(tx1.clone(), 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, 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.status().pending, 2); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// 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().pending, 0); | ||||||
| 		assert_eq!(txq.status().future, 1); | 		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
 | 		// then
 | ||||||
| 		let stats = txq.status(); | 		let stats = txq.status(); | ||||||
| @ -2303,15 +2351,15 @@ mod test { | |||||||
| 		let mut txq = TransactionQueue::default(); | 		let mut txq = TransactionQueue::default(); | ||||||
| 		let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); | 		let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); | ||||||
| 		let tx3 = new_tx_default(); | 		let tx3 = new_tx_default(); | ||||||
| 		txq.add(tx2.clone(), 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().future, 1); | ||||||
| 		txq.add(tx3.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, 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); | 		assert_eq!(txq.status().pending, 3); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		let sender = tx.sender().unwrap(); | 		let sender = tx.sender().unwrap(); | ||||||
| 		txq.remove_all(sender, default_nonce() + U256::one()); | 		txq.cull(sender, default_nonce() + U256::one()); | ||||||
| 
 | 
 | ||||||
| 		// then
 | 		// then
 | ||||||
| 		let stats = txq.status(); | 		let stats = txq.status(); | ||||||
| @ -2333,8 +2381,8 @@ mod test { | |||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		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(); | ||||||
| 		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(); | ||||||
| 
 | 
 | ||||||
| 		// then
 | 		// then
 | ||||||
| 		let stats = txq.status(); | 		let stats = txq.status(); | ||||||
| @ -2361,10 +2409,10 @@ mod test { | |||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// 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(); | ||||||
| 		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); | 		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
 | 		// then
 | ||||||
| 		let stats = txq.status(); | 		let stats = txq.status(); | ||||||
| @ -2376,18 +2424,16 @@ mod test { | |||||||
| 	#[test] | 	#[test] | ||||||
| 	fn should_recalculate_height_when_removing_from_future() { | 	fn should_recalculate_height_when_removing_from_future() { | ||||||
| 		// given
 | 		// given
 | ||||||
| 		let previous_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance: | 		let previous_nonce = |a: &Address| | ||||||
| 			!U256::zero() }; | 			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 mut txq = TransactionQueue::default(); | 		let mut txq = TransactionQueue::default(); | ||||||
| 		let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); | 		let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); | ||||||
| 		txq.add(tx1.clone(), 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, None, &previous_nonce, &gas_estimator).unwrap(); | 		txq.add(tx2, TransactionOrigin::External, 0, None, &previous_nonce, &gas_estimator).unwrap(); | ||||||
| 		assert_eq!(txq.status().future, 2); | 		assert_eq!(txq.status().future, 2); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		txq.remove_invalid(&tx1.hash(), &next_nonce); | 		txq.remove_invalid(&tx1.hash(), &|_| default_nonce() + 1.into()); | ||||||
| 
 | 
 | ||||||
| 		// then
 | 		// then
 | ||||||
| 		let stats = txq.status(); | 		let stats = txq.status(); | ||||||
| @ -2414,7 +2460,7 @@ mod test { | |||||||
| 		let details = |_a: &Address| AccountDetails { nonce: nonce, balance: !U256::zero() }; | 		let details = |_a: &Address| AccountDetails { nonce: nonce, balance: !U256::zero() }; | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		txq.add(tx, TransactionOrigin::External, None, &details, &gas_estimator).unwrap(); | 		txq.add(tx, TransactionOrigin::External, 0, None, &details, &gas_estimator).unwrap(); | ||||||
| 
 | 
 | ||||||
| 		// then
 | 		// then
 | ||||||
| 		assert_eq!(txq.last_nonce(&from), Some(nonce)); | 		assert_eq!(txq.last_nonce(&from), Some(nonce)); | ||||||
| @ -2429,17 +2475,17 @@ mod test { | |||||||
| 		let details1 = |_a: &Address| AccountDetails { nonce: nonce1, balance: !U256::zero() }; | 		let details1 = |_a: &Address| AccountDetails { nonce: nonce1, balance: !U256::zero() }; | ||||||
| 
 | 
 | ||||||
| 		// Insert first transaction
 | 		// 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
 | 		// when
 | ||||||
| 		txq.remove_all(tx2.sender().unwrap(), nonce2 + U256::one()); | 		txq.cull(tx2.sender().unwrap(), nonce2 + U256::one()); | ||||||
| 
 | 
 | ||||||
| 		// then
 | 		// then
 | ||||||
| 		assert!(txq.top_transactions().is_empty()); | 		assert!(txq.top_transactions().is_empty()); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	#[test] | 	#[test] | ||||||
| 	fn should_return_valid_last_nonce_after_remove_all() { | 	fn should_return_valid_last_nonce_after_cull() { | ||||||
| 		// given
 | 		// given
 | ||||||
| 		let mut txq = TransactionQueue::default(); | 		let mut txq = TransactionQueue::default(); | ||||||
| 		let (tx1, tx2) = new_tx_pair_default(4.into(), 0.into()); | 		let (tx1, tx2) = new_tx_pair_default(4.into(), 0.into()); | ||||||
| @ -2449,11 +2495,11 @@ mod test { | |||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		// Insert first transaction
 | 		// 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
 | 		// 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
 | 		// 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
 | 		// tx2 should be not be promoted to current
 | ||||||
| 		assert_eq!(txq.status().pending, 0); | 		assert_eq!(txq.status().pending, 0); | ||||||
| 		assert_eq!(txq.status().future, 1); | 		assert_eq!(txq.status().future, 1); | ||||||
| @ -2470,9 +2516,9 @@ mod test { | |||||||
| 		assert_eq!(txq.has_local_pending_transactions(), false); | 		assert_eq!(txq.has_local_pending_transactions(), false); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// 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.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
 | 		// then
 | ||||||
| 		assert_eq!(txq.has_local_pending_transactions(), true); | 		assert_eq!(txq.has_local_pending_transactions(), true); | ||||||
| @ -2487,8 +2533,8 @@ mod test { | |||||||
| 			default_account_details(a).balance }; | 			default_account_details(a).balance }; | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		assert_eq!(txq.add(tx2, 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, 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
 | 		// then
 | ||||||
| 		assert_eq!(txq.future.by_priority.len(), 1); | 		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)) | 			(tx.sign(secret, None), tx2.sign(secret, None), tx2_2.sign(secret, None), tx3.sign(secret, None)) | ||||||
| 		}; | 		}; | ||||||
| 		let sender = tx1.sender().unwrap(); | 		let sender = tx1.sender().unwrap(); | ||||||
| 		txq.add(tx1, 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, 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, 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.future.by_priority.len(), 0); | ||||||
| 		assert_eq!(txq.current.by_priority.len(), 3); | 		assert_eq!(txq.current.by_priority.len(), 3); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// 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
 | 		// then
 | ||||||
| 		assert_eq!(txq.last_nonce(&sender).unwrap(), 125.into()); | 		assert_eq!(txq.last_nonce(&sender).unwrap(), 125.into()); | ||||||
| @ -2536,8 +2582,8 @@ mod test { | |||||||
| 		let high_gas = |_: &SignedTransaction| 100_001.into(); | 		let high_gas = |_: &SignedTransaction| 100_001.into(); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		let res1 = txq.add(tx1, TransactionOrigin::Local, None, &default_account_details, &gas_estimator); | 		let res1 = txq.add(tx1, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator); | ||||||
| 		let res2 = txq.add(tx2, TransactionOrigin::Local, None, &default_account_details, &high_gas); | 		let res2 = txq.add(tx2, TransactionOrigin::Local, 0, None, &default_account_details, &high_gas); | ||||||
| 
 | 
 | ||||||
| 		// then
 | 		// then
 | ||||||
| 		assert_eq!(res1.unwrap(), TransactionImportResult::Current); | 		assert_eq!(res1.unwrap(), TransactionImportResult::Current); | ||||||
| @ -2554,20 +2600,45 @@ mod test { | |||||||
| 		let mut txq = TransactionQueue::default(); | 		let mut txq = TransactionQueue::default(); | ||||||
| 		let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); | 		let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); | ||||||
| 		let (tx3, tx4) = 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
 | 		// Insert all transactions
 | ||||||
| 		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(); | ||||||
| 		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(); | ||||||
| 		txq.add(tx3, TransactionOrigin::External, 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, 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); | 		assert_eq!(txq.top_transactions().len(), 4); | ||||||
| 
 | 
 | ||||||
| 		// when
 | 		// when
 | ||||||
| 		txq.remove_old(|_| nonce1 + U256::one()); | 		txq.remove_old(&next_nonce, 0); | ||||||
| 
 | 
 | ||||||
| 		// then
 | 		// then
 | ||||||
| 		assert_eq!(txq.top_transactions().len(), 2); | 		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 io::*; | ||||||
| use spec::Spec; | use spec::Spec; | ||||||
| use error::*; | use error::*; | ||||||
| use client::{Client, BlockChainClient, MiningBlockChainClient, ClientConfig, ChainNotify}; | use client::{Client, ClientConfig, ChainNotify}; | ||||||
| use miner::Miner; | use miner::Miner; | ||||||
| use snapshot::ManifestData; | use snapshot::ManifestData; | ||||||
| use snapshot::service::{Service as SnapshotService, ServiceParams as SnapServiceParams}; | use snapshot::service::{Service as SnapshotService, ServiceParams as SnapServiceParams}; | ||||||
| @ -46,12 +46,6 @@ pub enum ClientIoMessage { | |||||||
| 	FeedBlockChunk(H256, Bytes), | 	FeedBlockChunk(H256, Bytes), | ||||||
| 	/// Take a snapshot for the block with given number.
 | 	/// Take a snapshot for the block with given number.
 | ||||||
| 	TakeSnapshot(u64), | 	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.
 | 	/// New consensus message received.
 | ||||||
| 	NewMessage(Bytes) | 	NewMessage(Bytes) | ||||||
| } | } | ||||||
| @ -114,7 +108,7 @@ impl ClientService { | |||||||
| 		}); | 		}); | ||||||
| 		io_service.register_handler(client_io)?; | 		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(); | 		let stop_guard = ::devtools::StopGuard::new(); | ||||||
| 		run_ipc(ipc_path, client.clone(), snapshot.clone(), stop_guard.share()); | 		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); | 					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) { | 			ClientIoMessage::NewMessage(ref message) => if let Err(e) = self.client.engine().handle_message(message) { | ||||||
| 				trace!(target: "poa", "Invalid message received: {}", e); | 				trace!(target: "poa", "Invalid message received: {}", e); | ||||||
| 			}, | 			}, | ||||||
|  | |||||||
| @ -17,16 +17,17 @@ | |||||||
| //! Account state encoding and decoding
 | //! Account state encoding and decoding
 | ||||||
| 
 | 
 | ||||||
| use account_db::{AccountDB, AccountDBMut}; | use account_db::{AccountDB, AccountDBMut}; | ||||||
|  | use basic_account::BasicAccount; | ||||||
| use snapshot::Error; | use snapshot::Error; | ||||||
| 
 | 
 | ||||||
| use util::{U256, FixedHash, H256, Bytes, HashDB, SHA3_EMPTY, SHA3_NULL_RLP}; | use util::{U256, FixedHash, H256, Bytes, HashDB, SHA3_EMPTY, SHA3_NULL_RLP}; | ||||||
| use util::trie::{TrieDB, Trie}; | use util::trie::{TrieDB, Trie}; | ||||||
| use rlp::{Rlp, RlpStream, Stream, UntrustedRlp, View}; | use rlp::{RlpStream, Stream, UntrustedRlp, View}; | ||||||
| 
 | 
 | ||||||
| use std::collections::HashSet; | use std::collections::HashSet; | ||||||
| 
 | 
 | ||||||
| // An empty account -- these are replaced with RLP null data for a space optimization.
 | // 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]), | 	nonce: U256([0, 0, 0, 0]), | ||||||
| 	balance: U256([0, 0, 0, 0]), | 	balance: U256([0, 0, 0, 0]), | ||||||
| 	storage_root: SHA3_NULL_RLP, | 	storage_root: SHA3_NULL_RLP, | ||||||
| @ -59,48 +60,14 @@ impl CodeState { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // An alternate account structure from ::account::Account.
 | // walk the account's storage trie, returning an RLP item containing the
 | ||||||
| #[derive(PartialEq, Clone, Debug)] | // account properties and the storage.
 | ||||||
| pub struct Account { | pub fn to_fat_rlp(acc: &BasicAccount, acct_db: &AccountDB, used_code: &mut HashSet<H256>) -> Result<Bytes, Error> { | ||||||
| 	nonce: U256, | 	if acc == &ACC_EMPTY { | ||||||
| 	balance: U256, |  | ||||||
| 	storage_root: H256, |  | ||||||
| 	code_hash: H256, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Account { |  | ||||||
| 	// decode the account from rlp.
 |  | ||||||
| 	pub fn from_thin_rlp(rlp: &[u8]) -> Self { |  | ||||||
| 		let r: Rlp = Rlp::new(rlp); |  | ||||||
| 
 |  | ||||||
| 		Account { |  | ||||||
| 			nonce: r.val_at(0), |  | ||||||
| 			balance: r.val_at(1), |  | ||||||
| 			storage_root: r.val_at(2), |  | ||||||
| 			code_hash: r.val_at(3), |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// 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); |  | ||||||
| 
 |  | ||||||
| 		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()); | 		return Ok(::rlp::NULL_RLP.to_vec()); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 		let db = TrieDB::new(acct_db, &self.storage_root)?; | 	let db = TrieDB::new(acct_db, &acc.storage_root)?; | ||||||
| 
 | 
 | ||||||
| 	let mut pairs = Vec::new(); | 	let mut pairs = Vec::new(); | ||||||
| 
 | 
 | ||||||
| @ -118,18 +85,18 @@ impl Account { | |||||||
| 	let pairs_rlp = stream.out(); | 	let pairs_rlp = stream.out(); | ||||||
| 
 | 
 | ||||||
| 	let mut account_stream = RlpStream::new_list(5); | 	let mut account_stream = RlpStream::new_list(5); | ||||||
| 		account_stream.append(&self.nonce) | 	account_stream.append(&acc.nonce) | ||||||
| 					  .append(&self.balance); | 				  .append(&acc.balance); | ||||||
| 
 | 
 | ||||||
| 	// [has_code, code_hash].
 | 	// [has_code, code_hash].
 | ||||||
| 		if self.code_hash == SHA3_EMPTY { | 	if acc.code_hash == SHA3_EMPTY { | ||||||
| 		account_stream.append(&CodeState::Empty.raw()).append_empty_data(); | 		account_stream.append(&CodeState::Empty.raw()).append_empty_data(); | ||||||
| 		} else if used_code.contains(&self.code_hash) { | 	} else if used_code.contains(&acc.code_hash) { | ||||||
| 			account_stream.append(&CodeState::Hash.raw()).append(&self.code_hash); | 		account_stream.append(&CodeState::Hash.raw()).append(&acc.code_hash); | ||||||
| 	} else { | 	} else { | ||||||
| 			match acct_db.get(&self.code_hash) { | 		match acct_db.get(&acc.code_hash) { | ||||||
| 			Some(c) => { | 			Some(c) => { | ||||||
| 					used_code.insert(self.code_hash.clone()); | 				used_code.insert(acc.code_hash.clone()); | ||||||
| 				account_stream.append(&CodeState::Inline.raw()).append(&&*c); | 				account_stream.append(&CodeState::Inline.raw()).append(&&*c); | ||||||
| 			} | 			} | ||||||
| 			None => { | 			None => { | ||||||
| @ -142,15 +109,15 @@ impl Account { | |||||||
| 	account_stream.append_raw(&pairs_rlp, 1); | 	account_stream.append_raw(&pairs_rlp, 1); | ||||||
| 
 | 
 | ||||||
| 	Ok(account_stream.out()) | 	Ok(account_stream.out()) | ||||||
| 	} | } | ||||||
| 
 | 
 | ||||||
| 	// decode a fat rlp, and rebuild the storage trie as we go.
 | // decode a fat rlp, and rebuild the storage trie as we go.
 | ||||||
| 	// returns the account structure along with its newly recovered code,
 | // returns the account structure along with its newly recovered code,
 | ||||||
| 	// if it exists.
 | // if it exists.
 | ||||||
| 	pub fn from_fat_rlp( | pub fn from_fat_rlp( | ||||||
| 	acct_db: &mut AccountDBMut, | 	acct_db: &mut AccountDBMut, | ||||||
| 	rlp: UntrustedRlp, | 	rlp: UntrustedRlp, | ||||||
| 	) -> Result<(Self, Option<Bytes>), Error> { | ) -> Result<(BasicAccount, Option<Bytes>), Error> { | ||||||
| 	use util::{TrieDBMut, TrieMut}; | 	use util::{TrieDBMut, TrieMut}; | ||||||
| 
 | 
 | ||||||
| 	// check for special case of empty account.
 | 	// check for special case of empty account.
 | ||||||
| @ -194,7 +161,7 @@ impl Account { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 		let acc = Account { | 	let acc = BasicAccount { | ||||||
| 		nonce: nonce, | 		nonce: nonce, | ||||||
| 		balance: balance, | 		balance: balance, | ||||||
| 		storage_root: storage_root, | 		storage_root: storage_root, | ||||||
| @ -202,22 +169,12 @@ impl Account { | |||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	Ok((acc, new_code)) | 	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 |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod tests { | mod tests { | ||||||
| 	use account_db::{AccountDB, AccountDBMut}; | 	use account_db::{AccountDB, AccountDBMut}; | ||||||
|  | 	use basic_account::BasicAccount; | ||||||
| 	use tests::helpers::get_temp_state_db; | 	use tests::helpers::get_temp_state_db; | ||||||
| 	use snapshot::tests::helpers::fill_storage; | 	use snapshot::tests::helpers::fill_storage; | ||||||
| 
 | 
 | ||||||
| @ -227,26 +184,26 @@ mod tests { | |||||||
| 
 | 
 | ||||||
| 	use std::collections::HashSet; | 	use std::collections::HashSet; | ||||||
| 
 | 
 | ||||||
| 	use super::{ACC_EMPTY, Account}; | 	use super::{ACC_EMPTY, to_fat_rlp, from_fat_rlp}; | ||||||
| 
 | 
 | ||||||
| 	#[test] | 	#[test] | ||||||
| 	fn encoding_basic() { | 	fn encoding_basic() { | ||||||
| 		let mut db = get_temp_state_db(); | 		let mut db = get_temp_state_db(); | ||||||
| 		let addr = Address::random(); | 		let addr = Address::random(); | ||||||
| 
 | 
 | ||||||
| 		let account = Account { | 		let account = BasicAccount { | ||||||
| 			nonce: 50.into(), | 			nonce: 50.into(), | ||||||
| 			balance: 123456789.into(), | 			balance: 123456789.into(), | ||||||
| 			storage_root: SHA3_NULL_RLP, | 			storage_root: SHA3_NULL_RLP, | ||||||
| 			code_hash: SHA3_EMPTY, | 			code_hash: SHA3_EMPTY, | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 		let thin_rlp = account.to_thin_rlp(); | 		let thin_rlp = ::rlp::encode(&account); | ||||||
| 		assert_eq!(Account::from_thin_rlp(&thin_rlp), 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); | 		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] | 	#[test] | ||||||
| @ -258,7 +215,7 @@ mod tests { | |||||||
| 			let acct_db = AccountDBMut::new(db.as_hashdb_mut(), &addr); | 			let acct_db = AccountDBMut::new(db.as_hashdb_mut(), &addr); | ||||||
| 			let mut root = SHA3_NULL_RLP; | 			let mut root = SHA3_NULL_RLP; | ||||||
| 			fill_storage(acct_db, &mut root, &mut H256::zero()); | 			fill_storage(acct_db, &mut root, &mut H256::zero()); | ||||||
| 			Account { | 			BasicAccount { | ||||||
| 				nonce: 25.into(), | 				nonce: 25.into(), | ||||||
| 				balance: 987654321.into(), | 				balance: 987654321.into(), | ||||||
| 				storage_root: root, | 				storage_root: root, | ||||||
| @ -266,12 +223,12 @@ mod tests { | |||||||
| 			} | 			} | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 		let thin_rlp = account.to_thin_rlp(); | 		let thin_rlp = ::rlp::encode(&account); | ||||||
| 		assert_eq!(Account::from_thin_rlp(&thin_rlp), 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); | 		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] | 	#[test] | ||||||
| @ -291,14 +248,14 @@ mod tests { | |||||||
| 			acct_db.emplace(code_hash.clone(), DBValue::from_slice(b"this is definitely code")); | 			acct_db.emplace(code_hash.clone(), DBValue::from_slice(b"this is definitely code")); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		let account1 = Account { | 		let account1 = BasicAccount { | ||||||
| 			nonce: 50.into(), | 			nonce: 50.into(), | ||||||
| 			balance: 123456789.into(), | 			balance: 123456789.into(), | ||||||
| 			storage_root: SHA3_NULL_RLP, | 			storage_root: SHA3_NULL_RLP, | ||||||
| 			code_hash: code_hash, | 			code_hash: code_hash, | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 		let account2 = Account { | 		let account2 = BasicAccount { | ||||||
| 			nonce: 400.into(), | 			nonce: 400.into(), | ||||||
| 			balance: 98765432123456789usize.into(), | 			balance: 98765432123456789usize.into(), | ||||||
| 			storage_root: SHA3_NULL_RLP, | 			storage_root: SHA3_NULL_RLP, | ||||||
| @ -307,18 +264,18 @@ mod tests { | |||||||
| 
 | 
 | ||||||
| 		let mut used_code = HashSet::new(); | 		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_rlp1 = to_fat_rlp(&account1, &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_rlp2 = to_fat_rlp(&account2, &AccountDB::new(db.as_hashdb(), &addr2), &mut used_code).unwrap(); | ||||||
| 		assert_eq!(used_code.len(), 1); | 		assert_eq!(used_code.len(), 1); | ||||||
| 
 | 
 | ||||||
| 		let fat_rlp1 = UntrustedRlp::new(&fat_rlp1); | 		let fat_rlp1 = UntrustedRlp::new(&fat_rlp1); | ||||||
| 		let fat_rlp2 = UntrustedRlp::new(&fat_rlp2); | 		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!(maybe_code.is_none()); | ||||||
| 		assert_eq!(acc, account2); | 		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!(maybe_code, Some(b"this is definitely code".to_vec())); | ||||||
| 		assert_eq!(acc, account1); | 		assert_eq!(acc, account1); | ||||||
| 	} | 	} | ||||||
| @ -328,7 +285,7 @@ mod tests { | |||||||
| 		let mut db = get_temp_state_db(); | 		let mut db = get_temp_state_db(); | ||||||
| 		let mut used_code = HashSet::new(); | 		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!(to_fat_rlp(&ACC_EMPTY, &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!(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 rlp::{RlpStream, Stream, UntrustedRlp, View}; | ||||||
| use bloom_journal::Bloom; | use bloom_journal::Bloom; | ||||||
| 
 | 
 | ||||||
| use self::account::Account; |  | ||||||
| use self::block::AbridgedBlock; | use self::block::AbridgedBlock; | ||||||
| use self::io::SnapshotWriter; | 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.
 | 	// account_key here is the address' hash.
 | ||||||
| 	for item in account_trie.iter()? { | 	for item in account_trie.iter()? { | ||||||
| 		let (account_key, account_data) = item?; | 		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_key_hash = H256::from_slice(&account_key); | ||||||
| 
 | 
 | ||||||
| 		let account_db = AccountDB::from_hash(db, account_key_hash); | 		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)?; | 		chunker.push(account_key, fat_rlp)?; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -507,10 +506,10 @@ fn rebuild_accounts( | |||||||
| 			// fill out the storage trie and code while decoding.
 | 			// fill out the storage trie and code while decoding.
 | ||||||
| 			let (acc, maybe_code) = { | 			let (acc, maybe_code) = { | ||||||
| 				let mut acct_db = AccountDBMut::from_hash(db, hash); | 				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 { | 			match maybe_code { | ||||||
| 				// new inline code
 | 				// new inline code
 | ||||||
| 				Some(code) => status.new_code.push((code_hash, code, hash)), | 				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); | 		*out = (hash, thin_rlp); | ||||||
|  | |||||||
| @ -17,9 +17,9 @@ | |||||||
| //! Snapshot test helpers. These are used to build blockchains and state tries
 | //! Snapshot test helpers. These are used to build blockchains and state tries
 | ||||||
| //! which can be queried before and after a full snapshot/restore cycle.
 | //! which can be queried before and after a full snapshot/restore cycle.
 | ||||||
| 
 | 
 | ||||||
|  | use basic_account::BasicAccount; | ||||||
| use account_db::AccountDBMut; | use account_db::AccountDBMut; | ||||||
| use rand::Rng; | use rand::Rng; | ||||||
| use snapshot::account::Account; |  | ||||||
| 
 | 
 | ||||||
| use util::DBValue; | use util::DBValue; | ||||||
| use util::hash::{FixedHash, H256}; | use util::hash::{FixedHash, H256}; | ||||||
| @ -64,10 +64,10 @@ impl StateProducer { | |||||||
| 
 | 
 | ||||||
| 		// sweep once to alter storage tries.
 | 		// sweep once to alter storage tries.
 | ||||||
| 		for &mut (ref mut address_hash, ref mut account_data) in &mut accounts_to_modify { | 		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); | 			let acct_db = AccountDBMut::from_hash(db, *address_hash); | ||||||
| 			fill_storage(acct_db, account.storage_root_mut(), &mut self.storage_seed); | 			fill_storage(acct_db, &mut account.storage_root, &mut self.storage_seed); | ||||||
| 			*account_data = DBValue::from_vec(account.to_thin_rlp()); | 			*account_data = DBValue::from_vec(::rlp::encode(&account).to_vec()); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// sweep again to alter account trie.
 | 		// sweep again to alter account trie.
 | ||||||
|  | |||||||
| @ -16,8 +16,9 @@ | |||||||
| 
 | 
 | ||||||
| //! State snapshotting tests.
 | //! State snapshotting tests.
 | ||||||
| 
 | 
 | ||||||
|  | use basic_account::BasicAccount; | ||||||
|  | use snapshot::account; | ||||||
| use snapshot::{chunk_state, Error as SnapshotError, Progress, StateRebuilder}; | use snapshot::{chunk_state, Error as SnapshotError, Progress, StateRebuilder}; | ||||||
| use snapshot::account::Account; |  | ||||||
| use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter}; | use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter}; | ||||||
| use super::helpers::{compare_dbs, StateProducer}; | use super::helpers::{compare_dbs, StateProducer}; | ||||||
| 
 | 
 | ||||||
| @ -113,22 +114,21 @@ fn get_code_from_prev_chunk() { | |||||||
| 	// first one will have code inlined,
 | 	// first one will have code inlined,
 | ||||||
| 	// second will just have its hash.
 | 	// second will just have its hash.
 | ||||||
| 	let thin_rlp = acc_stream.out(); | 	let thin_rlp = acc_stream.out(); | ||||||
| 	let acc1 = Account::from_thin_rlp(&thin_rlp); | 	let acc: BasicAccount = ::rlp::decode(&thin_rlp); | ||||||
| 	let acc2 = Account::from_thin_rlp(&thin_rlp); |  | ||||||
| 
 | 
 | ||||||
| 	let mut make_chunk = |acc: Account, hash| { | 	let mut make_chunk = |acc, hash| { | ||||||
| 		let mut db = MemoryDB::new(); | 		let mut db = MemoryDB::new(); | ||||||
| 		AccountDBMut::from_hash(&mut db, hash).insert(&code[..]); | 		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); | 		let mut stream = RlpStream::new_list(1); | ||||||
| 		stream.begin_list(2).append(&hash).append_raw(&fat_rlp, 1); | 		stream.begin_list(2).append(&hash).append_raw(&fat_rlp, 1); | ||||||
| 		stream.out() | 		stream.out() | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	let chunk1 = make_chunk(acc1, h1); | 	let chunk1 = make_chunk(acc.clone(), h1); | ||||||
| 	let chunk2 = make_chunk(acc2, h2); | 	let chunk2 = make_chunk(acc, h2); | ||||||
| 
 | 
 | ||||||
| 	let db_path = RandomTempPath::create_dir(); | 	let db_path = RandomTempPath::create_dir(); | ||||||
| 	let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS); | 	let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS); | ||||||
|  | |||||||
| @ -14,7 +14,7 @@ | |||||||
| // You should have received a copy of the GNU General Public License
 | // You should have received a copy of the GNU General Public License
 | ||||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
| 
 | 
 | ||||||
| use util::{Address, H256, Uint, U256}; | use util::{Address, H256, Uint, U256, FixedHash}; | ||||||
| use util::sha3::SHA3_NULL_RLP; | use util::sha3::SHA3_NULL_RLP; | ||||||
| use ethjson; | use ethjson; | ||||||
| use super::seal::Seal; | use super::seal::Seal; | ||||||
| @ -50,9 +50,9 @@ impl From<ethjson::spec::Genesis> for Genesis { | |||||||
| 		Genesis { | 		Genesis { | ||||||
| 			seal: From::from(g.seal), | 			seal: From::from(g.seal), | ||||||
| 			difficulty: g.difficulty.into(), | 			difficulty: g.difficulty.into(), | ||||||
| 			author: g.author.into(), | 			author: g.author.map_or_else(Address::zero, Into::into), | ||||||
| 			timestamp: g.timestamp.into(), | 			timestamp: g.timestamp.map_or(0, Into::into), | ||||||
| 			parent_hash: g.parent_hash.into(), | 			parent_hash: g.parent_hash.map_or_else(H256::zero, Into::into), | ||||||
| 			gas_limit: g.gas_limit.into(), | 			gas_limit: g.gas_limit.into(), | ||||||
| 			transactions_root: g.transactions_root.map_or_else(|| SHA3_NULL_RLP.clone(), Into::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), | 			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 { | impl From<ethjson::spec::Params> for CommonParams { | ||||||
| 	fn from(p: ethjson::spec::Params) -> Self { | 	fn from(p: ethjson::spec::Params) -> Self { | ||||||
| 		CommonParams { | 		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(), | 			maximum_extra_data_size: p.maximum_extra_data_size.into(), | ||||||
| 			network_id: p.network_id.into(), | 			network_id: p.network_id.into(), | ||||||
| 			chain_id: if let Some(n) = p.chain_id { n.into() } else { 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") } | 	pub fn new_instant() -> Spec { load_bundled!("instant_seal") } | ||||||
| 
 | 
 | ||||||
| 	/// Create a new Spec with AuthorityRound consensus which does internal sealing (not requiring work).
 | 	/// 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") } | 	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).
 | 	/// Create a new Spec with Tendermint consensus which does internal sealing (not requiring work).
 | ||||||
| 	/// Account "0".sha3() and "1".sha3() are a authorities.
 | 	/// Account "0".sha3() and "1".sha3() are a authorities.
 | ||||||
| 	pub fn new_test_tendermint() -> Self { load_bundled!("tendermint") } | 	pub fn new_test_tendermint() -> Self { load_bundled!("tendermint") } | ||||||
|  | |||||||
| @ -20,6 +20,7 @@ use util::*; | |||||||
| use pod_account::*; | use pod_account::*; | ||||||
| use rlp::*; | use rlp::*; | ||||||
| use lru_cache::LruCache; | use lru_cache::LruCache; | ||||||
|  | use basic_account::BasicAccount; | ||||||
| 
 | 
 | ||||||
| use std::cell::{RefCell, Cell}; | use std::cell::{RefCell, Cell}; | ||||||
| 
 | 
 | ||||||
| @ -53,6 +54,23 @@ pub struct Account { | |||||||
| 	address_hash: Cell<Option<H256>>, | 	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 { | impl Account { | ||||||
| 	#[cfg(test)] | 	#[cfg(test)] | ||||||
| 	/// General constructor.
 | 	/// General constructor.
 | ||||||
| @ -109,19 +127,8 @@ impl Account { | |||||||
| 
 | 
 | ||||||
| 	/// Create a new account from RLP.
 | 	/// Create a new account from RLP.
 | ||||||
| 	pub fn from_rlp(rlp: &[u8]) -> Account { | 	pub fn from_rlp(rlp: &[u8]) -> Account { | ||||||
| 		let r: Rlp = Rlp::new(rlp); | 		let basic: BasicAccount = ::rlp::decode(rlp); | ||||||
| 		Account { | 		basic.into() | ||||||
| 			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), |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/// Create a new contract account.
 | 	/// 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 \ | 			SecTrieDBMut would not set it to an invalid state root. Therefore the root is valid and DB creation \ | ||||||
| 			using it will not fail.");
 | 			using it will not fail.");
 | ||||||
| 
 | 
 | ||||||
| 		let item: U256 = match db.get(key){ | 		let item: U256 = match db.get_with(key, ::rlp::decode) { | ||||||
| 			Ok(x) => x.map_or_else(U256::zero, |v| decode(&*v)), | 			Ok(x) => x.unwrap_or_else(U256::zero), | ||||||
| 			Err(e) => panic!("Encountered potential DB corruption: {}", e), | 			Err(e) => panic!("Encountered potential DB corruption: {}", e), | ||||||
| 		}; | 		}; | ||||||
| 		let value: H256 = item.into(); | 		let value: H256 = item.into(); | ||||||
| @ -446,12 +453,12 @@ impl Account { | |||||||
| 	/// omitted.
 | 	/// omitted.
 | ||||||
| 	pub fn prove_storage(&self, db: &HashDB, storage_key: H256, from_level: u32) -> Result<Vec<Bytes>, Box<TrieError>> { | 	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::{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 = 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()) | 		Ok(recorder.drain().into_iter().map(|r| r.data).collect()) | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -32,7 +32,7 @@ use state_db::StateDB; | |||||||
| 
 | 
 | ||||||
| use util::*; | use util::*; | ||||||
| 
 | 
 | ||||||
| use util::trie::recorder::{Recorder, BasicRecorder as TrieRecorder}; | use util::trie::recorder::Recorder; | ||||||
| 
 | 
 | ||||||
| mod account; | mod account; | ||||||
| mod substate; | mod substate; | ||||||
| @ -425,8 +425,8 @@ impl State { | |||||||
| 
 | 
 | ||||||
| 		// account is not found in the global cache, get from the DB and insert into local
 | 		// 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 db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); | ||||||
| 		let maybe_acc = match db.get(address) { | 		let maybe_acc = match db.get_with(address, Account::from_rlp) { | ||||||
| 			Ok(acc) => acc.map(|v| Account::from_rlp(&v)), | 			Ok(acc) => acc, | ||||||
| 			Err(e) => panic!("Potential DB corruption encountered: {}", e), | 			Err(e) => panic!("Potential DB corruption encountered: {}", e), | ||||||
| 		}; | 		}; | ||||||
| 		let r = maybe_acc.as_ref().map_or(H256::new(), |a| { | 		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
 | 				// not found in the global cache, get from the DB and insert into local
 | ||||||
| 				let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); | 				let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); | ||||||
| 				let mut maybe_acc = match db.get(a) { | 				let mut maybe_acc = match db.get_with(a, Account::from_rlp) { | ||||||
| 					Ok(acc) => acc.map(|v| Account::from_rlp(&v)), | 					Ok(acc) => acc, | ||||||
| 					Err(e) => panic!("Potential DB corruption encountered: {}", e), | 					Err(e) => panic!("Potential DB corruption encountered: {}", e), | ||||||
| 				}; | 				}; | ||||||
| 				if let Some(ref mut account) = maybe_acc.as_mut() { | 				if let Some(ref mut account) = maybe_acc.as_mut() { | ||||||
| @ -722,9 +722,8 @@ impl State { | |||||||
| 				None => { | 				None => { | ||||||
| 					let maybe_acc = if self.db.check_non_null_bloom(a) { | 					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); | 						let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); | ||||||
| 						match db.get(a) { | 						match db.get_with(a, Account::from_rlp) { | ||||||
| 							Ok(Some(acc)) => AccountEntry::new_clean(Some(Account::from_rlp(&acc))), | 							Ok(acc) => AccountEntry::new_clean(acc), | ||||||
| 							Ok(None) => AccountEntry::new_clean(None), |  | ||||||
| 							Err(e) => panic!("Potential DB corruption encountered: {}", e), | 							Err(e) => panic!("Potential DB corruption encountered: {}", e), | ||||||
| 						} | 						} | ||||||
| 					} else { | 					} else { | ||||||
| @ -770,9 +769,9 @@ impl State { | |||||||
| 	/// Requires a secure trie to be used for accurate results.
 | 	/// Requires a secure trie to be used for accurate results.
 | ||||||
| 	/// `account_key` == sha3(address)
 | 	/// `account_key` == sha3(address)
 | ||||||
| 	pub fn prove_account(&self, account_key: H256, from_level: u32) -> Result<Vec<Bytes>, Box<TrieError>> { | 	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 = 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()) | 		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
 | 		// TODO: probably could look into cache somehow but it's keyed by
 | ||||||
| 		// address, not sha3(address).
 | 		// address, not sha3(address).
 | ||||||
| 		let trie = TrieDB::new(self.db.as_hashdb(), &self.root)?; | 		let trie = TrieDB::new(self.db.as_hashdb(), &self.root)?; | ||||||
| 		let acc = match trie.get(&account_key)? { | 		let acc = match trie.get_with(&account_key, Account::from_rlp)? { | ||||||
| 			Some(rlp) => Account::from_rlp(&rlp), | 			Some(acc) => acc, | ||||||
| 			None => return Ok(Vec::new()), | 			None => return Ok(Vec::new()), | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| @ -799,8 +798,8 @@ impl State { | |||||||
| 	/// Only works when backed by a secure trie.
 | 	/// Only works when backed by a secure trie.
 | ||||||
| 	pub fn code_by_address_hash(&self, account_key: H256) -> Result<Option<Bytes>, Box<TrieError>> { | 	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 trie = TrieDB::new(self.db.as_hashdb(), &self.root)?; | ||||||
| 		let mut acc = match trie.get(&account_key)? { | 		let mut acc = match trie.get_with(&account_key, Account::from_rlp)? { | ||||||
| 			Some(rlp) => Account::from_rlp(&rlp), | 			Some(acc) => acc, | ||||||
| 			None => return Ok(None), | 			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.ready_transactions().len()); | ||||||
| 	assert_eq!(2, client.miner().pending_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, | 	pub transaction_index: usize, | ||||||
| 	/// Log position in the block.
 | 	/// Log position in the block.
 | ||||||
| 	pub log_index: usize, | 	pub log_index: usize, | ||||||
|  | 	/// Log position in the transaction.
 | ||||||
|  | 	pub transaction_log_index: usize, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Deref for LocalizedLogEntry { | impl Deref for LocalizedLogEntry { | ||||||
|  | |||||||
| @ -37,3 +37,4 @@ pub mod mode; | |||||||
| pub mod pruning_info; | pub mod pruning_info; | ||||||
| pub mod security_level; | pub mod security_level; | ||||||
| pub mod encoded; | pub mod encoded; | ||||||
|  | pub mod basic_account; | ||||||
|  | |||||||
| @ -391,6 +391,14 @@ impl Res { | |||||||
| 			Res::Call(_) | Res::FailedCall(_) | Res::FailedCreate(_) | Res::None => Default::default(), | 			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)] | #[derive(Debug, Clone, PartialEq)] | ||||||
| @ -561,4 +569,3 @@ impl Decodable for VMTrace { | |||||||
| 		Ok(res) | 		Ok(res) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 |  | ||||||
|  | |||||||
| @ -16,7 +16,7 @@ | |||||||
| 
 | 
 | ||||||
| //! Transaction data structure.
 | //! Transaction data structure.
 | ||||||
| 
 | 
 | ||||||
| use std::ops::Deref; | use std::ops::{Deref, DerefMut}; | ||||||
| use std::cell::*; | use std::cell::*; | ||||||
| use rlp::*; | use rlp::*; | ||||||
| use util::sha3::Hashable; | 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 { | impl Decodable for SignedTransaction { | ||||||
| 	fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder { | 	fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder { | ||||||
| 		let d = decoder.as_rlp(); | 		let d = decoder.as_rlp(); | ||||||
| @ -373,7 +379,7 @@ impl SignedTransaction { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Signed Transaction that is a part of canon blockchain.
 | /// Signed Transaction that is a part of canon blockchain.
 | ||||||
| #[derive(Debug, PartialEq, Eq)] | #[derive(Debug, Clone, PartialEq, Eq)] | ||||||
| #[cfg_attr(feature = "ipc", binary)] | #[cfg_attr(feature = "ipc", binary)] | ||||||
| pub struct LocalizedTransaction { | pub struct LocalizedTransaction { | ||||||
| 	/// Signed part.
 | 	/// Signed part.
 | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| [package] | [package] | ||||||
| name = "evmjit" | name = "evmjit" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| authors = ["Parity Technologies <admin@parity.io>"] | authors = ["Parity Technologies <admin@parity.io>"] | ||||||
| 
 | 
 | ||||||
| [lib] | [lib] | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ description = "Fetching hash-addressed content." | |||||||
| homepage = "http://parity.io" | homepage = "http://parity.io" | ||||||
| license = "GPL-3.0" | license = "GPL-3.0" | ||||||
| name = "parity-hash-fetch" | name = "parity-hash-fetch" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| authors = ["Parity Technologies <admin@parity.io>"] | authors = ["Parity Technologies <admin@parity.io>"] | ||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| [package] | [package] | ||||||
| description = "Types that implement IPC and are common to multiple modules." | description = "Types that implement IPC and are common to multiple modules." | ||||||
| name = "ipc-common-types" | name = "ipc-common-types" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| license = "GPL-3.0" | license = "GPL-3.0" | ||||||
| authors = ["Parity Technologies <admin@parity.io>"] | authors = ["Parity Technologies <admin@parity.io>"] | ||||||
| build = "build.rs" | build = "build.rs" | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| [package] | [package] | ||||||
| name = "ethcore-ipc-codegen" | name = "ethcore-ipc-codegen" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| authors = ["Parity Technologies <admin@parity.io>"] | authors = ["Parity Technologies <admin@parity.io>"] | ||||||
| license = "GPL-3.0" | license = "GPL-3.0" | ||||||
| description = "Macros to auto-generate implementations for ipc call" | description = "Macros to auto-generate implementations for ipc call" | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| [package] | [package] | ||||||
| name = "ethcore-ipc-nano" | name = "ethcore-ipc-nano" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| authors = ["Parity Technologies <admin@parity.io>"] | authors = ["Parity Technologies <admin@parity.io>"] | ||||||
| license = "GPL-3.0" | license = "GPL-3.0" | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| [package] | [package] | ||||||
| name = "ethcore-ipc" | name = "ethcore-ipc" | ||||||
| version = "1.5.0" | version = "1.6.0" | ||||||
| authors = ["Parity Technologies <admin@parity.io>"] | authors = ["Parity Technologies <admin@parity.io>"] | ||||||
| license = "GPL-3.0" | license = "GPL-3.0" | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "parity.js", |   "name": "parity.js", | ||||||
|   "version": "0.2.152", |   "version": "0.2.182", | ||||||
|   "main": "release/index.js", |   "main": "release/index.js", | ||||||
|   "jsnext:main": "src/index.js", |   "jsnext:main": "src/index.js", | ||||||
|   "author": "Parity Team <admin@parity.io>", |   "author": "Parity Team <admin@parity.io>", | ||||||
| @ -26,9 +26,9 @@ | |||||||
|   ], |   ], | ||||||
|   "scripts": { |   "scripts": { | ||||||
|     "build": "npm run build:lib && npm run build:dll && npm run build:app", |     "build": "npm run build:lib && npm run build:dll && npm run build:app", | ||||||
|     "build:app": "webpack --config webpack/app --progress", |     "build:app": "webpack --config webpack/app", | ||||||
|     "build:lib": "webpack --config webpack/libraries --progress", |     "build:lib": "webpack --config webpack/libraries", | ||||||
|     "build:dll": "webpack --config webpack/vendor --progress", |     "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": "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:app": "NODE_ENV=production webpack --config webpack/app", | ||||||
|     "ci:build:lib": "NODE_ENV=production webpack --config webpack/libraries", |     "ci:build:lib": "NODE_ENV=production webpack --config webpack/libraries", | ||||||
| @ -43,27 +43,27 @@ | |||||||
|     "lint:css": "stylelint ./src/**/*.css", |     "lint:css": "stylelint ./src/**/*.css", | ||||||
|     "lint:js": "eslint --ignore-path .gitignore ./src/", |     "lint:js": "eslint --ignore-path .gitignore ./src/", | ||||||
|     "lint:js:cached": "eslint --cache --ignore-path .gitignore ./src/", |     "lint:js:cached": "eslint --cache --ignore-path .gitignore ./src/", | ||||||
|     "test": "NODE_ENV=test mocha 'src/**/*.spec.js'", |     "test": "NODE_ENV=test mocha --compilers ejs:ejsify 'src/**/*.spec.js'", | ||||||
|     "test:coverage": "NODE_ENV=test istanbul cover _mocha -- '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:e2e": "NODE_ENV=test mocha 'src/**/*.e2e.js'", | ||||||
|     "test:npm": "(cd .npmjs && npm i) && node test/npmParity && (rm -rf .npmjs/node_modules)", |     "test:npm": "(cd .npmjs && npm i) && node test/npmParity && (rm -rf .npmjs/node_modules)", | ||||||
|     "prepush": "npm run lint:cached" |     "prepush": "npm run lint:cached" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "babel-cli": "6.18.0", |     "babel-cli": "6.18.0", | ||||||
|     "babel-core": "6.20.0", |     "babel-core": "6.21.0", | ||||||
|     "babel-eslint": "7.1.1", |     "babel-eslint": "7.1.1", | ||||||
|     "babel-loader": "6.2.10", |     "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-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-decorators-legacy": "1.3.4", | ||||||
|     "babel-plugin-transform-object-rest-spread": "6.20.2", |     "babel-plugin-transform-object-rest-spread": "6.20.2", | ||||||
|     "babel-plugin-transform-react-remove-prop-types": "0.2.11", |     "babel-plugin-transform-react-remove-prop-types": "0.2.11", | ||||||
|     "babel-plugin-transform-runtime": "6.15.0", |     "babel-plugin-transform-runtime": "6.15.0", | ||||||
|     "babel-plugin-webpack-alias": "2.1.2", |     "babel-plugin-webpack-alias": "2.1.2", | ||||||
|     "babel-polyfill": "6.20.0", |     "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-es2015": "6.18.0", | ||||||
|     "babel-preset-es2016": "6.16.0", |     "babel-preset-es2016": "6.16.0", | ||||||
|     "babel-preset-es2017": "6.16.0", |     "babel-preset-es2017": "6.16.0", | ||||||
| @ -80,57 +80,59 @@ | |||||||
|     "coveralls": "2.11.15", |     "coveralls": "2.11.15", | ||||||
|     "css-loader": "0.26.1", |     "css-loader": "0.26.1", | ||||||
|     "ejs-loader": "0.3.0", |     "ejs-loader": "0.3.0", | ||||||
|     "enzyme": "2.6.0", |     "ejsify": "1.0.0", | ||||||
|  |     "enzyme": "2.7.0", | ||||||
|     "eslint": "3.11.1", |     "eslint": "3.11.1", | ||||||
|     "eslint-config-semistandard": "7.0.0", |     "eslint-config-semistandard": "7.0.0", | ||||||
|     "eslint-config-standard": "6.2.1", |     "eslint-config-standard": "6.2.1", | ||||||
|     "eslint-config-standard-react": "4.2.0", |     "eslint-config-standard-react": "4.2.0", | ||||||
|     "eslint-plugin-promise": "3.4.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", |     "eslint-plugin-standard": "2.0.1", | ||||||
|     "express": "4.14.0", |     "express": "4.14.0", | ||||||
|     "extract-loader": "0.1.0", |     "extract-loader": "0.1.0", | ||||||
|     "extract-text-webpack-plugin": "2.0.0-beta.4", |     "extract-text-webpack-plugin": "2.0.0-beta.4", | ||||||
|     "file-loader": "0.9.0", |     "file-loader": "0.9.0", | ||||||
|     "happypack": "3.0.0", |     "happypack": "3.0.2", | ||||||
|     "html-loader": "0.4.4", |     "html-loader": "0.4.4", | ||||||
|     "html-webpack-plugin": "2.24.1", |     "html-webpack-plugin": "2.24.1", | ||||||
|     "http-proxy-middleware": "0.17.2", |     "http-proxy-middleware": "0.17.3", | ||||||
|     "husky": "0.11.9", |     "husky": "0.11.9", | ||||||
|     "ignore-styles": "5.0.1", |     "ignore-styles": "5.0.1", | ||||||
|     "image-webpack-loader": "3.0.0", |     "image-webpack-loader": "3.1.0", | ||||||
|     "istanbul": "1.0.0-alpha.2", |     "istanbul": "1.0.0-alpha.2", | ||||||
|     "jsdom": "9.8.3", |     "jsdom": "9.9.1", | ||||||
|     "json-loader": "0.5.4", |     "json-loader": "0.5.4", | ||||||
|     "mocha": "3.2.0", |     "mocha": "3.2.0", | ||||||
|     "mock-local-storage": "1.0.2", |     "mock-local-storage": "1.0.2", | ||||||
|     "mock-socket": "6.0.3", |     "mock-socket": "6.0.4", | ||||||
|     "nock": "9.0.2", |     "nock": "9.0.2", | ||||||
|     "postcss-import": "9.0.0", |     "postcss-import": "9.0.0", | ||||||
|     "postcss-loader": "1.2.0", |     "postcss-loader": "1.2.1", | ||||||
|     "postcss-nested": "1.0.0", |     "postcss-nested": "1.0.0", | ||||||
|     "postcss-simple-vars": "3.0.0", |     "postcss-simple-vars": "3.0.0", | ||||||
|     "progress": "1.1.8", |     "progress": "1.1.8", | ||||||
|  |     "progress-bar-webpack-plugin": "1.9.1", | ||||||
|     "raw-loader": "0.5.1", |     "raw-loader": "0.5.1", | ||||||
|     "react-addons-perf": "15.4.1", |     "react-addons-perf": "15.4.1", | ||||||
|     "react-addons-test-utils": "15.4.1", |     "react-addons-test-utils": "15.4.1", | ||||||
|     "react-hot-loader": "3.0.0-beta.6", |     "react-hot-loader": "3.0.0-beta.6", | ||||||
|     "react-intl-aggregate-webpack-plugin": "0.0.1", |     "react-intl-aggregate-webpack-plugin": "0.0.1", | ||||||
|     "rucksack-css": "0.9.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", |     "serviceworker-webpack-plugin": "0.1.7", | ||||||
|     "sinon": "1.17.6", |     "sinon": "1.17.6", | ||||||
|     "sinon-as-promised": "4.0.2", |     "sinon-as-promised": "4.0.2", | ||||||
|     "sinon-chai": "2.8.0", |     "sinon-chai": "2.8.0", | ||||||
|     "style-loader": "0.13.1", |     "style-loader": "0.13.1", | ||||||
|     "stylelint": "7.6.0", |     "stylelint": "7.7.0", | ||||||
|     "stylelint-config-standard": "15.0.0", |     "stylelint-config-standard": "15.0.1", | ||||||
|     "url-loader": "0.5.7", |     "url-loader": "0.5.7", | ||||||
|     "webpack": "2.2.0-rc.2", |     "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-error-notification": "0.1.6", | ||||||
|     "webpack-hot-middleware": "2.13.2", |     "webpack-hot-middleware": "2.14.0", | ||||||
|     "websocket": "1.0.23" |     "websocket": "1.0.24" | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "bignumber.js": "3.0.1", |     "bignumber.js": "3.0.1", | ||||||
| @ -150,6 +152,7 @@ | |||||||
|     "isomorphic-fetch": "2.2.1", |     "isomorphic-fetch": "2.2.1", | ||||||
|     "js-sha3": "0.5.5", |     "js-sha3": "0.5.5", | ||||||
|     "lodash": "4.17.2", |     "lodash": "4.17.2", | ||||||
|  |     "loglevel": "1.4.1", | ||||||
|     "marked": "0.3.6", |     "marked": "0.3.6", | ||||||
|     "material-ui": "0.16.5", |     "material-ui": "0.16.5", | ||||||
|     "material-ui-chip-input": "0.11.1", |     "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
 |   // page offset from 0
 | ||||||
|   return _call('txlist', { |   return _call('txlist', { | ||||||
|     address: address, |     address: address, | ||||||
|     page: (page || 0) + 1, |  | ||||||
|     offset: PAGE_SIZE, |     offset: PAGE_SIZE, | ||||||
|  |     page: (page || 0) + 1, | ||||||
|     sort: 'desc' |     sort: 'desc' | ||||||
|   }, test).then((transactions) => { |   }, test).then((transactions) => { | ||||||
|     return transactions.map((tx) => { |     return transactions.map((tx) => { | ||||||
|       return { |       return { | ||||||
|  |         blockNumber: new BigNumber(tx.blockNumber || 0), | ||||||
|         from: util.toChecksumAddress(tx.from), |         from: util.toChecksumAddress(tx.from), | ||||||
|         to: util.toChecksumAddress(tx.to), |  | ||||||
|         hash: tx.hash, |         hash: tx.hash, | ||||||
|         blockNumber: new BigNumber(tx.blockNumber), |  | ||||||
|         timeStamp: tx.timeStamp, |         timeStamp: tx.timeStamp, | ||||||
|  |         to: util.toChecksumAddress(tx.to), | ||||||
|         value: tx.value |         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 nock = require('nock'); | ||||||
| 
 | 
 | ||||||
| const ShapeShift = require('./'); |  | ||||||
| const initShapeshift = (ShapeShift.default || ShapeShift); |  | ||||||
| 
 |  | ||||||
| const APIKEY = '0x123454321'; | const APIKEY = '0x123454321'; | ||||||
| 
 | 
 | ||||||
| const shapeshift = initShapeshift(APIKEY); | function mockget (shapeshift, requests) { | ||||||
| const rpc = shapeshift.getRpc(); |   let scope = nock(shapeshift.getRpc().ENDPOINT); | ||||||
| 
 |  | ||||||
| function mockget (requests) { |  | ||||||
|   let scope = nock(rpc.ENDPOINT); |  | ||||||
| 
 | 
 | ||||||
|   requests.forEach((request) => { |   requests.forEach((request) => { | ||||||
|     scope = scope |     scope = scope | ||||||
| @ -38,8 +32,8 @@ function mockget (requests) { | |||||||
|   return scope; |   return scope; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function mockpost (requests) { | function mockpost (shapeshift, requests) { | ||||||
|   let scope = nock(rpc.ENDPOINT); |   let scope = nock(shapeshift.getRpc().ENDPOINT); | ||||||
| 
 | 
 | ||||||
|   requests.forEach((request) => { |   requests.forEach((request) => { | ||||||
|     scope = scope |     scope = scope | ||||||
| @ -58,7 +52,5 @@ function mockpost (requests) { | |||||||
| module.exports = { | module.exports = { | ||||||
|   APIKEY, |   APIKEY, | ||||||
|   mockget, |   mockget, | ||||||
|   mockpost, |   mockpost | ||||||
|   shapeshift, |  | ||||||
|   rpc |  | ||||||
| }; | }; | ||||||
|  | |||||||
							
								
								
									
										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 helpers = require('./helpers.spec.js'); | ||||||
| 
 | 
 | ||||||
| const APIKEY = helpers.APIKEY; | const ShapeShift = require('./'); | ||||||
|  | const initShapeshift = (ShapeShift.default || ShapeShift); | ||||||
|  | 
 | ||||||
| const mockget = helpers.mockget; | const mockget = helpers.mockget; | ||||||
| const mockpost = helpers.mockpost; | const mockpost = helpers.mockpost; | ||||||
| const rpc = helpers.rpc; |  | ||||||
| 
 | 
 | ||||||
| describe('shapeshift/rpc', () => { | describe('shapeshift/rpc', () => { | ||||||
|  |   let rpc; | ||||||
|  |   let shapeshift; | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => { | ||||||
|  |     shapeshift = initShapeshift(helpers.APIKEY); | ||||||
|  |     rpc = shapeshift.getRpc(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|   describe('GET', () => { |   describe('GET', () => { | ||||||
|     const REPLY = { test: 'this is some result' }; |     const REPLY = { test: 'this is some result' }; | ||||||
| 
 | 
 | ||||||
| @ -29,7 +38,7 @@ describe('shapeshift/rpc', () => { | |||||||
|     let result; |     let result; | ||||||
| 
 | 
 | ||||||
|     beforeEach(() => { |     beforeEach(() => { | ||||||
|       scope = mockget([{ path: 'test', reply: REPLY }]); |       scope = mockget(shapeshift, [{ path: 'test', reply: REPLY }]); | ||||||
| 
 | 
 | ||||||
|       return rpc |       return rpc | ||||||
|         .get('test') |         .get('test') | ||||||
| @ -54,7 +63,7 @@ describe('shapeshift/rpc', () => { | |||||||
|     let result; |     let result; | ||||||
| 
 | 
 | ||||||
|     beforeEach(() => { |     beforeEach(() => { | ||||||
|       scope = mockpost([{ path: 'test', reply: REPLY }]); |       scope = mockpost(shapeshift, [{ path: 'test', reply: REPLY }]); | ||||||
| 
 | 
 | ||||||
|       return rpc |       return rpc | ||||||
|         .post('test', { input: 'stuff' }) |         .post('test', { input: 'stuff' }) | ||||||
| @ -76,7 +85,7 @@ describe('shapeshift/rpc', () => { | |||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     it('passes the apikey specified', () => { |     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/>.
 | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
| 
 | 
 | ||||||
| export default function (rpc) { | export default function (rpc) { | ||||||
|   let subscriptions = []; |   let _subscriptions = []; | ||||||
|   let pollStatusIntervalId = null; |   let _pollStatusIntervalId = null; | ||||||
|  |   let _subscriptionPromises = null; | ||||||
| 
 | 
 | ||||||
|   function getCoins () { |   function getCoins () { | ||||||
|     return rpc.get('getcoins'); |     return rpc.get('getcoins'); | ||||||
| @ -36,75 +37,93 @@ export default function (rpc) { | |||||||
| 
 | 
 | ||||||
|   function shift (toAddress, returnAddress, pair) { |   function shift (toAddress, returnAddress, pair) { | ||||||
|     return rpc.post('shift', { |     return rpc.post('shift', { | ||||||
|       withdrawal: toAddress, |       pair, | ||||||
|       pair: pair, |       returnAddress, | ||||||
|       returnAddress: returnAddress |       withdrawal: toAddress | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function subscribe (depositAddress, callback) { |   function subscribe (depositAddress, callback) { | ||||||
|     const idx = subscriptions.length; |     if (!depositAddress || !callback) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     subscriptions.push({ |     const index = _subscriptions.length; | ||||||
|       depositAddress, | 
 | ||||||
|  |     _subscriptions.push({ | ||||||
|       callback, |       callback, | ||||||
|       idx |       depositAddress, | ||||||
|  |       index | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     // Only poll if there are subscriptions...
 |     if (_pollStatusIntervalId === null) { | ||||||
|     if (!pollStatusIntervalId) { |       _pollStatusIntervalId = setInterval(_pollStatus, 2000); | ||||||
|       pollStatusIntervalId = setInterval(_pollStatus, 2000); |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function unsubscribe (depositAddress) { |   function unsubscribe (depositAddress) { | ||||||
|     const newSubscriptions = [] |     _subscriptions = _subscriptions.filter((sub) => sub.depositAddress !== depositAddress); | ||||||
|       .concat(subscriptions) |  | ||||||
|       .filter((sub) => sub.depositAddress !== depositAddress); |  | ||||||
| 
 | 
 | ||||||
|     subscriptions = newSubscriptions; |     if (_subscriptions.length === 0) { | ||||||
| 
 |       clearInterval(_pollStatusIntervalId); | ||||||
|     if (subscriptions.length === 0) { |       _pollStatusIntervalId = null; | ||||||
|       clearInterval(pollStatusIntervalId); |  | ||||||
|       pollStatusIntervalId = null; |  | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function _getSubscriptionStatus (subscription) { |   function _getSubscriptionStatus (subscription) { | ||||||
|     if (!subscription) { |     if (!subscription) { | ||||||
|       return; |       return Promise.resolve(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     getStatus(subscription.depositAddress) |     return getStatus(subscription.depositAddress) | ||||||
|       .then((result) => { |       .then((result) => { | ||||||
|         switch (result.status) { |         switch (result.status) { | ||||||
|           case 'no_deposits': |           case 'no_deposits': | ||||||
|           case 'received': |           case 'received': | ||||||
|             subscription.callback(null, result); |             subscription.callback(null, result); | ||||||
|             return; |             return true; | ||||||
| 
 | 
 | ||||||
|           case 'complete': |           case 'complete': | ||||||
|             subscription.callback(null, result); |             subscription.callback(null, result); | ||||||
|             subscriptions[subscription.idx] = null; |             unsubscribe(subscription.depositAddress); | ||||||
|             return; |             return true; | ||||||
| 
 | 
 | ||||||
|           case 'failed': |           case 'failed': | ||||||
|             subscription.callback({ |             subscription.callback({ | ||||||
|               message: status.error, |               message: status.error, | ||||||
|               fatal: true |               fatal: true | ||||||
|             }); |             }); | ||||||
|             subscriptions[subscription.idx] = null; |             unsubscribe(subscription.depositAddress); | ||||||
|             return; |             return true; | ||||||
|         } |         } | ||||||
|       }) |       }) | ||||||
|       .catch(subscription.callback); |       .catch(() => { | ||||||
|  |         return true; | ||||||
|  |       }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function _pollStatus () { |   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 { |   return { | ||||||
|  |     _getSubscriptions, | ||||||
|  |     _getSubscriptionPromises, | ||||||
|  |     _isPolling, | ||||||
|     getCoins, |     getCoins, | ||||||
|     getMarketInfo, |     getMarketInfo, | ||||||
|     getRpc, |     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
 | // You should have received a copy of the GNU General Public License
 | ||||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
| 
 | 
 | ||||||
|  | const sinon = require('sinon'); | ||||||
|  | 
 | ||||||
|  | const ShapeShift = require('./'); | ||||||
|  | const initShapeshift = (ShapeShift.default || ShapeShift); | ||||||
|  | 
 | ||||||
| const helpers = require('./helpers.spec.js'); | const helpers = require('./helpers.spec.js'); | ||||||
| 
 | 
 | ||||||
| const mockget = helpers.mockget; | const mockget = helpers.mockget; | ||||||
| const mockpost = helpers.mockpost; | const mockpost = helpers.mockpost; | ||||||
| const shapeshift = helpers.shapeshift; |  | ||||||
| 
 | 
 | ||||||
| describe('shapeshift/calls', () => { | describe('shapeshift/calls', () => { | ||||||
|  |   let clock; | ||||||
|  |   let shapeshift; | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => { | ||||||
|  |     clock = sinon.useFakeTimers(); | ||||||
|  |     shapeshift = initShapeshift(helpers.APIKEY); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   afterEach(() => { | ||||||
|  |     clock.restore(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|   describe('getCoins', () => { |   describe('getCoins', () => { | ||||||
|     const REPLY = { |     const REPLY = { | ||||||
|       BTC: { |       BTC: { | ||||||
| @ -39,8 +55,8 @@ describe('shapeshift/calls', () => { | |||||||
| 
 | 
 | ||||||
|     let scope; |     let scope; | ||||||
| 
 | 
 | ||||||
|     before(() => { |     beforeEach(() => { | ||||||
|       scope = mockget([{ path: 'getcoins', reply: REPLY }]); |       scope = mockget(shapeshift, [{ path: 'getcoins', reply: REPLY }]); | ||||||
| 
 | 
 | ||||||
|       return shapeshift.getCoins(); |       return shapeshift.getCoins(); | ||||||
|     }); |     }); | ||||||
| @ -61,8 +77,8 @@ describe('shapeshift/calls', () => { | |||||||
| 
 | 
 | ||||||
|     let scope; |     let scope; | ||||||
| 
 | 
 | ||||||
|     before(() => { |     beforeEach(() => { | ||||||
|       scope = mockget([{ path: 'marketinfo/btc_ltc', reply: REPLY }]); |       scope = mockget(shapeshift, [{ path: 'marketinfo/btc_ltc', reply: REPLY }]); | ||||||
| 
 | 
 | ||||||
|       return shapeshift.getMarketInfo('btc_ltc'); |       return shapeshift.getMarketInfo('btc_ltc'); | ||||||
|     }); |     }); | ||||||
| @ -80,8 +96,8 @@ describe('shapeshift/calls', () => { | |||||||
| 
 | 
 | ||||||
|     let scope; |     let scope; | ||||||
| 
 | 
 | ||||||
|     before(() => { |     beforeEach(() => { | ||||||
|       scope = mockget([{ path: 'txStat/0x123', reply: REPLY }]); |       scope = mockget(shapeshift, [{ path: 'txStat/0x123', reply: REPLY }]); | ||||||
| 
 | 
 | ||||||
|       return shapeshift.getStatus('0x123'); |       return shapeshift.getStatus('0x123'); | ||||||
|     }); |     }); | ||||||
| @ -101,8 +117,8 @@ describe('shapeshift/calls', () => { | |||||||
| 
 | 
 | ||||||
|     let scope; |     let scope; | ||||||
| 
 | 
 | ||||||
|     before(() => { |     beforeEach(() => { | ||||||
|       scope = mockpost([{ path: 'shift', reply: REPLY }]); |       scope = mockpost(shapeshift, [{ path: 'shift', reply: REPLY }]); | ||||||
| 
 | 
 | ||||||
|       return shapeshift.shift('0x456', '1BTC', 'btc_eth'); |       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'); |       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 |     const inits = mediates | ||||||
|       .map((mediate, idx) => mediate.init(Mediate.offsetFor(mediates, idx))) |       .map((mediate, idx) => mediate.init(Mediate.offsetFor(mediates, idx))) | ||||||
|       .join(''); |       .join(''); | ||||||
| @ -36,11 +36,12 @@ export default class Encoder { | |||||||
|     return `${inits}${closings}`; |     return `${inits}${closings}`; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   static encodeToken (token) { |   static encodeToken (token, index = 0) { | ||||||
|     if (!isInstanceOf(token, Token)) { |     if (!isInstanceOf(token, Token)) { | ||||||
|       throw new Error('token should be instanceof Token'); |       throw new Error('token should be instanceof Token'); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     try { | ||||||
|       switch (token.type) { |       switch (token.type) { | ||||||
|         case 'address': |         case 'address': | ||||||
|           return new Mediate('raw', padAddress(token.value)); |           return new Mediate('raw', padAddress(token.value)); | ||||||
| @ -64,9 +65,11 @@ export default class Encoder { | |||||||
|         case 'fixedArray': |         case 'fixedArray': | ||||||
|         case 'array': |         case 'array': | ||||||
|           return new Mediate(token.type, token.value.map((token) => Encoder.encodeToken(token))); |           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}`); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|       default: |  | ||||||
|     throw new Error(`Invalid token type ${token.type} in encodeToken`); |     throw new Error(`Invalid token type ${token.type} in encodeToken`); | ||||||
|   } |   } | ||||||
|   } |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -41,6 +41,10 @@ export default class Interface { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   encodeTokens (paramTypes, values) { |   encodeTokens (paramTypes, values) { | ||||||
|  |     return Interface.encodeTokens(paramTypes, values); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static encodeTokens (paramTypes, values) { | ||||||
|     const createToken = function (paramType, value) { |     const createToken = function (paramType, value) { | ||||||
|       if (paramType.subtype) { |       if (paramType.subtype) { | ||||||
|         return new Token(paramType.type, value.map((entry) => createToken(paramType.subtype, entry))); |         return new Token(paramType.type, value.map((entry) => createToken(paramType.subtype, entry))); | ||||||
|  | |||||||
| @ -31,6 +31,12 @@ export default class Param { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   static toParams (params) { |   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].name).to.equal('foo'); | ||||||
|       expect(params[0].kind.type).to.equal('uint'); |       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) => { |           .catch((error) => { | ||||||
|  |             // Don't print if the request is rejected: that's ok
 | ||||||
|  |             if (error.type !== 'REQUEST_REJECTED') { | ||||||
|               console.error('pollMethod', error); |               console.error('pollMethod', error); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             reject(error); |             reject(error); | ||||||
|           }); |           }); | ||||||
|       }; |       }; | ||||||
|  | |||||||
| @ -14,7 +14,7 @@ | |||||||
| // You should have received a copy of the GNU General Public License
 | // You should have received a copy of the GNU General Public License
 | ||||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
| 
 | 
 | ||||||
| import Abi from '../../abi'; | import Abi from '~/abi'; | ||||||
| 
 | 
 | ||||||
| let nextSubscriptionId = 0; | let nextSubscriptionId = 0; | ||||||
| 
 | 
 | ||||||
| @ -53,6 +53,10 @@ export default class Contract { | |||||||
| 
 | 
 | ||||||
|     this._subscribedToBlock = false; |     this._subscribedToBlock = false; | ||||||
|     this._blockSubscriptionId = null; |     this._blockSubscriptionId = null; | ||||||
|  | 
 | ||||||
|  |     if (api && api.patch && api.patch.contract) { | ||||||
|  |       api.patch.contract(this); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   get address () { |   get address () { | ||||||
| @ -71,6 +75,10 @@ export default class Contract { | |||||||
|     return this._functions; |     return this._functions; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   get receipt () { | ||||||
|  |     return this._receipt; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   get instance () { |   get instance () { | ||||||
|     this._instance.address = this._address; |     this._instance.address = this._address; | ||||||
|     return this._instance; |     return this._instance; | ||||||
| @ -90,8 +98,10 @@ export default class Contract { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   deployEstimateGas (options, values) { |   deployEstimateGas (options, values) { | ||||||
|  |     const _options = this._encodeOptions(this.constructors[0], options, values); | ||||||
|  | 
 | ||||||
|     return this._api.eth |     return this._api.eth | ||||||
|       .estimateGas(this._encodeOptions(this.constructors[0], options, values)) |       .estimateGas(_options) | ||||||
|       .then((gasEst) => { |       .then((gasEst) => { | ||||||
|         return [gasEst, gasEst.mul(1.2)]; |         return [gasEst, gasEst.mul(1.2)]; | ||||||
|       }); |       }); | ||||||
| @ -115,8 +125,10 @@ export default class Contract { | |||||||
| 
 | 
 | ||||||
|         setState({ state: 'postTransaction', gas }); |         setState({ state: 'postTransaction', gas }); | ||||||
| 
 | 
 | ||||||
|  |         const _options = this._encodeOptions(this.constructors[0], options, values); | ||||||
|  | 
 | ||||||
|         return this._api.parity |         return this._api.parity | ||||||
|           .postTransaction(this._encodeOptions(this.constructors[0], options, values)) |           .postTransaction(_options) | ||||||
|           .then((requestId) => { |           .then((requestId) => { | ||||||
|             setState({ state: 'checkRequest', requestId }); |             setState({ state: 'checkRequest', requestId }); | ||||||
|             return this._pollCheckRequest(requestId); |             return this._pollCheckRequest(requestId); | ||||||
| @ -131,6 +143,7 @@ export default class Contract { | |||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             setState({ state: 'hasReceipt', receipt }); |             setState({ state: 'hasReceipt', receipt }); | ||||||
|  |             this._receipt = receipt; | ||||||
|             this._address = receipt.contractAddress; |             this._address = receipt.contractAddress; | ||||||
|             return this._address; |             return this._address; | ||||||
|           }); |           }); | ||||||
| @ -199,7 +212,7 @@ export default class Contract { | |||||||
|   getCallData = (func, options, values) => { |   getCallData = (func, options, values) => { | ||||||
|     let data = options.data; |     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; |     const call = tokens ? func.encodeCall(tokens) : null; | ||||||
| 
 | 
 | ||||||
|     if (data && data.substr(0, 2) === '0x') { |     if (data && data.substr(0, 2) === '0x') { | ||||||
| @ -210,17 +223,24 @@ export default class Contract { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   _encodeOptions (func, options, values) { |   _encodeOptions (func, options, values) { | ||||||
|     options.data = this.getCallData(func, options, values); |     const data = this.getCallData(func, options, values); | ||||||
|     return options; | 
 | ||||||
|  |     return { | ||||||
|  |       ...options, | ||||||
|  |       data | ||||||
|  |     }; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   _addOptionsTo (options = {}) { |   _addOptionsTo (options = {}) { | ||||||
|     return Object.assign({ |     return { | ||||||
|       to: this._address |       to: this._address, | ||||||
|     }, options); |       ...options | ||||||
|  |     }; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   _bindFunction = (func) => { |   _bindFunction = (func) => { | ||||||
|  |     func.contract = this; | ||||||
|  | 
 | ||||||
|     func.call = (options, values = []) => { |     func.call = (options, values = []) => { | ||||||
|       const callParams = this._encodeOptions(func, this._addOptionsTo(options), values); |       const callParams = this._encodeOptions(func, this._addOptionsTo(options), values); | ||||||
| 
 | 
 | ||||||
| @ -233,13 +253,13 @@ export default class Contract { | |||||||
| 
 | 
 | ||||||
|     if (!func.constant) { |     if (!func.constant) { | ||||||
|       func.postTransaction = (options, values = []) => { |       func.postTransaction = (options, values = []) => { | ||||||
|         return this._api.parity |         const _options = this._encodeOptions(func, this._addOptionsTo(options), values); | ||||||
|           .postTransaction(this._encodeOptions(func, this._addOptionsTo(options), values)); |         return this._api.parity.postTransaction(_options); | ||||||
|       }; |       }; | ||||||
| 
 | 
 | ||||||
|       func.estimateGas = (options, values = []) => { |       func.estimateGas = (options, values = []) => { | ||||||
|         return this._api.eth |         const _options = this._encodeOptions(func, this._addOptionsTo(options), values); | ||||||
|           .estimateGas(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', () => { | describe('api/contract/Contract', () => { | ||||||
|   const ADDR = '0x0123456789'; |   const ADDR = '0x0123456789'; | ||||||
|  | 
 | ||||||
|   const ABI = [ |   const ABI = [ | ||||||
|     { |     { | ||||||
|       type: 'function', name: 'test', |       type: 'function', name: 'test', | ||||||
| @ -41,12 +42,42 @@ describe('api/contract/Contract', () => { | |||||||
|       type: 'function', name: 'test2', |       type: 'function', name: 'test2', | ||||||
|       outputs: [{ type: 'uint' }, { type: 'uint' }] |       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: 'baz' }, | ||||||
|     { type: 'event', name: 'foo' } |     { 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 RETURN1 = '0000000000000000000000000000000000000000000000000000000000123456'; | ||||||
|   const RETURN2 = '0000000000000000000000000000000000000000000000000000000000456789'; |   const RETURN2 = '0000000000000000000000000000000000000000000000000000000000456789'; | ||||||
|   let scope; |   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', () => { |   describe('deploy', () => { | ||||||
|     const contract = new Contract(eth, ABI); |     const contract = new Contract(eth, ABI); | ||||||
|     const ADDRESS = '0xD337e80eEdBdf86eDBba021797d7e4e00Bb78351'; |     const ADDRESS = '0xD337e80eEdBdf86eDBba021797d7e4e00Bb78351'; | ||||||
| @ -252,7 +310,7 @@ describe('api/contract/Contract', () => { | |||||||
|           { method: 'eth_getCode', reply: { result: '0x456' } } |           { 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', () => { |       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)', () => { |       it('passes the options through to postTransaction (incl. gas calculation)', () => { | ||||||
|         expect(scope.body.parity_postTransaction.params).to.deep.equal([ |         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 |         return contract | ||||||
|           .deploy({ data: '0x123' }, []) |           .deploy({ data: '0x123' }, VALUES) | ||||||
|           .catch((error) => { |           .catch((error) => { | ||||||
|             expect(error.message).to.match(/not deployed, gasUsed/); |             expect(error.message).to.match(/not deployed, gasUsed/); | ||||||
|           }); |           }); | ||||||
| @ -296,7 +354,7 @@ describe('api/contract/Contract', () => { | |||||||
|         ]); |         ]); | ||||||
| 
 | 
 | ||||||
|         return contract |         return contract | ||||||
|           .deploy({ data: '0x123' }, []) |           .deploy({ data: '0x123' }, VALUES) | ||||||
|           .catch((error) => { |           .catch((error) => { | ||||||
|             expect(error.message).to.match(/not deployed, getCode/); |             expect(error.message).to.match(/not deployed, getCode/); | ||||||
|           }); |           }); | ||||||
|  | |||||||
| @ -16,7 +16,6 @@ | |||||||
| 
 | 
 | ||||||
| import BigNumber from 'bignumber.js'; | import BigNumber from 'bignumber.js'; | ||||||
| import sinon from 'sinon'; | import sinon from 'sinon'; | ||||||
| import 'sinon-as-promised'; |  | ||||||
| 
 | 
 | ||||||
| import Eth from './eth'; | import Eth from './eth'; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -15,7 +15,6 @@ | |||||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
| 
 | 
 | ||||||
| import sinon from 'sinon'; | import sinon from 'sinon'; | ||||||
| import 'sinon-as-promised'; |  | ||||||
| 
 | 
 | ||||||
| import Personal from './personal'; | import Personal from './personal'; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -209,7 +209,10 @@ export default class Ws extends JsonRpcBase { | |||||||
|       if (result.error) { |       if (result.error) { | ||||||
|         this.error(event.data); |         this.error(event.data); | ||||||
| 
 | 
 | ||||||
|  |         // 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}`); |           console.error(`${method}(${JSON.stringify(params)}): ${result.error.code}: ${result.error.message}`); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         const error = new TransportError(method, result.error.code, result.error.message); |         const error = new TransportError(method, result.error.code, result.error.message); | ||||||
|         reject(error); |         reject(error); | ||||||
|  | |||||||
| @ -26,7 +26,9 @@ export function decodeCallData (data) { | |||||||
| 
 | 
 | ||||||
|   if (data.substr(0, 2) === '0x') { |   if (data.substr(0, 2) === '0x') { | ||||||
|     return decodeCallData(data.slice(2)); |     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'); |     throw new Error('Input to decodeCallData should be method signature + data'); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -42,13 +44,15 @@ export function decodeCallData (data) { | |||||||
| export function decodeMethodInput (methodAbi, paramdata) { | export function decodeMethodInput (methodAbi, paramdata) { | ||||||
|   if (!methodAbi) { |   if (!methodAbi) { | ||||||
|     throw new Error('decodeMethodInput should receive valid method-specific ABI'); |     throw new Error('decodeMethodInput should receive valid method-specific ABI'); | ||||||
|   } else if (paramdata && paramdata.length) { |   } | ||||||
|  | 
 | ||||||
|  |   if (paramdata && paramdata.length) { | ||||||
|     if (!isHex(paramdata)) { |     if (!isHex(paramdata)) { | ||||||
|       throw new Error('Input to decodeMethodInput should be a hex value'); |       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)); |       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/); |       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', () => { |     it('correctly decodes valid inputs', () => { | ||||||
|       expect(decodeMethodInput({ |       expect(decodeMethodInput({ | ||||||
|         type: 'function', |         type: 'function', | ||||||
|  | |||||||
| @ -20,15 +20,21 @@ export function bytesToHex (bytes) { | |||||||
|   return '0x' + bytes.map((b) => ('0' + b.toString(16)).slice(-2)).join(''); |   return '0x' + bytes.map((b) => ('0' + b.toString(16)).slice(-2)).join(''); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function hex2Ascii (_hex) { | export function hexToBytes (hex) { | ||||||
|   const hex = /^(?:0x)?(.*)$/.exec(_hex.toString())[1]; |   const raw = toHex(hex).slice(2); | ||||||
|  |   const bytes = []; | ||||||
| 
 | 
 | ||||||
|   let str = ''; |   for (let i = 0; i < raw.length; i += 2) { | ||||||
| 
 |     bytes.push(parseInt(raw.substr(i, 2), 16)); | ||||||
|   for (let i = 0; i < hex.length; i += 2) { |  | ||||||
|     str += String.fromCharCode(parseInt(hex.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; |   return str; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -14,7 +14,7 @@ | |||||||
| // You should have received a copy of the GNU General Public License
 | // You should have received a copy of the GNU General Public License
 | ||||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
| 
 | 
 | ||||||
| import { bytesToHex } from './format'; | import { bytesToHex, hexToBytes, hexToAscii, bytesToAscii, asciiToHex } from './format'; | ||||||
| 
 | 
 | ||||||
| describe('api/util/format', () => { | describe('api/util/format', () => { | ||||||
|   describe('bytesToHex', () => { |   describe('bytesToHex', () => { | ||||||
| @ -26,4 +26,46 @@ describe('api/util/format', () => { | |||||||
|       expect(bytesToHex([0, 15, 16])).to.equal('0x000f10'); |       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) { | export function createIdentityImg (address, scale = 8) { | ||||||
|   return TEST_ENV |   return TEST_ENV | ||||||
|     ? '' |     ? 'test-createIdentityImg' | ||||||
|     : blockies({ |     : blockies({ | ||||||
|       seed: (address || '').toLowerCase(), |       seed: (address || '').toLowerCase(), | ||||||
|       size: 8, |       size: 8, | ||||||
|  | |||||||
| @ -16,7 +16,7 @@ | |||||||
| 
 | 
 | ||||||
| import { isAddress as isAddressValid, toChecksumAddress } from '../../abi/util/address'; | import { isAddress as isAddressValid, toChecksumAddress } from '../../abi/util/address'; | ||||||
| import { decodeCallData, decodeMethodInput, methodToAbi } from './decode'; | import { decodeCallData, decodeMethodInput, methodToAbi } from './decode'; | ||||||
| import { bytesToHex, hex2Ascii, asciiToHex } from './format'; | import { bytesToHex, hexToAscii, asciiToHex } from './format'; | ||||||
| import { fromWei, toWei } from './wei'; | import { fromWei, toWei } from './wei'; | ||||||
| import { sha3 } from './sha3'; | import { sha3 } from './sha3'; | ||||||
| import { isArray, isFunction, isHex, isInstanceOf, isString } from './types'; | import { isArray, isFunction, isHex, isInstanceOf, isString } from './types'; | ||||||
| @ -30,7 +30,7 @@ export default { | |||||||
|   isInstanceOf, |   isInstanceOf, | ||||||
|   isString, |   isString, | ||||||
|   bytesToHex, |   bytesToHex, | ||||||
|   hex2Ascii, |   hexToAscii, | ||||||
|   asciiToHex, |   asciiToHex, | ||||||
|   createIdentityImg, |   createIdentityImg, | ||||||
|   decodeCallData, |   decodeCallData, | ||||||
|  | |||||||
| @ -14,8 +14,22 @@ | |||||||
| // You should have received a copy of the GNU General Public License
 | // You should have received a copy of the GNU General Public License
 | ||||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
| 
 | 
 | ||||||
| import { keccak_256 } from 'js-sha3'; // eslint-disable-line camelcase
 | import { keccak_256 } from 'js-sha3'; // eslint-disable-line
 | ||||||
| 
 | 
 | ||||||
| export function sha3 (value) { | import { hexToBytes } from './format'; | ||||||
|   return `0x${keccak_256(value)}`; | 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', () => { |     it('constructs a correct sha3 value', () => { | ||||||
|       expect(sha3('jacogr')).to.equal('0x2f4ff4b5a87abbd2edfed699db48a97744e028c7f7ce36444d40d29d792aa4dc'); |       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) { | export function isHex (_test) { | ||||||
|  |   if (!isString(_test)) { | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   if (_test.substr(0, 2) === '0x') { |   if (_test.substr(0, 2) === '0x') { | ||||||
|     return isHex(_test.slice(2)); |     return isHex(_test.slice(2)); | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -66,6 +66,12 @@ describe('api/util/types', () => { | |||||||
|     it('correctly identifies non-hex values', () => { |     it('correctly identifies non-hex values', () => { | ||||||
|       expect(isHex('123j')).to.be.false; |       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', () => { |   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
 | // You should have received a copy of the GNU General Public License
 | ||||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
| 
 | 
 | ||||||
| import { bytesToHex, hex2Ascii } from '~/api/util/format'; | import { bytesToHex, hexToAscii } from '~/api/util/format'; | ||||||
| 
 | 
 | ||||||
| import ABI from './abi/certifier.json'; | import ABI from './abi/certifier.json'; | ||||||
| 
 | 
 | ||||||
| @ -62,7 +62,7 @@ export default class BadgeReg { | |||||||
|         name = bytesToHex(name); |         name = bytesToHex(name); | ||||||
|         name = name === ZERO32 |         name = name === ZERO32 | ||||||
|           ? null |           ? null | ||||||
|           : hex2Ascii(name); |           : hexToAscii(name); | ||||||
| 
 | 
 | ||||||
|         return this.fetchMeta(id) |         return this.fetchMeta(id) | ||||||
|           .then(({ title, icon }) => { |           .then(({ title, icon }) => { | ||||||
| @ -84,7 +84,7 @@ export default class BadgeReg { | |||||||
|       }) |       }) | ||||||
|       .then(([ title, icon ]) => { |       .then(([ title, icon ]) => { | ||||||
|         title = bytesToHex(title); |         title = bytesToHex(title); | ||||||
|         title = title === ZERO32 ? null : hex2Ascii(title); |         title = title === ZERO32 ? null : hexToAscii(title); | ||||||
| 
 | 
 | ||||||
|         if (bytesToHex(icon) === ZERO32) { |         if (bytesToHex(icon) === ZERO32) { | ||||||
|           icon = null; |           icon = null; | ||||||
|  | |||||||
| @ -91,7 +91,7 @@ export default class Registry { | |||||||
| 
 | 
 | ||||||
|   lookupAddress (_name) { |   lookupAddress (_name) { | ||||||
|     const name = _name.toLowerCase(); |     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 this.getInstance().then((instance) => { | ||||||
|       return instance.getAddress.call({}, [sha3, 'A']); |       return instance.getAddress.call({}, [sha3, 'A']); | ||||||
|  | |||||||
| @ -14,7 +14,7 @@ | |||||||
| // You should have received a copy of the GNU General Public License
 | // You should have received a copy of the GNU General Public License
 | ||||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
| 
 | 
 | ||||||
| import subscribeToEvent from '../util/subscribe-to-event'; | import subscribeToEvents from '../util/subscribe-to-events'; | ||||||
| 
 | 
 | ||||||
| export const checkIfVerified = (contract, account) => { | export const checkIfVerified = (contract, account) => { | ||||||
|   return contract.instance.certified.call({}, [account]); |   return contract.instance.certified.call({}, [account]); | ||||||
| @ -72,7 +72,7 @@ export const awaitPuzzle = (api, contract, account) => { | |||||||
|   return blockNumber(api) |   return blockNumber(api) | ||||||
|     .then((block) => { |     .then((block) => { | ||||||
|       return new Promise((resolve, reject) => { |       return new Promise((resolve, reject) => { | ||||||
|         const subscription = subscribeToEvent(contract, 'Puzzled', { |         const subscription = subscribeToEvents(contract, ['Puzzled'], { | ||||||
|           from: block.toNumber(), |           from: block.toNumber(), | ||||||
|           filter: (log) => log.params.who.value === account |           filter: (log) => log.params.who.value === account | ||||||
|         }); |         }); | ||||||
|  | |||||||
| @ -118,7 +118,7 @@ export function attachInstances () { | |||||||
|         ]); |         ]); | ||||||
|     }) |     }) | ||||||
|     .then(([managerAddress, registryAddress, tokenregAddress]) => { |     .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; |       managerInstance = api.newContract(abis.basiccoinmanager, managerAddress).instance; | ||||||
|       registryInstance = api.newContract(abis.tokenreg, registryAddress).instance; |       registryInstance = api.newContract(abis.tokenreg, registryAddress).instance; | ||||||
|  | |||||||
| @ -34,3 +34,16 @@ ReactDOM.render( | |||||||
|   </Provider>, |   </Provider>, | ||||||
|   document.querySelector('#container') |   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 = { |   static propTypes = { | ||||||
|     accounts: PropTypes.object.isRequired, |     accounts: PropTypes.object.isRequired, | ||||||
|     contract: nullableProptype(PropTypes.object).isRequired, |     contract: nullableProptype(PropTypes.object.isRequired), | ||||||
|     fee: nullableProptype(PropTypes.object).isRequired |     fee: nullableProptype(PropTypes.object.isRequired) | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   render () { |   render () { | ||||||
|  | |||||||
| @ -15,11 +15,13 @@ | |||||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
| 
 | 
 | ||||||
| import { sha3 } from '../parity.js'; | import { sha3 } from '../parity.js'; | ||||||
|  | import { getOwner } from '../util/registry'; | ||||||
| 
 | 
 | ||||||
| export const clear = () => ({ type: 'lookup clear' }); | export const clear = () => ({ type: 'lookup clear' }); | ||||||
| 
 | 
 | ||||||
| export const lookupStart = (name, key) => ({ type: 'lookup start', name, key }); | export const lookupStart = (name, key) => ({ type: 'lookup start', name, key }); | ||||||
| export const reverseLookupStart = (address) => ({ type: 'reverseLookup start', address }); | 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 }); | export const success = (action, result) => ({ type: `${action} success`, result: result }); | ||||||
| 
 | 
 | ||||||
| @ -37,7 +39,7 @@ export const lookup = (name, key) => (dispatch, getState) => { | |||||||
|   name = name.toLowerCase(); |   name = name.toLowerCase(); | ||||||
|   dispatch(lookupStart(name, key)); |   dispatch(lookupStart(name, key)); | ||||||
| 
 | 
 | ||||||
|   getAddress.call({}, [ sha3(name), key ]) |   getAddress.call({}, [ sha3.text(name), key ]) | ||||||
|     .then((address) => dispatch(success('lookup', address))) |     .then((address) => dispatch(success('lookup', address))) | ||||||
|     .catch((err) => { |     .catch((err) => { | ||||||
|       console.error(`could not lookup ${key} for ${name}`); |       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(); |   const { contract } = getState(); | ||||||
|  | 
 | ||||||
|   if (!contract) { |   if (!contract) { | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   const reverse = contract.functions |   dispatch(reverseLookupStart(lookupAddress)); | ||||||
|     .find((f) => f.name === 'reverse'); |  | ||||||
| 
 | 
 | ||||||
|   dispatch(reverseLookupStart(address)); |   contract.instance | ||||||
| 
 |     .reverse | ||||||
|   reverse.call({}, [ address ]) |     .call({}, [ lookupAddress ]) | ||||||
|     .then((address) => dispatch(success('reverseLookup', address))) |     .then((address) => { | ||||||
|  |       dispatch(success('reverseLookup', address)); | ||||||
|  |     }) | ||||||
|     .catch((err) => { |     .catch((err) => { | ||||||
|       console.error(`could not lookup reverse for ${address}`); |       console.error(`could not lookup reverse for ${lookupAddress}`); | ||||||
|       if (err) { |       if (err) { | ||||||
|         console.error(err.stack); |         console.error(err.stack); | ||||||
|       } |       } | ||||||
|       dispatch(fail('reverseLookup')); |       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 MenuItem from 'material-ui/MenuItem'; | ||||||
| import RaisedButton from 'material-ui/RaisedButton'; | import RaisedButton from 'material-ui/RaisedButton'; | ||||||
| import SearchIcon from 'material-ui/svg-icons/action/search'; | import SearchIcon from 'material-ui/svg-icons/action/search'; | ||||||
|  | import keycode from 'keycode'; | ||||||
| 
 | 
 | ||||||
| import { nullableProptype } from '~/util/proptypes'; | import { nullableProptype } from '~/util/proptypes'; | ||||||
| 
 | 
 | ||||||
| import Address from '../ui/address.js'; | import Address from '../ui/address.js'; | ||||||
| import renderImage from '../ui/image.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'; | import styles from './lookup.css'; | ||||||
| 
 | 
 | ||||||
| class Lookup extends Component { | class Lookup extends Component { | ||||||
| @ -39,6 +40,7 @@ class Lookup extends Component { | |||||||
| 
 | 
 | ||||||
|     clear: PropTypes.func.isRequired, |     clear: PropTypes.func.isRequired, | ||||||
|     lookup: PropTypes.func.isRequired, |     lookup: PropTypes.func.isRequired, | ||||||
|  |     ownerLookup: PropTypes.func.isRequired, | ||||||
|     reverseLookup: PropTypes.func.isRequired |     reverseLookup: PropTypes.func.isRequired | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -50,33 +52,6 @@ class Lookup extends Component { | |||||||
|     const { input, type } = this.state; |     const { input, type } = this.state; | ||||||
|     const { result } = this.props; |     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 ( |     return ( | ||||||
|       <Card className={ styles.lookup }> |       <Card className={ styles.lookup }> | ||||||
|         <CardHeader title={ 'Query the Registry' } /> |         <CardHeader title={ 'Query the Registry' } /> | ||||||
| @ -85,6 +60,7 @@ class Lookup extends Component { | |||||||
|             hintText={ type === 'reverse' ? 'address' : 'name' } |             hintText={ type === 'reverse' ? 'address' : 'name' } | ||||||
|             value={ input } |             value={ input } | ||||||
|             onChange={ this.onInputChange } |             onChange={ this.onInputChange } | ||||||
|  |             onKeyDown={ this.onKeyDown } | ||||||
|           /> |           /> | ||||||
|           <DropDownMenu |           <DropDownMenu | ||||||
|             value={ type } |             value={ type } | ||||||
| @ -94,6 +70,7 @@ class Lookup extends Component { | |||||||
|             <MenuItem value='IMG' primaryText='IMG – hash of a picture in the blockchain' /> |             <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='CONTENT' primaryText='CONTENT – hash of a data in the blockchain' /> | ||||||
|             <MenuItem value='reverse' primaryText='reverse – find a name for an address' /> |             <MenuItem value='reverse' primaryText='reverse – find a name for an address' /> | ||||||
|  |             <MenuItem value='owner' primaryText='owner – find a the owner' /> | ||||||
|           </DropDownMenu> |           </DropDownMenu> | ||||||
|           <RaisedButton |           <RaisedButton | ||||||
|             label='Lookup' |             label='Lookup' | ||||||
| @ -102,35 +79,102 @@ class Lookup extends Component { | |||||||
|             onTouchTap={ this.onLookupClick } |             onTouchTap={ this.onLookupClick } | ||||||
|           /> |           /> | ||||||
|         </div> |         </div> | ||||||
|         <CardText>{ output }</CardText> |         <CardText> | ||||||
|  |           { this.renderOutput(type, result) } | ||||||
|  |         </CardText> | ||||||
|       </Card> |       </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) => { |   onInputChange = (e) => { | ||||||
|     this.setState({ input: e.target.value }); |     this.setState({ input: e.target.value }); | ||||||
|   }; |   } | ||||||
|  | 
 | ||||||
|  |   onKeyDown = (event) => { | ||||||
|  |     const codeName = keycode(event); | ||||||
|  | 
 | ||||||
|  |     if (codeName !== 'enter') { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     this.onLookupClick(); | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   onTypeChange = (e, i, type) => { |   onTypeChange = (e, i, type) => { | ||||||
|     this.setState({ type }); |     this.setState({ type }); | ||||||
|     this.props.clear(); |     this.props.clear(); | ||||||
|   }; |   } | ||||||
| 
 | 
 | ||||||
|   onLookupClick = () => { |   onLookupClick = () => { | ||||||
|     const { input, type } = this.state; |     const { input, type } = this.state; | ||||||
| 
 | 
 | ||||||
|     if (type === 'reverse') { |     if (type === 'reverse') { | ||||||
|       this.props.reverseLookup(input); |       return this.props.reverseLookup(input); | ||||||
|     } else { |     } | ||||||
|       this.props.lookup(input, type); | 
 | ||||||
|  |     if (type === 'owner') { | ||||||
|  |       return this.props.ownerLookup(input); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return this.props.lookup(input, type); | ||||||
|   } |   } | ||||||
|   }; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const mapStateToProps = (state) => state.lookup; | const mapStateToProps = (state) => state.lookup; | ||||||
| const mapDispatchToProps = (dispatch) => | const mapDispatchToProps = (dispatch) => | ||||||
|   bindActionCreators({ |   bindActionCreators({ | ||||||
|     clear, lookup, reverseLookup |     clear, lookup, ownerLookup, reverseLookup | ||||||
|   }, dispatch); |   }, dispatch); | ||||||
| 
 | 
 | ||||||
| export default connect(mapStateToProps, mapDispatchToProps)(Lookup); | export default connect(mapStateToProps, mapDispatchToProps)(Lookup); | ||||||
|  | |||||||
| @ -24,7 +24,7 @@ const initialState = { | |||||||
| export default (state = initialState, action) => { | export default (state = initialState, action) => { | ||||||
|   const { type } = action; |   const { type } = action; | ||||||
| 
 | 
 | ||||||
|   if (type.slice(0, 7) !== 'lookup ' && type.slice(0, 14) !== 'reverseLookup ') { |   if (!/^(lookup|reverseLookup|ownerLookup)/.test(type)) { | ||||||
|     return state; |     return state; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -15,8 +15,13 @@ | |||||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
| 
 | 
 | ||||||
| import { sha3, api } from '../parity.js'; | import { sha3, api } from '../parity.js'; | ||||||
|  | import { getOwner, isOwned } from '../util/registry'; | ||||||
| import postTx from '../util/post-tx'; | import postTx from '../util/post-tx'; | ||||||
| 
 | 
 | ||||||
|  | export const clearError = () => ({ | ||||||
|  |   type: 'clearError' | ||||||
|  | }); | ||||||
|  | 
 | ||||||
| const alreadyQueued = (queue, action, name) => | const alreadyQueued = (queue, action, name) => | ||||||
|   !!queue.find((entry) => entry.action === action && entry.name === 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 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) => { | export const reserve = (name) => (dispatch, getState) => { | ||||||
|   const state = getState(); |   const state = getState(); | ||||||
|   const account = state.accounts.selected; |   const account = state.accounts.selected; | ||||||
|   const contract = state.contract; |   const contract = state.contract; | ||||||
|   const fee = state.fee; |   const fee = state.fee; | ||||||
|  | 
 | ||||||
|   if (!contract || !account) { |   if (!contract || !account) { | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| @ -40,27 +46,34 @@ export const reserve = (name) => (dispatch, getState) => { | |||||||
|   if (alreadyQueued(state.names.queue, 'reserve', name)) { |   if (alreadyQueued(state.names.queue, 'reserve', name)) { | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|   const reserve = contract.functions.find((f) => f.name === 'reserve'); |  | ||||||
| 
 | 
 | ||||||
|   dispatch(reserveStart(name)); |   dispatch(reserveStart(name)); | ||||||
| 
 | 
 | ||||||
|  |   return isOwned(contract, name) | ||||||
|  |     .then((owned) => { | ||||||
|  |       if (owned) { | ||||||
|  |         throw new Error(`"${name}" has already been reserved`); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       const { reserve } = contract.instance; | ||||||
|  | 
 | ||||||
|       const options = { |       const options = { | ||||||
|         from: account.address, |         from: account.address, | ||||||
|         value: fee |         value: fee | ||||||
|       }; |       }; | ||||||
|       const values = [ |       const values = [ | ||||||
|     sha3(name) |         sha3.text(name) | ||||||
|       ]; |       ]; | ||||||
| 
 | 
 | ||||||
|   postTx(api, reserve, options, values) |       return postTx(api, reserve, options, values); | ||||||
|  |     }) | ||||||
|     .then((txHash) => { |     .then((txHash) => { | ||||||
|       dispatch(reserveSuccess(name)); |       dispatch(reserveSuccess(name)); | ||||||
|     }) |     }) | ||||||
|     .catch((err) => { |     .catch((err) => { | ||||||
|       console.error(`could not reserve ${name}`); |       if (err.type !== 'REQUEST_REJECTED') { | ||||||
| 
 |         console.error(`error rerserving ${name}`, err); | ||||||
|       if (err) { |         return dispatch(reserveFail(name, err)); | ||||||
|         console.error(err.stack); |  | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       dispatch(reserveFail(name)); |       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 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) => { | export const drop = (name) => (dispatch, getState) => { | ||||||
|   const state = getState(); |   const state = getState(); | ||||||
|   const account = state.accounts.selected; |   const account = state.accounts.selected; | ||||||
|   const contract = state.contract; |   const contract = state.contract; | ||||||
|  | 
 | ||||||
|   if (!contract || !account) { |   if (!contract || !account) { | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   name = name.toLowerCase(); |   name = name.toLowerCase(); | ||||||
|  | 
 | ||||||
|   if (alreadyQueued(state.names.queue, 'drop', name)) { |   if (alreadyQueued(state.names.queue, 'drop', name)) { | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   const drop = contract.functions.find((f) => f.name === 'drop'); |  | ||||||
| 
 |  | ||||||
|   dispatch(dropStart(name)); |   dispatch(dropStart(name)); | ||||||
| 
 | 
 | ||||||
|  |   return getOwner(contract, name) | ||||||
|  |     .then((owner) => { | ||||||
|  |       if (owner.toLowerCase() !== account.address.toLowerCase()) { | ||||||
|  |         throw new Error(`you are not the owner of "${name}"`); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       const { drop } = contract.instance; | ||||||
|  | 
 | ||||||
|       const options = { |       const options = { | ||||||
|         from: account.address |         from: account.address | ||||||
|       }; |       }; | ||||||
|  | 
 | ||||||
|       const values = [ |       const values = [ | ||||||
|     sha3(name) |         sha3.text(name) | ||||||
|       ]; |       ]; | ||||||
| 
 | 
 | ||||||
|   postTx(api, drop, options, values) |       return postTx(api, drop, options, values); | ||||||
|  |     }) | ||||||
|     .then((txhash) => { |     .then((txhash) => { | ||||||
|       dispatch(dropSuccess(name)); |       dispatch(dropSuccess(name)); | ||||||
|     }) |     }) | ||||||
|     .catch((err) => { |     .catch((err) => { | ||||||
|       console.error(`could not drop ${name}`); |       if (err.type !== 'REQUEST_REJECTED') { | ||||||
| 
 |         console.error(`error dropping ${name}`, err); | ||||||
|       if (err) { |         return dispatch(dropFail(name, err)); | ||||||
|         console.error(err.stack); |  | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       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