Compare commits

...

85 Commits
main ... v1.5.3

Author SHA1 Message Date
arkpar
82e33fa033 Merge branch 'beta' of github.com:ethcore/parity into beta 2017-02-20 17:39:24 +01:00
arkpar
fa8eb22e79 Backported mac installer fix 2017-02-20 17:38:58 +01:00
Denis S. Soldatov aka General-Beck
55b51890dd [ci-skip] backport Dockerfile for hub 2017-02-20 20:08:07 +04:00
GitLab Build Bot
0f99b1e123 js-precompiled 20170220-130332 2017-02-20 15:18:20 +01:00
Arkadiy Paronyan
51e4c82337 v1.5.3 (#4611) 2017-02-20 13:48:38 +01:00
Jaco Greeff
7df702494d Handle invalid ABI retrieved from address_book gracefully (#4606) (#4610)
* Handle invalid ABI gracefully

* Also include failed abi in log
2017-02-20 13:48:23 +01:00
Arkadiy Paronyan
b5219bc723 Backporting to beta (#4602)
* Static link for snappy

* added 3 warpnodes for ropsten (#4289)

* added 3 warpnodes for ropsten

* Fixed indentation
2017-02-19 20:55:52 +01:00
Arkadiy Paronyan
27765a71dd Validate transaction before adding to the queue (#4600) 2017-02-19 17:55:55 +01:00
GitLab Build Bot
a1b9f03121 [ci skip] js-precompiled 20170216-173539 2017-02-16 17:42:25 +00:00
GitLab Build Bot
1c40421982 [ci skip] js-precompiled 20170216-171113 2017-02-16 17:18:45 +00:00
Tomasz Drwięga
07324795f1 Beta backports (#4569)
* Fixing evmbin compilation and added standard build. (#4561)

* Alias for personal_sendTransaction (#4554)

* Fix console dapp (#4544)

* Fixing linting issues. Better support for console as secure app

* Fixing linting issues

* Fix no data sent in TxQueue dapp (#4502)

* Fix wrong PropType req for Embedded Signer

* Fix wrong data for tx #4499
2017-02-16 17:46:25 +01:00
Jaco Greeff
19520442c1 Explicitly set seconds to 0 from selector (#4559) (#4571)
* Explicitly set seconds/milli to 0

* Use condition time & block setters consistently

* Fix failing test

* test for 0 ms & sec

* It cannot hurt, clone date before setting

* Prettier date test constants (OCD)
2017-02-16 17:46:18 +01:00
Denis S. Soldatov aka General-Beck
e0c5baece0 armv6 build -> only triggers
[ci-skip]
2017-02-16 20:45:27 +04:00
Nikolay Volf
f4f7b83d1f replace expect with an error (#4542) 2017-02-15 19:03:36 +01:00
GitLab Build Bot
30d7872226 [ci skip] js-precompiled 20170215-145227 2017-02-15 14:56:17 +00:00
Jaco Greeff
4d4821c577 [beta] Skip OOG check for simple transfers #4558 (#4560)
* Skip OOG check for simple transfers #4558

* Fix failing test
2017-02-15 15:45:52 +01:00
Denis S. Soldatov aka General-Beck
948a5387b3 downgrade rust (windows build) 2017-02-08 20:30:22 +01:00
Denis S. Soldatov aka General-Beck
8a050e4464 disable armv6 2017-02-08 23:21:17 +04:00
GitLab Build Bot
af998d8c40 js-precompiled 20170208-135150 2017-02-08 17:48:23 +01:00
Tomasz Drwięga
591673821c Work with string numbers in contract (Fixes #4472) (#4478) (#4480) 2017-02-08 14:45:06 +01:00
Tomasz Drwięga
ce5dfd8e34 Removing duplicate sign 2017-02-08 14:38:11 +01:00
Tomasz Drwięga
8c049e5d05 eth_sign improvements backport (#4473)
* Fix postsign (#4347)

* Fix whitespace.

* Fix post sign.

* Fix message.

* Fix tests.

* Rest of the problems.

* All hail the linter and its omniscience.

* ...and its divine omniscience.

* Grumbles and wording.

* Make signing compatible with geth. (#4468)
2017-02-08 10:40:42 +01:00
keorn
2bfcfd3813 sort corpus when hitting genesis (#4471) 2017-02-08 08:56:02 +01:00
GitLab Build Bot
4f869a9ba5 [ci skip] js-precompiled 20170207-191112 2017-02-07 19:16:45 +00:00
Jaco Greeff
dd0b2c2327 Handle registry not found errors (Fixes #4463) (#4465) (#4466) 2017-02-07 19:52:06 +01:00
GitLab Build Bot
889f85ea0d [ci skip] js-precompiled 20170207-184700 2017-02-07 18:50:56 +00:00
Tomasz Drwięga
42eec0f6bd Fixing histogram again (#4464) 2017-02-07 19:39:04 +01:00
Tomasz Drwięga
449e85743d Beta backports (#4462)
* Support HTML5-routed dapps (#4173)

* Fix compilation on latest nightly

* Updating precompiled
2017-02-07 19:10:24 +01:00
GitLab Build Bot
8764563222 [ci skip] js-precompiled 20170207-122754 2017-02-07 12:31:23 +00:00
Jaco Greeff
5b31af4e09 Fix Portal scrolling getting stuck (#4455) (#4456)
* Fix Portal scrolling getting stuck

* DappCard container flex

* Container height to 100%
2017-02-07 13:22:09 +01:00
GitLab Build Bot
b648bd403a [ci skip] js-precompiled 20170207-080055 2017-02-07 08:04:20 +00:00
Jaco Greeff
0994b91af5 Fix AccountCard stretch to 100% (#4450) (#4451) 2017-02-07 08:54:10 +01:00
GitLab Build Bot
2e595d60f0 [ci skip] js-precompiled 20170206-085912 2017-02-06 09:03:07 +00:00
Jaco Greeff
c2ceefbc22 Fix wrong output format of peers (#4270) (#4442)
* Fix wrong output format of peers

* Add outPeer tests
2017-02-06 09:53:22 +01:00
Tomasz Drwięga
ab2d02bbb6 Open popup without attempting inline (#4440) (#4441)
* Open popup without attempting inline

* Cater for all .web3.site addresses
2017-02-06 09:53:11 +01:00
Jaco Greeff
43bb862adb webpack config for inline icon svg (#4437) 2017-02-05 10:30:22 +01:00
Denis S. Soldatov aka General-Beck
172b40ae30 Create Docker file in beta
add for tags on hub docker
2017-02-05 03:36:48 +04:00
Denis S. Soldatov aka General-Beck
74e859a9d4 update gitlab-ci
add hub docker
[ci skip]
2017-02-05 03:33:16 +04:00
GitLab Build Bot
1fdbc07cb4 [ci skip] js-precompiled 20170204-162639 2017-02-04 16:30:03 +00:00
Arkadiy Paronyan
9912a3158c Backporting to beta (#4434)
* v1.5.2

* Fix eth_sign/parity_postSign (#4432)

* Fix dispatch for signing.

* Remove console log

* Fix signing & tests.
2017-02-04 17:06:40 +01:00
arkpar
fa6aceca42 mac build fix 2017-02-04 14:59:39 +01:00
GitLab Build Bot
503137a968 js-precompiled 20170204-084850 2017-02-04 12:57:40 +01:00
Tomasz Drwięga
184648a734 Returning default account as coinbase + allow altering sender in signer (#4323) (#4431)
* Returning first address as coinbase

* Allowing sender alteration in signer

* Adding default account RPC
2017-02-04 09:42:50 +01:00
Jaco Greeff
fb817fcdca [beta] UI updates for 1.5.1 (#4429)
* s/Delete Contract/Forget Contract/ (#4237)

* Adjust the location of the signer snippet (#4155)

* Additional building-block UI components (#4239)

* Currency WIP

* Expand tests

* Pass className

* Add QrCode

* Export new components in ~/ui

* s/this.props.netSymbol/netSymbol/

* Fix import case

* ui/SectionList component (#4292)

* array chunking utility

* add SectionList component

* Add TODOs to indicate possible future work

* Add missing overlay style (as used in dapps at present)

* Add a Playground for the UI Components (#4301)

* Playground // WIP

* Linting

* Add Examples with code

* CSS Linting

* Linting

* Add Connected Currency Symbol

* 2015-2017

* 2015-2017

* 2015-2017

* 2015-2017

* 2015-2017

* 2015-2017

* 2015-2017

* Added `renderSymbol` tests

* PR grumbles

* Add Eth and Btc QRCode examples

* 2015-2017

* Add tests for playground

* Fixing tests

* Split Dapp icon into ui/DappIcon (#4308)

* Add QrCode & Copy to ShapeShift (#4322)

* Extract CopyIcon to ~/ui/Icons

* Add copy & QrCode address

* Default size 4

* Add bitcoin: link

* use protocol links applicable to coin exchanged

* Remove .only

* Display QrCode for accounts, addresses & contracts (#4329)

* Allow Portal to be used as top-level modal (#4338)

* Portal

* Allow Portal to be used in as both top-level and popover

* modal/popover variable naming

* export Portal in ~/ui

* Properly handle optional onKeyDown

* Add simple Playground Example

* Add proper event listener to Portal (#4359)

* Display AccountCard name via IdentityName (#4235)

* Fix signing (#4363)

* Dapp Account Selection & Defaults (#4355)

* Add parity_defaultAccount RPC (with subscription) (#4383)

* Default Account selector in Signer overlay (#4375)

* Typo, fixes #4271 (#4391)

* Fix ParityBar account selection overflows (#4405)

* Available Dapp selection alignment with Permissions (Portal) (#4374)

* registry dapp: make lookup use lower case (#4409)

* Dapps use defaultAccount instead of own selectors (#4386)

* Poll for defaultAccount to update dapp & overlay subscriptions (#4417)

* Poll for defaultAccount (Fixes #4413)

* Fix nextTimeout on catch

* Store timers

* Re-enable default updates on change detection

* Add block & timestamp conditions to Signer (#4411)

* Extension installation overlay (#4423)

* Extension installation overlay

* Pr gumbles

* Spelling

* Update Chrome URL

* Fix for non-included jsonrpc

* Extend Portal component (as per Modal) #4392
2017-02-04 09:42:36 +01:00
Denis S. Soldatov aka General-Beck
f76b94c2c5 update gitlab-ci
fix darwin build
2017-02-04 02:34:02 +04:00
Denis S. Soldatov aka General-Beck
78afa8ed54 update gitlab-ci
type fix
2017-02-04 01:03:27 +04:00
Denis S. Soldatov aka General-Beck
63bf2755ed fix darwin build
add `cargo build -j 8 --features final --release -p ethstore #$CARGOFLAGS`
2017-02-04 00:55:15 +04:00
Arkadiy Paronyan
f187b15a43 Transaction timestamp condtiion (#4427) 2017-02-03 19:34:26 +01:00
GitLab Build Bot
e9b1921950 [ci skip] js-precompiled 20170203-132415 2017-02-03 13:27:34 +00:00
Tomasz Drwięga
3106835ae3 Fixing embedded bar not closing in chrome extension (#4421) 2017-02-03 14:17:17 +01:00
Arkadiy Paronyan
c7dbd87f8e Backporting to beta (#4418)
* v1.5.1

* Disable notifications (#4243)

* Fix wrong token handling (#4254)

* Fixing wrong token displayed

* Linting

* Revert filtering out

* Revert the revert

* Don't panic on uknown git commit hash (#4231)

* Additional logs for own transactions (#4278)

* Integration with zgp whitelist contract (#4215)

* zgp-transactions checker

* polishing

* rename + refactor

* refuse-service-transactions cl option

* fixed tests compilation

* Renaming signAndSendTransaction to sendTransaction (#4351)

* Fixed deadlock in external_url (#4354)

* Fixing web3 in console (#4382)

* Fixing estimate gas in case histogram is not available (#4387)

* Restarting fetch client every now and then (#4399)
2017-02-03 13:57:04 +01:00
Denis S. Soldatov aka General-Beck
b09cfe1885 update depends
Depends: libssl1.0.0 (>=1.0.0)
[ci skip]
2017-02-03 14:49:12 +04:00
Denis S. Soldatov aka General-Beck
070c40c02f change depends
libssl (>=1.0.0)
[ci skip]
2017-02-03 13:16:23 +04:00
Denis S. Soldatov aka General-Beck
33ecf1313f Update gitlab-ci
[ci-skip] Remove `triggers` from stage `push-release`
2017-01-25 19:04:08 +04:00
GitLab Build Bot
971a481b0d [ci skip] js-precompiled 20170124-193815 2017-01-24 19:43:16 +00:00
Tomasz Drwięga
2b132c38d6 Embeddable ParityBar (#4222) (#4287)
* Embeddable ParityBar

* Replacing storage with store

* Fixing  references.

* Addressing style issues

* Supporting parity background

Conflicts:
	js/src/views/ParityBar/parityBar.js
2017-01-24 19:19:05 +00:00
Denis S. Soldatov aka General-Beck
dcd9119452 update gitlab-ci
revert 1337
2017-01-20 02:31:19 +04:00
Denis S. Soldatov aka General-Beck
f3f25ad50c update Dockerfile
add dependencies for Docker beta
[ci skip]
2017-01-20 01:44:22 +04:00
Denis S. Soldatov aka General-Beck
f3c4ea4fb9 update gitlab-ci
[ci skip]
small fix
2017-01-20 01:05:00 +04:00
Denis S. Soldatov aka General-Beck
1b79fc3e27 update gitlab-ci
comment curl 1337
[ci skip]
2017-01-20 00:51:01 +04:00
Denis S. Soldatov aka General-Beck
ce824089ac update gitlab-ci
add debian build with openssl 1.0.1
[ci skip]
2017-01-20 00:18:26 +04:00
Gav
d2e6fc0a65 Dont push-release for beta/stable changes 2017-01-19 14:27:31 +01:00
GitLab Build Bot
29d50f8f30 js-precompiled 20170119-105731 2017-01-19 12:13:13 +01:00
arkpar
536cf73db8 Merge branch 'backporting' into beta 2017-01-19 11:30:32 +01:00
Jaco Greeff
f15a3ca977 Non-secure for DappReg (#4216) 2017-01-19 11:11:19 +01:00
Gav Wood
096a44d9bf Console now has admin (#4220)
Fixes #4210
2017-01-19 10:50:24 +01:00
Jannis Redmann
ff3ae33d99 verification: add mainnet BadgeReg ids (#4190)
* verification: mainnet BadgeReg ids

* verification: fetch contracts by name

* verification: better wording

* typo

* reregistered badges
2017-01-19 10:50:13 +01:00
GitLab Build Bot
8e6d09e6ac [ci skip] js-precompiled 20170119-091230 2017-01-19 09:15:08 +00:00
Tomasz Drwięga
516e16a36b Fixing minimal transaction queue price (#4204)
* Fixing minimal transaction queue price

* Fixing tests
2017-01-18 19:54:40 +01:00
Arkadiy Paronyan
77b14b2736 Fixed --base-path on windows (#4193)
* Fixed --base-path on windows

* Add support for optional args with default text
2017-01-18 19:11:01 +01:00
Tomasz Drwięga
e01636fc34 Fixing etherscan price parsing (#4202)
* Fixing etherscan price parsing

* Handling all errors
2017-01-18 19:10:25 +01:00
arkpar
7b2cfd1cfb JsonRPC bump for IPC fix 2017-01-18 19:10:07 +01:00
Arkadiy Paronyan
cf6d870b09 Backporting to beta (#4203)
* Minor typo to ensure it updates only when synced. (#4188)

* Updater fixes (#4196)

* Minor typo to ensure it updates only when synced.

* Fix deadlock.

* Skip unneeded arg in making list.

* Allow auto-restart even when not running an update.

* Fix trace.

* Update update info on each loop.

* Fix build.

* Shutdown all sockets

* Remove superfluous use.

* Poll for upgrades as part of global status (long) (#4197)

* Poll for upgrades as part of global status (long)

* Fix path

* Prevent duplicate incoming connections (#4180)
2017-01-18 17:56:32 +01:00
Arkadiy Paronyan
f20db41169 gas_limit for blocks, mined by Parity will be divisible by 37 (#4154) (#4176)
* gas_limit for new blocks will divide evenly by 13

* increased PARITY_GAS_LIMIT_DETERMINANT to 37

* separate method for marking mined block

* debug_asserts(gas_limit within protocol range)

* round_block_gas_limit method is now static

* made round_block_gas_limit free-function

* multiplier renamed to multiple
2017-01-16 20:55:07 +01:00
GitLab Build Bot
6830273cb9 [ci skip] js-precompiled 20170116-132441 2017-01-16 13:28:28 +00:00
Arkadiy Paronyan
65594b8865 Backporting to beta (#4175)
* verification: check if server is running (#4140)

* verification: check if server is running

See also ethcore/email-verification#67c6466 and ethcore/sms-verification#a585e42.

* verification: show in the UI if server is running

* verification: code style , more i18n

* fix i18n key

* Optimized hash lookups (#4144)

* Optimize hash comparison

* Use libc

* Ropsten fork detection (#4163)

* Stop flickering + added loader in AddressSelector (#4149)

* Stop UI flickering + added loader to AddressSelector #4103

* PR Grumbles

* Add a password strength component (#4153)

* Added new PasswordStrength Component

* Added tests

* PR Grumbles

* icarus -> update, increase web timeout. (#4165)

* icarus -> update, increase web timeout.

* Fix estimate gas

* Fix token images // Error in Contract Queries (#4169)

* Fix dapps not loading (#4170)

* Add secure to dappsreg

* Remove trailing slash // fix dapps
2017-01-16 13:41:37 +01:00
Tomasz Drwięga
5b30a61158 [beta] Bumping hyper (#4168)
* Bumping hyper

* Bumping again
2017-01-16 10:47:58 +01:00
Denis S. Soldatov aka General-Beck
0bc2aeca8d remove icarus
[ci skip]
2017-01-13 16:28:24 +04:00
GitLab Build Bot
1478109c43 [ci skip] js-precompiled 20170113-100252 2017-01-13 10:05:21 +00:00
Arkadiy Paronyan
bbd2bd0e17 Backporting to beta (#4158)
* Remove onSubmit of current (no auto-change on password edit) (#4151)

* Remove onSubmit from current password

* Remove onSubmit from hint

* Pull in console dapp as builtin (#4145)

* Copy static dapps from static (no build)

* Console sources

* Add console to builtins

* Remove console assets

* Disable eslint on console.js

* Enable eslint after disable

* Webpack copy
2017-01-13 10:55:59 +01:00
Arkadiy Paronyan
1e212771b5 Backporting to beta (#4152)
* Fix broken transfer total balance (#4127)

* Add proper label to method decoding inputs (#4136)

* Another minor estimation fix (#4133)

* Return 0 instead of error with out of gas on estimate_gas

* Fix stuff up.

* Another estimate gas fix.

* Alter balance to maximum possible rather than GP=0.

* Only increase to amount strictly necessary.

* Get rid of unsafe code in ethkey, propagate incorrect Secret errors. (#4119)

* Implementing secret

* Fixing tests

* Refactor VoteCollector (#4101)

* dir

* simple validator list

* stub validator contract

* make the engine hold Weak<Client> instead of IoChannel

* validator set factory

* register weak client with ValidatorContract

* check chain security

* add address array to generator

* register provider contract

* update validator set on notify

* add validator contract spec

* simple list test

* split update and contract test

* contract change

* use client in tendermint

* fix deadlock

* step duration in params

* adapt tendermint tests

* add storage fields to test spec

* constructor spec

* execute under wrong address

* create under correct address

* revert

* validator contract constructor

* move genesis block lookup

* add removal ability to contract

* validator contract adding validators

* fix basic authority

* validator changing test

* more docs

* update sync tests

* remove env_logger

* another env_logger

* cameltoe

* hold EngineClient instead of Client

* return error on misbehaviour

* nicer return

* sprinkle docs

* Reenable mainnet update server. (#4137)

* basic tests for subscribeToEvents (#4115)

* subscribeToEvent fixtures 

* subscribeToEvent tests 

* temporarily skip failing test (#4138)

* Improvements and optimisations to estimate_gas (#4142)

* Return 0 instead of error with out of gas on estimate_gas

* Fix stuff up.

* Another estimate gas fix.

* Alter balance to maximum possible rather than GP=0.

* Only increase to amount strictly necessary.

* Improvements and optimisations to estimate_gas.

- Introduce proper error type
- Avoid building costly traces

* Fix tests.

* Actually fix testsActually fix tests

* Use estimateGas error (as per updated implementation) (#4131)

* Use estimateGas error (as per updated implementation)

* EXCEPTION_ERROR as per #4142

* Better error log reporting & handling (#4128)

* Don't pop-up notifications after network switch (#4076)

* Better notifications

* Don't pollute with notifs if switched networks

* Better connection close/open events / No more notifs on change network

* PR Grumbles

* Add close and open events to HTTP // Add tests

* Fix tests

* WIP Signer Fix

* Fix Signer // Better reconnection handling

* PR Grumbles

* PR Grumbles

* Fixes wrong fetching of balances + Notifications

* Secure API WIP

* Updated Secure API Connection + Status

* Linting

* Linting

* Updated Secure API Logic

* Proper handling of token updates // Fixing poping notifications

* PR Grumbles

* PR Grumbles

* Fixing tests

* Trim spaces from InputAddress (#4126)

* Trim spaces for addresses

* onSubmit has only value, not event

* onSubmit (again)

* Length check on trimmed value

* Remove bindActionCreators({}, dispatch) (empty) (#4135)
2017-01-12 17:06:15 +01:00
GitLab Build Bot
72bb687f5e [ci skip] js-precompiled 20170111-193033 2017-01-11 19:33:04 +00:00
Arkadiy Paronyan
5e70507c78 Backporting to beta (#4118)
* Ignore get_price_info test by default. (#4112)

* Auto-detect hex encoded bytes in sha3 (#4108)

* Auto-detect hex encoded bytes in sha3

* Using types/isHex

* Removing unused imports

* Use binary chop to estimate gas accurately (#4100)

* Initial sketch.

* Building.

* Fix a few things.

* Fix issue, add tracing.

* Address grumbles

* Raise upper limit if needed

* Fix test.

* Fixing decoding API with signatures in names (#4125)

* Fix call/estimate_gas (#4121)

* Return 0 instead of error with out of gas on estimate_gas

* Fix stuff up.
2017-01-11 20:03:08 +01:00
Denis S. Soldatov aka General-Beck
2b588d57f0 Update gitlab-ci.yml
comment curl in arm
remove armv6 build from beta
2017-01-10 19:43:13 +04:00
arkpar
e7fbf10819 Release track set to beta 2017-01-10 15:03:23 +01:00
356 changed files with 12447 additions and 3430 deletions

View File

@@ -38,7 +38,7 @@ linux-stable:
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/parity.md5 --body parity.md5
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/"parity_"$VER"_amd64.deb" --body "parity_"$VER"_amd64.deb"
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/"parity_"$VER"_amd64.deb.md5" --body "parity_"$VER"_amd64.deb.md5"
# - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://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:1337/push-build/$CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu
tags:
- rust
@@ -47,6 +47,41 @@ linux-stable:
paths:
- target/release/parity
name: "stable-x86_64-unknown-linux-gnu_parity"
linux-stable-debian:
stage: build
image: ethcore/rust-debian:latest
only:
- beta
- tags
- stable
- triggers
script:
- cargo build -j $(nproc) --release --features final $CARGOFLAGS
- strip target/release/parity
- export SHA3=$(target/release/parity tools hash target/release/parity)
- md5sum target/release/parity > parity.md5
- sh scripts/deb-build.sh amd64
- cp target/release/parity deb/usr/bin/parity
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
- dpkg-deb -b deb "parity_"$VER"_amd64.deb"
- md5sum "parity_"$VER"_amd64.deb" > "parity_"$VER"_amd64.deb.md5"
- aws configure set aws_access_key_id $s3_key
- aws configure set aws_secret_access_key $s3_secret
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/x86_64-unknown-debian-gnu
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-debian-gnu/parity --body target/release/parity
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-debian-gnu/parity.md5 --body parity.md5
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-debian-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-debian-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://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/x86_64-unknown-debian-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-debian-gnu
tags:
- rust
- rust-debian
artifacts:
paths:
- target/release/parity
name: "stable-x86_64-unknown-debian-gnu_parity"
linux-beta:
stage: build
image: ethcore/rust:beta
@@ -107,7 +142,7 @@ linux-centos:
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity --body target/release/parity
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity.md5 --body parity.md5
# - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://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:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
tags:
- rust
@@ -146,7 +181,7 @@ linux-i686:
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_i386.deb" --body "parity_"$VER"_i386.deb"
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_i386.deb.md5" --body "parity_"$VER"_i386.deb.md5"
# - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://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:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
tags:
- rust
@@ -192,7 +227,7 @@ linux-armv7:
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_armhf.deb" --body "parity_"$VER"_armhf.deb"
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_armhf.deb.md5" --body "parity_"$VER"_armhf.deb.md5"
# - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://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:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
tags:
- rust
@@ -238,7 +273,7 @@ linux-arm:
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_armhf.deb" --body "parity_"$VER"_armhf.deb"
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_armhf.deb.md5" --body "parity_"$VER"_armhf.deb.md5"
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://icarus.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
tags:
- rust
@@ -252,10 +287,10 @@ linux-armv6:
stage: build
image: ethcore/rust-armv6:latest
only:
- beta
# - beta
# - tags
# - stable
# - triggers
- triggers
script:
- export CC=arm-linux-gnueabi-gcc
- export CXX=arm-linux-gnueabi-g++
@@ -277,7 +312,7 @@ linux-armv6:
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/$PLATFORM
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity --body target/$PLATFORM/release/parity
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
# - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://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:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
tags:
- rust
@@ -322,7 +357,7 @@ linux-aarch64:
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_arm64.deb" --body "parity_"$VER"_arm64.deb"
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_arm64.deb.md5" --body "parity_"$VER"_arm64.deb.md5"
# - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://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:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
tags:
- rust
@@ -339,28 +374,29 @@ darwin:
- tags
- stable
- triggers
script:
- export COMMIT=$(git rev-parse HEAD)
- export PLATFORM=x86_64-apple-darwin
- cargo build -j 8 --features final --release #$CARGOFLAGS
- cargo build -j 8 --features final --release -p ethstore #$CARGOFLAGS
- rm -rf parity.md5
- md5sum target/release/parity > parity.md5
- export SHA3=$(target/release/parity tools hash target/release/parity)
- packagesbuild -v mac/Parity.pkgproj
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
- mv target/release/Parity\ Ethereum.pkg "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg"
- md5sum "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg" >> "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg.md5"
- aws configure set aws_access_key_id $s3_key
- aws configure set aws_secret_access_key $s3_secret
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
- 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/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-"$VER"-osx-installer-EXPERIMENTAL.pkg" --body "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg"
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity-"$VER"-osx-installer-EXPERIMENTAL.pkg.md5" --body "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg.md5"
# - curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://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
script: |
export COMMIT=$(git rev-parse HEAD)
export PLATFORM=x86_64-apple-darwin
cargo build -j 8 --features final --release #$CARGOFLAGS
cargo build -j 8 --features final --release -p ethstore #$CARGOFLAGS
cargo build -j 8 --features final --release -p evmbin #$CARGOFLAGS
rm -rf parity.md5
md5sum target/release/parity > parity.md5
export SHA3=$(target/release/parity tools hash target/release/parity)
packagesbuild -v mac/Parity.pkgproj
export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
mv target/release/Parity\ Ethereum.pkg "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg"
md5sum "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg" >> "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg.md5"
aws configure set aws_access_key_id $s3_key
aws configure set aws_secret_access_key $s3_secret
if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
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/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-"$VER"-osx-installer-EXPERIMENTAL.pkg" --body "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg"
aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity-"$VER"-osx-installer-EXPERIMENTAL.pkg.md5" --body "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg.md5"
curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
tags:
- osx
artifacts:
@@ -383,7 +419,7 @@ windows:
- set LIB=C:\vs2015\VC\lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.10240.0\ucrt\x64
- set RUST_BACKTRACE=1
- set RUSTFLAGS=%RUSTFLAGS%
- rustup default stable-x86_64-pc-windows-msvc
- rustup default 1.14.0-x86_64-pc-windows-msvc
- cargo build --features final --release #%CARGOFLAGS%
- signtool sign /f %keyfile% /p %certpass% target\release\parity.exe
- target\release\parity.exe tools hash target\release\parity.exe > parity.sha3
@@ -421,7 +457,7 @@ windows:
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/InstallParity.exe.md5 --body nsis\InstallParity.exe.md5
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/win-installer.zip --body nsis\win-installer.zip
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/win-installer.zip.md5 --body nsis\win-installer.zip.md5
# - curl --data "commit=%CI_BUILD_REF%&sha3=%SHA3%&filename=parity.exe&secret=%RELEASES_SECRET%" http://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:1337/push-build/%CI_BUILD_REF_NAME%/%PLATFORM%
- curl --data "commit=%CI_BUILD_REF%&sha3=%SHA3%&filename=parity.exe&secret=%RELEASES_SECRET%" http://update.parity.io:1338/push-build/%CI_BUILD_REF_NAME%/%PLATFORM%
tags:
- rust-windows
@@ -431,6 +467,19 @@ windows:
- target/release/parity.pdb
- nsis/InstallParity.exe
name: "x86_64-pc-windows-msvc_parity"
docker-build:
stage: build
only:
- tags
before_script:
- docker info
script:
- cd docker/hub
- docker login -u $Docker_Hub_User -p $Docker_Hub_Pass
- docker build --tag ethcore/parity:$CI_BUILD_REF_NAME .
- docker push ethcore/parity:$CI_BUILD_REF_NAME
tags:
- docker
test-darwin:
stage: test
only:
@@ -452,7 +501,7 @@ test-windows:
- git submodule update --init --recursive
script:
- set RUST_BACKTRACE=1
- echo cargo test --features json-tests -p rlp -p ethash -p ethcore -p ethcore-bigint -p ethcore-dapps -p ethcore-rpc -p ethcore-signer -p ethcore-util -p ethcore-network -p ethcore-io -p ethkey -p ethstore -p ethsync -p ethcore-ipc -p ethcore-ipc-tests -p ethcore-ipc-nano -p parity %CARGOFLAGS% --verbose --release
- echo cargo test --features json-tests -p rlp -p ethash -p ethcore -p ethcore-bigint -p ethcore-dapps -p ethcore-rpc -p ethcore-signer -p ethcore-util -p ethcore-network -p ethcore-io -p ethkey -p ethstore -p evmbin -p ethsync -p ethcore-ipc -p ethcore-ipc-tests -p ethcore-ipc-nano -p parity %CARGOFLAGS% --verbose --release
tags:
- rust-windows
allow_failure: true
@@ -529,13 +578,10 @@ js-release:
push-release:
stage: push-release
only:
- beta
- tags
- stable
- triggers
image: ethcore/rust:stable
script:
# - 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:1337/push-release/$CI_BUILD_REF_NAME/$CI_BUILD_REF
- curl --data "secret=$RELEASES_SECRET" http://update.parity.io:1338/push-release/$CI_BUILD_REF_NAME/$CI_BUILD_REF
tags:
- curl

202
Cargo.lock generated
View File

@@ -1,6 +1,6 @@
[root]
name = "parity"
version = "1.5.0"
version = "1.5.3"
dependencies = [
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -23,8 +23,9 @@ dependencies = [
"ethcore-rpc 1.5.0",
"ethcore-signer 1.5.0",
"ethcore-stratum 1.5.0",
"ethcore-util 1.5.0",
"ethcore-util 1.5.3",
"ethsync 1.5.0",
"evmbin 1.5.0",
"fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.9.14 (registry+https://github.com/rust-lang/crates.io-index)",
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -72,6 +73,11 @@ name = "ansi_term"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "antidote"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "app_dirs"
version = "1.1.1"
@@ -292,6 +298,11 @@ name = "dtoa"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "dtoa"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "elastic-array"
version = "0.6.0"
@@ -362,7 +373,7 @@ dependencies = [
"ethcore-ipc 1.5.0",
"ethcore-ipc-codegen 1.5.0",
"ethcore-ipc-nano 1.5.0",
"ethcore-util 1.5.0",
"ethcore-util 1.5.3",
"ethjson 0.1.0",
"ethkey 0.2.0",
"ethstore 0.1.0",
@@ -390,6 +401,7 @@ version = "0.1.2"
dependencies = [
"bigint 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.16 (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)",
]
@@ -409,12 +421,12 @@ dependencies = [
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-devtools 1.5.0",
"ethcore-rpc 1.5.0",
"ethcore-util 1.5.0",
"ethcore-util 1.5.3",
"fetch 0.1.0",
"futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
"jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc.git)",
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=mio-old)",
"jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc.git?branch=mio-old)",
"linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -457,7 +469,7 @@ name = "ethcore-ipc"
version = "1.5.0"
dependencies = [
"ethcore-devtools 1.5.0",
"ethcore-util 1.5.0",
"ethcore-util 1.5.3",
"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)",
]
@@ -504,7 +516,7 @@ dependencies = [
"ethcore-ipc 1.5.0",
"ethcore-ipc-codegen 1.5.0",
"ethcore-ipc-nano 1.5.0",
"ethcore-util 1.5.0",
"ethcore-util 1.5.3",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
"semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -519,7 +531,7 @@ dependencies = [
"ethcore-ipc 1.5.0",
"ethcore-ipc-codegen 1.5.0",
"ethcore-network 1.5.0",
"ethcore-util 1.5.0",
"ethcore-util 1.5.3",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.1.0",
"smallvec 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -531,7 +543,7 @@ name = "ethcore-logger"
version = "1.5.0"
dependencies = [
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-util 1.5.0",
"ethcore-util 1.5.3",
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -547,7 +559,7 @@ dependencies = [
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-devtools 1.5.0",
"ethcore-io 1.5.0",
"ethcore-util 1.5.0",
"ethcore-util 1.5.3",
"ethcrypto 0.1.0",
"ethkey 0.2.0",
"igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -574,7 +586,7 @@ dependencies = [
"ethcore-devtools 1.5.0",
"ethcore-io 1.5.0",
"ethcore-ipc 1.5.0",
"ethcore-util 1.5.0",
"ethcore-util 1.5.3",
"ethcrypto 0.1.0",
"ethjson 0.1.0",
"ethkey 0.2.0",
@@ -582,10 +594,10 @@ dependencies = [
"ethsync 1.5.0",
"fetch 0.1.0",
"futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
"jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc.git)",
"jsonrpc-ipc-server 0.2.4 (git+https://github.com/ethcore/jsonrpc.git)",
"jsonrpc-macros 0.1.0 (git+https://github.com/ethcore/jsonrpc.git)",
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=mio-old)",
"jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc.git?branch=mio-old)",
"jsonrpc-ipc-server 0.2.4 (git+https://github.com/ethcore/jsonrpc.git?branch=mio-old)",
"jsonrpc-macros 0.1.0 (git+https://github.com/ethcore/jsonrpc.git?branch=mio-old)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-reactor 0.1.0",
"parity-updater 1.5.0",
@@ -609,8 +621,8 @@ dependencies = [
"ethcore-devtools 1.5.0",
"ethcore-io 1.5.0",
"ethcore-rpc 1.5.0",
"ethcore-util 1.5.0",
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
"ethcore-util 1.5.3",
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=mio-old)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-ui 1.5.0",
@@ -628,9 +640,9 @@ dependencies = [
"ethcore-ipc 1.5.0",
"ethcore-ipc-codegen 1.5.0",
"ethcore-ipc-nano 1.5.0",
"ethcore-util 1.5.0",
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
"jsonrpc-tcp-server 0.1.0 (git+https://github.com/ethcore/jsonrpc.git)",
"ethcore-util 1.5.3",
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=mio-old)",
"jsonrpc-tcp-server 0.1.0 (git+https://github.com/ethcore/jsonrpc.git?branch=mio-old)",
"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)",
"mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)",
@@ -639,7 +651,7 @@ dependencies = [
[[package]]
name = "ethcore-util"
version = "1.5.0"
version = "1.5.3"
dependencies = [
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -688,7 +700,7 @@ dependencies = [
name = "ethjson"
version = "0.1.0"
dependencies = [
"ethcore-util 1.5.0",
"ethcore-util 1.5.3",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_codegen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -744,7 +756,7 @@ dependencies = [
"ethcore-ipc-nano 1.5.0",
"ethcore-light 1.5.0",
"ethcore-network 1.5.0",
"ethcore-util 1.5.0",
"ethcore-util 1.5.3",
"ethkey 0.2.0",
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -755,6 +767,16 @@ dependencies = [
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "evmbin"
version = "1.5.0"
dependencies = [
"docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.5.0",
"ethcore-util 1.5.3",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "evmjit"
version = "1.5.0"
@@ -778,7 +800,8 @@ dependencies = [
"futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"reqwest 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"reqwest 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -879,7 +902,7 @@ dependencies = [
[[package]]
name = "hyper"
version = "0.10.0-a.0"
source = "git+https://github.com/ethcore/hyper#7d4f7fa0baddcb2b0c523f7c05855d67de94fe88"
source = "git+https://github.com/ethcore/hyper#6baea9d444dd1652220ee9b4aeadaebb3de6a955"
dependencies = [
"cookie 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -895,6 +918,35 @@ dependencies = [
"vecio 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "hyper"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "hyper-native-tls"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)",
"native-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "idna"
version = "0.1.0"
@@ -923,7 +975,7 @@ version = "1.5.0"
dependencies = [
"ethcore-ipc 1.5.0",
"ethcore-ipc-codegen 1.5.0",
"ethcore-util 1.5.0",
"ethcore-util 1.5.3",
"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)",
]
@@ -948,10 +1000,15 @@ name = "itoa"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "itoa"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "jsonrpc-core"
version = "4.0.0"
source = "git+https://github.com/ethcore/jsonrpc.git#33262d626a294a00c20435dec331058ba65e224a"
source = "git+https://github.com/ethcore/jsonrpc.git?branch=mio-old#057cc58a849d18933a9cce60a33559c7505bd7ce"
dependencies = [
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -963,10 +1020,10 @@ dependencies = [
[[package]]
name = "jsonrpc-http-server"
version = "6.1.1"
source = "git+https://github.com/ethcore/jsonrpc.git#33262d626a294a00c20435dec331058ba65e224a"
source = "git+https://github.com/ethcore/jsonrpc.git?branch=mio-old#057cc58a849d18933a9cce60a33559c7505bd7ce"
dependencies = [
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=mio-old)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -974,11 +1031,11 @@ dependencies = [
[[package]]
name = "jsonrpc-ipc-server"
version = "0.2.4"
source = "git+https://github.com/ethcore/jsonrpc.git#33262d626a294a00c20435dec331058ba65e224a"
source = "git+https://github.com/ethcore/jsonrpc.git?branch=mio-old#057cc58a849d18933a9cce60a33559c7505bd7ce"
dependencies = [
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=mio-old)",
"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)",
"mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -989,20 +1046,20 @@ dependencies = [
[[package]]
name = "jsonrpc-macros"
version = "0.1.0"
source = "git+https://github.com/ethcore/jsonrpc.git#33262d626a294a00c20435dec331058ba65e224a"
source = "git+https://github.com/ethcore/jsonrpc.git?branch=mio-old#057cc58a849d18933a9cce60a33559c7505bd7ce"
dependencies = [
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=mio-old)",
"serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "jsonrpc-tcp-server"
version = "0.1.0"
source = "git+https://github.com/ethcore/jsonrpc.git#33262d626a294a00c20435dec331058ba65e224a"
source = "git+https://github.com/ethcore/jsonrpc.git?branch=mio-old#057cc58a849d18933a9cce60a33559c7505bd7ce"
dependencies = [
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=mio-old)",
"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)",
"mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1443,7 +1500,7 @@ name = "parity-hash-fetch"
version = "1.5.0"
dependencies = [
"ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-util 1.5.0",
"ethcore-util 1.5.3",
"fetch 0.1.0",
"futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1468,9 +1525,9 @@ version = "1.4.0"
dependencies = [
"ethcore-rpc 1.5.0",
"ethcore-signer 1.5.0",
"ethcore-util 1.5.0",
"ethcore-util 1.5.3",
"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?branch=mio-old)",
"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)",
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1487,7 +1544,7 @@ name = "parity-ui"
version = "1.5.0"
dependencies = [
"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?branch=beta)",
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1501,7 +1558,7 @@ dependencies = [
[[package]]
name = "parity-ui-precompiled"
version = "1.4.0"
source = "git+https://github.com/ethcore/js-precompiled.git#66ef86da6f741953694199813e1eb6b76b3f5b5f"
source = "git+https://github.com/ethcore/js-precompiled.git?branch=beta#24137b8138964ea1cbada452aa56f8f5fe24bcaa"
dependencies = [
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1514,7 +1571,7 @@ dependencies = [
"ethcore 1.5.0",
"ethcore-ipc 1.5.0",
"ethcore-ipc-codegen 1.5.0",
"ethcore-util 1.5.0",
"ethcore-util 1.5.3",
"ethsync 1.5.0",
"ipc-common-types 1.5.0",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1714,15 +1771,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "reqwest"
version = "0.2.0"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"hyper 0.9.14 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper-native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"native-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_urlencoded 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_urlencoded 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1794,7 +1851,7 @@ version = "1.4.0"
dependencies = [
"ethcore-bigint 0.1.2",
"ethcore-rpc 1.5.0",
"ethcore-util 1.5.0",
"ethcore-util 1.5.3",
"futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-rpc-client 1.4.0",
"rpassword 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1908,6 +1965,11 @@ name = "serde"
version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde_codegen"
version = "0.8.4"
@@ -1942,11 +2004,24 @@ dependencies = [
]
[[package]]
name = "serde_urlencoded"
version = "0.3.0"
name = "serde_json"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)",
"dtoa 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"itoa 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_urlencoded"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dtoa 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"itoa 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -2363,6 +2438,7 @@ dependencies = [
"checksum advapi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e06588080cb19d0acb6739808aafa5f26bfb2ca015b2b6370028b44cf7cb8a9a"
"checksum aho-corasick 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "67077478f0a03952bed2e6786338d400d40c25e9836e08ad50af96607317fd03"
"checksum ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1f46cd5b1d660c938e3f92dfe7a73d832b3281479363dd0cd9c1c2fbf60f7962"
"checksum antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5"
"checksum app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7d1c0d48a81bbb13043847f957971f4d87c81542d80ece5e84ba3cba4058fd4"
"checksum arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "16e3bdb2f54b3ace0285975d59a97cf8ed3855294b2b6bc651fcf22a9c352975"
"checksum aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07d344974f0a155f091948aa389fb1b912d3a58414fbdb9c8d446d193ee3496a"
@@ -2392,6 +2468,7 @@ dependencies = [
"checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf"
"checksum docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4cc0acb4ce0828c6a5a11d47baa432fe885881c27428c3a4e473e454ffe57a76"
"checksum dtoa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0dd841b58510c9618291ffa448da2e4e0f699d984d436122372f446dae62263d"
"checksum dtoa 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5edd69c67b2f8e0911629b7e6b8a34cb3956613cd7c6e6414966dee349c2db4f"
"checksum elastic-array 0.6.0 (git+https://github.com/ethcore/elastic-array)" = "<none>"
"checksum env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aba65b63ffcc17ffacd6cf5aa843da7c5a25e3bd4bbe0b7def8b214e411250e5"
"checksum eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)" = "<none>"
@@ -2408,17 +2485,20 @@ dependencies = [
"checksum hpack 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d2da7d3a34cf6406d9d700111b8eafafe9a251de41ae71d8052748259343b58"
"checksum httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46534074dbb80b070d60a5cb8ecadd8963a00a438ae1a95268850a7ef73b67ae"
"checksum hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)" = "<none>"
"checksum hyper 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)" = "220407e5a263f110ec30a071787c9535918fdfc97def5680c90013c3f30c38c1"
"checksum hyper 0.9.14 (registry+https://github.com/rust-lang/crates.io-index)" = "bcb3fc65554155980167fb821d05c7c66177f92464976c0b676a19d9e03387a7"
"checksum hyper-native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "afe68f772f0497a7205e751626bb8e1718568b58534b6108c73a74ef80483409"
"checksum idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1053236e00ce4f668aeca4a769a09b3bf5a682d802abd6f3cb39374f6b162c11"
"checksum igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c8c12b1795b8b168f577c45fa10379b3814dcb11b7ab702406001f0d63f40484"
"checksum isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7408a548dc0e406b7912d9f84c261cc533c1866e047644a811c133c56041ac0c"
"checksum itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "086e1fa5fe48840b1cfdef3a20c7e3115599f8d5c4c87ef32a794a7cdd184d76"
"checksum itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae3088ea4baeceb0284ee9eea42f591226e6beaecf65373e41b38d95a1b8e7a1"
"checksum jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)" = "<none>"
"checksum jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc.git)" = "<none>"
"checksum jsonrpc-ipc-server 0.2.4 (git+https://github.com/ethcore/jsonrpc.git)" = "<none>"
"checksum jsonrpc-macros 0.1.0 (git+https://github.com/ethcore/jsonrpc.git)" = "<none>"
"checksum jsonrpc-tcp-server 0.1.0 (git+https://github.com/ethcore/jsonrpc.git)" = "<none>"
"checksum itoa 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "91fd9dc2c587067de817fec4ad355e3818c3d893a78cab32a0a474c7a15bb8d5"
"checksum jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=mio-old)" = "<none>"
"checksum jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc.git?branch=mio-old)" = "<none>"
"checksum jsonrpc-ipc-server 0.2.4 (git+https://github.com/ethcore/jsonrpc.git?branch=mio-old)" = "<none>"
"checksum jsonrpc-macros 0.1.0 (git+https://github.com/ethcore/jsonrpc.git?branch=mio-old)" = "<none>"
"checksum jsonrpc-tcp-server 0.1.0 (git+https://github.com/ethcore/jsonrpc.git?branch=mio-old)" = "<none>"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
"checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f"
@@ -2465,7 +2545,7 @@ dependencies = [
"checksum openssl-sys 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d2845e841700e7b04282ceaa115407ea84e0db918ae689ad9ceb6f06fa6046bd"
"checksum owning_ref 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8d91377085359426407a287ab16884a0111ba473aa6844ff01d4ec20ce3d75e7"
"checksum parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98378dec0a185da2b7180308752f0bad73aaa949c3e0a3b0528d0e067945f7ab"
"checksum parity-ui-precompiled 1.4.0 (git+https://github.com/ethcore/js-precompiled.git)" = "<none>"
"checksum parity-ui-precompiled 1.4.0 (git+https://github.com/ethcore/js-precompiled.git?branch=beta)" = "<none>"
"checksum parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e1435e7a2a00dfebededd6c6bdbd54008001e94b4a2aadd6aef0dc4c56317621"
"checksum parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb1b97670a2ffadce7c397fb80a3d687c4f3060140b885621ef1653d0e5d5068"
"checksum phf 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "447d9d45f2e0b4a9b532e808365abf18fc211be6ca217202fcd45236ef12f026"
@@ -2489,7 +2569,7 @@ dependencies = [
"checksum rayon 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "655df67c314c30fa3055a365eae276eb88aa4f3413a352a1ab32c1320eda41ea"
"checksum regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)" = "b4329b8928a284580a1c63ec9d846b12f6d3472317243ff7077aff11f23f2b29"
"checksum regex-syntax 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "841591b1e05609a643e3b4d0045fce04f701daba7151ddcd3ad47b080693d5a9"
"checksum reqwest 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83186fee0d4dbeb95e610b77b05b05cf5b31703dd375222acb74c3dff4be957c"
"checksum reqwest 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bef9ed8fdfcc30947d6b774938dc0c3f369a474efe440df2c7f278180b2d2e6"
"checksum rocksdb 0.4.5 (git+https://github.com/ethcore/rust-rocksdb)" = "<none>"
"checksum rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)" = "<none>"
"checksum rotor 0.6.3 (git+https://github.com/ethcore/rotor)" = "<none>"
@@ -2508,10 +2588,12 @@ dependencies = [
"checksum semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae2ff60ecdb19c255841c066cbfa5f8c2a4ada1eb3ae47c77ab6667128da71f5"
"checksum semver-parser 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e88e43a5a74dd2a11707f9c21dfd4a423c66bd871df813227bb0a3e78f3a1ae9"
"checksum serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)" = "58a19c0871c298847e6b68318484685cd51fa5478c0c905095647540031356e5"
"checksum serde 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4d8f810025e9d09c4eaa49c16eaf878f34a947889e878cd7d3b5bef3197cc119"
"checksum serde_codegen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e422ae53d7933f59c6ff57e7b5870b5c9094b1f473f78ec33d89f8a692c3ec02"
"checksum serde_codegen_internals 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f877e2781ed0a323295d1c9f0e26556117b5a11489fc47b1848dfb98b3173d21"
"checksum serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e10f8a9d94b06cf5d3bef66475f04c8ff90950f1be7004c357ff9472ccbaebc"
"checksum serde_urlencoded 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "53d4ebaa8d1d4f90d1b63dfca81ccd98ac20e1e479dbae393cbaf60f6fecd8d8"
"checksum serde_json 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fea48f4d4df4e620e3c81fd2bf28c93dd0d266361a76bac4f254b71f0e13f3cd"
"checksum serde_urlencoded 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a81f15da4b9780e1524697f73b09076b6e42298ef673bead9ca8f848b334ef84"
"checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c"
"checksum shell32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72f20b8f3c060374edb8046591ba28f62448c369ccbdc7b02075103fb3a9e38d"
"checksum siphasher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c44e42fa187b5a8782489cf7740cc27c3125806be2bf33563cf5e02e9533fcd"

View File

@@ -1,7 +1,7 @@
[package]
description = "Parity Ethereum client"
name = "parity"
version = "1.5.0"
version = "1.5.3"
license = "GPL-3.0"
authors = ["Parity Technologies <admin@parity.io>"]
build = "build.rs"
@@ -47,6 +47,7 @@ ethcore-ipc-hypervisor = { path = "ipc/hypervisor" }
ethcore-logger = { path = "logger" }
ethcore-stratum = { path = "stratum" }
ethcore-dapps = { path = "dapps", optional = true }
evmbin = { path = "evmbin" }
rpc-cli = { path = "rpc_cli" }
parity-rpc-client = { path = "rpc_client" }
ethcore-light = { path = "ethcore/light" }

View File

@@ -13,8 +13,8 @@ rand = "0.3"
log = "0.3"
env_logger = "0.3"
futures = "0.1"
jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git" }
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc.git" }
jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git", branch="mio-old" }
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc.git", branch="mio-old" }
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
unicase = "1.3"
url = "1.0"

View File

@@ -38,6 +38,7 @@ pub const RPC_PATH: &'static str = "rpc";
pub const API_PATH: &'static str = "api";
pub const UTILS_PATH: &'static str = "parity-utils";
pub const WEB_PATH: &'static str = "web";
pub const URL_REFERER: &'static str = "__referer=";
pub fn utils() -> Box<Endpoint> {
Box::new(PageEndpoint::with_prefix(parity_ui::App::default(), UTILS_PATH.to_owned()))

View File

@@ -34,7 +34,7 @@ use endpoint::EndpointPath;
use handlers::{ContentHandler, StreamingHandler};
use page::{LocalPageEndpoint, PageHandlerWaiting};
const FETCH_TIMEOUT: u64 = 30;
const FETCH_TIMEOUT: u64 = 300;
pub enum ValidatorResponse {
Local(LocalPageEndpoint),

View File

@@ -312,7 +312,7 @@ impl Server {
let special = Arc::new({
let mut special = HashMap::new();
special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, panic_handler.clone()));
special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, cors_domains.clone(), panic_handler.clone()));
special.insert(router::SpecialEndpoint::Utils, apps::utils());
special.insert(
router::SpecialEndpoint::Api,

View File

@@ -56,7 +56,6 @@ pub struct Router<A: Authorization + 'static> {
impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
fn on_request(&mut self, req: server::Request<HttpStream>) -> Next {
// Choose proper handler depending on path / domain
let url = handlers::extract_url(&req);
let endpoint = extract_endpoint(&url);
@@ -92,8 +91,7 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
self.handler = match (endpoint.0, endpoint.1, referer) {
// Handle invalid web requests that we can recover from
(ref path, SpecialEndpoint::None, Some((ref referer, ref referer_url)))
if is_get_request
&& referer.app_id == apps::WEB_PATH
if referer.app_id == apps::WEB_PATH
&& self.endpoints.contains_key(apps::WEB_PATH)
&& !is_web_endpoint(path)
=>
@@ -225,10 +223,28 @@ fn extract_referer_endpoint(req: &server::Request<HttpStream>) -> Option<(Endpoi
let url = referer.and_then(|referer| Url::parse(&referer.0).ok());
url.and_then(|url| {
let option = Some(url);
extract_endpoint(&option).0.map(|endpoint| (endpoint, option.expect("Just wrapped; qed")))
extract_url_referer_endpoint(&option).or_else(|| {
extract_endpoint(&option).0.map(|endpoint| (endpoint, option.expect("Just wrapped; qed")))
})
})
}
fn extract_url_referer_endpoint(url: &Option<Url>) -> Option<(EndpointPath, Url)> {
let query = url.as_ref().and_then(|url| url.query.as_ref());
match (url, query) {
(&Some(ref url), Some(ref query)) if query.starts_with(apps::URL_REFERER) => {
let referer_url = format!("http://{}:{}/{}", url.host, url.port, &query[apps::URL_REFERER.len()..]);
debug!(target: "dapps", "Recovering referer from query parameter: {}", referer_url);
let referer_url = Url::parse(&referer_url).ok();
extract_endpoint(&referer_url).0.map(|endpoint| {
(endpoint, referer_url.expect("Endpoint returned only when url `is_some`").clone())
})
},
_ => None,
}
}
fn extract_endpoint(url: &Option<Url>) -> (Option<EndpointPath>, SpecialEndpoint) {
fn special_endpoint(url: &Url) -> SpecialEndpoint {
if url.path.len() <= 1 {

View File

@@ -21,11 +21,15 @@ use jsonrpc_core::{IoHandler, ResponseHandler, Request, Response};
use jsonrpc_http_server::{ServerHandler, PanicHandler, AccessControlAllowOrigin, RpcHandler};
use endpoint::{Endpoint, EndpointPath, Handler};
pub fn rpc(handler: Arc<IoHandler>, panic_handler: Arc<Mutex<Option<Box<Fn() -> () + Send>>>>) -> Box<Endpoint> {
pub fn rpc(
handler: Arc<IoHandler>,
cors_domains: Vec<String>,
panic_handler: Arc<Mutex<Option<Box<Fn() -> () + Send>>>>,
) -> Box<Endpoint> {
Box::new(RpcEndpoint {
handler: Arc::new(RpcMiddleware::new(handler)),
panic_handler: panic_handler,
cors_domain: None,
cors_domain: Some(cors_domains.into_iter().map(AccessControlAllowOrigin::Value).collect()),
// NOTE [ToDr] We don't need to do any hosts validation here. It's already done in router.
allowed_hosts: None,
})
@@ -60,7 +64,12 @@ impl RpcMiddleware {
fn new(handler: Arc<IoHandler>) -> Self {
RpcMiddleware {
handler: handler,
methods: vec!["eth_accounts".into(), "parity_accountsInfo".into()],
methods: vec![
"eth_accounts".into(),
"eth_coinbase".into(),
"parity_accountsInfo".into(),
"parity_defaultAccount".into(),
],
}
}

View File

@@ -68,6 +68,7 @@ impl<F: Fetch> Endpoint for Web<F> {
struct WebInstaller {
embeddable_on: Embeddable,
referer: String,
}
impl ContentValidator for WebInstaller {
@@ -84,7 +85,12 @@ impl ContentValidator for WebInstaller {
self.embeddable_on.clone(),
);
if is_html {
handler.set_initial_content(&format!(r#"<script src="/{}/inject.js"></script>"#, apps::UTILS_PATH));
handler.set_initial_content(&format!(
r#"<script src="/{}/inject.js"></script><script>history.replaceState({{}}, "", "/?{}{}")</script>"#,
apps::UTILS_PATH,
apps::URL_REFERER,
&self.referer,
));
}
Ok(ValidatorResponse::Streaming(handler))
}
@@ -108,7 +114,7 @@ struct WebHandler<F: Fetch> {
}
impl<F: Fetch> WebHandler<F> {
fn extract_target_url(&self, url: Option<Url>) -> Result<String, State<F>> {
fn extract_target_url(&self, url: Option<Url>) -> Result<(String, String), State<F>> {
let (path, query) = match url {
Some(url) => (url.path, url.query),
None => {
@@ -157,7 +163,7 @@ impl<F: Fetch> WebHandler<F> {
None => "".into(),
};
Ok(format!("{}://{}{}", protocol, path[idx + 2..].join("/"), query))
Ok((format!("{}://{}{}", protocol, path[idx + 2..].join("/"), query), path[0..].join("/")))
}
}
@@ -166,7 +172,7 @@ impl<F: Fetch> server::Handler<net::HttpStream> for WebHandler<F> {
let url = extract_url(&request);
// First extract the URL (reject invalid URLs)
let target_url = match self.extract_target_url(url) {
let (target_url, referer) = match self.extract_target_url(url) {
Ok(url) => url,
Err(error) => {
self.state = error;
@@ -180,6 +186,7 @@ impl<F: Fetch> server::Handler<net::HttpStream> for WebHandler<F> {
self.control.clone(),
WebInstaller {
embeddable_on: self.embeddable_on.clone(),
referer: referer,
},
self.embeddable_on.clone(),
self.remote.clone(),

View File

@@ -11,7 +11,7 @@ rustc_version = "0.1"
[dependencies]
parity-ui-dev = { path = "../../js", optional = true }
parity-ui-precompiled = { git = "https://github.com/ethcore/js-precompiled.git", optional = true }
parity-ui-precompiled = { git = "https://github.com/ethcore/js-precompiled.git", branch = "beta", optional = true }
[features]
no-precompiled-js = ["parity-ui-dev"]

79
docker/hub/Dockerfile Normal file
View File

@@ -0,0 +1,79 @@
FROM ubuntu:14.04
MAINTAINER Parity Technologies <devops@parity.io>
WORKDIR /build
# install tools and dependencies
RUN apt-get update && \
apt-get install -y --force-yes --no-install-recommends \
# make
build-essential \
# add-apt-repository
software-properties-common \
make \
curl \
wget \
git \
g++ \
gcc \
libc6 \
libc6-dev \
binutils \
file \
openssl \
libssl-dev \
libudev-dev \
pkg-config \
dpkg-dev \
# evmjit dependencies
zlib1g-dev \
libedit-dev \
libudev-dev &&\
# cmake and llvm ppa's. then update ppa's
add-apt-repository -y "ppa:george-edison55/cmake-3.x" && \
add-apt-repository "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.7 main" && \
apt-get update && \
apt-get install -y --force-yes cmake llvm-3.7-dev && \
# install evmjit
git clone https://github.com/debris/evmjit && \
cd evmjit && \
mkdir build && cd build && \
cmake .. && make && make install && cd && \
# install rustup
curl https://sh.rustup.rs -sSf | sh -s -- -y && \
# rustup directory
PATH=/root/.cargo/bin:$PATH && \
# show backtraces
RUST_BACKTRACE=1 && \
# build parity
cd /build&&git clone https://github.com/ethcore/parity && \
cd parity && \
git pull&& \
git checkout $CI_BUILD_REF_NAME && \
cargo build --verbose --release --features final && \
#ls /build/parity/target/release/parity && \
strip /build/parity/target/release/parity && \
file /build/parity/target/release/parity&&mkdir -p /parity&& cp /build/parity/target/release/parity /parity&&\
#cleanup Docker image
rm -rf /root/.cargo&&rm -rf /root/.multirust&&rm -rf /root/.rustup&&rm -rf /build&&\
apt-get purge -y \
# make
build-essential \
# add-apt-repository
software-properties-common \
make \
curl \
wget \
git \
g++ \
gcc \
binutils \
file \
pkg-config \
dpkg-dev \
# evmjit dependencies
zlib1g-dev \
libedit-dev \
cmake llvm-3.7-dev&&\
rm -rf /var/lib/apt/lists/*
# setup ENTRYPOINT
EXPOSE 8080 8545 8180
ENTRYPOINT ["/parity/parity"]

View File

@@ -1,14 +1,19 @@
FROM ubuntu:14.04
WORKDIR /build
# install tools and dependencies
RUN apt-get update && \
apt-get install -y \
g++ \
build-essential \
curl \
git \
file \
binutils
RUN apt-get -y update && \
apt-get install -y --force-yes --no-install-recommends g++ gcc libc6 libc6-dev \
python binutils curl git make file ca-certificates zip dpkg-dev rhash openssl build-essential pkg-config libssl-dev
# install AWS CLI
RUN curl -O https://bootstrap.pypa.io/get-pip.py && \
python get-pip.py && \
pip install awscli
# install nodejs
RUN curl -sL https://deb.nodesource.com/setup_6.x | bash - && \
apt-get install -y nodejs && \
apt-get clean
# install rustup
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
@@ -30,7 +35,7 @@ RUN git clone https://github.com/ethcore/parity && \
cd parity && \
git checkout beta && \
git pull && \
cargo build --release --verbose && \
cargo build --features final --release --verbose && \
ls /build/parity/target/release/parity && \
strip /build/parity/target/release/parity

View File

@@ -197,6 +197,7 @@ impl HeaderChain {
pub fn get_header(&self, id: BlockId) -> Option<Bytes> {
match id {
BlockId::Earliest | BlockId::Number(0) => Some(self.genesis_header.clone()),
BlockId::Latest if self.headers.read().is_empty() => Some(self.genesis_header.clone()),
BlockId::Hash(hash) => self.headers.read().get(&hash).map(|x| x.to_vec()),
BlockId::Number(num) => {
if self.best_block.read().number < num { return None }

View File

@@ -21,8 +21,9 @@ use ethcore::block_status::BlockStatus;
use ethcore::client::ClientReport;
use ethcore::ids::BlockId;
use ethcore::header::Header;
use ethcore::views::HeaderView;
use ethcore::verification::queue::{self, HeaderQueue};
use ethcore::transaction::PendingTransaction;
use ethcore::transaction::{PendingTransaction, Condition as TransactionCondition};
use ethcore::blockchain_info::BlockChainInfo;
use ethcore::spec::Spec;
use ethcore::service::ClientIoMessage;
@@ -34,6 +35,7 @@ use util::{Bytes, Mutex, RwLock};
use provider::Provider;
use request;
use time;
use self::header_chain::HeaderChain;
@@ -109,7 +111,11 @@ impl Client {
let best_num = self.chain.best_block().number;
self.tx_pool.lock()
.values()
.filter(|t| t.min_block.as_ref().map_or(true, |x| x <= &best_num))
.filter(|t| match t.condition {
Some(TransactionCondition::Number(ref x)) => x <= &best_num,
Some(TransactionCondition::Timestamp(ref x)) => *x <= time::get_time().sec as u64,
None => true,
})
.cloned()
.collect()
}
@@ -134,6 +140,7 @@ impl Client {
genesis_hash: genesis_hash,
best_block_hash: best_block.hash,
best_block_number: best_block.number,
best_block_timestamp: HeaderView::new(&self.chain.get_header(BlockId::Latest).expect("Latest hash is always in the chain")).timestamp(),
ancient_block_hash: if first_block.is_some() { Some(genesis_hash) } else { None },
ancient_block_number: if first_block.is_some() { Some(0) } else { None },
first_block_hash: first_block.as_ref().map(|first| first.hash),

View File

@@ -24,7 +24,9 @@
"accountStartNonce": "0x0",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x3"
"networkID" : "0x3",
"forkBlock": 333922,
"forkCanonHash": "0x8737eb141d4f05db57af63fc8d3b4d4d8f9cddb0c4e1ab855de8c288fdc1924f"
},
"genesis": {
"seal": {
@@ -42,7 +44,10 @@
},
"nodes": [
"enode://a22f0977ce02653bf95e38730106356342df48b5222e2c2a1a6f9ef34769bf593bae9ca0a888cf60839edd52efc1b6e393c63a57d76f4c4fe14e641f1f9e637e@128.199.55.137:30303",
"enode://012239fccf3ff1d92b036983a430cb6705c6528c96c0354413f8854802138e5135c084ab36e7c54efb621c46728df8c3a6f4c1db9bb48a1330efe3f82f2dd7a6@52.169.94.142:30303"
"enode://012239fccf3ff1d92b036983a430cb6705c6528c96c0354413f8854802138e5135c084ab36e7c54efb621c46728df8c3a6f4c1db9bb48a1330efe3f82f2dd7a6@52.169.94.142:30303",
"enode://1462682e4b7ba2258346d55e25e5b9d264b0db40cee12bdfba4e72b1d7050350ea954c006e9106dd96a128e6e0bd6dffb17eed51f9f99bf7f9cdadfeaf8da4ff@51.15.61.253:30303",
"enode://98fbb020c799ae39a828bd75dc2bd5d4721539faf317076b275f91182a5c8900b592e8abfdddceae674a7c3bb40ea00a6ca9ccb7805ab58c4b7b29c61c8f7239@51.15.62.44:30303",
"enode://d801dd4e3d15a8bf785931add164bd9c313e3f6b5749d9302b311f2b48064cba5c86c32b1302c27cd983fc89ae07d4d306dd1197610835b8782e95dfb1b3f9ea@51.15.43.255:30303"
],
"accounts": {
"0000000000000000000000000000000000000001": { "balance": "1", "nonce": "0", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },

View File

@@ -24,6 +24,8 @@ pub struct BestBlock {
pub hash: H256,
/// Best block number.
pub number: BlockNumber,
/// Best block timestamp.
pub timestamp: u64,
/// Best block total difficulty.
pub total_difficulty: U256,
/// Best block uncompressed bytes

View File

@@ -490,6 +490,7 @@ impl BlockChain {
let best_block_number = bc.block_number(&best_block_hash).unwrap();
let best_block_total_difficulty = bc.block_details(&best_block_hash).unwrap().total_difficulty;
let best_block_rlp = bc.block(&best_block_hash).unwrap().into_inner();
let best_block_timestamp = BlockView::new(&best_block_rlp).header().timestamp();
let raw_first = bc.db.get(db::COL_EXTRA, b"first").unwrap().map(|v| v.to_vec());
let mut best_ancient = bc.db.get(db::COL_EXTRA, b"ancient").unwrap().map(|h| H256::from_slice(&h));
@@ -538,6 +539,7 @@ impl BlockChain {
number: best_block_number,
total_difficulty: best_block_total_difficulty,
hash: best_block_hash,
timestamp: best_block_timestamp,
block: best_block_rlp,
};
@@ -590,6 +592,7 @@ impl BlockChain {
number: extras.number - 1,
total_difficulty: best_block_total_difficulty,
hash: hash,
timestamp: BlockView::new(&best_block_rlp).header().timestamp(),
block: best_block_rlp,
};
// update parent extras
@@ -743,6 +746,7 @@ impl BlockChain {
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
info: info,
timestamp: header.timestamp(),
block: bytes
}, is_best);
@@ -791,6 +795,7 @@ impl BlockChain {
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
info: info,
timestamp: header.timestamp(),
block: bytes,
}, is_best);
true
@@ -855,6 +860,7 @@ impl BlockChain {
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
info: info.clone(),
timestamp: header.timestamp(),
block: bytes,
}, true);
@@ -926,6 +932,7 @@ impl BlockChain {
hash: update.info.hash,
number: update.info.number,
total_difficulty: update.info.total_difficulty,
timestamp: update.timestamp,
block: update.block.to_vec(),
});
},
@@ -1211,6 +1218,11 @@ impl BlockChain {
self.best_block.read().number
}
/// Get best block timestamp.
pub fn best_block_timestamp(&self) -> u64 {
self.best_block.read().timestamp
}
/// Get best block total difficulty.
pub fn best_block_total_difficulty(&self) -> U256 {
self.best_block.read().total_difficulty
@@ -1298,6 +1310,7 @@ impl BlockChain {
genesis_hash: self.genesis_hash(),
best_block_hash: best_block.hash.clone(),
best_block_number: best_block.number,
best_block_timestamp: best_block.timestamp,
first_block_hash: self.first_block(),
first_block_number: From::from(self.first_block_number()),
ancient_block_hash: best_ancient_block.as_ref().map(|b| b.hash.clone()),
@@ -1329,6 +1342,7 @@ mod tests {
use transaction::{Transaction, Action};
use log_entry::{LogEntry, LocalizedLogEntry};
use spec::Spec;
use ethkey::Secret;
fn new_db(path: &str) -> Arc<Database> {
Arc::new(Database::open(&DatabaseConfig::with_columns(::db::NUM_COLUMNS), path).unwrap())
@@ -1467,6 +1481,10 @@ mod tests {
// TODO: insert block that already includes one of them as an uncle to check it's not allowed.
}
fn secret() -> Secret {
Secret::from_slice(&"".sha3()).unwrap()
}
#[test]
fn test_fork_transaction_addresses() {
let mut canon_chain = ChainGenerator::default();
@@ -1482,7 +1500,7 @@ mod tests {
action: Action::Create,
value: 100.into(),
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
}.sign(&"".sha3(), None);
}.sign(&secret(), None);
let b1a = canon_chain
@@ -1546,7 +1564,7 @@ mod tests {
action: Action::Create,
value: 100.into(),
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
}.sign(&"".sha3(), None);
}.sign(&secret(), None);
let t2 = Transaction {
nonce: 1.into(),
@@ -1555,7 +1573,7 @@ mod tests {
action: Action::Create,
value: 100.into(),
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
}.sign(&"".sha3(), None);
}.sign(&secret(), None);
let t3 = Transaction {
nonce: 2.into(),
@@ -1564,7 +1582,7 @@ mod tests {
action: Action::Create,
value: 100.into(),
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
}.sign(&"".sha3(), None);
}.sign(&secret(), None);
let b1a = canon_chain
.with_transaction(t1.clone())
@@ -1870,7 +1888,7 @@ mod tests {
action: Action::Create,
value: 101.into(),
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
}.sign(&"".sha3(), None);
}.sign(&secret(), None);
let t2 = Transaction {
nonce: 0.into(),
gas_price: 0.into(),
@@ -1878,7 +1896,7 @@ mod tests {
action: Action::Create,
value: 102.into(),
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
}.sign(&"".sha3(), None);
}.sign(&secret(), None);
let t3 = Transaction {
nonce: 0.into(),
gas_price: 0.into(),
@@ -1886,7 +1904,7 @@ mod tests {
action: Action::Create,
value: 103.into(),
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
}.sign(&"".sha3(), None);
}.sign(&secret(), None);
let tx_hash1 = t1.hash();
let tx_hash2 = t2.hash();
let tx_hash3 = t3.hash();

View File

@@ -9,6 +9,8 @@ use super::extras::{BlockDetails, BlockReceipts, TransactionAddress, LogGroupPos
pub struct ExtrasUpdate<'a> {
/// Block info.
pub info: BlockInfo,
/// Block timestamp.
pub timestamp: u64,
/// Current block uncompressed rlp bytes
pub block: &'a [u8],
/// Modified block hashes.

View File

@@ -873,6 +873,83 @@ impl BlockChainClient for Client {
Ok(ret)
}
fn estimate_gas(&self, t: &SignedTransaction, block: BlockId) -> Result<U256, CallError> {
const UPPER_CEILING: u64 = 1_000_000_000_000u64;
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: UPPER_CEILING.into(),
};
// that's just a copy of the state.
let 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 options = TransactOptions { tracing: true, vm_tracing: false, check_nonce: false };
let mut tx = t.clone();
let mut cond = |gas| {
tx.gas = gas;
let mut state = original_state.clone();
let needed_balance = tx.value + tx.gas * tx.gas_price;
if balance < needed_balance {
// give the sender a sufficient balance
state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty);
}
Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm)
.transact(&tx, options.clone())
.map(|r| r.exception.is_none())
.unwrap_or(false)
};
let mut upper = header.gas_limit();
if !cond(upper) {
// impossible at block gas limit - try `UPPER_CEILING` instead.
// TODO: consider raising limit by powers of two.
upper = UPPER_CEILING.into();
if !cond(upper) {
trace!(target: "estimate_gas", "estimate_gas failed with {}", upper);
return Err(CallError::Execution(ExecutionError::Internal))
}
}
let lower = t.gas_required(&self.engine.schedule(&env_info)).into();
if cond(lower) {
trace!(target: "estimate_gas", "estimate_gas succeeded with {}", lower);
return Ok(lower)
}
/// Find transition point between `lower` and `upper` where `cond` changes from `false` to `true`.
/// Returns the lowest value between `lower` and `upper` for which `cond` returns true.
/// We assert: `cond(lower) = false`, `cond(upper) = true`
fn binary_chop<F>(mut lower: U256, mut upper: U256, mut cond: F) -> U256 where F: FnMut(U256) -> bool {
while upper - lower > 1.into() {
let mid = (lower + upper) / 2.into();
trace!(target: "estimate_gas", "{} .. {} .. {}", lower, mid, upper);
let c = cond(mid);
match c {
true => upper = mid,
false => lower = mid,
};
trace!(target: "estimate_gas", "{} => {} .. {}", c, lower, upper);
}
upper
}
// binary chop to non-excepting call with gas somewhere between 21000 and block gas limit
trace!(target: "estimate_gas", "estimate_gas chopping {} .. {}", lower, upper);
Ok(binary_chop(lower, upper, cond))
}
fn replay(&self, id: TransactionId, analytics: CallAnalytics) -> Result<Executed, CallError> {
let address = self.transaction_address(id).ok_or(CallError::TransactionNotFound)?;
let header = self.block_header(BlockId::Hash(address.block_hash)).ok_or(CallError::StatePruned)?;
@@ -1305,7 +1382,11 @@ impl BlockChainClient for Client {
}
fn ready_transactions(&self) -> Vec<PendingTransaction> {
self.miner.ready_transactions(self.chain.read().best_block_number())
let (number, timestamp) = {
let chain = self.chain.read();
(chain.best_block_number(), chain.best_block_timestamp())
};
self.miner.ready_transactions(number, timestamp)
}
fn queue_consensus_message(&self, message: Bytes) {
@@ -1594,7 +1675,7 @@ mod tests {
use util::Hashable;
// given
let key = KeyPair::from_secret("test".sha3()).unwrap();
let key = KeyPair::from_secret_slice(&"test".sha3()).unwrap();
let secret = key.secret();
let block_number = 1;

View File

@@ -242,7 +242,7 @@ impl TestBlockChainClient {
value: U256::from(100),
data: "3331600055".from_hex().unwrap(),
gas: U256::from(100_000),
gas_price: U256::one(),
gas_price: U256::from(200_000_000_000u64),
nonce: U256::zero()
};
let signed_tx = tx.sign(keypair.secret(), None);
@@ -300,22 +300,29 @@ impl TestBlockChainClient {
}
}
/// Inserts a transaction to miners transactions queue.
pub fn insert_transaction_to_queue(&self) {
/// Inserts a transaction with given gas price to miners transactions queue.
pub fn insert_transaction_with_gas_price_to_queue(&self, gas_price: U256) -> H256 {
let keypair = Random.generate().unwrap();
let tx = Transaction {
action: Action::Create,
value: U256::from(100),
data: "3331600055".from_hex().unwrap(),
gas: U256::from(100_000),
gas_price: U256::one(),
gas_price: gas_price,
nonce: U256::zero()
};
let signed_tx = tx.sign(keypair.secret(), None);
self.set_balance(signed_tx.sender().unwrap(), 10_000_000.into());
let res = self.miner.import_external_transactions(self, vec![signed_tx]);
self.set_balance(signed_tx.sender().unwrap(), 10_000_000_000_000_000_000u64.into());
let hash = signed_tx.hash();
let res = self.miner.import_external_transactions(self, vec![signed_tx.into()]);
let res = res.into_iter().next().unwrap().expect("Successful import");
assert_eq!(res, TransactionImportResult::Current);
hash
}
/// Inserts a transaction to miners transactions queue.
pub fn insert_transaction_to_queue(&self) -> H256 {
self.insert_transaction_with_gas_price_to_queue(U256::from(20_000_000_000u64))
}
/// Set reported history size.
@@ -379,6 +386,10 @@ impl BlockChainClient for TestBlockChainClient {
self.execution_result.read().clone().unwrap()
}
fn estimate_gas(&self, _t: &SignedTransaction, _block: BlockId) -> Result<U256, CallError> {
Ok(21000.into())
}
fn replay(&self, _id: TransactionId, _analytics: CallAnalytics) -> Result<Executed, CallError> {
self.execution_result.read().clone().unwrap()
}
@@ -650,12 +661,14 @@ impl BlockChainClient for TestBlockChainClient {
}
fn chain_info(&self) -> BlockChainInfo {
let number = self.blocks.read().len() as BlockNumber - 1;
BlockChainInfo {
total_difficulty: *self.difficulty.read(),
pending_total_difficulty: *self.difficulty.read(),
genesis_hash: self.genesis_hash.clone(),
best_block_hash: self.last_hash.read().clone(),
best_block_number: self.blocks.read().len() as BlockNumber - 1,
best_block_number: number,
best_block_timestamp: number,
first_block_hash: self.first_block.read().as_ref().map(|x| x.0),
first_block_number: self.first_block.read().as_ref().map(|x| x.1),
ancient_block_hash: self.ancient_block.read().as_ref().map(|x| x.0),
@@ -690,7 +703,8 @@ impl BlockChainClient for TestBlockChainClient {
}
fn ready_transactions(&self) -> Vec<PendingTransaction> {
self.miner.ready_transactions(self.chain_info().best_block_number)
let info = self.chain_info();
self.miner.ready_transactions(info.best_block_number, info.best_block_timestamp)
}
fn signing_network_id(&self) -> Option<u64> { None }

View File

@@ -184,6 +184,9 @@ pub trait BlockChainClient : Sync + Send {
/// Makes a non-persistent transaction call.
fn call(&self, t: &SignedTransaction, block: BlockId, analytics: CallAnalytics) -> Result<Executed, CallError>;
/// Estimates how much gas will be necessary for a call.
fn estimate_gas(&self, t: &SignedTransaction, block: BlockId) -> Result<U256, CallError>;
/// Replays a given transaction for inspection.
fn replay(&self, t: TransactionId, analytics: CallAnalytics) -> Result<Executed, CallError>;
@@ -220,6 +223,7 @@ pub trait BlockChainClient : Sync + Send {
let block = self.block(BlockId::Hash(h)).expect("h is either the best_block_hash or an ancestor; qed");
let header = block.header_view();
if header.number() == 0 {
corpus.sort();
return corpus;
}
block.transaction_views().iter().foreach(|t| corpus.push(t.gas_price()));

View File

@@ -354,6 +354,7 @@ mod tests {
use env_info::EnvInfo;
use header::Header;
use error::{Error, BlockError};
use ethkey::Secret;
use rlp::encode;
use block::*;
use tests::helpers::*;
@@ -411,8 +412,8 @@ mod tests {
#[test]
fn generates_seal_and_does_not_double_propose() {
let tap = AccountProvider::transient_provider();
let addr1 = tap.insert_account("1".sha3(), "1").unwrap();
let addr2 = tap.insert_account("2".sha3(), "2").unwrap();
let addr1 = tap.insert_account(Secret::from_slice(&"1".sha3()).unwrap(), "1").unwrap();
let addr2 = tap.insert_account(Secret::from_slice(&"2".sha3()).unwrap(), "2").unwrap();
let spec = Spec::new_test_round();
let engine = &*spec.engine;
@@ -445,7 +446,7 @@ mod tests {
fn proposer_switching() {
let mut header: Header = Header::default();
let tap = AccountProvider::transient_provider();
let addr = tap.insert_account("0".sha3(), "0").unwrap();
let addr = tap.insert_account(Secret::from_slice(&"0".sha3()).unwrap(), "0").unwrap();
header.set_author(addr);
@@ -464,7 +465,7 @@ mod tests {
fn rejects_future_block() {
let mut header: Header = Header::default();
let tap = AccountProvider::transient_provider();
let addr = tap.insert_account("0".sha3(), "0").unwrap();
let addr = tap.insert_account(Secret::from_slice(&"0".sha3()).unwrap(), "0").unwrap();
header.set_author(addr);

View File

@@ -201,6 +201,7 @@ mod tests {
use error::{BlockError, Error};
use tests::helpers::*;
use account_provider::AccountProvider;
use ethkey::Secret;
use header::Header;
use spec::Spec;
use engines::Seal;
@@ -261,7 +262,7 @@ mod tests {
#[test]
fn can_generate_seal() {
let tap = AccountProvider::transient_provider();
let addr = tap.insert_account("".sha3(), "").unwrap();
let addr = tap.insert_account(Secret::from_slice(&"".sha3()).unwrap(), "").unwrap();
let spec = new_test_authority();
let engine = &*spec.engine;
@@ -281,7 +282,7 @@ mod tests {
#[test]
fn seals_internally() {
let tap = AccountProvider::transient_provider();
let authority = tap.insert_account("".sha3(), "").unwrap();
let authority = tap.insert_account(Secret::from_slice(&"".sha3()).unwrap(), "").unwrap();
let engine = new_test_authority().engine;
assert!(!engine.is_sealer(&Address::default()).unwrap());

View File

@@ -23,15 +23,35 @@ use header::Header;
use rlp::*;
use ethkey::{recover, public_to_address};
#[derive(Debug, PartialEq, Eq, Clone)]
/// Message transmitted between consensus participants.
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct ConsensusMessage {
pub vote_step: VoteStep,
pub block_hash: Option<BlockHash>,
pub signature: H520,
}
/// Complete step of the consensus process.
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct VoteStep {
pub height: Height,
pub round: Round,
pub step: Step,
pub block_hash: Option<BlockHash>,
}
impl VoteStep {
pub fn new(height: Height, round: Round, step: Step) -> Self {
VoteStep { height: height, round: round, step: step }
}
pub fn is_height(&self, height: Height) -> bool {
self.height == height
}
pub fn is_round(&self, height: Height, round: Round) -> bool {
self.height == height && self.round == round
}
}
fn consensus_round(header: &Header) -> Result<Round, ::rlp::DecoderError> {
let round_rlp = header.seal().get(0).expect("seal passed basic verification; seal has 3 fields; qed");
@@ -42,53 +62,29 @@ impl ConsensusMessage {
pub fn new(signature: H520, height: Height, round: Round, step: Step, block_hash: Option<BlockHash>) -> Self {
ConsensusMessage {
signature: signature,
height: height,
round: round,
step: step,
block_hash: block_hash,
vote_step: VoteStep::new(height, round, step),
}
}
pub fn new_proposal(header: &Header) -> Result<Self, ::rlp::DecoderError> {
Ok(ConsensusMessage {
vote_step: VoteStep::new(header.number() as Height, consensus_round(header)?, Step::Propose),
signature: UntrustedRlp::new(header.seal().get(1).expect("seal passed basic verification; seal has 3 fields; qed").as_slice()).as_val()?,
height: header.number() as Height,
round: consensus_round(header)?,
step: Step::Propose,
block_hash: Some(header.bare_hash()),
})
}
pub fn new_commit(proposal: &ConsensusMessage, signature: H520) -> Self {
let mut vote_step = proposal.vote_step.clone();
vote_step.step = Step::Precommit;
ConsensusMessage {
signature: signature,
height: proposal.height,
round: proposal.round,
step: Step::Precommit,
vote_step: vote_step,
block_hash: proposal.block_hash,
signature: signature,
}
}
pub fn is_height(&self, height: Height) -> bool {
self.height == height
}
pub fn is_round(&self, height: Height, round: Round) -> bool {
self.height == height && self.round == round
}
pub fn is_step(&self, height: Height, round: Round, step: Step) -> bool {
self.height == height && self.round == round && self.step == step
}
pub fn is_block_hash(&self, h: Height, r: Round, s: Step, block_hash: Option<BlockHash>) -> bool {
self.height == h && self.round == r && self.step == s && self.block_hash == block_hash
}
pub fn is_aligned(&self, m: &ConsensusMessage) -> bool {
self.is_block_hash(m.height, m.round, m.step, m.block_hash)
}
pub fn verify(&self) -> Result<Address, Error> {
let full_rlp = ::rlp::encode(self);
let block_info = Rlp::new(&full_rlp).at(1);
@@ -97,16 +93,30 @@ impl ConsensusMessage {
}
pub fn precommit_hash(&self) -> H256 {
message_info_rlp(self.height, self.round, Step::Precommit, self.block_hash).sha3()
let mut vote_step = self.vote_step.clone();
vote_step.step = Step::Precommit;
message_info_rlp(&vote_step, self.block_hash).sha3()
}
}
impl PartialOrd for ConsensusMessage {
fn partial_cmp(&self, m: &ConsensusMessage) -> Option<Ordering> {
impl PartialOrd for VoteStep {
fn partial_cmp(&self, m: &VoteStep) -> Option<Ordering> {
Some(self.cmp(m))
}
}
impl Ord for VoteStep {
fn cmp(&self, m: &VoteStep) -> Ordering {
if self.height != m.height {
self.height.cmp(&m.height)
} else if self.round != m.round {
self.round.cmp(&m.round)
} else {
self.step.number().cmp(&m.step.number())
}
}
}
impl Step {
fn number(&self) -> u8 {
match *self {
@@ -118,20 +128,6 @@ impl Step {
}
}
impl Ord for ConsensusMessage {
fn cmp(&self, m: &ConsensusMessage) -> Ordering {
if self.height != m.height {
self.height.cmp(&m.height)
} else if self.round != m.round {
self.round.cmp(&m.round)
} else if self.step != m.step {
self.step.number().cmp(&m.step.number())
} else {
self.signature.cmp(&m.signature)
}
}
}
impl Decodable for Step {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
match decoder.as_rlp().as_val()? {
@@ -149,42 +145,39 @@ impl Encodable for Step {
}
}
/// (signature, height, round, step, block_hash)
/// (signature, (height, round, step, block_hash))
impl Decodable for ConsensusMessage {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let rlp = decoder.as_rlp();
let m = rlp.at(1)?;
let block_message: H256 = m.val_at(3)?;
Ok(ConsensusMessage {
signature: rlp.val_at(0)?,
height: m.val_at(0)?,
round: m.val_at(1)?,
step: m.val_at(2)?,
vote_step: VoteStep::new(m.val_at(0)?, m.val_at(1)?, m.val_at(2)?),
block_hash: match block_message.is_zero() {
true => None,
false => Some(block_message),
}
},
signature: rlp.val_at(0)?,
})
}
}
}
impl Encodable for ConsensusMessage {
fn rlp_append(&self, s: &mut RlpStream) {
let info = message_info_rlp(self.height, self.round, self.step, self.block_hash);
let info = message_info_rlp(&self.vote_step, self.block_hash);
s.begin_list(2)
.append(&self.signature)
.append_raw(&info, 1);
}
}
pub fn message_info_rlp(height: Height, round: Round, step: Step, block_hash: Option<BlockHash>) -> Bytes {
pub fn message_info_rlp(vote_step: &VoteStep, block_hash: Option<BlockHash>) -> Bytes {
// TODO: figure out whats wrong with nested list encoding
let mut s = RlpStream::new_list(5);
s.append(&height).append(&round).append(&step).append(&block_hash.unwrap_or_else(H256::zero));
s.append(&vote_step.height).append(&vote_step.round).append(&vote_step.step).append(&block_hash.unwrap_or_else(H256::zero));
s.out()
}
pub fn message_full_rlp(signature: &H520, vote_info: &Bytes) -> Bytes {
let mut s = RlpStream::new_list(2);
s.append(signature).append_raw(vote_info, 1);
@@ -199,14 +192,17 @@ mod tests {
use super::*;
use account_provider::AccountProvider;
use header::Header;
use ethkey::Secret;
#[test]
fn encode_decode() {
let message = ConsensusMessage {
signature: H520::default(),
height: 10,
round: 123,
step: Step::Precommit,
vote_step: VoteStep {
height: 10,
round: 123,
step: Step::Precommit,
},
block_hash: Some("1".sha3())
};
let raw_rlp = ::rlp::encode(&message).to_vec();
@@ -215,9 +211,11 @@ mod tests {
let message = ConsensusMessage {
signature: H520::default(),
height: 1314,
round: 0,
step: Step::Prevote,
vote_step: VoteStep {
height: 1314,
round: 0,
step: Step::Prevote,
},
block_hash: None
};
let raw_rlp = ::rlp::encode(&message);
@@ -228,10 +226,10 @@ mod tests {
#[test]
fn generate_and_verify() {
let tap = Arc::new(AccountProvider::transient_provider());
let addr = tap.insert_account("0".sha3(), "0").unwrap();
let addr = tap.insert_account(Secret::from_slice(&"0".sha3()).unwrap(), "0").unwrap();
tap.unlock_account_permanently(addr, "0".into()).unwrap();
let mi = message_info_rlp(123, 2, Step::Precommit, Some(H256::default()));
let mi = message_info_rlp(&VoteStep::new(123, 2, Step::Precommit), Some(H256::default()));
let raw_rlp = message_full_rlp(&tap.sign(addr, None, mi.sha3()).unwrap().into(), &mi);
@@ -254,9 +252,11 @@ mod tests {
message,
ConsensusMessage {
signature: Default::default(),
height: 0,
round: 0,
step: Step::Propose,
vote_step: VoteStep {
height: 0,
round: 0,
step: Step::Propose,
},
block_hash: Some(header.bare_hash())
}
);
@@ -267,12 +267,10 @@ mod tests {
let header = Header::default();
let pro = ConsensusMessage {
signature: Default::default(),
height: 0,
round: 0,
step: Step::Propose,
vote_step: VoteStep::new(0, 0, Step::Propose),
block_hash: Some(header.bare_hash())
};
let pre = message_info_rlp(0, 0, Step::Precommit, Some(header.bare_hash()));
let pre = message_info_rlp(&VoteStep::new(0, 0, Step::Precommit), Some(header.bare_hash()));
assert_eq!(pro.precommit_hash(), pre.sha3());
}

View File

@@ -53,7 +53,7 @@ use self::transition::TransitionHandler;
use self::params::TendermintParams;
use self::vote_collector::VoteCollector;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub enum Step {
Propose,
Prevote,
@@ -163,13 +163,13 @@ impl Tendermint {
let h = self.height.load(AtomicOrdering::SeqCst);
let r = self.round.load(AtomicOrdering::SeqCst);
let s = self.step.read();
let vote_info = message_info_rlp(h, r, *s, block_hash);
let vote_info = message_info_rlp(&VoteStep::new(h, r, *s), block_hash);
let authority = self.authority.read();
match ap.sign(*authority, self.password.read().clone(), vote_info.sha3()).map(Into::into) {
Ok(signature) => {
let message_rlp = message_full_rlp(&signature, &vote_info);
let message = ConsensusMessage::new(signature, h, r, *s, block_hash);
self.votes.vote(message.clone(), *authority);
self.votes.vote(message.clone(), &*authority);
debug!(target: "poa", "Generated {:?} as {}.", message, *authority);
self.handle_valid_message(&message);
@@ -221,7 +221,7 @@ impl Tendermint {
},
Step::Prevote => {
let block_hash = match *self.lock_change.read() {
Some(ref m) if !self.should_unlock(m.round) => m.block_hash,
Some(ref m) if !self.should_unlock(m.vote_step.round) => m.block_hash,
_ => self.proposal.read().clone(),
};
self.generate_and_broadcast_message(block_hash);
@@ -230,8 +230,8 @@ impl Tendermint {
trace!(target: "poa", "to_step: Precommit.");
let block_hash = match *self.lock_change.read() {
Some(ref m) if self.is_round(m) && m.block_hash.is_some() => {
trace!(target: "poa", "Setting last lock: {}", m.round);
self.last_lock.store(m.round, AtomicOrdering::SeqCst);
trace!(target: "poa", "Setting last lock: {}", m.vote_step.round);
self.last_lock.store(m.vote_step.round, AtomicOrdering::SeqCst);
m.block_hash
},
_ => None,
@@ -246,7 +246,7 @@ impl Tendermint {
if let Some(block_hash) = *self.proposal.read() {
// Generate seal and remove old votes.
if self.is_proposer(&*self.authority.read()).is_ok() {
if let Some(seal) = self.votes.seal_signatures(height, round, block_hash) {
if let Some(seal) = self.votes.seal_signatures(height, round, &block_hash) {
trace!(target: "poa", "Collected seal: {:?}", seal);
let seal = vec![
::rlp::encode(&round).to_vec(),
@@ -290,11 +290,11 @@ impl Tendermint {
}
fn is_height(&self, message: &ConsensusMessage) -> bool {
message.is_height(self.height.load(AtomicOrdering::SeqCst))
message.vote_step.is_height(self.height.load(AtomicOrdering::SeqCst))
}
fn is_round(&self, message: &ConsensusMessage) -> bool {
message.is_round(self.height.load(AtomicOrdering::SeqCst), self.round.load(AtomicOrdering::SeqCst))
message.vote_step.is_round(self.height.load(AtomicOrdering::SeqCst), self.round.load(AtomicOrdering::SeqCst))
}
fn increment_round(&self, n: Round) {
@@ -302,20 +302,20 @@ impl Tendermint {
self.round.fetch_add(n, AtomicOrdering::SeqCst);
}
fn should_unlock(&self, lock_change_round: Round) -> bool {
fn should_unlock(&self, lock_change_round: Round) -> bool {
self.last_lock.load(AtomicOrdering::SeqCst) < lock_change_round
&& lock_change_round < self.round.load(AtomicOrdering::SeqCst)
}
fn has_enough_any_votes(&self) -> bool {
let step_votes = self.votes.count_step_votes(self.height.load(AtomicOrdering::SeqCst), self.round.load(AtomicOrdering::SeqCst), *self.step.read());
let step_votes = self.votes.count_step_votes(&VoteStep::new(self.height.load(AtomicOrdering::SeqCst), self.round.load(AtomicOrdering::SeqCst), *self.step.read()));
self.is_above_threshold(step_votes)
}
fn has_enough_future_step_votes(&self, message: &ConsensusMessage) -> bool {
if message.round > self.round.load(AtomicOrdering::SeqCst) {
let step_votes = self.votes.count_step_votes(message.height, message.round, message.step);
fn has_enough_future_step_votes(&self, vote_step: &VoteStep) -> bool {
if vote_step.round > self.round.load(AtomicOrdering::SeqCst) {
let step_votes = self.votes.count_step_votes(vote_step);
self.is_above_threshold(step_votes)
} else {
false
@@ -328,12 +328,13 @@ impl Tendermint {
}
fn handle_valid_message(&self, message: &ConsensusMessage) {
let ref vote_step = message.vote_step;
let is_newer_than_lock = match *self.lock_change.read() {
Some(ref lock) => message > lock,
Some(ref lock) => vote_step > &lock.vote_step,
None => true,
};
let lock_change = is_newer_than_lock
&& message.step == Step::Prevote
&& vote_step.step == Step::Prevote
&& message.block_hash.is_some()
&& self.has_enough_aligned_votes(message);
if lock_change {
@@ -351,15 +352,15 @@ impl Tendermint {
Some(Step::Commit)
}
},
Step::Precommit if self.has_enough_future_step_votes(message) => {
self.increment_round(message.round - self.round.load(AtomicOrdering::SeqCst));
Step::Precommit if self.has_enough_future_step_votes(&vote_step) => {
self.increment_round(vote_step.round - self.round.load(AtomicOrdering::SeqCst));
Some(Step::Precommit)
},
// Avoid counting twice.
Step::Prevote if lock_change => Some(Step::Precommit),
Step::Prevote if self.has_enough_aligned_votes(message) => Some(Step::Precommit),
Step::Prevote if self.has_enough_future_step_votes(message) => {
self.increment_round(message.round - self.round.load(AtomicOrdering::SeqCst));
Step::Prevote if self.has_enough_future_step_votes(&vote_step) => {
self.increment_round(vote_step.round - self.round.load(AtomicOrdering::SeqCst));
Some(Step::Prevote)
},
_ => None,
@@ -390,8 +391,8 @@ impl Engine for Tendermint {
let message = ConsensusMessage::new_proposal(header).expect("Invalid header.");
map![
"signature".into() => message.signature.to_string(),
"height".into() => message.height.to_string(),
"round".into() => message.round.to_string(),
"height".into() => message.vote_step.height.to_string(),
"round".into() => message.vote_step.round.to_string(),
"block_hash".into() => message.block_hash.as_ref().map(ToString::to_string).unwrap_or("".into())
]
}
@@ -431,11 +432,11 @@ impl Engine for Tendermint {
let height = header.number() as Height;
let round = self.round.load(AtomicOrdering::SeqCst);
let bh = Some(header.bare_hash());
let vote_info = message_info_rlp(height, round, Step::Propose, bh.clone());
let vote_info = message_info_rlp(&VoteStep::new(height, round, Step::Propose), bh.clone());
if let Ok(signature) = ap.sign(*author, self.password.read().clone(), vote_info.sha3()).map(H520::from) {
// Insert Propose vote.
debug!(target: "poa", "Submitting proposal {} at height {} round {}.", header.bare_hash(), height, round);
self.votes.vote(ConsensusMessage::new(signature, height, round, Step::Propose, bh), *author);
self.votes.vote(ConsensusMessage::new(signature, height, round, Step::Propose, bh), author);
// Remember proposal for later seal submission.
*self.proposal.write() = bh;
Seal::Proposal(vec![
@@ -461,9 +462,11 @@ impl Engine for Tendermint {
if !self.is_authority(&sender) {
Err(EngineError::NotAuthorized(sender))?;
}
self.broadcast_message(rlp.as_raw().to_vec());
if self.votes.vote(message.clone(), &sender).is_some() {
Err(EngineError::DoubleVote(sender))?
}
trace!(target: "poa", "Handling a valid {:?} from {}.", message, sender);
self.votes.vote(message.clone(), sender);
self.broadcast_message(rlp.as_raw().to_vec());
self.handle_valid_message(&message);
}
Ok(())
@@ -502,7 +505,7 @@ impl Engine for Tendermint {
}
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
let proposal = ConsensusMessage::new_proposal(header)?;
let proposal = ConsensusMessage::new_proposal(header)?;
let proposer = proposal.verify()?;
if !self.is_authority(&proposer) {
Err(EngineError::NotAuthorized(proposer))?
@@ -541,7 +544,7 @@ impl Engine for Tendermint {
found: signatures_len
}))?;
}
self.is_round_proposer(proposal.height, proposal.round, &proposer)?;
self.is_round_proposer(proposal.vote_step.height, proposal.vote_step.round, &proposer)?;
}
Ok(())
}
@@ -607,16 +610,16 @@ impl Engine for Tendermint {
let proposal = ConsensusMessage::new_proposal(header).expect("block went through full verification; this Engine verifies new_proposal creation; qed");
if signatures_len != 1 {
// New Commit received, skip to next height.
trace!(target: "poa", "Received a commit for height {}, round {}.", proposal.height, proposal.round);
self.to_next_height(proposal.height);
trace!(target: "poa", "Received a commit: {:?}.", proposal.vote_step);
self.to_next_height(proposal.vote_step.height);
return false;
}
let proposer = proposal.verify().expect("block went through full verification; this Engine tries verify; qed");
debug!(target: "poa", "Received a new proposal for height {}, round {} from {}.", proposal.height, proposal.round, proposer);
debug!(target: "poa", "Received a new proposal {:?} from {}.", proposal.vote_step, proposer);
if self.is_round(&proposal) {
*self.proposal.write() = proposal.block_hash.clone();
}
self.votes.vote(proposal, proposer);
self.votes.vote(proposal, &proposer);
true
}
@@ -671,6 +674,7 @@ mod tests {
use error::{Error, BlockError};
use header::Header;
use env_info::EnvInfo;
use ethkey::Secret;
use client::chain_notify::ChainNotify;
use miner::MinerService;
use tests::helpers::*;
@@ -703,7 +707,7 @@ mod tests {
}
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(&VoteStep::new(height, round, step), block_hash);
let m = message_full_rlp(&signer(mi.sha3()).unwrap().into(), &mi);
engine.handle_message(&m).unwrap();
m
@@ -711,7 +715,7 @@ mod tests {
fn proposal_seal(tap: &Arc<AccountProvider>, header: &Header, round: Round) -> Vec<Bytes> {
let author = header.author();
let vote_info = message_info_rlp(header.number() as Height, round, Step::Propose, Some(header.bare_hash()));
let vote_info = message_info_rlp(&VoteStep::new(header.number() as Height, round, Step::Propose), Some(header.bare_hash()));
let signature = tap.sign(*author, None, vote_info.sha3()).unwrap();
vec![
::rlp::encode(&round).to_vec(),
@@ -721,7 +725,7 @@ mod tests {
}
fn insert_and_unlock(tap: &Arc<AccountProvider>, acc: &str) -> Address {
let addr = tap.insert_account(acc.sha3(), acc).unwrap();
let addr = tap.insert_account(Secret::from_slice(&acc.sha3()).unwrap(), acc).unwrap();
tap.unlock_account_permanently(addr, acc.into()).unwrap();
addr
}
@@ -825,7 +829,7 @@ mod tests {
header.set_author(proposer);
let mut seal = proposal_seal(&tap, &header, 0);
let vote_info = message_info_rlp(0, 0, Step::Precommit, Some(header.bare_hash()));
let vote_info = message_info_rlp(&VoteStep::new(0, 0, Step::Precommit), Some(header.bare_hash()));
let signature1 = tap.sign(proposer, None, vote_info.sha3()).unwrap();
seal[2] = ::rlp::encode(&vec![H520::from(signature1.clone())]).to_vec();
@@ -886,7 +890,7 @@ mod tests {
fn relays_messages() {
let (spec, tap) = setup();
let engine = spec.engine.clone();
let v0 = insert_and_register(&tap, engine.as_ref(), "0");
let v1 = insert_and_register(&tap, engine.as_ref(), "1");

View File

@@ -17,13 +17,50 @@
//! Collects votes on hashes at each height and round.
use util::*;
use super::message::ConsensusMessage;
use super::{Height, Round, Step};
use super::message::*;
use super::{Height, Round, Step, BlockHash};
#[derive(Debug)]
pub struct VoteCollector {
/// Storing all Proposals, Prevotes and Precommits.
votes: RwLock<BTreeMap<ConsensusMessage, Address>>,
votes: RwLock<BTreeMap<VoteStep, StepCollector>>,
}
#[derive(Debug, Default)]
struct StepCollector {
voted: HashSet<Address>,
pub block_votes: HashMap<Option<BlockHash>, HashMap<H520, Address>>,
messages: HashSet<ConsensusMessage>,
}
impl StepCollector {
/// Returns Some(&Address) when validator is double voting.
fn insert<'a>(&mut self, message: ConsensusMessage, address: &'a Address) -> Option<&'a Address> {
// Do nothing when message was seen.
if self.messages.insert(message.clone()) {
if self.voted.insert(address.clone()) {
self
.block_votes
.entry(message.block_hash)
.or_insert_with(HashMap::new)
.insert(message.signature, address.clone());
} else {
// Bad validator sent a different message.
return Some(address);
}
}
None
}
/// Count all votes for the given block hash at this step.
fn count_block(&self, block_hash: &Option<BlockHash>) -> usize {
self.block_votes.get(block_hash).map_or(0, HashMap::len)
}
/// Count all votes collected for the given step.
fn count(&self) -> usize {
self.block_votes.values().map(HashMap::len).sum()
}
}
#[derive(Debug)]
@@ -42,109 +79,105 @@ impl PartialEq for SealSignatures {
impl Eq for SealSignatures {}
impl VoteCollector {
pub fn new() -> VoteCollector {
pub fn new() -> Self {
let mut collector = BTreeMap::new();
// Insert dummy message to fulfill invariant: "only messages newer than the oldest are inserted".
collector.insert(ConsensusMessage {
signature: H520::default(),
height: 0,
round: 0,
step: Step::Propose,
block_hash: None
},
Address::default());
// Insert dummy entry to fulfill invariant: "only messages newer than the oldest are inserted".
collector.insert(VoteStep::new(0, 0, Step::Propose), Default::default());
VoteCollector { votes: RwLock::new(collector) }
}
/// Insert vote if it is newer than the oldest one.
pub fn vote(&self, message: ConsensusMessage, voter: Address) -> Option<Address> {
self.votes.write().insert(message, voter)
pub fn vote<'a>(&self, message: ConsensusMessage, voter: &'a Address) -> Option<&'a Address> {
self
.votes
.write()
.entry(message.vote_step.clone())
.or_insert_with(Default::default)
.insert(message, voter)
}
/// Checks if the message should be ignored.
pub fn is_old_or_known(&self, message: &ConsensusMessage) -> bool {
self.votes.read().get(message).map_or(false, |a| {
trace!(target: "poa", "Known message from {}: {:?}.", a, message);
true
}) || {
self
.votes
.read()
.get(&message.vote_step)
.map_or(false, |c| {
let is_known = c.messages.contains(message);
if is_known { trace!(target: "poa", "Known message: {:?}.", message); }
is_known
})
|| {
let guard = self.votes.read();
let is_old = guard.keys().next().map_or(true, |oldest| message <= oldest);
let is_old = guard.keys().next().map_or(true, |oldest| message.vote_step <= *oldest);
if is_old { trace!(target: "poa", "Old message {:?}.", message); }
is_old
}
}
/// Throws out messages older than message, leaves message as marker for the oldest.
pub fn throw_out_old(&self, message: &ConsensusMessage) {
pub fn throw_out_old(&self, vote_step: &VoteStep) {
let mut guard = self.votes.write();
let new_collector = guard.split_off(message);
let new_collector = guard.split_off(vote_step);
*guard = new_collector;
}
pub fn seal_signatures(&self, height: Height, round: Round, block_hash: H256) -> Option<SealSignatures> {
let bh = Some(block_hash);
let (proposal, votes) = {
/// Collects the signatures used to seal a block.
pub fn seal_signatures(&self, height: Height, round: Round, block_hash: &H256) -> Option<SealSignatures> {
let ref bh = Some(*block_hash);
let precommit_step = VoteStep::new(height, round, Step::Precommit);
let maybe_seal = {
let guard = self.votes.read();
let mut current_signatures = guard.keys().skip_while(|m| !m.is_block_hash(height, round, Step::Propose, bh));
let proposal = current_signatures.next().cloned();
let votes = current_signatures
.skip_while(|m| !m.is_block_hash(height, round, Step::Precommit, bh))
.filter(|m| m.is_block_hash(height, round, Step::Precommit, bh))
.cloned()
.collect::<Vec<_>>();
(proposal, votes)
guard
.get(&VoteStep::new(height, round, Step::Propose))
.and_then(|c| c.block_votes.get(bh))
.and_then(|proposals| proposals.keys().next())
.map(|proposal| SealSignatures {
proposal: proposal.clone(),
votes: guard
.get(&precommit_step)
.and_then(|c| c.block_votes.get(bh))
.map(|precommits| precommits.keys().cloned().collect())
.unwrap_or_else(Vec::new),
})
.and_then(|seal| if seal.votes.is_empty() { None } else { Some(seal) })
};
if votes.is_empty() {
return None;
if maybe_seal.is_some() {
// Remove messages that are no longer relevant.
self.throw_out_old(&precommit_step);
}
// Remove messages that are no longer relevant.
votes.last().map(|m| self.throw_out_old(m));
let mut votes_vec: Vec<_> = votes.into_iter().map(|m| m.signature).collect();
votes_vec.sort();
proposal.map(|p| SealSignatures {
proposal: p.signature,
votes: votes_vec,
})
maybe_seal
}
/// Count votes which agree with the given message.
pub fn count_aligned_votes(&self, message: &ConsensusMessage) -> usize {
let guard = self.votes.read();
guard.keys()
.skip_while(|m| !m.is_aligned(message))
// sorted by signature so might not be continuous
.filter(|m| m.is_aligned(message))
.count()
self
.votes
.read()
.get(&message.vote_step)
.map_or(0, |m| m.count_block(&message.block_hash))
}
pub fn count_step_votes(&self, height: Height, round: Round, step: Step) -> usize {
let guard = self.votes.read();
let current = guard.iter().skip_while(|&(m, _)| !m.is_step(height, round, step));
let mut origins = HashSet::new();
let mut n = 0;
for (message, origin) in current {
if message.is_step(height, round, step) {
if origins.insert(origin) {
n += 1;
} else {
warn!("count_step_votes: Authority {} has cast multiple step votes, this indicates malicious behaviour.", origin)
}
}
}
n
/// Count all votes collected for a given step.
pub fn count_step_votes(&self, vote_step: &VoteStep) -> usize {
self.votes.read().get(vote_step).map_or(0, StepCollector::count)
}
/// Get all messages older than the height.
pub fn get_up_to(&self, height: Height) -> Vec<Bytes> {
let guard = self.votes.read();
guard
.keys()
.filter(|m| m.step.is_pre())
.take_while(|m| m.height <= height)
.map(|m| ::rlp::encode(m).to_vec())
.collect()
.iter()
.filter(|&(s, _)| s.step.is_pre())
.take_while(|&(s, _)| s.height <= height)
.map(|(_, c)| c.messages.iter().map(|m| ::rlp::encode(m).to_vec()).collect::<Vec<_>>())
.fold(Vec::new(), |mut acc, mut messages| { acc.append(&mut messages); acc })
}
/// Retrieve address from which the message was sent from cache.
pub fn get(&self, message: &ConsensusMessage) -> Option<Address> {
let guard = self.votes.read();
guard.get(message).cloned()
guard.get(&message.vote_step).and_then(|c| c.block_votes.get(&message.block_hash)).and_then(|origins| origins.get(&message.signature).cloned())
}
}
@@ -152,15 +185,15 @@ impl VoteCollector {
mod tests {
use util::*;
use super::*;
use super::super::{Height, Round, BlockHash, Step};
use super::super::message::ConsensusMessage;
use super::super::{BlockHash, Step};
use super::super::message::*;
fn random_vote(collector: &VoteCollector, signature: H520, h: Height, r: Round, step: Step, block_hash: Option<BlockHash>) -> Option<H160> {
full_vote(collector, signature, h, r, step, block_hash, H160::random())
fn random_vote(collector: &VoteCollector, signature: H520, vote_step: VoteStep, block_hash: Option<BlockHash>) -> bool {
full_vote(collector, signature, vote_step, block_hash, &H160::random()).is_none()
}
fn full_vote(collector: &VoteCollector, signature: H520, h: Height, r: Round, step: Step, block_hash: Option<BlockHash>, address: Address) -> Option<H160> {
collector.vote(ConsensusMessage { signature: signature, height: h, round: r, step: step, block_hash: block_hash }, address)
fn full_vote<'a>(collector: &VoteCollector, signature: H520, vote_step: VoteStep, block_hash: Option<BlockHash>, address: &'a Address) -> Option<&'a Address> {
collector.vote(ConsensusMessage { signature: signature, vote_step: vote_step, block_hash: block_hash }, address)
}
#[test]
@@ -173,68 +206,71 @@ mod tests {
for _ in 0..5 {
signatures.push(H520::random());
}
let propose_step = VoteStep::new(h, r, Step::Propose);
let prevote_step = VoteStep::new(h, r, Step::Prevote);
let precommit_step = VoteStep::new(h, r, Step::Precommit);
// Wrong height proposal.
random_vote(&collector, signatures[4].clone(), h - 1, r, Step::Propose, bh.clone());
random_vote(&collector, signatures[4].clone(), VoteStep::new(h - 1, r, Step::Propose), bh.clone());
// Good proposal
random_vote(&collector, signatures[0].clone(), h, r, Step::Propose, bh.clone());
random_vote(&collector, signatures[0].clone(), propose_step.clone(), bh.clone());
// Wrong block proposal.
random_vote(&collector, signatures[0].clone(), h, r, Step::Propose, Some("0".sha3()));
random_vote(&collector, signatures[0].clone(), propose_step.clone(), Some("0".sha3()));
// Wrong block precommit.
random_vote(&collector, signatures[3].clone(), h, r, Step::Precommit, Some("0".sha3()));
random_vote(&collector, signatures[3].clone(), precommit_step.clone(), Some("0".sha3()));
// Wrong round proposal.
random_vote(&collector, signatures[0].clone(), h, r - 1, Step::Propose, bh.clone());
random_vote(&collector, signatures[0].clone(), VoteStep::new(h, r - 1, Step::Propose), bh.clone());
// Prevote.
random_vote(&collector, signatures[0].clone(), h, r, Step::Prevote, bh.clone());
random_vote(&collector, signatures[0].clone(), prevote_step.clone(), bh.clone());
// Relevant precommit.
random_vote(&collector, signatures[2].clone(), h, r, Step::Precommit, bh.clone());
random_vote(&collector, signatures[2].clone(), precommit_step.clone(), bh.clone());
// Replcated vote.
random_vote(&collector, signatures[2].clone(), h, r, Step::Precommit, bh.clone());
random_vote(&collector, signatures[2].clone(), precommit_step.clone(), bh.clone());
// Wrong round precommit.
random_vote(&collector, signatures[4].clone(), h, r + 1, Step::Precommit, bh.clone());
random_vote(&collector, signatures[4].clone(), VoteStep::new(h, r + 1, Step::Precommit), bh.clone());
// Wrong height precommit.
random_vote(&collector, signatures[3].clone(), h + 1, r, Step::Precommit, bh.clone());
random_vote(&collector, signatures[3].clone(), VoteStep::new(h + 1, r, Step::Precommit), bh.clone());
// Relevant precommit.
random_vote(&collector, signatures[1].clone(), h, r, Step::Precommit, bh.clone());
random_vote(&collector, signatures[1].clone(), precommit_step.clone(), bh.clone());
// Wrong round precommit, same signature.
random_vote(&collector, signatures[1].clone(), h, r + 1, Step::Precommit, bh.clone());
random_vote(&collector, signatures[1].clone(), VoteStep::new(h, r + 1, Step::Precommit), bh.clone());
// Wrong round precommit.
random_vote(&collector, signatures[4].clone(), h, r - 1, Step::Precommit, bh.clone());
random_vote(&collector, signatures[4].clone(), VoteStep::new(h, r - 1, Step::Precommit), bh.clone());
let seal = SealSignatures {
proposal: signatures[0],
votes: signatures[1..3].to_vec()
};
assert_eq!(seal, collector.seal_signatures(h, r, bh.unwrap()).unwrap());
assert_eq!(seal, collector.seal_signatures(h, r, &bh.unwrap()).unwrap());
}
#[test]
fn count_votes() {
let collector = VoteCollector::new();
let prevote_step = VoteStep::new(3, 2, Step::Prevote);
let precommit_step = VoteStep::new(3, 2, Step::Precommit);
// good prevote
random_vote(&collector, H520::random(), 3, 2, Step::Prevote, Some("0".sha3()));
random_vote(&collector, H520::random(), 3, 1, Step::Prevote, Some("0".sha3()));
random_vote(&collector, H520::random(), prevote_step.clone(), Some("0".sha3()));
random_vote(&collector, H520::random(), VoteStep::new(3, 1, Step::Prevote), Some("0".sha3()));
// good precommit
random_vote(&collector, H520::random(), 3, 2, Step::Precommit, Some("0".sha3()));
random_vote(&collector, H520::random(), 3, 3, Step::Precommit, Some("0".sha3()));
random_vote(&collector, H520::random(), precommit_step.clone(), Some("0".sha3()));
random_vote(&collector, H520::random(), VoteStep::new(3, 3, Step::Precommit), Some("0".sha3()));
// good prevote
random_vote(&collector, H520::random(), 3, 2, Step::Prevote, Some("1".sha3()));
random_vote(&collector, H520::random(), prevote_step.clone(), Some("1".sha3()));
// good prevote
let same_sig = H520::random();
random_vote(&collector, same_sig.clone(), 3, 2, Step::Prevote, Some("1".sha3()));
random_vote(&collector, same_sig, 3, 2, Step::Prevote, Some("1".sha3()));
random_vote(&collector, same_sig.clone(), prevote_step.clone(), Some("1".sha3()));
random_vote(&collector, same_sig, prevote_step.clone(), Some("1".sha3()));
// good precommit
random_vote(&collector, H520::random(), 3, 2, Step::Precommit, Some("1".sha3()));
random_vote(&collector, H520::random(), precommit_step.clone(), Some("1".sha3()));
// good prevote
random_vote(&collector, H520::random(), 3, 2, Step::Prevote, Some("0".sha3()));
random_vote(&collector, H520::random(), 2, 2, Step::Precommit, Some("2".sha3()));
random_vote(&collector, H520::random(), prevote_step.clone(), Some("0".sha3()));
random_vote(&collector, H520::random(), VoteStep::new(2, 2, Step::Precommit), Some("2".sha3()));
assert_eq!(collector.count_step_votes(3, 2, Step::Prevote), 4);
assert_eq!(collector.count_step_votes(3, 2, Step::Precommit), 2);
assert_eq!(collector.count_step_votes(&prevote_step), 4);
assert_eq!(collector.count_step_votes(&precommit_step), 2);
let message = ConsensusMessage {
signature: H520::default(),
height: 3,
round: 2,
step: Step::Prevote,
vote_step: prevote_step,
block_hash: Some("1".sha3())
};
assert_eq!(collector.count_aligned_votes(&message), 2);
@@ -243,30 +279,29 @@ mod tests {
#[test]
fn remove_old() {
let collector = VoteCollector::new();
random_vote(&collector, H520::random(), 3, 2, Step::Prevote, Some("0".sha3()));
random_vote(&collector, H520::random(), 3, 1, Step::Prevote, Some("0".sha3()));
random_vote(&collector, H520::random(), 3, 3, Step::Precommit, Some("0".sha3()));
random_vote(&collector, H520::random(), 3, 2, Step::Prevote, Some("1".sha3()));
random_vote(&collector, H520::random(), 3, 2, Step::Prevote, Some("1".sha3()));
random_vote(&collector, H520::random(), 3, 2, Step::Prevote, Some("0".sha3()));
random_vote(&collector, H520::random(), 2, 2, Step::Precommit, Some("2".sha3()));
let message = ConsensusMessage {
signature: H520::default(),
height: 3,
round: 2,
step: Step::Precommit,
block_hash: Some("1".sha3())
let vote = |height, round, step, hash| {
random_vote(&collector, H520::random(), VoteStep::new(height, round, step), hash);
};
collector.throw_out_old(&message);
vote(3, 2, Step::Prevote, Some("0".sha3()));
vote(3, 1, Step::Prevote, Some("0".sha3()));
vote(3, 3, Step::Precommit, Some("0".sha3()));
vote(3, 2, Step::Prevote, Some("1".sha3()));
vote(3, 2, Step::Prevote, Some("1".sha3()));
vote(3, 2, Step::Prevote, Some("0".sha3()));
vote(2, 2, Step::Precommit, Some("2".sha3()));
collector.throw_out_old(&VoteStep::new(3, 2, Step::Precommit));
assert_eq!(collector.votes.read().len(), 1);
}
#[test]
fn malicious_authority() {
let collector = VoteCollector::new();
full_vote(&collector, H520::random(), 3, 2, Step::Prevote, Some("0".sha3()), Address::default());
full_vote(&collector, H520::random(), 3, 2, Step::Prevote, Some("1".sha3()), Address::default());
assert_eq!(collector.count_step_votes(3, 2, Step::Prevote), 1);
let vote_step = VoteStep::new(3, 2, Step::Prevote);
// Vote is inserted fine.
assert!(full_vote(&collector, H520::random(), vote_step.clone(), Some("0".sha3()), &Address::default()).is_none());
// Returns the double voting address.
full_vote(&collector, H520::random(), vote_step.clone(), Some("1".sha3()), &Address::default()).unwrap();
assert_eq!(collector.count_step_votes(&vote_step), 1);
}
}

View File

@@ -118,17 +118,17 @@ mod provider {
}
}
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> {
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<_>>() }))
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<_>>() }))
}
}
}
@@ -140,6 +140,7 @@ mod tests {
use account_provider::AccountProvider;
use transaction::{Transaction, Action};
use client::{BlockChainClient, EngineClient};
use ethkey::Secret;
use miner::MinerService;
use tests::helpers::generate_dummy_client_with_spec_and_data;
use super::super::ValidatorSet;
@@ -158,8 +159,9 @@ mod tests {
#[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 s0 = Secret::from_slice(&"1".sha3()).unwrap();
let v0 = tap.insert_account(s0.clone(), "").unwrap();
let v1 = tap.insert_account(Secret::from_slice(&"0".sha3()).unwrap(), "").unwrap();
let spec_factory = || {
let spec = Spec::new_validator_contract();
spec.engine.register_account_provider(tap.clone());
@@ -178,7 +180,7 @@ mod tests {
action: Action::Call(validator_contract),
value: 0.into(),
data: "f94e18670000000000000000000000000000000000000000000000000000000000000001".from_hex().unwrap(),
}.sign(&"1".sha3(), None);
}.sign(&s0, None);
client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap();
client.update_sealing();
assert_eq!(client.chain_info().best_block_number, 1);
@@ -190,7 +192,7 @@ mod tests {
action: Action::Call(validator_contract),
value: 0.into(),
data: "4d238c8e00000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1".from_hex().unwrap(),
}.sign(&"1".sha3(), None);
}.sign(&s0, 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.
@@ -209,7 +211,7 @@ mod tests {
action: Action::Call(Address::default()),
value: 0.into(),
data: Vec::new(),
}.sign(&"1".sha3(), None);
}.sign(&s0, None);
client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap();
client.update_sealing();
// Able to seal again.

View File

@@ -31,6 +31,9 @@ use ethjson;
use rlp::{self, UntrustedRlp, View};
use blockchain::extras::BlockDetails;
/// Parity tries to round block.gas_limit to multiple of this constant
pub const PARITY_GAS_LIMIT_DETERMINANT: U256 = U256([37, 0, 0, 0]);
/// Ethash params.
#[derive(Debug, PartialEq)]
pub struct EthashParams {
@@ -180,14 +183,23 @@ impl Engine for Ethash {
let bound_divisor = self.ethash_params.gas_limit_bound_divisor;
let lower_limit = gas_limit - gas_limit / bound_divisor + 1.into();
let upper_limit = gas_limit + gas_limit / bound_divisor - 1.into();
if gas_limit < gas_floor_target {
min(gas_floor_target, upper_limit)
let gas_limit = if gas_limit < gas_floor_target {
let gas_limit = min(gas_floor_target, upper_limit);
round_block_gas_limit(gas_limit, lower_limit, upper_limit)
} else if gas_limit > gas_ceil_target {
max(gas_ceil_target, lower_limit)
let gas_limit = max(gas_ceil_target, lower_limit);
round_block_gas_limit(gas_limit, lower_limit, upper_limit)
} else {
max(gas_floor_target, min(min(gas_ceil_target, upper_limit),
lower_limit + (header.gas_used().clone() * 6.into() / 5.into()) / bound_divisor))
}
let total_lower_limit = max(lower_limit, gas_floor_target);
let total_upper_limit = min(upper_limit, gas_ceil_target);
let gas_limit = max(gas_floor_target, min(total_upper_limit,
lower_limit + (header.gas_used().clone() * 6.into() / 5.into()) / bound_divisor));
round_block_gas_limit(gas_limit, total_lower_limit, total_upper_limit)
};
// ensure that we are not violating protocol limits
debug_assert!(gas_limit >= lower_limit);
debug_assert!(gas_limit <= upper_limit);
gas_limit
};
header.set_difficulty(difficulty);
header.set_gas_limit(gas_limit);
@@ -338,6 +350,23 @@ pub fn is_new_best_block(best_total_difficulty: U256, parent_details: &BlockDeta
parent_details.total_difficulty + new_header.difficulty() > best_total_difficulty
}
// Try to round gas_limit a bit so that:
// 1) it will still be in desired range
// 2) it will be a nearest (with tendency to increase) multiple of PARITY_GAS_LIMIT_DETERMINANT
fn round_block_gas_limit(gas_limit: U256, lower_limit: U256, upper_limit: U256) -> U256 {
let increased_gas_limit = gas_limit + (PARITY_GAS_LIMIT_DETERMINANT - gas_limit % PARITY_GAS_LIMIT_DETERMINANT);
if increased_gas_limit > upper_limit {
let decreased_gas_limit = increased_gas_limit - PARITY_GAS_LIMIT_DETERMINANT;
if decreased_gas_limit < lower_limit {
gas_limit
} else {
decreased_gas_limit
}
} else {
increased_gas_limit
}
}
#[cfg_attr(feature="dev", allow(wrong_self_convention))]
impl Ethash {
fn calculate_difficulty(&self, header: &Header, parent: &Header) -> U256 {
@@ -435,11 +464,12 @@ mod tests {
use util::*;
use block::*;
use tests::helpers::*;
use engines::Engine;
use env_info::EnvInfo;
use error::{BlockError, Error};
use header::Header;
use super::super::{new_morden, new_homestead_test};
use super::{Ethash, EthashParams};
use super::{Ethash, EthashParams, PARITY_GAS_LIMIT_DETERMINANT};
use rlp;
#[test]
@@ -777,4 +807,47 @@ mod tests {
ethash.calculate_difficulty(&header, &parent_header)
);
}
#[test]
fn gas_limit_is_multiple_of_determinant() {
let spec = new_homestead_test();
let ethash = Ethash::new(spec.params, get_default_ethash_params(), BTreeMap::new());
let mut parent = Header::new();
let mut header = Header::new();
header.set_number(1);
// this test will work for this constant only
assert_eq!(PARITY_GAS_LIMIT_DETERMINANT, U256::from(37));
// when parent.gas_limit < gas_floor_target:
parent.set_gas_limit(U256::from(50_000));
ethash.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(200_000));
assert_eq!(*header.gas_limit(), U256::from(50_024));
// when parent.gas_limit > gas_ceil_target:
parent.set_gas_limit(U256::from(250_000));
ethash.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(200_000));
assert_eq!(*header.gas_limit(), U256::from(249_787));
// when parent.gas_limit is in miner's range
header.set_gas_used(U256::from(150_000));
parent.set_gas_limit(U256::from(150_000));
ethash.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(200_000));
assert_eq!(*header.gas_limit(), U256::from(150_035));
// when parent.gas_limit is in miner's range
// && we can NOT increase it to be multiple of constant
header.set_gas_used(U256::from(150_000));
parent.set_gas_limit(U256::from(150_000));
ethash.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(150_002));
assert_eq!(*header.gas_limit(), U256::from(149_998));
// when parent.gas_limit is in miner's range
// && we can NOT increase it to be multiple of constant
// && we can NOT decrease it to be multiple of constant
header.set_gas_used(U256::from(150_000));
parent.set_gas_limit(U256::from(150_000));
ethash.populate_from_parent(&mut header, &parent, U256::from(150_000), U256::from(150_002));
assert_eq!(*header.gas_limit(), U256::from(150_002));
}
}

View File

@@ -22,7 +22,7 @@ use action_params::ActionParams;
use evm::Ext;
/// Evm errors.
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Error {
/// `OutOfGas` is returned when transaction execution runs out of gas.
/// The state should be reverted to the state from before the

View File

@@ -37,7 +37,6 @@ use bit_set::BitSet;
use util::*;
type CodePosition = usize;
type ProgramCounter = usize;
const ONE: U256 = U256([1, 0, 0, 0]);

View File

@@ -45,7 +45,7 @@ pub fn contract_address(address: &Address, nonce: &U256) -> Address {
}
/// Transaction execution options.
#[derive(Default)]
#[derive(Default, Copy, Clone, PartialEq)]
pub struct TransactOptions {
/// Enable call tracing.
pub tracing: bool,
@@ -463,8 +463,9 @@ impl<'a> Executive<'a> {
match result {
Err(evm::Error::Internal) => Err(ExecutionError::Internal),
Err(_) => {
Err(exception) => {
Ok(Executed {
exception: Some(exception),
gas: t.gas,
gas_used: t.gas,
refunded: U256::zero(),
@@ -479,6 +480,7 @@ impl<'a> Executive<'a> {
},
_ => {
Ok(Executed {
exception: None,
gas: t.gas,
gas_used: gas_used,
refunded: refunded,

View File

@@ -22,7 +22,7 @@ use std::ops::{Deref, DerefMut};
use std::cell::Cell;
use transaction::{SignedTransaction, Action};
use transient_hashmap::TransientHashMap;
use miner::{TransactionQueue, TransactionImportResult, TransactionOrigin, AccountDetails};
use miner::{TransactionQueue, TransactionQueueDetailsProvider, TransactionImportResult, TransactionOrigin};
use miner::transaction_queue::QueuingInstant;
use error::{Error, TransactionError};
use util::{Uint, U256, H256, Address, Hashable};
@@ -76,16 +76,12 @@ impl BanningTransactionQueue {
/// Add to the queue taking bans into consideration.
/// May reject transaction because of the banlist.
pub fn add_with_banlist<F, G>(
pub fn add_with_banlist(
&mut self,
transaction: SignedTransaction,
time: QueuingInstant,
account_details: &F,
gas_estimator: &G,
) -> Result<TransactionImportResult, Error> where
F: Fn(&Address) -> AccountDetails,
G: Fn(&SignedTransaction) -> U256,
{
details_provider: &TransactionQueueDetailsProvider,
) -> Result<TransactionImportResult, Error> {
if let Threshold::BanAfter(threshold) = self.ban_threshold {
// NOTE In all checks use direct query to avoid increasing ban timeout.
@@ -117,7 +113,7 @@ impl BanningTransactionQueue {
}
}
}
self.queue.add(transaction, TransactionOrigin::External, time, None, account_details, gas_estimator)
self.queue.add(transaction, TransactionOrigin::External, time, None, details_provider)
}
/// Ban transaction with given hash.
@@ -220,22 +216,16 @@ mod tests {
use transaction::{Transaction, SignedTransaction, Action};
use error::{Error, TransactionError};
use client::TransactionImportResult;
use miner::{TransactionQueue, TransactionOrigin, AccountDetails};
use miner::{TransactionQueue, TransactionOrigin};
use util::{Uint, U256, Address, FromHex, Hashable};
use miner::transaction_queue::test::DummyTransactionDetailsProvider;
fn queue() -> BanningTransactionQueue {
BanningTransactionQueue::new(TransactionQueue::default(), Threshold::BanAfter(1), Duration::from_secs(180))
}
fn default_account_details(_address: &Address) -> AccountDetails {
AccountDetails {
nonce: U256::zero(),
balance: !U256::zero(),
}
}
fn gas_required(_tx: &SignedTransaction) -> U256 {
0.into()
fn default_tx_provider() -> DummyTransactionDetailsProvider {
DummyTransactionDetailsProvider::default().with_account_nonce(U256::zero())
}
fn transaction(action: Action) -> SignedTransaction {
@@ -265,7 +255,7 @@ mod tests {
let mut txq = queue();
// when
txq.queue().add(tx, TransactionOrigin::External, 0, None, &default_account_details, &gas_required).unwrap();
txq.queue().add(tx, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
// then
// should also deref to queue
@@ -281,12 +271,12 @@ mod tests {
let banlist1 = txq.ban_sender(tx.sender().unwrap());
assert!(!banlist1, "Threshold not reached yet.");
// Insert once
let import1 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required).unwrap();
let import1 = txq.add_with_banlist(tx.clone(), 0, &default_tx_provider()).unwrap();
assert_eq!(import1, TransactionImportResult::Current);
// when
let banlist2 = txq.ban_sender(tx.sender().unwrap());
let import2 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required);
let import2 = txq.add_with_banlist(tx.clone(), 0, &default_tx_provider());
// then
assert!(banlist2, "Threshold should be reached - banned.");
@@ -305,12 +295,12 @@ mod tests {
let banlist1 = txq.ban_recipient(recipient);
assert!(!banlist1, "Threshold not reached yet.");
// Insert once
let import1 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required).unwrap();
let import1 = txq.add_with_banlist(tx.clone(), 0, &default_tx_provider()).unwrap();
assert_eq!(import1, TransactionImportResult::Current);
// when
let banlist2 = txq.ban_recipient(recipient);
let import2 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required);
let import2 = txq.add_with_banlist(tx.clone(), 0, &default_tx_provider());
// then
assert!(banlist2, "Threshold should be reached - banned.");
@@ -327,12 +317,12 @@ mod tests {
let banlist1 = txq.ban_codehash(codehash);
assert!(!banlist1, "Threshold not reached yet.");
// Insert once
let import1 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required).unwrap();
let import1 = txq.add_with_banlist(tx.clone(), 0, &default_tx_provider()).unwrap();
assert_eq!(import1, TransactionImportResult::Current);
// when
let banlist2 = txq.ban_codehash(codehash);
let import2 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required);
let import2 = txq.add_with_banlist(tx.clone(), 0, &default_tx_provider());
// then
assert!(banlist2, "Threshold should be reached - banned.");

View File

@@ -70,36 +70,43 @@ impl LocalTransactionsList {
}
pub fn mark_pending(&mut self, hash: H256) {
debug!(target: "own_tx", "Imported to Current (hash {:?})", hash);
self.clear_old();
self.transactions.insert(hash, Status::Pending);
}
pub fn mark_future(&mut self, hash: H256) {
debug!(target: "own_tx", "Imported to Future (hash {:?})", hash);
self.transactions.insert(hash, Status::Future);
self.clear_old();
}
pub fn mark_rejected(&mut self, tx: SignedTransaction, err: TransactionError) {
debug!(target: "own_tx", "Transaction rejected (hash {:?}): {:?}", tx.hash(), err);
self.transactions.insert(tx.hash(), Status::Rejected(tx, err));
self.clear_old();
}
pub fn mark_replaced(&mut self, tx: SignedTransaction, gas_price: U256, hash: H256) {
debug!(target: "own_tx", "Transaction replaced (hash {:?}) by {:?} (new gas price: {:?})", tx.hash(), hash, gas_price);
self.transactions.insert(tx.hash(), Status::Replaced(tx, gas_price, hash));
self.clear_old();
}
pub fn mark_invalid(&mut self, tx: SignedTransaction) {
warn!(target: "own_tx", "Transaction marked invalid (hash {:?})", tx.hash());
self.transactions.insert(tx.hash(), Status::Invalid(tx));
self.clear_old();
}
pub fn mark_dropped(&mut self, tx: SignedTransaction) {
warn!(target: "own_tx", "Transaction dropped (hash {:?})", tx.hash());
self.transactions.insert(tx.hash(), Status::Dropped(tx));
self.clear_old();
}
pub fn mark_mined(&mut self, tx: SignedTransaction) {
info!(target: "own_tx", "Transaction mined (hash {:?})", tx.hash());
self.transactions.insert(tx.hash(), Status::Mined(tx));
self.clear_old();
}

View File

@@ -26,15 +26,17 @@ use client::TransactionImportResult;
use executive::contract_address;
use block::{ClosedBlock, IsBlock, Block};
use error::*;
use transaction::{Action, SignedTransaction, PendingTransaction};
use transaction::{Action, SignedTransaction, PendingTransaction, Condition as TransactionCondition};
use receipt::{Receipt, RichReceipt};
use spec::Spec;
use engines::{Engine, Seal};
use miner::{MinerService, MinerStatus, TransactionQueue, PrioritizationStrategy, AccountDetails, TransactionOrigin};
use miner::{MinerService, MinerStatus, TransactionQueue, TransactionQueueDetailsProvider, PrioritizationStrategy,
AccountDetails, TransactionOrigin};
use miner::banning_queue::{BanningTransactionQueue, Threshold};
use miner::work_notify::WorkPoster;
use miner::price_info::PriceInfo;
use miner::local_transactions::{Status as LocalTransactionStatus};
use miner::service_transaction_checker::ServiceTransactionChecker;
use header::BlockNumber;
/// Different possible definitions for pending transaction set.
@@ -103,8 +105,10 @@ pub struct MinerOptions {
pub enable_resubmission: bool,
/// Global gas limit for all transaction in the queue except for local and retracted.
pub tx_queue_gas_limit: GasLimit,
/// Banning settings
/// Banning settings.
pub tx_queue_banning: Banning,
/// Do we refuse to accept service transactions even if sender is certified.
pub refuse_service_transactions: bool,
}
impl Default for MinerOptions {
@@ -123,6 +127,7 @@ impl Default for MinerOptions {
work_queue_size: 20,
enable_resubmission: true,
tx_queue_banning: Banning::Disabled,
refuse_service_transactions: false,
}
}
}
@@ -149,7 +154,8 @@ impl GasPriceCalibrator {
if Instant::now() >= self.next_calibration {
let usd_per_tx = self.options.usd_per_tx;
trace!(target: "miner", "Getting price info");
let price_info = PriceInfo::get(move |price: PriceInfo| {
PriceInfo::get(move |price: PriceInfo| {
trace!(target: "miner", "Price info arrived: {:?}", price);
let usd_per_eth = price.ethusd;
let wei_per_usd: f32 = 1.0e18 / usd_per_eth;
@@ -159,11 +165,7 @@ impl GasPriceCalibrator {
set_price(U256::from(wei_per_gas as u64));
});
if price_info.is_ok() {
self.next_calibration = Instant::now() + self.options.recalibration_period;
} else {
warn!(target: "miner", "Unable to update Ether price.");
}
self.next_calibration = Instant::now() + self.options.recalibration_period;
}
}
}
@@ -225,6 +227,7 @@ pub struct Miner {
accounts: Option<Arc<AccountProvider>>,
work_poster: Option<WorkPoster>,
gas_pricer: Mutex<GasPricer>,
service_transaction_action: ServiceTransactionAction,
}
impl Miner {
@@ -248,6 +251,10 @@ impl Miner {
ban_duration,
),
};
let service_transaction_action = match options.refuse_service_transactions {
true => ServiceTransactionAction::Refuse,
false => ServiceTransactionAction::Check(ServiceTransactionChecker::default()),
};
Miner {
transaction_queue: Arc::new(Mutex::new(txq)),
next_allowed_reseal: Mutex::new(Instant::now()),
@@ -267,6 +274,7 @@ impl Miner {
engine: spec.engine.clone(),
work_poster: work_poster,
gas_pricer: Mutex::new(gas_pricer),
service_transaction_action: service_transaction_action,
}
}
@@ -307,20 +315,10 @@ impl Miner {
#[cfg_attr(feature="dev", allow(match_same_arms))]
/// Prepares new block for sealing including top transactions from queue.
fn prepare_block(&self, chain: &MiningBlockChainClient) -> (ClosedBlock, Option<H256>) {
{
trace!(target: "miner", "prepare_block: recalibrating...");
let txq = self.transaction_queue.clone();
self.gas_pricer.lock().recalibrate(move |price| {
trace!(target: "miner", "prepare_block: Got gas price! {}", price);
txq.lock().set_minimal_gas_price(price);
});
trace!(target: "miner", "prepare_block: done recalibration.");
}
let _timer = PerfTimer::new("prepare_block");
let chain_info = chain.chain_info();
let (transactions, mut open_block, original_work_hash) = {
let transactions = {self.transaction_queue.lock().top_transactions_at(chain_info.best_block_number)};
let transactions = {self.transaction_queue.lock().top_transactions_at(chain_info.best_block_number, chain_info.best_block_timestamp)};
let mut sealing_work = self.sealing_work.lock();
let last_work_hash = sealing_work.queue.peek_last_ref().map(|pb| pb.block().fields().header.hash());
let best_hash = chain_info.best_block_hash;
@@ -431,6 +429,16 @@ impl Miner {
(block, original_work_hash)
}
/// Asynchronously updates minimal gas price for transaction queue
pub fn recalibrate_minimal_gas_price(&self) {
debug!(target: "miner", "minimal_gas_price: recalibrating...");
let txq = self.transaction_queue.clone();
self.gas_pricer.lock().recalibrate(move |price| {
debug!(target: "miner", "minimal_gas_price: Got gas price! {}", price);
txq.lock().set_minimal_gas_price(price);
});
}
/// Check is reseal is allowed and necessary.
fn requires_reseal(&self, best_block: BlockNumber) -> bool {
let has_local_transactions = self.transaction_queue.lock().has_local_pending_transactions();
@@ -530,8 +538,8 @@ impl Miner {
}
}
fn update_gas_limit(&self, chain: &MiningBlockChainClient) {
let gas_limit = chain.best_block_header().gas_limit();
fn update_gas_limit(&self, client: &MiningBlockChainClient) {
let gas_limit = client.best_block_header().gas_limit();
let mut queue = self.transaction_queue.lock();
queue.set_gas_limit(gas_limit);
if let GasLimit::Auto = self.options.tx_queue_gas_limit {
@@ -541,7 +549,7 @@ impl Miner {
}
/// Returns true if we had to prepare new pending block.
fn prepare_work_sealing(&self, chain: &MiningBlockChainClient) -> bool {
fn prepare_work_sealing(&self, client: &MiningBlockChainClient) -> bool {
trace!(target: "miner", "prepare_work_sealing: entering");
let prepare_new = {
let mut sealing_work = self.sealing_work.lock();
@@ -559,11 +567,11 @@ impl Miner {
// | NOTE Code below requires transaction_queue and sealing_work locks. |
// | Make sure to release the locks before calling that method. |
// --------------------------------------------------------------------------
let (block, original_work_hash) = self.prepare_block(chain);
let (block, original_work_hash) = self.prepare_block(client);
self.prepare_work(block, original_work_hash);
}
let mut sealing_block_last_request = self.sealing_block_last_request.lock();
let best_number = chain.chain_info().best_block_number;
let best_number = client.chain_info().best_block_number;
if *sealing_block_last_request != best_number {
trace!(target: "miner", "prepare_work_sealing: Miner received request (was {}, now {}) - waking up.", *sealing_block_last_request, best_number);
*sealing_block_last_request = best_number;
@@ -575,54 +583,45 @@ impl Miner {
fn add_transactions_to_queue(
&self,
chain: &MiningBlockChainClient,
client: &MiningBlockChainClient,
transactions: Vec<SignedTransaction>,
default_origin: TransactionOrigin,
min_block: Option<BlockNumber>,
transaction_queue: &mut BanningTransactionQueue)
-> Vec<Result<TransactionImportResult, Error>> {
let fetch_account = |a: &Address| AccountDetails {
nonce: chain.latest_nonce(a),
balance: chain.latest_balance(a),
};
condition: Option<TransactionCondition>,
transaction_queue: &mut BanningTransactionQueue,
) -> Vec<Result<TransactionImportResult, Error>> {
let accounts = self.accounts.as_ref()
.and_then(|provider| provider.accounts().ok())
.map(|accounts| accounts.into_iter().collect::<HashSet<_>>());
let schedule = chain.latest_schedule();
let gas_required = |tx: &SignedTransaction| tx.gas_required(&schedule).into();
let best_block_header = chain.best_block_header().decode();
let insertion_time = chain.chain_info().best_block_number;
let insertion_time = client.chain_info().best_block_number;
let best_header = client.best_block_header().decode();
transactions.into_iter()
.map(|tx| {
if chain.transaction_block(TransactionId::Hash(tx.hash())).is_some() {
debug!(target: "miner", "Rejected tx {:?}: already in the blockchain", tx.hash());
let hash = tx.hash();
if client.transaction_block(TransactionId::Hash(hash)).is_some() {
debug!(target: "miner", "Rejected tx {:?}: already in the blockchain", hash);
return Err(Error::Transaction(TransactionError::AlreadyImported));
}
match self.engine.verify_transaction_basic(&tx, &best_block_header) {
Err(e) => {
debug!(target: "miner", "Rejected tx {:?} with invalid signature: {:?}", tx.hash(), e);
Err(e)
},
Ok(()) => {
let origin = accounts.as_ref().and_then(|accounts| {
tx.sender().ok().and_then(|sender| match accounts.contains(&sender) {
true => Some(TransactionOrigin::Local),
false => None,
})
}).unwrap_or(default_origin);
let origin = accounts.as_ref().and_then(|accounts| {
tx.sender().ok().and_then(|sender| match accounts.contains(&sender) {
true => Some(TransactionOrigin::Local),
false => None,
})
}).unwrap_or(default_origin);
match origin {
TransactionOrigin::Local | TransactionOrigin::RetractedBlock => {
transaction_queue.add(tx, origin, insertion_time, min_block, &fetch_account, &gas_required)
},
TransactionOrigin::External => {
transaction_queue.add_with_banlist(tx, insertion_time, &fetch_account, &gas_required)
}
}
// try to install service transaction checker before appending transactions
self.service_transaction_action.update_from_chain_client(client);
self.engine.verify_transaction_basic(&tx, &best_header)?;
let details_provider = TransactionDetailsProvider::new(client, &self.service_transaction_action);
match origin {
TransactionOrigin::Local | TransactionOrigin::RetractedBlock => {
transaction_queue.add(tx, origin, insertion_time, condition.clone(), &details_provider)
},
TransactionOrigin::External => {
transaction_queue.add_with_banlist(tx, insertion_time, &details_provider)
},
}
})
@@ -673,7 +672,7 @@ impl MinerService for Miner {
}
}
fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, CallError> {
fn call(&self, client: &MiningBlockChainClient, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, CallError> {
let sealing_work = self.sealing_work.lock();
match sealing_work.queue.peek_last_ref() {
Some(work) => {
@@ -681,7 +680,7 @@ impl MinerService for Miner {
// TODO: merge this code with client.rs's fn call somwhow.
let header = block.header();
let last_hashes = Arc::new(chain.last_hashes());
let last_hashes = Arc::new(client.last_hashes());
let env_info = EnvInfo {
number: header.number(),
author: *header.author(),
@@ -706,16 +705,14 @@ impl MinerService for Miner {
state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty);
}
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
let mut ret = Executive::new(&mut state, &env_info, &*self.engine, chain.vm_factory()).transact(t, options)?;
let mut ret = Executive::new(&mut state, &env_info, &*self.engine, client.vm_factory()).transact(t, options)?;
// TODO gav move this into Executive.
ret.state_diff = original_state.map(|original| state.diff_from(original));
Ok(ret)
},
None => {
chain.call(t, BlockId::Latest, analytics)
}
None => client.call(t, BlockId::Latest, analytics)
}
}
@@ -872,23 +869,20 @@ impl MinerService for Miner {
pending: PendingTransaction,
) -> Result<TransactionImportResult, Error> {
let hash = pending.transaction.hash();
trace!(target: "own_tx", "Importing transaction: {:?}", pending);
let imported = {
// Be sure to release the lock before we call prepare_work_sealing
let mut transaction_queue = self.transaction_queue.lock();
let import = self.add_transactions_to_queue(
chain, vec![pending.transaction], TransactionOrigin::Local, pending.min_block, &mut transaction_queue
chain, vec![pending.transaction], TransactionOrigin::Local, pending.condition, &mut transaction_queue
).pop().expect("one result returned per added transaction; one added => one result; qed");
match import {
Ok(ref res) => {
trace!(target: "own_tx", "Imported transaction to {:?} (hash: {:?})", res, hash);
Ok(_) => {
trace!(target: "own_tx", "Status: {:?}", transaction_queue.status());
},
Err(ref e) => {
trace!(target: "own_tx", "Failed to import transaction {:?} (hash: {:?})", e, hash);
trace!(target: "own_tx", "Status: {:?}", transaction_queue.status());
warn!(target: "own_tx", "Error importing transaction: {:?}", e);
},
@@ -916,7 +910,7 @@ impl MinerService for Miner {
fn pending_transactions(&self) -> Vec<PendingTransaction> {
let queue = self.transaction_queue.lock();
queue.pending_transactions(BlockNumber::max_value())
queue.pending_transactions(BlockNumber::max_value(), u64::max_value())
}
fn local_transactions(&self) -> BTreeMap<H256, LocalTransactionStatus> {
@@ -931,14 +925,14 @@ impl MinerService for Miner {
self.transaction_queue.lock().future_transactions()
}
fn ready_transactions(&self, best_block: BlockNumber) -> Vec<PendingTransaction> {
fn ready_transactions(&self, best_block: BlockNumber, best_block_timestamp: u64) -> Vec<PendingTransaction> {
let queue = self.transaction_queue.lock();
match self.options.pending_set {
PendingSet::AlwaysQueue => queue.pending_transactions(best_block),
PendingSet::AlwaysQueue => queue.pending_transactions(best_block, best_block_timestamp),
PendingSet::SealingOrElseQueue => {
self.from_pending_block(
best_block,
|| queue.pending_transactions(best_block),
|| queue.pending_transactions(best_block, best_block_timestamp),
|sealing| sealing.transactions().iter().map(|t| t.clone().into()).collect()
)
},
@@ -1126,6 +1120,9 @@ impl MinerService for Miner {
// First update gas limit in transaction queue
self.update_gas_limit(chain);
// Update minimal gas price
self.recalibrate_minimal_gas_price();
// Then import all transactions...
{
retracted.par_iter()
@@ -1167,6 +1164,60 @@ impl MinerService for Miner {
}
}
/// Action when service transaction is received
enum ServiceTransactionAction {
/// Refuse service transaction immediately
Refuse,
/// Accept if sender is certified to send service transactions
Check(ServiceTransactionChecker),
}
impl ServiceTransactionAction {
pub fn update_from_chain_client(&self, client: &MiningBlockChainClient) {
if let ServiceTransactionAction::Check(ref checker) = *self {
checker.update_from_chain_client(client);
}
}
pub fn check(&self, client: &MiningBlockChainClient, tx: &SignedTransaction) -> Result<bool, String> {
match *self {
ServiceTransactionAction::Refuse => Err("configured to refuse service transactions".to_owned()),
ServiceTransactionAction::Check(ref checker) => checker.check(client, tx),
}
}
}
struct TransactionDetailsProvider<'a> {
client: &'a MiningBlockChainClient,
service_transaction_action: &'a ServiceTransactionAction,
}
impl<'a> TransactionDetailsProvider<'a> {
pub fn new(client: &'a MiningBlockChainClient, service_transaction_action: &'a ServiceTransactionAction) -> Self {
TransactionDetailsProvider {
client: client,
service_transaction_action: service_transaction_action,
}
}
}
impl<'a> TransactionQueueDetailsProvider for TransactionDetailsProvider<'a> {
fn fetch_account(&self, address: &Address) -> AccountDetails {
AccountDetails {
nonce: self.client.latest_nonce(address),
balance: self.client.latest_balance(address),
}
}
fn estimate_gas_required(&self, tx: &SignedTransaction) -> U256 {
tx.gas_required(&self.client.latest_schedule()).into()
}
fn is_service_transaction_acceptable(&self, tx: &SignedTransaction) -> Result<bool, String> {
self.service_transaction_action.check(self.client, tx)
}
}
#[cfg(test)]
mod tests {
@@ -1231,6 +1282,7 @@ mod tests {
work_queue_size: 5,
enable_resubmission: true,
tx_queue_banning: Banning::Disabled,
refuse_service_transactions: false,
},
GasPricer::new_fixed(0u64.into()),
&Spec::new_test(),
@@ -1263,7 +1315,7 @@ mod tests {
// then
assert_eq!(res.unwrap(), TransactionImportResult::Current);
assert_eq!(miner.pending_transactions().len(), 1);
assert_eq!(miner.ready_transactions(best_block).len(), 1);
assert_eq!(miner.ready_transactions(best_block, 0).len(), 1);
assert_eq!(miner.pending_transactions_hashes(best_block).len(), 1);
assert_eq!(miner.pending_receipts(best_block).len(), 1);
// This method will let us know if pending block was created (before calling that method)
@@ -1283,7 +1335,7 @@ mod tests {
// then
assert_eq!(res.unwrap(), TransactionImportResult::Current);
assert_eq!(miner.pending_transactions().len(), 1);
assert_eq!(miner.ready_transactions(best_block).len(), 0);
assert_eq!(miner.ready_transactions(best_block, 0).len(), 0);
assert_eq!(miner.pending_transactions_hashes(best_block).len(), 0);
assert_eq!(miner.pending_receipts(best_block).len(), 0);
}
@@ -1302,7 +1354,7 @@ mod tests {
assert_eq!(res.unwrap(), TransactionImportResult::Current);
assert_eq!(miner.pending_transactions().len(), 1);
assert_eq!(miner.pending_transactions_hashes(best_block).len(), 0);
assert_eq!(miner.ready_transactions(best_block).len(), 0);
assert_eq!(miner.ready_transactions(best_block, 0).len(), 0);
assert_eq!(miner.pending_receipts(best_block).len(), 0);
// This method will let us know if pending block was created (before calling that method)
assert!(miner.prepare_work_sealing(&client));

View File

@@ -46,12 +46,14 @@ mod external;
mod local_transactions;
mod miner;
mod price_info;
mod service_transaction_checker;
mod transaction_queue;
mod work_notify;
pub use self::external::{ExternalMiner, ExternalMinerService};
pub use self::miner::{Miner, MinerOptions, Banning, PendingSet, GasPricer, GasPriceCalibratorOptions, GasLimit};
pub use self::transaction_queue::{TransactionQueue, PrioritizationStrategy, AccountDetails, TransactionOrigin};
pub use self::transaction_queue::{TransactionQueue, TransactionDetailsProvider as TransactionQueueDetailsProvider,
PrioritizationStrategy, AccountDetails, TransactionOrigin};
pub use self::local_transactions::{Status as LocalTransactionStatus};
pub use client::TransactionImportResult;
@@ -148,7 +150,7 @@ pub trait MinerService : Send + Sync {
fn pending_transactions(&self) -> Vec<PendingTransaction>;
/// Get a list of all transactions that can go into the given block.
fn ready_transactions(&self, best_block: BlockNumber) -> Vec<PendingTransaction>;
fn ready_transactions(&self, best_block: BlockNumber, best_block_timestamp: u64) -> Vec<PendingTransaction>;
/// Get a list of all future transactions.
fn future_transactions(&self) -> Vec<PendingTransaction>;

View File

@@ -21,7 +21,7 @@ use std::time::Duration;
use std::str::FromStr;
use std::sync::mpsc;
use hyper::client::{Handler, Request, Response, Client};
use hyper::{Next, Encoder, Decoder};
use hyper::{Url, Next, Encoder, Decoder};
use hyper::net::HttpStream;
#[derive(Debug)]
@@ -29,12 +29,12 @@ pub struct PriceInfo {
pub ethusd: f32,
}
pub struct SetPriceHandler<F: Fn(PriceInfo) + Sync + Send + 'static> {
pub struct SetPriceHandler<F> {
set_price: F,
channel: mpsc::Sender<()>,
}
impl<F: Fn(PriceInfo) + Sync + Send + 'static> Drop for SetPriceHandler<F> {
impl<F> Drop for SetPriceHandler<F> {
fn drop(&mut self) {
let _ = self.channel.send(());
}
@@ -47,42 +47,66 @@ impl<F: Fn(PriceInfo) + Sync + Send + 'static> Handler<HttpStream> for SetPriceH
fn on_response_readable(&mut self, r: &mut Decoder<HttpStream>) -> Next {
let mut body = String::new();
let _ = r.read_to_string(&mut body).ok()
.and_then(|_| Json::from_str(&body).ok())
.and_then(|json| json.find_path(&["result", "ethusd"])
.and_then(|obj| match *obj {
Json::String(ref s) => Some((self.set_price)(PriceInfo {
ethusd: FromStr::from_str(s)
.expect("Etherscan API will always return properly formatted price; qed")
})),
_ => None,
}));
let info = r.read_to_string(&mut body)
.map_err(|e| format!("Unable to read response: {:?}", e))
.and_then(|_| self.process_response(&body));
if let Err(e) = info {
warn!("Failed to auto-update latest ETH price: {:?}", e);
}
Next::end()
}
}
impl PriceInfo {
pub fn get<F: Fn(PriceInfo) + Sync + Send + 'static>(set_price: F) -> Result<(), ()> {
// TODO: Handle each error type properly
let client = Client::new().map_err(|_| ())?;
thread::spawn(move || {
let (tx, rx) = mpsc::channel();
let url = FromStr::from_str("http://api.etherscan.io/api?module=stats&action=ethprice")
.expect("string known to be a valid URL; qed");
let _ = client.request(
url,
SetPriceHandler {
set_price: set_price,
channel: tx,
}).ok().and_then(|_| rx.recv().ok());
client.close();
impl<F: Fn(PriceInfo) + Sync + Send + 'static> SetPriceHandler<F> {
fn process_response(&self, body: &str) -> Result<(), String> {
let json = Json::from_str(body).map_err(|e| format!("Invalid JSON returned: {:?}", e))?;
let obj = json.find_path(&["result", "ethusd"]).ok_or("USD price not found".to_owned())?;
let ethusd = match *obj {
Json::String(ref s) => FromStr::from_str(s).ok(),
_ => None,
}.ok_or("Unexpected price format.".to_owned())?;
(self.set_price)(PriceInfo {
ethusd: ethusd,
});
Ok(())
}
}
#[test]
impl PriceInfo {
pub fn get<F: Fn(PriceInfo) + Sync + Send + 'static>(set_price: F) {
thread::spawn(move || {
let url = FromStr::from_str("http://api.etherscan.io/api?module=stats&action=ethprice")
.expect("string known to be a valid URL; qed");
if let Err(e) = Self::request(url, set_price) {
warn!("Failed to auto-update latest ETH price: {:?}", e);
}
});
}
fn request<F: Fn(PriceInfo) + Send + Sync + 'static>(url: Url, set_price: F) -> Result<(), String> {
let (tx, rx) = mpsc::channel();
let client = Client::new().map_err(|e| format!("Unable to start client: {:?}", e))?;
client.request(
url,
SetPriceHandler {
set_price: set_price,
channel: tx,
},
).map_err(|_| "Request failed.".to_owned())?;
// Wait for exit
let _ = rx.recv().map_err(|e| format!("Request interrupted: {:?}", e))?;
client.close();
Ok(())
}
}
#[test] #[ignore]
fn should_get_price_info() {
use std::sync::Arc;
use std::time::Duration;
@@ -93,7 +117,7 @@ fn should_get_price_info() {
let done = Arc::new((Mutex::new(PriceInfo { ethusd: 0f32 }), Condvar::new()));
let rdone = done.clone();
PriceInfo::get(move |price| { let mut p = rdone.0.lock(); *p = price; rdone.1.notify_one(); }).unwrap();
PriceInfo::get(move |price| { let mut p = rdone.0.lock(); *p = price; rdone.1.notify_one(); });
let mut p = done.0.lock();
let t = done.1.wait_for(&mut p, Duration::from_millis(10000));
assert!(!t.timed_out());

View File

@@ -0,0 +1,212 @@
// Copyright 2017 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/>.
use client::MiningBlockChainClient;
use transaction::SignedTransaction;
use util::{U256, Uint, Mutex};
const SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME: &'static str = "service_transaction_checker";
/// Service transactions checker.
#[derive(Default)]
pub struct ServiceTransactionChecker {
contract: Mutex<Option<provider::Contract>>,
}
impl ServiceTransactionChecker {
/// Try to create instance, reading contract address from given chain client.
pub fn update_from_chain_client(&self, client: &MiningBlockChainClient) {
let mut contract = self.contract.lock();
if contract.is_none() {
*contract = client.registry_address(SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME.to_owned())
.and_then(|contract_addr| {
trace!(target: "txqueue", "Configuring for service transaction checker contract from {}", contract_addr);
Some(provider::Contract::new(contract_addr))
})
}
}
/// Checks if service transaction can be appended to the transaction queue.
pub fn check(&self, client: &MiningBlockChainClient, tx: &SignedTransaction) -> Result<bool, String> {
debug_assert_eq!(tx.gas_price, U256::zero());
if let Some(ref contract) = *self.contract.lock() {
let do_call = |a, d| client.call_contract(a, d);
contract.certified(&do_call, &tx.sender().unwrap())
} else {
Err("contract is not configured".to_owned())
}
}
}
mod provider {
// Autogenerated from JSON contract definition using Rust contract convertor.
// Command line: --jsonabi=SimpleCertifier.abi --explicit-do-call
#![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,
}
impl Contract {
pub fn new(address: util::Address) -> Self
{
Contract {
contract: ethabi::Contract::new(ethabi::Interface::load(b"[{\"constant\":false,\"inputs\":[{\"name\":\"_new\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_who\",\"type\":\"address\"}],\"name\":\"certify\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_who\",\"type\":\"address\"},{\"name\":\"_field\",\"type\":\"string\"}],\"name\":\"getAddress\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_who\",\"type\":\"address\"}],\"name\":\"revoke\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"delegate\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_who\",\"type\":\"address\"},{\"name\":\"_field\",\"type\":\"string\"}],\"name\":\"getUint\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_new\",\"type\":\"address\"}],\"name\":\"setDelegate\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_who\",\"type\":\"address\"}],\"name\":\"certified\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_who\",\"type\":\"address\"},{\"name\":\"_field\",\"type\":\"string\"}],\"name\":\"get\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"type\":\"function\"}]").expect("JSON is autogenerated; qed")),
address: address,
}
}
fn as_string<T: fmt::Debug>(e: T) -> String { format!("{:?}", e) }
/// Auto-generated from: `{"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"}`
#[allow(dead_code)]
pub fn set_owner<F>(&self, do_call: &F, _new: &util::Address) -> Result<(), String>
where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send {
let call = self.contract.function("setOwner".into()).map_err(Self::as_string)?;
let data = call.encode_call(
vec![ethabi::Token::Address(_new.clone().0)]
).map_err(Self::as_string)?;
call.decode_output((do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
Ok(())
}
/// Auto-generated from: `{"constant":false,"inputs":[{"name":"_who","type":"address"}],"name":"certify","outputs":[],"payable":false,"type":"function"}`
#[allow(dead_code)]
pub fn certify<F>(&self, do_call: &F, _who: &util::Address) -> Result<(), String>
where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send {
let call = self.contract.function("certify".into()).map_err(Self::as_string)?;
let data = call.encode_call(
vec![ethabi::Token::Address(_who.clone().0)]
).map_err(Self::as_string)?;
call.decode_output((do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
Ok(())
}
/// Auto-generated from: `{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"getAddress","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"}`
#[allow(dead_code)]
pub fn get_address<F>(&self, do_call: &F, _who: &util::Address, _field: &str) -> Result<util::Address, String>
where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send {
let call = self.contract.function("getAddress".into()).map_err(Self::as_string)?;
let data = call.encode_call(
vec![ethabi::Token::Address(_who.clone().0), ethabi::Token::String(_field.to_owned())]
).map_err(Self::as_string)?;
let output = call.decode_output((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_address().ok_or("Invalid type returned")?; util::Address::from(r) }))
}
/// Auto-generated from: `{"constant":false,"inputs":[{"name":"_who","type":"address"}],"name":"revoke","outputs":[],"payable":false,"type":"function"}`
#[allow(dead_code)]
pub fn revoke<F>(&self, do_call: &F, _who: &util::Address) -> Result<(), String>
where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send {
let call = self.contract.function("revoke".into()).map_err(Self::as_string)?;
let data = call.encode_call(
vec![ethabi::Token::Address(_who.clone().0)]
).map_err(Self::as_string)?;
call.decode_output((do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
Ok(())
}
/// Auto-generated from: `{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"}`
#[allow(dead_code)]
pub fn owner<F>(&self, do_call: &F) -> Result<util::Address, String>
where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send {
let call = self.contract.function("owner".into()).map_err(Self::as_string)?;
let data = call.encode_call(
vec![]
).map_err(Self::as_string)?;
let output = call.decode_output((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_address().ok_or("Invalid type returned")?; util::Address::from(r) }))
}
/// Auto-generated from: `{"constant":true,"inputs":[],"name":"delegate","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"}`
#[allow(dead_code)]
pub fn delegate<F>(&self, do_call: &F) -> Result<util::Address, String>
where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send {
let call = self.contract.function("delegate".into()).map_err(Self::as_string)?;
let data = call.encode_call(
vec![]
).map_err(Self::as_string)?;
let output = call.decode_output((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_address().ok_or("Invalid type returned")?; util::Address::from(r) }))
}
/// Auto-generated from: `{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"getUint","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"}`
#[allow(dead_code)]
pub fn get_uint<F>(&self, do_call: &F, _who: &util::Address, _field: &str) -> Result<util::U256, String>
where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send {
let call = self.contract.function("getUint".into()).map_err(Self::as_string)?;
let data = call.encode_call(
vec![ethabi::Token::Address(_who.clone().0), ethabi::Token::String(_field.to_owned())]
).map_err(Self::as_string)?;
let output = call.decode_output((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_uint().ok_or("Invalid type returned")?; util::U256::from(r.as_ref()) }))
}
/// Auto-generated from: `{"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setDelegate","outputs":[],"payable":false,"type":"function"}`
#[allow(dead_code)]
pub fn set_delegate<F>(&self, do_call: &F, _new: &util::Address) -> Result<(), String>
where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send {
let call = self.contract.function("setDelegate".into()).map_err(Self::as_string)?;
let data = call.encode_call(
vec![ethabi::Token::Address(_new.clone().0)]
).map_err(Self::as_string)?;
call.decode_output((do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
Ok(())
}
/// Auto-generated from: `{"constant":true,"inputs":[{"name":"_who","type":"address"}],"name":"certified","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"}`
#[allow(dead_code)]
pub fn certified<F>(&self, do_call: &F, _who: &util::Address) -> Result<bool, String>
where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send {
let call = self.contract.function("certified".into()).map_err(Self::as_string)?;
let data = call.encode_call(
vec![ethabi::Token::Address(_who.clone().0)]
).map_err(Self::as_string)?;
let output = call.decode_output((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_bool().ok_or("Invalid type returned")?; r }))
}
/// Auto-generated from: `{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"get","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"}`
#[allow(dead_code)]
pub fn get<F>(&self, do_call: &F, _who: &util::Address, _field: &str) -> Result<util::H256, String>
where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send {
let call = self.contract.function("get".into()).map_err(Self::as_string)?;
let data = call.encode_call(
vec![ethabi::Token::Address(_who.clone().0), ethabi::Token::String(_field.to_owned())]
).map_err(Self::as_string)?;
let output = call.decode_output((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_fixed_bytes().ok_or("Invalid type returned")?; util::H256::from_slice(r.as_ref()) }))
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -844,6 +844,7 @@ mod tests {
use std::str::FromStr;
use rustc_serialize::hex::FromHex;
use super::*;
use ethkey::Secret;
use util::{U256, H256, FixedHash, Address, Hashable};
use tests::helpers::*;
use devtools::*;
@@ -854,6 +855,10 @@ mod tests {
use trace::{FlatTrace, TraceError, trace};
use types::executed::CallType;
fn secret() -> Secret {
Secret::from_slice(&"".sha3()).unwrap()
}
#[test]
fn should_apply_create_transaction() {
init_log();
@@ -872,7 +877,7 @@ mod tests {
action: Action::Create,
value: 100.into(),
data: FromHex::from_hex("601080600c6000396000f3006000355415600957005b60203560003555").unwrap(),
}.sign(&"".sha3(), None);
}.sign(&secret(), None);
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
let result = state.apply(&info, &engine, &t, true).unwrap();
@@ -932,7 +937,7 @@ mod tests {
action: Action::Create,
value: 100.into(),
data: FromHex::from_hex("5b600056").unwrap(),
}.sign(&"".sha3(), None);
}.sign(&secret(), None);
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
let result = state.apply(&info, &engine, &t, true).unwrap();
@@ -969,7 +974,7 @@ mod tests {
action: Action::Call(0xa.into()),
value: 100.into(),
data: vec![],
}.sign(&"".sha3(), None);
}.sign(&secret(), None);
state.init_code(&0xa.into(), FromHex::from_hex("6000").unwrap());
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
@@ -1012,7 +1017,7 @@ mod tests {
action: Action::Call(0xa.into()),
value: 100.into(),
data: vec![],
}.sign(&"".sha3(), None);
}.sign(&secret(), None);
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
let result = state.apply(&info, &engine, &t, true).unwrap();
@@ -1054,7 +1059,7 @@ mod tests {
action: Action::Call(0x1.into()),
value: 0.into(),
data: vec![],
}.sign(&"".sha3(), None);
}.sign(&secret(), None);
let result = state.apply(&info, engine, &t, true).unwrap();
@@ -1096,7 +1101,7 @@ mod tests {
action: Action::Call(0xa.into()),
value: 0.into(),
data: vec![],
}.sign(&"".sha3(), None);
}.sign(&secret(), None);
state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060006001610be0f1").unwrap());
let result = state.apply(&info, engine, &t, true).unwrap();
@@ -1139,7 +1144,7 @@ mod tests {
action: Action::Call(0xa.into()),
value: 0.into(),
data: vec![],
}.sign(&"".sha3(), None);
}.sign(&secret(), None);
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b611000f2").unwrap());
state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap());
@@ -1201,7 +1206,7 @@ mod tests {
action: Action::Call(0xa.into()),
value: 0.into(),
data: vec![],
}.sign(&"".sha3(), None);
}.sign(&secret(), None);
state.init_code(&0xa.into(), FromHex::from_hex("6000600060006000600b618000f4").unwrap());
state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap());
@@ -1260,7 +1265,7 @@ mod tests {
action: Action::Call(0xa.into()),
value: 100.into(),
data: vec![],
}.sign(&"".sha3(), None);
}.sign(&secret(), None);
state.init_code(&0xa.into(), FromHex::from_hex("5b600056").unwrap());
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
@@ -1300,7 +1305,7 @@ mod tests {
action: Action::Call(0xa.into()),
value: 100.into(),
data: vec![],
}.sign(&"".sha3(), None);
}.sign(&secret(), None);
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap());
@@ -1360,7 +1365,7 @@ mod tests {
action: Action::Call(0xa.into()),
value: 100.into(),
data: vec![],
}.sign(&"".sha3(), None);
}.sign(&secret(), None);
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006045600b6000f1").unwrap());
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
@@ -1415,7 +1420,7 @@ mod tests {
action: Action::Call(0xa.into()),
value: 100.into(),
data: vec![],
}.sign(&"".sha3(), None);
}.sign(&secret(), None);
state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()); // not enough funds.
state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty);
@@ -1458,7 +1463,7 @@ mod tests {
action: Action::Call(0xa.into()),
value: 100.into(),
data: vec![],//600480600b6000396000f35b600056
}.sign(&"".sha3(), None);
}.sign(&secret(), None);
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
state.init_code(&0xb.into(), FromHex::from_hex("5b600056").unwrap());
@@ -1514,7 +1519,7 @@ mod tests {
action: Action::Call(0xa.into()),
value: 100.into(),
data: vec![],
}.sign(&"".sha3(), None);
}.sign(&secret(), None);
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap());
@@ -1589,7 +1594,7 @@ mod tests {
action: Action::Call(0xa.into()),
value: 100.into(),
data: vec![],//600480600b6000396000f35b600056
}.sign(&"".sha3(), None);
}.sign(&secret(), None);
state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap());
@@ -1662,7 +1667,7 @@ mod tests {
action: Action::Call(0xa.into()),
value: 100.into(),
data: vec![],
}.sign(&"".sha3(), None);
}.sign(&secret(), None);
state.init_code(&0xa.into(), FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap());
state.add_balance(&0xa.into(), &50.into(), CleanupMode::NoEmpty);

View File

@@ -28,8 +28,8 @@ use rlp::View;
use spec::Spec;
use views::BlockView;
use util::stats::Histogram;
use ethkey::KeyPair;
use transaction::{PendingTransaction, Transaction, Action};
use ethkey::{KeyPair, Secret};
use transaction::{PendingTransaction, Transaction, Action, Condition};
use miner::MinerService;
#[test]
@@ -233,6 +233,13 @@ fn empty_gas_price_histogram() {
assert!(client.gas_price_histogram(20, 5).is_none());
}
#[test]
fn corpus_is_sorted() {
let client_result = generate_dummy_client_with_data(2, 1, slice_into![U256::from_str("11426908979").unwrap(), U256::from_str("50426908979").unwrap()]);
let client = client_result.reference();
let corpus = client.gas_price_corpus(20);
assert!(corpus[0] < corpus[1]);
}
#[test]
fn can_handle_long_fork() {
@@ -290,7 +297,7 @@ fn change_history_size() {
#[test]
fn does_not_propagate_delayed_transactions() {
let key = KeyPair::from_secret("test".sha3()).unwrap();
let key = KeyPair::from_secret(Secret::from_slice(&"test".sha3()).unwrap()).unwrap();
let secret = key.secret();
let tx0 = PendingTransaction::new(Transaction {
nonce: 0.into(),
@@ -299,7 +306,7 @@ fn does_not_propagate_delayed_transactions() {
action: Action::Call(Address::default()),
value: 0.into(),
data: Vec::new(),
}.sign(secret, None), Some(2));
}.sign(secret, None), Some(Condition::Number(2)));
let tx1 = PendingTransaction::new(Transaction {
nonce: 1.into(),
gas_price: 0.into(),

View File

@@ -163,7 +163,7 @@ pub fn generate_dummy_client_with_spec_and_data<F>(get_test_spec: F, block_numbe
let mut last_hashes = vec![];
let mut last_header = genesis_header.clone();
let kp = KeyPair::from_secret("".sha3()).unwrap();
let kp = KeyPair::from_secret_slice(&"".sha3()).unwrap();
let author = kp.address();
let mut n = 0;

View File

@@ -16,7 +16,7 @@
//! Trace database.
use std::ops::Deref;
use std::collections::HashMap;
use std::collections::{HashMap, VecDeque};
use std::sync::Arc;
use bloomchain::{Number, Config as BloomConfig};
use bloomchain::group::{BloomGroupDatabase, BloomGroupChain, GroupPosition, BloomGroup};
@@ -305,7 +305,7 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
}
fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: Vec<usize>) -> Option<LocalizedTrace> {
let trace_position_deq = trace_position.into_iter().collect();
let trace_position_deq = trace_position.into_iter().collect::<VecDeque<usize>>();
self.extras.block_hash(block_number)
.and_then(|block_hash| self.transactions_traces(&block_hash)
.and_then(|traces| traces.into_iter().nth(tx_position))

View File

@@ -34,6 +34,8 @@ pub struct BlockChainInfo {
pub best_block_hash: H256,
/// Best blockchain block number.
pub best_block_number: BlockNumber,
/// Best blockchain block timestamp.
pub best_block_timestamp: u64,
/// Best ancient block hash.
pub ancient_block_hash: Option<H256>,
/// Best ancient block number.

View File

@@ -18,6 +18,7 @@
use util::{Bytes, U256, Address, U512};
use rlp::*;
use evm;
use trace::{VMTrace, FlatTrace};
use types::log_entry::LogEntry;
use types::state_diff::StateDiff;
@@ -65,6 +66,9 @@ impl Decodable for CallType {
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "ipc", binary)]
pub struct Executed {
/// True if the outer call/create resulted in an exceptional exit.
pub exception: Option<evm::Error>,
/// Gas paid up front for execution of transaction.
pub gas: U256,
@@ -178,6 +182,8 @@ pub enum CallError {
TransactionNotFound,
/// Couldn't find requested block's state in the chain.
StatePruned,
/// Couldn't find an amount of gas that didn't result in an exception.
Exceptional,
/// Error executing.
Execution(ExecutionError),
}
@@ -195,6 +201,7 @@ impl fmt::Display for CallError {
let msg = match *self {
TransactionNotFound => "Transaction couldn't be found in the chain".into(),
StatePruned => "Couldn't find the transaction block's state in the chain".into(),
Exceptional => "An exception happened in the execution".into(),
Execution(ref e) => format!("{}", e),
};

View File

@@ -391,6 +391,14 @@ impl Res {
Res::Call(_) | Res::FailedCall(_) | Res::FailedCreate(_) | Res::None => Default::default(),
}
}
/// Did this call fail?
pub fn succeeded(&self) -> bool {
match *self {
Res::Call(_) | Res::Create(_) => true,
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq)]
@@ -561,4 +569,3 @@ impl Decodable for VMTrace {
Ok(res)
}
}

View File

@@ -16,7 +16,7 @@
//! Transaction data structure.
use std::ops::Deref;
use std::ops::{Deref, DerefMut};
use std::cell::*;
use rlp::*;
use util::sha3::Hashable;
@@ -53,6 +53,16 @@ impl Decodable for Action {
}
}
/// Transaction activation condition.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "ipc", binary)]
pub enum Condition {
/// Valid at this block number or later.
Number(BlockNumber),
/// Valid at this unix time or later.
Timestamp(u64),
}
/// A set of information describing an externally-originating message call
/// or contract creation operation.
#[derive(Default, Debug, Clone, PartialEq, Eq)]
@@ -102,6 +112,7 @@ impl HeapSizeOf for Transaction {
impl From<ethjson::state::Transaction> for SignedTransaction {
fn from(t: ethjson::state::Transaction) -> Self {
let to: Option<ethjson::hash::Address> = t.to.into();
let secret = Secret::from_slice(&t.secret.0).expect("Valid secret expected.");
Transaction {
nonce: t.nonce.into(),
gas_price: t.gas_price.into(),
@@ -112,7 +123,7 @@ impl From<ethjson::state::Transaction> for SignedTransaction {
},
value: t.value.into(),
data: t.data.into(),
}.sign(&t.secret.into(), None)
}.sign(&secret, None)
}
}
@@ -239,6 +250,12 @@ impl Deref for SignedTransaction {
}
}
impl DerefMut for SignedTransaction {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.unsigned
}
}
impl Decodable for SignedTransaction {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
@@ -400,16 +417,16 @@ impl Deref for LocalizedTransaction {
pub struct PendingTransaction {
/// Signed transaction data.
pub transaction: SignedTransaction,
/// To be activated at this block. `None` for immediately.
pub min_block: Option<BlockNumber>,
/// To be activated at this condition. `None` for immediately.
pub condition: Option<Condition>,
}
impl PendingTransaction {
/// Create a new pending transaction from signed transaction.
pub fn new(signed: SignedTransaction, min_block: Option<BlockNumber>) -> Self {
pub fn new(signed: SignedTransaction, condition: Option<Condition>) -> Self {
PendingTransaction {
transaction: signed,
min_block: min_block,
condition: condition,
}
}
}
@@ -418,7 +435,7 @@ impl From<SignedTransaction> for PendingTransaction {
fn from(t: SignedTransaction) -> Self {
PendingTransaction {
transaction: t,
min_block: None,
condition: None,
}
}
}

View File

@@ -166,7 +166,7 @@ pub mod aes {
/// ECDH functions
#[cfg_attr(feature="dev", allow(similar_names))]
pub mod ecdh {
use secp256k1::{ecdh, key};
use secp256k1::{ecdh, key, Error as SecpError};
use ethkey::{Secret, Public, SECP256K1};
use Error;
@@ -180,13 +180,11 @@ pub mod ecdh {
};
let publ = key::PublicKey::from_slice(context, &pdata)?;
// no way to create SecretKey from raw byte array.
let sec: &key::SecretKey = unsafe { ::std::mem::transmute(secret) };
let shared = ecdh::SharedSecret::new_raw(context, &publ, sec);
let sec = key::SecretKey::from_slice(context, &secret)?;
let shared = ecdh::SharedSecret::new_raw(context, &publ, &sec);
let mut s = Secret::default();
s.copy_from_slice(&shared[0..32]);
Ok(s)
Secret::from_slice(&shared[0..32])
.map_err(|_| Error::Secp(SecpError::InvalidSecretKey))
}
}

View File

@@ -15,7 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use keccak::Keccak256;
use super::{KeyPair, Error, Generator};
use super::{KeyPair, Error, Generator, Secret};
/// Simple brainwallet.
pub struct Brain(String);
@@ -34,13 +34,15 @@ impl Generator for Brain {
let mut i = 0;
loop {
secret = secret.keccak256();
match i > 16384 {
false => i += 1,
true => {
let result = KeyPair::from_secret(secret.clone().into());
if result.as_ref().ok().map_or(false, |r| r.address()[0] == 0) {
return result;
if let Ok(secret) = Secret::from_slice(&secret) {
let result = KeyPair::from_secret(secret);
if result.as_ref().ok().map_or(false, |r| r.address()[0] == 0) {
return result;
}
}
},
}

View File

@@ -60,11 +60,14 @@ impl KeyPair {
Ok(keypair)
}
pub fn from_secret_slice(slice: &[u8]) -> Result<KeyPair, Error> {
Self::from_secret(Secret::from_slice(slice)?)
}
pub fn from_keypair(sec: key::SecretKey, publ: key::PublicKey) -> Self {
let context = &SECP256K1;
let serialized = publ.serialize_vec(context, false);
let mut secret = Secret::default();
secret.copy_from_slice(&sec[0..32]);
let secret = Secret::from(sec);
let mut public = Public::default();
public.copy_from_slice(&serialized[1..65]);

View File

@@ -29,6 +29,7 @@ mod keccak;
mod prefix;
mod random;
mod signature;
mod secret;
lazy_static! {
pub static ref SECP256K1: secp256k1::Secp256k1 = secp256k1::Secp256k1::new();
@@ -46,10 +47,10 @@ pub use self::keypair::{KeyPair, public_to_address};
pub use self::prefix::Prefix;
pub use self::random::Random;
pub use self::signature::{sign, verify_public, verify_address, recover, Signature};
pub use self::secret::Secret;
use bigint::hash::{H160, H256, H512};
pub type Address = H160;
pub type Secret = H256;
pub type Message = H256;
pub type Public = H512;

69
ethkey/src/secret.rs Normal file
View File

@@ -0,0 +1,69 @@
// 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/>.
use std::fmt;
use std::ops::Deref;
use std::str::FromStr;
use secp256k1::key;
use bigint::hash::H256;
use {Error};
#[derive(Clone, PartialEq, Eq)]
pub struct Secret {
inner: H256,
}
impl fmt::Debug for Secret {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "Secret: 0x{:x}{:x}..{:x}{:x}", self.inner[0], self.inner[1], self.inner[30], self.inner[31])
}
}
impl Secret {
pub fn from_slice(key: &[u8]) -> Result<Self, Error> {
if key.len() != 32 {
return Err(Error::InvalidSecret);
}
let mut h = H256::default();
h.copy_from_slice(&key[0..32]);
Ok(Secret { inner: h })
}
}
impl FromStr for Secret {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let hash = H256::from_str(s).map_err(|e| Error::Custom(format!("{:?}", e)))?;
Self::from_slice(&hash)
}
}
impl From<key::SecretKey> for Secret {
fn from(key: key::SecretKey) -> Self {
Self::from_slice(&key[0..32])
.expect("`key::SecretKey` is valid (no way to construct invalid one); qed")
}
}
impl Deref for Secret {
type Target = H256;
fn deref(&self) -> &Self::Target {
&self.inner
}
}

View File

@@ -16,7 +16,7 @@
use std::ops::{Deref, DerefMut};
use std::cmp::PartialEq;
use std::{mem, fmt};
use std::fmt;
use std::str::FromStr;
use std::hash::{Hash, Hasher};
use secp256k1::{Message as SecpMessage, RecoverableSignature, RecoveryId, Error as SecpError};
@@ -169,9 +169,8 @@ impl DerefMut for Signature {
pub fn sign(secret: &Secret, message: &Message) -> Result<Signature, Error> {
let context = &SECP256K1;
// no way to create from raw byte array.
let sec: &SecretKey = unsafe { mem::transmute(secret) };
let s = context.sign_recoverable(&SecpMessage::from_slice(&message[..])?, sec)?;
let sec = SecretKey::from_slice(context, &secret)?;
let s = context.sign_recoverable(&SecpMessage::from_slice(&message[..])?, &sec)?;
let (rec_id, data) = s.serialize_compact(context);
let mut data_arr = [0; 65];

View File

@@ -122,16 +122,14 @@ impl Crypto {
return Err(Error::InvalidPassword);
}
let mut secret = Secret::default();
match self.cipher {
Cipher::Aes128Ctr(ref params) => {
let from = 32 - self.ciphertext.len();
crypto::aes::decrypt(&derived_left_bits, &params.iv, &self.ciphertext, &mut (&mut *secret)[from..])
let mut secret = [0; 32];
crypto::aes::decrypt(&derived_left_bits, &params.iv, &self.ciphertext, &mut secret[from..]);
Ok(Secret::from_slice(&secret)?)
},
}
Ok(secret)
}
}

View File

@@ -271,7 +271,9 @@ impl SimpleSecretStore for EthMultiStore {
// Remove from cache
let mut cache = self.cache.write();
let is_empty = {
let mut accounts = cache.get_mut(address).expect("Entry exists, because it was returned by `get`; qed");
let mut accounts = cache.get_mut(address)
.ok_or(Error::InvalidAccount)?;
if let Some(position) = accounts.iter().position(|acc| acc == &account) {
accounts.remove(position);
}

View File

@@ -47,7 +47,7 @@ impl PresaleWallet {
let len = crypto::aes::decrypt_cbc(&derived_key, &self.iv, &self.ciphertext, &mut key).map_err(|_| Error::InvalidPassword)?;
let unpadded = &key[..len];
let secret = Secret::from(unpadded.keccak256());
let secret = Secret::from_slice(&unpadded.keccak256())?;
if let Ok(kp) = KeyPair::from_secret(secret) {
if kp.address() == self.address {
return Ok(kp)

View File

@@ -133,9 +133,9 @@ fn secret_store_load_pat_files() {
#[test]
fn test_decrypting_files_with_short_ciphertext() {
// 31e9d1e6d844bd3a536800ef8d8be6a9975db509, 30
let kp1 = KeyPair::from_secret("000081c29e8142bb6a81bef5a92bda7a8328a5c85bb2f9542e76f9b0f94fc018".into()).unwrap();
let kp1 = KeyPair::from_secret("000081c29e8142bb6a81bef5a92bda7a8328a5c85bb2f9542e76f9b0f94fc018".parse().unwrap()).unwrap();
// d1e64e5480bfaf733ba7d48712decb8227797a4e , 31
let kp2 = KeyPair::from_secret("00fa7b3db73dc7dfdf8c5fbdb796d741e4488628c41fc4febd9160a866ba0f35".into()).unwrap();
let kp2 = KeyPair::from_secret("00fa7b3db73dc7dfdf8c5fbdb796d741e4488628c41fc4febd9160a866ba0f35".parse().unwrap()).unwrap();
let dir = DiskDirectory::at(ciphertext_path());
let store = EthStore::open(Box::new(dir)).unwrap();
let accounts = store.accounts().unwrap();

View File

@@ -1,7 +1,7 @@
[package]
name = "evm"
name = "evmbin"
description = "Parity's EVM implementation"
version = "0.1.0"
version = "1.5.0"
authors = ["Parity Technologies <admin@parity.io>"]
[lib]

View File

@@ -31,7 +31,7 @@ pub struct FakeExt {
impl Default for FakeExt {
fn default() -> Self {
FakeExt {
schedule: Schedule::new_homestead_gas_fix(),
schedule: Schedule::new_post_eip150(usize::max_value(), true, true, true),
store: HashMap::new(),
depth: 1,
}
@@ -51,8 +51,8 @@ impl Ext for FakeExt {
unimplemented!();
}
fn exists_and_not_null(&self, address: &Address) -> bool {
unimplemented!();
fn exists_and_not_null(&self, _address: &Address) -> bool {
unimplemented!();
}
fn origin_balance(&self) -> U256 {

View File

@@ -21,7 +21,6 @@
extern crate ethcore;
extern crate rustc_serialize;
extern crate docopt;
#[macro_use]
extern crate ethcore_util as util;
mod ext;

View File

@@ -17,8 +17,9 @@
//! Types used in the public API
use std::fmt;
use std::str::FromStr;
use semver::{Version};
use util::{H160};
use util::{H160, FixedHash};
use util::misc::raw_package_info;
use release_track::ReleaseTrack;
@@ -47,7 +48,7 @@ impl VersionInfo {
VersionInfo {
track: raw.0.into(),
version: { let mut v = Version::parse(raw.1).expect("Environment variables are known to be valid; qed"); v.build = vec![]; v.pre = vec![]; v },
hash: raw.2.into(),
hash: H160::from_str(raw.2).unwrap_or_else(|_| H160::zero()),
}
}

View File

@@ -163,6 +163,7 @@
"phoneformat.js": "1.0.3",
"promise-worker": "1.1.1",
"push.js": "0.0.11",
"qrcode-npm": "0.0.3",
"qs": "6.3.0",
"react": "15.4.1",
"react-ace": "4.1.0",
@@ -170,6 +171,8 @@
"react-copy-to-clipboard": "4.2.3",
"react-dom": "15.4.1",
"react-dropzone": "3.7.3",
"react-element-to-jsx-string": "6.0.0",
"react-event-listener": "0.4.1",
"react-intl": "2.1.5",
"react-portal": "3.0.0",
"react-redux": "4.4.6",
@@ -185,10 +188,12 @@
"scryptsy": "2.0.0",
"solc": "ngotchac/solc-js",
"store": "1.3.20",
"useragent.js": "0.5.6",
"utf8": "2.1.2",
"valid-url": "1.0.9",
"validator": "6.2.0",
"web3": "0.17.0-beta",
"whatwg-fetch": "2.0.1"
"whatwg-fetch": "2.0.1",
"zxcvbn": "4.4.1"
}
}

View File

@@ -16,6 +16,19 @@
import { stringify } from 'querystring';
export const isServerRunning = (isTestnet = false) => {
const port = isTestnet ? 28443 : 18443;
return fetch(`https://email-verification.parity.io:${port}/health`, {
mode: 'cors', cache: 'no-store'
})
.then((res) => {
return res.ok;
})
.catch(() => {
return false;
});
};
export const postToServer = (query, isTestnet = false) => {
const port = isTestnet ? 28443 : 18443;
query = stringify(query);

View File

@@ -16,6 +16,19 @@
import { stringify } from 'querystring';
export const isServerRunning = (isTestnet = false) => {
const port = isTestnet ? 8443 : 443;
return fetch(`https://sms-verification.parity.io:${port}/health`, {
mode: 'cors', cache: 'no-store'
})
.then((res) => {
return res.ok;
})
.catch(() => {
return false;
});
};
export const postToServer = (query, isTestnet = false) => {
const port = isTestnet ? 8443 : 443;
query = stringify(query);

View File

@@ -23,12 +23,12 @@ import { eventSignature } from '../../util/signature';
export default class Event {
constructor (abi) {
this._name = abi.name;
this._inputs = EventParam.toEventParams(abi.inputs || []);
this._anonymous = !!abi.anonymous;
const { id, signature } = eventSignature(this._name, this.inputParamTypes());
const { id, name, signature } = eventSignature(abi.name, this.inputParamTypes());
this._id = id;
this._name = name;
this._signature = signature;
}

View File

@@ -22,14 +22,14 @@ import { methodSignature } from '../util/signature';
export default class Func {
constructor (abi) {
this._abi = abi;
this._name = abi.name;
this._constant = !!abi.constant;
this._payable = abi.payable;
this._inputs = Param.toParams(abi.inputs || []);
this._outputs = Param.toParams(abi.outputs || []);
const { id, signature } = methodSignature(this._name, this.inputParamTypes());
const { id, name, signature } = methodSignature(abi.name, this.inputParamTypes());
this._id = id;
this._name = name;
this._signature = signature;
}

View File

@@ -35,6 +35,18 @@ describe('abi/spec/Function', () => {
});
describe('constructor', () => {
it('returns signature correctly if name already contains it', () => {
const func = new Func({
name: 'test(bool,string)',
inputs: inputsArr,
outputs: outputsArr
});
expect(func.name).to.equal('test');
expect(func.id).to.equal('test(bool,string)');
expect(func.signature).to.equal('02356205');
});
it('stores the parameters as received', () => {
expect(func.name).to.equal('test');
expect(func.constant).to.be.false;

View File

@@ -17,15 +17,31 @@
import { keccak_256 } from 'js-sha3'; // eslint-disable-line camelcase
import { fromParamType } from '../spec/paramType/format';
export function eventSignature (name, params) {
export function eventSignature (eventName, params) {
const { strName, name } = parseName(eventName);
const types = (params || []).map(fromParamType).join(',');
const id = `${name || ''}(${types})`;
const id = `${strName}(${types})`;
return { id, signature: keccak_256(id) };
return { id, name, signature: keccak_256(id) };
}
export function methodSignature (name, params) {
const { id, signature } = eventSignature(name, params);
export function methodSignature (methodName, params) {
const { id, name, signature } = eventSignature(methodName, params);
return { id, signature: signature.substr(0, 8) };
return { id, name, signature: signature.substr(0, 8) };
}
function parseName (name) {
const strName = `${name || ''}`;
const idx = strName.indexOf('(');
if (idx === -1) {
return { strName, name };
}
const trimmedName = strName.slice(0, idx);
return {
strName: trimmedName,
name: trimmedName
};
}

View File

@@ -19,50 +19,93 @@ import { eventSignature, methodSignature } from './signature';
describe('abi/util/signature', () => {
describe('eventSignature', () => {
it('encodes signature baz() correctly', () => {
expect(eventSignature('baz', []))
.to.deep.equal({ id: 'baz()', signature: 'a7916fac4f538170f7cd12c148552e2cba9fcd72329a2dd5b07a6fa906488ddf' });
expect(eventSignature('baz', [])).to.deep.equal({
id: 'baz()',
name: 'baz',
signature: 'a7916fac4f538170f7cd12c148552e2cba9fcd72329a2dd5b07a6fa906488ddf'
});
});
it('encodes signature baz(uint32) correctly', () => {
expect(eventSignature('baz', [{ type: 'uint', length: 32 }]))
.to.deep.equal({ id: 'baz(uint32)', signature: '7d68785e8fc871be024b75964bd86d093511d4bc2dc7cf7bea32c48a0efaecb1' });
expect(eventSignature('baz', [{ type: 'uint', length: 32 }])).to.deep.equal({
id: 'baz(uint32)',
name: 'baz',
signature: '7d68785e8fc871be024b75964bd86d093511d4bc2dc7cf7bea32c48a0efaecb1'
});
});
it('encodes signature baz(uint32, bool) correctly', () => {
expect(eventSignature('baz', [{ type: 'uint', length: 32 }, { type: 'bool' }]))
.to.deep.equal({ id: 'baz(uint32,bool)', signature: 'cdcd77c0992ec5bbfc459984220f8c45084cc24d9b6efed1fae540db8de801d2' });
expect(eventSignature('baz', [{ type: 'uint', length: 32 }, { type: 'bool' }])).to.deep.equal({
id: 'baz(uint32,bool)',
name: 'baz',
signature: 'cdcd77c0992ec5bbfc459984220f8c45084cc24d9b6efed1fae540db8de801d2'
});
});
it('encodes no-name signature correctly as ()', () => {
expect(eventSignature(undefined, []))
.to.deep.equal({ id: '()', signature: '861731d50c3880a2ca1994d5ec287b94b2f4bd832a67d3e41c08177bdd5674fe' });
expect(eventSignature(undefined, [])).to.deep.equal({
id: '()',
name: undefined,
signature: '861731d50c3880a2ca1994d5ec287b94b2f4bd832a67d3e41c08177bdd5674fe'
});
});
it('encodes no-params signature correctly as ()', () => {
expect(eventSignature(undefined, undefined))
.to.deep.equal({ id: '()', signature: '861731d50c3880a2ca1994d5ec287b94b2f4bd832a67d3e41c08177bdd5674fe' });
expect(eventSignature(undefined, undefined)).to.deep.equal({
id: '()',
name: undefined,
signature: '861731d50c3880a2ca1994d5ec287b94b2f4bd832a67d3e41c08177bdd5674fe'
});
});
});
describe('methodSignature', () => {
it('encodes signature baz() correctly', () => {
expect(methodSignature('baz', [])).to.deep.equal({ id: 'baz()', signature: 'a7916fac' });
expect(methodSignature('baz', [])).to.deep.equal({
id: 'baz()',
name: 'baz',
signature: 'a7916fac'
});
});
it('encodes signature baz(uint32) correctly', () => {
expect(methodSignature('baz', [{ type: 'uint', length: 32 }])).to.deep.equal({ id: 'baz(uint32)', signature: '7d68785e' });
expect(methodSignature('baz', [{ type: 'uint', length: 32 }])).to.deep.equal({
id: 'baz(uint32)',
name: 'baz',
signature: '7d68785e'
});
});
it('encodes signature baz(uint32, bool) correctly', () => {
expect(methodSignature('baz', [{ type: 'uint', length: 32 }, { type: 'bool' }])).to.deep.equal({ id: 'baz(uint32,bool)', signature: 'cdcd77c0' });
expect(methodSignature('baz', [{ type: 'uint', length: 32 }, { type: 'bool' }])).to.deep.equal({
id: 'baz(uint32,bool)',
name: 'baz',
signature: 'cdcd77c0'
});
});
it('encodes signature in name correctly', () => {
expect(methodSignature('baz(uint32,bool)', [{ type: 'uint', length: 32 }, { type: 'bool' }])).to.deep.equal({
id: 'baz(uint32,bool)',
name: 'baz',
signature: 'cdcd77c0'
});
});
it('encodes no-name signature correctly as ()', () => {
expect(methodSignature(undefined, [])).to.deep.equal({ id: '()', signature: '861731d5' });
expect(methodSignature(undefined, [])).to.deep.equal({
id: '()',
name: undefined,
signature: '861731d5'
});
});
it('encodes no-params signature correctly as ()', () => {
expect(methodSignature(undefined, undefined)).to.deep.equal({ id: '()', signature: '861731d5' });
expect(methodSignature(undefined, undefined)).to.deep.equal({
id: '()',
name: undefined,
signature: '861731d5'
});
});
});
});

View File

@@ -14,6 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import EventEmitter from 'eventemitter3';
import { Http, Ws } from './transport';
import Contract from './contract';
@@ -22,8 +24,10 @@ import Subscriptions from './subscriptions';
import util from './util';
import { isFunction } from './util/types';
export default class Api {
export default class Api extends EventEmitter {
constructor (transport) {
super();
if (!transport || !isFunction(transport.execute)) {
throw new Error('EthApi needs transport with execute() function defined');
}

View File

@@ -248,18 +248,32 @@ export default class Contract {
.call(callParams)
.then((encoded) => func.decodeOutput(encoded))
.then((tokens) => tokens.map((token) => token.value))
.then((returns) => returns.length === 1 ? returns[0] : returns);
.then((returns) => returns.length === 1 ? returns[0] : returns)
.catch((error) => {
console.warn(`${func.name}.call`, values, error);
throw error;
});
};
if (!func.constant) {
func.postTransaction = (options, values = []) => {
const _options = this._encodeOptions(func, this._addOptionsTo(options), values);
return this._api.parity.postTransaction(_options);
return this._api.parity
.postTransaction(_options)
.catch((error) => {
console.warn(`${func.name}.postTransaction`, values, error);
throw error;
});
};
func.estimateGas = (options, values = []) => {
const _options = this._encodeOptions(func, this._addOptionsTo(options), values);
return this._api.eth.estimateGas(_options);
return this._api.eth
.estimateGas(_options)
.catch((error) => {
console.warn(`${func.name}.estimateGas`, values, error);
throw error;
});
};
}
@@ -385,6 +399,10 @@ export default class Contract {
this._subscribeToChanges();
return subscriptionId;
});
})
.catch((error) => {
console.warn('subscribe', event, _options, error);
throw error;
});
}

View File

@@ -127,6 +127,18 @@ export function inNumber16 (number) {
return inHex(bn.toString(16));
}
export function inOptionsCondition (condition) {
if (condition) {
if (condition.block) {
condition.block = condition.block ? inNumber10(condition.block) : null;
} else if (condition.time) {
condition.time = inNumber10(Math.floor(condition.time.getTime() / 1000));
}
}
return condition;
}
export function inOptions (options) {
if (options) {
Object.keys(options).forEach((key) => {
@@ -136,6 +148,10 @@ export function inOptions (options) {
options[key] = inAddress(options[key]);
break;
case 'condition':
options[key] = inOptionsCondition(options[key]);
break;
case 'gas':
case 'gasPrice':
options[key] = inNumber16((new BigNumber(options[key])).round());

View File

@@ -129,12 +129,31 @@ export function outNumber (number) {
return new BigNumber(number || 0);
}
export function outPeer (peer) {
const protocols = Object.keys(peer.protocols)
.reduce((obj, key) => {
if (peer.protocols[key]) {
obj[key] = {
...peer.protocols[key],
difficulty: outNumber(peer.protocols[key].difficulty)
};
}
return obj;
}, {});
return {
...peer,
protocols
};
}
export function outPeers (peers) {
return {
active: outNumber(peers.active),
connected: outNumber(peers.connected),
max: outNumber(peers.max),
peers: peers.peers.map(p => { Object.keys(p.protocols).forEach(k => { p.protocols[k].difficulty = outNumber(p.protocols[k].difficulty); }); return p; })
peers: peers.peers.map((peer) => outPeer(peer))
};
}
@@ -200,6 +219,18 @@ export function outSyncing (syncing) {
return syncing;
}
export function outTransactionCondition (condition) {
if (condition) {
if (condition.block) {
condition.block = outNumber(condition.block);
} else if (condition.time) {
condition.time = outDate(condition.time);
}
}
return condition;
}
export function outTransaction (tx) {
if (tx) {
Object.keys(tx).forEach((key) => {
@@ -213,8 +244,14 @@ export function outTransaction (tx) {
tx[key] = outNumber(tx[key]);
break;
case 'condition':
tx[key] = outTransactionCondition(tx[key]);
break;
case 'minBlock':
tx[key] = tx[key] ? outNumber(tx[key]) : null;
tx[key] = tx[key]
? outNumber(tx[key])
: null;
break;
case 'creates':

View File

@@ -16,7 +16,7 @@
import BigNumber from 'bignumber.js';
import { outBlock, outAccountInfo, outAddress, outChainStatus, outDate, outHistogram, outNumber, outPeers, outReceipt, outSyncing, outTransaction, outTrace } from './output';
import { outBlock, outAccountInfo, outAddress, outChainStatus, outDate, outHistogram, outNumber, outPeer, outPeers, outReceipt, outSyncing, outTransaction, outTrace } from './output';
import { isAddress, isBigNumber, isInstanceOf } from '../../../test/types';
describe('api/format/output', () => {
@@ -165,6 +165,66 @@ describe('api/format/output', () => {
});
});
describe('outPeer', () => {
it('converts all internal numbers to BigNumbers', () => {
expect(outPeer({
caps: ['par/1'],
id: '0x01',
name: 'Parity',
network: {
localAddress: '10.0.0.1',
remoteAddress: '10.0.0.1'
},
protocols: {
par: {
difficulty: '0x0f',
head: '0x02',
version: 63
}
}
})).to.deep.equal({
caps: ['par/1'],
id: '0x01',
name: 'Parity',
network: {
localAddress: '10.0.0.1',
remoteAddress: '10.0.0.1'
},
protocols: {
par: {
difficulty: new BigNumber(15),
head: '0x02',
version: 63
}
}
});
});
it('does not output null protocols', () => {
expect(outPeer({
caps: ['par/1'],
id: '0x01',
name: 'Parity',
network: {
localAddress: '10.0.0.1',
remoteAddress: '10.0.0.1'
},
protocols: {
les: null
}
})).to.deep.equal({
caps: ['par/1'],
id: '0x01',
name: 'Parity',
network: {
localAddress: '10.0.0.1',
remoteAddress: '10.0.0.1'
},
protocols: {}
});
});
});
describe('outPeers', () => {
it('converts all internal numbers to BigNumbers', () => {
expect(outPeers({
@@ -185,7 +245,8 @@ describe('api/format/output', () => {
difficulty: '0x0f',
head: '0x02',
version: 63
}
},
les: null
}
}
]

View File

@@ -1,4 +1,4 @@
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { inAddress, inAddresses, inData, inHex, inNumber16, inOptions } from '../../format/input';
import { inAddress, inAddresses, inData, inHex, inNumber16, inOptions, inBlockNumber } from '../../format/input';
import { outAccountInfo, outAddress, outAddresses, outChainStatus, outHistogram, outNumber, outPeers, outTransaction } from '../../format/output';
export default class Parity {
@@ -76,6 +76,17 @@ export default class Parity {
.execute('parity_dappsInterface');
}
decryptMessage (address, data) {
return this._transport
.execute('parity_decryptMessage', inAddress(address), inHex(data));
}
defaultAccount () {
return this._transport
.execute('parity_defaultAccount')
.then(outAddress);
}
defaultExtraData () {
return this._transport
.execute('parity_defaultExtraData');
@@ -101,6 +112,11 @@ export default class Parity {
.execute('parity_enode');
}
encryptMessage (pubkey, data) {
return this._transport
.execute('parity_encryptMessage', inHex(pubkey), inHex(data));
}
executeUpgrade () {
return this._transport
.execute('parity_executeUpgrade');
@@ -111,6 +127,17 @@ export default class Parity {
.execute('parity_extraData');
}
futureTransactions () {
return this._transport
.execute('parity_futureTransactions');
}
gasCeilTarget () {
return this._transport
.execute('parity_gasCeilTarget')
.then(outNumber);
}
gasFloorTarget () {
return this._transport
.execute('parity_gasFloorTarget')
@@ -156,11 +183,22 @@ export default class Parity {
.execute('parity_killAccount', inAddress(account), password);
}
listAccounts (count, offset = null, blockNumber = 'latest') {
return this._transport
.execute('parity_listAccounts', count, inAddress(offset), inBlockNumber(blockNumber))
.then((accounts) => (accounts || []).map(outAddress));
}
listRecentDapps () {
return this._transport
.execute('parity_listRecentDapps');
}
listStorageKeys (address, count, hash = null, blockNumber = 'latest') {
return this._transport
.execute('parity_listStorageKeys', inAddress(address), count, inHex(hash), inBlockNumber(blockNumber));
}
removeAddress (address) {
return this._transport
.execute('parity_removeAddress', inAddress(address));
@@ -265,6 +303,11 @@ export default class Parity {
.then(outAddress);
}
postSign (from, message) {
return this._transport
.execute('parity_postSign', inAddress(from), inHex(message));
}
postTransaction (options) {
return this._transport
.execute('parity_postTransaction', inOptions(options));
@@ -311,16 +354,31 @@ export default class Parity {
.execute('parity_setDappsAddresses', dappId, inAddresses(addresses));
}
setEngineSigner (address, password) {
return this._transport
.execute('parity_setEngineSigner', inAddress(address), password);
}
setExtraData (data) {
return this._transport
.execute('parity_setExtraData', inData(data));
}
setGasCeilTarget (quantity) {
return this._transport
.execute('parity_setGasCeilTarget', inNumber16(quantity));
}
setGasFloorTarget (quantity) {
return this._transport
.execute('parity_setGasFloorTarget', inNumber16(quantity));
}
setMaxTransactionGas (quantity) {
return this._transport
.execute('parity_setMaxTransactionGas', inNumber16(quantity));
}
setMinGasPrice (quantity) {
return this._transport
.execute('parity_setMinGasPrice', inNumber16(quantity));

View File

@@ -34,9 +34,9 @@ export default class Personal {
.then(outAddress);
}
signAndSendTransaction (options, password) {
sendTransaction (options, password) {
return this._transport
.execute('personal_signAndSendTransaction', inOptions(options), password);
.execute('personal_sendTransaction', inOptions(options), password);
}
unlockAccount (account, password, duration = 1) {

View File

@@ -23,6 +23,7 @@ export default class Eth {
this._started = false;
this._lastBlock = new BigNumber(-1);
this._pollTimerId = null;
}
get isStarted () {
@@ -37,7 +38,7 @@ export default class Eth {
_blockNumber = () => {
const nextTimeout = (timeout = 1000) => {
setTimeout(() => {
this._pollTimerId = setTimeout(() => {
this._blockNumber();
}, timeout);
};
@@ -57,6 +58,6 @@ export default class Eth {
nextTimeout();
})
.catch(nextTimeout);
.catch(() => nextTimeout());
}
}

View File

@@ -25,6 +25,7 @@ const events = {
'logging': { module: 'logging' },
'eth_blockNumber': { module: 'eth' },
'parity_allAccountsInfo': { module: 'personal' },
'parity_defaultAccount': { module: 'personal' },
'eth_accounts': { module: 'personal' },
'signer_requestsToConfirm': { module: 'signer' }
};

View File

@@ -1,4 +1,4 @@
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
@@ -20,6 +20,9 @@ export default class Personal {
this._api = api;
this._updateSubscriptions = updateSubscriptions;
this._started = false;
this._lastDefaultAccount = '0x0';
this._pollTimerId = null;
}
get isStarted () {
@@ -30,12 +33,44 @@ export default class Personal {
this._started = true;
return Promise.all([
this._defaultAccount(),
this._listAccounts(),
this._accountsInfo(),
this._loggingSubscribe()
]);
}
// FIXME: Because of the different API instances, the "wait for valid changes" approach
// doesn't work. Since the defaultAccount is critical to operation, we poll in exactly
// same way we do in ../eth (ala same as eth_blockNumber) and update. This should be moved
// to pub-sub as it becomes available
_defaultAccount = (timerDisabled = false) => {
const nextTimeout = (timeout = 1000) => {
if (!timerDisabled) {
this._pollTimerId = setTimeout(() => {
this._defaultAccount();
}, timeout);
}
};
if (!this._api.transport.isConnected) {
nextTimeout(500);
return;
}
return this._api.parity
.defaultAccount()
.then((defaultAccount) => {
if (this._lastDefaultAccount !== defaultAccount) {
this._lastDefaultAccount = defaultAccount;
this._updateSubscriptions('parity_defaultAccount', null, defaultAccount);
}
nextTimeout();
})
.catch(() => nextTimeout());
}
_listAccounts = () => {
return this._api.eth
.accounts()
@@ -46,9 +81,19 @@ export default class Personal {
_accountsInfo = () => {
return this._api.parity
.allAccountsInfo()
.accountsInfo()
.then((info) => {
this._updateSubscriptions('parity_allAccountsInfo', null, info);
this._updateSubscriptions('parity_accountsInfo', null, info);
return this._api.parity
.allAccountsInfo()
.catch(() => {
// NOTE: This fails on non-secure APIs, swallow error
return {};
})
.then((allInfo) => {
this._updateSubscriptions('parity_allAccountsInfo', null, allInfo);
});
});
}
@@ -73,6 +118,11 @@ export default class Personal {
case 'parity_setAccountMeta':
this._accountsInfo();
return;
case 'parity_setDappsAddresses':
case 'parity_setNewDappsWhitelist':
this._defaultAccount(true);
return;
}
});
}

View File

@@ -1,4 +1,4 @@
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
@@ -18,31 +18,51 @@ import sinon from 'sinon';
import Personal from './personal';
const TEST_DEFAULT = '0xfa64203C044691aA57251aF95f4b48d85eC00Dd5';
const TEST_INFO = {
'0xfa64203C044691aA57251aF95f4b48d85eC00Dd5': {
[TEST_DEFAULT]: {
name: 'test'
}
};
const TEST_LIST = ['0xfa64203C044691aA57251aF95f4b48d85eC00Dd5'];
const TEST_LIST = [TEST_DEFAULT];
function stubApi (accounts, info) {
const _calls = {
accountsInfo: [],
allAccountsInfo: [],
listAccounts: []
listAccounts: [],
defaultAccount: []
};
return {
_calls,
transport: {
isConnected: true
},
parity: {
accountsInfo: () => {
const stub = sinon.stub().resolves(info || TEST_INFO)();
_calls.accountsInfo.push(stub);
return stub;
},
allAccountsInfo: () => {
const stub = sinon.stub().resolves(info || TEST_INFO)();
_calls.allAccountsInfo.push(stub);
return stub;
},
defaultAccount: () => {
const stub = sinon.stub().resolves(Object.keys(info || TEST_INFO)[0])();
_calls.defaultAccount.push(stub);
return stub;
}
},
eth: {
accounts: () => {
const stub = sinon.stub().resolves(accounts || TEST_LIST)();
_calls.listAccounts.push(stub);
return stub;
}
@@ -85,6 +105,10 @@ describe('api/subscriptions/personal', () => {
expect(personal.isStarted).to.be.true;
});
it('calls parity_accountsInfo', () => {
expect(api._calls.accountsInfo.length).to.be.ok;
});
it('calls parity_allAccountsInfo', () => {
expect(api._calls.allAccountsInfo.length).to.be.ok;
});
@@ -94,8 +118,10 @@ describe('api/subscriptions/personal', () => {
});
it('updates subscribers', () => {
expect(cb.firstCall).to.have.been.calledWith('eth_accounts', null, TEST_LIST);
expect(cb.secondCall).to.have.been.calledWith('parity_allAccountsInfo', null, TEST_INFO);
expect(cb).to.have.been.calledWith('parity_defaultAccount', null, TEST_DEFAULT);
expect(cb).to.have.been.calledWith('eth_accounts', null, TEST_LIST);
expect(cb).to.have.been.calledWith('parity_accountsInfo', null, TEST_INFO);
expect(cb).to.have.been.calledWith('parity_allAccountsInfo', null, TEST_INFO);
});
});
@@ -110,7 +136,15 @@ describe('api/subscriptions/personal', () => {
expect(personal.isStarted).to.be.true;
});
it('calls parity_defaultAccount', () => {
expect(api._calls.defaultAccount.length).to.be.ok;
});
it('calls personal_accountsInfo', () => {
expect(api._calls.accountsInfo.length).to.be.ok;
});
it('calls personal_allAccountsInfo', () => {
expect(api._calls.allAccountsInfo.length).to.be.ok;
});

View File

@@ -25,6 +25,7 @@ export const ERROR_CODES = {
UNKNOWN_ERROR: -32009,
TRANSACTION_ERROR: -32010,
EXECUTION_ERROR: -32015,
EXCEPTION_ERROR: -32016,
ACCOUNT_LOCKED: -32020,
PASSWORD_INVALID: -32021,
ACCOUNT_ERROR: -32023,

View File

@@ -51,14 +51,14 @@ export default class Http extends JsonRpcBase {
return fetch(this._url, request)
.catch((error) => {
this._connected = false;
this._setDisconnected();
throw error;
})
.then((response) => {
this._connected = true;
this._setConnected();
if (response.status !== 200) {
this._connected = false;
this._setDisconnected();
this.error(JSON.stringify({ status: response.status, statusText: response.statusText }));
console.error(`${method}(${JSON.stringify(params)}): ${response.status}: ${response.statusText}`);

View File

@@ -37,6 +37,26 @@ describe('api/transport/Http', () => {
});
});
describe('transport emitter', () => {
it('emits close event', (done) => {
transport.once('close', () => {
done();
});
transport.execute('eth_call');
});
it('emits open event', (done) => {
mockHttp([{ method: 'eth_call', reply: { result: '' } }]);
transport.once('open', () => {
done();
});
transport.execute('eth_call');
});
});
describe('transport', () => {
const RESULT = ['this is some result'];

View File

@@ -14,8 +14,12 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default class JsonRpcBase {
import EventEmitter from 'eventemitter3';
export default class JsonRpcBase extends EventEmitter {
constructor () {
super();
this._id = 1;
this._debug = false;
this._connected = false;
@@ -32,6 +36,20 @@ export default class JsonRpcBase {
return json;
}
_setConnected () {
if (!this._connected) {
this._connected = true;
this.emit('open');
}
}
_setDisconnected () {
if (this._connected) {
this._connected = false;
this.emit('close');
}
}
get id () {
return this._id;
}

View File

@@ -22,7 +22,7 @@ import TransportError from '../error';
/* global WebSocket */
export default class Ws extends JsonRpcBase {
constructor (url, token, connect = true) {
constructor (url, token, autoconnect = true) {
super();
this._url = url;
@@ -32,14 +32,14 @@ export default class Ws extends JsonRpcBase {
this._connecting = false;
this._connected = false;
this._lastError = null;
this._autoConnect = false;
this._autoConnect = autoconnect;
this._retries = 0;
this._reconnectTimeoutId = null;
this._connectPromise = null;
this._connectPromiseFunctions = {};
if (connect) {
if (autoconnect) {
this.connect();
}
}
@@ -124,11 +124,8 @@ export default class Ws extends JsonRpcBase {
}
_onOpen = (event) => {
console.log('ws:onOpen');
this._connected = true;
this._setConnected();
this._connecting = false;
this._autoConnect = true;
this._retries = 0;
Object.keys(this._messages)
@@ -142,7 +139,7 @@ export default class Ws extends JsonRpcBase {
}
_onClose = (event) => {
this._connected = false;
this._setDisconnected();
this._connecting = false;
event.timestamp = Date.now();
@@ -209,8 +206,8 @@ export default class Ws extends JsonRpcBase {
if (result.error) {
this.error(event.data);
// Don't print error if request rejected...
if (!/rejected/.test(result.error.message)) {
// Don't print error if request rejected or not is not yet up...
if (!/(rejected|not yet up)/.test(result.error.message)) {
console.error(`${method}(${JSON.stringify(params)}): ${result.error.code}: ${result.error.message}`);
}

View File

@@ -21,6 +21,37 @@ describe('api/transport/Ws', () => {
let transport;
let scope;
describe('transport emitter', () => {
const connect = () => {
const scope = mockWs();
const transport = new Ws(TEST_WS_URL);
return { transport, scope };
};
it('emits open event', (done) => {
const { transport, scope } = connect();
transport.once('open', () => {
done();
});
scope.stop();
});
it('emits close event', (done) => {
const { transport, scope } = connect();
transport.once('open', () => {
scope.server.close();
});
transport.once('close', () => {
done();
});
});
});
describe('transport', () => {
let result;

View File

@@ -17,9 +17,12 @@
import { keccak_256 } from 'js-sha3'; // eslint-disable-line
import { hexToBytes } from './format';
import { isHex } from './types';
export function sha3 (value, options) {
if (options && options.encoding === 'hex') {
const forceHex = options && options.encoding === 'hex';
if (forceHex || (!options && isHex(value))) {
const bytes = hexToBytes(value);
return sha3(bytes);
}
@@ -28,3 +31,5 @@ export function sha3 (value, options) {
return `0x${hash}`;
}
sha3.text = (val) => sha3(val, { encoding: 'raw' });

View File

@@ -32,5 +32,14 @@ describe('api/util/sha3', () => {
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');
});
});
});

View File

@@ -29,6 +29,10 @@ export function isFunction (test) {
}
export function isHex (_test) {
if (!isString(_test)) {
return false;
}
if (_test.substr(0, 2) === '0x') {
return isHex(_test.slice(2));
}

View File

@@ -66,6 +66,12 @@ describe('api/util/types', () => {
it('correctly identifies non-hex values', () => {
expect(isHex('123j')).to.be.false;
});
it('correctly indentifies non-string values', () => {
expect(isHex(false)).to.be.false;
expect(isHex()).to.be.false;
expect(isHex([1, 2, 3])).to.be.false;
});
});
describe('isInstanceOf', () => {

View File

@@ -17,12 +17,20 @@
import LogLevel from 'loglevel';
export const LOG_KEYS = {
Balances: {
key: 'balances',
desc: 'Balances fetching'
},
TransferModalStore: {
path: 'modals/Transfer/store',
desc: 'Transfer Modal MobX Store'
key: 'modalsTransferStore',
desc: 'Transfer modal MobX store'
},
Signer: {
key: 'secureApi',
desc: 'The Signer and the Secure API'
}
};
export const getLogger = (LOG_KEY) => {
return LogLevel.getLogger(LOG_KEY.path);
return LogLevel.getLogger(LOG_KEY.key);
};

View File

@@ -73,6 +73,26 @@ export default class BadgeReg {
});
}
fetchCertifierByName (name) {
return this
.getContract()
.then((badgeReg) => {
return badgeReg.instance.fromName.call({}, [ name ]);
})
.then(([ id, address, owner ]) => {
if (address === ZERO20) {
throw new Error(`Certifier ${name} does not exist.`);
}
return this.fetchMeta(id)
.then(({ title, icon }) => {
const data = { address, id, name, title, icon };
this.certifiers[id] = data;
return data;
});
});
}
fetchMeta (id) {
return this
.getContract()

View File

@@ -91,7 +91,7 @@ export default class Registry {
lookupAddress (_name) {
const name = _name.toLowerCase();
const sha3 = this._api.util.sha3(name);
const sha3 = this._api.util.sha3.text(name);
return this.getInstance().then((instance) => {
return instance.getAddress.call({}, [sha3, 'A']);

View File

@@ -45,7 +45,7 @@ export default class Application extends Component {
}
componentDidMount () {
this.attachInstance();
return this.attachInstance();
}
render () {
@@ -80,12 +80,12 @@ export default class Application extends Component {
}
attachInstance () {
Promise
return Promise
.all([
attachInstances(),
api.parity.accountsInfo()
api.parity.accountsInfo(),
attachInstances()
])
.then(([{ managerInstance, registryInstance, tokenregInstance }, accountsInfo]) => {
.then(([accountsInfo, { managerInstance, registryInstance, tokenregInstance }]) => {
accountsInfo = accountsInfo || {};
this.setState({
loading: false,

View File

@@ -1,4 +1,4 @@
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
@@ -17,7 +17,6 @@
import React, { Component, PropTypes } from 'react';
import { api } from '../../parity';
import AddressSelect from '../../AddressSelect';
import Container from '../../Container';
import styles from './deployment.css';
@@ -122,41 +121,20 @@ export default class Deployment extends Component {
}
renderForm () {
const { accounts } = this.context;
const { baseText, name, nameError, tla, tlaError, totalSupply, totalSupplyError } = this.state;
const hasError = !!(nameError || tlaError || totalSupplyError);
const error = `${styles.input} ${styles.error}`;
const addresses = Object.keys(accounts);
// <div className={ styles.input }>
// <label>global registration</label>
// <select onChange={ this.onChangeRegistrar }>
// <option value='no'>No, only for me</option>
// <option value='yes'>Yes, for everybody</option>
// </select>
// <div className={ styles.hint }>
// register on network (fee: { globalFeeText }ETH)
// </div>
// </div>
return (
<Container>
<div className={ styles.form }>
<div className={ styles.input }>
<label>deployment account</label>
<AddressSelect
addresses={ addresses }
onChange={ this.onChangeFrom } />
<div className={ styles.hint }>
the owner account to deploy from
</div>
</div>
<div className={ nameError ? error : styles.input }>
<label>token name</label>
<input
value={ name }
name='name'
onChange={ this.onChangeName } />
onChange={ this.onChangeName }
/>
<div className={ styles.hint }>
{ nameError || 'an identifying name for the token' }
</div>
@@ -167,7 +145,8 @@ export default class Deployment extends Component {
className={ styles.small }
name='tla'
value={ tla }
onChange={ this.onChangeTla } />
onChange={ this.onChangeTla }
/>
<div className={ styles.hint }>
{ tlaError || 'unique network acronym for this token' }
</div>
@@ -180,7 +159,8 @@ export default class Deployment extends Component {
max='999999999999'
name='totalSupply'
value={ totalSupply }
onChange={ this.onChangeSupply } />
onChange={ this.onChangeSupply }
/>
<div className={ styles.hint }>
{ totalSupplyError || `number of tokens (base: ${baseText})` }
</div>
@@ -191,7 +171,8 @@ export default class Deployment extends Component {
<div
className={ styles.button }
disabled={ hasError }
onClick={ this.onDeploy }>
onClick={ this.onDeploy }
>
Deploy Token
</div>
</div>
@@ -201,12 +182,6 @@ export default class Deployment extends Component {
);
}
onChangeFrom = (event) => {
const fromAddress = event.target.value;
this.setState({ fromAddress });
}
onChangeName = (event) => {
const name = event.target.value;
const nameError = name && (name.length > 2) && (name.length < 32)
@@ -266,7 +241,7 @@ export default class Deployment extends Component {
onDeploy = () => {
const { managerInstance, registryInstance, tokenregInstance } = this.context;
const { base, deployBusy, fromAddress, globalReg, globalFee, name, nameError, tla, tlaError, totalSupply, totalSupplyError } = this.state;
const { base, deployBusy, globalReg, globalFee, name, nameError, tla, tlaError, totalSupply, totalSupplyError } = this.state;
const hasError = !!(nameError || tlaError || totalSupplyError);
if (hasError || deployBusy) {
@@ -276,18 +251,23 @@ export default class Deployment extends Component {
const tokenreg = (globalReg ? tokenregInstance : registryInstance).address;
const values = [base.mul(totalSupply), tla, name, tokenreg];
const options = {
from: fromAddress,
value: globalReg ? globalFee : 0
};
this.setState({ deployBusy: true, deployState: 'Estimating gas for the transaction' });
managerInstance
.deploy.estimateGas(options, values)
return api.parity
.defaultAccount()
.then((defaultAddress) => {
options.from = defaultAddress;
return managerInstance.deploy.estimateGas(options, values);
})
.then((gas) => {
this.setState({ deployState: 'Gas estimated, Posting transaction to the network' });
const gasPassed = gas.mul(1.2);
options.gas = gasPassed.toFixed(0);
console.log(`gas estimated at ${gas.toFormat(0)}, passing ${gasPassed.toFormat(0)}`);

View File

@@ -25,6 +25,8 @@ let registryInstance;
const registries = {};
const subscriptions = {};
let defaultSubscriptionId;
let nextSubscriptionId = 1000;
let isTest = false;
@@ -65,6 +67,20 @@ export function unsubscribeEvents (subscriptionId) {
delete subscriptions[subscriptionId];
}
export function subscribeDefaultAddress (callback) {
return api
.subscribe('parity_defaultAccount', callback)
.then((subscriptionId) => {
defaultSubscriptionId = subscriptionId;
return defaultSubscriptionId;
});
}
export function unsubscribeDefaultAddress () {
return api.unsubscribe(defaultSubscriptionId);
}
function pollEvents () {
const loop = Object.values(subscriptions);
const timeout = () => setTimeout(pollEvents, 1000);

View File

@@ -1,4 +1,4 @@
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
@@ -17,10 +17,9 @@
import React, { Component } from 'react';
import { api } from '../parity';
import { attachInterface } from '../services';
import { attachInterface, subscribeDefaultAddress, unsubscribeDefaultAddress } from '../services';
import Button from '../Button';
import Events from '../Events';
import IdentityIcon from '../IdentityIcon';
import Loading from '../Loading';
import styles from './application.css';
@@ -32,7 +31,7 @@ let nextEventId = 0;
export default class Application extends Component {
state = {
fromAddress: null,
defaultAddress: null,
loading: true,
url: '',
urlError: null,
@@ -47,19 +46,32 @@ export default class Application extends Component {
registerType: 'file',
repo: '',
repoError: null,
subscriptionId: null,
events: {},
eventIds: []
}
componentDidMount () {
attachInterface()
.then((state) => {
this.setState(state, () => {
this.setState({ loading: false });
});
return Promise
.all([
attachInterface(),
subscribeDefaultAddress((error, defaultAddress) => {
if (!error) {
this.setState({ defaultAddress });
}
})
])
.then(([state]) => {
this.setState(Object.assign({}, state, {
loading: false
}));
});
}
componentWillUnmount () {
return unsubscribeDefaultAddress();
}
render () {
const { loading } = this.state;
@@ -75,16 +87,20 @@ export default class Application extends Component {
}
renderPage () {
const { fromAddress, registerBusy, url, urlError, contentHash, contentHashError, contentHashOwner, commit, commitError, registerType, repo, repoError } = this.state;
const { defaultAddress, registerBusy, url, urlError, contentHash, contentHashError, contentHashOwner, commit, commitError, registerType, repo, repoError } = this.state;
let hashClass = null;
if (contentHashError) {
hashClass = contentHashOwner !== fromAddress ? styles.hashError : styles.hashWarning;
hashClass = contentHashOwner !== defaultAddress
? styles.hashError
: styles.hashWarning;
} else if (contentHash) {
hashClass = styles.hashOk;
}
let valueInputs = null;
if (registerType === 'content') {
valueInputs = [
<div className={ styles.capture } key='repo'>
@@ -94,7 +110,8 @@ export default class Application extends Component {
disabled={ registerBusy }
value={ repo }
className={ repoError ? styles.error : null }
onChange={ this.onChangeRepo } />
onChange={ this.onChangeRepo }
/>
</div>,
<div className={ styles.capture } key='hash'>
<input
@@ -103,7 +120,8 @@ export default class Application extends Component {
disabled={ registerBusy }
value={ commit }
className={ commitError ? styles.error : null }
onChange={ this.onChangeCommit } />
onChange={ this.onChangeCommit }
/>
</div>
];
} else {
@@ -115,7 +133,8 @@ export default class Application extends Component {
disabled={ registerBusy }
value={ url }
className={ urlError ? styles.error : null }
onChange={ this.onChangeUrl } />
onChange={ this.onChangeUrl }
/>
</div>
);
}
@@ -128,11 +147,17 @@ export default class Application extends Component {
<Button
disabled={ registerBusy }
invert={ registerType !== 'file' }
onClick={ this.onClickTypeNormal }>File Link</Button>
onClick={ this.onClickTypeNormal }
>
File Link
</Button>
<Button
disabled={ registerBusy }
invert={ registerType !== 'content' }
onClick={ this.onClickTypeContent }>Content Bundle</Button>
onClick={ this.onClickTypeContent }
>
Content Bundle
</Button>
</div>
<div className={ styles.box }>
<div className={ styles.description }>
@@ -148,26 +173,21 @@ export default class Application extends Component {
</div>
<Events
eventIds={ this.state.eventIds }
events={ this.state.events } />
events={ this.state.events }
/>
</div>
);
}
renderButtons () {
const { accounts, fromAddress, urlError, repoError, commitError, contentHashError, contentHashOwner } = this.state;
const account = accounts[fromAddress];
const { defaultAddress, urlError, repoError, commitError, contentHashError, contentHashOwner } = this.state;
return (
<div className={ styles.buttons }>
<div className={ styles.addressSelect }>
<Button invert onClick={ this.onSelectFromAddress }>
<IdentityIcon address={ account.address } />
<div>{ account.name || account.address }</div>
</Button>
</div>
<Button
onClick={ this.onClickRegister }
disabled={ (contentHashError && contentHashOwner !== fromAddress) || urlError || repoError || commitError }>register url</Button>
disabled={ (contentHashError && contentHashOwner !== defaultAddress) || urlError || repoError || commitError }
>register url</Button>
</div>
);
}
@@ -264,6 +284,7 @@ export default class Application extends Component {
// TODO: field validation
if (!urlError) {
const parts = url.split('/');
hasContent = parts.length !== 0;
if (parts[2] === 'github.com' || parts[2] === 'raw.githubusercontent.com') {
@@ -280,11 +301,11 @@ export default class Application extends Component {
}
onClickRegister = () => {
const { commit, commitError, contentHashError, contentHashOwner, fromAddress, url, urlError, registerType, repo, repoError } = this.state;
const { defaultAddress, commit, commitError, contentHashError, contentHashOwner, url, urlError, registerType, repo, repoError } = this.state;
// TODO: No errors are currently set, validation to be expanded and added for each
// field (query is fast to pick up the issues, so not burning atm)
if ((contentHashError && contentHashOwner !== fromAddress) || repoError || urlError || commitError) {
if ((contentHashError && contentHashOwner !== defaultAddress) || repoError || urlError || commitError) {
return;
}
@@ -354,12 +375,15 @@ export default class Application extends Component {
}
registerContent (contentRepo, contentCommit) {
const { contentHash, fromAddress, instance } = this.state;
contentCommit = contentCommit.substr(0, 2) === '0x' ? contentCommit : `0x${contentCommit}`;
const { defaultAddress, contentHash, instance } = this.state;
contentCommit = contentCommit.substr(0, 2) === '0x'
? contentCommit
: `0x${contentCommit}`;
const eventId = nextEventId++;
const values = [contentHash, contentRepo, contentCommit];
const options = { from: fromAddress };
const options = { from: defaultAddress };
this.setState({
eventIds: [eventId].concat(this.state.eventIds),
@@ -368,7 +392,7 @@ export default class Application extends Component {
contentHash,
contentRepo,
contentCommit,
fromAddress,
defaultAddress,
registerBusy: true,
registerState: 'Estimating gas for the transaction',
timestamp: new Date()
@@ -396,6 +420,7 @@ export default class Application extends Component {
});
const gasPassed = gas.mul(1.2);
options.gas = gasPassed.toFixed(0);
console.log(`gas estimated at ${gas.toFormat(0)}, passing ${gasPassed.toFormat(0)}`);
@@ -405,11 +430,11 @@ export default class Application extends Component {
}
registerUrl (contentUrl) {
const { contentHash, fromAddress, instance } = this.state;
const { contentHash, defaultAddress, instance } = this.state;
const eventId = nextEventId++;
const values = [contentHash, contentUrl];
const options = { from: fromAddress };
const options = { from: defaultAddress };
this.setState({
eventIds: [eventId].concat(this.state.eventIds),
@@ -417,7 +442,7 @@ export default class Application extends Component {
[eventId]: {
contentHash,
contentUrl,
fromAddress,
defaultAddress,
registerBusy: true,
registerState: 'Estimating gas for the transaction',
timestamp: new Date()
@@ -445,6 +470,7 @@ export default class Application extends Component {
});
const gasPassed = gas.mul(1.2);
options.gas = gasPassed.toFixed(0);
console.log(`gas estimated at ${gas.toFormat(0)}, passing ${gasPassed.toFormat(0)}`);
@@ -453,25 +479,6 @@ export default class Application extends Component {
);
}
onSelectFromAddress = () => {
const { accounts, fromAddress } = this.state;
const addresses = Object.keys(accounts);
let index = 0;
addresses.forEach((address, _index) => {
if (address === fromAddress) {
index = _index;
}
});
index++;
if (index >= addresses.length) {
index = 0;
}
this.setState({ fromAddress: addresses[index] });
}
lookupHash (url) {
const { instance } = this.state;

View File

@@ -17,48 +17,44 @@
import * as abis from '~/contracts/abi';
import { api } from './parity';
let defaultSubscriptionId;
export function attachInterface () {
return api.parity
.registryAddress()
.then((registryAddress) => {
console.log(`the registry was found at ${registryAddress}`);
const registry = api.newContract(abis.registry, registryAddress).instance;
return Promise
.all([
registry.getAddress.call({}, [api.util.sha3('githubhint'), 'A']),
api.parity.accountsInfo()
]);
return api
.newContract(abis.registry, registryAddress).instance
.getAddress.call({}, [api.util.sha3('githubhint'), 'A']);
})
.then(([address, accountsInfo]) => {
.then((address) => {
console.log(`githubhint was found at ${address}`);
const contract = api.newContract(abis.githubhint, address);
const accounts = Object
.keys(accountsInfo)
.reduce((obj, address) => {
const account = accountsInfo[address];
return Object.assign(obj, {
[address]: {
address,
name: account.name
}
});
}, {});
const fromAddress = Object.keys(accounts)[0];
return {
accounts,
address,
accountsInfo,
contract,
instance: contract.instance,
fromAddress
instance: contract.instance
};
})
.catch((error) => {
console.error('attachInterface', error);
});
}
export function subscribeDefaultAddress (callback) {
return api
.subscribe('parity_defaultAccount', callback)
.then((subscriptionId) => {
defaultSubscriptionId = subscriptionId;
return defaultSubscriptionId;
});
}
export function unsubscribeDefaultAddress () {
return api.unsubscribe(defaultSubscriptionId);
}

Some files were not shown because too many files have changed in this diff Show More