Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ddc7b588dc | ||
|
|
f90bdcd1e5 | ||
|
|
7bf840f80a | ||
|
|
4992064663 | ||
|
|
75eb542275 | ||
|
|
b0f89becfd | ||
|
|
fcc388703f | ||
|
|
fc226ce868 | ||
|
|
ee0df5adc0 | ||
|
|
5f2cabd6e3 | ||
|
|
65e4bad3dd | ||
|
|
a554b81f32 | ||
|
|
06be7271aa | ||
|
|
33a553726a | ||
|
|
fdc781dbfd | ||
|
|
2c95ee60b9 | ||
|
|
ac8533a56c | ||
|
|
8f75042fee | ||
|
|
3f7ac5c64b | ||
|
|
e23753eabd | ||
|
|
9f41af5077 | ||
|
|
28019a472a | ||
|
|
8116ad995d | ||
|
|
6a564f3334 | ||
|
|
2d7aecf5d9 | ||
|
|
2f066535cb | ||
|
|
e734ca4bf1 | ||
|
|
6f2ec07baa | ||
|
|
7ef4bbd5f7 | ||
|
|
96e60d9c91 | ||
|
|
ac02d12fa8 | ||
|
|
e7102d9f38 | ||
|
|
cabf626c61 | ||
|
|
91be3a4fc3 | ||
|
|
b9979137b7 | ||
|
|
4becd27029 | ||
|
|
54536dfa1b | ||
|
|
c4a58efed4 | ||
|
|
1f4d6fced3 |
@@ -77,8 +77,10 @@ linux-snap:
|
||||
- echo "Version:"$VER
|
||||
- snapcraft
|
||||
- ls
|
||||
- cp "parity_"$CI_BUILD"_REF_NAME_amd64.snap" "parity_"$VER"_amd64.snap"
|
||||
#- cp "parity_"$CI_BUILD"_amd64.snap" "parity_"$VER"_amd64.snap"
|
||||
- md5sum "parity_"$VER"_amd64.snap" > "parity_"$VER"_amd64.snap.md5"
|
||||
- file "parity_"$VER"_amd64.snap.md5"
|
||||
- cat "parity_"$VER"_amd64.snap.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|nightly)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
||||
@@ -561,11 +563,9 @@ docker-build:
|
||||
- docker info
|
||||
script:
|
||||
- if [ "$CI_BUILD_REF_NAME" == "beta-release" ]; then DOCKER_TAG="latest"; else DOCKER_TAG=$CI_BUILD_REF_NAME; fi
|
||||
- docker login -u $Docker_Hub_User -p $Docker_Hub_Pass
|
||||
- sh scripts/docker-build.sh $DOCKER_TAG ethcore
|
||||
- docker logout
|
||||
- echo "Tag:" $DOCKER_TAG
|
||||
- docker login -u $Docker_Hub_User_Parity -p $Docker_Hub_Pass_Parity
|
||||
- sh scripts/docker-build.sh $DOCKER_TAG parity
|
||||
- sh scripts/docker-build.sh $DOCKER_TAG
|
||||
- docker logout
|
||||
tags:
|
||||
- docker
|
||||
|
||||
486
CHANGELOG.md
486
CHANGELOG.md
@@ -1,3 +1,489 @@
|
||||
## Parity [v1.7.0](https://github.com/paritytech/parity/releases/tag/v1.7.0) (2017-07-28)
|
||||
|
||||
Parity 1.7.0 is a major release introducing several important features:
|
||||
|
||||
- **Experimental [Light client](https://github.com/paritytech/parity/wiki/The-Parity-Light-Protocol-(PIP)) support**. Start Parity with `--light` to enable light mode. Please, note: The wallet UI integration for the light client is not included, yet.
|
||||
- **Experimental web wallet**. A hosted version of Parity that keeps the keys and signs transactions using your browser storage. Try it at https://wallet.parity.io or run your own with `--public-node`.
|
||||
- **WASM contract support**. Private networks can run contracts compiled into WASM bytecode. _More information and documentation to follow_.
|
||||
- **DApps and RPC server merge**. DApp and RPC are now available through a single API endpoint. DApp server related settings are deprecated.
|
||||
- **Export accounts from the wallet**. Backing up your keys can now simply be managed through the wallet interface.
|
||||
- **PoA/Kovan validator set contract**. The PoA network validator-set management via smart contract is now supported by warp and, in the near future, light sync.
|
||||
- **PubSub API**. https://github.com/paritytech/parity/wiki/JSONRPC-Parity-Pub-Sub-module
|
||||
- **Signer apps for IOS and Android**.
|
||||
|
||||
Full list of included changes:
|
||||
|
||||
- Backports [#6163](https://github.com/paritytech/parity/pull/6163)
|
||||
- Light client improvements ([#6156](https://github.com/paritytech/parity/pull/6156))
|
||||
- No seal checking
|
||||
- Import command and --no-seal-check for light client
|
||||
- Fix eth_call
|
||||
- Tweak registry dapps lookup
|
||||
- Ignore failed requests to non-server peers
|
||||
- Fix connecting to wildcard addresses. ([#6167](https://github.com/paritytech/parity/pull/6167))
|
||||
- Don't display an overlay in case the time sync check fails. ([#6164](https://github.com/paritytech/parity/pull/6164))
|
||||
- Small improvements to time estimation.
|
||||
- Temporarily disable NTP time check by default.
|
||||
- Light client fixes ([#6148](https://github.com/paritytech/parity/pull/6148)) [#6151](https://github.com/paritytech/parity/pull/6151)
|
||||
- Light client fixes
|
||||
- Fix memory-lru-cache
|
||||
- Clear pending reqs on disconnect
|
||||
- Filter tokens logs from current block, not genesis ([#6128](https://github.com/paritytech/parity/pull/6128)) [#6141](https://github.com/paritytech/parity/pull/6141)
|
||||
- Fix QR scanner returning null on confirm [#6122](https://github.com/paritytech/parity/pull/6122)
|
||||
- Check QR before lowercase ([#6119](https://github.com/paritytech/parity/pull/6119)) [#6120](https://github.com/paritytech/parity/pull/6120)
|
||||
- Remove chunk to restore from pending set only upon successful import [#6117](https://github.com/paritytech/parity/pull/6117)
|
||||
- Fixed node address detection on incoming connection [#6094](https://github.com/paritytech/parity/pull/6094)
|
||||
- Place RETURNDATA behind block number gate [#6095](https://github.com/paritytech/parity/pull/6095)
|
||||
- Update wallet library binaries [#6108](https://github.com/paritytech/parity/pull/6108)
|
||||
- Backported wallet fix [#6105](https://github.com/paritytech/parity/pull/6105)
|
||||
- Fix initialisation bug. ([#6102](https://github.com/paritytech/parity/pull/6102))
|
||||
- Update wallet library modifiers ([#6103](https://github.com/paritytech/parity/pull/6103))
|
||||
- Place RETURNDATA behind block number gate [#6095](https://github.com/paritytech/parity/pull/6095)
|
||||
- Fixed node address detection on incoming connection [#6094](https://github.com/paritytech/parity/pull/6094)
|
||||
- Bump snap version and tweak importing detection logic ([#6079](https://github.com/paritytech/parity/pull/6079)) [#6081](https://github.com/paritytech/parity/pull/6081)
|
||||
- bump last tick just before printing info and restore sync detection
|
||||
- bump kovan snapshot version
|
||||
- Fixed sync tests
|
||||
- Fixed rpc tests
|
||||
- Acquire client report under lock in informant [#6071](https://github.com/paritytech/parity/pull/6071)
|
||||
- Show busy indicator on Address forget [#6069](https://github.com/paritytech/parity/pull/6069)
|
||||
- Add CSP for worker-src ([#6059](https://github.com/paritytech/parity/pull/6059)) [#6064](https://github.com/paritytech/parity/pull/6064)
|
||||
- Specify worker-src seperately, add blob
|
||||
- Upgrade react-qr-scan to latest version
|
||||
- Set release channel to beta
|
||||
- Limit transaction queue memory & limit future queue [#6038](https://github.com/paritytech/parity/pull/6038)
|
||||
- Fix CI build issue [#6050](https://github.com/paritytech/parity/pull/6050)
|
||||
- New contract PoA sync fixes [#5991](https://github.com/paritytech/parity/pull/5991)
|
||||
- Fixed link to Multisig Contract Wallet on master [#5984](https://github.com/paritytech/parity/pull/5984)
|
||||
- Ethcore crate split part 1 [#6041](https://github.com/paritytech/parity/pull/6041)
|
||||
- Fix status icon [#6039](https://github.com/paritytech/parity/pull/6039)
|
||||
- Errors & warnings for inappropriate RPCs [#6029](https://github.com/paritytech/parity/pull/6029)
|
||||
- Add missing CSP for web3.site [#5992](https://github.com/paritytech/parity/pull/5992)
|
||||
- Remove cargo install --git from README.md [#6037](https://github.com/paritytech/parity/pull/6037)
|
||||
- Node Health warnings [#5951](https://github.com/paritytech/parity/pull/5951)
|
||||
- RPC cpu pool [#6023](https://github.com/paritytech/parity/pull/6023)
|
||||
- Use crates.io dependencies for parity-wasm [#6036](https://github.com/paritytech/parity/pull/6036)
|
||||
- Add test for loading the chain specs [#6028](https://github.com/paritytech/parity/pull/6028)
|
||||
- Whitelist APIs for generic Pub-Sub [#5840](https://github.com/paritytech/parity/pull/5840)
|
||||
- WASM contracts MVP [#5679](https://github.com/paritytech/parity/pull/5679)
|
||||
- Fix valid QR scan not advancing [#6033](https://github.com/paritytech/parity/pull/6033)
|
||||
- --reseal-on-uncle [#5940](https://github.com/paritytech/parity/pull/5940)
|
||||
- Support comments in reserved peers file ([#6004](https://github.com/paritytech/parity/pull/6004)) [#6012](https://github.com/paritytech/parity/pull/6012)
|
||||
- Add new md tnc [#5937](https://github.com/paritytech/parity/pull/5937)
|
||||
- Fix output of parity-evm in case of bad instruction [#5955](https://github.com/paritytech/parity/pull/5955)
|
||||
- Don't send notifications to unsubscribed clients of PubSub [#5960](https://github.com/paritytech/parity/pull/5960)
|
||||
- Proper light client informant and more verification of imported headers [#5897](https://github.com/paritytech/parity/pull/5897)
|
||||
- New Kovan bootnodes [#6017](https://github.com/paritytech/parity/pull/6017)
|
||||
- Use standard paths for Ethash cache [#5881](https://github.com/paritytech/parity/pull/5881)
|
||||
- Defer code hash calculation. [#5959](https://github.com/paritytech/parity/pull/5959)
|
||||
- Fix first run wizard. [#6000](https://github.com/paritytech/parity/pull/6000)
|
||||
- migration to serde 1.0 [#5996](https://github.com/paritytech/parity/pull/5996)
|
||||
- SecretStore: generating signatures [#5764](https://github.com/paritytech/parity/pull/5764)
|
||||
- bigint upgraded to version 3.0 [#5986](https://github.com/paritytech/parity/pull/5986)
|
||||
- config: don't allow dev chain with force sealing option [#5965](https://github.com/paritytech/parity/pull/5965)
|
||||
- Update lockfile for miniz-sys and gcc [#5969](https://github.com/paritytech/parity/pull/5969)
|
||||
- Clean up function naming in RPC error module [#5995](https://github.com/paritytech/parity/pull/5995)
|
||||
- Fix underflow in gas calculation [#5975](https://github.com/paritytech/parity/pull/5975)
|
||||
- PubSub for parity-js [#5830](https://github.com/paritytech/parity/pull/5830)
|
||||
- Report whether a peer was kept from `Handler::on_connect` [#5958](https://github.com/paritytech/parity/pull/5958)
|
||||
- Implement skeleton for transaction index and epoch transition proof PIP messages [#5908](https://github.com/paritytech/parity/pull/5908)
|
||||
- TransactionQueue improvements [#5917](https://github.com/paritytech/parity/pull/5917)
|
||||
- constant time HMAC comparison and clarify docs in ethkey [#5952](https://github.com/paritytech/parity/pull/5952)
|
||||
- Avoid pre-computing jump destinations [#5954](https://github.com/paritytech/parity/pull/5954)
|
||||
- Upgrade elastic array [#5949](https://github.com/paritytech/parity/pull/5949)
|
||||
- PoA: Wait for transition finality before applying [#5774](https://github.com/paritytech/parity/pull/5774)
|
||||
- Logs Pub-Sub [#5705](https://github.com/paritytech/parity/pull/5705)
|
||||
- Add the command to install the parity snap [#5945](https://github.com/paritytech/parity/pull/5945)
|
||||
- Reduce unnecessary allocations [#5944](https://github.com/paritytech/parity/pull/5944)
|
||||
- Clarify confusing messages. [#5935](https://github.com/paritytech/parity/pull/5935)
|
||||
- Content Security Policy [#5790](https://github.com/paritytech/parity/pull/5790)
|
||||
- CLI: Export error message and less verbose peer counter. [#5870](https://github.com/paritytech/parity/pull/5870)
|
||||
- network: make it more explicit about StreamToken and TimerToken [#5939](https://github.com/paritytech/parity/pull/5939)
|
||||
- sync: make it more idiomatic rust [#5938](https://github.com/paritytech/parity/pull/5938)
|
||||
- Prioritize accounts over address book [#5909](https://github.com/paritytech/parity/pull/5909)
|
||||
- Fixing failing compilation of RPC test on master. [#5916](https://github.com/paritytech/parity/pull/5916)
|
||||
- Empty local middleware, until explicitly requested [#5912](https://github.com/paritytech/parity/pull/5912)
|
||||
- Cancel propagated TX [#5899](https://github.com/paritytech/parity/pull/5899)
|
||||
- fix minor race condition in aura seal generation [#5910](https://github.com/paritytech/parity/pull/5910)
|
||||
- Docs for Pub-Sub, optional parameter for parity_subscribe [#5833](https://github.com/paritytech/parity/pull/5833)
|
||||
- Fix gas editor doubling-up on gas [#5820](https://github.com/paritytech/parity/pull/5820)
|
||||
- Information about used paths added to general output block [#5904](https://github.com/paritytech/parity/pull/5904)
|
||||
- Domain-locked web tokens. [#5894](https://github.com/paritytech/parity/pull/5894)
|
||||
- Removed panic handlers [#5895](https://github.com/paritytech/parity/pull/5895)
|
||||
- Latest changes from Rust RocksDB binding merged [#5905](https://github.com/paritytech/parity/pull/5905)
|
||||
- Adjust keyethereum/secp256 aliasses [#5903](https://github.com/paritytech/parity/pull/5903)
|
||||
- Keyethereum fs dependency [#5902](https://github.com/paritytech/parity/pull/5902)
|
||||
- Ethereum Classic Monetary Policy [#5741](https://github.com/paritytech/parity/pull/5741)
|
||||
- Initial token should allow full access. [#5873](https://github.com/paritytech/parity/pull/5873)
|
||||
- Fixed account selection for Dapps on public node [#5856](https://github.com/paritytech/parity/pull/5856)
|
||||
- blacklist bad snapshot manifest hashes upon failure [#5874](https://github.com/paritytech/parity/pull/5874)
|
||||
- Fix wrongly called timeouts [#5838](https://github.com/paritytech/parity/pull/5838)
|
||||
- ArchiveDB and other small fixes [#5867](https://github.com/paritytech/parity/pull/5867)
|
||||
- convert try!() to ? [#5866](https://github.com/paritytech/parity/pull/5866)
|
||||
- Make config file optional in systemd [#5847](https://github.com/paritytech/parity/pull/5847)
|
||||
- EIP-116 (214), [#4833](https://github.com/paritytech/parity/issues/4833) [#4851](https://github.com/paritytech/parity/pull/4851)
|
||||
- all executables are workspace members [#5865](https://github.com/paritytech/parity/pull/5865)
|
||||
- minor optimizations of the modexp builtin [#5860](https://github.com/paritytech/parity/pull/5860)
|
||||
- three small commits for HashDB and MemoryDB [#5766](https://github.com/paritytech/parity/pull/5766)
|
||||
- use rust 1.18's retain to boost the purge performance [#5801](https://github.com/paritytech/parity/pull/5801)
|
||||
- Allow IPFS server to accept POST requests [#5858](https://github.com/paritytech/parity/pull/5858)
|
||||
- Dutch i18n from [#5802](https://github.com/paritytech/parity/issues/5802) for master [#5836](https://github.com/paritytech/parity/pull/5836)
|
||||
- Typos in token deploy dapp ui [#5851](https://github.com/paritytech/parity/pull/5851)
|
||||
- A CLI flag to allow fast transaction signing when account is unlocked. [#5778](https://github.com/paritytech/parity/pull/5778)
|
||||
- Removing `additional` field from EVM instructions [#5821](https://github.com/paritytech/parity/pull/5821)
|
||||
- Don't fail on wrong log decoding [#5813](https://github.com/paritytech/parity/pull/5813)
|
||||
- Use randomized subscription ids for PubSub [#5756](https://github.com/paritytech/parity/pull/5756)
|
||||
- Fixed mem write for empty slice [#5827](https://github.com/paritytech/parity/pull/5827)
|
||||
- Fix party technologies [#5810](https://github.com/paritytech/parity/pull/5810)
|
||||
- Revert "Fixed mem write for empty slice" [#5826](https://github.com/paritytech/parity/pull/5826)
|
||||
- Fixed mem write for empty slice [#5825](https://github.com/paritytech/parity/pull/5825)
|
||||
- Fix JS tests [#5822](https://github.com/paritytech/parity/pull/5822)
|
||||
- Bump native-tls and openssl crates. [#5817](https://github.com/paritytech/parity/pull/5817)
|
||||
- Public node using WASM [#5734](https://github.com/paritytech/parity/pull/5734)
|
||||
- enforce block signer == author field in PoA [#5808](https://github.com/paritytech/parity/pull/5808)
|
||||
- Fix stack display in evmbin. [#5733](https://github.com/paritytech/parity/pull/5733)
|
||||
- Disable UI if it's not compiled in. [#5773](https://github.com/paritytech/parity/pull/5773)
|
||||
- Require phrase confirmation. [#5731](https://github.com/paritytech/parity/pull/5731)
|
||||
- Duration limit made optional for EthashParams [#5777](https://github.com/paritytech/parity/pull/5777)
|
||||
- Update Changelog for 1.6.8 [#5798](https://github.com/paritytech/parity/pull/5798)
|
||||
- Replace Ethcore comany name in T&C and some other places [#5796](https://github.com/paritytech/parity/pull/5796)
|
||||
- PubSub for IPC. [#5800](https://github.com/paritytech/parity/pull/5800)
|
||||
- Fix terminology distributed -> decentralized applications [#5797](https://github.com/paritytech/parity/pull/5797)
|
||||
- Disable compression for RLP strings [#5786](https://github.com/paritytech/parity/pull/5786)
|
||||
- update the source for the snapcraft package [#5781](https://github.com/paritytech/parity/pull/5781)
|
||||
- Fixed default UI port for mac installer [#5782](https://github.com/paritytech/parity/pull/5782)
|
||||
- Block invalid account name creation [#5784](https://github.com/paritytech/parity/pull/5784)
|
||||
- Update Cid/multihash/ring/tinykeccak [#5785](https://github.com/paritytech/parity/pull/5785)
|
||||
- use NULL_RLP, remove NULL_RLP_STATIC [#5742](https://github.com/paritytech/parity/pull/5742)
|
||||
- Blacklist empty phrase account. [#5730](https://github.com/paritytech/parity/pull/5730)
|
||||
- EIP-211 RETURNDATACOPY and RETURNDATASIZE [#5678](https://github.com/paritytech/parity/pull/5678)
|
||||
- Bump mio [#5763](https://github.com/paritytech/parity/pull/5763)
|
||||
- Fixing UI issues after UI server refactor [#5710](https://github.com/paritytech/parity/pull/5710)
|
||||
- Fix WS server expose issue. [#5728](https://github.com/paritytech/parity/pull/5728)
|
||||
- Fix local transactions without condition. [#5716](https://github.com/paritytech/parity/pull/5716)
|
||||
- Bump parity-wordlist. [#5748](https://github.com/paritytech/parity/pull/5748)
|
||||
- two small changes in evm [#5700](https://github.com/paritytech/parity/pull/5700)
|
||||
- Evmbin: JSON format printing pre-state. [#5712](https://github.com/paritytech/parity/pull/5712)
|
||||
- Recover from empty phrase in dev mode [#5698](https://github.com/paritytech/parity/pull/5698)
|
||||
- EIP-210 BLOCKHASH changes [#5505](https://github.com/paritytech/parity/pull/5505)
|
||||
- fixes typo [#5708](https://github.com/paritytech/parity/pull/5708)
|
||||
- Bump rocksdb [#5707](https://github.com/paritytech/parity/pull/5707)
|
||||
- Fixed --datadir option [#5697](https://github.com/paritytech/parity/pull/5697)
|
||||
- rpc -> weak to arc [#5688](https://github.com/paritytech/parity/pull/5688)
|
||||
- typo fix [#5699](https://github.com/paritytech/parity/pull/5699)
|
||||
- Revamping parity-evmbin [#5696](https://github.com/paritytech/parity/pull/5696)
|
||||
- Update dependencies and bigint api [#5685](https://github.com/paritytech/parity/pull/5685)
|
||||
- UI server refactoring [#5580](https://github.com/paritytech/parity/pull/5580)
|
||||
- Fix from/into electrum in ethkey [#5686](https://github.com/paritytech/parity/pull/5686)
|
||||
- Add unit tests [#5668](https://github.com/paritytech/parity/pull/5668)
|
||||
- Guanqun add unit tests [#5671](https://github.com/paritytech/parity/pull/5671)
|
||||
- Parity-PubSub as a separate API. [#5676](https://github.com/paritytech/parity/pull/5676)
|
||||
- EIP-140 REVERT opcode [#5477](https://github.com/paritytech/parity/pull/5477)
|
||||
- Update CHANGELOG for 1.6.7 [#5683](https://github.com/paritytech/parity/pull/5683)
|
||||
- Updated docs slightly. [#5674](https://github.com/paritytech/parity/pull/5674)
|
||||
- Fix build [#5684](https://github.com/paritytech/parity/pull/5684)
|
||||
- Back-references for the on-demand service [#5573](https://github.com/paritytech/parity/pull/5573)
|
||||
- Dynamically adjust PIP request costs based on gathered data [#5603](https://github.com/paritytech/parity/pull/5603)
|
||||
- use cargo workspace [#5601](https://github.com/paritytech/parity/pull/5601)
|
||||
- Latest headers Pub-Sub [#5655](https://github.com/paritytech/parity/pull/5655)
|
||||
- improved dockerfile builds [#5659](https://github.com/paritytech/parity/pull/5659)
|
||||
- Adding CLI options: port shift and unsafe expose. [#5677](https://github.com/paritytech/parity/pull/5677)
|
||||
- Report missing author in Aura [#5583](https://github.com/paritytech/parity/pull/5583)
|
||||
- typo fix [#5669](https://github.com/paritytech/parity/pull/5669)
|
||||
- Remove public middleware (temporary) [#5665](https://github.com/paritytech/parity/pull/5665)
|
||||
- Remove additional polyfill [#5663](https://github.com/paritytech/parity/pull/5663)
|
||||
- Importing accounts from files. [#5644](https://github.com/paritytech/parity/pull/5644)
|
||||
- remove the deprecated options in rustfmt.toml [#5616](https://github.com/paritytech/parity/pull/5616)
|
||||
- Update the Console dapp [#5602](https://github.com/paritytech/parity/pull/5602)
|
||||
- Create an account for chain=dev [#5612](https://github.com/paritytech/parity/pull/5612)
|
||||
- Use babel-runtime as opposed to babel-polyfill [#5662](https://github.com/paritytech/parity/pull/5662)
|
||||
- Connection dialog timestamp info [#5554](https://github.com/paritytech/parity/pull/5554)
|
||||
- use copy_from_slice instead of for loop [#5647](https://github.com/paritytech/parity/pull/5647)
|
||||
- Light friendly dapps [#5634](https://github.com/paritytech/parity/pull/5634)
|
||||
- Add Recover button to Accounts and warnings [#5645](https://github.com/paritytech/parity/pull/5645)
|
||||
- Update eth_sign docs. [#5631](https://github.com/paritytech/parity/pull/5631)
|
||||
- Proper signer Pub-Sub for pending requests. [#5594](https://github.com/paritytech/parity/pull/5594)
|
||||
- Bump bigint to 1.0.5 [#5641](https://github.com/paritytech/parity/pull/5641)
|
||||
- PoA warp implementation [#5488](https://github.com/paritytech/parity/pull/5488)
|
||||
- Improve on-demand dispatch and add support for batch requests [#5419](https://github.com/paritytech/parity/pull/5419)
|
||||
- Use default account for sending transactions [#5588](https://github.com/paritytech/parity/pull/5588)
|
||||
- Add peer management to the Status tab [#5566](https://github.com/paritytech/parity/pull/5566)
|
||||
- Add monotonic step transition [#5587](https://github.com/paritytech/parity/pull/5587)
|
||||
- Decrypting for external accounts. [#5581](https://github.com/paritytech/parity/pull/5581)
|
||||
- only enable warp sync when engine supports it [#5595](https://github.com/paritytech/parity/pull/5595)
|
||||
- fix the doc of installing rust [#5586](https://github.com/paritytech/parity/pull/5586)
|
||||
- Small fixes [#5584](https://github.com/paritytech/parity/pull/5584)
|
||||
- SecretStore: remove session on master node [#5545](https://github.com/paritytech/parity/pull/5545)
|
||||
- run-clean [#5607](https://github.com/paritytech/parity/pull/5607)
|
||||
- relicense RLP to MIT/Apache2 [#5591](https://github.com/paritytech/parity/pull/5591)
|
||||
- Fix eth_sign signature encoding. [#5597](https://github.com/paritytech/parity/pull/5597)
|
||||
- Check pending request on Node local transactions [#5564](https://github.com/paritytech/parity/pull/5564)
|
||||
- Add tooltips on ActionBar [#5562](https://github.com/paritytech/parity/pull/5562)
|
||||
- Can't deploy without compiling Contract [#5593](https://github.com/paritytech/parity/pull/5593)
|
||||
- Add a warning when node is syncing [#5565](https://github.com/paritytech/parity/pull/5565)
|
||||
- Update registry middleware [#5585](https://github.com/paritytech/parity/pull/5585)
|
||||
- Set block condition to BigNumber in MethodDecoding [#5592](https://github.com/paritytech/parity/pull/5592)
|
||||
- Load the sources immediately in Contract Dev [#5575](https://github.com/paritytech/parity/pull/5575)
|
||||
- Remove formal verification messages in Dev Contract [#5574](https://github.com/paritytech/parity/pull/5574)
|
||||
- Fix event params decoding when no names for parameters [#5567](https://github.com/paritytech/parity/pull/5567)
|
||||
- Do not convert to Dates twice [#5563](https://github.com/paritytech/parity/pull/5563)
|
||||
- Fix Multisig wallet settings [#5560](https://github.com/paritytech/parity/pull/5560)
|
||||
- Typo [#5547](https://github.com/paritytech/parity/pull/5547)
|
||||
- Generic PubSub implementation [#5456](https://github.com/paritytech/parity/pull/5456)
|
||||
- Fix CI paths. [#5570](https://github.com/paritytech/parity/pull/5570)
|
||||
- reorg into blocks before minimum history [#5558](https://github.com/paritytech/parity/pull/5558)
|
||||
- EIP-86 update [#5506](https://github.com/paritytech/parity/pull/5506)
|
||||
- Secretstore RPCs + integration [#5439](https://github.com/paritytech/parity/pull/5439)
|
||||
- Fixes Parity Bar position [#5557](https://github.com/paritytech/parity/pull/5557)
|
||||
- Fixes invalid log in BadgeReg events [#5556](https://github.com/paritytech/parity/pull/5556)
|
||||
- Fix issues in Contract Development view [#5555](https://github.com/paritytech/parity/pull/5555)
|
||||
- Added missing methods [#5542](https://github.com/paritytech/parity/pull/5542)
|
||||
- option to disable persistent txqueue [#5544](https://github.com/paritytech/parity/pull/5544)
|
||||
- Bump jsonrpc [#5552](https://github.com/paritytech/parity/pull/5552)
|
||||
- Retrieve block headers only for header-only info [#5480](https://github.com/paritytech/parity/pull/5480)
|
||||
- add snap to CI [#5519](https://github.com/paritytech/parity/pull/5519)
|
||||
- Pass additional data when reporting [#5527](https://github.com/paritytech/parity/pull/5527)
|
||||
- Calculate post-constructors state root in spec at load time [#5523](https://github.com/paritytech/parity/pull/5523)
|
||||
- Fix utf8 decoding [#5533](https://github.com/paritytech/parity/pull/5533)
|
||||
- Add CHANGELOG.md [#5513](https://github.com/paritytech/parity/pull/5513)
|
||||
- Change all occurrences of ethcore.io into parity.io [#5528](https://github.com/paritytech/parity/pull/5528)
|
||||
- Memory usage optimization [#5526](https://github.com/paritytech/parity/pull/5526)
|
||||
- Compose transaction RPC. [#5524](https://github.com/paritytech/parity/pull/5524)
|
||||
- Support external eth_sign [#5481](https://github.com/paritytech/parity/pull/5481)
|
||||
- Treat block numbers as strings, not BigNums. [#5449](https://github.com/paritytech/parity/pull/5449)
|
||||
- npm cleanups [#5512](https://github.com/paritytech/parity/pull/5512)
|
||||
- Export acc js [#4973](https://github.com/paritytech/parity/pull/4973)
|
||||
- YARN [#5395](https://github.com/paritytech/parity/pull/5395)
|
||||
- Fix linting issues [#5511](https://github.com/paritytech/parity/pull/5511)
|
||||
- Chinese Translation [#5460](https://github.com/paritytech/parity/pull/5460)
|
||||
- Fixing secretstore TODOs - part 2 [#5416](https://github.com/paritytech/parity/pull/5416)
|
||||
- fix json format of state snapshot [#5504](https://github.com/paritytech/parity/pull/5504)
|
||||
- Bump jsonrpc version [#5489](https://github.com/paritytech/parity/pull/5489)
|
||||
- Groundwork for generalized warp sync [#5454](https://github.com/paritytech/parity/pull/5454)
|
||||
- Add the packaging metadata to build the parity snap [#5496](https://github.com/paritytech/parity/pull/5496)
|
||||
- Cancel tx JS [#4958](https://github.com/paritytech/parity/pull/4958)
|
||||
- EIP-212 (bn128 curve pairing) [#5307](https://github.com/paritytech/parity/pull/5307)
|
||||
- fix panickers in tree-route [#5479](https://github.com/paritytech/parity/pull/5479)
|
||||
- Update links to etherscan.io [#5455](https://github.com/paritytech/parity/pull/5455)
|
||||
- Refresh UI on nodeKind changes, e.g. personal -> public [#5312](https://github.com/paritytech/parity/pull/5312)
|
||||
- Correct contract address for EIP-86 [#5473](https://github.com/paritytech/parity/pull/5473)
|
||||
- Force two decimals for USD conversion rate [#5471](https://github.com/paritytech/parity/pull/5471)
|
||||
- Refactoring of Tokens & Balances [#5372](https://github.com/paritytech/parity/pull/5372)
|
||||
- Background-repeat round [#5475](https://github.com/paritytech/parity/pull/5475)
|
||||
- nl i18n updated [#5461](https://github.com/paritytech/parity/pull/5461)
|
||||
- Show ETH value (even 0) if ETH transfer in transaction list [#5406](https://github.com/paritytech/parity/pull/5406)
|
||||
- Store the pending requests per network version [#5405](https://github.com/paritytech/parity/pull/5405)
|
||||
- Use in-memory database for tests [#5451](https://github.com/paritytech/parity/pull/5451)
|
||||
- WebSockets RPC server [#5425](https://github.com/paritytech/parity/pull/5425)
|
||||
- Added missing docs [#5452](https://github.com/paritytech/parity/pull/5452)
|
||||
- Tests and tweaks for public node middleware [#5417](https://github.com/paritytech/parity/pull/5417)
|
||||
- Fix removal of hash-mismatched files. [#5440](https://github.com/paritytech/parity/pull/5440)
|
||||
- parity_getBlockHeaderByNumber and LightFetch utility [#5383](https://github.com/paritytech/parity/pull/5383)
|
||||
- New state tests [#5418](https://github.com/paritytech/parity/pull/5418)
|
||||
- Fix buffer length for QR code gen. [#5447](https://github.com/paritytech/parity/pull/5447)
|
||||
- Add raw hash signing [#5423](https://github.com/paritytech/parity/pull/5423)
|
||||
- Filters and block RPCs for the light client [#5320](https://github.com/paritytech/parity/pull/5320)
|
||||
- Work around mismatch for QR checksum [#5374](https://github.com/paritytech/parity/pull/5374)
|
||||
- easy to use conversion from and to string for ethstore::Crypto [#5437](https://github.com/paritytech/parity/pull/5437)
|
||||
- Tendermint fixes [#5415](https://github.com/paritytech/parity/pull/5415)
|
||||
- Adrianbrink lightclientcache branch. [#5428](https://github.com/paritytech/parity/pull/5428)
|
||||
- Add caching to HeaderChain struct [#5403](https://github.com/paritytech/parity/pull/5403)
|
||||
- Add decryption to the UI (in the Signer) [#5422](https://github.com/paritytech/parity/pull/5422)
|
||||
- Add CIDv0 RPC [#5414](https://github.com/paritytech/parity/pull/5414)
|
||||
- Updating documentation for RPCs [#5392](https://github.com/paritytech/parity/pull/5392)
|
||||
- Fixing secretstore TODOs - part 1 [#5386](https://github.com/paritytech/parity/pull/5386)
|
||||
- Fixing disappearing content. [#5399](https://github.com/paritytech/parity/pull/5399)
|
||||
- Snapshot chunks packed by size [#5318](https://github.com/paritytech/parity/pull/5318)
|
||||
- APIs wildcards and simple arithmetic. [#5402](https://github.com/paritytech/parity/pull/5402)
|
||||
- Fixing compilation without dapps. [#5410](https://github.com/paritytech/parity/pull/5410)
|
||||
- Don't use port 8080 anymore [#5397](https://github.com/paritytech/parity/pull/5397)
|
||||
- Quick'n'dirty CLI for the light client [#5002](https://github.com/paritytech/parity/pull/5002)
|
||||
- set gas limit before proving transactions [#5401](https://github.com/paritytech/parity/pull/5401)
|
||||
- Public node: perf and fixes [#5390](https://github.com/paritytech/parity/pull/5390)
|
||||
- Straight download path in the readme [#5393](https://github.com/paritytech/parity/pull/5393)
|
||||
- On-chain ACL checker for secretstore [#5015](https://github.com/paritytech/parity/pull/5015)
|
||||
- Allow empty-encoded values from QR encoding [#5385](https://github.com/paritytech/parity/pull/5385)
|
||||
- Update npm build for new inclusions [#5381](https://github.com/paritytech/parity/pull/5381)
|
||||
- Fix for Ubuntu Dockerfile [#5356](https://github.com/paritytech/parity/pull/5356)
|
||||
- Secretstore over network [#4974](https://github.com/paritytech/parity/pull/4974)
|
||||
- Dapps and RPC server merge [#5365](https://github.com/paritytech/parity/pull/5365)
|
||||
- trigger js build release [#5379](https://github.com/paritytech/parity/pull/5379)
|
||||
- Update expanse json with fork at block 600000 [#5351](https://github.com/paritytech/parity/pull/5351)
|
||||
- Futures-based native wrappers for contract ABIs [#5341](https://github.com/paritytech/parity/pull/5341)
|
||||
- Kovan warp sync fixed [#5337](https://github.com/paritytech/parity/pull/5337)
|
||||
- Aura eip155 validation transition [#5362](https://github.com/paritytech/parity/pull/5362)
|
||||
- Shared wordlist for brain wallets [#5331](https://github.com/paritytech/parity/pull/5331)
|
||||
- Allow signing via Qr [#4881](https://github.com/paritytech/parity/pull/4881)
|
||||
- Allow entry of url or hash for DappReg meta [#5360](https://github.com/paritytech/parity/pull/5360)
|
||||
- Adjust tx overlay colours [#5353](https://github.com/paritytech/parity/pull/5353)
|
||||
- Add ability to disallow API subscriptions [#5366](https://github.com/paritytech/parity/pull/5366)
|
||||
- EIP-213 (bn128 curve operations) [#4999](https://github.com/paritytech/parity/pull/4999)
|
||||
- Fix analize output file name [#5357](https://github.com/paritytech/parity/pull/5357)
|
||||
- Add default eip155 validation [#5346](https://github.com/paritytech/parity/pull/5346)
|
||||
- Add new seed nodes for Classic chain [#5345](https://github.com/paritytech/parity/pull/5345)
|
||||
- Shared wordlist for frontend [#5336](https://github.com/paritytech/parity/pull/5336)
|
||||
- fix rpc tests [#5338](https://github.com/paritytech/parity/pull/5338)
|
||||
- Public node with accounts and signing in Frontend [#5304](https://github.com/paritytech/parity/pull/5304)
|
||||
- Rename Status/Status -> Status/NodeStatus [#5332](https://github.com/paritytech/parity/pull/5332)
|
||||
- Updating paths to repos. [#5330](https://github.com/paritytech/parity/pull/5330)
|
||||
- Separate status for canceled local transactions. [#5319](https://github.com/paritytech/parity/pull/5319)
|
||||
- Cleanup the Status View [#5317](https://github.com/paritytech/parity/pull/5317)
|
||||
- Update UI minimised requests [#5324](https://github.com/paritytech/parity/pull/5324)
|
||||
- Order signer transactions FIFO [#5321](https://github.com/paritytech/parity/pull/5321)
|
||||
- updating dependencies [#5028](https://github.com/paritytech/parity/pull/5028)
|
||||
- Minimise transactions progress [#4942](https://github.com/paritytech/parity/pull/4942)
|
||||
- Fix eth_sign showing as wallet account [#5309](https://github.com/paritytech/parity/pull/5309)
|
||||
- Ropsten revival [#5302](https://github.com/paritytech/parity/pull/5302)
|
||||
- Strict validation transitions [#4988](https://github.com/paritytech/parity/pull/4988)
|
||||
- Fix default list sorting [#5303](https://github.com/paritytech/parity/pull/5303)
|
||||
- Use unique owners for multisig wallets [#5298](https://github.com/paritytech/parity/pull/5298)
|
||||
- Copy all existing i18n strings into zh (as-is translation aid) [#5305](https://github.com/paritytech/parity/pull/5305)
|
||||
- Fix booleans in Typedinput [#5295](https://github.com/paritytech/parity/pull/5295)
|
||||
- node kind RPC [#5025](https://github.com/paritytech/parity/pull/5025)
|
||||
- Fix the use of MobX in playground [#5294](https://github.com/paritytech/parity/pull/5294)
|
||||
- Fine grained snapshot chunking [#5019](https://github.com/paritytech/parity/pull/5019)
|
||||
- Add lint:i18n to find missing & extra keys [#5290](https://github.com/paritytech/parity/pull/5290)
|
||||
- Scaffolding for zh translations, including first-round by @btceth [#5289](https://github.com/paritytech/parity/pull/5289)
|
||||
- JS package bumps [#5287](https://github.com/paritytech/parity/pull/5287)
|
||||
- Auto-extract new i18n strings (update) [#5288](https://github.com/paritytech/parity/pull/5288)
|
||||
- eip100b [#5027](https://github.com/paritytech/parity/pull/5027)
|
||||
- Set earliest era in snapshot restoration [#5021](https://github.com/paritytech/parity/pull/5021)
|
||||
- Avoid clogging up tmp when updater dir has bad permissions. [#5024](https://github.com/paritytech/parity/pull/5024)
|
||||
- Resilient warp sync [#5018](https://github.com/paritytech/parity/pull/5018)
|
||||
- Create webpack analysis files (size) [#5009](https://github.com/paritytech/parity/pull/5009)
|
||||
- Dispatch an open event on drag of Parity Bar [#4987](https://github.com/paritytech/parity/pull/4987)
|
||||
- Various installer and tray apps fixes [#4970](https://github.com/paritytech/parity/pull/4970)
|
||||
- Export account RPC [#4967](https://github.com/paritytech/parity/pull/4967)
|
||||
- Switching ValidatorSet [#4961](https://github.com/paritytech/parity/pull/4961)
|
||||
- Implement PIP messages, request builder, and handlers [#4945](https://github.com/paritytech/parity/pull/4945)
|
||||
- auto lint [#5003](https://github.com/paritytech/parity/pull/5003)
|
||||
- Fix FireFox overflows [#5000](https://github.com/paritytech/parity/pull/5000)
|
||||
- Show busy indicator, focus first field in password change [#4997](https://github.com/paritytech/parity/pull/4997)
|
||||
- Consistent store naming in the Signer components [#4996](https://github.com/paritytech/parity/pull/4996)
|
||||
- second (and last) part of rlp refactor [#4901](https://github.com/paritytech/parity/pull/4901)
|
||||
- Double click to select account creation type [#4986](https://github.com/paritytech/parity/pull/4986)
|
||||
- Fixes to the Registry dapp [#4984](https://github.com/paritytech/parity/pull/4984)
|
||||
- Extend api.util [#4979](https://github.com/paritytech/parity/pull/4979)
|
||||
- Updating JSON-RPC crates [#4934](https://github.com/paritytech/parity/pull/4934)
|
||||
- splitting part of util into smaller crates [#4956](https://github.com/paritytech/parity/pull/4956)
|
||||
- Updating syntex et al [#4983](https://github.com/paritytech/parity/pull/4983)
|
||||
- EIP198 and built-in activation [#4926](https://github.com/paritytech/parity/pull/4926)
|
||||
- Fix MethodDecoding for Arrays [#4977](https://github.com/paritytech/parity/pull/4977)
|
||||
- Try to fix WS race condition connection [#4976](https://github.com/paritytech/parity/pull/4976)
|
||||
- eth_sign where account === undefined [#4964](https://github.com/paritytech/parity/pull/4964)
|
||||
- Fix references to api outside of `parity.js` [#4981](https://github.com/paritytech/parity/pull/4981)
|
||||
- Fix Password Dialog form overflow [#4968](https://github.com/paritytech/parity/pull/4968)
|
||||
- Changing Mutex into RwLock for transaction queue [#4951](https://github.com/paritytech/parity/pull/4951)
|
||||
- Disable max seal period for external sealing [#4927](https://github.com/paritytech/parity/pull/4927)
|
||||
- Attach hardware wallets already in addressbook [#4912](https://github.com/paritytech/parity/pull/4912)
|
||||
- rlp serialization refactor [#4873](https://github.com/paritytech/parity/pull/4873)
|
||||
- Bump nanomsg [#4965](https://github.com/paritytech/parity/pull/4965)
|
||||
- Fixed multi-chunk ledger transactions on windows [#4960](https://github.com/paritytech/parity/pull/4960)
|
||||
- Fix outputs in Contract Constant Queries [#4953](https://github.com/paritytech/parity/pull/4953)
|
||||
- systemd: Start parity after network.target [#4952](https://github.com/paritytech/parity/pull/4952)
|
||||
- Remove transaction RPC [#4949](https://github.com/paritytech/parity/pull/4949)
|
||||
- Swap out ethcore.io url for parity.io [#4947](https://github.com/paritytech/parity/pull/4947)
|
||||
- Don't remove confirmed requests to early. [#4933](https://github.com/paritytech/parity/pull/4933)
|
||||
- Ensure sealing work enabled in miner once subscribers added [#4930](https://github.com/paritytech/parity/pull/4930)
|
||||
- Add z-index to small modals as well [#4923](https://github.com/paritytech/parity/pull/4923)
|
||||
- Bump nanomsg [#4946](https://github.com/paritytech/parity/pull/4946)
|
||||
- Bumping multihash and libc [#4943](https://github.com/paritytech/parity/pull/4943)
|
||||
- Edit ETH value, gas and gas price in Contract Deployment [#4919](https://github.com/paritytech/parity/pull/4919)
|
||||
- Add ability to configure Secure API [#4922](https://github.com/paritytech/parity/pull/4922)
|
||||
- Add Token image from URL [#4916](https://github.com/paritytech/parity/pull/4916)
|
||||
- Use the registry fee in Token Deployment dapp [#4915](https://github.com/paritytech/parity/pull/4915)
|
||||
- Add reseal max period [#4903](https://github.com/paritytech/parity/pull/4903)
|
||||
- Detect rust compiler version in Parity build script, closes 4742 [#4907](https://github.com/paritytech/parity/pull/4907)
|
||||
- Add Vaults logic to First Run [#4914](https://github.com/paritytech/parity/pull/4914)
|
||||
- Updated gcc and rayon crates to remove outdated num_cpus dependency [#4909](https://github.com/paritytech/parity/pull/4909)
|
||||
- Renaming evm binary to avoid conflicts. [#4899](https://github.com/paritytech/parity/pull/4899)
|
||||
- Better error handling for traces RPC [#4849](https://github.com/paritytech/parity/pull/4849)
|
||||
- Safari SectionList fix [#4895](https://github.com/paritytech/parity/pull/4895)
|
||||
- Safari Dialog scrolling fix [#4893](https://github.com/paritytech/parity/pull/4893)
|
||||
- Spelling :) [#4900](https://github.com/paritytech/parity/pull/4900)
|
||||
- Additional kovan params [#4892](https://github.com/paritytech/parity/pull/4892)
|
||||
- trigger js-precompiled build [#4898](https://github.com/paritytech/parity/pull/4898)
|
||||
- Recalculate receipt roots in close_and_lock [#4884](https://github.com/paritytech/parity/pull/4884)
|
||||
- Reload UI on network switch [#4864](https://github.com/paritytech/parity/pull/4864)
|
||||
- Update parity-ui-precompiled with branch [#4850](https://github.com/paritytech/parity/pull/4850)
|
||||
- OSX Installer is no longer experimental [#4882](https://github.com/paritytech/parity/pull/4882)
|
||||
- Chain-selection from UI [#4859](https://github.com/paritytech/parity/pull/4859)
|
||||
- removed redundant (and unused) FromJson trait [#4871](https://github.com/paritytech/parity/pull/4871)
|
||||
- fix typos and grammar [#4880](https://github.com/paritytech/parity/pull/4880)
|
||||
- Remove old experimental remote-db code [#4872](https://github.com/paritytech/parity/pull/4872)
|
||||
- removed redundant FixedHash trait, fixes [#4029](https://github.com/paritytech/parity/issues/4029) [#4866](https://github.com/paritytech/parity/pull/4866)
|
||||
- Reference JSON-RPC more changes-friendly [#4870](https://github.com/paritytech/parity/pull/4870)
|
||||
- Better handling of Solidity compliation [#4860](https://github.com/paritytech/parity/pull/4860)
|
||||
- Go through contract links in Transaction List display [#4863](https://github.com/paritytech/parity/pull/4863)
|
||||
- Fix Gas Price Selector Tooltips [#4865](https://github.com/paritytech/parity/pull/4865)
|
||||
- Fix auto-updater [#4867](https://github.com/paritytech/parity/pull/4867)
|
||||
- Make the UI work offline [#4861](https://github.com/paritytech/parity/pull/4861)
|
||||
- Subscribe to accounts info in Signer / ParityBar [#4856](https://github.com/paritytech/parity/pull/4856)
|
||||
- Don't link libsnappy explicitly [#4841](https://github.com/paritytech/parity/pull/4841)
|
||||
- Fix paste in Inputs [#4854](https://github.com/paritytech/parity/pull/4854)
|
||||
- Extract i18n from shared UI components [#4834](https://github.com/paritytech/parity/pull/4834)
|
||||
- Fix paste in Inputs [#4844](https://github.com/paritytech/parity/pull/4844)
|
||||
- Pull contract deployment title from available steps [#4848](https://github.com/paritytech/parity/pull/4848)
|
||||
- Supress USB error message [#4839](https://github.com/paritytech/parity/pull/4839)
|
||||
- Fix getTransactionCount in --geth mode [#4837](https://github.com/paritytech/parity/pull/4837)
|
||||
- CI: test coverage (for core and js) [#4832](https://github.com/paritytech/parity/pull/4832)
|
||||
- Lowering threshold for transactions above gas limit [#4831](https://github.com/paritytech/parity/pull/4831)
|
||||
- Fix TxViewer when no `to` (contract deployment) [#4847](https://github.com/paritytech/parity/pull/4847)
|
||||
- Fix method decoding [#4845](https://github.com/paritytech/parity/pull/4845)
|
||||
- Add React Hot Reload to dapps + TokenDeploy fix [#4846](https://github.com/paritytech/parity/pull/4846)
|
||||
- Dapps show multiple times in some cases [#4843](https://github.com/paritytech/parity/pull/4843)
|
||||
- Fixes to the Registry dapp [#4838](https://github.com/paritytech/parity/pull/4838)
|
||||
- Show token icons on list summary pages [#4826](https://github.com/paritytech/parity/pull/4826)
|
||||
- Calibrate step before rejection [#4800](https://github.com/paritytech/parity/pull/4800)
|
||||
- Add replay protection [#4808](https://github.com/paritytech/parity/pull/4808)
|
||||
- Better icon on windows [#4804](https://github.com/paritytech/parity/pull/4804)
|
||||
- Better logic for contract deployments detection [#4821](https://github.com/paritytech/parity/pull/4821)
|
||||
- Fix wrong default values for contract queries inputs [#4819](https://github.com/paritytech/parity/pull/4819)
|
||||
- Adjust selection colours/display [#4811](https://github.com/paritytech/parity/pull/4811)
|
||||
- Update the Wallet Library Registry key [#4817](https://github.com/paritytech/parity/pull/4817)
|
||||
- Update Wallet to new Wallet Code [#4805](https://github.com/paritytech/parity/pull/4805)
|
||||
|
||||
## Parity [v1.6.10](https://github.com/paritytech/parity/releases/tag/v1.6.10) (2017-07-25)
|
||||
|
||||
This is a hotfix release for the stable channel addressing the recent [multi-signature wallet vulnerability](https://blog.parity.io/security-alert-high-2/). Note, upgrading is not mandatory, and all future multi-sig wallets created by any version of Parity are secure.
|
||||
|
||||
All Changes:
|
||||
|
||||
- Backports for stable [#6116](https://github.com/paritytech/parity/pull/6116)
|
||||
- Remove chunk to restore from pending set only upon successful import [#6112](https://github.com/paritytech/parity/pull/6112)
|
||||
- Blacklist bad snapshot manifest hashes upon failure [#5874](https://github.com/paritytech/parity/pull/5874)
|
||||
- Bump snap version and tweak importing detection logic [#6079](https://github.com/paritytech/parity/pull/6079) (modified to work)
|
||||
- Fix docker build for stable [#6118](https://github.com/paritytech/parity/pull/6118)
|
||||
- Update wallet library binaries [#6108](https://github.com/paritytech/parity/pull/6108)
|
||||
- Backported wallet fix [#6104](https://github.com/paritytech/parity/pull/6104)
|
||||
- Fix initialisation bug. ([#6102](https://github.com/paritytech/parity/pull/6102))
|
||||
- Update wallet library modifiers ([#6103](https://github.com/paritytech/parity/pull/6103))
|
||||
- Bump to v1.6.10
|
||||
|
||||
## Parity [v1.6.9](https://github.com/paritytech/parity/releases/tag/v1.6.9) (2017-07-16)
|
||||
|
||||
This is a first stable release of 1.6 series. It contains a number of minor fixes and introduces the `--reseal-on-uncles` option for miners.
|
||||
|
||||
Full list of changes:
|
||||
|
||||
- Backports [#6061](https://github.com/paritytech/parity/pull/6061)
|
||||
- Ethereum Classic Monetary Policy [#5741](https://github.com/paritytech/parity/pull/5741)
|
||||
- Update rewards for uncle miners for ECIP1017
|
||||
- Fix an off-by-one error in ECIP1017 era calculation
|
||||
- `ecip1017_era_rounds` missing from EthashParams when run in build bot
|
||||
- strip out ecip1017_eras_block_reward function and add unit test
|
||||
- JS precompiled set to stable
|
||||
- Backports [#6060](https://github.com/paritytech/parity/pull/6060)
|
||||
- --reseal-on-uncle [#5940](https://github.com/paritytech/parity/pull/5940)
|
||||
- Optimized uncle check
|
||||
- Additional uncle check
|
||||
- Updated comment
|
||||
- Bump to v1.6.9
|
||||
- CLI: Export error message and less verbose peer counter. [#5870](https://github.com/paritytech/parity/pull/5870)
|
||||
- Removed numbed of active connections from informant
|
||||
- Print error message when fatdb is required
|
||||
- Remove peers from UI
|
||||
|
||||
## Parity [v1.6.8](https://github.com/paritytech/parity/releases/tag/v1.6.8) (2017-06-08)
|
||||
|
||||
This release addresses:
|
||||
|
||||
74
Cargo.lock
generated
74
Cargo.lock
generated
@@ -257,7 +257,7 @@ dependencies = [
|
||||
name = "common-types"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ethcore-util 1.7.0",
|
||||
"ethcore-util 1.7.1",
|
||||
"ethjson 0.1.0",
|
||||
"rlp 0.2.0",
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -459,7 +459,7 @@ dependencies = [
|
||||
"ethcore-ipc-nano 1.7.0",
|
||||
"ethcore-logger 1.7.0",
|
||||
"ethcore-stratum 1.7.0",
|
||||
"ethcore-util 1.7.0",
|
||||
"ethcore-util 1.7.1",
|
||||
"ethjson 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
"ethstore 0.1.0",
|
||||
@@ -527,7 +527,7 @@ name = "ethcore-ipc"
|
||||
version = "1.7.0"
|
||||
dependencies = [
|
||||
"ethcore-devtools 1.7.0",
|
||||
"ethcore-util 1.7.0",
|
||||
"ethcore-util 1.7.1",
|
||||
"nanomsg 0.5.1 (git+https://github.com/paritytech/nanomsg.rs.git?branch=parity-1.7)",
|
||||
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -576,7 +576,7 @@ dependencies = [
|
||||
"ethcore-ipc 1.7.0",
|
||||
"ethcore-ipc-codegen 1.7.0",
|
||||
"ethcore-ipc-nano 1.7.0",
|
||||
"ethcore-util 1.7.0",
|
||||
"ethcore-util 1.7.1",
|
||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nanomsg 0.5.1 (git+https://github.com/paritytech/nanomsg.rs.git?branch=parity-1.7)",
|
||||
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -593,7 +593,7 @@ dependencies = [
|
||||
"ethcore-ipc 1.7.0",
|
||||
"ethcore-ipc-codegen 1.7.0",
|
||||
"ethcore-network 1.7.0",
|
||||
"ethcore-util 1.7.0",
|
||||
"ethcore-util 1.7.1",
|
||||
"evm 0.1.0",
|
||||
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -632,7 +632,7 @@ dependencies = [
|
||||
"ethcore-devtools 1.7.0",
|
||||
"ethcore-io 1.7.0",
|
||||
"ethcore-logger 1.7.0",
|
||||
"ethcore-util 1.7.0",
|
||||
"ethcore-util 1.7.1",
|
||||
"ethcrypto 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
"igd 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -663,7 +663,7 @@ dependencies = [
|
||||
"ethcore-ipc-codegen 1.7.0",
|
||||
"ethcore-ipc-nano 1.7.0",
|
||||
"ethcore-logger 1.7.0",
|
||||
"ethcore-util 1.7.0",
|
||||
"ethcore-util 1.7.1",
|
||||
"ethcrypto 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -693,7 +693,7 @@ dependencies = [
|
||||
"ethcore-ipc-codegen 1.7.0",
|
||||
"ethcore-ipc-nano 1.7.0",
|
||||
"ethcore-logger 1.7.0",
|
||||
"ethcore-util 1.7.0",
|
||||
"ethcore-util 1.7.1",
|
||||
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-macros 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
@@ -706,7 +706,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ethcore-util"
|
||||
version = "1.7.0"
|
||||
version = "1.7.1"
|
||||
dependencies = [
|
||||
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -756,7 +756,7 @@ name = "ethjson"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-util 1.7.0",
|
||||
"ethcore-util 1.7.1",
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -837,7 +837,7 @@ dependencies = [
|
||||
"ethcore-ipc-nano 1.7.0",
|
||||
"ethcore-light 1.7.0",
|
||||
"ethcore-network 1.7.0",
|
||||
"ethcore-util 1.7.0",
|
||||
"ethcore-util 1.7.1",
|
||||
"ethkey 0.2.0",
|
||||
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -856,7 +856,7 @@ dependencies = [
|
||||
"bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"common-types 0.1.0",
|
||||
"ethcore-util 1.7.0",
|
||||
"ethcore-util 1.7.1",
|
||||
"ethjson 0.1.0",
|
||||
"evmjit 1.7.0",
|
||||
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -873,7 +873,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore 1.7.0",
|
||||
"ethcore-util 1.7.0",
|
||||
"ethcore-util 1.7.1",
|
||||
"evm 0.1.0",
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1118,7 +1118,7 @@ version = "1.7.0"
|
||||
dependencies = [
|
||||
"ethcore-ipc 1.7.0",
|
||||
"ethcore-ipc-codegen 1.7.0",
|
||||
"ethcore-util 1.7.0",
|
||||
"ethcore-util 1.7.1",
|
||||
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -1148,7 +1148,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
[[package]]
|
||||
name = "jsonrpc-core"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#5e79be8a098cdda221713992f4a46b41a1d4d8f0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed"
|
||||
dependencies = [
|
||||
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1160,7 +1160,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "jsonrpc-http-server"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#5e79be8a098cdda221713992f4a46b41a1d4d8f0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed"
|
||||
dependencies = [
|
||||
"hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)",
|
||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
@@ -1173,7 +1173,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "jsonrpc-ipc-server"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#5e79be8a098cdda221713992f4a46b41a1d4d8f0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed"
|
||||
dependencies = [
|
||||
"bytes 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
@@ -1186,7 +1186,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "jsonrpc-macros"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#5e79be8a098cdda221713992f4a46b41a1d4d8f0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed"
|
||||
dependencies = [
|
||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-pubsub 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
@@ -1196,7 +1196,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "jsonrpc-minihttp-server"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#5e79be8a098cdda221713992f4a46b41a1d4d8f0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed"
|
||||
dependencies = [
|
||||
"bytes 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
@@ -1211,7 +1211,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "jsonrpc-pubsub"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#5e79be8a098cdda221713992f4a46b41a1d4d8f0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed"
|
||||
dependencies = [
|
||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1221,7 +1221,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "jsonrpc-server-utils"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#5e79be8a098cdda221713992f4a46b41a1d4d8f0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed"
|
||||
dependencies = [
|
||||
"bytes 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"globset 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1234,7 +1234,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "jsonrpc-tcp-server"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#5e79be8a098cdda221713992f4a46b41a1d4d8f0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed"
|
||||
dependencies = [
|
||||
"bytes 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
@@ -1248,7 +1248,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "jsonrpc-ws-server"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#5e79be8a098cdda221713992f4a46b41a1d4d8f0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed"
|
||||
dependencies = [
|
||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
@@ -1512,7 +1512,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-util 1.7.0",
|
||||
"ethcore-util 1.7.1",
|
||||
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"native-contract-generator 0.1.0",
|
||||
]
|
||||
@@ -1702,7 +1702,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "parity"
|
||||
version = "1.7.0"
|
||||
version = "1.7.1"
|
||||
dependencies = [
|
||||
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1722,7 +1722,7 @@ dependencies = [
|
||||
"ethcore-logger 1.7.0",
|
||||
"ethcore-secretstore 1.0.0",
|
||||
"ethcore-stratum 1.7.0",
|
||||
"ethcore-util 1.7.0",
|
||||
"ethcore-util 1.7.1",
|
||||
"ethkey 0.2.0",
|
||||
"ethsync 1.7.0",
|
||||
"fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1767,7 +1767,7 @@ dependencies = [
|
||||
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-devtools 1.7.0",
|
||||
"ethcore-util 1.7.0",
|
||||
"ethcore-util 1.7.1",
|
||||
"fetch 0.1.0",
|
||||
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1812,7 +1812,7 @@ name = "parity-hash-fetch"
|
||||
version = "1.7.0"
|
||||
dependencies = [
|
||||
"ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-util 1.7.0",
|
||||
"ethcore-util 1.7.1",
|
||||
"fetch 0.1.0",
|
||||
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1830,7 +1830,7 @@ version = "1.7.0"
|
||||
dependencies = [
|
||||
"cid 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore 1.7.0",
|
||||
"ethcore-util 1.7.0",
|
||||
"ethcore-util 1.7.1",
|
||||
"jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"multihash 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1843,7 +1843,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"ethcore 1.7.0",
|
||||
"ethcore-io 1.7.0",
|
||||
"ethcore-util 1.7.0",
|
||||
"ethcore-util 1.7.1",
|
||||
"ethkey 0.2.0",
|
||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rlp 0.2.0",
|
||||
@@ -1873,7 +1873,7 @@ dependencies = [
|
||||
"ethcore-ipc 1.7.0",
|
||||
"ethcore-light 1.7.0",
|
||||
"ethcore-logger 1.7.0",
|
||||
"ethcore-util 1.7.0",
|
||||
"ethcore-util 1.7.1",
|
||||
"ethcrypto 0.1.0",
|
||||
"ethjson 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
@@ -1914,7 +1914,7 @@ dependencies = [
|
||||
name = "parity-rpc-client"
|
||||
version = "1.4.0"
|
||||
dependencies = [
|
||||
"ethcore-util 1.7.0",
|
||||
"ethcore-util 1.7.1",
|
||||
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-ws-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
@@ -1950,7 +1950,7 @@ name = "parity-ui"
|
||||
version = "1.7.0"
|
||||
dependencies = [
|
||||
"parity-ui-dev 1.7.0",
|
||||
"parity-ui-precompiled 1.4.0 (git+https://github.com/paritytech/js-precompiled.git)",
|
||||
"parity-ui-precompiled 1.4.0 (git+https://github.com/paritytech/js-precompiled.git?branch=beta)",
|
||||
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -1964,7 +1964,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "parity-ui-precompiled"
|
||||
version = "1.4.0"
|
||||
source = "git+https://github.com/paritytech/js-precompiled.git#b49a1d46cc6c545403d18579ef7ae9f9d14eea7e"
|
||||
source = "git+https://github.com/paritytech/js-precompiled.git?branch=beta#79143dc37fee32f2b1d7ca0805540d1de02db7a9"
|
||||
dependencies = [
|
||||
"parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -1977,7 +1977,7 @@ dependencies = [
|
||||
"ethcore 1.7.0",
|
||||
"ethcore-ipc 1.7.0",
|
||||
"ethcore-ipc-codegen 1.7.0",
|
||||
"ethcore-util 1.7.0",
|
||||
"ethcore-util 1.7.1",
|
||||
"ethsync 1.7.0",
|
||||
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ipc-common-types 1.7.0",
|
||||
@@ -2324,7 +2324,7 @@ name = "rpc-cli"
|
||||
version = "1.4.0"
|
||||
dependencies = [
|
||||
"bigint 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-util 1.7.0",
|
||||
"ethcore-util 1.7.1",
|
||||
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-rpc 1.7.0",
|
||||
"parity-rpc-client 1.4.0",
|
||||
@@ -3200,7 +3200,7 @@ dependencies = [
|
||||
"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
|
||||
"checksum parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1d06f6ee0fda786df3784a96ee3f0629f529b91cbfb7d142f6410e6bcd1ce2c"
|
||||
"checksum parity-tokio-ipc 0.1.5 (git+https://github.com/nikvolf/parity-tokio-ipc)" = "<none>"
|
||||
"checksum parity-ui-precompiled 1.4.0 (git+https://github.com/paritytech/js-precompiled.git)" = "<none>"
|
||||
"checksum parity-ui-precompiled 1.4.0 (git+https://github.com/paritytech/js-precompiled.git?branch=beta)" = "<none>"
|
||||
"checksum parity-wasm 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "51104c8b8da5cd0ebe0ab765dfab37bc1927b4a01a3d870b0fe09d9ee65e35ea"
|
||||
"checksum parity-wordlist 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "52142d717754f7ff7ef0fc8da1bdce4f302dd576fb9bf8b727d6a5fdef33348d"
|
||||
"checksum parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aebb68eebde2c99f89592d925288600fde220177e46b5c9a91ca218d245aeedf"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
description = "Parity Ethereum client"
|
||||
name = "parity"
|
||||
version = "1.7.0"
|
||||
version = "1.7.1"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
build = "build.rs"
|
||||
|
||||
52
README.md
52
README.md
@@ -1,59 +1,47 @@
|
||||
# [Parity](https://parity.io/parity.html)
|
||||
### Fast, light, and robust Ethereum implementation
|
||||
# [Parity](https://parity.io/parity.html) - fast, light, and robust Ethereum client
|
||||
|
||||
### [Download latest release](https://github.com/paritytech/parity/releases)
|
||||
[](https://gitlab.parity.io/parity/parity/commits/master)
|
||||
[](https://build.snapcraft.io/user/paritytech/parity)
|
||||
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
|
||||
|
||||
[](https://gitlab.parity.io/parity/parity/commits/master) [![Coverage Status][coveralls-image]][coveralls-url] [![GPLv3][license-image]][license-url]
|
||||
- [Download the latest release here.](https://github.com/paritytech/parity/releases)
|
||||
|
||||
### Join the chat!
|
||||
|
||||
Parity [![Join the chat at https://gitter.im/ethcore/parity][gitter-image]][gitter-url] and
|
||||
parity.js [](https://gitter.im/ethcore/parity.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
[Internal Documentation][doc-url]
|
||||
|
||||
|
||||
Be sure to check out [our wiki][wiki-url] for more information.
|
||||
|
||||
[coveralls-image]: https://coveralls.io/repos/github/paritytech/parity/badge.svg?branch=master
|
||||
[coveralls-url]: https://coveralls.io/github/paritytech/parity?branch=master
|
||||
[gitter-image]: https://badges.gitter.im/Join%20Chat.svg
|
||||
[gitter-url]: https://gitter.im/ethcore/parity?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
||||
[license-image]: https://img.shields.io/badge/license-GPL%20v3-green.svg
|
||||
[license-url]: https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
[doc-url]: https://paritytech.github.io/parity/ethcore/index.html
|
||||
[wiki-url]: https://github.com/paritytech/parity/wiki
|
||||
Get in touch with us on Gitter:
|
||||
[](https://gitter.im/paritytech/parity)
|
||||
[](https://gitter.im/paritytech/parity.js)
|
||||
[](https://gitter.im/paritytech/parity/miners)
|
||||
[](https://gitter.im/paritytech/parity-poa)
|
||||
|
||||
Be sure to check out [our wiki](https://github.com/paritytech/parity/wiki) and the [internal documentation](https://paritytech.github.io/parity/ethcore/index.html) for more information.
|
||||
|
||||
----
|
||||
|
||||
|
||||
## About Parity
|
||||
|
||||
Parity's goal is to be the fastest, lightest, and most secure Ethereum client. We are developing Parity using the sophisticated and
|
||||
cutting-edge Rust programming language. Parity is licensed under the GPLv3, and can be used for all your Ethereum needs.
|
||||
Parity's goal is to be the fastest, lightest, and most secure Ethereum client. We are developing Parity using the sophisticated and cutting-edge Rust programming language. Parity is licensed under the GPLv3, and can be used for all your Ethereum needs.
|
||||
|
||||
Parity comes with a built-in wallet. To access [Parity Wallet](http://web3.site/) simply go to http://web3.site/ (if you don't have access to the internet, but still want to use the service, you can also use http://127.0.0.1:8180/). It includes various functionality allowing you to:
|
||||
|
||||
Parity comes with a built-in wallet. To access [Parity Wallet](http://web3.site/) simply go to http://web3.site/ (if you don't have access to the internet, but still want to use the service, you can also use http://127.0.0.1:8180/). It
|
||||
includes various functionality allowing you to:
|
||||
- create and manage your Ethereum accounts;
|
||||
- manage your Ether and any Ethereum tokens;
|
||||
- create and register your own tokens;
|
||||
- and much more.
|
||||
|
||||
By default, Parity will also run a JSONRPC server on `127.0.0.1:8545`. This is fully configurable and supports a number
|
||||
of RPC APIs.
|
||||
By default, Parity will also run a JSONRPC server on `127.0.0.1:8545`. This is fully configurable and supports a number of RPC APIs.
|
||||
|
||||
If you run into an issue while using parity, feel free to file one in this repository
|
||||
or hop on our [gitter chat room][gitter-url] to ask a question. We are glad to help!
|
||||
If you run into an issue while using parity, feel free to file one in this repository or hop on our [gitter chat room](https://gitter.im/paritytech/parity) to ask a question. We are glad to help!
|
||||
|
||||
Parity's current release is 1.6. You can download it at https://github.com/paritytech/parity/releases or follow the instructions
|
||||
below to build from source.
|
||||
**For security-critical issues**, please refer to the security policy outlined in `SECURITY.MD`.
|
||||
|
||||
Parity's current beta-release is 1.7. You can download it at https://github.com/paritytech/parity/releases or follow the instructions below to build from source.
|
||||
|
||||
----
|
||||
|
||||
## Build dependencies
|
||||
|
||||
**Parity requires Rust version 1.18.0 to build**
|
||||
**Parity 1.7.x-beta requires Rust version 1.19.0 to build**.
|
||||
|
||||
We recommend installing Rust through [rustup](https://www.rustup.rs/). If you don't already have rustup, you can install it like this:
|
||||
|
||||
|
||||
54
SECURITY.md
Normal file
54
SECURITY.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# Security Policy
|
||||
|
||||
For security inquiries or vulnerability reports, please send a message to security@parity.io.
|
||||
|
||||
Please use a descriptive subject line so we can identify the report as such.
|
||||
|
||||
If you send a report, we will respond to the e-mail within 48 hours, and provide regular updates from that time onwards.
|
||||
|
||||
If you would like to encrypt your report, please use the PGP key provided below.
|
||||
It is also reproduced [on MIT's key server](https://pgp.mit.edu/pks/lookup?op=get&search=0x5D0F03018D07DE73)
|
||||
|
||||
```
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQENBFlyIAwBCACe0keNPjgYzZ1Oy/8t3zj/Qw9bHHqrzx7FWy8NbXnYBM19NqOZ
|
||||
DIP7Oe0DvCaf/uruBskCS0iVstHlEFQ2AYe0Ei0REt9lQdy61GylU/DEB3879IG+
|
||||
6FO0SnFeYeerv1/hFI2K6uv8v7PyyVDiiJSW0I1KIs2OBwJicTKmWxLAeQsRgx9G
|
||||
yRGalrVk4KP+6pWTA7k3DxmDZKZyfYV/Ej10NtuzmsemwDbv98HKeomp/kgFOfSy
|
||||
3AZjeCpctlsNqpjUuXa0/HudmH2WLxZ0fz8XeoRh8XM9UudNIecjrDqmAFrt/btQ
|
||||
/3guvlzhFCdhYPVGsUusKMECk/JG+Xx1/1ZjABEBAAG0LFBhcml0eSBTZWN1cml0
|
||||
eSBDb250YWN0IDxzZWN1cml0eUBwYXJpdHkuaW8+iQFUBBMBCAA+FiEE2uUVYCjP
|
||||
N6B8aTiDXQ8DAY0H3nMFAllyIAwCGwMFCQPCZwAFCwkIBwIGFQgJCgsCBBYCAwEC
|
||||
HgECF4AACgkQXQ8DAY0H3nM60wgAkS3A36Zc+upiaxU7tumcGv+an17j7gin0sif
|
||||
+0ELSjVfrXInM6ovai+NhUdcLkJ7tCrKS90fvlaELK5Sg9CXBWCTFccKN4A/B7ey
|
||||
rOg2NPXUecnyBB/XqQgKYH7ujYlOlqBDXMfz6z8Hj6WToxg9PPMGGomyMGh8AWxM
|
||||
3yRPFs5RKt0VKgN++5N00oly5Y8ri5pgCidDvCLYMGTVDHFKwkuc9w6BlWlu1R1e
|
||||
/hXFWUFAP1ffTAul3QwyKhjPn2iotCdxXjvt48KaU8DN4iL7aMBN/ZBKqGS7yRdF
|
||||
D/JbJyaaJ0ZRvFSTSXy/sWY3z1B5mtCPBxco8hqqNfRkCwuZ6LkBDQRZciAMAQgA
|
||||
8BP8xrwe12TOUTqL/Vrbxv/FLdhKh53J6TrPKvC2TEEKOrTNo5ahRq+XOS5E7G2N
|
||||
x3b+fq8gR9BzFcldAx0XWUtGs/Wv++ulaSNqTBxj13J3G3WGsUfMKxRgj//piCUD
|
||||
bCFLQfGZdKk0M1o9QkPVARwwmvCNiNB/l++xGqPtfc44H5jWj3GoGvL2MkShPzrN
|
||||
yN/bJ+m+R5gtFGdInqa5KXBuxxuW25eDKJ+LzjbgUgeC76wNcfOiQHTdMkcupjdO
|
||||
bbGFwo10hcbRAOcZEv6//Zrlmk/6nPxEd2hN20St2bSN0+FqfZ267mWEu3ejsgF8
|
||||
ArdCpv5h4fBvJyNwiTZwIQARAQABiQE8BBgBCAAmFiEE2uUVYCjPN6B8aTiDXQ8D
|
||||
AY0H3nMFAllyIAwCGwwFCQPCZwAACgkQXQ8DAY0H3nNisggAl4fqhRlA34wIb190
|
||||
sqXHVxiCuzPaqS6krE9xAa1+gncX485OtcJNqnjugHm2rFE48lv7oasviuPXuInE
|
||||
/OgVFnXYv9d/Xx2JUeDs+bFTLouCDRY2Unh7KJZasfqnMcCHWcxHx5FvRNZRssaB
|
||||
WTZVo6sizPurGUtbpYe4/OLFhadBqAE0EUmVRFEUMc1YTnu4eLaRBzoWN4d2UWwi
|
||||
LN25RSrVSke7LTSFbgn9ntQrQ2smXSR+cdNkkfRCjFcpUaecvFl9HwIqoyVbT4Ym
|
||||
0hbpbbX/cJdc91tKa+psa29uMeGL/cgL9fAu19yNFRyOTMxjZnvql1X/WE1pLmoP
|
||||
ETBD1Q==
|
||||
=K9Qw
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
```
|
||||
|
||||
Important Legal Information:
|
||||
|
||||
Your submission might be eligible for a bug bounty. The bug bounty program is an experimental and discretionary rewards program for the Parity community to reward those who are helping to improve the Parity software. Rewards are at the sole discretion of Parity Technologies Ltd..
|
||||
|
||||
We are not able to issue rewards to individuals who are on sanctions lists or who are in countries on sanctions lists (e.g. North Korea, Iran, etc).
|
||||
|
||||
You are responsible for all taxes. All rewards are subject to applicable law.
|
||||
|
||||
Finally, your testing must not violate any law or compromise any data that is not yours.
|
||||
@@ -21,7 +21,7 @@ use hyper::method::Method;
|
||||
use hyper::status::StatusCode;
|
||||
|
||||
use api::{response, types};
|
||||
use api::time::TimeChecker;
|
||||
use api::time::{TimeChecker, MAX_DRIFT};
|
||||
use apps::fetcher::Fetcher;
|
||||
use handlers::{self, extract_url};
|
||||
use endpoint::{Endpoint, Handler, EndpointPath};
|
||||
@@ -122,7 +122,6 @@ impl RestApiRouter {
|
||||
|
||||
// Check time
|
||||
let time = {
|
||||
const MAX_DRIFT: i64 = 500;
|
||||
let (status, message, details) = match time {
|
||||
Ok(Ok(diff)) if diff < MAX_DRIFT && diff > -MAX_DRIFT => {
|
||||
(HealthStatus::Ok, "".into(), diff)
|
||||
|
||||
@@ -33,10 +33,12 @@
|
||||
|
||||
use std::io;
|
||||
use std::{fmt, mem, time};
|
||||
use std::sync::atomic::{self, AtomicUsize};
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use futures::{self, Future, BoxFuture};
|
||||
use futures_cpupool::CpuPool;
|
||||
use futures::future::{self, IntoFuture};
|
||||
use futures_cpupool::{CpuPool, CpuFuture};
|
||||
use ntp;
|
||||
use time::{Duration, Timespec};
|
||||
use util::{Arc, RwLock};
|
||||
@@ -44,6 +46,8 @@ use util::{Arc, RwLock};
|
||||
/// Time checker error.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Error {
|
||||
/// No servers are currently available for a query.
|
||||
NoServersAvailable,
|
||||
/// There was an error when trying to reach the NTP server.
|
||||
Ntp(String),
|
||||
/// IO error when reading NTP response.
|
||||
@@ -55,6 +59,7 @@ impl fmt::Display for Error {
|
||||
use self::Error::*;
|
||||
|
||||
match *self {
|
||||
NoServersAvailable => write!(fmt, "No NTP servers available"),
|
||||
Ntp(ref err) => write!(fmt, "NTP error: {}", err),
|
||||
Io(ref err) => write!(fmt, "Connection Error: {}", err),
|
||||
}
|
||||
@@ -71,52 +76,123 @@ impl From<ntp::errors::Error> for Error {
|
||||
|
||||
/// NTP time drift checker.
|
||||
pub trait Ntp {
|
||||
/// Returned Future.
|
||||
type Future: IntoFuture<Item=Duration, Error=Error>;
|
||||
|
||||
/// Returns the current time drift.
|
||||
fn drift(&self) -> BoxFuture<Duration, Error>;
|
||||
fn drift(&self) -> Self::Future;
|
||||
}
|
||||
|
||||
const SERVER_MAX_POLL_INTERVAL_SECS: u64 = 60;
|
||||
#[derive(Debug)]
|
||||
struct Server {
|
||||
pub address: String,
|
||||
next_call: RwLock<time::Instant>,
|
||||
failures: AtomicUsize,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
pub fn is_available(&self) -> bool {
|
||||
*self.next_call.read() < time::Instant::now()
|
||||
}
|
||||
|
||||
pub fn report_success(&self) {
|
||||
self.failures.store(0, atomic::Ordering::SeqCst);
|
||||
self.update_next_call(1)
|
||||
}
|
||||
|
||||
pub fn report_failure(&self) {
|
||||
let errors = self.failures.fetch_add(1, atomic::Ordering::SeqCst);
|
||||
self.update_next_call(1 << errors)
|
||||
}
|
||||
|
||||
fn update_next_call(&self, delay: usize) {
|
||||
*self.next_call.write() = time::Instant::now() + time::Duration::from_secs(delay as u64 * SERVER_MAX_POLL_INTERVAL_SECS);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> From<T> for Server {
|
||||
fn from(t: T) -> Self {
|
||||
Server {
|
||||
address: t.as_ref().to_owned(),
|
||||
next_call: RwLock::new(time::Instant::now()),
|
||||
failures: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// NTP client using the SNTP algorithm for calculating drift.
|
||||
#[derive(Clone)]
|
||||
pub struct SimpleNtp {
|
||||
address: Arc<String>,
|
||||
addresses: Vec<Arc<Server>>,
|
||||
pool: CpuPool,
|
||||
}
|
||||
|
||||
impl fmt::Debug for SimpleNtp {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Ntp {{ address: {} }}", self.address)
|
||||
f
|
||||
.debug_struct("SimpleNtp")
|
||||
.field("addresses", &self.addresses)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl SimpleNtp {
|
||||
fn new(address: &str, pool: CpuPool) -> SimpleNtp {
|
||||
fn new<T: AsRef<str>>(addresses: &[T], pool: CpuPool) -> SimpleNtp {
|
||||
SimpleNtp {
|
||||
address: Arc::new(address.to_owned()),
|
||||
addresses: addresses.iter().map(Server::from).map(Arc::new).collect(),
|
||||
pool: pool,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ntp for SimpleNtp {
|
||||
fn drift(&self) -> BoxFuture<Duration, Error> {
|
||||
let address = self.address.clone();
|
||||
self.pool.spawn_fn(move || {
|
||||
let packet = ntp::request(&*address)?;
|
||||
let dest_time = ::time::now_utc().to_timespec();
|
||||
let orig_time = Timespec::from(packet.orig_time);
|
||||
let recv_time = Timespec::from(packet.recv_time);
|
||||
let transmit_time = Timespec::from(packet.transmit_time);
|
||||
type Future = future::Either<
|
||||
CpuFuture<Duration, Error>,
|
||||
future::FutureResult<Duration, Error>,
|
||||
>;
|
||||
|
||||
let drift = ((recv_time - orig_time) + (transmit_time - dest_time)) / 2;
|
||||
fn drift(&self) -> Self::Future {
|
||||
use self::future::Either::{A, B};
|
||||
|
||||
Ok(drift)
|
||||
}).boxed()
|
||||
let server = self.addresses.iter().find(|server| server.is_available());
|
||||
server.map(|server| {
|
||||
let server = server.clone();
|
||||
A(self.pool.spawn_fn(move || {
|
||||
debug!(target: "dapps", "Fetching time from {}.", server.address);
|
||||
|
||||
match ntp::request(&server.address) {
|
||||
Ok(packet) => {
|
||||
let dest_time = ::time::now_utc().to_timespec();
|
||||
let orig_time = Timespec::from(packet.orig_time);
|
||||
let recv_time = Timespec::from(packet.recv_time);
|
||||
let transmit_time = Timespec::from(packet.transmit_time);
|
||||
|
||||
let drift = ((recv_time - orig_time) + (transmit_time - dest_time)) / 2;
|
||||
|
||||
server.report_success();
|
||||
Ok(drift)
|
||||
},
|
||||
Err(err) => {
|
||||
server.report_failure();
|
||||
Err(err.into())
|
||||
},
|
||||
}
|
||||
}))
|
||||
}).unwrap_or_else(|| B(future::err(Error::NoServersAvailable)))
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE In a positive scenario first results will be seen after:
|
||||
// MAX_RESULTS * UPDATE_TIMEOUT_INCOMPLETE_SECS seconds.
|
||||
const MAX_RESULTS: usize = 4;
|
||||
const UPDATE_TIMEOUT_OK_SECS: u64 = 30;
|
||||
const UPDATE_TIMEOUT_ERR_SECS: u64 = 2;
|
||||
const UPDATE_TIMEOUT_OK_SECS: u64 = 6 * 60 * 60;
|
||||
const UPDATE_TIMEOUT_WARN_SECS: u64 = 15 * 60;
|
||||
const UPDATE_TIMEOUT_ERR_SECS: u64 = 60;
|
||||
const UPDATE_TIMEOUT_INCOMPLETE_SECS: u64 = 10;
|
||||
|
||||
/// Maximal valid time drift.
|
||||
pub const MAX_DRIFT: i64 = 500;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// A time checker.
|
||||
@@ -127,13 +203,13 @@ pub struct TimeChecker<N: Ntp = SimpleNtp> {
|
||||
|
||||
impl TimeChecker<SimpleNtp> {
|
||||
/// Creates new time checker given the NTP server address.
|
||||
pub fn new(ntp_address: String, pool: CpuPool) -> Self {
|
||||
pub fn new<T: AsRef<str>>(ntp_addresses: &[T], pool: CpuPool) -> Self {
|
||||
let last_result = Arc::new(RwLock::new(
|
||||
// Assume everything is ok at the very beginning.
|
||||
(time::Instant::now(), vec![Ok(0)].into())
|
||||
));
|
||||
|
||||
let ntp = SimpleNtp::new(&ntp_address, pool);
|
||||
let ntp = SimpleNtp::new(ntp_addresses, pool);
|
||||
|
||||
TimeChecker {
|
||||
ntp,
|
||||
@@ -142,22 +218,34 @@ impl TimeChecker<SimpleNtp> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Ntp> TimeChecker<N> {
|
||||
impl<N: Ntp> TimeChecker<N> where <N::Future as IntoFuture>::Future: Send + 'static {
|
||||
/// Updates the time
|
||||
pub fn update(&self) -> BoxFuture<i64, Error> {
|
||||
trace!(target: "dapps", "Updating time from NTP.");
|
||||
let last_result = self.last_result.clone();
|
||||
self.ntp.drift().then(move |res| {
|
||||
self.ntp.drift().into_future().then(move |res| {
|
||||
let res = res.map(|d| d.num_milliseconds());
|
||||
|
||||
if let Err(Error::NoServersAvailable) = res {
|
||||
debug!(target: "dapps", "No NTP servers available. Selecting an older result.");
|
||||
return select_result(last_result.read().1.iter());
|
||||
}
|
||||
|
||||
// Update the results.
|
||||
let mut results = mem::replace(&mut last_result.write().1, VecDeque::new());
|
||||
let has_all_results = results.len() >= MAX_RESULTS;
|
||||
let valid_till = time::Instant::now() + time::Duration::from_secs(
|
||||
if res.is_ok() && results.len() == MAX_RESULTS {
|
||||
UPDATE_TIMEOUT_OK_SECS
|
||||
} else {
|
||||
UPDATE_TIMEOUT_ERR_SECS
|
||||
match res {
|
||||
Ok(time) if has_all_results && time < MAX_DRIFT => UPDATE_TIMEOUT_OK_SECS,
|
||||
Ok(_) if has_all_results => UPDATE_TIMEOUT_WARN_SECS,
|
||||
Err(_) if has_all_results => UPDATE_TIMEOUT_ERR_SECS,
|
||||
_ => UPDATE_TIMEOUT_INCOMPLETE_SECS,
|
||||
}
|
||||
);
|
||||
|
||||
trace!(target: "dapps", "New time drift received: {:?}", res);
|
||||
// Push the result.
|
||||
results.push_back(res.map(|d| d.num_milliseconds()));
|
||||
results.push_back(res);
|
||||
while results.len() > MAX_RESULTS {
|
||||
results.pop_front();
|
||||
}
|
||||
@@ -202,7 +290,7 @@ mod tests {
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::time::Instant;
|
||||
use time::Duration;
|
||||
use futures::{self, BoxFuture, Future};
|
||||
use futures::{future, Future};
|
||||
use super::{Ntp, TimeChecker, Error};
|
||||
use util::RwLock;
|
||||
|
||||
@@ -217,15 +305,17 @@ mod tests {
|
||||
}
|
||||
|
||||
impl Ntp for FakeNtp {
|
||||
fn drift(&self) -> BoxFuture<Duration, Error> {
|
||||
type Future = future::FutureResult<Duration, Error>;
|
||||
|
||||
fn drift(&self) -> Self::Future {
|
||||
self.1.set(self.1.get() + 1);
|
||||
futures::future::ok(self.0.borrow_mut().pop().expect("Unexpected call to drift().")).boxed()
|
||||
future::ok(self.0.borrow_mut().pop().expect("Unexpected call to drift()."))
|
||||
}
|
||||
}
|
||||
|
||||
fn time_checker() -> TimeChecker<FakeNtp> {
|
||||
let last_result = Arc::new(RwLock::new(
|
||||
(Instant::now(), vec![Err(Error::Ntp("NTP server unavailable.".into()))].into())
|
||||
(Instant::now(), vec![Err(Error::Ntp("NTP server unavailable".into()))].into())
|
||||
));
|
||||
|
||||
TimeChecker {
|
||||
|
||||
@@ -67,7 +67,20 @@ pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Embedd
|
||||
// Allow fonts from data: and HTTPS.
|
||||
b"font-src 'self' data: https:;".to_vec(),
|
||||
// Allow inline scripts and scripts eval (webpack/jsconsole)
|
||||
b"script-src 'self' 'unsafe-inline' 'unsafe-eval';".to_vec(),
|
||||
{
|
||||
let script_src = embeddable_on.as_ref()
|
||||
.map(|e| e.extra_script_src.iter()
|
||||
.map(|&(ref host, port)| address(host, port))
|
||||
.join(" ")
|
||||
).unwrap_or_default();
|
||||
format!(
|
||||
"script-src 'self' 'unsafe-inline' 'unsafe-eval' {};",
|
||||
script_src
|
||||
).into_bytes()
|
||||
},
|
||||
// Same restrictions as script-src with additional
|
||||
// blob: that is required for camera access (worker)
|
||||
b"worker-src 'self' 'unsafe-inline' 'unsafe-eval' https: blob:;".to_vec(),
|
||||
// Restrict everything else to the same origin.
|
||||
b"default-src 'self';".to_vec(),
|
||||
// Run in sandbox mode (although it's not fully safe since we allow same-origin and script)
|
||||
@@ -87,7 +100,7 @@ pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Embedd
|
||||
.into_iter()
|
||||
.chain(embed.extra_embed_on
|
||||
.iter()
|
||||
.map(|&(ref host, port)| format!("{}:{}", host, port))
|
||||
.map(|&(ref host, port)| address(host, port))
|
||||
);
|
||||
|
||||
let ancestors = if embed.host == "127.0.0.1" {
|
||||
@@ -140,4 +153,3 @@ pub fn convert_uri_to_url(uri: &uri::RequestUri, host: Option<&header::Host>) ->
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ impl Middleware {
|
||||
|
||||
/// Creates new middleware for UI server.
|
||||
pub fn ui<F: Fetch>(
|
||||
ntp_server: &str,
|
||||
ntp_servers: &[String],
|
||||
pool: CpuPool,
|
||||
remote: Remote,
|
||||
dapps_domain: &str,
|
||||
@@ -146,7 +146,7 @@ impl Middleware {
|
||||
).embeddable_on(None).allow_dapps(false));
|
||||
let special = {
|
||||
let mut special = special_endpoints(
|
||||
ntp_server,
|
||||
ntp_servers,
|
||||
pool,
|
||||
content_fetcher.clone(),
|
||||
remote.clone(),
|
||||
@@ -171,11 +171,12 @@ impl Middleware {
|
||||
|
||||
/// Creates new Dapps server middleware.
|
||||
pub fn dapps<F: Fetch>(
|
||||
ntp_server: &str,
|
||||
ntp_servers: &[String],
|
||||
pool: CpuPool,
|
||||
remote: Remote,
|
||||
ui_address: Option<(String, u16)>,
|
||||
extra_embed_on: Vec<(String, u16)>,
|
||||
extra_script_src: Vec<(String, u16)>,
|
||||
dapps_path: PathBuf,
|
||||
extra_dapps: Vec<PathBuf>,
|
||||
dapps_domain: &str,
|
||||
@@ -184,7 +185,7 @@ impl Middleware {
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
fetch: F,
|
||||
) -> Self {
|
||||
let embeddable = as_embeddable(ui_address, extra_embed_on, dapps_domain);
|
||||
let embeddable = as_embeddable(ui_address, extra_embed_on, extra_script_src, dapps_domain);
|
||||
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
|
||||
hash_fetch::urlhint::URLHintContract::new(registrar),
|
||||
sync_status.clone(),
|
||||
@@ -203,7 +204,7 @@ impl Middleware {
|
||||
|
||||
let special = {
|
||||
let mut special = special_endpoints(
|
||||
ntp_server,
|
||||
ntp_servers,
|
||||
pool,
|
||||
content_fetcher.clone(),
|
||||
remote.clone(),
|
||||
@@ -237,8 +238,8 @@ impl http::RequestMiddleware for Middleware {
|
||||
}
|
||||
}
|
||||
|
||||
fn special_endpoints(
|
||||
ntp_server: &str,
|
||||
fn special_endpoints<T: AsRef<str>>(
|
||||
ntp_servers: &[T],
|
||||
pool: CpuPool,
|
||||
content_fetcher: Arc<apps::fetcher::Fetcher>,
|
||||
remote: Remote,
|
||||
@@ -250,7 +251,7 @@ fn special_endpoints(
|
||||
special.insert(router::SpecialEndpoint::Api, Some(api::RestApi::new(
|
||||
content_fetcher,
|
||||
sync_status,
|
||||
api::TimeChecker::new(ntp_server.into(), pool),
|
||||
api::TimeChecker::new(ntp_servers, pool),
|
||||
remote,
|
||||
)));
|
||||
special
|
||||
@@ -263,12 +264,14 @@ fn address(host: &str, port: u16) -> String {
|
||||
fn as_embeddable(
|
||||
ui_address: Option<(String, u16)>,
|
||||
extra_embed_on: Vec<(String, u16)>,
|
||||
extra_script_src: Vec<(String, u16)>,
|
||||
dapps_domain: &str,
|
||||
) -> Option<ParentFrameSettings> {
|
||||
ui_address.map(|(host, port)| ParentFrameSettings {
|
||||
host,
|
||||
port,
|
||||
extra_embed_on,
|
||||
extra_script_src,
|
||||
dapps_domain: dapps_domain.to_owned(),
|
||||
})
|
||||
}
|
||||
@@ -289,8 +292,10 @@ pub struct ParentFrameSettings {
|
||||
pub host: String,
|
||||
/// Port
|
||||
pub port: u16,
|
||||
/// Additional pages the pages can be embedded on.
|
||||
/// Additional URLs the dapps can be embedded on.
|
||||
pub extra_embed_on: Vec<(String, u16)>,
|
||||
/// Additional URLs the dapp scripts can be loaded from.
|
||||
pub extra_script_src: Vec<(String, u16)>,
|
||||
/// Dapps Domain (web3.site)
|
||||
pub dapps_domain: String,
|
||||
}
|
||||
|
||||
@@ -255,11 +255,12 @@ impl Server {
|
||||
fetch: F,
|
||||
) -> Result<Server, http::Error> {
|
||||
let middleware = Middleware::dapps(
|
||||
"pool.ntp.org:123",
|
||||
&["0.pool.ntp.org:123".into(), "1.pool.ntp.org:123".into()],
|
||||
CpuPool::new(4),
|
||||
remote,
|
||||
signer_address,
|
||||
vec![],
|
||||
vec![],
|
||||
dapps_path,
|
||||
extra_dapps,
|
||||
DAPPS_DOMAIN.into(),
|
||||
|
||||
@@ -12,7 +12,7 @@ rustc_version = "0.1"
|
||||
[dependencies]
|
||||
parity-ui-dev = { path = "../../js", optional = true }
|
||||
# This is managed by the js/scripts/release.sh script on CI - keep it in a single line
|
||||
parity-ui-precompiled = { git = "https://github.com/paritytech/js-precompiled.git", optional = true, branch = "master" }
|
||||
parity-ui-precompiled = { git = "https://github.com/paritytech/js-precompiled.git", optional = true, branch = "beta" }
|
||||
|
||||
[features]
|
||||
no-precompiled-js = ["parity-ui-dev"]
|
||||
|
||||
@@ -4,7 +4,7 @@ WORKDIR /build
|
||||
#ENV for build TAG
|
||||
ARG BUILD_TAG
|
||||
ENV BUILD_TAG ${BUILD_TAG:-master}
|
||||
RUN echo $BUILD_TAG
|
||||
RUN echo "Build tag:" $BUILD_TAG
|
||||
# install tools and dependencies
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --force-yes --no-install-recommends \
|
||||
@@ -48,7 +48,7 @@ RUN apt-get update && \
|
||||
# show backtraces
|
||||
RUST_BACKTRACE=1 && \
|
||||
# build parity
|
||||
cd /build&&git clone https://github.com/paritytech/parity && \
|
||||
cd /build&&git clone https://github.com/paritytech/parity && \
|
||||
cd parity && \
|
||||
git pull&& \
|
||||
git checkout $BUILD_TAG && \
|
||||
|
||||
@@ -211,6 +211,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
if (instruction == instructions::DELEGATECALL && !schedule.have_delegate_call) ||
|
||||
(instruction == instructions::CREATE2 && !schedule.have_create2) ||
|
||||
(instruction == instructions::STATICCALL && !schedule.have_static_call) ||
|
||||
((instruction == instructions::RETURNDATACOPY || instruction == instructions::RETURNDATASIZE) && !schedule.have_return_data) ||
|
||||
(instruction == instructions::REVERT && !schedule.have_revert) {
|
||||
|
||||
return Err(evm::Error::BadInstruction {
|
||||
|
||||
@@ -107,6 +107,8 @@ pub struct Schedule {
|
||||
pub blockhash_gas: usize,
|
||||
/// Static Call opcode enabled.
|
||||
pub have_static_call: bool,
|
||||
/// RETURNDATA and RETURNDATASIZE opcodes enabled.
|
||||
pub have_return_data: bool,
|
||||
/// Kill basic accounts below this balance if touched.
|
||||
pub kill_dust: CleanDustMode,
|
||||
}
|
||||
@@ -140,6 +142,7 @@ impl Schedule {
|
||||
have_delegate_call: true,
|
||||
have_create2: false,
|
||||
have_revert: false,
|
||||
have_return_data: false,
|
||||
stack_limit: 1024,
|
||||
max_depth: 1024,
|
||||
tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0],
|
||||
@@ -190,6 +193,7 @@ impl Schedule {
|
||||
schedule.have_create2 = true;
|
||||
schedule.have_revert = true;
|
||||
schedule.have_static_call = true;
|
||||
schedule.have_return_data = true;
|
||||
schedule.blockhash_gas = 350;
|
||||
schedule
|
||||
}
|
||||
@@ -200,6 +204,7 @@ impl Schedule {
|
||||
have_delegate_call: hdc,
|
||||
have_create2: false,
|
||||
have_revert: false,
|
||||
have_return_data: false,
|
||||
stack_limit: 1024,
|
||||
max_depth: 1024,
|
||||
tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0],
|
||||
|
||||
@@ -405,6 +405,7 @@ impl HeaderChain {
|
||||
|
||||
match id {
|
||||
BlockId::Earliest | BlockId::Number(0) => Some(self.genesis_header.clone()),
|
||||
BlockId::Hash(hash) if hash == self.genesis_hash() => { Some(self.genesis_header.clone()) }
|
||||
BlockId::Hash(hash) => load_from_db(hash),
|
||||
BlockId::Number(num) => {
|
||||
if self.best_block.read().number < num { return None }
|
||||
@@ -781,4 +782,18 @@ mod tests {
|
||||
assert_eq!(chain.block_header(BlockId::Latest).unwrap().number(), 10);
|
||||
assert!(chain.candidates.read().get(&100).is_some())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn genesis_header_available() {
|
||||
let spec = Spec::new_test();
|
||||
let genesis_header = spec.genesis_header();
|
||||
let db = make_db();
|
||||
let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::hours(6))));
|
||||
|
||||
let chain = HeaderChain::new(db.clone(), None, &::rlp::encode(&genesis_header), cache.clone()).unwrap();
|
||||
|
||||
assert!(chain.block_header(BlockId::Earliest).is_some());
|
||||
assert!(chain.block_header(BlockId::Number(0)).is_some());
|
||||
assert!(chain.block_header(BlockId::Hash(genesis_header.hash())).is_some());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,8 @@ pub struct Config {
|
||||
pub db_wal: bool,
|
||||
/// Should it do full verification of blocks?
|
||||
pub verify_full: bool,
|
||||
/// Should it check the seal of blocks?
|
||||
pub check_seal: bool,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@@ -69,6 +71,7 @@ impl Default for Config {
|
||||
db_compaction: CompactionProfile::default(),
|
||||
db_wal: true,
|
||||
verify_full: true,
|
||||
check_seal: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -168,7 +171,7 @@ impl Client {
|
||||
let gh = ::rlp::encode(&spec.genesis_header());
|
||||
|
||||
Ok(Client {
|
||||
queue: HeaderQueue::new(config.queue, spec.engine.clone(), io_channel, true),
|
||||
queue: HeaderQueue::new(config.queue, spec.engine.clone(), io_channel, config.check_seal),
|
||||
engine: spec.engine.clone(),
|
||||
chain: HeaderChain::new(db.clone(), chain_col, &gh, cache)?,
|
||||
report: RwLock::new(ClientReport::default()),
|
||||
@@ -282,6 +285,7 @@ impl Client {
|
||||
let mut good = Vec::new();
|
||||
for verified_header in self.queue.drain(MAX) {
|
||||
let (num, hash) = (verified_header.number(), verified_header.hash());
|
||||
trace!(target: "client", "importing block {}", num);
|
||||
|
||||
if self.verify_full && !self.check_header(&mut bad, &verified_header) {
|
||||
continue
|
||||
@@ -381,13 +385,17 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
// return true if should skip, false otherwise. may push onto bad if
|
||||
// return false if should skip, true otherwise. may push onto bad if
|
||||
// should skip.
|
||||
fn check_header(&self, bad: &mut Vec<H256>, verified_header: &Header) -> bool {
|
||||
let hash = verified_header.hash();
|
||||
let parent_header = match self.chain.block_header(BlockId::Hash(*verified_header.parent_hash())) {
|
||||
Some(header) => header,
|
||||
None => return false, // skip import of block with missing parent.
|
||||
None => {
|
||||
trace!(target: "client", "No parent for block ({}, {})",
|
||||
verified_header.number(), hash);
|
||||
return false // skip import of block with missing parent.
|
||||
}
|
||||
};
|
||||
|
||||
// Verify Block Family
|
||||
|
||||
@@ -806,6 +806,9 @@ impl LightProtocol {
|
||||
trace!(target: "pip", "Connected peer with chain head {:?}", (status.head_hash, status.head_num));
|
||||
|
||||
if (status.network_id, status.genesis_hash) != (self.network_id, self.genesis_hash) {
|
||||
trace!(target: "pip", "peer {} wrong network: network_id is {} vs our {}, gh is {} vs our {}",
|
||||
peer, status.network_id, self.network_id, status.genesis_hash, self.genesis_hash);
|
||||
|
||||
return Err(Error::WrongNetwork);
|
||||
}
|
||||
|
||||
|
||||
@@ -54,13 +54,21 @@ struct Peer {
|
||||
}
|
||||
|
||||
impl Peer {
|
||||
// whether this peer can fulfill the
|
||||
fn can_fulfill(&self, c: &Capabilities) -> bool {
|
||||
let caps = &self.capabilities;
|
||||
// whether this peer can fulfill the necessary capabilities for the given
|
||||
// request.
|
||||
fn can_fulfill(&self, request: &Capabilities) -> bool {
|
||||
let local_caps = &self.capabilities;
|
||||
let can_serve_since = |req, local| {
|
||||
match (req, local) {
|
||||
(Some(request_block), Some(serve_since)) => request_block >= serve_since,
|
||||
(Some(_), None) => false,
|
||||
(None, _) => true,
|
||||
}
|
||||
};
|
||||
|
||||
caps.serve_headers == c.serve_headers &&
|
||||
caps.serve_chain_since >= c.serve_chain_since &&
|
||||
caps.serve_state_since >= c.serve_chain_since
|
||||
local_caps.serve_headers >= request.serve_headers &&
|
||||
can_serve_since(request.serve_chain_since, local_caps.serve_chain_since) &&
|
||||
can_serve_since(request.serve_state_since, local_caps.serve_state_since)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,7 +252,7 @@ impl OnDemand {
|
||||
peers: RwLock::new(HashMap::new()),
|
||||
in_transit: RwLock::new(HashMap::new()),
|
||||
cache: cache,
|
||||
no_immediate_dispatch: true,
|
||||
no_immediate_dispatch: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,7 +274,6 @@ impl OnDemand {
|
||||
-> Result<Receiver<Vec<Response>>, basic_request::NoSuchOutput>
|
||||
{
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
|
||||
if requests.is_empty() {
|
||||
assert!(sender.send(Vec::new()).is_ok(), "receiver still in scope; qed");
|
||||
return Ok(receiver);
|
||||
@@ -335,6 +342,7 @@ impl OnDemand {
|
||||
// dispatch pending requests, and discard those for which the corresponding
|
||||
// receiver has been dropped.
|
||||
fn dispatch_pending(&self, ctx: &BasicContext) {
|
||||
|
||||
// wrapper future for calling `poll_cancel` on our `Senders` to preserve
|
||||
// the invariant that it's always within a task.
|
||||
struct CheckHangup<'a, T: 'a>(&'a mut Sender<T>);
|
||||
@@ -360,6 +368,8 @@ impl OnDemand {
|
||||
if self.pending.read().is_empty() { return }
|
||||
let mut pending = self.pending.write();
|
||||
|
||||
debug!(target: "on_demand", "Attempting to dispatch {} pending requests", pending.len());
|
||||
|
||||
// iterate over all pending requests, and check them for hang-up.
|
||||
// then, try and find a peer who can serve it.
|
||||
let peers = self.peers.read();
|
||||
@@ -378,16 +388,21 @@ impl OnDemand {
|
||||
|
||||
match ctx.request_from(*peer_id, pending.net_requests.clone()) {
|
||||
Ok(req_id) => {
|
||||
trace!(target: "on_demand", "Dispatched request {} to peer {}", req_id, peer_id);
|
||||
self.in_transit.write().insert(req_id, pending);
|
||||
return None
|
||||
}
|
||||
Err(net::Error::NoCredits) => {}
|
||||
Err(net::Error::NoCredits) | Err(net::Error::NotServer) => {}
|
||||
Err(e) => debug!(target: "on_demand", "Error dispatching request to peer: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: maximum number of failures _when we have peers_.
|
||||
Some(pending)
|
||||
})
|
||||
.collect(); // `pending` now contains all requests we couldn't dispatch.
|
||||
|
||||
debug!(target: "on_demand", "Was unable to dispatch {} requests.", pending.len());
|
||||
}
|
||||
|
||||
// submit a pending request set. attempts to answer from cache before
|
||||
@@ -395,6 +410,7 @@ impl OnDemand {
|
||||
fn submit_pending(&self, ctx: &BasicContext, mut pending: Pending) {
|
||||
// answer as many requests from cache as we can, and schedule for dispatch
|
||||
// if incomplete.
|
||||
|
||||
pending.answer_from_cache(&*self.cache);
|
||||
if let Some(mut pending) = pending.try_complete() {
|
||||
pending.update_net_requests();
|
||||
|
||||
Submodule ethcore/res/ethereum/tests updated: 4e8b9be3fb...ef191fdc61
@@ -705,6 +705,7 @@ impl Engine for AuthorityRound {
|
||||
|
||||
// apply immediate transitions.
|
||||
if let Some(change) = self.validators.is_epoch_end(first, chain_head) {
|
||||
let change = combine_proofs(chain_head.number(), &change, &[]);
|
||||
return Some(change)
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,6 @@ impl Engine for NullEngine {
|
||||
}
|
||||
|
||||
fn snapshot_components(&self) -> Option<Box<::snapshot::SnapshotComponents>> {
|
||||
Some(Box::new(::snapshot::PowSnapshot(10000)))
|
||||
Some(Box::new(::snapshot::PowSnapshot::new(10000, 10000)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,10 @@ pub const PARITY_GAS_LIMIT_DETERMINANT: U256 = U256([37, 0, 0, 0]);
|
||||
|
||||
/// Number of blocks in an ethash snapshot.
|
||||
// make dependent on difficulty incrment divisor?
|
||||
const SNAPSHOT_BLOCKS: u64 = 30000;
|
||||
const SNAPSHOT_BLOCKS: u64 = 5000;
|
||||
/// Maximum number of blocks allowed in an ethash snapshot.
|
||||
const MAX_SNAPSHOT_BLOCKS: u64 = 30000;
|
||||
|
||||
|
||||
/// Ethash params.
|
||||
#[derive(Debug, PartialEq)]
|
||||
@@ -416,7 +419,7 @@ impl Engine for Arc<Ethash> {
|
||||
}
|
||||
|
||||
fn snapshot_components(&self) -> Option<Box<::snapshot::SnapshotComponents>> {
|
||||
Some(Box::new(::snapshot::PowSnapshot(SNAPSHOT_BLOCKS)))
|
||||
Some(Box::new(::snapshot::PowSnapshot::new(SNAPSHOT_BLOCKS, MAX_SNAPSHOT_BLOCKS)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
||||
pub fn transact_virtual(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result<Executed, ExecutionError> {
|
||||
let sender = t.sender();
|
||||
let balance = self.state.balance(&sender)?;
|
||||
let needed_balance = t.value + t.gas * t.gas_price;
|
||||
let needed_balance = t.value.saturating_add(t.gas.saturating_mul(t.gas_price));
|
||||
if balance < needed_balance {
|
||||
// give the sender a sufficient balance
|
||||
self.state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty)?;
|
||||
|
||||
@@ -32,7 +32,6 @@ use util::Mutex;
|
||||
use miner::{self, Miner, MinerService};
|
||||
use client::Client;
|
||||
use block::IsBlock;
|
||||
use std::str::FromStr;
|
||||
use rlp::encode;
|
||||
|
||||
/// Configures stratum server options.
|
||||
@@ -60,7 +59,7 @@ impl SubmitPayload {
|
||||
return Err(PayloadError::ArgumentsAmountUnexpected(payload.len()));
|
||||
}
|
||||
|
||||
let nonce = match H64::from_str(clean_0x(&payload[0])) {
|
||||
let nonce = match clean_0x(&payload[0]).parse::<H64>() {
|
||||
Ok(nonce) => nonce,
|
||||
Err(e) => {
|
||||
warn!(target: "stratum", "submit_work ({}): invalid nonce ({:?})", &payload[0], e);
|
||||
@@ -68,7 +67,7 @@ impl SubmitPayload {
|
||||
}
|
||||
};
|
||||
|
||||
let pow_hash = match H256::from_str(clean_0x(&payload[1])) {
|
||||
let pow_hash = match clean_0x(&payload[1]).parse::<H256>() {
|
||||
Ok(pow_hash) => pow_hash,
|
||||
Err(e) => {
|
||||
warn!(target: "stratum", "submit_work ({}): invalid hash ({:?})", &payload[1], e);
|
||||
@@ -76,7 +75,7 @@ impl SubmitPayload {
|
||||
}
|
||||
};
|
||||
|
||||
let mix_hash = match H256::from_str(clean_0x(&payload[2])) {
|
||||
let mix_hash = match clean_0x(&payload[2]).parse::<H256>() {
|
||||
Ok(mix_hash) => mix_hash,
|
||||
Err(e) => {
|
||||
warn!(target: "stratum", "submit_work ({}): invalid mix-hash ({:?})", &payload[2], e);
|
||||
@@ -133,7 +132,7 @@ impl JobDispatcher for StratumJobDispatcher {
|
||||
|
||||
fn submit(&self, payload: Vec<String>) -> Result<(), StratumServiceError> {
|
||||
let payload = SubmitPayload::from_args(payload).map_err(|e|
|
||||
StratumServiceError::Dispatch(format!("{}", e))
|
||||
StratumServiceError::Dispatch(e.to_string())
|
||||
)?;
|
||||
|
||||
trace!(
|
||||
@@ -144,14 +143,16 @@ impl JobDispatcher for StratumJobDispatcher {
|
||||
payload.mix_hash,
|
||||
);
|
||||
|
||||
self.with_core_void(|client, miner| {
|
||||
self.with_core_result(|client, miner| {
|
||||
let seal = vec![encode(&payload.mix_hash).into_vec(), encode(&payload.nonce).into_vec()];
|
||||
if let Err(e) = miner.submit_seal(&*client, payload.pow_hash, seal) {
|
||||
warn!(target: "stratum", "submit_seal error: {:?}", e);
|
||||
};
|
||||
});
|
||||
|
||||
Ok(())
|
||||
match miner.submit_seal(&*client, payload.pow_hash, seal) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => {
|
||||
warn!(target: "stratum", "submit_seal error: {:?}", e);
|
||||
Err(StratumServiceError::Dispatch(e.to_string()))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,8 +182,11 @@ impl StratumJobDispatcher {
|
||||
self.client.upgrade().and_then(|client| self.miner.upgrade().and_then(|miner| (f)(client, miner)))
|
||||
}
|
||||
|
||||
fn with_core_void<F>(&self, f: F) where F: Fn(Arc<Client>, Arc<Miner>) {
|
||||
self.client.upgrade().map(|client| self.miner.upgrade().map(|miner| (f)(client, miner)));
|
||||
fn with_core_result<F>(&self, f: F) -> Result<(), StratumServiceError> where F: Fn(Arc<Client>, Arc<Miner>) -> Result<(), StratumServiceError> {
|
||||
match (self.client.upgrade(), self.miner.upgrade()) {
|
||||
(Some(client), Some(miner)) => f(client, miner),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,7 +234,7 @@ impl Stratum {
|
||||
let dispatcher = Arc::new(StratumJobDispatcher::new(miner, client));
|
||||
|
||||
let stratum_svc = StratumService::start(
|
||||
&SocketAddr::new(IpAddr::from_str(&options.listen_addr)?, options.port),
|
||||
&SocketAddr::new(options.listen_addr.parse::<IpAddr>()?, options.port),
|
||||
dispatcher.clone(),
|
||||
options.secret.clone(),
|
||||
)?;
|
||||
|
||||
@@ -37,11 +37,24 @@ use rand::OsRng;
|
||||
/// Snapshot creation and restoration for PoW chains.
|
||||
/// This includes blocks from the head of the chain as a
|
||||
/// loose assurance that the chain is valid.
|
||||
///
|
||||
/// The field is the number of blocks from the head of the chain
|
||||
/// to include in the snapshot.
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub struct PowSnapshot(pub u64);
|
||||
pub struct PowSnapshot {
|
||||
/// Number of blocks from the head of the chain
|
||||
/// to include in the snapshot.
|
||||
pub blocks: u64,
|
||||
/// Number of to allow in the snapshot when restoring.
|
||||
pub max_restore_blocks: u64,
|
||||
}
|
||||
|
||||
impl PowSnapshot {
|
||||
/// Create a new instance.
|
||||
pub fn new(blocks: u64, max_restore_blocks: u64) -> PowSnapshot {
|
||||
PowSnapshot {
|
||||
blocks: blocks,
|
||||
max_restore_blocks: max_restore_blocks,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SnapshotComponents for PowSnapshot {
|
||||
fn chunk_all(
|
||||
@@ -57,7 +70,7 @@ impl SnapshotComponents for PowSnapshot {
|
||||
current_hash: block_at,
|
||||
writer: chunk_sink,
|
||||
preferred_size: preferred_size,
|
||||
}.chunk_all(self.0)
|
||||
}.chunk_all(self.blocks)
|
||||
}
|
||||
|
||||
fn rebuilder(
|
||||
@@ -66,7 +79,7 @@ impl SnapshotComponents for PowSnapshot {
|
||||
db: Arc<KeyValueDB>,
|
||||
manifest: &ManifestData,
|
||||
) -> Result<Box<Rebuilder>, ::error::Error> {
|
||||
PowRebuilder::new(chain, db, manifest, self.0).map(|r| Box::new(r) as Box<_>)
|
||||
PowRebuilder::new(chain, db, manifest, self.max_restore_blocks).map(|r| Box::new(r) as Box<_>)
|
||||
}
|
||||
|
||||
fn min_supported_version(&self) -> u64 { ::snapshot::MIN_SUPPORTED_STATE_CHUNK_VERSION }
|
||||
@@ -218,7 +231,7 @@ impl Rebuilder for PowRebuilder {
|
||||
trace!(target: "snapshot", "restoring block chunk with {} blocks.", item_count - 3);
|
||||
|
||||
if self.fed_blocks + num_blocks > self.snapshot_blocks {
|
||||
return Err(Error::TooManyBlocks(self.snapshot_blocks, self.fed_blocks).into())
|
||||
return Err(Error::TooManyBlocks(self.snapshot_blocks, self.fed_blocks + num_blocks).into())
|
||||
}
|
||||
|
||||
// todo: assert here that these values are consistent with chunks being in order.
|
||||
|
||||
@@ -46,6 +46,9 @@ struct Guard(bool, PathBuf);
|
||||
impl Guard {
|
||||
fn new(path: PathBuf) -> Self { Guard(true, path) }
|
||||
|
||||
#[cfg(test)]
|
||||
fn benign() -> Self { Guard(false, PathBuf::default()) }
|
||||
|
||||
fn disarm(mut self) { self.0 = false }
|
||||
}
|
||||
|
||||
@@ -123,7 +126,7 @@ impl Restoration {
|
||||
|
||||
// feeds a state chunk, aborts early if `flag` becomes false.
|
||||
fn feed_state(&mut self, hash: H256, chunk: &[u8], flag: &AtomicBool) -> Result<(), Error> {
|
||||
if self.state_chunks_left.remove(&hash) {
|
||||
if self.state_chunks_left.contains(&hash) {
|
||||
let len = snappy::decompress_into(chunk, &mut self.snappy_buffer)?;
|
||||
|
||||
self.state.feed(&self.snappy_buffer[..len], flag)?;
|
||||
@@ -131,6 +134,8 @@ impl Restoration {
|
||||
if let Some(ref mut writer) = self.writer.as_mut() {
|
||||
writer.write_state_chunk(hash, chunk)?;
|
||||
}
|
||||
|
||||
self.state_chunks_left.remove(&hash);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -138,13 +143,15 @@ impl Restoration {
|
||||
|
||||
// feeds a block chunk
|
||||
fn feed_blocks(&mut self, hash: H256, chunk: &[u8], engine: &Engine, flag: &AtomicBool) -> Result<(), Error> {
|
||||
if self.block_chunks_left.remove(&hash) {
|
||||
if self.block_chunks_left.contains(&hash) {
|
||||
let len = snappy::decompress_into(chunk, &mut self.snappy_buffer)?;
|
||||
|
||||
self.secondary.feed(&self.snappy_buffer[..len], engine, flag)?;
|
||||
if let Some(ref mut writer) = self.writer.as_mut() {
|
||||
writer.write_block_chunk(hash, chunk)?;
|
||||
}
|
||||
|
||||
self.block_chunks_left.remove(&hash);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -558,9 +565,9 @@ impl SnapshotService for Service {
|
||||
self.reader.read().as_ref().map(|r| r.manifest().clone())
|
||||
}
|
||||
|
||||
fn min_supported_version(&self) -> Option<u64> {
|
||||
fn supported_versions(&self) -> Option<(u64, u64)> {
|
||||
self.engine.snapshot_components()
|
||||
.map(|c| c.min_supported_version())
|
||||
.map(|c| (c.min_supported_version(), c.current_version()))
|
||||
}
|
||||
|
||||
fn chunk(&self, hash: H256) -> Option<Bytes> {
|
||||
@@ -668,4 +675,50 @@ mod tests {
|
||||
service.restore_state_chunk(Default::default(), vec![]);
|
||||
service.restore_block_chunk(Default::default(), vec![]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cannot_finish_with_invalid_chunks() {
|
||||
use util::H256;
|
||||
use util::kvdb::DatabaseConfig;
|
||||
|
||||
let spec = get_test_spec();
|
||||
let dir = RandomTempPath::new();
|
||||
|
||||
let state_hashes: Vec<_> = (0..5).map(|_| H256::random()).collect();
|
||||
let block_hashes: Vec<_> = (0..5).map(|_| H256::random()).collect();
|
||||
let db_config = DatabaseConfig::with_columns(::db::NUM_COLUMNS);
|
||||
let gb = spec.genesis_block();
|
||||
let flag = ::std::sync::atomic::AtomicBool::new(true);
|
||||
|
||||
let params = RestorationParams {
|
||||
manifest: ManifestData {
|
||||
version: 2,
|
||||
state_hashes: state_hashes.clone(),
|
||||
block_hashes: block_hashes.clone(),
|
||||
state_root: H256::default(),
|
||||
block_number: 100000,
|
||||
block_hash: H256::default(),
|
||||
},
|
||||
pruning: Algorithm::Archive,
|
||||
db_path: dir.as_path().to_owned(),
|
||||
db_config: &db_config,
|
||||
writer: None,
|
||||
genesis: &gb,
|
||||
guard: Guard::benign(),
|
||||
engine: &*spec.engine,
|
||||
};
|
||||
|
||||
let mut restoration = Restoration::new(params).unwrap();
|
||||
let definitely_bad_chunk = [1, 2, 3, 4, 5];
|
||||
|
||||
for hash in state_hashes {
|
||||
assert!(restoration.feed_state(hash, &definitely_bad_chunk, &flag).is_err());
|
||||
assert!(!restoration.is_done());
|
||||
}
|
||||
|
||||
for hash in block_hashes {
|
||||
assert!(restoration.feed_blocks(hash, &definitely_bad_chunk, &*spec.engine, &flag).is_err());
|
||||
assert!(!restoration.is_done());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,9 +27,9 @@ pub trait SnapshotService : Sync + Send {
|
||||
/// Query the most recent manifest data.
|
||||
fn manifest(&self) -> Option<ManifestData>;
|
||||
|
||||
/// Get the minimum supported snapshot version number.
|
||||
/// Get the supported range of snapshot version numbers.
|
||||
/// `None` indicates warp sync isn't supported by the consensus engine.
|
||||
fn min_supported_version(&self) -> Option<u64>;
|
||||
fn supported_versions(&self) -> Option<(u64, u64)>;
|
||||
|
||||
/// Get raw chunk for a given hash.
|
||||
fn chunk(&self, hash: H256) -> Option<Bytes>;
|
||||
|
||||
@@ -30,7 +30,7 @@ use util::kvdb::{self, KeyValueDB, DBTransaction};
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
const SNAPSHOT_MODE: ::snapshot::PowSnapshot = ::snapshot::PowSnapshot(30000);
|
||||
const SNAPSHOT_MODE: ::snapshot::PowSnapshot = ::snapshot::PowSnapshot { blocks: 30000, max_restore_blocks: 30000 };
|
||||
|
||||
fn chunk_and_restore(amount: u64) {
|
||||
let mut canon_chain = ChainGenerator::default();
|
||||
|
||||
@@ -100,6 +100,7 @@ impl CommonParams {
|
||||
schedule.have_create2 = block_number >= self.eip86_transition;
|
||||
schedule.have_revert = block_number >= self.eip140_transition;
|
||||
schedule.have_static_call = block_number >= self.eip214_transition;
|
||||
schedule.have_return_data = block_number >= self.eip211_transition;
|
||||
if block_number >= self.eip210_transition {
|
||||
schedule.blockhash_gas = 350;
|
||||
}
|
||||
|
||||
13360
js/package-lock.json
generated
Normal file
13360
js/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -209,7 +209,7 @@
|
||||
"react-intl": "2.1.5",
|
||||
"react-markdown": "2.4.4",
|
||||
"react-portal": "3.0.0",
|
||||
"react-qr-reader": "1.0.3",
|
||||
"react-qr-reader": "1.1.3",
|
||||
"react-redux": "4.4.6",
|
||||
"react-router": "3.0.0",
|
||||
"react-router-redux": "4.0.7",
|
||||
|
||||
510
js/src/contracts/abi/consensys-multisig-wallet.json
Normal file
510
js/src/contracts/abi/consensys-multisig-wallet.json
Normal file
@@ -0,0 +1,510 @@
|
||||
[
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "owners",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "removeOwner",
|
||||
"outputs": [],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "transactionId",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "revokeConfirmation",
|
||||
"outputs": [],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "isOwner",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "confirmations",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "pending",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"name": "executed",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"name": "getTransactionCount",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "count",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "addOwner",
|
||||
"outputs": [],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "transactionId",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "isConfirmed",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "transactionId",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getConfirmationCount",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "count",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "transactions",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "destination",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "value",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "data",
|
||||
"type": "bytes"
|
||||
},
|
||||
{
|
||||
"name": "executed",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "getOwners",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address[]"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "from",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "to",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "pending",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"name": "executed",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"name": "getTransactionIds",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "_transactionIds",
|
||||
"type": "uint256[]"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "transactionId",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getConfirmations",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "_confirmations",
|
||||
"type": "address[]"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "transactionCount",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_required",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "changeRequirement",
|
||||
"outputs": [],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "transactionId",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "confirmTransaction",
|
||||
"outputs": [],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "destination",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "value",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "data",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"name": "submitTransaction",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "transactionId",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "MAX_OWNER_COUNT",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "required",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "newOwner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "replaceOwner",
|
||||
"outputs": [],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "transactionId",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "executeTransaction",
|
||||
"outputs": [],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_owners",
|
||||
"type": "address[]"
|
||||
},
|
||||
{
|
||||
"name": "_required",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"type": "constructor"
|
||||
},
|
||||
{
|
||||
"payable": true,
|
||||
"type": "fallback"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "sender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "transactionId",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "Confirmation",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "sender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "transactionId",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "Revocation",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "transactionId",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "Submission",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "transactionId",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "Execution",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "transactionId",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "ExecutionFailure",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "sender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "value",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "Deposit",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "OwnerAddition",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "OwnerRemoval",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "required",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "RequirementChange",
|
||||
"type": "event"
|
||||
}
|
||||
]
|
||||
@@ -28,4 +28,4 @@ export registry2 from './registry2.json';
|
||||
export signaturereg from './signaturereg.json';
|
||||
export smsverification from './sms-verification.json';
|
||||
export tokenreg from './tokenreg.json';
|
||||
export wallet from './wallet.json';
|
||||
export foundationWallet from './foundation-multisig-wallet.json';
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -104,7 +104,7 @@ contract WalletLibrary is WalletEvents {
|
||||
|
||||
// constructor is given number of sigs required to do protected "onlymanyowners" transactions
|
||||
// as well as the selection of addresses capable of confirming them.
|
||||
function initMultiowned(address[] _owners, uint _required) {
|
||||
function initMultiowned(address[] _owners, uint _required) only_uninitialized {
|
||||
m_numOwners = _owners.length + 1;
|
||||
m_owners[1] = uint(msg.sender);
|
||||
m_ownerIndex[uint(msg.sender)] = 1;
|
||||
@@ -198,7 +198,7 @@ contract WalletLibrary is WalletEvents {
|
||||
}
|
||||
|
||||
// constructor - stores initial daily limit and records the present day's index.
|
||||
function initDaylimit(uint _limit) {
|
||||
function initDaylimit(uint _limit) only_uninitialized {
|
||||
m_dailyLimit = _limit;
|
||||
m_lastDay = today();
|
||||
}
|
||||
@@ -211,9 +211,12 @@ contract WalletLibrary is WalletEvents {
|
||||
m_spentToday = 0;
|
||||
}
|
||||
|
||||
// throw unless the contract is not yet initialized.
|
||||
modifier only_uninitialized { if (m_numOwners > 0) throw; _; }
|
||||
|
||||
// constructor - just pass on the owner array to the multiowned and
|
||||
// the limit to daylimit
|
||||
function initWallet(address[] _owners, uint _required, uint _daylimit) {
|
||||
function initWallet(address[] _owners, uint _required, uint _daylimit) only_uninitialized {
|
||||
initDaylimit(_daylimit);
|
||||
initMultiowned(_owners, _required);
|
||||
}
|
||||
|
||||
@@ -64,14 +64,19 @@ class FakeTransport {
|
||||
|
||||
class FrameSecureApi extends SecureApi {
|
||||
constructor (transport) {
|
||||
super(transport.uiUrl, null, () => {
|
||||
return transport;
|
||||
});
|
||||
super(
|
||||
transport.uiUrl,
|
||||
null,
|
||||
() => transport,
|
||||
() => 'http:'
|
||||
);
|
||||
}
|
||||
|
||||
connect () {
|
||||
// Do nothing - this API does not need connecting
|
||||
this.emit('connecting');
|
||||
// Fetch settings
|
||||
this._fetchSettings();
|
||||
// Fire connected event with some delay.
|
||||
setTimeout(() => {
|
||||
this.emit('connected');
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { eip20, wallet } from '~/contracts/abi';
|
||||
import { eip20, foundationWallet } from '~/contracts/abi';
|
||||
|
||||
const ABI_TYPES = [
|
||||
{
|
||||
@@ -72,7 +72,7 @@ const ABI_TYPES = [
|
||||
),
|
||||
readOnly: true,
|
||||
type: 'multisig',
|
||||
value: JSON.stringify(wallet)
|
||||
value: JSON.stringify(foundationWallet)
|
||||
},
|
||||
{
|
||||
description: (
|
||||
|
||||
@@ -120,7 +120,6 @@ export default class Store {
|
||||
}
|
||||
|
||||
@computed get qrAddressValid () {
|
||||
console.log('qrValid', this.qrAddress, this._api.util.isAddressValid(this.qrAddress));
|
||||
return this._api.util.isAddressValid(this.qrAddress);
|
||||
}
|
||||
|
||||
@@ -191,7 +190,7 @@ export default class Store {
|
||||
}
|
||||
|
||||
// FIXME: Current native signer encoding is not 100% for EIP-55, lowercase for now
|
||||
this.qrAddress = this._api.util
|
||||
this.qrAddress = qrAddress && this._api.util
|
||||
? this._api.util.toChecksumAddress(qrAddress.toLowerCase())
|
||||
: qrAddress;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import Contract from '~/api/contract';
|
||||
import Contracts from '~/contracts';
|
||||
import { wallet as walletAbi } from '~/contracts/abi';
|
||||
import { foundationWallet as walletAbi } from '~/contracts/abi';
|
||||
import { wallet as walletCode, walletLibrary as walletLibraryCode, walletLibraryRegKey, fullWalletCode } from '~/contracts/code/wallet';
|
||||
|
||||
import { validateUint, validateAddress, validateName } from '~/util/validation';
|
||||
@@ -163,11 +163,11 @@ export default class CreateWalletStore {
|
||||
WalletsUtils.fetchOwners(walletContract),
|
||||
WalletsUtils.fetchDailylimit(walletContract)
|
||||
])
|
||||
.then(([ require, owners, dailylimit ]) => {
|
||||
.then(([ require, owners, daylimit ]) => {
|
||||
transaction(() => {
|
||||
this.wallet.owners = owners;
|
||||
this.wallet.required = require.toNumber();
|
||||
this.wallet.dailylimit = dailylimit.limit;
|
||||
this.wallet.daylimit = daylimit.limit;
|
||||
|
||||
this.wallet = this.getWalletWithMeta(this.wallet);
|
||||
});
|
||||
@@ -191,6 +191,8 @@ export default class CreateWalletStore {
|
||||
return null; // exception when registry is not available
|
||||
})
|
||||
.then((address) => {
|
||||
console.warn('WalletLibrary address in registry', address);
|
||||
|
||||
if (!address || /^(0x)?0*$/.test(address)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -18,12 +18,12 @@ import { noop } from 'lodash';
|
||||
import { observable, computed, action, transaction } from 'mobx';
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
import { eip20 as tokenAbi, wallet as walletAbi } from '~/contracts/abi';
|
||||
import { eip20 as tokenAbi } from '~/contracts/abi';
|
||||
import { fromWei } from '~/api/util/wei';
|
||||
import Contract from '~/api/contract';
|
||||
import ERRORS from './errors';
|
||||
import { DEFAULT_GAS, DEFAULT_GASPRICE, MAX_GAS_ESTIMATION } from '~/util/constants';
|
||||
import { DEFAULT_GAS } from '~/util/constants';
|
||||
import { ETH_TOKEN } from '~/util/tokens';
|
||||
import { getTxOptions } from '~/util/tx';
|
||||
import GasPriceStore from '~/ui/GasPriceEditor/store';
|
||||
import { getLogger, LOG_KEYS } from '~/config';
|
||||
|
||||
@@ -92,7 +92,6 @@ export default class TransferStore {
|
||||
|
||||
if (this.isWallet) {
|
||||
this.wallet = props.wallet;
|
||||
this.walletContract = new Contract(this.api, walletAbi);
|
||||
}
|
||||
|
||||
if (senders) {
|
||||
@@ -115,19 +114,13 @@ export default class TransferStore {
|
||||
@computed get isValid () {
|
||||
const detailsValid = !this.recipientError && !this.valueError && !this.totalError && !this.senderError;
|
||||
const extrasValid = !this.gasStore.errorGas && !this.gasStore.errorPrice && !this.gasStore.conditionBlockError && !this.totalError;
|
||||
const verifyValid = !this.passwordError;
|
||||
|
||||
switch (this.stage) {
|
||||
case 0:
|
||||
return detailsValid;
|
||||
|
||||
case 1:
|
||||
return this.extras
|
||||
? extrasValid
|
||||
: verifyValid;
|
||||
|
||||
case 2:
|
||||
return verifyValid;
|
||||
return extrasValid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,16 +256,21 @@ export default class TransferStore {
|
||||
|
||||
if (this.isWallet && !valueError) {
|
||||
const { last, limit, spent } = this.wallet.dailylimit;
|
||||
const remains = fromWei(limit.minus(spent));
|
||||
const today = Math.round(Date.now() / (24 * 3600 * 1000));
|
||||
const isResetable = last.lt(today);
|
||||
|
||||
if ((!isResetable && remains.lt(value)) || fromWei(limit).lt(value)) {
|
||||
// already spent too much today
|
||||
this.walletWarning = WALLET_WARNING_SPENT_TODAY_LIMIT;
|
||||
} else if (this.walletWarning) {
|
||||
// all ok
|
||||
this.walletWarning = null;
|
||||
// Don't show a warning if the limit is 0
|
||||
// (will always need confirmations)
|
||||
if (limit.gt(0)) {
|
||||
const remains = fromWei(limit.minus(spent));
|
||||
const today = Math.round(Date.now() / (24 * 3600 * 1000));
|
||||
const willResetLimit = last.lt(today);
|
||||
|
||||
if ((!willResetLimit && remains.lt(value)) || fromWei(limit).lt(value)) {
|
||||
// already spent too much today
|
||||
this.walletWarning = WALLET_WARNING_SPENT_TODAY_LIMIT;
|
||||
} else if (this.walletWarning) {
|
||||
// all ok
|
||||
this.walletWarning = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,24 +310,16 @@ export default class TransferStore {
|
||||
});
|
||||
}
|
||||
|
||||
getBalance (forceSender = false) {
|
||||
if (this.isWallet && !forceSender) {
|
||||
return this.balance;
|
||||
}
|
||||
|
||||
const balance = this.senders
|
||||
? this.sendersBalances[this.sender]
|
||||
: this.balance;
|
||||
|
||||
return balance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the balance of the selected token
|
||||
* (in WEI for ETH, without formating for other tokens)
|
||||
*/
|
||||
getTokenBalance (token = this.token, forceSender = false) {
|
||||
return new BigNumber(this.balance[token.id] || 0);
|
||||
getTokenBalance (token = this.token, address = this.account.address) {
|
||||
const balance = address === this.account.address
|
||||
? this.balance
|
||||
: this.sendersBalances[address];
|
||||
|
||||
return new BigNumber(balance[token.id] || 0);
|
||||
}
|
||||
|
||||
getTokenValue (token = this.token, value = this.value, inverse = false) {
|
||||
@@ -348,54 +338,30 @@ export default class TransferStore {
|
||||
return _value.mul(token.format);
|
||||
}
|
||||
|
||||
getValues (_gasTotal) {
|
||||
const gasTotal = new BigNumber(_gasTotal || 0);
|
||||
getValue () {
|
||||
const { valueAll, isEth, isWallet } = this;
|
||||
|
||||
log.debug('@getValues', 'gas', gasTotal.toFormat());
|
||||
|
||||
if (!valueAll) {
|
||||
const value = this.getTokenValue();
|
||||
|
||||
// If it's a token or a wallet, eth is the estimated gas,
|
||||
// and value is the user input
|
||||
if (!isEth || isWallet) {
|
||||
return {
|
||||
eth: gasTotal,
|
||||
token: value
|
||||
};
|
||||
}
|
||||
|
||||
// Otherwise, eth is the sum of the gas and the user input
|
||||
const totalEthValue = gasTotal.plus(value);
|
||||
|
||||
return {
|
||||
eth: totalEthValue,
|
||||
token: value
|
||||
};
|
||||
return value;
|
||||
}
|
||||
|
||||
// If it's the total balance that needs to be sent, send the total balance
|
||||
// if it's not a proper ETH transfer
|
||||
const balance = this.getTokenBalance();
|
||||
|
||||
if (!isEth || isWallet) {
|
||||
const tokenBalance = this.getTokenBalance();
|
||||
|
||||
return {
|
||||
eth: gasTotal,
|
||||
token: tokenBalance
|
||||
};
|
||||
return balance;
|
||||
}
|
||||
|
||||
// Otherwise, substract the gas estimate
|
||||
const availableEth = this.getTokenBalance(ETH_TOKEN);
|
||||
const totalEthValue = availableEth.gt(gasTotal)
|
||||
? availableEth.minus(gasTotal)
|
||||
// substract the gas estimate
|
||||
const gasTotal = new BigNumber(this.gasStore.price || 0)
|
||||
.mul(new BigNumber(this.gasStore.gas || 0));
|
||||
|
||||
const totalEthValue = balance.gt(gasTotal)
|
||||
? balance.minus(gasTotal)
|
||||
: new BigNumber(0);
|
||||
|
||||
return {
|
||||
eth: totalEthValue.plus(gasTotal),
|
||||
token: totalEthValue
|
||||
};
|
||||
return totalEthValue;
|
||||
}
|
||||
|
||||
getFormattedTokenValue (tokenValue) {
|
||||
@@ -403,160 +369,125 @@ export default class TransferStore {
|
||||
}
|
||||
|
||||
@action recalculate = (redo = false) => {
|
||||
const { account } = this;
|
||||
const { account, balance } = this;
|
||||
|
||||
if (!account || !this.balance) {
|
||||
if (!account || !balance) {
|
||||
return;
|
||||
}
|
||||
|
||||
const balance = this.getBalance();
|
||||
return this.getTransactionOptions()
|
||||
.then((options) => {
|
||||
const gasTotal = options.gas.mul(options.gasPrice);
|
||||
|
||||
if (!balance) {
|
||||
return;
|
||||
}
|
||||
const tokenValue = this.getValue();
|
||||
const ethValue = options.value.add(gasTotal);
|
||||
|
||||
const gasTotal = new BigNumber(this.gasStore.price || 0).mul(new BigNumber(this.gasStore.gas || 0));
|
||||
const tokenBalance = this.getTokenBalance();
|
||||
const ethBalance = this.getTokenBalance(ETH_TOKEN, options.from);
|
||||
|
||||
const ethBalance = this.getTokenBalance(ETH_TOKEN, true);
|
||||
const tokenBalance = this.getTokenBalance();
|
||||
const { eth, token } = this.getValues(gasTotal);
|
||||
let totalError = null;
|
||||
let valueError = null;
|
||||
|
||||
let totalError = null;
|
||||
let valueError = null;
|
||||
if (tokenValue.gt(tokenBalance)) {
|
||||
valueError = ERRORS.largeAmount;
|
||||
}
|
||||
|
||||
if (eth.gt(ethBalance)) {
|
||||
totalError = ERRORS.largeAmount;
|
||||
}
|
||||
if (ethValue.gt(ethBalance)) {
|
||||
totalError = ERRORS.largeAmount;
|
||||
}
|
||||
|
||||
if (token && token.gt(tokenBalance)) {
|
||||
valueError = ERRORS.largeAmount;
|
||||
}
|
||||
log.debug('@recalculate', {
|
||||
eth: ethValue.toFormat(),
|
||||
token: tokenValue.toFormat(),
|
||||
ethBalance: ethBalance.toFormat(),
|
||||
tokenBalance: tokenBalance.toFormat(),
|
||||
gasTotal: gasTotal.toFormat()
|
||||
});
|
||||
|
||||
log.debug('@recalculate', {
|
||||
eth: eth.toFormat(),
|
||||
token: token.toFormat(),
|
||||
ethBalance: ethBalance.toFormat(),
|
||||
tokenBalance: tokenBalance.toFormat(),
|
||||
gasTotal: gasTotal.toFormat()
|
||||
});
|
||||
transaction(() => {
|
||||
this.totalError = totalError;
|
||||
this.valueError = valueError;
|
||||
this.gasStore.setErrorTotal(totalError);
|
||||
this.gasStore.setEthValue(options.value);
|
||||
|
||||
transaction(() => {
|
||||
this.totalError = totalError;
|
||||
this.valueError = valueError;
|
||||
this.gasStore.setErrorTotal(totalError);
|
||||
this.gasStore.setEthValue(eth.sub(gasTotal));
|
||||
this.total = fromWei(ethValue).toFixed();
|
||||
|
||||
this.total = this.api.util.fromWei(eth).toFixed();
|
||||
const nextValue = this.getFormattedTokenValue(tokenValue);
|
||||
let prevValue;
|
||||
|
||||
const nextValue = this.getFormattedTokenValue(token);
|
||||
let prevValue;
|
||||
try {
|
||||
prevValue = new BigNumber(this.value || 0);
|
||||
} catch (error) {
|
||||
prevValue = new BigNumber(0);
|
||||
}
|
||||
|
||||
try {
|
||||
prevValue = new BigNumber(this.value || 0);
|
||||
} catch (error) {
|
||||
prevValue = new BigNumber(0);
|
||||
}
|
||||
// Change the input only if necessary
|
||||
if (!nextValue.eq(prevValue)) {
|
||||
this.value = nextValue.toString();
|
||||
}
|
||||
|
||||
// Change the input only if necessary
|
||||
if (!nextValue.eq(prevValue)) {
|
||||
this.value = nextValue.toString();
|
||||
}
|
||||
|
||||
// Re Calculate gas once more to be sure
|
||||
if (redo) {
|
||||
return this.recalculateGas(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
send () {
|
||||
const { options, values } = this._getTransferParams();
|
||||
|
||||
log.debug('@send', 'transfer value', options.value && options.value.toFormat());
|
||||
|
||||
return this._getTransferMethod().postTransaction(options, values);
|
||||
}
|
||||
|
||||
_estimateGas (forceToken = false) {
|
||||
const { options, values } = this._getTransferParams(true, forceToken);
|
||||
|
||||
return this._getTransferMethod(true, forceToken).estimateGas(options, values);
|
||||
// Re Calculate gas once more to be sure
|
||||
if (redo) {
|
||||
return this.recalculateGas(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
estimateGas () {
|
||||
return this._estimateGas();
|
||||
return this.getTransactionOptions()
|
||||
.then((options) => {
|
||||
return this.api.eth.estimateGas(options);
|
||||
});
|
||||
}
|
||||
|
||||
_getTransferMethod (gas = false, forceToken = false) {
|
||||
const { isEth, isWallet } = this;
|
||||
send () {
|
||||
return this.getTransactionOptions()
|
||||
.then((options) => {
|
||||
log.debug('@send', 'transfer value', options.value && options.value.toFormat());
|
||||
|
||||
if (isEth && !isWallet && !forceToken) {
|
||||
return gas ? this.api.eth : this.api.parity;
|
||||
}
|
||||
|
||||
if (isWallet && !forceToken) {
|
||||
return this.wallet.instance.execute;
|
||||
}
|
||||
|
||||
return this.tokenContract.at(this.token.address).instance.transfer;
|
||||
return this.api.parity.postTransaction(options);
|
||||
});
|
||||
}
|
||||
|
||||
_getData (gas = false) {
|
||||
const { isEth, isWallet } = this;
|
||||
getTransactionOptions () {
|
||||
const [ func, options, values ] = this._getTransactionArgs();
|
||||
|
||||
if (!isWallet || isEth) {
|
||||
return this.data && this.data.length ? this.data : '';
|
||||
}
|
||||
|
||||
const func = this._getTransferMethod(gas, true);
|
||||
const { options, values } = this._getTransferParams(gas, true);
|
||||
|
||||
return this.tokenContract.at(this.token.address).getCallData(func, options, values);
|
||||
return getTxOptions(this.api, func, options, values)
|
||||
.then((_options) => {
|
||||
delete _options.sender;
|
||||
return _options;
|
||||
});
|
||||
}
|
||||
|
||||
_getTransferParams (gas = false, forceToken = false) {
|
||||
const { isEth, isWallet } = this;
|
||||
|
||||
const to = (isEth && !isWallet) ? this.recipient
|
||||
: (this.isWallet ? this.wallet.address : this.token.address);
|
||||
_getTransactionArgs () {
|
||||
const { isEth } = this;
|
||||
|
||||
const value = this.getValue();
|
||||
const options = this.gasStore.overrideTransaction({
|
||||
from: this.sender || this.account.address,
|
||||
to
|
||||
from: this.account.address,
|
||||
sender: this.sender
|
||||
});
|
||||
|
||||
if (gas) {
|
||||
options.gas = MAX_GAS_ESTIMATION;
|
||||
}
|
||||
|
||||
const gasTotal = new BigNumber(options.gas || DEFAULT_GAS).mul(options.gasPrice || DEFAULT_GASPRICE);
|
||||
const { token } = this.getValues(gasTotal);
|
||||
|
||||
if (isEth && !isWallet && !forceToken) {
|
||||
options.value = token;
|
||||
options.data = this._getData(gas);
|
||||
|
||||
return { options, values: [] };
|
||||
}
|
||||
|
||||
if (isWallet && !forceToken) {
|
||||
const to = isEth ? this.recipient : this.token.address;
|
||||
const value = isEth ? token : new BigNumber(0);
|
||||
|
||||
const values = [
|
||||
to, value,
|
||||
this._getData(gas)
|
||||
];
|
||||
|
||||
return { options, values };
|
||||
// A simple ETH transfer
|
||||
if (isEth) {
|
||||
options.value = value;
|
||||
options.data = this.data || '';
|
||||
options.to = this.recipient;
|
||||
|
||||
return [ null, options ];
|
||||
}
|
||||
|
||||
// A token transfer
|
||||
const tokenContract = this.tokenContract.at(this.token.address);
|
||||
const values = [
|
||||
this.recipient,
|
||||
token.toFixed(0)
|
||||
value
|
||||
];
|
||||
|
||||
return { options, values };
|
||||
options.to = this.token.address;
|
||||
|
||||
return [ tokenContract.instance.transfer, options, values ];
|
||||
}
|
||||
|
||||
_validatePositiveNumber (num) {
|
||||
|
||||
@@ -20,6 +20,7 @@ import BigNumber from 'bignumber.js';
|
||||
|
||||
import { validateUint, validateAddress } from '~/util/validation';
|
||||
import { DEFAULT_GAS, MAX_GAS_ESTIMATION } from '~/util/constants';
|
||||
import WalletsUtils from '~/util/wallets';
|
||||
|
||||
const STEPS = {
|
||||
EDIT: { title: 'wallet settings' },
|
||||
@@ -220,8 +221,6 @@ export default class WalletSettingsStore {
|
||||
this.api = api;
|
||||
this.step = this.stepsKeys[0];
|
||||
|
||||
this.walletInstance = wallet.instance;
|
||||
|
||||
this.initialWallet = {
|
||||
address: wallet.address,
|
||||
owners: wallet.owners,
|
||||
@@ -280,72 +279,43 @@ export default class WalletSettingsStore {
|
||||
|
||||
@action send = () => {
|
||||
const changes = this.changes;
|
||||
const walletInstance = this.walletInstance;
|
||||
|
||||
Promise.all(changes.map((change) => this.sendChange(change, walletInstance)));
|
||||
Promise.all(changes.map((change) => this.sendChange(change)));
|
||||
this.onClose();
|
||||
}
|
||||
|
||||
@action sendChange = (change, walletInstance) => {
|
||||
const { method, values } = this.getChangeMethod(change, walletInstance);
|
||||
@action sendChange = (change) => {
|
||||
const { api, initialWallet } = this;
|
||||
|
||||
const options = {
|
||||
from: this.wallet.sender,
|
||||
to: this.initialWallet.address,
|
||||
gas: MAX_GAS_ESTIMATION
|
||||
};
|
||||
|
||||
return method
|
||||
.estimateGas(options, values)
|
||||
.then((gasEst) => {
|
||||
let gas = gasEst;
|
||||
|
||||
if (gas.gt(DEFAULT_GAS)) {
|
||||
gas = gas.mul(1.2);
|
||||
WalletsUtils.getChangeMethod(api, initialWallet.address, change)
|
||||
.then((changeMethod) => {
|
||||
if (!changeMethod) {
|
||||
return;
|
||||
}
|
||||
options.gas = gas;
|
||||
|
||||
return method.postTransaction(options, values);
|
||||
const { method, values } = changeMethod;
|
||||
|
||||
const options = {
|
||||
from: this.wallet.sender,
|
||||
to: initialWallet.address,
|
||||
gas: MAX_GAS_ESTIMATION
|
||||
};
|
||||
|
||||
return method
|
||||
.estimateGas(options, values)
|
||||
.then((gasEst) => {
|
||||
let gas = gasEst;
|
||||
|
||||
if (gas.gt(DEFAULT_GAS)) {
|
||||
gas = gas.mul(1.2);
|
||||
}
|
||||
options.gas = gas;
|
||||
|
||||
return method.postTransaction(options, values);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getChangeMethod = (change, walletInstance) => {
|
||||
if (change.type === 'require') {
|
||||
return {
|
||||
method: walletInstance.changeRequirement,
|
||||
values: [ change.value ]
|
||||
};
|
||||
}
|
||||
|
||||
if (change.type === 'dailylimit') {
|
||||
return {
|
||||
method: walletInstance.setDailyLimit,
|
||||
values: [ change.value ]
|
||||
};
|
||||
}
|
||||
|
||||
if (change.type === 'add_owner') {
|
||||
return {
|
||||
method: walletInstance.addOwner,
|
||||
values: [ change.value ]
|
||||
};
|
||||
}
|
||||
|
||||
if (change.type === 'change_owner') {
|
||||
return {
|
||||
method: walletInstance.changeOwner,
|
||||
values: [ change.value.from, change.value.to ]
|
||||
};
|
||||
}
|
||||
|
||||
if (change.type === 'remove_owner') {
|
||||
return {
|
||||
method: walletInstance.removeOwner,
|
||||
values: [ change.value ]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@action validateWallet = (_wallet) => {
|
||||
const senderValidation = validateAddress(_wallet.sender);
|
||||
const requireValidation = validateUint(_wallet.require);
|
||||
|
||||
@@ -144,34 +144,37 @@ export function updateTokensFilter (_addresses, _tokens, options = {}) {
|
||||
promises.push(api.eth.uninstallFilter(tokensFilter.filterToId));
|
||||
}
|
||||
|
||||
const promise = Promise.all(promises);
|
||||
Promise
|
||||
.all([
|
||||
api.eth.blockNumber()
|
||||
].concat(promises))
|
||||
.then(([ block ]) => {
|
||||
const topicsFrom = [ TRANSFER_SIGNATURE, addresses, null ];
|
||||
const topicsTo = [ TRANSFER_SIGNATURE, null, addresses ];
|
||||
|
||||
const topicsFrom = [ TRANSFER_SIGNATURE, addresses, null ];
|
||||
const topicsTo = [ TRANSFER_SIGNATURE, null, addresses ];
|
||||
const filterOptions = {
|
||||
fromBlock: block,
|
||||
toBlock: 'pending',
|
||||
address: tokenAddresses
|
||||
};
|
||||
|
||||
const filterOptions = {
|
||||
fromBlock: 0,
|
||||
toBlock: 'pending',
|
||||
address: tokenAddresses
|
||||
};
|
||||
const optionsFrom = {
|
||||
...filterOptions,
|
||||
topics: topicsFrom
|
||||
};
|
||||
|
||||
const optionsFrom = {
|
||||
...filterOptions,
|
||||
topics: topicsFrom
|
||||
};
|
||||
const optionsTo = {
|
||||
...filterOptions,
|
||||
topics: topicsTo
|
||||
};
|
||||
|
||||
const optionsTo = {
|
||||
...filterOptions,
|
||||
topics: topicsTo
|
||||
};
|
||||
const newFilters = Promise.all([
|
||||
api.eth.newFilter(optionsFrom),
|
||||
api.eth.newFilter(optionsTo)
|
||||
]);
|
||||
|
||||
const newFilters = Promise.all([
|
||||
api.eth.newFilter(optionsFrom),
|
||||
api.eth.newFilter(optionsTo)
|
||||
]);
|
||||
|
||||
promise
|
||||
.then(() => newFilters)
|
||||
return newFilters;
|
||||
})
|
||||
.then(([ filterFromId, filterToId ]) => {
|
||||
const nextTokensFilter = {
|
||||
filterFromId, filterToId,
|
||||
|
||||
@@ -23,7 +23,7 @@ import { attachWallets } from './walletActions';
|
||||
import Contract from '~/api/contract';
|
||||
import MethodDecodingStore from '~/ui/MethodDecoding/methodDecodingStore';
|
||||
import WalletsUtils from '~/util/wallets';
|
||||
import { wallet as WalletAbi } from '~/contracts/abi';
|
||||
import { foundationWallet as WalletAbi } from '~/contracts/abi';
|
||||
|
||||
export function personalAccountsInfo (accountsInfo) {
|
||||
const accounts = {};
|
||||
|
||||
@@ -227,11 +227,12 @@ export default class Status {
|
||||
}
|
||||
|
||||
_overallStatus = (health) => {
|
||||
const all = [health.peers, health.sync, health.time].filter(x => x);
|
||||
const allWithTime = [health.peers, health.sync, health.time].filter(x => x);
|
||||
const all = [health.peers, health.sync].filter(x => x);
|
||||
const statuses = all.map(x => x.status);
|
||||
const bad = statuses.find(x => x === STATUS_BAD);
|
||||
const needsAttention = statuses.find(x => x === STATUS_WARN);
|
||||
const message = all.map(x => x.message).filter(x => x);
|
||||
const message = allWithTime.map(x => x.message).filter(x => x);
|
||||
|
||||
if (all.length) {
|
||||
return {
|
||||
|
||||
@@ -35,7 +35,7 @@ const initialState = {
|
||||
status: DEFAULT_STATUS
|
||||
},
|
||||
overall: {
|
||||
isReady: false,
|
||||
isNotReady: true,
|
||||
status: DEFAULT_STATUS,
|
||||
message: []
|
||||
}
|
||||
|
||||
@@ -17,19 +17,17 @@
|
||||
import { isEqual, uniq } from 'lodash';
|
||||
|
||||
import Contract from '~/api/contract';
|
||||
import { bytesToHex, toHex } from '~/api/util/format';
|
||||
import { ERROR_CODES } from '~/api/transport/error';
|
||||
import { wallet as WALLET_ABI } from '~/contracts/abi';
|
||||
import { MAX_GAS_ESTIMATION } from '~/util/constants';
|
||||
import { foundationWallet as WALLET_ABI } from '~/contracts/abi';
|
||||
import WalletsUtils from '~/util/wallets';
|
||||
|
||||
import { newError } from '~/ui/Errors/actions';
|
||||
|
||||
const UPDATE_OWNERS = 'owners';
|
||||
const UPDATE_REQUIRE = 'require';
|
||||
const UPDATE_DAILYLIMIT = 'dailylimit';
|
||||
const UPDATE_TRANSACTIONS = 'transactions';
|
||||
const UPDATE_CONFIRMATIONS = 'confirmations';
|
||||
import {
|
||||
UPDATE_OWNERS,
|
||||
UPDATE_REQUIRE,
|
||||
UPDATE_DAILYLIMIT,
|
||||
UPDATE_TRANSACTIONS,
|
||||
UPDATE_CONFIRMATIONS
|
||||
} from '~/util/wallets/updates';
|
||||
|
||||
export function confirmOperation (address, owner, operation) {
|
||||
return modifyOperation('confirm', address, owner, operation);
|
||||
@@ -39,41 +37,25 @@ export function revokeOperation (address, owner, operation) {
|
||||
return modifyOperation('revoke', address, owner, operation);
|
||||
}
|
||||
|
||||
function modifyOperation (method, address, owner, operation) {
|
||||
function modifyOperation (modification, address, owner, operation) {
|
||||
return (dispatch, getState) => {
|
||||
const { api } = getState();
|
||||
const contract = new Contract(api, WALLET_ABI).at(address);
|
||||
|
||||
const options = {
|
||||
from: owner,
|
||||
gas: MAX_GAS_ESTIMATION
|
||||
};
|
||||
|
||||
const values = [ operation ];
|
||||
|
||||
dispatch(setOperationPendingState(address, operation, true));
|
||||
|
||||
contract.instance[method]
|
||||
.estimateGas(options, values)
|
||||
.then((gas) => {
|
||||
options.gas = gas.mul(1.2);
|
||||
return contract.instance[method].postTransaction(options, values);
|
||||
})
|
||||
WalletsUtils.postModifyOperation(api, address, modification, owner, operation)
|
||||
.then((requestId) => {
|
||||
return api
|
||||
.pollMethod('parity_checkRequest', requestId)
|
||||
.catch((e) => {
|
||||
dispatch(setOperationPendingState(address, operation, false));
|
||||
if (e.code === ERROR_CODES.REQUEST_REJECTED) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw e;
|
||||
});
|
||||
return api.pollMethod('parity_checkRequest', requestId);
|
||||
})
|
||||
.catch((error) => {
|
||||
dispatch(setOperationPendingState(address, operation, false));
|
||||
if (error.code === ERROR_CODES.REQUEST_REJECTED) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(newError(error));
|
||||
})
|
||||
.then(() => {
|
||||
dispatch(setOperationPendingState(address, operation, false));
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -97,14 +79,18 @@ export function attachWallets (_wallets) {
|
||||
return dispatch(updateWallets({ wallets: {}, walletsAddresses: [], filterSubId: null }));
|
||||
}
|
||||
|
||||
const filterOptions = {
|
||||
fromBlock: 0,
|
||||
toBlock: 'latest',
|
||||
address: nextAddresses
|
||||
};
|
||||
|
||||
// Filter the logs from the current block
|
||||
api.eth
|
||||
.newFilter(filterOptions)
|
||||
.blockNumber()
|
||||
.then((block) => {
|
||||
const filterOptions = {
|
||||
fromBlock: block,
|
||||
toBlock: 'latest',
|
||||
address: nextAddresses
|
||||
};
|
||||
|
||||
return api.eth.newFilter(filterOptions);
|
||||
})
|
||||
.then((filterId) => {
|
||||
dispatch(updateWallets({ wallets: _wallets, walletsAddresses: nextAddresses, filterSubId: filterId }));
|
||||
})
|
||||
@@ -142,7 +128,6 @@ export function load (api) {
|
||||
|
||||
api.eth
|
||||
.getFilterChanges(filterSubId)
|
||||
.then((logs) => contract.parseEventLogs(logs))
|
||||
.then((logs) => {
|
||||
parseLogs(logs)(dispatch, getState);
|
||||
})
|
||||
@@ -292,202 +277,57 @@ function fetchWalletDailylimit (contract) {
|
||||
}
|
||||
|
||||
function fetchWalletConfirmations (contract, _operations, _owners = null, _transactions = null, getState) {
|
||||
const walletInstance = contract.instance;
|
||||
|
||||
const wallet = getState().wallet.wallets[contract.address];
|
||||
|
||||
const owners = _owners || (wallet && wallet.owners) || null;
|
||||
const transactions = _transactions || (wallet && wallet.transactions) || null;
|
||||
// Full load if no operations given, or if the one given aren't loaded yet
|
||||
const fullLoad = !Array.isArray(_operations) || _operations
|
||||
.filter((op) => !wallet.confirmations.find((conf) => conf.operation === op))
|
||||
.length > 0;
|
||||
const cache = { owners, transactions };
|
||||
|
||||
let promise;
|
||||
|
||||
if (fullLoad) {
|
||||
promise = walletInstance
|
||||
.ConfirmationNeeded
|
||||
.getAllLogs()
|
||||
.then((logs) => {
|
||||
return logs.map((log) => ({
|
||||
initiator: log.params.initiator.value,
|
||||
to: log.params.to.value,
|
||||
data: log.params.data.value,
|
||||
value: log.params.value.value,
|
||||
operation: bytesToHex(log.params.operation.value),
|
||||
transactionIndex: log.transactionIndex,
|
||||
transactionHash: log.transactionHash,
|
||||
blockNumber: log.blockNumber,
|
||||
confirmedBy: []
|
||||
}));
|
||||
})
|
||||
.then((logs) => {
|
||||
return logs.sort((logA, logB) => {
|
||||
const comp = logA.blockNumber.comparedTo(logB.blockNumber);
|
||||
|
||||
if (comp !== 0) {
|
||||
return comp;
|
||||
}
|
||||
|
||||
return logA.transactionIndex.comparedTo(logB.transactionIndex);
|
||||
});
|
||||
})
|
||||
.then((confirmations) => {
|
||||
if (confirmations.length === 0) {
|
||||
return confirmations;
|
||||
}
|
||||
|
||||
// Only fetch confirmations for operations not
|
||||
// yet confirmed (ie. not yet a transaction)
|
||||
if (transactions) {
|
||||
const operations = transactions
|
||||
.filter((t) => t.operation)
|
||||
.map((t) => t.operation);
|
||||
|
||||
return confirmations.filter((confirmation) => {
|
||||
return !operations.includes(confirmation.operation);
|
||||
});
|
||||
}
|
||||
|
||||
return confirmations;
|
||||
});
|
||||
} else {
|
||||
const { confirmations } = wallet;
|
||||
const nextConfirmations = confirmations
|
||||
.filter((conf) => _operations.includes(conf.operation));
|
||||
|
||||
promise = Promise.resolve(nextConfirmations);
|
||||
}
|
||||
|
||||
return promise
|
||||
return WalletsUtils.fetchPendingTransactions(contract, cache)
|
||||
.then((confirmations) => {
|
||||
if (confirmations.length === 0) {
|
||||
return confirmations;
|
||||
}
|
||||
|
||||
const uniqConfirmations = Object.values(
|
||||
confirmations.reduce((confirmations, confirmation) => {
|
||||
confirmations[confirmation.operation] = confirmation;
|
||||
return confirmations;
|
||||
}, {})
|
||||
);
|
||||
|
||||
const operations = uniqConfirmations.map((conf) => conf.operation);
|
||||
|
||||
return Promise
|
||||
.all(operations.map((op) => fetchOperationConfirmations(contract, op, owners)))
|
||||
.then((confirmedBys) => {
|
||||
uniqConfirmations.forEach((_, index) => {
|
||||
uniqConfirmations[index].confirmedBy = confirmedBys[index];
|
||||
});
|
||||
|
||||
return uniqConfirmations;
|
||||
});
|
||||
})
|
||||
.then((confirmations) => {
|
||||
const prevConfirmations = wallet.confirmations || [];
|
||||
const nextConfirmations = prevConfirmations
|
||||
.filter((conA) => !confirmations.find((conB) => conB.operation === conA.operation))
|
||||
.concat(confirmations)
|
||||
.map((conf) => ({
|
||||
...conf,
|
||||
pending: false
|
||||
}));
|
||||
|
||||
return {
|
||||
key: UPDATE_CONFIRMATIONS,
|
||||
value: nextConfirmations
|
||||
value: confirmations
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function fetchOperationConfirmations (contract, operation, owners = null) {
|
||||
if (!owners) {
|
||||
console.warn('[fetchOperationConfirmations] try to provide the owners for the Wallet', contract.address);
|
||||
}
|
||||
|
||||
const walletInstance = contract.instance;
|
||||
|
||||
const promise = owners
|
||||
? Promise.resolve({ value: owners })
|
||||
: fetchWalletOwners(contract);
|
||||
|
||||
return promise
|
||||
.then((result) => {
|
||||
const owners = result.value;
|
||||
|
||||
return Promise
|
||||
.all(owners.map((owner) => walletInstance.hasConfirmed.call({}, [ operation, owner ])))
|
||||
.then((data) => {
|
||||
return owners.filter((owner, index) => data[index]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function parseLogs (logs) {
|
||||
return (dispatch, getState) => {
|
||||
if (!logs || logs.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const WalletSignatures = WalletsUtils.getWalletSignatures();
|
||||
|
||||
const { api } = getState();
|
||||
const updates = {};
|
||||
|
||||
logs.forEach((log) => {
|
||||
const { address, topics } = log;
|
||||
const eventSignature = toHex(topics[0]);
|
||||
const prev = updates[address] || {
|
||||
[ UPDATE_DAILYLIMIT ]: true,
|
||||
address
|
||||
};
|
||||
const promises = logs.map((log) => {
|
||||
const { address } = log;
|
||||
|
||||
switch (eventSignature) {
|
||||
case WalletSignatures.OwnerChanged:
|
||||
case WalletSignatures.OwnerAdded:
|
||||
case WalletSignatures.OwnerRemoved:
|
||||
updates[address] = {
|
||||
...prev,
|
||||
[ UPDATE_OWNERS ]: true
|
||||
return WalletsUtils.logToUpdate(api, address, log)
|
||||
.then((update) => {
|
||||
const prev = updates[address] || {
|
||||
[ UPDATE_DAILYLIMIT ]: true,
|
||||
address
|
||||
};
|
||||
return;
|
||||
|
||||
case WalletSignatures.RequirementChanged:
|
||||
updates[address] = {
|
||||
...prev,
|
||||
[ UPDATE_REQUIRE ]: true
|
||||
};
|
||||
return;
|
||||
if (update[UPDATE_CONFIRMATIONS]) {
|
||||
const operations = (prev[UPDATE_CONFIRMATIONS] || []).concat(update[UPDATE_CONFIRMATIONS]);
|
||||
|
||||
case WalletSignatures.ConfirmationNeeded:
|
||||
case WalletSignatures.Confirmation:
|
||||
case WalletSignatures.Revoke:
|
||||
const operation = bytesToHex(log.params.operation.value);
|
||||
update[UPDATE_CONFIRMATIONS] = uniq(operations);
|
||||
}
|
||||
|
||||
updates[address] = {
|
||||
...prev,
|
||||
[ UPDATE_CONFIRMATIONS ]: uniq(
|
||||
(prev[UPDATE_CONFIRMATIONS] || []).concat(operation)
|
||||
)
|
||||
...update
|
||||
};
|
||||
|
||||
return;
|
||||
|
||||
case WalletSignatures.Deposit:
|
||||
case WalletSignatures.SingleTransact:
|
||||
case WalletSignatures.MultiTransact:
|
||||
case WalletSignatures.Old.SingleTransact:
|
||||
case WalletSignatures.Old.MultiTransact:
|
||||
updates[address] = {
|
||||
...prev,
|
||||
[ UPDATE_TRANSACTIONS ]: true
|
||||
};
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
fetchWalletsInfo(updates)(dispatch, getState);
|
||||
return Promise.all(promises)
|
||||
.then(() => {
|
||||
fetchWalletsInfo(updates)(dispatch, getState);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -101,13 +101,7 @@ export default class SecureApi extends Api {
|
||||
return 'dapps.parity';
|
||||
}
|
||||
|
||||
const { host } = this._dappsAddress;
|
||||
|
||||
if (!host || host === '0.0.0.0') {
|
||||
return window.location.hostname;
|
||||
}
|
||||
|
||||
return host;
|
||||
return this._dappsAddress.host;
|
||||
}
|
||||
|
||||
get isConnecting () {
|
||||
@@ -173,6 +167,25 @@ export default class SecureApi extends Api {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a wildcard address to `window.location.hostname`;
|
||||
*/
|
||||
_resolveHost (url) {
|
||||
const parts = url ? url.split(':') : [];
|
||||
const port = parts[1];
|
||||
let host = parts[0];
|
||||
|
||||
if (!host) {
|
||||
return host;
|
||||
}
|
||||
|
||||
if (host === '0.0.0.0') {
|
||||
host = window.location.hostname;
|
||||
}
|
||||
|
||||
return port ? `${host}:${port}` : host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Promise that gets resolved with
|
||||
* a boolean: `true` if the node is up, `false`
|
||||
@@ -316,8 +329,8 @@ export default class SecureApi extends Api {
|
||||
this._uiApi.parity.wsUrl()
|
||||
])
|
||||
.then(([dappsUrl, wsUrl]) => {
|
||||
this._dappsUrl = dappsUrl;
|
||||
this._wsUrl = wsUrl;
|
||||
this._dappsUrl = this._resolveHost(dappsUrl);
|
||||
this._wsUrl = this._resolveHost(wsUrl);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -69,14 +69,14 @@ export default class ConfirmDialog extends Component {
|
||||
<Portal
|
||||
buttons={ [
|
||||
<Button
|
||||
disabled={ disabledDeny }
|
||||
disabled={ disabledDeny || busy }
|
||||
icon={ iconDeny || <CancelIcon /> }
|
||||
key='deny'
|
||||
label={ labelDeny || DEFAULT_NO }
|
||||
onClick={ onDeny }
|
||||
/>,
|
||||
<Button
|
||||
disabled={ disabledConfirm }
|
||||
disabled={ disabledConfirm || busy }
|
||||
icon={ iconConfirm || <CheckIcon /> }
|
||||
key='confirm'
|
||||
label={ labelConfirm || DEFAULT_YES }
|
||||
|
||||
@@ -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 BigNumber from 'bignumber.js';
|
||||
|
||||
import WalletsUtils from '~/util/wallets';
|
||||
|
||||
/**
|
||||
@@ -71,11 +73,14 @@ const isValidReceipt = (receipt) => {
|
||||
return receipt && receipt.blockNumber && receipt.blockNumber.gt(0);
|
||||
};
|
||||
|
||||
function getTxArgs (func, options, values = []) {
|
||||
const { contract } = func;
|
||||
const { api } = contract;
|
||||
export function getTxOptions (api, func, _options, values = []) {
|
||||
const options = { ..._options };
|
||||
const address = options.from;
|
||||
|
||||
if (func && func.contract) {
|
||||
options.to = options.to || func.contract.address;
|
||||
}
|
||||
|
||||
if (!address) {
|
||||
return Promise.resolve({ func, options, values });
|
||||
}
|
||||
@@ -87,8 +92,9 @@ function getTxArgs (func, options, values = []) {
|
||||
return { func, options, values };
|
||||
}
|
||||
|
||||
options.data = contract.getCallData(func, options, values);
|
||||
options.to = options.to || contract.address;
|
||||
if (func && func.contract) {
|
||||
options.data = func.contract.getCallData(func, options, values);
|
||||
}
|
||||
|
||||
if (!options.to) {
|
||||
return { func, options, values };
|
||||
@@ -103,24 +109,35 @@ function getTxArgs (func, options, values = []) {
|
||||
|
||||
return callArgs;
|
||||
});
|
||||
})
|
||||
.then(({ func, options, values }) => {
|
||||
if (func) {
|
||||
options.data = func.contract.getCallData(func, options, values);
|
||||
}
|
||||
|
||||
if (!options.value) {
|
||||
options.value = new BigNumber(0);
|
||||
}
|
||||
|
||||
return options;
|
||||
});
|
||||
}
|
||||
|
||||
export function estimateGas (_func, _options, _values = []) {
|
||||
return getTxArgs(_func, _options, _values)
|
||||
.then((callArgs) => {
|
||||
const { func, options, values } = callArgs;
|
||||
const { api } = _func.contract;
|
||||
|
||||
return func._estimateGas(options, values);
|
||||
return getTxOptions(api, _func, _options, _values)
|
||||
.then((options) => {
|
||||
return api.eth.estimateGas(options);
|
||||
});
|
||||
}
|
||||
|
||||
export function postTransaction (_func, _options, _values = []) {
|
||||
return getTxArgs(_func, _options, _values)
|
||||
.then((callArgs) => {
|
||||
const { func, options, values } = callArgs;
|
||||
const { api } = _func.contract;
|
||||
|
||||
return func._postTransaction(options, values);
|
||||
return getTxOptions(api, _func, _options, _values)
|
||||
.then((options) => {
|
||||
return api.parity.postTransaction(options);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -182,42 +199,35 @@ export function deploy (contract, options, values, skipGasEstimate = false) {
|
||||
}
|
||||
|
||||
export function parseTransactionReceipt (api, options, receipt) {
|
||||
const { metadata } = options;
|
||||
const address = options.from;
|
||||
|
||||
if (receipt.gasUsed.eq(options.gas)) {
|
||||
const error = new Error(`Contract not deployed, gasUsed == ${options.gas.toFixed(0)}`);
|
||||
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
const logs = WalletsUtils.parseLogs(api, receipt.logs || []);
|
||||
// If regular contract creation, only validate the contract
|
||||
if (receipt.contractAddress) {
|
||||
return validateContract(api, receipt.contractAddress);
|
||||
}
|
||||
|
||||
const confirmationLog = logs.find((log) => log.event === 'ConfirmationNeeded');
|
||||
const transactionLog = logs.find((log) => log.event === 'SingleTransact');
|
||||
// Otherwise, needs to check for a contract deployment
|
||||
// from a multisig wallet
|
||||
const walletResult = WalletsUtils.parseTransactionLogs(api, options, receipt.logs || []);
|
||||
|
||||
if (!confirmationLog && !transactionLog && !receipt.contractAddress) {
|
||||
if (!walletResult) {
|
||||
const error = new Error('Something went wrong in the contract deployment...');
|
||||
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
// Confirmations are needed from the other owners
|
||||
if (confirmationLog) {
|
||||
const operationHash = api.util.bytesToHex(confirmationLog.params.operation.value);
|
||||
|
||||
// Add the contract to pending contracts
|
||||
WalletsUtils.addPendingContract(address, operationHash, metadata);
|
||||
if (walletResult.pending) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
if (transactionLog) {
|
||||
// Set the contract address in the receipt
|
||||
receipt.contractAddress = transactionLog.params.created.value;
|
||||
}
|
||||
|
||||
const contractAddress = receipt.contractAddress;
|
||||
return validateContract(api, walletResult.contractAddress);
|
||||
}
|
||||
|
||||
function validateContract (api, contractAddress) {
|
||||
return api.eth
|
||||
.getCode(contractAddress)
|
||||
.then((code) => {
|
||||
|
||||
@@ -15,95 +15,96 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { intersection, range, uniq } from 'lodash';
|
||||
import store from 'store';
|
||||
import { intersection } from 'lodash';
|
||||
|
||||
import Abi from '~/abi';
|
||||
import Contract from '~/api/contract';
|
||||
import { bytesToHex, toHex } from '~/api/util/format';
|
||||
import { validateAddress } from '~/util/validation';
|
||||
import WalletAbi from '~/contracts/abi/wallet.json';
|
||||
import OldWalletAbi from '~/contracts/abi/old-wallet.json';
|
||||
import ConsensysWalletUtils from './wallets/consensys-wallet';
|
||||
import FoundationWalletUtils from './wallets/foundation-wallet';
|
||||
|
||||
const LS_PENDING_CONTRACTS_KEY = '_parity::wallets::pendingContracts';
|
||||
const CONSENSYS_WALLET = 'CONSENSYS_WALLET';
|
||||
const FOUNDATION_WALLET = 'FOUNDATION_WALLET';
|
||||
|
||||
const _cachedWalletLookup = {};
|
||||
const _cachedWalletTypes = {};
|
||||
let _cachedAccounts = {};
|
||||
|
||||
const walletAbi = new Abi(WalletAbi);
|
||||
const oldWalletAbi = new Abi(OldWalletAbi);
|
||||
|
||||
const walletEvents = walletAbi.events.reduce((events, event) => {
|
||||
events[event.name] = event;
|
||||
return events;
|
||||
}, {});
|
||||
|
||||
const oldWalletEvents = oldWalletAbi.events.reduce((events, event) => {
|
||||
events[event.name] = event;
|
||||
return events;
|
||||
}, {});
|
||||
|
||||
const WalletSignatures = {
|
||||
OwnerChanged: toHex(walletEvents.OwnerChanged.signature),
|
||||
OwnerAdded: toHex(walletEvents.OwnerAdded.signature),
|
||||
OwnerRemoved: toHex(walletEvents.OwnerRemoved.signature),
|
||||
RequirementChanged: toHex(walletEvents.RequirementChanged.signature),
|
||||
Confirmation: toHex(walletEvents.Confirmation.signature),
|
||||
Revoke: toHex(walletEvents.Revoke.signature),
|
||||
Deposit: toHex(walletEvents.Deposit.signature),
|
||||
SingleTransact: toHex(walletEvents.SingleTransact.signature),
|
||||
MultiTransact: toHex(walletEvents.MultiTransact.signature),
|
||||
ConfirmationNeeded: toHex(walletEvents.ConfirmationNeeded.signature),
|
||||
|
||||
Old: {
|
||||
SingleTransact: toHex(oldWalletEvents.SingleTransact.signature),
|
||||
MultiTransact: toHex(oldWalletEvents.MultiTransact.signature)
|
||||
}
|
||||
};
|
||||
|
||||
export default class WalletsUtils {
|
||||
static getWalletSignatures () {
|
||||
return WalletSignatures;
|
||||
}
|
||||
|
||||
static getPendingContracts () {
|
||||
return store.get(LS_PENDING_CONTRACTS_KEY) || {};
|
||||
}
|
||||
|
||||
static setPendingContracts (contracts = {}) {
|
||||
return store.set(LS_PENDING_CONTRACTS_KEY, contracts);
|
||||
}
|
||||
|
||||
static removePendingContract (operationHash) {
|
||||
const nextContracts = WalletsUtils.getPendingContracts();
|
||||
|
||||
delete nextContracts[operationHash];
|
||||
WalletsUtils.setPendingContracts(nextContracts);
|
||||
}
|
||||
|
||||
static addPendingContract (address, operationHash, metadata) {
|
||||
const nextContracts = {
|
||||
...WalletsUtils.getPendingContracts(),
|
||||
[ operationHash ]: {
|
||||
address,
|
||||
metadata,
|
||||
operationHash
|
||||
}
|
||||
};
|
||||
|
||||
WalletsUtils.setPendingContracts(nextContracts);
|
||||
}
|
||||
|
||||
static cacheAccounts (accounts) {
|
||||
_cachedAccounts = accounts;
|
||||
}
|
||||
|
||||
static getCallArgs (api, options, values = []) {
|
||||
const walletContract = new Contract(api, WalletAbi);
|
||||
const walletAddress = options.from;
|
||||
static delegateCall (api, address, method, args = []) {
|
||||
return WalletsUtils.getWalletType(api, address)
|
||||
.then((walletType) => {
|
||||
if (walletType === CONSENSYS_WALLET) {
|
||||
return ConsensysWalletUtils[method].apply(null, args);
|
||||
}
|
||||
|
||||
return FoundationWalletUtils[method].apply(null, args);
|
||||
});
|
||||
}
|
||||
|
||||
static fetchDailylimit (walletContract) {
|
||||
const { api } = walletContract;
|
||||
|
||||
return WalletsUtils
|
||||
.fetchOwners(walletContract.at(walletAddress))
|
||||
.delegateCall(api, walletContract.address, 'fetchDailylimit', [ walletContract ]);
|
||||
}
|
||||
|
||||
static fetchOwners (walletContract) {
|
||||
const { api } = walletContract;
|
||||
|
||||
return WalletsUtils
|
||||
.delegateCall(api, walletContract.address, 'fetchOwners', [ walletContract ]);
|
||||
}
|
||||
|
||||
static fetchRequire (walletContract) {
|
||||
const { api } = walletContract;
|
||||
|
||||
return WalletsUtils
|
||||
.delegateCall(api, walletContract.address, 'fetchRequire', [ walletContract ]);
|
||||
}
|
||||
|
||||
static fetchPendingTransactions (walletContract, cache) {
|
||||
const { api } = walletContract;
|
||||
|
||||
return WalletsUtils
|
||||
.delegateCall(api, walletContract.address, 'fetchPendingTransactions', [ walletContract, cache ]);
|
||||
}
|
||||
|
||||
static fetchTransactions (walletContract) {
|
||||
const { api } = walletContract;
|
||||
|
||||
return WalletsUtils
|
||||
.delegateCall(api, walletContract.address, 'fetchTransactions', [ walletContract ])
|
||||
.then((transactions) => {
|
||||
return transactions.sort((txA, txB) => {
|
||||
const comp = txB.blockNumber.comparedTo(txA.blockNumber);
|
||||
|
||||
if (comp !== 0) {
|
||||
return comp;
|
||||
}
|
||||
|
||||
return txB.transactionIndex.comparedTo(txA.transactionIndex);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static getCallArgs (api, options, values = []) {
|
||||
const walletAddress = options.from;
|
||||
let walletContract;
|
||||
let submitMethod;
|
||||
|
||||
return Promise
|
||||
.all([
|
||||
WalletsUtils.getWalletContract(api, walletAddress),
|
||||
WalletsUtils.delegateCall(api, walletAddress, 'getSubmitMethod')
|
||||
])
|
||||
.then(([ _walletContract, _submitMethod ]) => {
|
||||
walletContract = _walletContract;
|
||||
submitMethod = _submitMethod;
|
||||
|
||||
return WalletsUtils.fetchOwners(walletContract);
|
||||
})
|
||||
.then((owners) => {
|
||||
const addresses = Object.keys(_cachedAccounts);
|
||||
const ownerAddress = intersection(addresses, owners).pop();
|
||||
@@ -121,12 +122,12 @@ export default class WalletsUtils {
|
||||
const nextValues = [ to, value, data ];
|
||||
const nextOptions = {
|
||||
..._options,
|
||||
from: ownerAddress,
|
||||
from: options.sender || ownerAddress,
|
||||
to: walletAddress,
|
||||
value: new BigNumber(0)
|
||||
};
|
||||
|
||||
const execFunc = walletContract.instance.execute;
|
||||
const execFunc = walletContract.instance[submitMethod];
|
||||
const callArgs = { func: execFunc, options: nextOptions, values: nextValues };
|
||||
|
||||
if (!account.wallet) {
|
||||
@@ -139,6 +140,11 @@ export default class WalletsUtils {
|
||||
});
|
||||
}
|
||||
|
||||
static getChangeMethod (api, address, change) {
|
||||
return WalletsUtils
|
||||
.delegateCall(api, address, 'getChangeMethod', [ api, address, change ]);
|
||||
}
|
||||
|
||||
static getDeployArgs (contract, options, values) {
|
||||
const { api } = contract;
|
||||
const func = contract.constructors[0];
|
||||
@@ -158,10 +164,41 @@ export default class WalletsUtils {
|
||||
});
|
||||
}
|
||||
|
||||
static parseLogs (api, logs = []) {
|
||||
const walletContract = new Contract(api, WalletAbi);
|
||||
static getWalletContract (api, address) {
|
||||
return WalletsUtils
|
||||
.delegateCall(api, address, 'getWalletContract', [ api ])
|
||||
.then((walletContract) => {
|
||||
return walletContract.at(address);
|
||||
});
|
||||
}
|
||||
|
||||
return walletContract.parseEventLogs(logs);
|
||||
static getWalletType (api, address) {
|
||||
if (_cachedWalletTypes[address] === undefined) {
|
||||
_cachedWalletTypes[address] = Promise.resolve(null)
|
||||
.then((result) => {
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return FoundationWalletUtils.isWallet(api, address)
|
||||
.then((isWallet) => isWallet && FOUNDATION_WALLET);
|
||||
})
|
||||
.then((result) => {
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return ConsensysWalletUtils.isWallet(api, address)
|
||||
.then((isWallet) => isWallet && CONSENSYS_WALLET);
|
||||
})
|
||||
.then((result) => {
|
||||
_cachedWalletTypes[address] = result || null;
|
||||
|
||||
return _cachedWalletTypes[address];
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve(_cachedWalletTypes[address]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,20 +212,8 @@ export default class WalletsUtils {
|
||||
}
|
||||
|
||||
if (!_cachedWalletLookup[address]) {
|
||||
const walletContract = new Contract(api, WalletAbi);
|
||||
|
||||
_cachedWalletLookup[address] = walletContract
|
||||
.at(address)
|
||||
.instance
|
||||
.m_numOwners
|
||||
.call()
|
||||
.then((result) => {
|
||||
if (!result || result.equals(0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
_cachedWalletLookup[address] = WalletsUtils.getWalletType(api, address)
|
||||
.then((walletType) => walletType !== null)
|
||||
.then((bool) => {
|
||||
_cachedWalletLookup[address] = Promise.resolve(bool);
|
||||
return bool;
|
||||
@@ -198,207 +223,34 @@ export default class WalletsUtils {
|
||||
return _cachedWalletLookup[address];
|
||||
}
|
||||
|
||||
static fetchRequire (walletContract) {
|
||||
return walletContract.instance.m_required.call();
|
||||
}
|
||||
|
||||
static fetchOwners (walletContract) {
|
||||
const walletInstance = walletContract.instance;
|
||||
|
||||
return walletInstance
|
||||
.m_numOwners.call()
|
||||
.then((mNumOwners) => {
|
||||
const promises = range(mNumOwners.toNumber())
|
||||
.map((idx) => walletInstance.getOwner.call({}, [ idx ]));
|
||||
|
||||
return Promise
|
||||
.all(promises)
|
||||
.then((owners) => {
|
||||
const uniqOwners = uniq(owners);
|
||||
|
||||
// If all owners are the zero account : must be Mist wallet contract
|
||||
if (uniqOwners.length === 1 && /^(0x)?0*$/.test(owners[0])) {
|
||||
return WalletsUtils.fetchMistOwners(walletContract, mNumOwners.toNumber());
|
||||
}
|
||||
|
||||
return owners;
|
||||
})
|
||||
.then((owners) => uniq(owners));
|
||||
});
|
||||
}
|
||||
|
||||
static fetchMistOwners (walletContract, mNumOwners) {
|
||||
const walletAddress = walletContract.address;
|
||||
|
||||
static logToUpdate (api, address, log) {
|
||||
return WalletsUtils
|
||||
.getMistOwnersOffset(walletContract)
|
||||
.then((result) => {
|
||||
if (!result || result.offset === -1) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const owners = [ result.address ];
|
||||
|
||||
if (mNumOwners === 1) {
|
||||
return owners;
|
||||
}
|
||||
|
||||
const initOffset = result.offset + 1;
|
||||
let promise = Promise.resolve();
|
||||
|
||||
range(initOffset, initOffset + mNumOwners - 1).forEach((offset) => {
|
||||
promise = promise
|
||||
.then(() => {
|
||||
return walletContract.api.eth.getStorageAt(walletAddress, offset);
|
||||
})
|
||||
.then((result) => {
|
||||
const resultAddress = '0x' + (result || '').slice(-40);
|
||||
const { address } = validateAddress(resultAddress);
|
||||
|
||||
owners.push(address);
|
||||
});
|
||||
});
|
||||
|
||||
return promise.then(() => owners);
|
||||
});
|
||||
.delegateCall(api, address, 'logToUpdate', [ log ]);
|
||||
}
|
||||
|
||||
static getMistOwnersOffset (walletContract, offset = 3) {
|
||||
return walletContract.api.eth
|
||||
.getStorageAt(walletContract.address, offset)
|
||||
.then((result) => {
|
||||
if (result && !/^(0x)?0*$/.test(result)) {
|
||||
const resultAddress = '0x' + result.slice(-40);
|
||||
const { address, addressError } = validateAddress(resultAddress);
|
||||
|
||||
if (!addressError) {
|
||||
return { offset, address };
|
||||
}
|
||||
}
|
||||
|
||||
if (offset >= 100) {
|
||||
return { offset: -1 };
|
||||
}
|
||||
|
||||
return WalletsUtils.getMistOwnersOffset(walletContract, offset + 1);
|
||||
});
|
||||
static parseTransactionLogs (api, options, rawLogs) {
|
||||
return WalletsUtils
|
||||
.delegateCall(api, options.from, 'parseTransactionLogs', [ api, options, rawLogs ]);
|
||||
}
|
||||
|
||||
static fetchDailylimit (walletContract) {
|
||||
const walletInstance = walletContract.instance;
|
||||
static postModifyOperation (api, walletAddress, modification, owner, operation) {
|
||||
const options = { from: owner };
|
||||
const values = [ operation ];
|
||||
|
||||
return Promise
|
||||
.all([
|
||||
walletInstance.m_dailyLimit.call(),
|
||||
walletInstance.m_spentToday.call(),
|
||||
walletInstance.m_lastDay.call()
|
||||
WalletsUtils
|
||||
.getWalletContract(api, walletAddress),
|
||||
WalletsUtils
|
||||
.delegateCall(api, walletAddress, 'getModifyOperationMethod', [ modification ])
|
||||
])
|
||||
.then(([ limit, spent, last ]) => ({
|
||||
limit, spent, last
|
||||
}));
|
||||
}
|
||||
|
||||
static fetchTransactions (walletContract) {
|
||||
const { api } = walletContract;
|
||||
const pendingContracts = WalletsUtils.getPendingContracts();
|
||||
|
||||
return walletContract
|
||||
.getAllLogs({
|
||||
topics: [ [
|
||||
WalletSignatures.SingleTransact,
|
||||
WalletSignatures.MultiTransact,
|
||||
WalletSignatures.Deposit,
|
||||
WalletSignatures.Old.SingleTransact,
|
||||
WalletSignatures.Old.MultiTransact
|
||||
] ]
|
||||
})
|
||||
.then((logs) => {
|
||||
return logs.sort((logA, logB) => {
|
||||
const comp = logB.blockNumber.comparedTo(logA.blockNumber);
|
||||
|
||||
if (comp !== 0) {
|
||||
return comp;
|
||||
}
|
||||
|
||||
return logB.transactionIndex.comparedTo(logA.transactionIndex);
|
||||
});
|
||||
})
|
||||
.then((logs) => {
|
||||
const transactions = logs.map((log) => {
|
||||
const signature = toHex(log.topics[0]);
|
||||
|
||||
const value = log.params.value.value;
|
||||
const from = signature === WalletSignatures.Deposit
|
||||
? log.params['_from'].value
|
||||
: walletContract.address;
|
||||
|
||||
const to = signature === WalletSignatures.Deposit
|
||||
? walletContract.address
|
||||
: log.params.to.value;
|
||||
|
||||
const transaction = {
|
||||
transactionHash: log.transactionHash,
|
||||
blockNumber: log.blockNumber,
|
||||
from, to, value
|
||||
};
|
||||
|
||||
if (log.params.created && log.params.created.value && !/^(0x)?0*$/.test(log.params.created.value)) {
|
||||
transaction.creates = log.params.created.value;
|
||||
delete transaction.to;
|
||||
}
|
||||
|
||||
if (log.params.operation) {
|
||||
const operation = bytesToHex(log.params.operation.value);
|
||||
|
||||
// Add the pending contract to the contracts
|
||||
if (pendingContracts[operation]) {
|
||||
const { metadata } = pendingContracts[operation];
|
||||
const contractName = metadata.name;
|
||||
|
||||
metadata.blockNumber = log.blockNumber;
|
||||
|
||||
// The contract creation might not be in the same log,
|
||||
// but must be in the same transaction (eg. Contract creation
|
||||
// from Wallet within a Wallet)
|
||||
api.eth
|
||||
.getTransactionReceipt(log.transactionHash)
|
||||
.then((transactionReceipt) => {
|
||||
const transactionLogs = WalletsUtils.parseLogs(api, transactionReceipt.logs);
|
||||
const creationLog = transactionLogs.find((log) => {
|
||||
return log.params.created && !/^(0x)?0*$/.test(log.params.created.value);
|
||||
});
|
||||
|
||||
if (!creationLog) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const contractAddress = creationLog.params.created.value;
|
||||
|
||||
return Promise
|
||||
.all([
|
||||
api.parity.setAccountName(contractAddress, contractName),
|
||||
api.parity.setAccountMeta(contractAddress, metadata)
|
||||
])
|
||||
.then(() => {
|
||||
WalletsUtils.removePendingContract(operation);
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('adding wallet contract', error);
|
||||
});
|
||||
}
|
||||
|
||||
transaction.operation = operation;
|
||||
}
|
||||
|
||||
if (log.params.data) {
|
||||
transaction.data = log.params.data.value;
|
||||
}
|
||||
|
||||
return transaction;
|
||||
});
|
||||
|
||||
return transactions;
|
||||
.then(([ wallet, method ]) => {
|
||||
return wallet.instance[method]
|
||||
.estimateGas(options, values)
|
||||
.then((gas) => {
|
||||
options.gas = gas.mul(1.5);
|
||||
return wallet.instance[method].postTransaction(options, values);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
354
js/src/util/wallets/consensys-wallet.js
Normal file
354
js/src/util/wallets/consensys-wallet.js
Normal file
@@ -0,0 +1,354 @@
|
||||
// Copyright 2015-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/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
import Abi from '~/abi';
|
||||
import Contract from '~/api/contract';
|
||||
import { toHex } from '~/api/util/format';
|
||||
|
||||
import WalletAbi from '~/contracts/abi/consensys-multisig-wallet.json';
|
||||
|
||||
import {
|
||||
UPDATE_OWNERS,
|
||||
UPDATE_REQUIRE,
|
||||
UPDATE_TRANSACTIONS,
|
||||
UPDATE_CONFIRMATIONS
|
||||
} from './updates';
|
||||
|
||||
const WALLET_CONTRACT = new Contract({}, WalletAbi);
|
||||
const WALLET_ABI = new Abi(WalletAbi);
|
||||
|
||||
const walletEvents = WALLET_ABI.events.reduce((events, event) => {
|
||||
events[event.name] = event;
|
||||
return events;
|
||||
}, {});
|
||||
|
||||
const WalletSignatures = {
|
||||
Confirmation: toHex(walletEvents.Confirmation.signature),
|
||||
Revocation: toHex(walletEvents.Revocation.signature),
|
||||
Deposit: toHex(walletEvents.Deposit.signature),
|
||||
Execution: toHex(walletEvents.Execution.signature),
|
||||
OwnerAddition: toHex(walletEvents.OwnerAddition.signature),
|
||||
OwnerRemoval: toHex(walletEvents.OwnerRemoval.signature),
|
||||
RequirementChange: toHex(walletEvents.RequirementChange.signature),
|
||||
Submission: toHex(walletEvents.Submission.signature)
|
||||
};
|
||||
|
||||
export default class ConsensysWalletUtils {
|
||||
static fetchOwners (inWallet) {
|
||||
const wallet = new Contract(inWallet.api, WalletAbi).at(inWallet.address);
|
||||
|
||||
return wallet.instance.getOwners.call()
|
||||
.then((owners) => {
|
||||
return owners.map((token) => token.value);
|
||||
});
|
||||
}
|
||||
|
||||
static fetchPendingTransactions (inWallet) {
|
||||
const wallet = new Contract(inWallet.api, WalletAbi).at(inWallet.address);
|
||||
|
||||
let transactions;
|
||||
let txIds;
|
||||
|
||||
// Get pending and not exectued transactions
|
||||
return wallet.instance.getTransactionCount
|
||||
.call({}, [ true, false ])
|
||||
.then((txCount) => {
|
||||
// Get all the pending transactions
|
||||
const fromId = 0;
|
||||
const toId = txCount;
|
||||
|
||||
return wallet.instance.getTransactionIds
|
||||
.call({}, [ fromId, toId, true, false ]);
|
||||
})
|
||||
.then((_txIds) => {
|
||||
txIds = _txIds.map((token) => token.value);
|
||||
|
||||
const promises = txIds.map((txId) => {
|
||||
return wallet.instance.transactions
|
||||
.call({}, [ txId ]);
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
})
|
||||
.then((transactions) => {
|
||||
return transactions.map((transaction, index) => {
|
||||
const [ destination, value, data ] = transaction;
|
||||
const id = toHex(txIds[index]);
|
||||
|
||||
return {
|
||||
to: destination,
|
||||
data,
|
||||
value,
|
||||
operation: id
|
||||
};
|
||||
});
|
||||
})
|
||||
.then((_transactions) => {
|
||||
transactions = _transactions;
|
||||
|
||||
return wallet
|
||||
.getAllLogs({
|
||||
topics: [
|
||||
WalletSignatures.Submission,
|
||||
txIds.map((txId) => toHex(txId))
|
||||
]
|
||||
});
|
||||
})
|
||||
.then((logs) => {
|
||||
transactions.forEach((tx) => {
|
||||
const log = logs
|
||||
.find((log) => {
|
||||
const id = toHex(log.params.transactionId.value);
|
||||
|
||||
return id === tx.operation;
|
||||
});
|
||||
|
||||
if (!log) {
|
||||
console.warn('could not find a Submission log for this operation', tx);
|
||||
return;
|
||||
}
|
||||
|
||||
tx.transactionIndex = log.transactionIndex;
|
||||
tx.transactionHash = log.transactionHash;
|
||||
tx.blockNumber = log.blockNumber;
|
||||
});
|
||||
|
||||
const confirmationsPromises = transactions.map((tx) => {
|
||||
return wallet.instance.getConfirmations
|
||||
.call({}, [ tx.operation ])
|
||||
.then((owners) => {
|
||||
return owners.map((token) => token.value);
|
||||
});
|
||||
});
|
||||
|
||||
return Promise.all(confirmationsPromises);
|
||||
})
|
||||
.then((confirmations) => {
|
||||
transactions.forEach((tx, index) => {
|
||||
tx.confirmedBy = confirmations[index];
|
||||
});
|
||||
|
||||
return transactions;
|
||||
});
|
||||
}
|
||||
|
||||
static fetchRequire (inWallet) {
|
||||
const wallet = new Contract(inWallet.api, WalletAbi).at(inWallet.address);
|
||||
|
||||
return wallet.instance.required.call();
|
||||
}
|
||||
|
||||
static fetchTransactions (inWallet) {
|
||||
const wallet = new Contract(inWallet.api, WalletAbi).at(inWallet.address);
|
||||
|
||||
let transactions;
|
||||
let txIds;
|
||||
|
||||
return wallet.instance.getTransactionCount
|
||||
.call({}, [ false, true ])
|
||||
.then((txCount) => {
|
||||
// Get the 20 last transactions
|
||||
const fromId = Math.max(0, txCount - 20);
|
||||
const toId = txCount;
|
||||
|
||||
return wallet.instance.getTransactionIds
|
||||
.call({}, [ fromId, toId, false, true ]);
|
||||
})
|
||||
.then((_txIds) => {
|
||||
txIds = _txIds.map((token) => token.value);
|
||||
|
||||
const promises = txIds.map((txId) => {
|
||||
return wallet.instance.transactions
|
||||
.call({}, [ txId ]);
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
})
|
||||
.then((transactions) => {
|
||||
return transactions.map((transaction, index) => {
|
||||
const [ destination, value, data, executed ] = transaction;
|
||||
const id = toHex(txIds[index]);
|
||||
|
||||
return {
|
||||
destination, value, data, executed, id
|
||||
};
|
||||
});
|
||||
})
|
||||
.then((_transactions) => {
|
||||
transactions = _transactions;
|
||||
|
||||
const depositLogs = wallet
|
||||
.getAllLogs({
|
||||
topics: [ WalletSignatures.Deposit ]
|
||||
});
|
||||
|
||||
const executionLogs = wallet
|
||||
.getAllLogs({
|
||||
topics: [ WalletSignatures.Execution, txIds ]
|
||||
});
|
||||
|
||||
return Promise.all([ depositLogs, executionLogs ]);
|
||||
})
|
||||
.then(([ depositLogs, executionLogs ]) => {
|
||||
const logs = [].concat(depositLogs, executionLogs);
|
||||
|
||||
return logs.map((log) => {
|
||||
const signature = toHex(log.topics[0]);
|
||||
|
||||
const transaction = {
|
||||
transactionHash: log.transactionHash,
|
||||
blockNumber: log.blockNumber
|
||||
};
|
||||
|
||||
if (signature === WalletSignatures.Deposit) {
|
||||
transaction.from = log.params.sender.value;
|
||||
transaction.value = log.params.value.value;
|
||||
transaction.to = wallet.address;
|
||||
} else {
|
||||
const txId = toHex(log.params.transactionId.value);
|
||||
const tx = transactions.find((tx) => tx.id === txId);
|
||||
|
||||
transaction.from = wallet.address;
|
||||
transaction.to = tx.destination;
|
||||
transaction.value = tx.value;
|
||||
transaction.data = tx.data;
|
||||
transaction.operation = toHex(tx.id);
|
||||
}
|
||||
|
||||
return transaction;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static getChangeMethod (api, address, change) {
|
||||
const wallet = new Contract(api, WalletAbi).at(address);
|
||||
const walletInstance = wallet.instance;
|
||||
|
||||
let data = '';
|
||||
|
||||
if (change.type === 'require') {
|
||||
const func = walletInstance.changeRequirement;
|
||||
|
||||
data = wallet.getCallData(func, {}, [ change.value ]);
|
||||
}
|
||||
|
||||
if (change.type === 'add_owner') {
|
||||
const func = walletInstance.addOwner;
|
||||
|
||||
data = wallet.getCallData(func, {}, [ change.value ]);
|
||||
}
|
||||
|
||||
if (change.type === 'change_owner') {
|
||||
const func = walletInstance.replaceOwner;
|
||||
|
||||
data = wallet.getCallData(func, {}, [ change.value.from, change.value.to ]);
|
||||
}
|
||||
|
||||
if (change.type === 'remove_owner') {
|
||||
const func = walletInstance.removeOwner;
|
||||
|
||||
data = wallet.getCallData(func, {}, [ change.value ]);
|
||||
}
|
||||
|
||||
const method = walletInstance.submitTransaction;
|
||||
const values = [ address, 0, data ];
|
||||
|
||||
return { method, values };
|
||||
}
|
||||
|
||||
static getModifyOperationMethod (modification) {
|
||||
switch (modification) {
|
||||
case 'confirm':
|
||||
return 'confirmTransaction';
|
||||
|
||||
case 'revoke':
|
||||
return 'revokeConfirmation';
|
||||
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
static getSubmitMethod () {
|
||||
return 'submitTransaction';
|
||||
}
|
||||
|
||||
static getWalletContract (api) {
|
||||
return new Contract(api, WalletAbi);
|
||||
}
|
||||
|
||||
static getWalletSignatures () {
|
||||
return WalletSignatures;
|
||||
}
|
||||
|
||||
static fetchDailylimit () {
|
||||
return {
|
||||
last: new BigNumber(0),
|
||||
limit: new BigNumber(0),
|
||||
spent: new BigNumber(0)
|
||||
};
|
||||
}
|
||||
|
||||
static isWallet (api, address) {
|
||||
const wallet = new Contract(api, WalletAbi).at(address);
|
||||
|
||||
return ConsensysWalletUtils.fetchRequire(wallet)
|
||||
.then((result) => {
|
||||
if (!result || result.equals(0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static logToUpdate (log) {
|
||||
const eventSignature = toHex(log.topics[0]);
|
||||
|
||||
switch (eventSignature) {
|
||||
case WalletSignatures.OwnerAddition:
|
||||
case WalletSignatures.OwnerRemoval:
|
||||
return { [ UPDATE_OWNERS ]: true };
|
||||
|
||||
case WalletSignatures.RequirementChange:
|
||||
return { [ UPDATE_REQUIRE ]: true };
|
||||
|
||||
case WalletSignatures.Deposit:
|
||||
case WalletSignatures.Execution:
|
||||
return { [ UPDATE_TRANSACTIONS ]: true };
|
||||
|
||||
case WalletSignatures.Submission:
|
||||
case WalletSignatures.Confirmation:
|
||||
case WalletSignatures.Revocation:
|
||||
const parsedLog = WALLET_CONTRACT.parseEventLogs([ log ])[0];
|
||||
const operation = toHex(parsedLog.params.transactionId.value);
|
||||
|
||||
return { [ UPDATE_CONFIRMATIONS ]: operation };
|
||||
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This type of wallet cannot create any contract...
|
||||
*/
|
||||
static parseTransactionLogs (api, options, rawLogs) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
500
js/src/util/wallets/foundation-wallet.js
Normal file
500
js/src/util/wallets/foundation-wallet.js
Normal file
@@ -0,0 +1,500 @@
|
||||
// Copyright 2015-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/>.
|
||||
|
||||
import { range, uniq } from 'lodash';
|
||||
|
||||
import Abi from '~/abi';
|
||||
import Contract from '~/api/contract';
|
||||
import { bytesToHex, toHex } from '~/api/util/format';
|
||||
import { validateAddress } from '~/util/validation';
|
||||
|
||||
import WalletAbi from '~/contracts/abi/foundation-multisig-wallet.json';
|
||||
import OldWalletAbi from '~/contracts/abi/old-wallet.json';
|
||||
|
||||
import PendingContracts from './pending-contracts';
|
||||
import {
|
||||
UPDATE_OWNERS,
|
||||
UPDATE_REQUIRE,
|
||||
UPDATE_TRANSACTIONS,
|
||||
UPDATE_CONFIRMATIONS
|
||||
} from './updates';
|
||||
|
||||
const WALLET_CONTRACT = new Contract({}, WalletAbi);
|
||||
const WALLET_ABI = new Abi(WalletAbi);
|
||||
const OLD_WALLET_ABI = new Abi(OldWalletAbi);
|
||||
|
||||
const walletEvents = WALLET_ABI.events.reduce((events, event) => {
|
||||
events[event.name] = event;
|
||||
return events;
|
||||
}, {});
|
||||
|
||||
const oldWalletEvents = OLD_WALLET_ABI.events.reduce((events, event) => {
|
||||
events[event.name] = event;
|
||||
return events;
|
||||
}, {});
|
||||
|
||||
const WalletSignatures = {
|
||||
OwnerChanged: toHex(walletEvents.OwnerChanged.signature),
|
||||
OwnerAdded: toHex(walletEvents.OwnerAdded.signature),
|
||||
OwnerRemoved: toHex(walletEvents.OwnerRemoved.signature),
|
||||
RequirementChanged: toHex(walletEvents.RequirementChanged.signature),
|
||||
Confirmation: toHex(walletEvents.Confirmation.signature),
|
||||
Revoke: toHex(walletEvents.Revoke.signature),
|
||||
Deposit: toHex(walletEvents.Deposit.signature),
|
||||
SingleTransact: toHex(walletEvents.SingleTransact.signature),
|
||||
MultiTransact: toHex(walletEvents.MultiTransact.signature),
|
||||
ConfirmationNeeded: toHex(walletEvents.ConfirmationNeeded.signature),
|
||||
|
||||
Old: {
|
||||
SingleTransact: toHex(oldWalletEvents.SingleTransact.signature),
|
||||
MultiTransact: toHex(oldWalletEvents.MultiTransact.signature)
|
||||
}
|
||||
};
|
||||
|
||||
export default class FoundationWalletUtils {
|
||||
static fetchConfirmations (walletContract, operation, _owners = null) {
|
||||
const ownersPromise = _owners
|
||||
? Promise.resolve(_owners)
|
||||
: FoundationWalletUtils.fetchOwners(walletContract);
|
||||
|
||||
return ownersPromise
|
||||
.then((owners) => {
|
||||
const promises = owners.map((owner) => {
|
||||
return walletContract.instance.hasConfirmed.call({}, [ operation, owner ]);
|
||||
});
|
||||
|
||||
return Promise
|
||||
.all(promises)
|
||||
.then((data) => {
|
||||
return owners.filter((_, index) => data[index]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static fetchDailylimit (walletContract) {
|
||||
const walletInstance = walletContract.instance;
|
||||
|
||||
return Promise
|
||||
.all([
|
||||
walletInstance.m_dailyLimit.call(),
|
||||
walletInstance.m_spentToday.call(),
|
||||
walletInstance.m_lastDay.call()
|
||||
])
|
||||
.then(([ limit, spent, last ]) => ({
|
||||
limit, spent, last
|
||||
}));
|
||||
}
|
||||
|
||||
static fetchOwners (walletContract) {
|
||||
const walletInstance = walletContract.instance;
|
||||
|
||||
return walletInstance
|
||||
.m_numOwners.call()
|
||||
.then((mNumOwners) => {
|
||||
const promises = range(mNumOwners.toNumber())
|
||||
.map((idx) => walletInstance.getOwner.call({}, [ idx ]));
|
||||
|
||||
return Promise
|
||||
.all(promises)
|
||||
.then((_owners) => {
|
||||
const owners = validateOwners(_owners);
|
||||
|
||||
// If all owners are the zero account : must be Mist wallet contract
|
||||
if (!owners) {
|
||||
return fetchMistOwners(walletContract, mNumOwners.toNumber());
|
||||
}
|
||||
|
||||
return owners;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static fetchPendingTransactions (walletContract, cache = {}) {
|
||||
const { owners, transactions } = cache;
|
||||
|
||||
return walletContract
|
||||
.instance
|
||||
.ConfirmationNeeded
|
||||
.getAllLogs()
|
||||
.then((logs) => {
|
||||
return logs.map((log) => ({
|
||||
initiator: log.params.initiator.value,
|
||||
to: log.params.to.value,
|
||||
data: log.params.data.value,
|
||||
value: log.params.value.value,
|
||||
operation: bytesToHex(log.params.operation.value),
|
||||
transactionIndex: log.transactionIndex,
|
||||
transactionHash: log.transactionHash,
|
||||
blockNumber: log.blockNumber,
|
||||
confirmedBy: []
|
||||
}));
|
||||
})
|
||||
.then((logs) => {
|
||||
return logs.sort((logA, logB) => {
|
||||
const comp = logA.blockNumber.comparedTo(logB.blockNumber);
|
||||
|
||||
if (comp !== 0) {
|
||||
return comp;
|
||||
}
|
||||
|
||||
return logA.transactionIndex.comparedTo(logB.transactionIndex);
|
||||
});
|
||||
})
|
||||
.then((pendingTxs) => {
|
||||
if (pendingTxs.length === 0) {
|
||||
return pendingTxs;
|
||||
}
|
||||
|
||||
// Only fetch confirmations for operations not
|
||||
// yet confirmed (ie. not yet a transaction)
|
||||
if (transactions) {
|
||||
const operations = transactions
|
||||
.filter((t) => t.operation)
|
||||
.map((t) => t.operation);
|
||||
|
||||
return pendingTxs.filter((pendingTx) => {
|
||||
return !operations.includes(pendingTx.operation);
|
||||
});
|
||||
}
|
||||
|
||||
return pendingTxs;
|
||||
})
|
||||
.then((pendingTxs) => {
|
||||
const promises = pendingTxs.map((tx) => {
|
||||
return FoundationWalletUtils
|
||||
.fetchConfirmations(walletContract, tx.operation, owners)
|
||||
.then((confirmedBy) => {
|
||||
tx.confirmedBy = confirmedBy;
|
||||
|
||||
return tx;
|
||||
});
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
}
|
||||
|
||||
static fetchRequire (wallet) {
|
||||
return wallet.instance.m_required.call();
|
||||
}
|
||||
|
||||
static fetchTransactions (walletContract) {
|
||||
const { api } = walletContract;
|
||||
|
||||
return walletContract
|
||||
.getAllLogs({
|
||||
topics: [ [
|
||||
WalletSignatures.SingleTransact,
|
||||
WalletSignatures.MultiTransact,
|
||||
WalletSignatures.Deposit,
|
||||
WalletSignatures.Old.SingleTransact,
|
||||
WalletSignatures.Old.MultiTransact
|
||||
] ]
|
||||
})
|
||||
.then((logs) => {
|
||||
const transactions = logs.map((log) => {
|
||||
const signature = toHex(log.topics[0]);
|
||||
|
||||
const value = log.params.value.value;
|
||||
const from = signature === WalletSignatures.Deposit
|
||||
? log.params['_from'].value
|
||||
: walletContract.address;
|
||||
|
||||
const to = signature === WalletSignatures.Deposit
|
||||
? walletContract.address
|
||||
: log.params.to.value;
|
||||
|
||||
const transaction = {
|
||||
transactionHash: log.transactionHash,
|
||||
blockNumber: log.blockNumber,
|
||||
from, to, value
|
||||
};
|
||||
|
||||
if (log.params.created && log.params.created.value && !/^(0x)?0*$/.test(log.params.created.value)) {
|
||||
transaction.creates = log.params.created.value;
|
||||
delete transaction.to;
|
||||
}
|
||||
|
||||
if (log.params.operation) {
|
||||
transaction.operation = bytesToHex(log.params.operation.value);
|
||||
checkPendingOperation(api, log, transaction.operation);
|
||||
}
|
||||
|
||||
if (log.params.data) {
|
||||
transaction.data = log.params.data.value;
|
||||
}
|
||||
|
||||
return transaction;
|
||||
});
|
||||
|
||||
return transactions;
|
||||
});
|
||||
}
|
||||
|
||||
static getChangeMethod (api, address, change) {
|
||||
const wallet = new Contract(api, WalletAbi).at(address);
|
||||
const walletInstance = wallet.instance;
|
||||
|
||||
if (change.type === 'require') {
|
||||
return {
|
||||
method: walletInstance.changeRequirement,
|
||||
values: [ change.value ]
|
||||
};
|
||||
}
|
||||
|
||||
if (change.type === 'dailylimit') {
|
||||
return {
|
||||
method: walletInstance.setDailyLimit,
|
||||
values: [ change.value ]
|
||||
};
|
||||
}
|
||||
|
||||
if (change.type === 'add_owner') {
|
||||
return {
|
||||
method: walletInstance.addOwner,
|
||||
values: [ change.value ]
|
||||
};
|
||||
}
|
||||
|
||||
if (change.type === 'change_owner') {
|
||||
return {
|
||||
method: walletInstance.changeOwner,
|
||||
values: [ change.value.from, change.value.to ]
|
||||
};
|
||||
}
|
||||
|
||||
if (change.type === 'remove_owner') {
|
||||
return {
|
||||
method: walletInstance.removeOwner,
|
||||
values: [ change.value ]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static getModifyOperationMethod (modification) {
|
||||
switch (modification) {
|
||||
case 'confirm':
|
||||
return 'confirm';
|
||||
|
||||
case 'revoke':
|
||||
return 'revoke';
|
||||
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
static getSubmitMethod () {
|
||||
return 'execute';
|
||||
}
|
||||
|
||||
static getWalletContract (api) {
|
||||
return new Contract(api, WalletAbi);
|
||||
}
|
||||
|
||||
static getWalletSignatures () {
|
||||
return WalletSignatures;
|
||||
}
|
||||
|
||||
static isWallet (api, address) {
|
||||
const walletContract = new Contract(api, WalletAbi);
|
||||
|
||||
return walletContract
|
||||
.at(address)
|
||||
.instance
|
||||
.m_numOwners
|
||||
.call()
|
||||
.then((result) => {
|
||||
if (!result || result.equals(0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static logToUpdate (log) {
|
||||
const eventSignature = toHex(log.topics[0]);
|
||||
|
||||
switch (eventSignature) {
|
||||
case WalletSignatures.OwnerChanged:
|
||||
case WalletSignatures.OwnerAdded:
|
||||
case WalletSignatures.OwnerRemoved:
|
||||
return { [ UPDATE_OWNERS ]: true };
|
||||
|
||||
case WalletSignatures.RequirementChanged:
|
||||
return { [ UPDATE_REQUIRE ]: true };
|
||||
|
||||
case WalletSignatures.ConfirmationNeeded:
|
||||
case WalletSignatures.Confirmation:
|
||||
case WalletSignatures.Revoke:
|
||||
const parsedLog = WALLET_CONTRACT.parseEventLogs([ log ])[0];
|
||||
const operation = bytesToHex(parsedLog.params.operation.value);
|
||||
|
||||
return { [ UPDATE_CONFIRMATIONS ]: operation };
|
||||
|
||||
case WalletSignatures.Deposit:
|
||||
case WalletSignatures.SingleTransact:
|
||||
case WalletSignatures.MultiTransact:
|
||||
case WalletSignatures.Old.SingleTransact:
|
||||
case WalletSignatures.Old.MultiTransact:
|
||||
return { [ UPDATE_TRANSACTIONS ]: true };
|
||||
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
static parseLogs (api, logs = []) {
|
||||
const walletContract = new Contract(api, WalletAbi);
|
||||
|
||||
return walletContract.parseEventLogs(logs);
|
||||
}
|
||||
|
||||
static parseTransactionLogs (api, options, rawLogs) {
|
||||
const { metadata } = options;
|
||||
const address = options.from;
|
||||
const logs = FoundationWalletUtils.parseLogs(api, rawLogs);
|
||||
|
||||
const confirmationLog = logs.find((log) => log.event === 'ConfirmationNeeded');
|
||||
const transactionLog = logs.find((log) => log.event === 'SingleTransact');
|
||||
|
||||
if (!confirmationLog && !transactionLog) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Confirmations are needed from the other owners
|
||||
if (confirmationLog) {
|
||||
const operationHash = bytesToHex(confirmationLog.params.operation.value);
|
||||
|
||||
// Add the contract to pending contracts
|
||||
PendingContracts.addPendingContract(address, operationHash, metadata);
|
||||
|
||||
return { pending: true };
|
||||
}
|
||||
|
||||
return { contractAddress: transactionLog.params.created.value };
|
||||
}
|
||||
}
|
||||
|
||||
function checkPendingOperation (api, log, operation) {
|
||||
const pendingContracts = PendingContracts.getPendingContracts();
|
||||
|
||||
// Add the pending contract to the contracts
|
||||
if (pendingContracts[operation]) {
|
||||
const { metadata } = pendingContracts[operation];
|
||||
const contractName = metadata.name;
|
||||
|
||||
metadata.blockNumber = log.blockNumber;
|
||||
|
||||
// The contract creation might not be in the same log,
|
||||
// but must be in the same transaction (eg. Contract creation
|
||||
// from Wallet within a Wallet)
|
||||
api.eth
|
||||
.getTransactionReceipt(log.transactionHash)
|
||||
.then((transactionReceipt) => {
|
||||
const transactionLogs = FoundationWalletUtils.parseLogs(api, transactionReceipt.logs);
|
||||
const creationLog = transactionLogs.find((log) => {
|
||||
return log.params.created && !/^(0x)?0*$/.test(log.params.created.value);
|
||||
});
|
||||
|
||||
if (!creationLog) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const contractAddress = creationLog.params.created.value;
|
||||
|
||||
return Promise
|
||||
.all([
|
||||
api.parity.setAccountName(contractAddress, contractName),
|
||||
api.parity.setAccountMeta(contractAddress, metadata)
|
||||
])
|
||||
.then(() => {
|
||||
PendingContracts.removePendingContract(operation);
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('adding wallet contract', error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function fetchMistOwners (walletContract, mNumOwners) {
|
||||
const walletAddress = walletContract.address;
|
||||
|
||||
return getMistOwnersOffset(walletContract)
|
||||
.then((result) => {
|
||||
if (!result || result.offset === -1) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const owners = [ result.address ];
|
||||
|
||||
if (mNumOwners === 1) {
|
||||
return owners;
|
||||
}
|
||||
|
||||
const initOffset = result.offset + 1;
|
||||
let promise = Promise.resolve();
|
||||
|
||||
range(initOffset, initOffset + mNumOwners - 1).forEach((offset) => {
|
||||
promise = promise
|
||||
.then(() => {
|
||||
return walletContract.api.eth.getStorageAt(walletAddress, offset);
|
||||
})
|
||||
.then((result) => {
|
||||
const resultAddress = '0x' + (result || '').slice(-40);
|
||||
const { address } = validateAddress(resultAddress);
|
||||
|
||||
owners.push(address);
|
||||
});
|
||||
});
|
||||
|
||||
return promise.then(() => owners);
|
||||
});
|
||||
}
|
||||
|
||||
function getMistOwnersOffset (walletContract, offset = 3) {
|
||||
return walletContract.api.eth
|
||||
.getStorageAt(walletContract.address, offset)
|
||||
.then((result) => {
|
||||
if (result && !/^(0x)?0*$/.test(result)) {
|
||||
const resultAddress = '0x' + result.slice(-40);
|
||||
const { address, addressError } = validateAddress(resultAddress);
|
||||
|
||||
if (!addressError) {
|
||||
return { offset, address };
|
||||
}
|
||||
}
|
||||
|
||||
if (offset >= 100) {
|
||||
return { offset: -1 };
|
||||
}
|
||||
|
||||
return getMistOwnersOffset(walletContract, offset + 1);
|
||||
});
|
||||
}
|
||||
|
||||
function validateOwners (owners) {
|
||||
const uniqOwners = uniq(owners);
|
||||
|
||||
// If all owners are the zero account : must be Mist wallet contract
|
||||
if (uniqOwners.length === 1 && /^(0x)?0*$/.test(owners[0])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return uniqOwners;
|
||||
}
|
||||
49
js/src/util/wallets/pending-contracts.js
Normal file
49
js/src/util/wallets/pending-contracts.js
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright 2015-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/>.
|
||||
|
||||
import store from 'store';
|
||||
|
||||
const LS_PENDING_CONTRACTS_KEY = '_parity::wallets::pendingContracts';
|
||||
|
||||
export default class PendingContracts {
|
||||
static getPendingContracts () {
|
||||
return store.get(LS_PENDING_CONTRACTS_KEY) || {};
|
||||
}
|
||||
|
||||
static setPendingContracts (contracts = {}) {
|
||||
return store.set(LS_PENDING_CONTRACTS_KEY, contracts);
|
||||
}
|
||||
|
||||
static removePendingContract (operationHash) {
|
||||
const nextContracts = PendingContracts.getPendingContracts();
|
||||
|
||||
delete nextContracts[operationHash];
|
||||
PendingContracts.setPendingContracts(nextContracts);
|
||||
}
|
||||
|
||||
static addPendingContract (address, operationHash, metadata) {
|
||||
const nextContracts = {
|
||||
...PendingContracts.getPendingContracts(),
|
||||
[ operationHash ]: {
|
||||
address,
|
||||
metadata,
|
||||
operationHash
|
||||
}
|
||||
};
|
||||
|
||||
PendingContracts.setPendingContracts(nextContracts);
|
||||
}
|
||||
}
|
||||
21
js/src/util/wallets/updates.js
Normal file
21
js/src/util/wallets/updates.js
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2015-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/>.
|
||||
|
||||
export const UPDATE_OWNERS = 'owners';
|
||||
export const UPDATE_REQUIRE = 'require';
|
||||
export const UPDATE_DAILYLIMIT = 'dailylimit';
|
||||
export const UPDATE_TRANSACTIONS = 'transactions';
|
||||
export const UPDATE_CONFIRMATIONS = 'confirmations';
|
||||
@@ -41,8 +41,13 @@ class Delete extends Component {
|
||||
newError: PropTypes.func
|
||||
};
|
||||
|
||||
state = {
|
||||
isBusy: false
|
||||
};
|
||||
|
||||
render () {
|
||||
const { account, confirmMessage, visible } = this.props;
|
||||
const { isBusy } = this.state;
|
||||
|
||||
if (!visible) {
|
||||
return null;
|
||||
@@ -50,6 +55,7 @@ class Delete extends Component {
|
||||
|
||||
return (
|
||||
<ConfirmDialog
|
||||
busy={ isBusy }
|
||||
className={ styles.delete }
|
||||
title={
|
||||
<FormattedMessage
|
||||
@@ -99,6 +105,8 @@ class Delete extends Component {
|
||||
const { api, router } = this.context;
|
||||
const { account, route, newError } = this.props;
|
||||
|
||||
this.setState({ isBusy: true });
|
||||
|
||||
api.parity
|
||||
.removeAddress(account.address)
|
||||
.then(() => {
|
||||
|
||||
@@ -28,11 +28,18 @@ export default class Store {
|
||||
this._migrateStore();
|
||||
|
||||
this._api = api;
|
||||
// Show the first run if it hasn't been shown before
|
||||
// (thus an undefined value)
|
||||
this.firstrunVisible = store.get(LS_FIRST_RUN_KEY) === undefined;
|
||||
|
||||
this._checkAccounts();
|
||||
// Show the first run the storage doesn't hold `false` value
|
||||
const firstrunVisible = store.get(LS_FIRST_RUN_KEY) !== false;
|
||||
|
||||
// Only check accounts if we might show the first run
|
||||
if (firstrunVisible) {
|
||||
api.transport.once('open', () => {
|
||||
this._checkAccounts();
|
||||
});
|
||||
} else {
|
||||
this.firstrunVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
@action closeFirstrun = () => {
|
||||
@@ -50,7 +57,7 @@ export default class Store {
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate the old LocalStorage ket format
|
||||
* Migrate the old LocalStorage key format
|
||||
* to the new one
|
||||
*/
|
||||
_migrateStore () {
|
||||
@@ -70,12 +77,16 @@ export default class Store {
|
||||
this._api.parity.allAccountsInfo()
|
||||
])
|
||||
.then(([ vaults, info ]) => {
|
||||
const accounts = Object.keys(info).filter((address) => info[address].uuid);
|
||||
const accounts = Object.keys(info)
|
||||
.filter((address) => info[address].uuid)
|
||||
// In DEV mode, the empty phrase account is already added
|
||||
.filter((address) => address.toLowerCase() !== '0x00a329c0648769a73afac7f9381e08fb43dbea72');
|
||||
|
||||
// Has accounts if any vaults or accounts
|
||||
const hasAccounts = (accounts && accounts.length > 0) || (vaults && vaults.length > 0);
|
||||
|
||||
// Show First Run if no accounts and no vaults
|
||||
this.toggleFirstrun(this.firstrunVisible || !hasAccounts);
|
||||
this.toggleFirstrun(!hasAccounts);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('checkAccounts', error);
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
color: rgb(0, 151, 167);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
|
||||
@@ -127,7 +127,7 @@ export default class Urls extends Component {
|
||||
this.props.store.gotoUrl(url);
|
||||
|
||||
if (extensionStore.hasExtension) {
|
||||
window.open(this.props.store.currentUrl, '_blank');
|
||||
window.open(url, '_blank');
|
||||
} else {
|
||||
router.push('/web');
|
||||
}
|
||||
|
||||
@@ -407,7 +407,11 @@ export default class TransactionPendingFormConfirm extends Component {
|
||||
onScan = (signature) => {
|
||||
const { chainId, rlp, tx, data, decrypt } = this.state.qr;
|
||||
|
||||
if (signature && signature.substr(0, 2) !== '0x') {
|
||||
if (!signature) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (signature.substr(0, 2) !== '0x') {
|
||||
signature = `0x${signature}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ class SyncWarning extends Component {
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const { health } = state.nodeStatus;
|
||||
const isNotAvailableYet = health.overall.isReady;
|
||||
const isNotAvailableYet = health.overall.isNotReady;
|
||||
const isOk = isNotAvailableYet || health.overall.status === 'ok';
|
||||
|
||||
return {
|
||||
|
||||
@@ -64,12 +64,9 @@ export default class Store {
|
||||
if (!hasProtocol.test(url)) {
|
||||
url = `https://${url}`;
|
||||
}
|
||||
|
||||
this.setNextUrl(url);
|
||||
return this.generateToken(url).then(() => {
|
||||
transaction(() => {
|
||||
this.setNextUrl(url);
|
||||
this.setCurrentUrl(this.nextUrl);
|
||||
});
|
||||
this.setCurrentUrl(this.nextUrl);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -21,10 +21,39 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:root .warningClose {
|
||||
text-align: right;
|
||||
a {
|
||||
color: #fff;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.warning {
|
||||
color: #fff;
|
||||
background: #f80;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 50%;
|
||||
padding: 1.5em;
|
||||
z-index: 100;
|
||||
animation: fadein 0.3s;
|
||||
}
|
||||
|
||||
@keyframes fadein {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.loading {
|
||||
text-align: center;
|
||||
margin-top: 5em;
|
||||
color: #999;
|
||||
color: #444;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,10 @@ export default class Web extends Component {
|
||||
|
||||
store = Store.get(this.context.api);
|
||||
|
||||
state = {
|
||||
isWarningDismissed: false
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.store.gotoUrl(this.props.params.url);
|
||||
}
|
||||
@@ -83,10 +87,36 @@ export default class Web extends Component {
|
||||
scrolling='auto'
|
||||
src={ encodedPath }
|
||||
/>
|
||||
{ this.renderWarning() }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderWarning () {
|
||||
if (this.state.isWarningDismissed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.warning }>
|
||||
<p>
|
||||
WARNING: The web browser dapp is not safe as a general purpose browser.
|
||||
Make sure to only visit web3-enabled sites that you trust.
|
||||
Do not use it to browse web2.0 and never log in to any service - web3 dapps should not require that.
|
||||
</p>
|
||||
<div className={ styles.warningClose }>
|
||||
<a onClick={ this.dismissWarning }>Okay!</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
dismissWarning = () => {
|
||||
this.setState({
|
||||
isWarningDismissed: true
|
||||
});
|
||||
};
|
||||
|
||||
iframeOnLoad = () => {
|
||||
this.store.setLoading(false);
|
||||
};
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
<key>OVERWRITE_PERMISSIONS</key>
|
||||
<false/>
|
||||
<key>VERSION</key>
|
||||
<string>1.7.0</string>
|
||||
<string>1.7.1</string>
|
||||
</dict>
|
||||
<key>UUID</key>
|
||||
<string>2DCD5B81-7BAF-4DA1-9251-6274B089FD36</string>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.6</string>
|
||||
<string>1.7</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
!define DESCRIPTION "Fast, light, robust Ethereum implementation"
|
||||
!define VERSIONMAJOR 1
|
||||
!define VERSIONMINOR 7
|
||||
!define VERSIONBUILD 0
|
||||
!define VERSIONBUILD 1
|
||||
!define ARGS "--warp"
|
||||
!define FIRST_START_ARGS "ui --warp --mode=passive"
|
||||
|
||||
|
||||
@@ -93,6 +93,7 @@ pub struct ImportBlockchain {
|
||||
pub check_seal: bool,
|
||||
pub with_color: bool,
|
||||
pub verifier_settings: VerifierSettings,
|
||||
pub light: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@@ -138,12 +139,165 @@ pub struct ExportState {
|
||||
pub fn execute(cmd: BlockchainCmd) -> Result<(), String> {
|
||||
match cmd {
|
||||
BlockchainCmd::Kill(kill_cmd) => kill_db(kill_cmd),
|
||||
BlockchainCmd::Import(import_cmd) => execute_import(import_cmd),
|
||||
BlockchainCmd::Import(import_cmd) => {
|
||||
if import_cmd.light {
|
||||
execute_import_light(import_cmd)
|
||||
} else {
|
||||
execute_import(import_cmd)
|
||||
}
|
||||
}
|
||||
BlockchainCmd::Export(export_cmd) => execute_export(export_cmd),
|
||||
BlockchainCmd::ExportState(export_cmd) => execute_export_state(export_cmd),
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_import_light(cmd: ImportBlockchain) -> Result<(), String> {
|
||||
use light::client::{Service as LightClientService, Config as LightClientConfig};
|
||||
use light::cache::Cache as LightDataCache;
|
||||
|
||||
let timer = Instant::now();
|
||||
|
||||
// load spec file
|
||||
let spec = cmd.spec.spec(&cmd.dirs.cache)?;
|
||||
|
||||
// load genesis hash
|
||||
let genesis_hash = spec.genesis_header().hash();
|
||||
|
||||
// database paths
|
||||
let db_dirs = cmd.dirs.database(genesis_hash, None, spec.data_dir.clone());
|
||||
|
||||
// user defaults path
|
||||
let user_defaults_path = db_dirs.user_defaults_path();
|
||||
|
||||
// load user defaults
|
||||
let user_defaults = UserDefaults::load(&user_defaults_path)?;
|
||||
|
||||
fdlimit::raise_fd_limit();
|
||||
|
||||
// select pruning algorithm
|
||||
let algorithm = cmd.pruning.to_algorithm(&user_defaults);
|
||||
|
||||
// prepare client and snapshot paths.
|
||||
let client_path = db_dirs.client_path(algorithm);
|
||||
|
||||
// execute upgrades
|
||||
let compaction = cmd.compaction.compaction_profile(db_dirs.db_root_path().as_path());
|
||||
execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, compaction)?;
|
||||
|
||||
// create dirs used by parity
|
||||
cmd.dirs.create_dirs(false, false, false)?;
|
||||
|
||||
let cache = Arc::new(::util::Mutex::new(
|
||||
LightDataCache::new(Default::default(), ::time::Duration::seconds(0))
|
||||
));
|
||||
|
||||
let mut config = LightClientConfig {
|
||||
queue: Default::default(),
|
||||
chain_column: ::ethcore::db::COL_LIGHT_CHAIN,
|
||||
db_cache_size: Some(cmd.cache_config.blockchain() as usize * 1024 * 1024),
|
||||
db_compaction: compaction,
|
||||
db_wal: cmd.wal,
|
||||
verify_full: true,
|
||||
check_seal: cmd.check_seal,
|
||||
};
|
||||
|
||||
config.queue.max_mem_use = cmd.cache_config.queue() as usize * 1024 * 1024;
|
||||
config.queue.verifier_settings = cmd.verifier_settings;
|
||||
|
||||
let service = LightClientService::start(config, &spec, &client_path, cache)
|
||||
.map_err(|e| format!("Failed to start client: {}", e))?;
|
||||
|
||||
// free up the spec in memory.
|
||||
drop(spec);
|
||||
|
||||
let client = service.client();
|
||||
|
||||
let mut instream: Box<io::Read> = match cmd.file_path {
|
||||
Some(f) => Box::new(fs::File::open(&f).map_err(|_| format!("Cannot open given file: {}", f))?),
|
||||
None => Box::new(io::stdin()),
|
||||
};
|
||||
|
||||
const READAHEAD_BYTES: usize = 8;
|
||||
|
||||
let mut first_bytes: Vec<u8> = vec![0; READAHEAD_BYTES];
|
||||
let mut first_read = 0;
|
||||
|
||||
let format = match cmd.format {
|
||||
Some(format) => format,
|
||||
None => {
|
||||
first_read = instream.read(&mut first_bytes).map_err(|_| "Error reading from the file/stream.")?;
|
||||
match first_bytes[0] {
|
||||
0xf9 => DataFormat::Binary,
|
||||
_ => DataFormat::Hex,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let do_import = |bytes: Vec<u8>| {
|
||||
while client.queue_info().is_full() { sleep(Duration::from_secs(1)); }
|
||||
|
||||
let header: ::ethcore::header::Header = ::rlp::UntrustedRlp::new(&bytes).val_at(0)
|
||||
.map_err(|e| format!("Bad block: {}", e))?;
|
||||
|
||||
if client.best_block_header().number() >= header.number() { return Ok(()) }
|
||||
|
||||
if header.number() % 10000 == 0 {
|
||||
info!("#{}", header.number());
|
||||
}
|
||||
|
||||
match client.import_header(header) {
|
||||
Err(BlockImportError::Import(ImportError::AlreadyInChain)) => {
|
||||
trace!("Skipping block already in chain.");
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(format!("Cannot import block: {:?}", e));
|
||||
},
|
||||
Ok(_) => {},
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
|
||||
match format {
|
||||
DataFormat::Binary => {
|
||||
loop {
|
||||
let mut bytes = if first_read > 0 {first_bytes.clone()} else {vec![0; READAHEAD_BYTES]};
|
||||
let n = if first_read > 0 {
|
||||
first_read
|
||||
} else {
|
||||
instream.read(&mut bytes).map_err(|_| "Error reading from the file/stream.")?
|
||||
};
|
||||
if n == 0 { break; }
|
||||
first_read = 0;
|
||||
let s = PayloadInfo::from(&bytes).map_err(|e| format!("Invalid RLP in the file/stream: {:?}", e))?.total();
|
||||
bytes.resize(s, 0);
|
||||
instream.read_exact(&mut bytes[n..]).map_err(|_| "Error reading from the file/stream.")?;
|
||||
do_import(bytes)?;
|
||||
}
|
||||
}
|
||||
DataFormat::Hex => {
|
||||
for line in BufReader::new(instream).lines() {
|
||||
let s = line.map_err(|_| "Error reading from the file/stream.")?;
|
||||
let s = if first_read > 0 {from_utf8(&first_bytes).unwrap().to_owned() + &(s[..])} else {s};
|
||||
first_read = 0;
|
||||
let bytes = s.from_hex().map_err(|_| "Invalid hex in file/stream.")?;
|
||||
do_import(bytes)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
client.flush_queue();
|
||||
|
||||
let ms = timer.elapsed().as_milliseconds();
|
||||
let report = client.report();
|
||||
|
||||
info!("Import completed in {} seconds, {} headers, {} hdr/s",
|
||||
ms / 1000,
|
||||
report.blocks_imported,
|
||||
(report.blocks_imported * 1000) as u64 / ms,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn execute_import(cmd: ImportBlockchain) -> Result<(), String> {
|
||||
let timer = Instant::now();
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ disable_periodic = true
|
||||
jit = false
|
||||
|
||||
[misc]
|
||||
ntp_server = "pool.ntp.org:123"
|
||||
ntp_servers = ["0.parity.pool.ntp.org:123"]
|
||||
logging = "own_tx=trace"
|
||||
log_file = "/var/log/parity.log"
|
||||
color = true
|
||||
|
||||
@@ -194,7 +194,7 @@ usage! {
|
||||
or |c: &Config| otry!(c.websockets).interface.clone(),
|
||||
flag_ws_apis: String = "web3,eth,pubsub,net,parity,parity_pubsub,traces,rpc,secretstore",
|
||||
or |c: &Config| otry!(c.websockets).apis.as_ref().map(|vec| vec.join(",")),
|
||||
flag_ws_origins: String = "chrome-extension://*",
|
||||
flag_ws_origins: String = "chrome-extension://*,moz-extension://*",
|
||||
or |c: &Config| otry!(c.websockets).origins.as_ref().map(|vec| vec.join(",")),
|
||||
flag_ws_hosts: String = "none",
|
||||
or |c: &Config| otry!(c.websockets).hosts.as_ref().map(|vec| vec.join(",")),
|
||||
@@ -356,8 +356,8 @@ usage! {
|
||||
or |c: &Config| otry!(c.vm).jit.clone(),
|
||||
|
||||
// -- Miscellaneous Options
|
||||
flag_ntp_server: String = "pool.ntp.org:123",
|
||||
or |c: &Config| otry!(c.misc).ntp_server.clone(),
|
||||
flag_ntp_servers: String = "0.parity.pool.ntp.org:123,1.parity.pool.ntp.org:123,2.parity.pool.ntp.org:123,3.parity.pool.ntp.org:123",
|
||||
or |c: &Config| otry!(c.misc).ntp_servers.clone().map(|vec| vec.join(",")),
|
||||
flag_logging: Option<String> = None,
|
||||
or |c: &Config| otry!(c.misc).logging.clone().map(Some),
|
||||
flag_log_file: Option<String> = None,
|
||||
@@ -595,7 +595,7 @@ struct VM {
|
||||
|
||||
#[derive(Default, Debug, PartialEq, Deserialize)]
|
||||
struct Misc {
|
||||
ntp_server: Option<String>,
|
||||
ntp_servers: Option<Vec<String>>,
|
||||
logging: Option<String>,
|
||||
log_file: Option<String>,
|
||||
color: Option<bool>,
|
||||
@@ -897,7 +897,7 @@ mod tests {
|
||||
flag_dapps_apis_all: None,
|
||||
|
||||
// -- Miscellaneous Options
|
||||
flag_ntp_server: "pool.ntp.org:123".into(),
|
||||
flag_ntp_servers: "0.parity.pool.ntp.org:123,1.parity.pool.ntp.org:123,2.parity.pool.ntp.org:123,3.parity.pool.ntp.org:123".into(),
|
||||
flag_version: false,
|
||||
flag_logging: Some("own_tx=trace".into()),
|
||||
flag_log_file: Some("/var/log/parity.log".into()),
|
||||
@@ -1075,7 +1075,7 @@ mod tests {
|
||||
jit: Some(false),
|
||||
}),
|
||||
misc: Some(Misc {
|
||||
ntp_server: Some("pool.ntp.org:123".into()),
|
||||
ntp_servers: Some(vec!["0.parity.pool.ntp.org:123".into()]),
|
||||
logging: Some("own_tx=trace".into()),
|
||||
log_file: Some("/var/log/parity.log".into()),
|
||||
color: Some(true),
|
||||
|
||||
@@ -470,8 +470,10 @@ Internal Options:
|
||||
--can-restart Executable will auto-restart if exiting with 69.
|
||||
|
||||
Miscellaneous Options:
|
||||
--ntp-server HOST NTP server to provide current time (host:port). Used to verify node health.
|
||||
(default: {flag_ntp_server})
|
||||
--ntp-servers HOSTS Comma separated list of NTP servers to provide current time (host:port).
|
||||
Used to verify node health. Parity uses pool.ntp.org NTP servers,
|
||||
consider joining the pool: http://www.pool.ntp.org/join.html
|
||||
(default: {flag_ntp_servers})
|
||||
-l --logging LOGGING Specify the logging level. Must conform to the same
|
||||
format as RUST_LOG. (default: {flag_logging:?})
|
||||
--log-file FILENAME Specify a filename into which logging should be
|
||||
|
||||
@@ -20,6 +20,7 @@ use std::net::SocketAddr;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::collections::BTreeMap;
|
||||
use std::cmp::max;
|
||||
use std::str::FromStr;
|
||||
use cli::{Args, ArgsError};
|
||||
use util::{Hashable, H256, U256, Bytes, version_data, Address};
|
||||
use util::journaldb::Algorithm;
|
||||
@@ -243,6 +244,7 @@ impl Configuration {
|
||||
check_seal: !self.args.flag_no_seal_check,
|
||||
with_color: logger_config.color,
|
||||
verifier_settings: self.verifier_settings(),
|
||||
light: self.args.flag_light,
|
||||
};
|
||||
Cmd::Blockchain(BlockchainCmd::Import(import_cmd))
|
||||
} else if self.args.cmd_export {
|
||||
@@ -555,31 +557,57 @@ impl Configuration {
|
||||
Ok(options)
|
||||
}
|
||||
|
||||
fn ui_port(&self) -> u16 {
|
||||
self.args.flag_ports_shift + self.args.flag_ui_port
|
||||
}
|
||||
|
||||
fn ntp_servers(&self) -> Vec<String> {
|
||||
self.args.flag_ntp_servers.split(",").map(str::to_owned).collect()
|
||||
}
|
||||
|
||||
fn ui_config(&self) -> UiConfiguration {
|
||||
UiConfiguration {
|
||||
enabled: self.ui_enabled(),
|
||||
ntp_server: self.args.flag_ntp_server.clone(),
|
||||
ntp_servers: self.ntp_servers(),
|
||||
interface: self.ui_interface(),
|
||||
port: self.args.flag_ports_shift + self.args.flag_ui_port,
|
||||
port: self.ui_port(),
|
||||
hosts: self.ui_hosts(),
|
||||
}
|
||||
}
|
||||
|
||||
fn dapps_config(&self) -> DappsConfiguration {
|
||||
let dev_ui = if self.args.flag_ui_no_validation { vec![("localhost".to_owned(), 3000)] } else { vec![] };
|
||||
let ui_port = self.ui_port();
|
||||
|
||||
DappsConfiguration {
|
||||
enabled: self.dapps_enabled(),
|
||||
ntp_server: self.args.flag_ntp_server.clone(),
|
||||
ntp_servers: self.ntp_servers(),
|
||||
dapps_path: PathBuf::from(self.directories().dapps),
|
||||
extra_dapps: if self.args.cmd_dapp {
|
||||
self.args.arg_path.iter().map(|path| PathBuf::from(path)).collect()
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
extra_embed_on: if self.args.flag_ui_no_validation {
|
||||
vec![("localhost".to_owned(), 3000)]
|
||||
} else {
|
||||
vec![]
|
||||
extra_embed_on: {
|
||||
let mut extra_embed = dev_ui.clone();
|
||||
match self.ui_hosts() {
|
||||
// In case host validation is disabled allow all frame ancestors
|
||||
None => extra_embed.push(("*".to_owned(), ui_port)),
|
||||
Some(hosts) => extra_embed.extend(hosts.into_iter().filter_map(|host| {
|
||||
let mut it = host.split(":");
|
||||
let host = it.next();
|
||||
let port = it.next().and_then(|v| u16::from_str(v).ok());
|
||||
|
||||
match (host, port) {
|
||||
(Some(host), Some(port)) => Some((host.into(), port)),
|
||||
(Some(host), None) => Some((host.into(), ui_port)),
|
||||
_ => None,
|
||||
}
|
||||
})),
|
||||
}
|
||||
extra_embed
|
||||
},
|
||||
extra_script_src: dev_ui,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -907,13 +935,10 @@ impl Configuration {
|
||||
} else {
|
||||
self.args.flag_db_path.as_ref().map_or(dir::CHAINS_PATH, |s| &s)
|
||||
};
|
||||
let cache_path = if is_using_base_path {
|
||||
"$BASE/cache".into()
|
||||
} else {
|
||||
replace_home_and_local(&data_path, &local_path, &dir::CACHE_PATH)
|
||||
};
|
||||
let cache_path = if is_using_base_path { "$BASE/cache" } else { dir::CACHE_PATH };
|
||||
|
||||
let db_path = replace_home_and_local(&data_path, &local_path, &base_db_path);
|
||||
let cache_path = replace_home_and_local(&data_path, &local_path, cache_path);
|
||||
let keys_path = replace_home(&data_path, &self.args.flag_keys_path);
|
||||
let dapps_path = replace_home(&data_path, &self.args.flag_dapps_path);
|
||||
let secretstore_path = replace_home(&data_path, &self.args.flag_secretstore_path);
|
||||
@@ -1179,6 +1204,7 @@ mod tests {
|
||||
check_seal: true,
|
||||
with_color: !cfg!(windows),
|
||||
verifier_settings: Default::default(),
|
||||
light: false,
|
||||
})));
|
||||
}
|
||||
|
||||
@@ -1263,14 +1289,19 @@ mod tests {
|
||||
interface: "127.0.0.1".into(),
|
||||
port: 8546,
|
||||
apis: ApiSet::UnsafeContext,
|
||||
origins: Some(vec!["chrome-extension://*".into()]),
|
||||
origins: Some(vec!["chrome-extension://*".into(), "moz-extension://*".into()]),
|
||||
hosts: Some(vec![]),
|
||||
signer_path: expected.into(),
|
||||
ui_address: Some(("127.0.0.1".to_owned(), 8180)),
|
||||
support_token_api: true
|
||||
}, UiConfiguration {
|
||||
enabled: true,
|
||||
ntp_server: "pool.ntp.org:123".into(),
|
||||
ntp_servers: vec![
|
||||
"0.parity.pool.ntp.org:123".into(),
|
||||
"1.parity.pool.ntp.org:123".into(),
|
||||
"2.parity.pool.ntp.org:123".into(),
|
||||
"3.parity.pool.ntp.org:123".into(),
|
||||
],
|
||||
interface: "127.0.0.1".into(),
|
||||
port: 8180,
|
||||
hosts: Some(vec![]),
|
||||
@@ -1508,10 +1539,16 @@ mod tests {
|
||||
let conf3 = parse(&["parity", "--ui-path", "signer", "--ui-interface", "test"]);
|
||||
|
||||
// then
|
||||
let ntp_servers = vec![
|
||||
"0.parity.pool.ntp.org:123".into(),
|
||||
"1.parity.pool.ntp.org:123".into(),
|
||||
"2.parity.pool.ntp.org:123".into(),
|
||||
"3.parity.pool.ntp.org:123".into(),
|
||||
];
|
||||
assert_eq!(conf0.directories().signer, "signer".to_owned());
|
||||
assert_eq!(conf0.ui_config(), UiConfiguration {
|
||||
enabled: true,
|
||||
ntp_server: "pool.ntp.org:123".into(),
|
||||
ntp_servers: ntp_servers.clone(),
|
||||
interface: "127.0.0.1".into(),
|
||||
port: 8180,
|
||||
hosts: Some(vec![]),
|
||||
@@ -1520,7 +1557,7 @@ mod tests {
|
||||
assert_eq!(conf1.directories().signer, "signer".to_owned());
|
||||
assert_eq!(conf1.ui_config(), UiConfiguration {
|
||||
enabled: true,
|
||||
ntp_server: "pool.ntp.org:123".into(),
|
||||
ntp_servers: ntp_servers.clone(),
|
||||
interface: "127.0.0.1".into(),
|
||||
port: 8180,
|
||||
hosts: Some(vec![]),
|
||||
@@ -1530,7 +1567,7 @@ mod tests {
|
||||
assert_eq!(conf2.directories().signer, "signer".to_owned());
|
||||
assert_eq!(conf2.ui_config(), UiConfiguration {
|
||||
enabled: true,
|
||||
ntp_server: "pool.ntp.org:123".into(),
|
||||
ntp_servers: ntp_servers.clone(),
|
||||
interface: "127.0.0.1".into(),
|
||||
port: 3123,
|
||||
hosts: Some(vec![]),
|
||||
@@ -1539,7 +1576,7 @@ mod tests {
|
||||
assert_eq!(conf3.directories().signer, "signer".to_owned());
|
||||
assert_eq!(conf3.ui_config(), UiConfiguration {
|
||||
enabled: true,
|
||||
ntp_server: "pool.ntp.org:123".into(),
|
||||
ntp_servers: ntp_servers.clone(),
|
||||
interface: "test".into(),
|
||||
port: 8180,
|
||||
hosts: Some(vec![]),
|
||||
@@ -1653,4 +1690,15 @@ mod tests {
|
||||
assert_eq!(&conf0.ipfs_config().interface, "0.0.0.0");
|
||||
assert_eq!(conf0.ipfs_config().hosts, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_use_correct_cache_path_if_base_is_set() {
|
||||
let std = parse(&["parity"]);
|
||||
let base = parse(&["parity", "--base-path", "/test"]);
|
||||
|
||||
let base_path = ::dir::default_data_path();
|
||||
let local_path = ::dir::default_local_path();
|
||||
assert_eq!(std.directories().cache, ::helpers::replace_home_and_local(&base_path, &local_path, ::dir::CACHE_PATH));
|
||||
assert_eq!(base.directories().cache, "/test/cache");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,10 +36,11 @@ use util::{Bytes, Address};
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Configuration {
|
||||
pub enabled: bool,
|
||||
pub ntp_server: String,
|
||||
pub ntp_servers: Vec<String>,
|
||||
pub dapps_path: PathBuf,
|
||||
pub extra_dapps: Vec<PathBuf>,
|
||||
pub extra_embed_on: Vec<(String, u16)>,
|
||||
pub extra_script_src: Vec<(String, u16)>,
|
||||
}
|
||||
|
||||
impl Default for Configuration {
|
||||
@@ -47,10 +48,16 @@ impl Default for Configuration {
|
||||
let data_dir = default_data_path();
|
||||
Configuration {
|
||||
enabled: true,
|
||||
ntp_server: "pool.ntp.org:123".into(),
|
||||
ntp_servers: vec![
|
||||
"0.parity.pool.ntp.org:123".into(),
|
||||
"1.parity.pool.ntp.org:123".into(),
|
||||
"2.parity.pool.ntp.org:123".into(),
|
||||
"3.parity.pool.ntp.org:123".into(),
|
||||
],
|
||||
dapps_path: replace_home(&data_dir, "$BASE/dapps").into(),
|
||||
extra_dapps: vec![],
|
||||
extra_embed_on: vec![],
|
||||
extra_script_src: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,7 +121,7 @@ impl ContractClient for LightRegistrar {
|
||||
tx: Transaction {
|
||||
nonce: self.client.engine().account_start_nonce(header.number()),
|
||||
action: Action::Call(address),
|
||||
gas: 50_000_000.into(),
|
||||
gas: 50_000.into(), // should be enough for all registry lookups. TODO: exponential backoff
|
||||
gas_price: 0.into(),
|
||||
value: 0.into(),
|
||||
data: data,
|
||||
@@ -158,22 +165,23 @@ pub fn new(configuration: Configuration, deps: Dependencies) -> Result<Option<Mi
|
||||
|
||||
server::dapps_middleware(
|
||||
deps,
|
||||
&configuration.ntp_server,
|
||||
&configuration.ntp_servers,
|
||||
configuration.dapps_path,
|
||||
configuration.extra_dapps,
|
||||
rpc::DAPPS_DOMAIN,
|
||||
configuration.extra_embed_on,
|
||||
configuration.extra_script_src,
|
||||
).map(Some)
|
||||
}
|
||||
|
||||
pub fn new_ui(enabled: bool, ntp_server: &str, deps: Dependencies) -> Result<Option<Middleware>, String> {
|
||||
pub fn new_ui(enabled: bool, ntp_servers: &[String], deps: Dependencies) -> Result<Option<Middleware>, String> {
|
||||
if !enabled {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
server::ui_middleware(
|
||||
deps,
|
||||
ntp_server,
|
||||
ntp_servers,
|
||||
rpc::DAPPS_DOMAIN,
|
||||
).map(Some)
|
||||
}
|
||||
@@ -201,18 +209,19 @@ mod server {
|
||||
|
||||
pub fn dapps_middleware(
|
||||
_deps: Dependencies,
|
||||
_ntp_server: &str,
|
||||
_ntp_servers: &[String],
|
||||
_dapps_path: PathBuf,
|
||||
_extra_dapps: Vec<PathBuf>,
|
||||
_dapps_domain: &str,
|
||||
_extra_embed_on: Vec<(String, u16)>,
|
||||
_extra_script_src: Vec<(String, u16)>,
|
||||
) -> Result<Middleware, String> {
|
||||
Err("Your Parity version has been compiled without WebApps support.".into())
|
||||
}
|
||||
|
||||
pub fn ui_middleware(
|
||||
_deps: Dependencies,
|
||||
_ntp_server: &str,
|
||||
_ntp_servers: &[String],
|
||||
_dapps_domain: &str,
|
||||
) -> Result<Middleware, String> {
|
||||
Err("Your Parity version has been compiled without UI support.".into())
|
||||
@@ -238,22 +247,24 @@ mod server {
|
||||
|
||||
pub fn dapps_middleware(
|
||||
deps: Dependencies,
|
||||
ntp_server: &str,
|
||||
ntp_servers: &[String],
|
||||
dapps_path: PathBuf,
|
||||
extra_dapps: Vec<PathBuf>,
|
||||
dapps_domain: &str,
|
||||
extra_embed_on: Vec<(String, u16)>,
|
||||
extra_script_src: Vec<(String, u16)>,
|
||||
) -> Result<Middleware, String> {
|
||||
let signer = deps.signer;
|
||||
let parity_remote = parity_reactor::Remote::new(deps.remote.clone());
|
||||
let web_proxy_tokens = Arc::new(move |token| signer.web_proxy_access_token_domain(&token));
|
||||
|
||||
Ok(parity_dapps::Middleware::dapps(
|
||||
ntp_server,
|
||||
ntp_servers,
|
||||
deps.pool,
|
||||
parity_remote,
|
||||
deps.ui_address,
|
||||
extra_embed_on,
|
||||
extra_script_src,
|
||||
dapps_path,
|
||||
extra_dapps,
|
||||
dapps_domain,
|
||||
@@ -266,12 +277,12 @@ mod server {
|
||||
|
||||
pub fn ui_middleware(
|
||||
deps: Dependencies,
|
||||
ntp_server: &str,
|
||||
ntp_servers: &[String],
|
||||
dapps_domain: &str,
|
||||
) -> Result<Middleware, String> {
|
||||
let parity_remote = parity_reactor::Remote::new(deps.remote.clone());
|
||||
Ok(parity_dapps::Middleware::ui(
|
||||
ntp_server,
|
||||
ntp_servers,
|
||||
deps.pool,
|
||||
parity_remote,
|
||||
dapps_domain,
|
||||
|
||||
@@ -152,7 +152,7 @@ impl InformantData for FullNodeInformantData {
|
||||
max_peers: status.current_max_peers(net_config.min_peers, net_config.max_peers),
|
||||
}))
|
||||
}
|
||||
_ => (is_major_importing(None, queue_info.clone()), None),
|
||||
_ => (is_major_importing(self.sync.as_ref().map(|s| s.status().state), queue_info.clone()), None),
|
||||
};
|
||||
|
||||
Report {
|
||||
@@ -254,21 +254,22 @@ impl<T: InformantData> Informant<T> {
|
||||
return;
|
||||
}
|
||||
|
||||
let (client_report, full_report) = {
|
||||
let mut last_report = self.last_report.lock();
|
||||
let full_report = self.target.report();
|
||||
let diffed = full_report.client_report.clone() - &*last_report;
|
||||
*last_report = full_report.client_report.clone();
|
||||
(diffed, full_report)
|
||||
};
|
||||
|
||||
let Report {
|
||||
importing,
|
||||
chain_info,
|
||||
client_report,
|
||||
queue_info,
|
||||
cache_sizes,
|
||||
sync_info,
|
||||
} = self.target.report();
|
||||
|
||||
let client_report = {
|
||||
let mut last_report = self.last_report.lock();
|
||||
let diffed = client_report.clone() - &*last_report;
|
||||
*last_report = client_report.clone();
|
||||
diffed
|
||||
};
|
||||
..
|
||||
} = full_report;
|
||||
|
||||
let rpc_stats = self.rpc_stats.as_ref();
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ impl Default for HttpConfiguration {
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct UiConfiguration {
|
||||
pub enabled: bool,
|
||||
pub ntp_server: String,
|
||||
pub ntp_servers: Vec<String>,
|
||||
pub interface: String,
|
||||
pub port: u16,
|
||||
pub hosts: Option<Vec<String>>,
|
||||
@@ -95,7 +95,7 @@ impl From<UiConfiguration> for HttpConfiguration {
|
||||
enabled: conf.enabled,
|
||||
interface: conf.interface,
|
||||
port: conf.port,
|
||||
apis: rpc_apis::ApiSet::SafeContext,
|
||||
apis: rpc_apis::ApiSet::UnsafeContext,
|
||||
cors: None,
|
||||
hosts: conf.hosts,
|
||||
server_threads: None,
|
||||
@@ -108,7 +108,12 @@ impl Default for UiConfiguration {
|
||||
fn default() -> Self {
|
||||
UiConfiguration {
|
||||
enabled: true && cfg!(feature = "ui-enabled"),
|
||||
ntp_server: "pool.ntp.org:123".into(),
|
||||
ntp_servers: vec![
|
||||
"0.parity.pool.ntp.org:123".into(),
|
||||
"1.parity.pool.ntp.org:123".into(),
|
||||
"2.parity.pool.ntp.org:123".into(),
|
||||
"3.parity.pool.ntp.org:123".into(),
|
||||
],
|
||||
port: 8180,
|
||||
interface: "127.0.0.1".into(),
|
||||
hosts: Some(vec![]),
|
||||
@@ -159,7 +164,7 @@ impl Default for WsConfiguration {
|
||||
interface: "127.0.0.1".into(),
|
||||
port: 8546,
|
||||
apis: ApiSet::UnsafeContext,
|
||||
origins: Some(vec!["chrome-extension://*".into()]),
|
||||
origins: Some(vec!["chrome-extension://*".into(), "moz-extension://*".into()]),
|
||||
hosts: Some(Vec::new()),
|
||||
signer_path: replace_home(&data_dir, "$BASE/signer").into(),
|
||||
support_token_api: true,
|
||||
|
||||
@@ -56,7 +56,7 @@ use signer;
|
||||
use url;
|
||||
|
||||
// how often to take periodic snapshots.
|
||||
const SNAPSHOT_PERIOD: u64 = 10000;
|
||||
const SNAPSHOT_PERIOD: u64 = 5000;
|
||||
|
||||
// how many blocks to wait before starting a periodic snapshot.
|
||||
const SNAPSHOT_HISTORY: u64 = 100;
|
||||
@@ -210,6 +210,7 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) ->
|
||||
db_compaction: compaction,
|
||||
db_wal: cmd.wal,
|
||||
verify_full: true,
|
||||
check_seal: cmd.check_seal,
|
||||
};
|
||||
|
||||
config.queue.max_mem_use = cmd.cache_config.queue() as usize * 1024 * 1024;
|
||||
@@ -297,7 +298,7 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) ->
|
||||
};
|
||||
|
||||
let dapps_middleware = dapps::new(cmd.dapps_conf.clone(), dapps_deps.clone())?;
|
||||
let ui_middleware = dapps::new_ui(cmd.ui_conf.enabled, &cmd.ui_conf.ntp_server, dapps_deps)?;
|
||||
let ui_middleware = dapps::new_ui(cmd.ui_conf.enabled, &cmd.ui_conf.ntp_servers, dapps_deps)?;
|
||||
|
||||
// start RPCs
|
||||
let dapps_service = dapps::service(&dapps_middleware);
|
||||
@@ -659,7 +660,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
|
||||
}
|
||||
};
|
||||
let dapps_middleware = dapps::new(cmd.dapps_conf.clone(), dapps_deps.clone())?;
|
||||
let ui_middleware = dapps::new_ui(cmd.ui_conf.enabled, &cmd.ui_conf.ntp_server, dapps_deps)?;
|
||||
let ui_middleware = dapps::new_ui(cmd.ui_conf.enabled, &cmd.ui_conf.ntp_servers, dapps_deps)?;
|
||||
|
||||
let dapps_service = dapps::service(&dapps_middleware);
|
||||
let deps_for_rpc_apis = Arc::new(rpc_apis::FullDependencies {
|
||||
|
||||
@@ -21,9 +21,10 @@ use ethsync::SyncState;
|
||||
|
||||
/// Check if client is during major sync or during block import.
|
||||
pub fn is_major_importing(sync_state: Option<SyncState>, queue_info: BlockQueueInfo) -> bool {
|
||||
let is_syncing_state = sync_state.map_or(false, |s|
|
||||
s != SyncState::Idle && s != SyncState::NewBlocks
|
||||
);
|
||||
let is_syncing_state = sync_state.map_or(false, |s| match s {
|
||||
SyncState::Idle | SyncState::NewBlocks | SyncState::WaitingPeers => false,
|
||||
_ => true,
|
||||
});
|
||||
let is_verifying = queue_info.unverified_queue_size + queue_info.verified_queue_size > 3;
|
||||
is_verifying || is_syncing_state
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ pub fn fetch_gas_price_corpus(
|
||||
) -> BoxFuture<Corpus<U256>, Error> {
|
||||
const GAS_PRICE_SAMPLE_SIZE: usize = 100;
|
||||
|
||||
if let Some(cached) = cache.lock().gas_price_corpus() {
|
||||
if let Some(cached) = { cache.lock().gas_price_corpus() } {
|
||||
return future::ok(cached).boxed()
|
||||
}
|
||||
|
||||
@@ -512,6 +512,10 @@ pub fn execute<D: Dispatcher + 'static>(
|
||||
).boxed()
|
||||
},
|
||||
ConfirmationPayload::EthSignMessage(address, data) => {
|
||||
if accounts.is_hardware_address(address) {
|
||||
return future::err(errors::unsupported("Signing via hardware wallets is not supported.", None)).boxed();
|
||||
}
|
||||
|
||||
let hash = eth_data_hash(data);
|
||||
let res = signature(&accounts, address, hash, pass)
|
||||
.map(|result| result
|
||||
@@ -522,6 +526,10 @@ pub fn execute<D: Dispatcher + 'static>(
|
||||
future::done(res).boxed()
|
||||
},
|
||||
ConfirmationPayload::Decrypt(address, data) => {
|
||||
if accounts.is_hardware_address(address) {
|
||||
return future::err(errors::unsupported("Decrypting via hardware wallets is not supported.", None)).boxed();
|
||||
}
|
||||
|
||||
let res = decrypt(&accounts, address, data, pass)
|
||||
.map(|result| result
|
||||
.map(RpcBytes)
|
||||
|
||||
@@ -71,6 +71,14 @@ pub fn public_unsupported(details: Option<String>) -> Error {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unsupported<T: Into<String>>(msg: T, details: Option<T>) -> Error {
|
||||
Error {
|
||||
code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST),
|
||||
message: msg.into(),
|
||||
data: details.map(Into::into).map(Value::String),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request_not_found() -> Error {
|
||||
Error {
|
||||
code: ErrorCode::ServerError(codes::REQUEST_NOT_FOUND),
|
||||
|
||||
@@ -18,6 +18,7 @@ use std::sync::Arc;
|
||||
use ethcore::client::MiningBlockChainClient;
|
||||
use ethcore::miner::MinerService;
|
||||
use ethcore::transaction::{Transaction, SignedTransaction, Action};
|
||||
use util::U256;
|
||||
|
||||
use jsonrpc_core::Error;
|
||||
use v1::helpers::CallRequest;
|
||||
@@ -27,13 +28,22 @@ pub fn sign_call<B: MiningBlockChainClient, M: MinerService>(
|
||||
client: &Arc<B>,
|
||||
miner: &Arc<M>,
|
||||
request: CallRequest,
|
||||
gas_cap: bool,
|
||||
) -> Result<SignedTransaction, Error> {
|
||||
let from = request.from.unwrap_or(0.into());
|
||||
let mut gas = request.gas.unwrap_or(U256::max_value());
|
||||
if gas_cap {
|
||||
let max_gas = 50_000_000.into();
|
||||
if gas > max_gas {
|
||||
warn!("Gas limit capped to {} (from {})", max_gas, gas);
|
||||
gas = max_gas
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Transaction {
|
||||
nonce: request.nonce.unwrap_or_else(|| client.latest_nonce(&from)),
|
||||
action: request.to.map_or(Action::Create, Action::Call),
|
||||
gas: request.gas.unwrap_or(50_000_000.into()),
|
||||
gas,
|
||||
gas_price: request.gas_price.unwrap_or_else(|| default_gas_price(&**client, &**miner)),
|
||||
value: request.value.unwrap_or(0.into()),
|
||||
data: request.data.unwrap_or_default(),
|
||||
|
||||
@@ -26,6 +26,7 @@ use ethcore::filter::Filter as EthcoreFilter;
|
||||
use ethcore::transaction::{Action, Transaction as EthTransaction};
|
||||
|
||||
use futures::{future, Future, BoxFuture};
|
||||
use futures::future::Either;
|
||||
use jsonrpc_core::Error;
|
||||
use jsonrpc_macros::Trailing;
|
||||
|
||||
@@ -159,7 +160,9 @@ impl LightFetch {
|
||||
|
||||
/// helper for getting proved execution.
|
||||
pub fn proved_execution(&self, req: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<ExecutionResult, Error> {
|
||||
const DEFAULT_GAS_PRICE: U256 = U256([0, 0, 0, 21_000_000]);
|
||||
const DEFAULT_GAS_PRICE: u64 = 21_000;
|
||||
// starting gas when gas not provided.
|
||||
const START_GAS: u64 = 50_000;
|
||||
|
||||
let (sync, on_demand, client) = (self.sync.clone(), self.on_demand.clone(), self.client.clone());
|
||||
let req: CallRequestHelper = req.into();
|
||||
@@ -167,21 +170,21 @@ impl LightFetch {
|
||||
|
||||
let from = req.from.unwrap_or(Address::zero());
|
||||
let nonce_fut = match req.nonce {
|
||||
Some(nonce) => future::ok(Some(nonce)).boxed(),
|
||||
None => self.account(from, id).map(|acc| acc.map(|a| a.nonce)).boxed(),
|
||||
Some(nonce) => Either::A(future::ok(Some(nonce))),
|
||||
None => Either::B(self.account(from, id).map(|acc| acc.map(|a| a.nonce))),
|
||||
};
|
||||
|
||||
let gas_price_fut = match req.gas_price {
|
||||
Some(price) => future::ok(price).boxed(),
|
||||
None => dispatch::fetch_gas_price_corpus(
|
||||
Some(price) => Either::A(future::ok(price)),
|
||||
None => Either::B(dispatch::fetch_gas_price_corpus(
|
||||
self.sync.clone(),
|
||||
self.client.clone(),
|
||||
self.on_demand.clone(),
|
||||
self.cache.clone(),
|
||||
).map(|corp| match corp.median() {
|
||||
Some(median) => *median,
|
||||
None => DEFAULT_GAS_PRICE,
|
||||
}).boxed()
|
||||
None => DEFAULT_GAS_PRICE.into(),
|
||||
}))
|
||||
};
|
||||
|
||||
// if nonce resolves, this should too since it'll be in the LRU-cache.
|
||||
@@ -190,22 +193,29 @@ impl LightFetch {
|
||||
// fetch missing transaction fields from the network.
|
||||
nonce_fut.join(gas_price_fut).and_then(move |(nonce, gas_price)| {
|
||||
let action = req.to.map_or(Action::Create, Action::Call);
|
||||
let gas = req.gas.unwrap_or(U256::from(10_000_000)); // better gas amount?
|
||||
let value = req.value.unwrap_or_else(U256::zero);
|
||||
let data = req.data.unwrap_or_default();
|
||||
|
||||
future::done(match nonce {
|
||||
Some(n) => Ok(EthTransaction {
|
||||
future::done(match (nonce, req.gas) {
|
||||
(Some(n), Some(gas)) => Ok((true, EthTransaction {
|
||||
nonce: n,
|
||||
action: action,
|
||||
gas: gas,
|
||||
gas_price: gas_price,
|
||||
value: value,
|
||||
data: data,
|
||||
}.fake_sign(from)),
|
||||
None => Err(errors::unknown_block()),
|
||||
})),
|
||||
(Some(n), None) => Ok((false, EthTransaction {
|
||||
nonce: n,
|
||||
action: action,
|
||||
gas: START_GAS.into(),
|
||||
gas_price: gas_price,
|
||||
value: value,
|
||||
data: data,
|
||||
})),
|
||||
(None, _) => Err(errors::unknown_block()),
|
||||
})
|
||||
}).join(header_fut).and_then(move |(tx, hdr)| {
|
||||
}).join(header_fut).and_then(move |((gas_known, tx), hdr)| {
|
||||
// then request proved execution.
|
||||
// TODO: get last-hashes from network.
|
||||
let env_info = match client.env_info(id) {
|
||||
@@ -213,24 +223,15 @@ impl LightFetch {
|
||||
_ => return future::err(errors::unknown_block()).boxed(),
|
||||
};
|
||||
|
||||
let request = request::TransactionProof {
|
||||
execute_tx(gas_known, ExecuteParams {
|
||||
from: from,
|
||||
tx: tx,
|
||||
header: hdr.into(),
|
||||
hdr: hdr,
|
||||
env_info: env_info,
|
||||
engine: client.engine().clone(),
|
||||
};
|
||||
|
||||
let proved_future = sync.with_context(move |ctx| {
|
||||
on_demand
|
||||
.request(ctx, request)
|
||||
.expect("no back-references; therefore all back-refs valid; qed")
|
||||
.map_err(errors::on_demand_cancel).boxed()
|
||||
});
|
||||
|
||||
match proved_future {
|
||||
Some(fut) => fut.boxed(),
|
||||
None => future::err(errors::network_disabled()).boxed(),
|
||||
}
|
||||
on_demand: on_demand,
|
||||
sync: sync,
|
||||
})
|
||||
}).boxed()
|
||||
}
|
||||
|
||||
@@ -320,3 +321,66 @@ impl LightFetch {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct ExecuteParams {
|
||||
from: Address,
|
||||
tx: EthTransaction,
|
||||
hdr: encoded::Header,
|
||||
env_info: ::evm::env_info::EnvInfo,
|
||||
engine: Arc<::ethcore::engines::Engine>,
|
||||
on_demand: Arc<OnDemand>,
|
||||
sync: Arc<LightSync>,
|
||||
}
|
||||
|
||||
// has a peer execute the transaction with given params. If `gas_known` is false,
|
||||
// this will double the gas on each `OutOfGas` error.
|
||||
fn execute_tx(gas_known: bool, params: ExecuteParams) -> BoxFuture<ExecutionResult, Error> {
|
||||
if !gas_known {
|
||||
future::loop_fn(params, |mut params| {
|
||||
execute_tx(true, params.clone()).and_then(move |res| {
|
||||
match res {
|
||||
Ok(executed) => {
|
||||
// TODO: how to distinguish between actual OOG and
|
||||
// exception?
|
||||
if executed.exception.is_some() {
|
||||
let old_gas = params.tx.gas;
|
||||
params.tx.gas = params.tx.gas * 2.into();
|
||||
if params.tx.gas > params.hdr.gas_limit() {
|
||||
params.tx.gas = old_gas;
|
||||
} else {
|
||||
return Ok(future::Loop::Continue(params))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(future::Loop::Break(Ok(executed)))
|
||||
}
|
||||
failed => Ok(future::Loop::Break(failed)),
|
||||
}
|
||||
})
|
||||
}).boxed()
|
||||
} else {
|
||||
trace!(target: "light_fetch", "Placing execution request for {} gas in on_demand",
|
||||
params.tx.gas);
|
||||
|
||||
let request = request::TransactionProof {
|
||||
tx: params.tx.fake_sign(params.from),
|
||||
header: params.hdr.into(),
|
||||
env_info: params.env_info,
|
||||
engine: params.engine,
|
||||
};
|
||||
|
||||
let on_demand = params.on_demand;
|
||||
let proved_future = params.sync.with_context(move |ctx| {
|
||||
on_demand
|
||||
.request(ctx, request)
|
||||
.expect("no back-references; therefore all back-refs valid; qed")
|
||||
.map_err(errors::on_demand_cancel)
|
||||
});
|
||||
|
||||
match proved_future {
|
||||
Some(fut) => fut.boxed(),
|
||||
None => future::err(errors::network_disabled()).boxed(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -641,9 +641,9 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
|
||||
self.send_raw_transaction(raw)
|
||||
}
|
||||
|
||||
fn call(&self, request: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<Bytes, Error> {
|
||||
fn call(&self, meta: Self::Metadata, request: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<Bytes, Error> {
|
||||
let request = CallRequest::into(request);
|
||||
let signed = match fake_sign::sign_call(&self.client, &self.miner, request) {
|
||||
let signed = match fake_sign::sign_call(&self.client, &self.miner, request, meta.is_dapp()) {
|
||||
Ok(signed) => signed,
|
||||
Err(e) => return future::err(e).boxed(),
|
||||
};
|
||||
@@ -659,9 +659,9 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
|
||||
).boxed()
|
||||
}
|
||||
|
||||
fn estimate_gas(&self, request: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256, Error> {
|
||||
fn estimate_gas(&self, meta: Self::Metadata, request: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256, Error> {
|
||||
let request = CallRequest::into(request);
|
||||
let signed = match fake_sign::sign_call(&self.client, &self.miner, request) {
|
||||
let signed = match fake_sign::sign_call(&self.client, &self.miner, request, meta.is_dapp()) {
|
||||
Ok(signed) => signed,
|
||||
Err(e) => return future::err(e).boxed(),
|
||||
};
|
||||
|
||||
@@ -383,7 +383,7 @@ impl Eth for EthClient {
|
||||
self.send_raw_transaction(raw)
|
||||
}
|
||||
|
||||
fn call(&self, req: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<Bytes, Error> {
|
||||
fn call(&self, _meta: Self::Metadata, req: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<Bytes, Error> {
|
||||
self.fetcher().proved_execution(req, num).and_then(|res| {
|
||||
match res {
|
||||
Ok(exec) => Ok(exec.output.into()),
|
||||
@@ -392,7 +392,7 @@ impl Eth for EthClient {
|
||||
}).boxed()
|
||||
}
|
||||
|
||||
fn estimate_gas(&self, req: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256, Error> {
|
||||
fn estimate_gas(&self, _meta: Self::Metadata, req: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256, Error> {
|
||||
// TODO: binary chop for more accurate estimates.
|
||||
self.fetcher().proved_execution(req, num).and_then(|res| {
|
||||
match res {
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
//! Traces api implementation.
|
||||
|
||||
use jsonrpc_core::Error;
|
||||
use jsonrpc_core::futures::{future, Future, BoxFuture};
|
||||
use jsonrpc_macros::Trailing;
|
||||
use v1::Metadata;
|
||||
use v1::traits::Traces;
|
||||
use v1::helpers::errors;
|
||||
use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, H256};
|
||||
@@ -27,6 +29,8 @@ use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, By
|
||||
pub struct TracesClient;
|
||||
|
||||
impl Traces for TracesClient {
|
||||
type Metadata = Metadata;
|
||||
|
||||
fn filter(&self, _filter: TraceFilter) -> Result<Option<Vec<LocalizedTrace>>, Error> {
|
||||
Err(errors::light_unimplemented(None))
|
||||
}
|
||||
@@ -43,8 +47,8 @@ impl Traces for TracesClient {
|
||||
Err(errors::light_unimplemented(None))
|
||||
}
|
||||
|
||||
fn call(&self, _request: CallRequest, _flags: Vec<String>, _block: Trailing<BlockNumber>) -> Result<TraceResults, Error> {
|
||||
Err(errors::light_unimplemented(None))
|
||||
fn call(&self, _meta: Self::Metadata, _request: CallRequest, _flags: Vec<String>, _block: Trailing<BlockNumber>) -> BoxFuture<TraceResults, Error> {
|
||||
future::err(errors::light_unimplemented(None)).boxed()
|
||||
}
|
||||
|
||||
fn raw_transaction(&self, _raw_transaction: Bytes, _flags: Vec<String>, _block: Trailing<BlockNumber>) -> Result<TraceResults, Error> {
|
||||
|
||||
@@ -18,13 +18,15 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use rlp::UntrustedRlp;
|
||||
use ethcore::client::{MiningBlockChainClient, CallAnalytics, TransactionId, TraceId};
|
||||
use ethcore::miner::MinerService;
|
||||
use ethcore::transaction::SignedTransaction;
|
||||
use rlp::UntrustedRlp;
|
||||
|
||||
use jsonrpc_core::Error;
|
||||
use jsonrpc_core::futures::{self, Future, BoxFuture};
|
||||
use jsonrpc_macros::Trailing;
|
||||
use v1::Metadata;
|
||||
use v1::traits::Traces;
|
||||
use v1::helpers::{errors, fake_sign};
|
||||
use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, H256};
|
||||
@@ -54,6 +56,8 @@ impl<C, M> TracesClient<C, M> {
|
||||
}
|
||||
|
||||
impl<C, M> Traces for TracesClient<C, M> where C: MiningBlockChainClient + 'static, M: MinerService + 'static {
|
||||
type Metadata = Metadata;
|
||||
|
||||
fn filter(&self, filter: TraceFilter) -> Result<Option<Vec<LocalizedTrace>>, Error> {
|
||||
Ok(self.client.filter_traces(filter.into())
|
||||
.map(|traces| traces.into_iter().map(LocalizedTrace::from).collect()))
|
||||
@@ -79,15 +83,17 @@ impl<C, M> Traces for TracesClient<C, M> where C: MiningBlockChainClient + 'stat
|
||||
.map(LocalizedTrace::from))
|
||||
}
|
||||
|
||||
fn call(&self, request: CallRequest, flags: Vec<String>, block: Trailing<BlockNumber>) -> Result<TraceResults, Error> {
|
||||
fn call(&self, meta: Self::Metadata, request: CallRequest, flags: Vec<String>, block: Trailing<BlockNumber>) -> BoxFuture<TraceResults, Error> {
|
||||
let block = block.unwrap_or_default();
|
||||
|
||||
let request = CallRequest::into(request);
|
||||
let signed = fake_sign::sign_call(&self.client, &self.miner, request)?;
|
||||
let signed = try_bf!(fake_sign::sign_call(&self.client, &self.miner, request, meta.is_dapp()));
|
||||
|
||||
self.client.call(&signed, block.into(), to_call_analytics(flags))
|
||||
let res = self.client.call(&signed, block.into(), to_call_analytics(flags))
|
||||
.map(TraceResults::from)
|
||||
.map_err(errors::call)
|
||||
.map_err(errors::call);
|
||||
|
||||
futures::done(res).boxed()
|
||||
}
|
||||
|
||||
fn raw_transaction(&self, raw_transaction: Bytes, flags: Vec<String>, block: Trailing<BlockNumber>) -> Result<TraceResults, Error> {
|
||||
|
||||
@@ -42,6 +42,15 @@ impl Metadata {
|
||||
_ => DappId::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the request originates from a Dapp.
|
||||
pub fn is_dapp(&self) -> bool {
|
||||
if let Origin::Dapps(_) = self.origin {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl jsonrpc_core::Metadata for Metadata {}
|
||||
|
||||
@@ -41,7 +41,7 @@ impl TestSnapshotService {
|
||||
|
||||
impl SnapshotService for TestSnapshotService {
|
||||
fn manifest(&self) -> Option<ManifestData> { None }
|
||||
fn min_supported_version(&self) -> Option<u64> { None }
|
||||
fn supported_versions(&self) -> Option<(u64, u64)> { None }
|
||||
fn chunk(&self, _hash: H256) -> Option<Bytes> { None }
|
||||
fn status(&self) -> RestorationStatus { self.status.lock().clone() }
|
||||
fn begin_restore(&self, _manifest: ManifestData) { }
|
||||
|
||||
@@ -25,12 +25,12 @@ use evm::CallType;
|
||||
|
||||
use jsonrpc_core::IoHandler;
|
||||
use v1::tests::helpers::{TestMinerService};
|
||||
use v1::{Traces, TracesClient};
|
||||
use v1::{Metadata, Traces, TracesClient};
|
||||
|
||||
struct Tester {
|
||||
client: Arc<TestBlockChainClient>,
|
||||
_miner: Arc<TestMinerService>,
|
||||
io: IoHandler,
|
||||
io: IoHandler<Metadata>,
|
||||
}
|
||||
|
||||
fn io() -> Tester {
|
||||
@@ -67,7 +67,7 @@ fn io() -> Tester {
|
||||
}));
|
||||
let miner = Arc::new(TestMinerService::default());
|
||||
let traces = TracesClient::new(&client, &miner);
|
||||
let mut io = IoHandler::new();
|
||||
let mut io = IoHandler::default();
|
||||
io.extend_with(traces.to_delegate());
|
||||
|
||||
Tester {
|
||||
|
||||
@@ -110,12 +110,12 @@ build_rpc_trait! {
|
||||
fn submit_transaction(&self, Bytes) -> Result<H256, Error>;
|
||||
|
||||
/// Call contract, returning the output data.
|
||||
#[rpc(async, name = "eth_call")]
|
||||
fn call(&self, CallRequest, Trailing<BlockNumber>) -> BoxFuture<Bytes, Error>;
|
||||
#[rpc(meta, name = "eth_call")]
|
||||
fn call(&self, Self::Metadata, CallRequest, Trailing<BlockNumber>) -> BoxFuture<Bytes, Error>;
|
||||
|
||||
/// Estimate gas needed for execution of given contract.
|
||||
#[rpc(async, name = "eth_estimateGas")]
|
||||
fn estimate_gas(&self, CallRequest, Trailing<BlockNumber>) -> BoxFuture<U256, Error>;
|
||||
#[rpc(meta, name = "eth_estimateGas")]
|
||||
fn estimate_gas(&self, Self::Metadata, CallRequest, Trailing<BlockNumber>) -> BoxFuture<U256, Error>;
|
||||
|
||||
/// Get transaction by its hash.
|
||||
#[rpc(name = "eth_getTransactionByHash")]
|
||||
|
||||
@@ -17,12 +17,15 @@
|
||||
//! Traces specific rpc interface.
|
||||
|
||||
use jsonrpc_core::Error;
|
||||
use jsonrpc_core::futures::BoxFuture;
|
||||
use jsonrpc_macros::Trailing;
|
||||
use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, H256};
|
||||
|
||||
build_rpc_trait! {
|
||||
/// Traces specific rpc interface.
|
||||
pub trait Traces {
|
||||
type Metadata;
|
||||
|
||||
/// Returns traces matching given filter.
|
||||
#[rpc(name = "trace_filter")]
|
||||
fn filter(&self, TraceFilter) -> Result<Option<Vec<LocalizedTrace>>, Error>;
|
||||
@@ -40,8 +43,8 @@ build_rpc_trait! {
|
||||
fn block_traces(&self, BlockNumber) -> Result<Option<Vec<LocalizedTrace>>, Error>;
|
||||
|
||||
/// Executes the given call and returns a number of possible traces for it.
|
||||
#[rpc(name = "trace_call")]
|
||||
fn call(&self, CallRequest, Vec<String>, Trailing<BlockNumber>) -> Result<TraceResults, Error>;
|
||||
#[rpc(meta, name = "trace_call")]
|
||||
fn call(&self, Self::Metadata, CallRequest, Vec<String>, Trailing<BlockNumber>) -> BoxFuture<TraceResults, Error>;
|
||||
|
||||
/// Executes the given raw transaction and returns a number of possible traces for it.
|
||||
#[rpc(name = "trace_rawTransaction")]
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#!/bin/bash
|
||||
cd docker/hub
|
||||
if [ "$1" == "latest" ]; then DOCKER_BUILD_TAG="beta-release"; fi
|
||||
docker build --build-arg BUILD_TAG=$DOCKER_BUILD_TAG --no-cache=true --tag $2/parity:$1 .
|
||||
docker push $2/parity:$1
|
||||
DOCKER_BUILD_TAG=$1
|
||||
echo "Docker build tag: " $DOCKER_BUILD_TAG
|
||||
docker build --build-arg BUILD_TAG=$DOCKER_BUILD_TAG --no-cache=true --tag parity/parity:$DOCKER_BUILD_TAG .
|
||||
docker run -it parity/parity:$DOCKER_BUILD_TAG -v
|
||||
docker push parity/parity:$DOCKER_BUILD_TAG
|
||||
|
||||
@@ -17,6 +17,6 @@ apps:
|
||||
|
||||
parts:
|
||||
parity:
|
||||
source: .
|
||||
source: ..
|
||||
plugin: rust
|
||||
build-packages: [g++, libudev-dev, libssl-dev, make, pkg-config]
|
||||
|
||||
@@ -131,7 +131,7 @@ const MAX_TRANSACTION_PACKET_SIZE: usize = 8 * 1024 * 1024;
|
||||
// Maximal number of transactions in sent in single packet.
|
||||
const MAX_TRANSACTIONS_TO_PROPAGATE: usize = 64;
|
||||
// Min number of blocks to be behind for a snapshot sync
|
||||
const SNAPSHOT_RESTORE_THRESHOLD: BlockNumber = 100000;
|
||||
const SNAPSHOT_RESTORE_THRESHOLD: BlockNumber = 10000;
|
||||
const SNAPSHOT_MIN_PEERS: usize = 3;
|
||||
|
||||
const STATUS_PACKET: u8 = 0x00;
|
||||
@@ -504,7 +504,7 @@ impl ChainSync {
|
||||
}
|
||||
|
||||
fn maybe_start_snapshot_sync(&mut self, io: &mut SyncIo) {
|
||||
if !self.enable_warp_sync || io.snapshot_service().min_supported_version().is_none() {
|
||||
if !self.enable_warp_sync || io.snapshot_service().supported_versions().is_none() {
|
||||
return;
|
||||
}
|
||||
if self.state != SyncState::WaitingPeers && self.state != SyncState::Blocks && self.state != SyncState::Waiting {
|
||||
@@ -1044,11 +1044,11 @@ impl ChainSync {
|
||||
Ok(manifest) => manifest,
|
||||
};
|
||||
|
||||
let is_supported_version = io.snapshot_service().min_supported_version()
|
||||
.map_or(false, |v| manifest.version >= v);
|
||||
let is_supported_version = io.snapshot_service().supported_versions()
|
||||
.map_or(false, |(l, h)| manifest.version >= l && manifest.version <= h);
|
||||
|
||||
if !is_supported_version {
|
||||
trace!(target: "sync", "{}: Snapshot manifest version too low: {}", peer_id, manifest.version);
|
||||
trace!(target: "sync", "{}: Snapshot manifest version not supported: {}", peer_id, manifest.version);
|
||||
io.disable_peer(peer_id);
|
||||
self.continue_sync(io);
|
||||
return Ok(());
|
||||
|
||||
@@ -285,6 +285,13 @@ impl<L: AsLightClient + Send + Sync> Handler for LightSync<L> {
|
||||
best.clone()
|
||||
};
|
||||
|
||||
{
|
||||
let mut pending_reqs = self.pending_reqs.lock();
|
||||
for unfulfilled in unfulfilled {
|
||||
pending_reqs.remove(&unfulfilled);
|
||||
}
|
||||
}
|
||||
|
||||
if new_best.is_none() {
|
||||
debug!(target: "sync", "No peers remain. Reverting to idle");
|
||||
*self.state.lock() = SyncState::Idle;
|
||||
@@ -503,10 +510,12 @@ impl<L: AsLightClient> LightSync<L> {
|
||||
None
|
||||
}
|
||||
}).collect();
|
||||
|
||||
let mut rng = self.rng.lock();
|
||||
let mut requested_from = HashSet::new();
|
||||
|
||||
// naive request dispatcher: just give to any peer which says it will
|
||||
// give us responses.
|
||||
// give us responses. but only one request per peer per state transition.
|
||||
let dispatcher = move |req: HeadersRequest| {
|
||||
rng.shuffle(&mut peer_ids);
|
||||
|
||||
@@ -521,9 +530,12 @@ impl<L: AsLightClient> LightSync<L> {
|
||||
builder.build()
|
||||
};
|
||||
for peer in &peer_ids {
|
||||
if requested_from.contains(peer) { continue }
|
||||
match ctx.request_from(*peer, request.clone()) {
|
||||
Ok(id) => {
|
||||
self.pending_reqs.lock().insert(id.clone());
|
||||
requested_from.insert(peer.clone());
|
||||
|
||||
return Some(id)
|
||||
}
|
||||
Err(NetError::NoCredits) => {}
|
||||
|
||||
@@ -71,8 +71,8 @@ impl SnapshotService for TestSnapshotService {
|
||||
self.manifest.as_ref().cloned()
|
||||
}
|
||||
|
||||
fn min_supported_version(&self) -> Option<u64> {
|
||||
Some(1)
|
||||
fn supported_versions(&self) -> Option<(u64, u64)> {
|
||||
Some((1, 2))
|
||||
}
|
||||
|
||||
fn chunk(&self, hash: H256) -> Option<Bytes> {
|
||||
|
||||
@@ -3,7 +3,7 @@ description = "Ethcore utility library"
|
||||
homepage = "http://parity.io"
|
||||
license = "GPL-3.0"
|
||||
name = "ethcore-util"
|
||||
version = "1.7.0"
|
||||
version = "1.7.1"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
build = "build.rs"
|
||||
|
||||
|
||||
@@ -857,11 +857,16 @@ impl Host {
|
||||
// Add it to the node table
|
||||
if !s.info.originated {
|
||||
if let Ok(address) = s.remote_addr() {
|
||||
let entry = NodeEntry { id: id, endpoint: NodeEndpoint { address: address, udp_port: address.port() } };
|
||||
self.nodes.write().add_node(Node::new(entry.id.clone(), entry.endpoint.clone()));
|
||||
let mut discovery = self.discovery.lock();
|
||||
if let Some(ref mut discovery) = *discovery {
|
||||
discovery.add_node(entry);
|
||||
// We can't know remote listening ports, so just assume defaults and hope for the best.
|
||||
let endpoint = NodeEndpoint { address: SocketAddr::new(address.ip(), DEFAULT_PORT), udp_port: DEFAULT_PORT };
|
||||
let entry = NodeEntry { id: id, endpoint: endpoint };
|
||||
let mut nodes = self.nodes.write();
|
||||
if !nodes.contains(&entry.id) {
|
||||
nodes.add_node(Node::new(entry.id.clone(), entry.endpoint.clone()));
|
||||
let mut discovery = self.discovery.lock();
|
||||
if let Some(ref mut discovery) = *discovery {
|
||||
discovery.add_node(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,6 +236,11 @@ impl NodeTable {
|
||||
self.nodes.get_mut(id)
|
||||
}
|
||||
|
||||
/// Check if a node exists in the table.
|
||||
pub fn contains(&self, id: &NodeId) -> bool {
|
||||
self.nodes.contains_key(id)
|
||||
}
|
||||
|
||||
/// Apply table changes coming from discovery
|
||||
pub fn update(&mut self, mut update: TableUpdates, reserved: &HashSet<NodeId>) {
|
||||
for (_, node) in update.added.drain() {
|
||||
|
||||
@@ -30,6 +30,8 @@ pub enum DecoderError {
|
||||
RlpInvalidIndirection,
|
||||
/// Declared length is inconsistent with data specified after.
|
||||
RlpInconsistentLengthAndData,
|
||||
/// Declared length is invalid and results in overflow
|
||||
RlpInvalidLength,
|
||||
/// Custom rlp decoding error.
|
||||
Custom(&'static str),
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user